Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add external content fetcher
  • Loading branch information
cbeer committed May 27, 2014
1 parent 9afee37 commit 21a24ac
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 22 deletions.
Expand Up @@ -18,6 +18,7 @@
import com.codahale.metrics.annotation.Timed;
import com.sun.jersey.core.header.ContentDisposition;
import org.fcrepo.http.commons.api.rdf.HttpIdentifierTranslator;
import org.fcrepo.http.commons.domain.ContentInputStream;
import org.fcrepo.http.commons.session.InjectedSession;
import org.fcrepo.kernel.Datastream;
import org.fcrepo.kernel.exception.InvalidChecksumException;
Expand Down Expand Up @@ -86,7 +87,7 @@ public Response create(@PathParam("path")
@HeaderParam("Content-Disposition") final String contentDisposition,
@QueryParam("checksum") final String checksum,
@HeaderParam("Content-Type") final MediaType requestContentType,
final InputStream requestBodyStream, @Context final HttpServletResponse servletResponse)
final ContentInputStream requestBodyStream, @Context final HttpServletResponse servletResponse)
throws InvalidChecksumException, RepositoryException, URISyntaxException, ParseException {
final MediaType contentType = getSimpleContentType(requestContentType);

Expand Down Expand Up @@ -143,7 +144,7 @@ public Response create(@PathParam("path")

final Datastream datastream =
datastreamService.createDatastream(session, newDatastreamPath,
contentType.toString(), originalFileName, requestBodyStream,
contentType.toString(), originalFileName, requestBodyStream.getEntityStream(),
checksumURI);

final HttpIdentifierTranslator subjects =
Expand Down Expand Up @@ -181,7 +182,7 @@ public Response modifyContent(@PathParam("path") final List<PathSegment> pathLis
@QueryParam("checksum") final String checksum,
@HeaderParam("Content-Disposition") final String contentDisposition,
@HeaderParam("Content-Type") final MediaType requestContentType,
final InputStream requestBodyStream,
final ContentInputStream requestBodyStream,
@Context final Request request, @Context final HttpServletResponse servletResponse)
throws RepositoryException, InvalidChecksumException, URISyntaxException, ParseException {

Expand Down Expand Up @@ -218,7 +219,7 @@ public Response modifyContent(@PathParam("path") final List<PathSegment> pathLis

final Datastream datastream =
datastreamService.createDatastream(session, path,
contentType.toString(), originalFileName, requestBodyStream, checksumURI);
contentType.toString(), originalFileName, requestBodyStream.getEntityStream(), checksumURI);

final boolean isNew = datastream.isNew();
session.save();
Expand Down
Expand Up @@ -39,6 +39,7 @@

import org.fcrepo.http.commons.AbstractResource;
import org.fcrepo.http.commons.api.rdf.HttpIdentifierTranslator;
import org.fcrepo.http.commons.domain.ContentInputStream;
import org.fcrepo.http.commons.session.InjectedSession;
import org.fcrepo.kernel.exception.InvalidChecksumException;
import org.fcrepo.serialization.SerializerUtil;
Expand Down Expand Up @@ -72,7 +73,7 @@ public class FedoraImport extends AbstractResource {
*
* @param pathList
* @param format
* @param stream
* @param requestBodyStream
* @return 201 with Location header to the path of the imported resource
* @throws IOException
* @throws RepositoryException
Expand All @@ -82,7 +83,7 @@ public class FedoraImport extends AbstractResource {
@POST
public Response importObject(@PathParam("path") final List<PathSegment> pathList,
@QueryParam("format") @DefaultValue("jcr/xml") final String format,
final InputStream stream)
final ContentInputStream requestBodyStream)
throws IOException, RepositoryException, InvalidChecksumException,
URISyntaxException {

Expand All @@ -94,7 +95,7 @@ public Response importObject(@PathParam("path") final List<PathSegment> pathList

try {
serializers.getSerializer(format)
.deserialize(session, path, stream);
.deserialize(session, path, requestBodyStream.getEntityStream());
session.save();
return created(new URI(subjects.getSubject(path).getURI())).build();
} catch ( ItemExistsException ex ) {
Expand Down
22 changes: 12 additions & 10 deletions fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraNodes.java
Expand Up @@ -99,6 +99,7 @@
import org.apache.jena.riot.Lang;
import org.fcrepo.http.commons.AbstractResource;
import org.fcrepo.http.commons.api.rdf.HttpIdentifierTranslator;
import org.fcrepo.http.commons.domain.ContentInputStream;
import org.fcrepo.http.commons.domain.MOVE;
import org.fcrepo.http.commons.domain.PATCH;
import org.fcrepo.http.commons.domain.COPY;
Expand Down Expand Up @@ -346,7 +347,7 @@ public Response updateSparql(@PathParam("path")
final List<PathSegment> pathList,
@Context
final UriInfo uriInfo,
final InputStream requestBodyStream,
final ContentInputStream requestBodyStream,
@Context final Request request, @Context final HttpServletResponse servletResponse)
throws RepositoryException, IOException {

Expand All @@ -364,7 +365,7 @@ public Response updateSparql(@PathParam("path")

final Dataset properties = resource.updatePropertiesDataset(new HttpIdentifierTranslator(
session, FedoraNodes.class, uriInfo), IOUtils
.toString(requestBodyStream));
.toString(requestBodyStream.getEntityStream()));


final Model problems = properties.getNamedModel(PROBLEMS_MODEL_NAME);
Expand Down Expand Up @@ -409,7 +410,7 @@ public Response createOrReplaceObjectRdf(
@Context final UriInfo uriInfo,
@HeaderParam("Content-Type")
final MediaType requestContentType,
final InputStream requestBodyStream,
final ContentInputStream requestBodyStream,
@Context final Request request,
@Context final HttpServletResponse servletResponse) throws RepositoryException, ParseException,
IOException, InvalidChecksumException, URISyntaxException {
Expand Down Expand Up @@ -450,7 +451,7 @@ public Response createOrReplaceObjectRdf(
final String format = contentTypeToLang(contentType.toString()).getName().toUpperCase();

final Model inputModel = createDefaultModel()
.read(requestBodyStream,
.read(requestBodyStream.getEntityStream(),
graphSubjects.getSubject(resource.getNode().getPath()).toString(),
format);

Expand Down Expand Up @@ -492,7 +493,8 @@ public Response createObject(@PathParam("path")
final String slug,
@Context final HttpServletResponse servletResponse,
@Context
final UriInfo uriInfo, final InputStream requestBodyStream)
final UriInfo uriInfo,
final ContentInputStream requestBodyStream)
throws RepositoryException, ParseException, IOException,
InvalidChecksumException, URISyntaxException {

Expand Down Expand Up @@ -553,7 +555,7 @@ public Response createObject(@PathParam("path")

if (contentTypeString.equals(contentTypeSPARQLUpdate)) {
LOGGER.trace("Found SPARQL-Update content, applying..");
result.updatePropertiesDataset(idTranslator, IOUtils.toString(requestBodyStream));
result.updatePropertiesDataset(idTranslator, IOUtils.toString(requestBodyStream.getEntityStream()));
if (result.isNew()) {
response = created(location).entity(location.toString());
} else {
Expand All @@ -567,7 +569,7 @@ public Response createObject(@PathParam("path")
final String format = lang.getName().toUpperCase();

final Model inputModel =
createDefaultModel().read(requestBodyStream,
createDefaultModel().read(requestBodyStream.getEntityStream(),
idTranslator.getSubject(result.getNode().getPath()).toString(), format);

result.replaceProperties(idTranslator, inputModel);
Expand All @@ -594,13 +596,13 @@ public Response createObject(@PathParam("path")
}

datastreamService.createDatastream(session,
newObjectPath, contentTypeString, originalFileName, requestBodyStream, checksumURI);
newObjectPath, contentTypeString, originalFileName, requestBodyStream.getEntityStream(), checksumURI);
final URI contentLocation =
new URI(idTranslator.getSubject(((Datastream) result).getContentNode().getPath()).getURI());
response = created(contentLocation).entity(contentLocation.toString());

} else {
if (requestBodyStream.read() != -1) {
if (requestBodyStream.getEntityStream().read() != -1) {
LOGGER.trace("Unknown content type: {}", contentType);
response = notAcceptable().entity("Invalid Content type " + contentType);
throw new WebApplicationException(response.build());
Expand Down Expand Up @@ -696,7 +698,7 @@ public Response createObjectFromFormPost(
) throws RepositoryException, URISyntaxException, InvalidChecksumException, ParseException, IOException {

final MediaType effectiveContentType = file == null ? null : MediaType.APPLICATION_OCTET_STREAM_TYPE;
return createObject(pathList, mixin, null, null, effectiveContentType, slug, servletResponse, uriInfo, file);
return createObject(pathList, mixin, null, null, effectiveContentType, slug, servletResponse, uriInfo, new ContentInputStream(file));

}

Expand Down
Expand Up @@ -53,6 +53,7 @@
import javax.ws.rs.core.Response;

import org.apache.commons.io.IOUtils;
import org.fcrepo.http.commons.domain.ContentInputStream;
import org.fcrepo.kernel.Datastream;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.exception.InvalidChecksumException;
Expand Down Expand Up @@ -147,7 +148,7 @@ public void testPutContent()
when(mockDatastreams.exists(mockSession, dsPath)).thenReturn(true);
final Response actual =
testObj.modifyContent(createPathList(pid, dsId), null, "inline; filename=\"xyz\"", null,
dsContentStream, null, mockResponse);
new ContentInputStream(dsContentStream), null, mockResponse);
assertEquals(CREATED.getStatusCode(), actual.getStatus());
verify(mockDatastreams).createDatastream(any(Session.class),
eq(dsPath), anyString(), eq("xyz"), any(InputStream.class), eq((URI)null));
Expand All @@ -174,7 +175,7 @@ public void testCreateContent() throws RepositoryException, IOException,
when(mockDatastreams.exists(mockSession, dsPath)).thenReturn(true);
final Response actual =
testObj.create(createPathList(pid, dsId), null, null, null, TEXT_PLAIN_TYPE,
dsContentStream, mockResponse);
new ContentInputStream(dsContentStream), mockResponse);
assertEquals(CREATED.getStatusCode(), actual.getStatus());
verify(mockDatastreams).createDatastream(mockSession, dsPath,
"text/plain", null, dsContentStream, null);
Expand Down Expand Up @@ -208,7 +209,7 @@ public void testCreateContentAtMintedPath() throws RepositoryException, InvalidC

final Response actual =
testObj.create(createPathList(pid), null, null, null, TEXT_PLAIN_TYPE,
dsContentStream, mockResponse);
new ContentInputStream(dsContentStream), mockResponse);
assertEquals(CREATED.getStatusCode(), actual.getStatus());
verify(mockDatastreams).createDatastream(mockSession,
"/" + pid + "/xyz", "text/plain", null, dsContentStream, null);
Expand Down Expand Up @@ -241,7 +242,7 @@ public void testCreateContentWithSlug() throws RepositoryException, InvalidCheck
when(mockResource.hasContent()).thenReturn(false);
final Response actual =
testObj.create(createPathList(pid), dsid, null, null, TEXT_PLAIN_TYPE,
dsContentStream, mockResponse);
new ContentInputStream(dsContentStream), mockResponse);
assertEquals(CREATED.getStatusCode(), actual.getStatus());
verify(mockDatastreams).createDatastream(mockSession,
"/" + pid + "/slug", "text/plain", null, dsContentStream, null);
Expand Down Expand Up @@ -277,7 +278,7 @@ public void testModifyContent()
when(mockDatastreams.exists(mockSession, dsPath)).thenReturn(true);
final Response actual =
testObj.modifyContent(createPathList(pid, dsId), "urn:sha1:some-checksum", null, null,
dsContentStream, mockRequest, mockResponse);
new ContentInputStream(dsContentStream), mockRequest, mockResponse);
assertEquals(NO_CONTENT.getStatusCode(), actual.getStatus());
verify(mockDatastreams).createDatastream(any(Session.class),
eq(dsPath),
Expand Down
Expand Up @@ -16,6 +16,7 @@
package org.fcrepo.integration.http.api;

import static java.util.TimeZone.getTimeZone;
import static javax.ws.rs.core.Response.Status.CREATED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
Expand All @@ -25,6 +26,7 @@
import java.util.Date;
import java.util.Locale;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
Expand Down Expand Up @@ -69,6 +71,35 @@ public void testAddDatastream() throws Exception {
assertNotEquals("Last-Modified should not be blank for new nodes", lastmod.trim(), "");
}

@Test
public void testAddDatastreamByContentLocation() throws Exception {
final String pid = getRandomUniquePid();
createObject(pid);

final HttpPost httpPost = postDSMethod(pid, "zxc", "");
httpPost.addHeader("Content-Location", serverAddress);
final HttpResponse response =
client.execute(httpPost);
assertEquals(CREATED.getStatusCode(), response.getStatusLine().getStatusCode());

assertTrue("Didn't find Last-Modified header!", response.containsHeader("Last-Modified"));
assertTrue("Didn't find ETag header!", response.containsHeader("ETag"));

final String location = response.getFirstHeader("Location").getValue();
assertEquals(
"Got wrong URI in Location header for datastream creation!",
serverAddress + pid + "/zxc/fcr:content", location);

final HttpGet httpGet = new HttpGet(location);

final HttpResponse contentResponse = client.execute(httpGet);

final String body = IOUtils.toString(contentResponse.getEntity().getContent());

assertTrue(body.contains(pid));


}
@Test
public void testAddDeepDatastream() throws Exception {
final String uuid = getRandomUniquePid();
Expand Down
2 changes: 2 additions & 0 deletions fcrepo-http-api/src/test/resources/spring-test/repo.xml
Expand Up @@ -17,4 +17,6 @@

<bean class="org.modeshape.jcr.JcrRepositoryFactory"/>

<bean id="connectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">
</bean>
</beans>
@@ -0,0 +1,15 @@
package org.fcrepo.http.commons.domain;

import java.io.InputStream;

public class ContentInputStream {
private final InputStream entityStream;

public ContentInputStream(final InputStream entityStream) {
this.entityStream = entityStream;
}

public InputStream getEntityStream() {
return entityStream;
}
}
@@ -0,0 +1,58 @@
package org.fcrepo.http.commons.domain;

import org.fcrepo.kernel.services.ExternalContentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;

import static javax.ws.rs.core.Response.Status.BAD_REQUEST;

/**
* @author cabeer
*/
@Provider
@Component
public class ExternalMessageBodyReader implements MessageBodyReader<ContentInputStream> {

/**
* The fcrepo node service
*/
@Autowired
private ExternalContentService contentService;

@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == ContentInputStream.class;
}

@Override
public ContentInputStream readFrom(Class<ContentInputStream> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
final InputStream realEntityStream;

if (httpHeaders.containsKey("Content-Location")) {
final String location = httpHeaders.getFirst("Content-Location");
final URI sourceUri;
try {
sourceUri = new URI(location);
} catch (URISyntaxException e) {
throw new WebApplicationException(e, BAD_REQUEST);
}
realEntityStream = contentService.retrieveExternalContent(sourceUri);
} else {
realEntityStream = entityStream;
}

return new ContentInputStream(realEntityStream);
}
}
@@ -0,0 +1,18 @@
package org.fcrepo.kernel.services;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

/**
* @author cabeer
*/
public interface ExternalContentService {
/**
* Fetch a
* @param sourceUri
* @return
* @throws IOException
*/
InputStream retrieveExternalContent(URI sourceUri) throws IOException;
}

0 comments on commit 21a24ac

Please sign in to comment.