Skip to content
Permalink
Browse files

Optionally include inbound references in the object rdf serialization

Inbound references can be included in the GET /some/object/path response
by using the Prefer: header, e.g.:

```
GET /some/object/path HTTP/1.1
...
Prefer: return="representation"; include="http://fedora.info/definitions/v4/repository#InboundReferences"
...

```

And the response will include inbound triples, e.g.:

```
</some/other/path> <fedorarelsext:isPartOf> </some/object/path>
```

;# Please enter the commit message for your changes. Lines starting
  • Loading branch information...
cbeer authored and awoods committed May 5, 2014
1 parent 38482a1 commit d77194ca77f9eb67cf735fe71d1e30c0d34fa033
@@ -49,6 +49,7 @@
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_DATASTREAM;
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_OBJECT;
import static org.fcrepo.kernel.RdfLexicon.FIRST_PAGE;
import static org.fcrepo.kernel.RdfLexicon.INBOUND_REFERENCES;
import static org.fcrepo.kernel.RdfLexicon.LDP_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.NEXT_PAGE;
import static org.fcrepo.kernel.rdf.GraphProperties.PROBLEMS_MODEL_NAME;
@@ -259,6 +260,9 @@ public RdfStream describe(@PathParam("path") final List<PathSegment> pathList,
&& !contains(omits, LDP_NAMESPACE + "PreferContainment");


final boolean references = contains(includes, INBOUND_REFERENCES.toString())
&& !contains(omits, INBOUND_REFERENCES.toString());

final HierarchyRdfContextOptions hierarchyRdfContextOptions
= new HierarchyRdfContextOptions(limit, offset, membership, containment);

@@ -270,6 +274,11 @@ public RdfStream describe(@PathParam("path") final List<PathSegment> pathList,
appliedIncludes.add(LDP_NAMESPACE + "PreferContainment");
}

if (references) {
rdfStream.concat(resource.getReferencesTriples(subjects));
appliedIncludes.add(INBOUND_REFERENCES.toString());
}

rdfStream.concat(resource.getHierarchyTriples(subjects, hierarchyRdfContextOptions));

final String preferences = "return=representation; include=\""
@@ -46,6 +46,7 @@
import static org.fcrepo.kernel.RdfLexicon.DC_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.DC_TITLE;
import static org.fcrepo.kernel.RdfLexicon.FIRST_PAGE;
import static org.fcrepo.kernel.RdfLexicon.INBOUND_REFERENCES;
import static org.fcrepo.kernel.RdfLexicon.NEXT_PAGE;
import static org.fcrepo.kernel.RdfLexicon.HAS_CHILD;
import static org.fcrepo.kernel.RdfLexicon.HAS_OBJECT_COUNT;
@@ -79,6 +80,7 @@

import javax.ws.rs.core.Variant;

import com.hp.hpl.jena.graph.NodeFactory;
import nu.validator.htmlparser.sax.HtmlParser;
import nu.validator.saxtree.TreeBuilder;

@@ -98,6 +100,7 @@
import org.apache.http.impl.client.cache.CachingHttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.fcrepo.http.commons.domain.RDFMediaType;
import org.fcrepo.kernel.RdfLexicon;
import org.junit.Ignore;
import org.junit.Test;
import org.xml.sax.ErrorHandler;
@@ -577,6 +580,51 @@ public void testGetObjectOmitContainment() throws Exception {

}

@Test
public void testGetObjectReferences() throws Exception {
final String pid = getRandomUniquePid();
createObject(pid);
createObject(pid + "/a");
createObject(pid + "/b");

final HttpPatch updateObjectGraphMethod = new HttpPatch(serverAddress + pid + "/a");

updateObjectGraphMethod.addHeader("Content-Type", "application/sparql-update");

BasicHttpEntity e = new BasicHttpEntity();
e.setContent(
new ByteArrayInputStream(
("INSERT { " +
"<" + serverAddress + pid + "/a" + "> <http://fedora.info/definitions/v4/rels-ext#isPartOf> <" + serverAddress + pid + "/b" + "> . \n" +
"<" + serverAddress + pid + "/a" + "> <info:xyz#some-other-property> <" + serverAddress + pid + "/b" + "> " +
"} WHERE {}").getBytes()
)
);

updateObjectGraphMethod.setEntity(e);
client.execute(updateObjectGraphMethod);

final HttpGet getObjMethod = new HttpGet(serverAddress + pid + "/b");

getObjMethod.addHeader("Prefer", "return=representation; include=\"" + INBOUND_REFERENCES.toString() + "\"");
getObjMethod.addHeader("Accept", "application/n-triples");

final GraphStore graphStore = getGraphStore(getObjMethod);

assertTrue(graphStore.contains(Node.ANY,
NodeFactory.createURI(serverAddress + pid + "/a"),
NodeFactory.createURI("http://fedora.info/definitions/v4/rels-ext#isPartOf"),
NodeFactory.createURI(serverAddress + pid + "/b")
));

assertTrue(graphStore.contains(Node.ANY,
NodeFactory.createURI(serverAddress + pid + "/a"),
NodeFactory.createURI("info:xyz#some-other-property"),
NodeFactory.createURI(serverAddress + pid + "/b")
));

}

@Test
public void testGetObjectGraphByUUID() throws Exception {
final HttpResponse createResponse = createObject("");
@@ -146,6 +146,14 @@ RdfStream getHierarchyTriples(final IdentifierTranslator graphSubjects,
RdfStream getVersionTriples(final IdentifierTranslator graphSubjects)
throws RepositoryException;

/**
* Serialize inbound References to this object as an {@link RdfStream}
* @param graphSubjects
* @return
* @throws RepositoryException
*/
RdfStream getReferencesTriples(final IdentifierTranslator graphSubjects) throws RepositoryException;

/**
* Tag the current version of the Node with a version label that
* can be retrieved by name later.
@@ -294,6 +294,7 @@
// RDF EXTRACTION
public static final Property COULD_NOT_STORE_PROPERTY =
createProperty(REPOSITORY_NAMESPACE + "couldNotStoreProperty");
public static final Property INBOUND_REFERENCES = createProperty(REPOSITORY_NAMESPACE + "InboundReferences");

// IMPORTANT JCR PROPERTIES
public static final Property HAS_PRIMARY_IDENTIFIER =
@@ -304,6 +304,11 @@ public RdfStream getVersionTriples(final IdentifierTranslator graphSubjects)
.getVersionTriples(node);
}

@Override
public RdfStream getReferencesTriples(final IdentifierTranslator graphSubjects) throws RepositoryException {
return JcrRdfTools.withContext(graphSubjects, node.getSession()).getReferencesTriples(node);
}

/* (non-Javadoc)
* @see org.fcrepo.kernel.FedoraResource#addVersionLabel(java.lang.String)
*/
@@ -32,7 +32,9 @@
import static org.fcrepo.kernel.RdfLexicon.JCR_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.LDP_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.REPOSITORY_NAMESPACE;
import static org.fcrepo.kernel.utils.FedoraTypesUtils.isReferenceProperty;
import static org.fcrepo.kernel.utils.NamespaceTools.getNamespaceRegistry;
import static org.fcrepo.kernel.utils.NodePropertiesTools.getReferencePropertyOriginalName;
import static org.slf4j.LoggerFactory.getLogger;

import java.util.Iterator;
@@ -53,6 +55,7 @@
import org.fcrepo.kernel.rdf.impl.HierarchyRdfContext;
import org.fcrepo.kernel.rdf.impl.NamespaceRdfContext;
import org.fcrepo.kernel.rdf.impl.PropertiesRdfContext;
import org.fcrepo.kernel.rdf.impl.ReferencesRdfContext;
import org.fcrepo.kernel.rdf.impl.VersionsRdfContext;
import org.fcrepo.kernel.rdf.impl.WorkspaceRdfContext;
import org.fcrepo.kernel.services.LowLevelStorageService;
@@ -329,6 +332,17 @@ public RdfStream getTreeTriples(final Node node) throws RepositoryException {
return getTreeTriples(node, HierarchyRdfContextOptions.DEFAULT);
}


/**
* Add the properties for inbound references to this node
* @param node
* @return
* @throws RepositoryException
*/
public RdfStream getReferencesTriples(final Node node) throws RepositoryException {
return new ReferencesRdfContext(node, graphSubjects);
}

/**
* Decides whether the RDF representation of this {@link Node} will receive LDP Container status.
*
@@ -652,9 +666,17 @@ public int getPropertyType(final NodeType nodeType, final String propertyName) {
if (property instanceof Namespaced) {
final Namespaced nsProperty = (Namespaced) property;
final String uri = nsProperty.getNamespaceURI();
final String localName = nsProperty.getLocalName();
final String rdfLocalName;

if (isReferenceProperty.apply(property)) {
rdfLocalName = getReferencePropertyOriginalName(localName);
} else {
rdfLocalName = localName;
}
return createProperty(
getRDFNamespaceForJcrNamespace(uri),
nsProperty.getLocalName());
rdfLocalName);
}
return createProperty(property.getName());
} catch (final RepositoryException e) {
@@ -663,5 +685,4 @@ public int getPropertyType(final NodeType nodeType, final String propertyName) {

}
};

}
@@ -0,0 +1,88 @@
/**
* Copyright 2014 DuraSpace, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fcrepo.kernel.rdf.impl;

import com.google.common.collect.Iterators;
import com.hp.hpl.jena.graph.Triple;
import org.fcrepo.kernel.rdf.IdentifierTranslator;
import org.fcrepo.kernel.rdf.impl.mappings.PropertyToTriple;
import org.fcrepo.kernel.rdf.impl.mappings.ZippingIterator;
import org.fcrepo.kernel.utils.iterators.PropertyIterator;
import org.fcrepo.kernel.utils.iterators.RdfStream;

import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;

import java.util.Iterator;

import static org.fcrepo.kernel.utils.FedoraTypesUtils.property2values;

/**
* Accumulate inbound references to a given node
*
* @author cabeer
*/
public class ReferencesRdfContext extends RdfStream {

private final Node node;
private PropertyToTriple property2triple;

/**
* Add the inbound references from other nodes to this node to the {@link RdfStream}
*
* @param node
* @param graphSubjects
* @throws javax.jcr.RepositoryException
*/

public ReferencesRdfContext(final javax.jcr.Node node, final IdentifierTranslator graphSubjects)
throws RepositoryException {
super();
this.node = node;
property2triple = new PropertyToTriple(graphSubjects);
concat(putStrongReferencePropertiesIntoContext());
concat(putWeakReferencePropertiesIntoContext());
}

private Iterator<Triple> putWeakReferencePropertiesIntoContext() throws RepositoryException {
final Iterator<Property> properties = new PropertyIterator(node.getWeakReferences());

final Iterator<Property> propertiesCopy = new PropertyIterator(node.getWeakReferences());

return zipPropertiesToTriples(properties, propertiesCopy);

}

private Iterator<Triple> putStrongReferencePropertiesIntoContext() throws RepositoryException {
final Iterator<Property> properties = new PropertyIterator(node.getReferences());

final Iterator<Property> propertiesCopy = new PropertyIterator(node.getReferences());

return zipPropertiesToTriples(properties, propertiesCopy);

}

private Iterator<Triple> zipPropertiesToTriples(final Iterator<Property> propertyIterator,
final Iterator<Property> propertyIteratorCopy) {
return Iterators.concat(
new ZippingIterator<>(
Iterators.transform(propertyIterator, property2values),
Iterators.transform(propertyIteratorCopy, property2triple)
)
);
}
}
@@ -234,6 +234,21 @@ public static String getReferencePropertyName(final String propertyName) {
return propertyName + REFERENCE_PROPERTY_SUFFIX;
}

/**
* Given an internal reference node property, get the original name
* @param refPropertyName
* @return
*/
public static String getReferencePropertyOriginalName(final String refPropertyName) {
final int i = refPropertyName.lastIndexOf(REFERENCE_PROPERTY_SUFFIX);

if (i < 0) {
return refPropertyName;
} else {
return refPropertyName.substring(0, i);
}
}

private static String getReferencePropertyName(final Property property) throws RepositoryException {
return getReferencePropertyName(property.getName());
}
@@ -40,8 +40,10 @@

import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeTypeDefinition;
import javax.jcr.nodetype.NodeTypeTemplate;
import javax.jcr.PropertyType;
@@ -50,6 +52,8 @@
import javax.jcr.Session;
import javax.jcr.nodetype.NodeTypeManager;

import com.hp.hpl.jena.rdf.model.ResourceFactory;
import org.fcrepo.kernel.FedoraObject;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.exception.InvalidChecksumException;
import org.fcrepo.kernel.rdf.impl.DefaultIdentifierTranslator;
@@ -523,4 +527,24 @@ public void testEtagValue() throws RepositoryException {
assertNotNull(actual);
assertNotEquals("", actual);
}

@Test
public void testGetReferences() throws RepositoryException {
final String pid = UUID.randomUUID().toString();
objectService.createObject(session, pid);
final FedoraObject subject = objectService.createObject(session, pid + "/a");
final FedoraObject object = objectService.createObject(session, pid + "/b");
final Value value = session.getValueFactory().createValue(object.getNode());
subject.getNode().setProperty("fedorarelsext:isPartOf", new Value[] { value });

session.save();

final Model model = object.getReferencesTriples(subjects).asModel();

assertTrue(
model.contains(subjects.getSubject(subject.getPath()),
ResourceFactory.createProperty("http://fedora.info/definitions/v4/rels-ext#isPartOf"),
subjects.getSubject(object.getPath()))
);
}
}
Oops, something went wrong.

0 comments on commit d77194c

Please sign in to comment.
You can’t perform that action at this time.