New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
poke object graph through the REST API #54
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package org.fcrepo.api; | ||
|
||
import com.codahale.metrics.annotation.Timed; | ||
import com.hp.hpl.jena.update.GraphStore; | ||
import com.hp.hpl.jena.update.UpdateAction; | ||
import org.apache.commons.io.IOUtils; | ||
import org.apache.http.HttpStatus; | ||
import org.apache.jena.riot.WebContent; | ||
import org.fcrepo.AbstractResource; | ||
import org.fcrepo.Datastream; | ||
import org.fcrepo.FedoraObject; | ||
import org.fcrepo.exception.InvalidChecksumException; | ||
import org.fcrepo.utils.FedoraJcrTypes; | ||
import org.slf4j.Logger; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.jcr.Node; | ||
import javax.jcr.RepositoryException; | ||
import javax.jcr.Session; | ||
import javax.ws.rs.Consumes; | ||
import javax.ws.rs.DefaultValue; | ||
import javax.ws.rs.GET; | ||
import javax.ws.rs.HeaderParam; | ||
import javax.ws.rs.POST; | ||
import javax.ws.rs.Path; | ||
import javax.ws.rs.PathParam; | ||
import javax.ws.rs.Produces; | ||
import javax.ws.rs.QueryParam; | ||
import javax.ws.rs.WebApplicationException; | ||
import javax.ws.rs.core.Context; | ||
import javax.ws.rs.core.MediaType; | ||
import javax.ws.rs.core.PathSegment; | ||
import javax.ws.rs.core.Request; | ||
import javax.ws.rs.core.Response; | ||
import javax.ws.rs.core.StreamingOutput; | ||
import javax.ws.rs.core.Variant; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
import java.util.List; | ||
|
||
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE; | ||
import static javax.ws.rs.core.Response.created; | ||
import static javax.ws.rs.core.Response.ok; | ||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
@Component | ||
@Path("/rest/{path: .*}/fcr:graph") | ||
public class FedoraGraph extends AbstractResource { | ||
|
||
private static final Logger logger = getLogger(FedoraGraph.class); | ||
|
||
@GET | ||
@Produces({WebContent.contentTypeN3, | ||
WebContent.contentTypeN3Alt1, | ||
WebContent.contentTypeN3Alt2, | ||
WebContent.contentTypeTurtle, | ||
WebContent.contentTypeRDFXML, | ||
WebContent.contentTypeRDFJSON, | ||
WebContent.contentTypeNTriples}) | ||
public StreamingOutput describeRdf(@PathParam("path") final List<PathSegment> pathList, @Context Request request) throws RepositoryException, IOException { | ||
|
||
final String path = toPath(pathList); | ||
logger.trace("getting profile for {}", path); | ||
|
||
|
||
|
||
List<Variant> possibleResponseVariants = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yuck. Help? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you trying to avoid reproducing the array of contentType strings? I could push a kind of goofy bit of reflection that pulls the values out of the Produces annotation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto Ben's question, and if these two guys should always be the same, might we not define them somewhere else and just use the definition twice? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well:
Maybe I've missed something in Jena too that'd make this less annoying.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can context-inject the request and inspect it. That's a clean two-liner. And yes, there's MediaType.valueOf(type). |
||
Variant.mediaTypes(new MediaType(WebContent.contentTypeN3.split("/")[0], WebContent.contentTypeN3.split("/")[1]), | ||
new MediaType(WebContent.contentTypeN3Alt1.split("/")[0], WebContent.contentTypeN3Alt1.split("/")[1]), | ||
new MediaType(WebContent.contentTypeN3Alt2.split("/")[0], WebContent.contentTypeN3Alt2.split("/")[1]), | ||
new MediaType(WebContent.contentTypeTurtle.split("/")[0], WebContent.contentTypeTurtle.split("/")[1]), | ||
new MediaType(WebContent.contentTypeRDFXML.split("/")[0], WebContent.contentTypeRDFXML.split("/")[1]), | ||
new MediaType(WebContent.contentTypeRDFJSON.split("/")[0], WebContent.contentTypeRDFJSON.split("/")[1]), | ||
new MediaType(WebContent.contentTypeNTriples.split("/")[0], WebContent.contentTypeNTriples.split("/")[1]), | ||
new MediaType(WebContent.contentTypeTriG.split("/")[0], WebContent.contentTypeTriG.split("/")[1]), | ||
new MediaType(WebContent.contentTypeNQuads.split("/")[0], WebContent.contentTypeNQuads.split("/")[1]) | ||
) | ||
.add().build(); | ||
Variant bestPossibleResponse = request.selectVariant(possibleResponseVariants); | ||
|
||
|
||
final String rdfWriterFormat = WebContent.contentTypeToLang(bestPossibleResponse.getMediaType().toString()).getName().toUpperCase(); | ||
|
||
return new StreamingOutput() { | ||
@Override | ||
public void write(final OutputStream out) throws IOException { | ||
|
||
final Session session = getAuthenticatedSession(); | ||
try { | ||
Node node = session.getNode(path); | ||
|
||
final FedoraObject object = objectService.getObject(node.getSession(), path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason to get the node first, and then pass the session in? I think you could just pass the session in and let the objectService find the node, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the pattern we're slowly coming to is "inject the Session as a field, then use it with the *Services". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not totally sure how JAX-RS context injection plays with anonymous classes... I wouldn't think there's any problem, tho'. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cribbed this from |
||
final GraphStore graphStore = object.getGraphStore(); | ||
|
||
graphStore.toDataset().getDefaultModel().write(out, rdfWriterFormat); | ||
} catch (final RepositoryException e) { | ||
throw new WebApplicationException(e); | ||
} finally { | ||
session.logout(); | ||
} | ||
} | ||
|
||
}; | ||
|
||
} | ||
|
||
/** | ||
* Creates a new object. | ||
* | ||
* @param pathList | ||
* @return 201 | ||
* @throws RepositoryException | ||
* @throws org.fcrepo.exception.InvalidChecksumException | ||
* @throws IOException | ||
*/ | ||
@POST | ||
@Consumes({WebContent.contentTypeSPARQLUpdate}) | ||
@Timed | ||
public Response updateSparql( | ||
@PathParam("path") final List<PathSegment> pathList, | ||
final InputStream requestBodyStream | ||
) throws RepositoryException, IOException, InvalidChecksumException { | ||
|
||
String path = toPath(pathList); | ||
logger.debug("Attempting to ingest with path: {}", path); | ||
|
||
final Session session = getAuthenticatedSession(); | ||
|
||
try { | ||
if (objectService.exists(session, path)) { | ||
|
||
if(requestBodyStream != null) { | ||
|
||
final FedoraObject result = objectService.getObject(session, path); | ||
|
||
UpdateAction.parseExecute(IOUtils.toString(requestBodyStream), result.getGraphStore()); | ||
|
||
session.save(); | ||
|
||
return ok().build(); | ||
} else { | ||
return Response.status(HttpStatus.SC_CONFLICT).entity(path + " is an existing resource").build(); | ||
} | ||
} else { | ||
return Response.status(HttpStatus.SC_NOT_FOUND).entity(path + " must be an existing resource").build(); | ||
} | ||
|
||
} finally { | ||
session.logout(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajs6f I couldn't get this to play nice as a part of the FedoraNodes routing.. it seemed like some of the IT requests were hitting the rdf-producing endpoints instead of the object profile one..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea what FedoraNodes is/does, so I'll start there. More soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, FedoraNodes seems to be the endpoint formerly known as "fcr:describe". It's not clear to me why there's any difficulty-- you're not doing anything that plenty of other working endpoints aren't doing. What exactly are the paths that are failing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is a question for @barmintor then..
When I tried to put this method in
FedoraNodes
, several of the integration tests (perhaps all of them?) that tried to get the ObjectProfile or DatastreamProfile XML/JSON responses were getting this response instead. I assume in the absence of anAccept
header, JAX-RS magically chooses a response, and ended up choosing wrong.Maybe this isn't a problem in the long term (if we kill of those profile responses), but was more than I wanted to bite off..