/* 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.integration;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Random;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import org.junit.Test;
import junit.framework.JUnit4TestAdapter;
import fedora.client.FedoraClient;
import fedora.server.access.FedoraAPIA;
import fedora.server.management.FedoraAPIM;
import fedora.server.types.gen.MIMETypedStream;
import fedora.test.FedoraTestCase;
/**
* Performs ingest and export tests using large datastreams.
* This test creates and moves very large files and as such
* takes significant time to run.
*
* Non-SSL transports *MUST* be available for all APIs in order
* for this test to run properly.
*
* @author Bill Branan
*/
public class TestLargeDatastreams
extends FedoraTestCase {
private FedoraClient fedoraClient;
private FedoraAPIM apim;
private FedoraAPIA apia;
private static final String pid = "demo:LargeDatastreams";
private static byte[] DEMO_FOXML;
private static long gigabyte = 1073741824;
private long fileSize = gigabyte * 5;
static {
// Test FOXML object
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append("<foxml:digitalObject VERSION=\"1.1\" PID=\"" + pid + "\" ");
sb.append(" xmlns:foxml=\"info:fedora/fedora-system:def/foxml#\" ");
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
sb.append(" xsi:schemaLocation=\"info:fedora/fedora-system:def/foxml# ");
sb.append(" http://www.fedora.info/definitions/1/0/foxml1-1.xsd\">");
sb.append(" <foxml:objectProperties>");
sb.append(" <foxml:property NAME=\"info:fedora/fedora-system:def/model#state\" VALUE=\"A\"/>");
sb.append(" </foxml:objectProperties>");
sb.append("</foxml:digitalObject>");
try {
DEMO_FOXML = sb.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
}
}
@Override
public void setUp() throws Exception {
fedoraClient = getFedoraClient();
apim = fedoraClient.getAPIM();
apia = fedoraClient.getAPIA();
}
@Test
public void testLargeDatastreamIO() throws Exception {
System.out.println("Running testLargeDatastreams...");
// Create the object, add the datastream
System.out.println(" Uploading a file of size " + fileSize + " bytes...");
String uploadId = upload();
System.out.println(" Creating data object...");
apim.ingest(DEMO_FOXML, FOXML1_1.uri, "new foxml object");
System.out.println(" Adding uploaded file as a datastream...");
apim.addDatastream(pid,
"DS1",
null,
"Large Datastream",
false,
"text/plain",
null,
uploadId,
"M",
"A",
null,
null,
"Adding Large Datastream");
// Export the datastream via API-A-Lite
System.out.println(" Exporting datastream via API-A-Lite...");
long exportFileAPIALiteSize = exportAPIALite("DS1");
// Check the file size to make sure the entire file was retrieved
assertEquals(fileSize, exportFileAPIALiteSize);
// Export the datastream via API-A
System.out.println(" Exporting datastream via API-A...");
long exportFileAPIASize = exportAPIA("DS1");
// If file was small enough to transfer via API-A
if(exportFileAPIASize >= 0) {
assertEquals(fileSize, exportFileAPIASize);
}
// Clean up
apim.purgeObject(pid, "Removing Test Object", false);
System.out.println(" Test Complete.");
}
private String upload() throws Exception {
String url = fedoraClient.getUploadURL();
EntityEnclosingMethod httpMethod = new PostMethod(url);
httpMethod.setDoAuthentication(true);
httpMethod.getParams().setParameter("Connection", "Keep-Alive");
httpMethod.setContentChunked(true);
Part[] parts = {new FilePart("file", new SizedPartSource())};
httpMethod.setRequestEntity(
new MultipartRequestEntity(parts, httpMethod.getParams()));
HttpClient client = fedoraClient.getHttpClient();
try {
int status = client.executeMethod(httpMethod);
String response = new String(httpMethod.getResponseBody());
if (status != HttpStatus.SC_CREATED) {
throw new IOException("Upload failed: "
+ HttpStatus.getStatusText(status) + ": "
+ replaceNewlines(response, " "));
} else {
response = response.replaceAll("\r", "").replaceAll("\n", "");
return response;
}
} finally {
httpMethod.releaseConnection();
}
}
private long exportAPIALite(String dsId) throws Exception {
String url = apia.describeRepository().getRepositoryBaseURL() +
"/get/" + pid + "/" + dsId;
HttpMethod httpMethod = new GetMethod(url);
httpMethod.setDoAuthentication(true);
httpMethod.getParams().setParameter("Connection", "Keep-Alive");
HttpClient client = fedoraClient.getHttpClient();
client.executeMethod(httpMethod);
BufferedInputStream dataStream =
new BufferedInputStream(httpMethod.getResponseBodyAsStream());
long bytesRead = 0;
while (dataStream.read() >= 0) {
++bytesRead;
}
return bytesRead;
}
private long exportAPIA(String dsId) throws Exception {
MIMETypedStream fileStream = null;
try {
fileStream = apia.getDatastreamDissemination(pid, dsId, null);
} catch(Exception e) {
if(e.getMessage().indexOf("The datastream you are attempting " +
"to retrieve is too large") > 0) {
System.out.println(" Expected error generated in API-A export:");
System.out.println(" Error text: " + e.getMessage());
return -1;
} else {
fail(" Unexpected exception encountered in exportAPIA: " +
e.getMessage());
}
}
return fileStream.getStream().length;
}
/**
* Replace newlines with the given string.
*/
private static String replaceNewlines(String in, String replaceWith) {
return in.replaceAll("\r", replaceWith).replaceAll("\n", replaceWith);
}
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(TestLargeDatastreams.class);
}
private class SizedPartSource implements PartSource {
public InputStream createInputStream() throws IOException {
return new SizedInputStream();
}
public String getFileName() {
return "file";
}
public long getLength() {
return fileSize;
}
}
private class SizedInputStream extends InputStream {
private long bytesRead = 0;
Random generator = new Random(System.currentTimeMillis());
@Override
public int available() throws IOException {
return (int)(fileSize - bytesRead);
}
@Override
public int read() throws IOException {
if(bytesRead < fileSize) {
++bytesRead;
return generator.nextInt(100);
} else {
return -1;
}
}
}
}