/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package fedora.server.storage.types; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Date; import org.apache.log4j.Logger; import fedora.server.errors.GeneralException; import fedora.server.errors.StreamIOException; import fedora.server.utilities.StringUtility; /** * A Fedora datastream. * * @author Sandy Payette */ public class Datastream { /** Logger for this class. */ private static final Logger LOG = Logger.getLogger(Datastream.class.getName()); public final static String CHECKSUMTYPE_DISABLED = "DISABLED"; public final static String CHECKSUM_NONE = "none"; public final static String CHECKSUM_IOEXCEPTION = "ExceptionReadingStream"; public boolean isNew = false; public String DatastreamID; public String[] DatastreamAltIDs = new String[0]; public String DSFormatURI; public String DSMIME; /** * Datastream Control Group: This indicates the nature of the repository's * control over the datastream content. Values are: * <p> * R = Redirected. The datastream resides on an external server and is * referenced by a URL. When a dissemination request for the *datastream* * comes through, Fedora sends an HTTP redirect to the client, thereby * causing the client to directly access the datastream from its external * location. This is useful in cases where the datastream is really some * sort of streaming media that cannot be piped through Fedora, or the * datastream is an HTML document with relative hyperlinks to the server on * which is is normally hosted. E = External Referenced. The datastream * content is external to the repository and referenced by URL. The content * is not under the direct custodianship of the repository. The URL is * considered public so the repository does not worry about whether it * exposes the datastream location to collaborating services. M = Managed * Content. The datastream content is stored and managed by the repository. * The content is considered under the direct custodianship of the * repository. The repository does not expose the underlying storage * location to collaborating services and it mediates all access to the * content by collaborating services. X = Inline XML Metadata. The * datastream content is user-defined XML metadata that is stored within the * digital object XML file itself. As such, it is intrinsically bound to the * digital object, and by implication, it is stored and managed by the * repository. The content considered under the custodianship of the * repository. */ public String DSControlGrp; /** Info Type: DATA or one of the METS MDType values */ /** Used to maintain backwards compatibility with METS-Fedora */ public String DSInfoType; public String DSState; public boolean DSVersionable; // Version-level attributes: public String DSVersionID; public String DSLabel; public Date DSCreateDT; public long DSSize; public String DSLocation; public String DSLocationType; public String DSChecksumType; public String DSChecksum; public static boolean autoChecksum = false; public static String defaultChecksumType = "DISABLED"; public Datastream() { } public InputStream getContentStream() throws StreamIOException { return null; } public InputStream getContentStreamForChecksum() throws StreamIOException { return getContentStream(); } public static String getDefaultChecksumType() { return defaultChecksumType; } public String getChecksumType() { if (DSChecksumType == null || DSChecksumType.equals("") || DSChecksumType.equals("none")) { DSChecksumType = getDefaultChecksumType(); if (DSChecksumType == null) { LOG.warn("checksumType is null"); } } return DSChecksumType; } public String getChecksum() { if (DSChecksum == null || DSChecksum.equals("none")) { DSChecksum = computeChecksum(getChecksumType()); } LOG.debug("Checksum = " + DSChecksum); return DSChecksum; } public String setChecksum(String csType) { if (csType != null) { DSChecksumType = csType; } LOG.debug("setting ChecksumType to " + DSChecksumType); DSChecksum = computeChecksum(DSChecksumType); return DSChecksum; } public boolean compareChecksum() { if (DSChecksumType == null || DSChecksumType.equals("") || DSChecksumType.equals("none")) { return false; } if (DSChecksum == null) { return false; } if (DSChecksumType.equals(CHECKSUMTYPE_DISABLED)) { return true; } String curChecksum = computeChecksum(DSChecksumType); if (curChecksum.equals(DSChecksum)) { return true; } return false; } private String computeChecksum(String csType) { LOG.debug("checksumType is " + csType); String checksum = "none"; if (csType == null) { LOG.warn("checksumType is null"); } if (csType.equals(CHECKSUMTYPE_DISABLED)) { checksum = "none"; return checksum; } try { MessageDigest md = MessageDigest.getInstance(csType); LOG.debug("Classname = " + this.getClass().getName()); LOG.debug("location = " + DSLocation); InputStream is = getContentStreamForChecksum(); if (is != null) { byte buffer[] = new byte[5000]; int numread; LOG.debug("Reading content..."); while ((numread = is.read(buffer, 0, 5000)) > 0) { md.update(buffer, 0, numread); } LOG.debug("...Done reading content"); checksum = StringUtility.byteArraytoHexString(md.digest()); } } catch (NoSuchAlgorithmException e) { checksum = "none"; } catch (StreamIOException e) { // TODO Auto-generated catch block checksum = CHECKSUM_IOEXCEPTION; LOG.warn("IOException reading datastream to generate checksum"); } catch (IOException e) { // TODO Auto-generated catch block checksum = CHECKSUM_IOEXCEPTION; LOG.warn("IOException reading datastream to generate checksum"); } return checksum; } public static String validateChecksumType(String checksumType) throws GeneralException { String csType = null; if (checksumType == null || checksumType.equalsIgnoreCase("DEFAULT")) { return Datastream.getDefaultChecksumType(); } if (checksumType.equalsIgnoreCase("DISABLED")) { return "DISABLED"; } if (checksumType.equalsIgnoreCase("MD5")) { csType = "MD5"; } if (checksumType.equalsIgnoreCase("SHA-1")) { csType = "SHA-1"; } if (checksumType.equalsIgnoreCase("SHA-256")) { csType = "SHA-256"; } if (checksumType.equalsIgnoreCase("SHA-384")) { csType = "SHA-384"; } if (checksumType.equalsIgnoreCase("SHA-512")) { csType = "SHA-512"; } if (checksumType.equalsIgnoreCase("HAVAL")) { csType = "HAVAL"; } if (checksumType.equalsIgnoreCase("TIGER")) { csType = "TIGER"; } if (checksumType.equalsIgnoreCase("WHIRLPOOL")) { csType = "WHIRLPOOL"; } if (csType == null) { throw new GeneralException("Unknown checksum algorithm specified: " + checksumType); } try { MessageDigest.getInstance(csType); } catch (NoSuchAlgorithmException e) { throw new GeneralException("Checksum algorithm not yet implemented: " + csType); } return csType; } // Get a complete copy of this datastream public Datastream copy() { Datastream ds = new Datastream(); copy(ds); return ds; } // Copy this instance into target public void copy(Datastream target) { target.isNew = isNew; target.DatastreamID = DatastreamID; if (DatastreamAltIDs != null) { target.DatastreamAltIDs = new String[DatastreamAltIDs.length]; for (int i = 0; i < DatastreamAltIDs.length; i++) { target.DatastreamAltIDs[i] = DatastreamAltIDs[i]; } } target.DSFormatURI = DSFormatURI; target.DSMIME = DSMIME; target.DSControlGrp = DSControlGrp; target.DSInfoType = DSInfoType; target.DSState = DSState; target.DSVersionable = DSVersionable; target.DSVersionID = DSVersionID; target.DSLabel = DSLabel; target.DSCreateDT = DSCreateDT; target.DSSize = DSSize; target.DSLocation = DSLocation; target.DSLocationType = DSLocationType; } }