Skip to content

Commit

Permalink
Skolemize blank nodes provided by POST, PUT, and SPARQL-Update reques…
Browse files Browse the repository at this point in the history
…t into .well-known/genid/* nodes
  • Loading branch information
cbeer committed May 22, 2014
1 parent 8b51bd1 commit 4f2154e
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 18 deletions.
Expand Up @@ -26,9 +26,11 @@
import javax.jcr.Session;
import javax.jcr.Value;

import com.hp.hpl.jena.rdf.model.AnonId;
import org.fcrepo.kernel.RdfLexicon;
import org.fcrepo.kernel.rdf.IdentifierTranslator;
import org.fcrepo.kernel.rdf.JcrRdfTools;
import org.modeshape.jcr.api.JcrTools;
import org.slf4j.Logger;

import com.hp.hpl.jena.rdf.listeners.StatementListener;
Expand All @@ -37,6 +39,9 @@
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.RDF;

import java.util.HashMap;
import java.util.UUID;

/**
* Listen to Jena statement events, and when the statement is changed in the
* graph store, make the change within JCR as well.
Expand All @@ -46,6 +51,7 @@
public class JcrPropertyStatementListener extends StatementListener {

private final JcrRdfTools jcrRdfTools;
private final HashMap<AnonId, Node> skolemizedBnodeMap;

private NodePropertiesTools propertiesTools = new NodePropertiesTools();

Expand All @@ -58,6 +64,8 @@ public class JcrPropertyStatementListener extends StatementListener {

private final Session session;

private static final JcrTools jcrTools = new JcrTools();

/**
* Return a Listener given the subject factory and JcrSession.
* @param subjects
Expand All @@ -83,6 +91,7 @@ private JcrPropertyStatementListener(final IdentifierTranslator subjects,
this.subjects = subjects;
this.problems = problems;
this.jcrRdfTools = JcrRdfTools.withContext(subjects, session);
this.skolemizedBnodeMap = new HashMap<AnonId, Node>();
}

/**
Expand All @@ -98,11 +107,24 @@ public void addedStatement(final Statement s) {
final Resource subject = s.getSubject();

// if it's not about a node, ignore it.
if (!subjects.isFedoraGraphSubject(subject)) {
if (!subjects.isFedoraGraphSubject(subject) && !subject.isAnon()) {
return;
}

final Node subjectNode = session.getNode(subjects.getPathFromSubject(subject));
final Node subjectNode;

if (subject.isAnon()) {
if (skolemizedBnodeMap.containsKey(subject.getId())) {
subjectNode = skolemizedBnodeMap.get(subject.getId());
} else {
subjectNode
= jcrTools.findOrCreateNode(session, "/.well-known/genid/" + UUID.randomUUID().toString());
subjectNode.addMixin("fedora:blanknode");
skolemizedBnodeMap.put(subject.getId(), subjectNode);
}
} else {
subjectNode = session.getNode(subjects.getPathFromSubject(subject));
}

// special logic for handling rdf:type updates.
// if the object is an already-existing mixin, update
Expand Down
Expand Up @@ -28,8 +28,10 @@
import javax.jcr.Session;
import javax.jcr.Value;

import com.hp.hpl.jena.rdf.model.AnonId;
import org.fcrepo.kernel.rdf.IdentifierTranslator;
import org.fcrepo.kernel.rdf.JcrRdfTools;
import org.modeshape.jcr.api.JcrTools;
import org.slf4j.Logger;

import com.google.common.base.Predicate;
Expand All @@ -42,6 +44,10 @@
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
* @author ajs6f
* @since Oct 24, 2013
Expand All @@ -62,6 +68,8 @@ public abstract class PersistingRdfStreamConsumer implements RdfStreamConsumer {
private static final Model m = createDefaultModel();

private static final Logger LOGGER = getLogger(PersistingRdfStreamConsumer.class);
private static final JcrTools jcrTools = new JcrTools();
private final Map<AnonId, Resource> skolemizedBnodeMap;

/**
* Ordinary constructor.
Expand All @@ -81,19 +89,19 @@ public boolean apply(final Triple t) {

final boolean result =
graphSubjects.isFedoraGraphSubject(m.asStatement(t)
.getSubject());
.getSubject()) || t.getSubject().isBlank();
if (result) {
LOGGER.debug(
"Discovered a Fedora-relevant subject in triple: {}.",
t);
} else {
LOGGER.debug("Ignoring triple: {}.", t);

}
return result;
}

};
this.skolemizedBnodeMap = new HashMap<>();
// we knock out managed RDF and non-Fedora RDF
this.stream =
stream.withThisContext(stream.filter(and(not(isManagedTriple),
Expand All @@ -104,13 +112,39 @@ public boolean apply(final Triple t) {
@Override
public void consume() throws RepositoryException {
while (stream.hasNext()) {
final Statement t = m.asStatement(stream.next());
LOGGER.debug("Operating on triple {}.", t);
Statement t = m.asStatement(stream.next());
LOGGER.debug("Operating triple {}.", t);

if (t.getObject().isAnon()) {
t = t.changeObject(getSkolemizedResource(t.getObject()));
}

if (t.getSubject().isAnon()) {
t = m.createStatement(getSkolemizedResource(t.getSubject()), t.getPredicate(), t.getObject());
}

LOGGER.trace("Operating on skolemized triple {}.", t);

operateOnTriple(t);
}

}

private Resource getSkolemizedResource(final RDFNode resource) throws RepositoryException {
final AnonId id = resource.asResource().getId();

if (!skolemizedBnodeMap.containsKey(id)) {
final Node orCreateNode
= jcrTools.findOrCreateNode(session, "/.well-known/genid/" + UUID.randomUUID().toString());
orCreateNode.addMixin("fedora:blanknode");
final Resource skolemizedSubject
= idTranslator().getSubject(orCreateNode.getPath());
skolemizedBnodeMap.put(id, skolemizedSubject);
}

return skolemizedBnodeMap.get(id);
}

protected void operateOnTriple(final Statement t)
throws RepositoryException {
final Resource subject = t.getSubject();
Expand Down
2 changes: 2 additions & 0 deletions fcrepo-kernel/src/main/resources/fedora-node-types.cnd
Expand Up @@ -112,3 +112,5 @@
- premis:hasOriginalName (STRING)
- premis:hasSize (LONG) COPY
- fedora:digest (URI) COPY

[fedora:blanknode] > mix:referenceable mixin
Expand Up @@ -18,6 +18,7 @@
import static com.hp.hpl.jena.graph.Node.ANY;
import static com.hp.hpl.jena.graph.NodeFactory.createLiteral;
import static com.hp.hpl.jena.graph.NodeFactory.createURI;
import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createPlainLiteral;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createTypedLiteral;
Expand Down Expand Up @@ -51,14 +52,19 @@
import javax.jcr.Session;
import javax.jcr.nodetype.NodeTypeManager;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import org.fcrepo.kernel.FedoraObject;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.exception.InvalidChecksumException;
import org.fcrepo.kernel.rdf.impl.DefaultIdentifierTranslator;
import org.fcrepo.kernel.services.DatastreamService;
import org.fcrepo.kernel.services.NodeService;
import org.fcrepo.kernel.services.ObjectService;
import org.fcrepo.kernel.utils.iterators.PropertyIterator;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
Expand Down Expand Up @@ -543,4 +549,48 @@ public void testGetReferences() throws RepositoryException {
subjects.getSubject(object.getPath()))
);
}

@Test
public void testReplaceProperties() throws RepositoryException {
final String pid = UUID.randomUUID().toString();
final FedoraObject object = objectService.createObject(session, pid);

final StmtIterator stmtIterator = object.getPropertiesDataset(subjects).getDefaultModel().listStatements();
final Model model = createDefaultModel().add(stmtIterator);

final Resource resource = model.createResource();
final Resource subject = subjects.getSubject(object.getPath());
final Property predicate = model.createProperty("info:xyz");
model.add(subject, predicate, resource);
model.add(resource, model.createProperty("http://purl.org/dc/elements/1.1/title"), "xyz");

object.replaceProperties(subjects, model);

final PropertyIterator properties = new PropertyIterator(object.getNode().getProperties());

final UnmodifiableIterator<javax.jcr.Property> relation
= Iterators.filter(properties, new Predicate<javax.jcr.Property>() {
@Override
public boolean apply(final javax.jcr.Property property) {
try {
return property.getName().contains("xyz_ref");
} catch (RepositoryException e) {
return false;
}
}
});

assertTrue(relation.hasNext());

final javax.jcr.Property next = relation.next();
final Value[] values = next.getValues();
assertEquals(1, values.length);

final javax.jcr.Node skolemizedNode = session.getNodeByIdentifier(values[0].getString());

assertTrue(skolemizedNode.getPath().contains("/.well-known/genid/"));
assertEquals("xyz", skolemizedNode.getProperty("dc:title").getValues()[0].getString());

}

}
Expand Up @@ -239,7 +239,7 @@ public void testRemovedIrrelevantStatement() {
@Test
public void testAddRdfType() throws RepositoryException {

final Resource resource = createResource();
final Resource resource = createResource("xyz");
when(mockSubjects.isFedoraGraphSubject(resource)).thenReturn(true);
when(mockSession.getNode(mockSubjects.getPathFromSubject(resource))).thenReturn(mockSubjectNode);

Expand Down Expand Up @@ -280,7 +280,7 @@ public void testRemoveRdfType() throws RepositoryException {
@Test
public void testAddRdfTypeForNonMixin() throws RepositoryException {

final Resource resource = createResource();
final Resource resource = createResource("xyz");
when(mockSubjects.isFedoraGraphSubject(resource)).thenReturn(true);
when(mockSession.getNode(mockSubjects.getPathFromSubject(resource))).thenReturn(mockSubjectNode);

Expand Down
Expand Up @@ -156,20 +156,24 @@ public void setUp() throws RepositoryException {

private static final Model m = createDefaultModel();

private static final Triple propertyTriple = create(createAnon(),
createAnon(), createAnon());
private static final com.hp.hpl.jena.graph.Node subject = m.createResource("x").asNode();
private static final com.hp.hpl.jena.graph.Node object = m.createResource("y").asNode();
private static final com.hp.hpl.jena.graph.Node foreignSubject = m.createResource("z").asNode();

private static final Triple propertyTriple = create(subject,
createAnon(), object);

private static final Statement propertyStatement = m
.asStatement(propertyTriple);

private static final Triple ldpManagedPropertyTriple = create(createAnon(),
PAGE.asNode(), createAnon());
private static final Triple ldpManagedPropertyTriple = create(subject,
PAGE.asNode(), object);

private static final Statement ldpManagedPropertyStatement = m
.asStatement(ldpManagedPropertyTriple);

private static final Triple fedoraManagedPropertyTriple = create(createAnon(),
createURI(REPOSITORY_NAMESPACE + "thing"), createAnon());
private static final Triple fedoraManagedPropertyTriple = create(subject,
createURI(REPOSITORY_NAMESPACE + "thing"), object);

private static final Statement fedoraManagedPropertyStatement = m
.asStatement(fedoraManagedPropertyTriple);
Expand All @@ -179,19 +183,19 @@ public void setUp() throws RepositoryException {
ResourceFactory.createProperty(JCR_NAMESPACE, "thing"),
ResourceFactory.createResource());

private static final Triple managedMixinTriple = create(createAnon(), type
private static final Triple managedMixinTriple = create(subject, type
.asNode(), createURI(RESTAPI_NAMESPACE + "mixin"));

private static final Statement managedMixinStatement = m.asStatement(managedMixinTriple);

private static final Triple mixinTriple = create(createAnon(),
private static final Triple mixinTriple = create(subject,
type.asNode(), createURI("myNS:mymixin"));

private static final Statement mixinStatement = m.asStatement(mixinTriple);


private static final Triple foreignTriple = create(createAnon(),
createAnon(), createAnon());
private static final Triple foreignTriple = create(foreignSubject,
createAnon(), object);

private static final Statement foreignStatement = m.asStatement(foreignTriple);

Expand Down

0 comments on commit 4f2154e

Please sign in to comment.