Skip to content

Commit

Permalink
create mixin fedora:checksum (mixed into our datastream jcr:content n…
Browse files Browse the repository at this point in the history
…ode) that records a checksum and content size for the jcr:data
  • Loading branch information
cbeer committed Mar 6, 2013
1 parent 8f0a94a commit b4afade
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 3 deletions.
6 changes: 6 additions & 0 deletions fcrepo-kernel/pom.xml
Expand Up @@ -73,6 +73,12 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
45 changes: 42 additions & 3 deletions fcrepo-kernel/src/main/java/org/fcrepo/Datastream.java
Expand Up @@ -10,6 +10,11 @@
import static org.modeshape.jcr.api.JcrConstants.NT_RESOURCE;

import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

import javax.jcr.Node;
Expand All @@ -20,6 +25,8 @@
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;

import org.apache.commons.codec.binary.Hex;
import org.fcrepo.utils.ContentDigest;
import org.modeshape.jcr.api.JcrTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -32,6 +39,11 @@
*/
public class Datastream extends JcrTools {

private static MessageDigest md;
private final static String CONTENT_SIZE = "fedora:size";
private final static String DIGEST_VALUE = "fedora:digest";
private final static String DIGEST_ALGORITHM = "fedora:digestAlgorithm";

private final static Logger logger = LoggerFactory
.getLogger(Datastream.class);

Expand All @@ -48,6 +60,18 @@ public Node getNode() {
return node;
}

private MessageDigest getMessageDigest() {

if(this.md == null) {
try {
this.md = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}

return md;
}
/**
* @return The InputStream of content associated with this datastream.
* @throws RepositoryException
Expand All @@ -66,6 +90,13 @@ public InputStream getContent() throws RepositoryException {
public void setContent(InputStream content) throws RepositoryException {
final Node contentNode =
findOrCreateChild(node, JCR_CONTENT, NT_RESOURCE);

if(contentNode.canAddMixin("fedora:checksum")) {
contentNode.addMixin("fedora:checksum");
}

final DigestInputStream dis = new DigestInputStream(content, getMessageDigest());

logger.debug("Created content node at path: " + contentNode.getPath());
/*
* This next line of code deserves explanation. If we chose for the
Expand All @@ -84,7 +115,11 @@ public void setContent(InputStream content) throws RepositoryException {
*/
Property dataProperty =
contentNode.setProperty(JCR_DATA, node.getSession()
.getValueFactory().createBinary(content));
.getValueFactory().createBinary(dis));

contentNode.setProperty(CONTENT_SIZE, dataProperty.getLength());
contentNode.setProperty(DIGEST_VALUE, Hex.encodeHexString(dis.getMessageDigest().digest()));
contentNode.setProperty(DIGEST_ALGORITHM, dis.getMessageDigest().getAlgorithm());
logger.debug("Created data property at path: " + dataProperty.getPath());

}
Expand All @@ -100,8 +135,12 @@ public void setContent(InputStream content, String mimeType)
* @throws RepositoryException
*/
public long getContentSize() throws RepositoryException {
return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary()
.getSize();
return node.getNode(JCR_CONTENT).getProperty(CONTENT_SIZE).getLong();
}

public URI getContentDigest() throws RepositoryException {
final Node contentNode = node.getNode(JCR_CONTENT);
return ContentDigest.asURI(contentNode.getProperty(DIGEST_ALGORITHM).getString(), contentNode.getProperty(DIGEST_VALUE).getString());
}

/**
Expand Down
27 changes: 27 additions & 0 deletions fcrepo-kernel/src/main/java/org/fcrepo/utils/ContentDigest.java
@@ -0,0 +1,27 @@
package org.fcrepo.utils;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

public class ContentDigest {
public final static Map<String, String> algorithmToScheme;

static
{
algorithmToScheme = new HashMap<String, String>();
algorithmToScheme.put("SHA-1", "urn:sha1");
}

public static URI asURI(String algorithm, String value) {
try {
String scheme = algorithmToScheme.get(algorithm);

return new URI(scheme, value, null);
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
}
}
}
8 changes: 8 additions & 0 deletions fcrepo-kernel/src/main/resources/fedora-node-types.cnd
Expand Up @@ -84,3 +84,11 @@
*/
- foaf:name (STRING) multiple COPY


/*
* Some content that can have a checksum
*/
[fedora:checksum] mixin
- fedora:size (LONG) COPY
- fedora:digest (STRING) COPY
- fedora:digestAlgorithm (STRING) COPY
67 changes: 67 additions & 0 deletions fcrepo-kernel/src/test/java/org/fcrepo/DatastreamTest.java
Expand Up @@ -9,13 +9,15 @@

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;

import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;

Expand Down Expand Up @@ -57,4 +59,69 @@ public void testCreatedDate() throws RepositoryException, IOException {
final Datastream ds = getDatastream("testObject", "testDatastreamNode1");
assertNotNull("Couldn't find created date on datastream!", ds.getCreatedDate());
}

@Test
public void testDatastreamContent() throws IOException, RepositoryException {
Session session = repo.login();
createObjectNode(session, "testObject");
createDatastreamNode(session,
"/objects/testObject/testDatastreamNode1",
"application/octet-stream", new ByteArrayInputStream(
"asdf".getBytes()));

session.save();

final Datastream ds = getDatastream("testObject", "testDatastreamNode1");
String contentString = IOUtils.toString(ds.getContent(), "ASCII");

assertEquals("asdf", contentString);

}

@Test
public void testDatastreamContentDigestAndLength() throws IOException, RepositoryException {
Session session = repo.login();
createObjectNode(session, "testObject");
createDatastreamNode(session,
"/objects/testObject/testDatastreamNode2",
"application/octet-stream", new ByteArrayInputStream(
"asdf".getBytes()));


session.save();

final Datastream ds = getDatastream("testObject", "testDatastreamNode2");
assertEquals("urn:sha1:3da541559918a808c2402bba5012f6c60b27661c", ds.getContentDigest().toString());
assertEquals(4L, ds.getContentSize());


String contentString = IOUtils.toString(ds.getContent(), "ASCII");

assertEquals("asdf", contentString);
}

@Test
public void testModifyDatastreamContentDigestAndLength() throws IOException, RepositoryException {
Session session = repo.login();
createObjectNode(session, "testObject");
createDatastreamNode(session,
"/objects/testObject/testDatastreamNode3",
"application/octet-stream", new ByteArrayInputStream(
"asdf".getBytes()));


session.save();

final Datastream ds = getDatastream("testObject", "testDatastreamNode3");
ds.setContent(new ByteArrayInputStream(
"0123456789".getBytes()));

assertEquals("urn:sha1:87acec17cd9dcd20a716cc2cf67417b71c8a7016", ds.getContentDigest().toString());
assertEquals(10L, ds.getContentSize());


String contentString = IOUtils.toString(ds.getContent(), "ASCII");

assertEquals("0123456789", contentString);
}
}

0 comments on commit b4afade

Please sign in to comment.