Navigation Menu

Skip to content

Commit

Permalink
Add appropriate inbound triples based on LDP containment properties
Browse files Browse the repository at this point in the history
- Add IT to cover typical PCDM use case of proxies

Resolves: https://jira.duraspace.org/browse/FCREPO-1497
  • Loading branch information
escowles authored and Andrew Woods committed Jul 20, 2015
1 parent aadf58d commit 8845a22
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 21 deletions.
Expand Up @@ -1097,7 +1097,84 @@ public void testGetObjectReferences() throws IOException {
}

@Test
public void testGetObjectGraphLacksUUID() throws IOException {
public void testGetObjectReferencesIndirect() throws Exception {
final String uuid = getRandomUniqueId();
final String pid1 = uuid + "/parent";
final String pid2 = uuid + "/child";
createObjectAndClose(pid1);
createObjectAndClose(pid2);

final String memberRelation = "http://pcdm.org/models#hasMember";

// create an indirect container
final HttpPut createContainer = new HttpPut(serverAddress + pid1 + "/members");
createContainer.addHeader("Content-Type", "text/turtle");
final String membersRDF = "<> a <http://www.w3.org/ns/ldp#IndirectContainer>; "
+ "<http://www.w3.org/ns/ldp#hasMemberRelation> <" + memberRelation + ">; "
+ "<http://www.w3.org/ns/ldp#insertedContentRelation> <http://www.openarchives.org/ore/terms/proxyFor>; "
+ "<http://www.w3.org/ns/ldp#membershipResource> <" + serverAddress + pid1 + "> . ";
createContainer.setEntity(new StringEntity(membersRDF));
assertEquals(CREATED.getStatusCode(), getStatus(createContainer));

// create a proxy in the indirect container
final HttpPost createProxy = new HttpPost(serverAddress + pid1 + "/members");
createProxy.addHeader("Content-Type", "text/turtle");
final String proxyRDF = "<> <http://www.openarchives.org/ore/terms/proxyFor> <" + serverAddress + pid2 + ">;"
+ " <http://www.openarchives.org/ore/terms/proxyIn> <" + serverAddress + pid1 + "> .";
createProxy.setEntity(new StringEntity(proxyRDF));
assertEquals(CREATED.getStatusCode(), getStatus(createProxy));

// retrieve the parent and verify the outbound triples exist
final HttpGet getParent = new HttpGet(serverAddress + pid1);
getParent.addHeader("Accept", "application/n-triples");
try (final CloseableGraphStore parentGraph = getGraphStore(getParent)) {
assertTrue(parentGraph.contains(Node.ANY,
createURI(serverAddress + pid1),
createURI(memberRelation),
createURI(serverAddress + pid2)));
}

// retrieve the members container and verify the LDP triples exist
final HttpGet getContainer = new HttpGet(serverAddress + pid1 + "/members");
getContainer.addHeader("Prefer", "return=representation;include=\"http://www.w3.org/ns/ldp#PreferMembership\"");
getContainer.addHeader("Accept", "application/n-triples");
try (final CloseableGraphStore containerGraph = getGraphStore(getContainer)) {
assertTrue(containerGraph.contains(Node.ANY,
createURI(serverAddress + pid1 + "/members"),
createURI("http://www.w3.org/ns/ldp#hasMemberRelation"),
createURI(memberRelation)));

assertTrue(containerGraph.contains(Node.ANY,
createURI(serverAddress + pid1 + "/members"),
createURI("http://www.w3.org/ns/ldp#insertedContentRelation"),
createURI("http://www.openarchives.org/ore/terms/proxyFor")));

assertTrue(containerGraph.contains(Node.ANY,
createURI(serverAddress + pid1 + "/members"),
createURI("http://www.w3.org/ns/ldp#membershipResource"),
createURI(serverAddress + pid1)));
}


// retrieve the member and verify inbound triples exist
final HttpGet getMember = new HttpGet(serverAddress + pid2);
getMember.addHeader("Prefer", "return=representation; include=\"" + INBOUND_REFERENCES.toString() + "\"");
getMember.addHeader("Accept", "application/n-triples");
try (final CloseableGraphStore memberGraph = getGraphStore(getMember)) {
assertTrue(memberGraph.contains(Node.ANY,
Node.ANY,
createURI("http://www.openarchives.org/ore/terms/proxyFor"),
createURI(serverAddress + pid2)));

assertTrue(memberGraph.contains(Node.ANY,
createURI(serverAddress + pid1),
createURI(memberRelation),
createURI(serverAddress + pid2)));
}
}

@Test
public void testGetObjectGraphLacksUUID() throws Exception {
final String location = getLocation(postObjMethod());
final HttpGet getObjMethod = new HttpGet(location);
try (final CloseableGraphStore graphStore = getGraphStore(getObjMethod)) {
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.slf4j.Logger;

import javax.jcr.Node;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
Expand Down Expand Up @@ -153,15 +154,26 @@ private static RDFNode stringliteral2node(final String literal) {
}
}

private RDFNode traverseLink(final Value v)
throws RepositoryException {
final javax.jcr.Node refNode;
private RDFNode traverseLink(final Value v) throws RepositoryException {
return getGraphSubject(nodeForValue(session,v));
}

/**
* Get the node that a property value refers to.
* @param session Session to use to load the node.
* @param v Value that refers to a node.
* @throws RepositoryException When there is an error accessing the node.
* @throws RepositoryRuntimeException When the value type is not PATH, REFERENCE or WEAKREFERENCE.
**/
public static javax.jcr.Node nodeForValue(final Session session, final Value v) throws RepositoryException {
if (v.getType() == PATH) {
refNode = session.getNode(v.getString());
return session.getNode(v.getString());
} else if (v.getType() == REFERENCE || v.getType() == WEAKREFERENCE) {
return session.getNodeByIdentifier(v.getString());
} else {
refNode = session.getNodeByIdentifier(v.getString());
throw new RepositoryRuntimeException("Cannot convert value of type "
+ PropertyType.nameFromValue(v.getType()) + " to a node reference");
}
return getGraphSubject(refNode);
}

private RDFNode getGraphSubject(final javax.jcr.Node n) {
Expand Down
Expand Up @@ -203,7 +203,7 @@ public Iterator<Triple> apply(final FedoraResource child) {
return Iterators.transform(values, new Function<Value, Triple>() {
@Override
public Triple apply(final Value input) {
final RDFNode membershipResource = new ValueConverter(session(), translator())
final RDFNode membershipResource = new ValueConverter(session(),translator())
.convert(input);
return create(subject(), memberRelation, membershipResource.asNode());
}
Expand Down
Expand Up @@ -18,7 +18,9 @@
import static org.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeToResource;

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

import org.fcrepo.kernel.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.identifiers.IdentifierConverter;
import org.fcrepo.kernel.models.FedoraResource;
import org.fcrepo.kernel.utils.iterators.RdfStream;
Expand Down Expand Up @@ -53,6 +55,11 @@ public NodeRdfContext(final FedoraResource resource,
this.resource = resource;
this.idTranslator = idTranslator;
this.subject = idTranslator.reverse().convert(resource).asNode();
try {
session(resource.getNode().getSession());
} catch (RepositoryException ex) {
throw new RepositoryRuntimeException(ex);
}
}

/**
Expand Down
Expand Up @@ -15,21 +15,34 @@
*/
package org.fcrepo.kernel.impl.rdf.impl;

import static org.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeConverter;
import static org.fcrepo.kernel.impl.rdf.converters.ValueConverter.nodeForValue;
import static javax.jcr.PropertyType.PATH;
import static javax.jcr.PropertyType.REFERENCE;
import static javax.jcr.PropertyType.WEAKREFERENCE;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.rdf.model.Resource;
import org.fcrepo.kernel.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.models.FedoraResource;
import org.fcrepo.kernel.identifiers.IdentifierConverter;
import org.fcrepo.kernel.impl.rdf.impl.mappings.PropertyToTriple;
import org.fcrepo.kernel.impl.rdf.impl.mappings.PropertyValueIterator;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Value;

import java.util.Iterator;

/**
* Accumulate inbound references to a given resource
*
* @author cabeer
* @author escowles
*/
public class ReferencesRdfContext extends NodeRdfContext {

Expand All @@ -48,21 +61,44 @@ public ReferencesRdfContext(final FedoraResource resource,
throws RepositoryException {
super(resource, idTranslator);
property2triple = new PropertyToTriple(resource.getNode().getSession(), idTranslator);
concat(putStrongReferencePropertiesIntoContext());
concat(putWeakReferencePropertiesIntoContext());
putReferencesIntoContext(resource.getNode());
}

private Iterator<Triple> putWeakReferencePropertiesIntoContext() throws RepositoryException {
final Iterator<Property> properties = resource().getNode().getWeakReferences();

return Iterators.concat(Iterators.transform(properties, property2triple));
private void putReferencesIntoContext(final Node node) throws RepositoryException {
concat(Iterators.concat(Iterators.transform(
Iterators.concat(node.getReferences(), node.getWeakReferences()), property2triple)));
concat(Iterators.concat(Iterators.transform(Iterators.concat(Iterators.transform(
Iterators.concat(node.getReferences(), node.getWeakReferences()), potentialProxies)), triplesForValue)));
}

private Iterator<Triple> putStrongReferencePropertiesIntoContext() throws RepositoryException {
final Iterator<Property> properties = resource().getNode().getReferences();

return Iterators.concat(Iterators.transform(properties, property2triple));

}
/* References from LDP indirect containers are generated dynamically by LdpContainerRdfContext, so they won't
show up in getReferences()/getWeakReferences(). Instead, we should check referrers to see if they are
members of an IndirectContainer and generate the appropriate inbound references. */
private Function<Property, Iterator<Value>> potentialProxies = new Function<Property, Iterator<Value>>() {
@Override
public Iterator<Value> apply(final Property p) {
try {
return Iterators.filter(new PropertyValueIterator(p.getParent().getProperties()), isReference);
} catch (RepositoryException ex) {
throw new RepositoryRuntimeException(ex);
}
}
};
private Function<Value, Iterator<Triple>> triplesForValue = new Function<Value, Iterator<Triple>>() {
@Override
public Iterator<Triple> apply(final Value v) {
try {
return new LdpContainerRdfContext(nodeConverter.convert(nodeForValue(session(), v)), translator());
} catch (RepositoryException ex) {
throw new RepositoryRuntimeException(ex);
}
}
};
private Predicate<Value> isReference = new Predicate<Value>() {
@Override
public boolean apply(final Value v) {
return v.getType() == PATH || v.getType() == REFERENCE || v.getType() == WEAKREFERENCE;
}
};

}
Expand Up @@ -25,6 +25,8 @@
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.fcrepo.kernel.models.Container;
Expand All @@ -51,6 +53,10 @@ public class LdpRdfContextTest {

@Mock
private Container mockContainer;

@Mock
private Node mockNode;

@Mock
private Session mockSession;

Expand All @@ -60,11 +66,15 @@ public class LdpRdfContextTest {


@Before
public void setUp() {
public void setUp() throws RepositoryException {
initMocks(this);
when(mockResource.getPath()).thenReturn("/a");
when(mockBinary.getPath()).thenReturn("/a");
when(mockContainer.getPath()).thenReturn("/a");
when(mockResource.getNode()).thenReturn(mockNode);
when(mockBinary.getNode()).thenReturn(mockNode);
when(mockContainer.getNode()).thenReturn(mockNode);
when(mockNode.getSession()).thenReturn(mockSession);

subjects = new DefaultIdentifierTranslator(mockSession);
}
Expand Down
Expand Up @@ -24,6 +24,7 @@

import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
Expand Down Expand Up @@ -71,6 +72,8 @@ public class ReferencesRdfContextTest {
@Mock
private Value mockStrongValue;

@Mock
private PropertyIterator mockPropertyIterator;

@Before
public void setUp() throws RepositoryException {
Expand Down Expand Up @@ -102,6 +105,9 @@ public void setUp() throws RepositoryException {
strongReferencesProperties = new TestPropertyIterator(mockStrongProperty);
when(mockNode.getReferences()).thenReturn(strongReferencesProperties);

when(mockPropertyParent.getProperties()).thenReturn(mockPropertyIterator);
when(mockPropertyIterator.hasNext()).thenReturn(false);

testObj = new ReferencesRdfContext(mockResource, translator);
}

Expand Down

0 comments on commit 8845a22

Please sign in to comment.