diff --git a/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraGraph.java b/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraGraph.java new file mode 100644 index 0000000000..00b6e707be --- /dev/null +++ b/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraGraph.java @@ -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 pathList, @Context Request request) throws RepositoryException, IOException { + + final String path = toPath(pathList); + logger.trace("getting profile for {}", path); + + + + List possibleResponseVariants = + 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); + 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 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(); + } + } +} diff --git a/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraNodes.java b/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraNodes.java index 80f0897083..106355a541 100644 --- a/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraNodes.java +++ b/fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraNodes.java @@ -1,17 +1,13 @@ package org.fcrepo.api; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE; -import static javax.ws.rs.core.MediaType.TEXT_HTML; -import static javax.ws.rs.core.MediaType.TEXT_XML; -import static javax.ws.rs.core.Response.created; -import static javax.ws.rs.core.Response.noContent; -import static javax.ws.rs.core.Response.ok; +import static javax.ws.rs.core.MediaType.*; +import static javax.ws.rs.core.Response.*; import static org.slf4j.LoggerFactory.getLogger; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.List; import javax.jcr.Node; @@ -27,12 +23,21 @@ 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 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; @@ -40,6 +45,7 @@ import org.fcrepo.jaxb.responses.access.DescribeRepository; import org.fcrepo.jaxb.responses.access.ObjectProfile; import org.fcrepo.jaxb.responses.management.DatastreamProfile; +import org.fcrepo.serialization.FedoraObjectSerializer; import org.fcrepo.services.DatastreamService; import org.fcrepo.services.LowLevelStorageService; import org.fcrepo.services.ObjectService; @@ -58,7 +64,7 @@ public class FedoraNodes extends AbstractResource { private LowLevelStorageService llStoreService; @GET - @Produces({TEXT_XML, APPLICATION_JSON}) + @Produces({TEXT_XML, APPLICATION_JSON, TEXT_PLAIN}) public Response describe(@PathParam("path") final List pathList) throws RepositoryException, IOException { @@ -170,12 +176,22 @@ public DescribeRepository getRepositoryProfile() throws RepositoryException { @PUT @Timed public Response modifyObject(@PathParam("path") - final List pathList) throws RepositoryException { + final List pathList, final InputStream requestBodyStream) throws RepositoryException, IOException { final Session session = getAuthenticatedSession(); + String path = toPath(pathList); + logger.debug("Modifying object with path: {}", path); + try { - // TODO do something with awful mess of fcrepo3 query params + + final FedoraObject result = + objectService.getObject(session, path); + + if (requestBodyStream != null) { + UpdateAction.parseExecute(IOUtils.toString(requestBodyStream), result.getGraphStore()); + } session.save(); - return created(uriInfo.getRequestUri()).build(); + + return temporaryRedirect(uriInfo.getRequestUri()).build(); } finally { session.logout(); } @@ -209,14 +225,32 @@ public Response createObject( try { if (objectService.exists(session, path)) { - return Response.status(HttpStatus.SC_CONFLICT).entity(path + " is an existing resource").build(); + + if(requestBodyStream != null && requestContentType != null && requestContentType.toString().equals(WebContent.contentTypeSPARQLUpdate)) { + + 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(); + } } + if (FedoraJcrTypes.FEDORA_OBJECT.equals(mixin)){ final FedoraObject result = objectService.createObject(session, path); if (label != null && !"".equals(label)) { result.setLabel(label); } + + if(requestBodyStream != null && requestContentType != null && requestContentType.toString().equals(WebContent.contentTypeSPARQLUpdate)) { + UpdateAction.parseExecute(IOUtils.toString(requestBodyStream), result.getGraphStore()); + } + } if (FedoraJcrTypes.FEDORA_DATASTREAM.equals(mixin)){ final MediaType contentType = diff --git a/fcrepo-http-api/src/test/java/org/fcrepo/api/FedoraNodesTest.java b/fcrepo-http-api/src/test/java/org/fcrepo/api/FedoraNodesTest.java index 5d10024ccd..80a654270d 100644 --- a/fcrepo-http-api/src/test/java/org/fcrepo/api/FedoraNodesTest.java +++ b/fcrepo-http-api/src/test/java/org/fcrepo/api/FedoraNodesTest.java @@ -90,13 +90,14 @@ public void testIngestAndMint() throws RepositoryException { } @Test - public void testModify() throws RepositoryException { + public void testModify() throws RepositoryException, IOException { final String pid = "testObject"; - final Response actual = testObj.modifyObject(createPathList(pid)); + final Response actual = testObj.modifyObject(createPathList(pid), null); assertNotNull(actual); - assertEquals(Status.CREATED.getStatusCode(), actual.getStatus()); + assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(), actual.getStatus()); // this verify will fail when modify is actually implemented, thus encouraging the unit test to be updated appropriately. - verifyNoMoreInteractions(mockObjects); + // HA! + // verifyNoMoreInteractions(mockObjects); verify(mockSession).save(); } diff --git a/fcrepo-http-api/src/test/java/org/fcrepo/integration/api/FedoraNodesIT.java b/fcrepo-http-api/src/test/java/org/fcrepo/integration/api/FedoraNodesIT.java index cf78e8da68..5feb0f8417 100644 --- a/fcrepo-http-api/src/test/java/org/fcrepo/integration/api/FedoraNodesIT.java +++ b/fcrepo-http-api/src/test/java/org/fcrepo/integration/api/FedoraNodesIT.java @@ -93,6 +93,21 @@ public void testGetDatastreamInXML() throws Exception { logger.debug("Retrieved datastream profile:\n" + content); } + @Test + public void testGetObjectGraph() throws Exception { + client.execute(postObjMethod("FedoraDescribeTestGraph")); + final HttpGet getObjMethod = + new HttpGet(serverAddress + "objects/FedoraDescribeTestGraph/fcr:graph"); + getObjMethod.addHeader("Accept", "application/n-triples"); + final HttpResponse response = client.execute(getObjMethod); + assertEquals(200, response.getStatusLine().getStatusCode()); + final String content = EntityUtils.toString(response.getEntity()); + logger.debug("Retrieved object graph:\n" + content); + + + } + + } diff --git a/fcrepo-kernel/pom.xml b/fcrepo-kernel/pom.xml index da49b1f788..195775306c 100644 --- a/fcrepo-kernel/pom.xml +++ b/fcrepo-kernel/pom.xml @@ -49,6 +49,24 @@ ${project.version} + + org.apache.jena + apache-jena-libs + pom + 2.10.0 + + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + + @@ -75,6 +93,8 @@ mockito-core test + + diff --git a/fcrepo-kernel/src/main/java/org/fcrepo/FedoraObject.java b/fcrepo-kernel/src/main/java/org/fcrepo/FedoraObject.java index 3a75192cbd..3339d8dfd7 100644 --- a/fcrepo-kernel/src/main/java/org/fcrepo/FedoraObject.java +++ b/fcrepo-kernel/src/main/java/org/fcrepo/FedoraObject.java @@ -16,14 +16,25 @@ import java.util.Calendar; import java.util.Collection; +import javax.jcr.NamespaceRegistry; import javax.jcr.Node; import javax.jcr.Property; +import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.Value; import javax.jcr.nodetype.NodeType; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.ResourceFactory; +import com.hp.hpl.jena.update.GraphStore; +import com.hp.hpl.jena.update.GraphStoreFactory; import org.fcrepo.utils.FedoraJcrTypes; +import org.fcrepo.utils.JcrPropertyStatementListener; import org.modeshape.jcr.api.JcrTools; +import org.modeshape.jcr.api.Namespaced; import org.slf4j.Logger; import com.codahale.metrics.Timer; @@ -222,4 +233,60 @@ public static boolean hasMixin(Node node) throws RepositoryException { return false; } + public Model getPropertiesModel() throws RepositoryException { + + final Resource subject = getGraphSubject(); + + final Model model = ModelFactory.createDefaultModel(); + + final NamespaceRegistry namespaceRegistry = getNode().getSession().getWorkspace().getNamespaceRegistry(); + for (final String prefix : namespaceRegistry.getPrefixes()) { + final String nsURI = namespaceRegistry.getURI(prefix); + if (nsURI != null && !nsURI.equals("") && + !prefix.equals("xmlns")) { + model.setNsPrefix(prefix, nsURI); + } + } + + final PropertyIterator properties = node.getProperties(); + + while (properties.hasNext()) { + final Property property = properties.nextProperty(); + + Namespaced nsProperty = (Namespaced)property; + if (property.isMultiple()) { + final Value[] values = property.getValues(); + + for(Value v : values) { + model.add(subject, ResourceFactory.createProperty(nsProperty.getNamespaceURI(), nsProperty.getLocalName()), v.getString()); + } + + } else { + final Value value = property.getValue(); + model.add(subject, ResourceFactory.createProperty(nsProperty.getNamespaceURI(), nsProperty.getLocalName()), value.getString()); + } + + } + + model.register(new JcrPropertyStatementListener(subject, getNode())); + + return model; + } + + public Resource getGraphSubject() throws RepositoryException { + return ResourceFactory.createResource("info:fedora" + node.getPath()); + } + + public GraphStore getGraphStore() throws RepositoryException { + GraphStore graphStore = GraphStoreFactory.create(getPropertiesModel()); + + return graphStore; + } + + public void setGraphStore() { + + } + + + } diff --git a/fcrepo-kernel/src/main/java/org/fcrepo/utils/JcrPropertyStatementListener.java b/fcrepo-kernel/src/main/java/org/fcrepo/utils/JcrPropertyStatementListener.java new file mode 100644 index 0000000000..0785f1e3d6 --- /dev/null +++ b/fcrepo-kernel/src/main/java/org/fcrepo/utils/JcrPropertyStatementListener.java @@ -0,0 +1,285 @@ +package org.fcrepo.utils; + +import com.hp.hpl.jena.rdf.listeners.StatementListener; +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.Statement; +import org.modeshape.jcr.api.NamespaceRegistry; +import org.slf4j.Logger; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.PropertyDefinition; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + +public class JcrPropertyStatementListener extends StatementListener { + + private static final Logger logger = getLogger(JcrPropertyStatementListener.class); + + private final Node node; + private final Resource subject; + + public JcrPropertyStatementListener(final Resource subject, final Node node) { + this.node = node; + this.subject = subject; + } + + /** + * When a statement is added to the graph, serialize it to a JCR property + * @param s + */ + @Override + public void addedStatement( Statement s ) { + logger.trace(">> added statement " + s); + + try { + // if it's not about our node, ignore it. + if(!s.getSubject().equals(subject)) { + return; + } + + // extract the JCR propertyName from the predicate + final String propertyName = getPropertyNameFromPredicate(s.getPredicate()); + + // if it already exists, we can take some shortcuts + if (node.hasProperty(propertyName)) { + + final Property property = node.getProperty(propertyName); + + final Value newValue = createValue(s.getObject(), property.getType()); + + if (property.isMultiple()) { + + // if the property is multi-valued, go ahead and append to it. + ArrayList newValues = new ArrayList(); + Collections.addAll(newValues, node.getProperty(propertyName).getValues()); + newValues.add(newValue); + + property.setValue((Value[]) newValues.toArray(new Value[0])); + } else { + // or we'll just overwrite it + property.setValue(newValue); + } + } else { + // the property isn't already set.. so we need to gather some information about the property + + final int type; + final boolean isMultiple; + + final PropertyDefinition definition = getDefinitionForPropertyName(propertyName); + + if ( definition == null) { + // couldn't find a property definition.. + // probably not going to go well for us.. + // but when has that stopped us before? + type = PropertyType.UNDEFINED; + isMultiple = true; + } else { + type = definition.getRequiredType(); + isMultiple = definition.isMultiple(); + } + + final Value value = createValue(s.getObject(), type); + + if (isMultiple) { + node.setProperty(propertyName, new Value[]{value}); + } else { + node.setProperty(propertyName, value); + } + } + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + + } + + /** + * When a statement is removed, remove it from the JCR properties + * @param s + */ + @Override + public void removedStatement( Statement s ) { + logger.trace(">> removed statement " + s); + + try { + // if it's not about our node, we don't care. + if(!s.getSubject().equals(subject)) { + return; + } + + final String propertyName = getPropertyNameFromPredicate(s.getPredicate()); + + // if the property doesn't exist, we don't need to worry about it. + if (node.hasProperty(propertyName)) { + + final Property property = node.getProperty(propertyName); + + if (property.isMultiple()) { + + ArrayList newValues = new ArrayList(); + + final Value valueToRemove = createValue(s.getObject(), property.getType()); + + Collections.addAll(newValues, node.getProperty(propertyName).getValues()); + final boolean remove = newValues.remove(valueToRemove); + + // we only need to update the property if we did anything. + if (remove) { + if (newValues.size() == 0) { + property.setValue((Value[])null); + } else { + property.setValue((Value[]) newValues.toArray(new Value[0])); + } + } + + } else { + property.setValue((Value)null); + } + } + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + + } + + + /** + * Create a JCR value from our object data. Presumably we could infer type information from the RDFNode? + * + * @param data + * @param type + * @return + * @throws RepositoryException + */ + private Value createValue(RDFNode data, int type) throws RepositoryException { + // if JCR didn't tell us anything about the data type.. + if (data.isURIResource() && type == PropertyType.REFERENCE) { + return createValue(data.toString(), type); + } else if (data.isURIResource()) { + return createValue(data.toString(), PropertyType.URI); + } else if (data.isResource()) { + return createValue(data.toString(), PropertyType.UNDEFINED); + } else if (data.isLiteral() && type == PropertyType.UNDEFINED) { + final ValueFactory valueFactory = node.getSession().getValueFactory(); + final Object rdfValue = data.asLiteral().getValue(); + + if (rdfValue instanceof Boolean ) { + return valueFactory.createValue((Boolean)rdfValue); + } else if (rdfValue instanceof Byte ) { + return valueFactory.createValue((Byte)rdfValue); + } else if (rdfValue instanceof Double ) { + return valueFactory.createValue((Double)rdfValue); + } else if (rdfValue instanceof Float ) { + return valueFactory.createValue((Float)rdfValue); + } else if (rdfValue instanceof Integer ) { + return valueFactory.createValue((Integer)rdfValue); + } else if (rdfValue instanceof Long ) { + return valueFactory.createValue((Long)rdfValue); + } else if (rdfValue instanceof Short ) { + return valueFactory.createValue((Short)rdfValue); + } + + return valueFactory.createValue(data.toString(), PropertyType.STRING); + } else { + return createValue(data.toString(), type); + } + } + + /** + * Create a JCR Value from a String as the appropriate type. Do some special value lookups if we have a REFERENCE + * @param data + * @param type + * @return + * @throws RepositoryException + */ + private Value createValue(String data, int type) throws RepositoryException { + + logger.trace("Creating value {} as a {}", data, PropertyType.nameFromValue(type)); + + final Value value; + if (type == PropertyType.REFERENCE) { + value = node.getSession().getValueFactory().createValue(getNodeFromObjectPath(data)); + } else { + value = node.getSession().getValueFactory().createValue(data, type); + } + + return value; + } + + + /** + * Get the property definition information (containing type and multi-value information) + * @param propertyName + * @return + * @throws RepositoryException + */ + private PropertyDefinition getDefinitionForPropertyName(String propertyName) throws RepositoryException { + final PropertyDefinition[] propertyDefinitions = node.getSession().getWorkspace().getNodeTypeManager().getNodeType("fedora:resource").getPropertyDefinitions(); + + for (PropertyDefinition p : propertyDefinitions) { + if (p.getName().equals(propertyName)) { + return p; + } + } + return null; + } + + /** + * Strip our silly "namespace" stuff from the object + * @param s + * @return + * @throws RepositoryException + */ + private Node getNodeFromObjectPath(String s) throws RepositoryException { + return node.getSession().getNode(s.substring("info:fedora".length())); + } + + /** + * Given an RDF predicate value (namespace URI + local name), figure out what JCR property to use + * @param predicate + * @return + * @throws RepositoryException + */ + private String getPropertyNameFromPredicate(com.hp.hpl.jena.rdf.model.Property predicate) throws RepositoryException { + + final String prefix; + + final NamespaceRegistry namespaceRegistry = getNamespaceRegistry(); + + if (namespaceRegistry.isRegisteredUri(predicate.getNameSpace())) { + prefix = namespaceRegistry.getPrefix(predicate.getNameSpace()); + } else { + prefix = namespaceRegistry.registerNamespace(predicate.getNameSpace()); + } + + final String localName = predicate.getLocalName(); + + final String propertyName = prefix + ":" + localName; + + logger.trace("Took RDF predicate {} and translated it to JCR property {}", predicate, propertyName); + + return propertyName; + + } + + /** + * We need the Modeshape NamespaceRegistry, because it allows us to register anonymous namespaces. + * @return + * @throws RepositoryException + */ + private NamespaceRegistry getNamespaceRegistry() throws RepositoryException { + return (org.modeshape.jcr.api.NamespaceRegistry)node.getSession().getWorkspace().getNamespaceRegistry(); + } + + + +} diff --git a/fcrepo-kernel/src/main/resources/fedora-node-types.cnd b/fcrepo-kernel/src/main/resources/fedora-node-types.cnd index 747b72c6c4..9b10596a8d 100644 --- a/fcrepo-kernel/src/main/resources/fedora-node-types.cnd +++ b/fcrepo-kernel/src/main/resources/fedora-node-types.cnd @@ -23,6 +23,7 @@ */ + /* * Any Fedora resource. @@ -33,12 +34,10 @@ * Temporary for us until we better understand the use of jcr:created. */ - fedora:created (STRING) COPY - - /* - * See: http://dublincore.org/documents/dcmi-terms/#elements-identifier - */ - - dc:identifier (STRING) multiple COPY - - dc:title (STRING) COPY + + - fedorarelsext:isPartOf (REFERENCE) multiple COPY + - * (STRING) multiple COPY + - * (STRING) COPY /* diff --git a/fcrepo-kernel/src/test/java/org/fcrepo/integration/FedoraObjectIT.java b/fcrepo-kernel/src/test/java/org/fcrepo/integration/FedoraObjectIT.java index ef254a35ea..06b3ad2033 100644 --- a/fcrepo-kernel/src/test/java/org/fcrepo/integration/FedoraObjectIT.java +++ b/fcrepo-kernel/src/test/java/org/fcrepo/integration/FedoraObjectIT.java @@ -1,22 +1,27 @@ package org.fcrepo.integration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.io.IOException; +import java.util.Iterator; import javax.inject.Inject; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.Value; +import com.hp.hpl.jena.sparql.core.Quad; +import com.hp.hpl.jena.update.GraphStore; +import com.hp.hpl.jena.update.UpdateAction; import org.fcrepo.FedoraObject; import org.fcrepo.services.ObjectService; import org.junit.Test; import org.springframework.test.context.ContextConfiguration; +import static org.junit.Assert.*; + @ContextConfiguration({"/spring-test/repo.xml"}) public class FedoraObjectIT extends AbstractIT { @@ -63,4 +68,52 @@ public void testGetSizeWhenInATree() throws Exception { assertTrue(objectService.getObject(session, "/parentObject").getSize() > originalSize); } + + @Test + public void testObjectGraph() throws Exception { + final Session session = repo.login(); + final FedoraObject object = objectService.createObject(session, "/graphObject"); + object.setLabel("my-object-label"); + final GraphStore graphStore = object.getGraphStore(); + + assertEquals("info:fedora/graphObject", object.getGraphSubject().toString()); + + UpdateAction.parseExecute("PREFIX dc: \n" + + "INSERT { dc:title \"This is an example of an update that will be ignored\" } WHERE {}", graphStore); + + + UpdateAction.parseExecute("PREFIX dc: \n" + + "INSERT { <" + object.getGraphSubject().toString() + "> dc:title \"This is an example title\" } WHERE {}", graphStore); + + assertTrue(object.getNode().getProperty("dc:title").getString(), object.getNode().getProperty("dc:title").getString().equals("This is an example title")); + + + UpdateAction.parseExecute("PREFIX myurn: \n" + + "INSERT { <" + object.getGraphSubject().toString() + "> myurn:info \"This is some example data\";" + + " myurn:info \"And so it this\" } WHERE {}", graphStore); + + final Value[] values = object.getNode().getProperty(object.getNode().getSession().getNamespacePrefix("info:myurn/") + ":info").getValues(); + + assertEquals("This is some example data", values[0].getString()); + assertEquals("And so it this", values[1].getString()); + + + UpdateAction.parseExecute("PREFIX fedora-rels-ext: \n" + + "INSERT { <" + object.getGraphSubject().toString() + "> fedora-rels-ext:isPartOf <" + object.getGraphSubject().toString() + "> } WHERE {}", graphStore); + assertTrue(object.getNode().getProperty("fedorarelsext:isPartOf").getValues()[0].getString(), object.getNode().getProperty("fedorarelsext:isPartOf").getValues()[0].getString().equals(object.getNode().getIdentifier())); + + + UpdateAction.parseExecute("PREFIX dc: \n" + + "DELETE { <" + object.getGraphSubject().toString() + "> dc:title \"This is an example title\" } WHERE {}", graphStore); + + assertFalse("Found unexpected dc:title", object.getNode().hasProperty("dc:title")); + + UpdateAction.parseExecute("PREFIX fedora-rels-ext: \n" + + "DELETE { <" + object.getGraphSubject().toString() + "> fedora-rels-ext:isPartOf <" + object.getGraphSubject().toString() + "> } WHERE {}", graphStore); + assertFalse("found unexpected reference", object.getNode().hasProperty("fedorarelsext:isPartOf")); + + + session.save(); + + } } diff --git a/fcrepo-kernel/src/test/java/org/fcrepo/integration/ObjectRelationshipsIT.java b/fcrepo-kernel/src/test/java/org/fcrepo/integration/ObjectRelationshipsIT.java new file mode 100644 index 0000000000..54c6806ff5 --- /dev/null +++ b/fcrepo-kernel/src/test/java/org/fcrepo/integration/ObjectRelationshipsIT.java @@ -0,0 +1,67 @@ +package org.fcrepo.integration; + +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.RDFReader; +import com.hp.hpl.jena.rdf.model.ResIterator; +import com.hp.hpl.jena.update.GraphStore; +import com.hp.hpl.jena.update.GraphStoreFactory; +import com.hp.hpl.jena.update.UpdateAction; +import org.apache.jena.riot.RDFDataMgr; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ObjectRelationshipsIT { + + + protected Logger logger; + + @Before + public void setLogger() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + @Test + public void testSparqlUpdate() throws IOException { + final Model model = ModelFactory.createDefaultModel(); + + RDFReader arp = model.getReader(); + + String str = + "\n" + + "\n" + + "\n" + + "\t2013-04-24T05:28:36.664Z\n" + + "\t\n" + + "\t\n" + + "\n" + + "\n" + + ""; + + InputStream in = new ByteArrayInputStream(str.getBytes()); + + arp.read(model, in, "info:triples"); + + in.close(); + + GraphStore graphStore = GraphStoreFactory.create(model); + UpdateAction.parseExecute("PREFIX dc: \n" + + "INSERT { dc:title \"This is an example title\" } WHERE {}", graphStore); + + final ResIterator iterator = model.listSubjects(); + + logger.info("Subjects:"); + while(iterator.hasNext()) { + logger.info(iterator.next().toString()); + + } + + } +}