/* 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.test.api;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.rmi.RemoteException;
import java.security.MessageDigest;
import java.util.Date;
import org.apache.abdera.Abdera;
import org.apache.abdera.ext.thread.ThreadHelper;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import junit.framework.JUnit4TestAdapter;
import fedora.common.PID;
import fedora.server.management.FedoraAPIM;
import fedora.server.utilities.StringUtility;
import fedora.test.FedoraServerTestCase;
import fedora.utilities.Foxml11Document;
import fedora.utilities.Foxml11Document.ControlGroup;
import fedora.utilities.Foxml11Document.Property;
import fedora.utilities.Foxml11Document.State;
/**
* @author Edwin Shin
* @since 3.0
* @version $Id$
*/
public class TestManagedDatastreams
extends FedoraServerTestCase {
private FedoraAPIM apim;
private Abdera abdera;
private final String[] copyTempFileLocations = {
"copy:///tmp/foo.txt",
"copy://tmp/foo.txt",
"copy://../etc/passwd",
"temp:///tmp/foo.txt",
"temp://tmp/foo.txt",
"temp://../etc/passwd"
};
private final String[] uploadedLocations = {
"uploaded:///tmp/foo.txt",
"uploaded://tmp/foo.txt"};
/**
* @throws java.lang.Exception
*/
@Override
@Before
public void setUp() throws Exception {
abdera = Abdera.getInstance();
apim = getFedoraClient().getAPIM();
System.setProperty("fedoraServerHost", "localhost");
System.setProperty("fedoraServerPort", "8080");
}
/**
* @throws java.lang.Exception
*/
@Override
@After
public void tearDown() throws Exception {
}
@Test
public void testIngest() throws Exception {
String pid = "demo:m_ds_test";
for (String contentLocation : copyTempFileLocations) {
try {
apim.ingest(getAtomObject(pid, contentLocation), ATOM1_1.uri, null);
fail("ingest should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("ValidationException"));
}
try {
apim.ingest(getFoxmlObject(pid, contentLocation), FOXML1_1.uri, null);
fail("ingest should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("ObjectIntegrityException"));
}
}
for (String contentLocation : uploadedLocations) {
try {
apim.ingest(getAtomObject(pid, contentLocation), ATOM1_1.uri, null);
fail("ingest should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("StreamReadException"));
}
try {
apim.ingest(getFoxmlObject(pid, contentLocation), FOXML1_1.uri, null);
fail("ingest should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("StreamReadException"));
}
}
}
@Test
public void testAddDatastream() throws Exception {
String pid = "demo:m_ds_test_add";
apim.ingest(getAtomObject(pid, null), ATOM1_1.uri, null);
try {
for (String contentLocation : copyTempFileLocations) {
try {
addDatastream(pid, contentLocation);
fail("addDatastream should have failed with "
+ contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("ValidationException"));
}
}
for (String contentLocation : uploadedLocations) {
try {
addDatastream(pid, contentLocation);
fail("addDatastream should have failed with "
+ contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("StreamReadException"));
}
}
} finally {
apim.purgeObject(pid, "test", false);
}
}
@Test
public void testModifyDatastreamByReference() throws Exception {
String pid = "demo:m_ds_test_add";
String dsLocation = getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC";
apim.ingest(getAtomObject(pid, dsLocation), ATOM1_1.uri, null);
try {
for (String contentLocation : copyTempFileLocations) {
try {
modifyDatastreamByReference(pid, contentLocation);
fail("modifyDatastreamByReference should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("ValidationException"));
}
}
for (String contentLocation : uploadedLocations) {
try {
modifyDatastreamByReference(pid, contentLocation);
fail("modifyDatastreamByReference should have failed with " + contentLocation);
} catch (RemoteException e) {
assertTrue(e.getMessage().contains("StreamReadException"));
}
}
// A null contentLocation should cause the server to generate a
// copy:// url
modifyDatastreamByReference(pid, null);
} finally {
apim.purgeObject(pid, "test", false);
}
}
@Test
public void testAddDatastreamWithChecksum() throws Exception {
String pid = "demo:m_ds_test_add";
String checksumType = "MD5";
apim.ingest(getAtomObject(pid, null), ATOM1_1.uri, null);
File temp = null;
try {
temp = File.createTempFile("foo", "bar");
String contentLocation = getFedoraClient().uploadFile(temp);
String checksum = computeChecksum(checksumType, new FileInputStream(temp));
String dsId = addDatastream(pid, contentLocation, checksumType, checksum);
assertEquals("DS", dsId);
// Now ensure that bogus checksums do indeed fail
apim.purgeDatastream(pid, dsId, null, null, null, false);
checksum = "bogus";
try {
addDatastream(pid, contentLocation, checksumType, checksum);
fail("Adding datastream with bogus checksum should have failed.");
} catch(RemoteException e) {
assertTrue(e.getMessage().contains("Checksum Mismatch"));
}
} finally {
apim.purgeObject(pid, "test", false);
if (temp != null) {
temp.delete();
}
}
}
@Test
public void testModifyDatastreamByReferenceWithChecksum() throws Exception {
String pid = "demo:m_ds_test_add";
String checksumType = "MD5";
apim.ingest(getAtomObject(pid, null), ATOM1_1.uri, null);
File temp = null;
try {
temp = File.createTempFile("foo", "bar");
String contentLocation = getFedoraClient().uploadFile(temp);
String checksum = computeChecksum(checksumType, new FileInputStream(temp));
String dsId = addDatastream(pid, contentLocation, checksumType, checksum);
assertEquals("DS", dsId);
FileOutputStream os = new FileOutputStream(temp);
os.write("testModifyDatastreamByReferenceWithChecksum".getBytes());
os.close();
contentLocation = getFedoraClient().uploadFile(temp);
checksum = computeChecksum(checksumType, new FileInputStream(temp));
modifyDatastreamByReference(pid, contentLocation, checksumType, checksum);
// Now ensure that bogus checksums do indeed fail
checksum = "bogus";
try {
modifyDatastreamByReference(pid, contentLocation, checksumType, checksum);
fail("Modifying datastream with bogus checksum should have failed.");
} catch(RemoteException e) {
assertTrue(e.getMessage().contains("Checksum Mismatch"));
}
} finally {
apim.purgeObject(pid, "test", false);
if (temp != null) {
temp.delete();
}
}
}
private byte[] getAtomObject(String pid, String contentLocation) throws Exception {
Feed feed = createAtomObject(pid, contentLocation);
Writer sWriter = new StringWriter();
feed.writeTo("prettyxml", sWriter);
return sWriter.toString().getBytes("UTF-8");
}
private byte[] getFoxmlObject(String pid, String contentLocation) throws Exception {
Foxml11Document doc = createFoxmlObject(pid, contentLocation);
ByteArrayOutputStream out = new ByteArrayOutputStream();
doc.serialize(out);
return out.toByteArray();
}
private String addDatastream(String pid, String contentLocation)
throws Exception {
return addDatastream(pid, contentLocation, null, null);
}
private String addDatastream(String pid, String contentLocation, String checksumType, String checksum)
throws Exception {
return apim.addDatastream(pid,
"DS",
null,
"testManagedDatastreams",
true,
"text/plain",
"",
contentLocation,
"M",
"A",
checksumType,
checksum,
"testManagedDatastreams");
}
private String modifyDatastreamByReference(String pid,
String contentLocation)
throws Exception {
return modifyDatastreamByReference(pid, contentLocation, null, null);
}
private String modifyDatastreamByReference(String pid,
String contentLocation,
String checksumType,
String checksum)
throws Exception {
return apim.modifyDatastreamByReference(pid,
"DS",
new String[] {},
"testManagedDatastreams",
"text/plain",
"",
contentLocation,
checksumType,
checksum,
"testManagedDatastreams",
false);
}
private Feed createAtomObject(String spid, String contentLocation) throws Exception {
PID pid = PID.getInstance(spid);
Date date = new Date(1);
String title = "title";
String author = "fedora.test.api.TestManagedDatastreams";
Feed feed = abdera.newFeed();
feed.setId(pid.toURI());
feed.setTitle(title);
feed.setUpdated(date);
feed.addAuthor(author);
if (contentLocation != null && contentLocation.length() > 0) {
addAtomManagedDatastream(feed, contentLocation);
}
return feed;
}
private void addAtomManagedDatastream(Feed feed, String contentLocation) throws Exception {
String dsId = "DS";
Entry dsEntry = feed.addEntry();
dsEntry.setId(feed.getId().toString() + "/" + dsId);
Entry dsvEntry = feed.addEntry();
dsvEntry.setId(dsEntry.getId().toString() + "/" + feed.getUpdatedString());
dsEntry.setTitle(feed.getTitle());
dsEntry.setUpdated(feed.getUpdated());
dsEntry.addLink(dsvEntry.getId().toString(), Link.REL_ALTERNATE);
dsEntry.addCategory(MODEL.STATE.uri, "A", null);
dsEntry.addCategory(MODEL.CONTROL_GROUP.uri, "M", null);
dsEntry.addCategory(MODEL.VERSIONABLE.uri, "true", null);
dsvEntry.setTitle(feed.getTitle());
dsvEntry.setUpdated(feed.getUpdated());
ThreadHelper.addInReplyTo(dsvEntry, dsEntry.getId());
dsvEntry.setSummary("summary");
dsvEntry.setContent(new IRI(contentLocation), "text/plain");
}
private Foxml11Document createFoxmlObject(String spid, String contentLocation) throws Exception {
PID pid = PID.getInstance(spid);
Date date = new Date(1);
Foxml11Document doc = new Foxml11Document(pid.toString());
doc.addObjectProperty(Property.STATE, "A");
if (contentLocation != null && contentLocation.length() > 0) {
String ds = "DS";
String dsv = "DS1.0";
doc.addDatastream(ds, State.A, ControlGroup.M, true);
doc.addDatastreamVersion(ds, dsv, "text/plain", "label", 1, date);
doc.setContentLocation(dsv, contentLocation, "URL");
}
return doc;
}
private String computeChecksum(String csType, InputStream is) throws Exception {
MessageDigest md = MessageDigest.getInstance(csType);
if (is == null) {
throw new IllegalArgumentException("InputStream cannot be null");
}
byte buffer[] = new byte[5000];
int numread;
while ((numread = is.read(buffer, 0, 5000)) > 0) {
md.update(buffer, 0, numread);
}
return StringUtility.byteArraytoHexString(md.digest());
}
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(TestManagedDatastreams.class);
}
}