/* 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 org.fcrepo.test.api;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.security.MessageDigest;
import java.util.Date;
import javax.xml.ws.soap.SOAPFaultException;
import junit.framework.JUnit4TestAdapter;
import org.fcrepo.client.FedoraClient;
import org.fcrepo.common.PID;
import org.fcrepo.server.access.FedoraAPIA;
import org.fcrepo.server.management.FedoraAPIM;
import org.fcrepo.server.utilities.StringUtility;
import org.fcrepo.server.utilities.TypeUtility;
import org.fcrepo.test.FedoraServerTestCase;
import org.fcrepo.utilities.Foxml11Document;
import org.fcrepo.utilities.Foxml11Document.ControlGroup;
import org.fcrepo.utilities.Foxml11Document.Property;
import org.fcrepo.utilities.Foxml11Document.State;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Edwin Shin, Benjamin Armintor
* @version $Id$
* @since 3.6
*/
public class TestExternalDatastreams
extends FedoraServerTestCase {
private static final Logger LOGGER =
LoggerFactory.getLogger(TestExternalDatastreams.class);
private static FedoraClient s_client;
private FedoraAPIM apim;
private FedoraAPIA apia;
@BeforeClass
public static void bootStrap() throws Exception {
s_client = getFedoraClient();
}
@AfterClass
public static void cleanUp() {
s_client.shutdown();
}
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
apim = s_client.getAPIM();
apia = s_client.getAPIA();
System.setProperty("fedoraServerHost", "localhost");
System.setProperty("fedoraServerPort", "8080");
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
@Test
public void testIngest() throws Exception {
String pid = "demo:e_ds_test";
File content = null;
try {
content = getTempFile(true);
String dsLocation = content.getCanonicalFile().toURI().toString();
LOGGER.debug("Expect to succeed: {}", dsLocation);
apim.ingest(getFoxmlObject(pid, dsLocation), FOXML1_1.uri, null);
apia.getDatastreamDissemination(pid, "DS", null);
} finally {
if (content != null) content.delete();
apim.purgeObject(pid, "test", false);
}
try {
content = getTempFile(false);
String dsLocation = content.getCanonicalFile().toURI().toString();
LOGGER.debug("Expect to fail: {}", dsLocation);
apim.ingest(getFoxmlObject(pid, dsLocation), FOXML1_1.uri, null);
apia.getDatastreamDissemination(pid, "DS", null);
fail("Datastream Dissemination with unallowed dsLocation should have failed.");
} catch (SOAPFaultException e) {
LOGGER.debug("Checking failure :\"{}\" for \"Policy blocked datastream resolution\"", e.getMessage());
assertTrue(e.getMessage().contains("Policy blocked datastream resolution"));
} finally {
if (content != null) content.delete();
apim.purgeObject(pid, "test", false);
}
}
@Test
public void testAddDatastream() throws Exception {
String pid = "demo:e_ds_test_add";
File content = null;
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, null);
try {
content = getTempFile(true);
String dsLocation = content.getCanonicalFile().toURI().toString();
addDatastream(pid, dsLocation);
apia.getDatastreamDissemination(pid, "DS", null);
} finally {
if (content != null) content.delete();
apim.purgeObject(pid, "test", false);
}
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, null);
try {
content = getTempFile(false);
String dsLocation = content.getCanonicalFile().toURI().toString();
addDatastream(pid, dsLocation);
apia.getDatastreamDissemination(pid, "DS", null);
fail("Datastream Dissemination with unallowed dsLocation should have failed.");
} catch (SOAPFaultException e) {
LOGGER.debug("Checking failure :\"{}\" for \"Policy blocked datastream resolution\"", e.getMessage());
assertTrue(e.getMessage().contains("Policy blocked datastream resolution"));
} finally {
if (content != null) content.delete();
apim.purgeObject(pid, "test", false);
}
}
@Test
public void testModifyDatastreamByReference() throws Exception {
String pid = "demo:e_ds_test_add";
String dsLocation = getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC";
apim.ingest(getFoxmlObject(pid, dsLocation), FOXML1_1.uri, null);
apia.getDatastreamDissemination(pid, "DS", null);
try {
dsLocation = getBaseURL() + "/get/fedora-system:ContentModel-3.0/RELS-EXT";
modifyDatastreamByReference(pid, dsLocation);
} finally {
apim.purgeObject(pid, "test", false);
}
File content = null;
dsLocation = getBaseURL() + "/get/fedora-system:ContentModel-3.0/DC";
apim.ingest(getFoxmlObject(pid, dsLocation), FOXML1_1.uri, null);
try {
content = getTempFile(false);
dsLocation = content.getCanonicalFile().toURI().toString();
modifyDatastreamByReference(pid, dsLocation);
apia.getDatastreamDissemination(pid, "DS", null);
fail("Pointing a datastream to an unallowed dsLocation should have failed.");
} catch (SOAPFaultException e) {
LOGGER.debug("Checking failure :\"{}\" for \"Policy blocked datastream resolution\"", e.getMessage());
assertTrue(e.getMessage().contains("Policy blocked datastream resolution"));
} finally {
if (content != null) content.delete();
apim.purgeObject(pid, "test", false);
}
}
@Test
public void testAddDatastreamWithChecksum() throws Exception {
String pid = "demo:e_ds_test_add";
String checksumType = "MD5";
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, null);
File temp = null;
try {
temp = getTempFile(true);
String dsLocation = temp.getCanonicalFile().toURI().toString();
String checksum = computeChecksum(checksumType, new FileInputStream(temp));
String dsId = addDatastream(pid, dsLocation, checksumType, checksum);
assertEquals("DS", dsId);
// Now ensure that bogus checksums do indeed fail
apim.purgeDatastream(pid, dsId, null, null, null, false);
addDatastream(pid, dsLocation, checksumType, checksum);
apim.purgeDatastream(pid, dsId, null, null, null, false);
checksum = "bogus";
try {
addDatastream(pid, dsLocation, checksumType, checksum);
fail("Adding datastream with bogus checksum should have failed.");
} catch (SOAPFaultException e) {
assertTrue(e.getMessage().contains("Checksum Mismatch"));
}
} finally {
apim.purgeObject(pid, "test", false);
if (temp != null) temp.delete();
}
pid = "demo:e_ds_test_add_portable";
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, null);
try {
String dsLocation = "http://local.fedora.server/fedora/objects/fedora-system:ContentModel-3.0/datastreams/DC/content";
org.fcrepo.server.types.gen.MIMETypedStream dc_content = apia.getDatastreamDissemination("fedora-system:ContentModel-3.0", "DC", null);
String checksum = computeChecksum(checksumType, new ByteArrayInputStream(dc_content.getStream()));
String dsId = addDatastream(pid, dsLocation, checksumType, checksum);
assertEquals("DS", dsId);
} finally {
apim.purgeObject(pid, "testAddDatastreamWithChecksumType", false);
if (temp != null) temp.delete();
}
}
@Test
public void testAddDatastreamWithChecksumType() throws Exception {
String pid = "demo:e_ds_test_add";
String checksumType = "MD5";
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, "testAddDatastreamWithChecksumType");
File temp = null;
try {
temp = getTempFile(true);
String dsLocation = temp.getCanonicalFile().toURI().toString();
String dsId = addDatastream(pid, dsLocation, checksumType, null);
assertEquals("DS", dsId);
String checksum = computeChecksum(checksumType, new FileInputStream(temp));
assertEquals(apim.getDatastream(pid, "DS", null).getChecksum(),checksum);
} finally {
apim.purgeObject(pid, "testAddDatastreamWithChecksumType", false);
if (temp != null) temp.delete();
}
// Now test portable URLs
pid = "demo:e_ds_test_portable";
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, "testAddDatastreamWithChecksumType");
try {
String dsLocation = "http://local.fedora.server/fedora/objects/fedora-system:ContentModel-3.0/datastreams/DC/content";
String dsId = addDatastream(pid, dsLocation, checksumType, null);
assertEquals("DS", dsId);
} finally {
apim.purgeObject(pid, "testAddDatastreamWithChecksumType", false);
if (temp != null) temp.delete();
}
}
@Test
public void testModifyDatastreamByReferenceWithChecksum() throws Exception {
String pid = "file:e_ds_test_add"; // file pid namespace should work as well as demo
String checksumType = "MD5";
apim.ingest(getFoxmlObject(pid, null), FOXML1_1.uri, null);
File temp = null;
try {
temp = getTempFile(true);
String contentLocation = temp.getCanonicalFile().toURI().toString();
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();
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 (SOAPFaultException e) {
assertTrue(e.getMessage().contains("Checksum Mismatch"));
}
} finally {
apim.purgeObject(pid, "test", false);
if (temp != null) temp.delete();
}
}
private File getTempFile(boolean allowed) throws IOException {
File temp = allowed? File.createTempFile("foo", "isallowed") : File.createTempFile("foo", "banned");
Writer writer = new FileWriter(temp);
writer.write("Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit");
writer.flush();
writer.close();
return temp;
}
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 dsLocation, String checksumType, String checksum)
throws Exception {
return apim.addDatastream(pid,
"DS",
null,
"testExternalDatastreams",
true,
"text/plain",
"",
dsLocation,
"E",
"A",
checksumType,
checksum,
"testExternalDatastreams");
}
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",
TypeUtility.convertStringtoAOS(new String[]{}),
"testExternalDatastreams",
"text/plain",
"",
contentLocation,
checksumType,
checksum,
"testExternalDatastreams",
false);
}
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.E, true);
doc.addDatastreamVersion(ds, dsv, "text/plain", "label", 1, date);
doc.setContentLocation(dsv, contentLocation, org.fcrepo.server.storage.types.Datastream.DS_LOCATION_TYPE_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(TestExternalDatastreams.class);
}
}