/* * SRBBitStore.java * * Version: $Revision: 1759 $ * * Date: $Date: 2007-04-05 15:26:37 +0000 (Thu, 05 Apr 2007) $ * * Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts * Institute of Technology. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.dspace.storage.bitstore.impl; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import edu.sdsc.grid.io.GeneralFile; import edu.sdsc.grid.io.srb.SRBAccount; import edu.sdsc.grid.io.srb.SRBFile; import edu.sdsc.grid.io.srb.SRBFileInputStream; import edu.sdsc.grid.io.srb.SRBFileOutputStream; import edu.sdsc.grid.io.srb.SRBFileSystem; import org.dspace.core.ConfigurationManager; import org.dspace.core.Utils; import org.dspace.storage.bitstore.BitStore; /** * Implements an asset store using the Storage Resource Broker * * @author David Little, Richard Rodgers */ public class SRBBitStore implements BitStore { /** log4j log */ private static Logger log = Logger.getLogger(SRBBitStore.class); // You should not change these settings if you have data in the // asset store, as the BitstreamStorageManager will be unable // to find your existing data. private static final int digitsPerLevel = 2; private static final int directoryLevels = 3; // Checksum algorithm private static final String CSA = "MD5"; private SRBFile baseDir = null; public SRBBitStore() { } public void init(String params) { SRBAccount account = new SRBAccount( ConfigurationManager.getProperty("srb.host"), ConfigurationManager.getIntProperty("srb.port"), ConfigurationManager.getProperty("srb.username"), ConfigurationManager.getProperty("srb.password"), ConfigurationManager.getProperty("srb.homedirectory"), ConfigurationManager.getProperty("srb.mdasdomainname"), ConfigurationManager.getProperty("srb.defaultstorageresource"), ConfigurationManager.getProperty("srb.mcatzone")); SRBFileSystem srbFileSystem = null; try { srbFileSystem = new SRBFileSystem((SRBAccount)account); } catch (NullPointerException e) { log.error("No SRBAccount for assetstore "); } catch (IOException e) { log.error("Problem getting SRBFileSystem for assetstore" ); } if (srbFileSystem == null) { log.error("SRB FileSystem is null for assetstore "); } String sSRBAssetstore = ConfigurationManager.getProperty("srb.parentdir"); if (sSRBAssetstore == null) { log.error("srb.parentdir is undefined for assetstore "); } baseDir = new SRBFile(srbFileSystem, sSRBAssetstore); } public String generateId() { return Utils.generateKey(); } public InputStream get(String id) throws IOException { return new SRBFileInputStream(getFile(id)); } /** * Store a stream of bits. * *

* If this method returns successfully, the bits have been stored. * If an exception is thrown, the bits have not been stored. *

* * @param context * The current context * @param in * The stream of bits to store * @exception IOException * If a problem occurs while storing the bits * * @return Map containing technical metadata (size, checksum, etc) */ public Map put(InputStream in, String id) throws IOException { SRBFile file = getFile(id); // Make the parent dirs if necessary GeneralFile parent = file.getParentFile(); if (!parent.exists()) { parent.mkdirs(); } //Create the corresponding file and open it file.createNewFile(); SRBFileOutputStream fos = new SRBFileOutputStream(file); // Read through a digest input stream that will work out the MD5 DigestInputStream dis = null; try { dis = new DigestInputStream(in, MessageDigest.getInstance(CSA)); } // Should never happen catch (NoSuchAlgorithmException nsae) { log.warn("Caught NoSuchAlgorithmException", nsae); } Utils.bufferedCopy(dis, fos); fos.close(); in.close(); Map attrs = new HashMap(); attrs.put( "size_bytes", String.valueOf(file.length())); attrs.put( "checksum", Utils.toHex(dis.getMessageDigest().digest())); attrs.put( "checksum_algorithm", CSA); return attrs; } /** * Obtain technical metadata about an asset in the asset store. * * @param context * The current context * @param id * The ID of the asset to describe * @param attrs * A Map whose keys consist of desired metadata fields * * @exception IOException * If a problem occurs while obtaining metadata * @return attrs * A Map with key/value pairs of desired metadata */ public Map about(String id, Map attrs) throws IOException { // pretty worthless hack - get MD5 on just the filename (!) for SRB file SRBFile file = getFile(id); if (file != null && file.exists()) { if (attrs.containsKey("size_bytes")) { attrs.put("size_bytes", String.valueOf(file.length())); } if (attrs.containsKey("checksum")) { // get MD5 on just the filename (!) for SRB file int iLastSlash = id.lastIndexOf('/'); String filename = id.substring(iLastSlash + 1); MessageDigest md = null; try { md = MessageDigest.getInstance(CSA); } catch (NoSuchAlgorithmException e) { log.error("Caught NoSuchAlgorithmException", e); throw new IOException("Invalid checksum algorithm"); } attrs.put("checksum", Utils.toHex(md.digest(filename.getBytes()))); attrs.put("checksum_algorithm", CSA); } return attrs; } return null; } public void remove(String id) throws IOException { //return true; } //////////////////////////////////////// // Internal methods //////////////////////////////////////// /** * Delete empty parent directories. * * @param file * The file with parent directories to delete */ private synchronized static void deleteParents(SRBFile file) { if (file == null) { return; } GeneralFile tmp = file; for (int i = 0; i < directoryLevels; i++) { GeneralFile directory = tmp.getParentFile(); GeneralFile[] files = directory.listFiles(); // Only delete empty directories if (files.length != 0) { break; } directory.delete(); tmp = directory; } } private SRBFile getFile(String id) throws IOException { String fileName = getIntermediatePath(id) + id; if (log.isDebugEnabled()) { log.debug("SRB filename for " + id + " is " + (baseDir.toString() + fileName)); } return new SRBFile(baseDir, fileName); } /** * Return the path derived from the internal_id. This method * splits the id into groups which become subdirectories. * * @param iInternalId * The internal_id * @return The path based on the id without leading or trailing separators */ private static String getIntermediatePath(String id) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < directoryLevels; i++) { int digits = i * digitsPerLevel; if (i > 0) { buf.append(File.separator); } buf.append(id.substring(digits, digits + digitsPerLevel)); } buf.append(File.separator); return buf.toString(); } }