Skip to content

Commit

Permalink
Removing reciprocal _ref properties
Browse files Browse the repository at this point in the history
  • Loading branch information
escowles committed Nov 12, 2015
1 parent 6ea28b2 commit f152b17
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 295 deletions.
Expand Up @@ -62,6 +62,7 @@
import static org.fcrepo.kernel.api.RdfLexicon.BASIC_CONTAINER;
import static org.fcrepo.kernel.api.RdfLexicon.CONSTRAINED_BY;
import static org.fcrepo.kernel.api.RdfLexicon.CONTAINS;
import static org.fcrepo.kernel.api.RdfLexicon.CREATED_DATE;
import static org.fcrepo.kernel.api.RdfLexicon.DC_TITLE;
import static org.fcrepo.kernel.api.RdfLexicon.DIRECT_CONTAINER;
import static org.fcrepo.kernel.api.RdfLexicon.HAS_CHILD;
Expand All @@ -73,6 +74,7 @@
import static org.fcrepo.kernel.api.RdfLexicon.INBOUND_REFERENCES;
import static org.fcrepo.kernel.api.RdfLexicon.INDIRECT_CONTAINER;
import static org.fcrepo.kernel.api.RdfLexicon.JCR_NT_NAMESPACE;
import static org.fcrepo.kernel.api.RdfLexicon.LAST_MODIFIED_DATE;
import static org.fcrepo.kernel.api.RdfLexicon.LDP_MEMBER;
import static org.fcrepo.kernel.api.RdfLexicon.LDP_NAMESPACE;
import static org.fcrepo.kernel.api.RdfLexicon.MEMBERSHIP_RESOURCE;
Expand Down Expand Up @@ -625,6 +627,7 @@ public void testCreateGraphWithBlanknodes() throws IOException {
final HttpGet getObjMethod = new HttpGet(subjectURI);
getObjMethod.addHeader("Accept", "application/rdf+xml");
try (final CloseableGraphStore graph = getGraphStore(getObjMethod)) {
System.out.println("XXX quads: " + graph);
final Iterator<Quad> quads =
graph.find(ANY, createURI(subjectURI), createURI("info:some-predicate"), ANY);
assertTrue("Didn't find skolemized blank node assertion", quads.hasNext());
Expand Down Expand Up @@ -1966,4 +1969,142 @@ public void testPutReferenceRoot() throws Exception {
}
}

}
@Test
public void testInboundLinksDoNotUpdateEtag() throws IOException {
final String id1 = getRandomUniqueId();
final HttpPut httpPut = putObjMethod(id1);
final String oldETag;
final String oldMod;
try (final CloseableHttpResponse response = execute(httpPut)) {
assertEquals(CREATED.getStatusCode(), getStatus(response));
oldETag = response.getFirstHeader("ETag").getValue();
oldMod = response.getFirstHeader("Last-Modified").getValue();
}

final String id2 = getRandomUniqueId();
createObject(id2).close();

final HttpPatch patch = patchObjMethod(id2);
patch.addHeader("Content-Type", "application/sparql-update");
patch.setEntity(new StringEntity(
"INSERT { <> <http://purl.org/dc/elements/1.1/relation> <" + serverAddress + id1 + "> } WHERE {}"));
try (final CloseableHttpResponse response = execute(patch)) {
assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
}

try (final CloseableHttpResponse response = execute(getObjMethod(id1))) {
final String etag = response.getFirstHeader("ETag").getValue();
final String lastmod = response.getFirstHeader("Last-Modified").getValue();
assertEquals(oldMod, lastmod);
assertEquals(oldETag, etag);
}
}

@Test
public void testBinaryLastModified() throws Exception {
final String objid = getRandomUniqueId();
final String objURI = serverAddress + objid;
final String binURI = objURI + "/binary1";

final long lastmod1;
try (final CloseableHttpResponse response = execute(putDSMethod(objid, "binary1", "some test content"))) {
assertEquals(CREATED.getStatusCode(), getStatus(response));
lastmod1 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
}

sleep(1000); // wait a second to make sure last-modified value will be different

try (final CloseableGraphStore graph = getGraphStore(new HttpGet(binURI + "/fcr:metadata"))) {
verifyModifiedMatchesCreated(graph);
}

final HttpPatch patchBinary = new HttpPatch(binURI + "/fcr:metadata");
patchBinary.addHeader("Content-Type", "application/sparql-update");
patchBinary.setEntity(new StringEntity("INSERT { <" + binURI + "> " +
"<http://www.w3.org/TR/rdf-schema/label> \"this is a label\" } WHERE {}"));
final long lastmod2;
try (final CloseableHttpResponse response = execute(patchBinary)) {
assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
lastmod2 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue(lastmod2 > lastmod1);
}

sleep(1000); // wait a second to make sure last-modified value will be different

final long lastmod3;
try (final CloseableHttpResponse response = execute(putDSMethod(objid, "binary1", "new test content"))) {
assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
lastmod3 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue(lastmod3 > lastmod2);
}
}

@Test
public void testContainerLastModified() throws Exception {
final String objid = getRandomUniqueId();
final String objURI = serverAddress + objid;

// create an object
final long lastmod1;
try (final CloseableHttpResponse response = execute(putObjMethod(objid))) {
assertEquals(CREATED.getStatusCode(), getStatus(response));
lastmod1 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
}

sleep(1000); // wait a second to make sure last-modified value will be different

// initial created and last-modified properties should match
try (final CloseableGraphStore graph = getGraphStore(getObjMethod(objid))) {
verifyModifiedMatchesCreated(graph);
}

// update the object properties (last-modified should be updated)
final HttpPatch patchObject = new HttpPatch(objURI);
patchObject.addHeader("Content-Type", "application/sparql-update");
patchObject.setEntity(new StringEntity("INSERT { <> " +
"<http://www.w3.org/TR/rdf-schema/label> \"this is a label\" } WHERE {}"));
final long lastmod2;
try (final CloseableHttpResponse response = execute(patchObject)) {
assertEquals(NO_CONTENT.getStatusCode(), getStatus(response));
lastmod2 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue(lastmod2 > lastmod1);
}

sleep(1000); // wait a second to make sure last-modified value will be different

// create a direct container (last-modified should be updated)
final long lastmod3;
final HttpPut createContainer = new HttpPut(objURI + "/members");
createContainer.addHeader("Content-Type", "text/turtle");
final String membersRDF = "<> a <http://www.w3.org/ns/ldp#DirectContainer>; "
+ "<http://www.w3.org/ns/ldp#hasMemberRelation> <http://pcdm.org/models#hasMember>; "
+ "<http://www.w3.org/ns/ldp#membershipResource> <" + objURI + "> . ";
createContainer.setEntity(new StringEntity(membersRDF));
try (final CloseableHttpResponse response = execute(createContainer)) {
assertEquals(CREATED.getStatusCode(), getStatus(response));
lastmod3 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue(lastmod3 > lastmod2);
}

sleep(1000); // wait a second to make sure last-modified value will be different

// create child in the container
final long lastmod4;
assertEquals(CREATED.getStatusCode(), getStatus(new HttpPut(objURI + "/members/member1")));

// last-modified should be updated
try (final CloseableHttpResponse response = execute(headObjMethod(objid))) {
assertEquals(OK.getStatusCode(), getStatus(response));
lastmod4 = headerFormat.parse(response.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue(lastmod4 > lastmod3);
}
}

private static void verifyModifiedMatchesCreated(final GraphStore graph) {
final Node cre = graph.find(ANY, ANY, CREATED_DATE.asNode(), ANY).next().getObject();
final Node mod = graph.find(ANY, ANY, LAST_MODIFIED_DATE.asNode(), ANY).next().getObject();
System.out.println("cre: " + cre.getLiteralValue());
System.out.println("mod: " + mod.getLiteralValue());
assertEquals(cre.getLiteralValue(), mod.getLiteralValue());
}
}
Expand Up @@ -97,13 +97,13 @@ public interface FedoraResource {
void delete();

/**
* Get the date this datastream was created
* Get the date this resource was created
* @return created date
*/
Date getCreatedDate();

/**
* Get the date this datastream was last modified
* Get the date this resource was last modified
* @return last modified date
*/
Date getLastModifiedDate();
Expand Down
Expand Up @@ -29,7 +29,6 @@
import static org.fcrepo.kernel.modeshape.rdf.converters.PropertyConverter.getPropertyNameFromPredicate;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.getClosestExistingAncestor;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.getPropertyType;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.isReferenceProperty;
import static org.modeshape.jcr.api.JcrConstants.NT_FOLDER;
import static org.slf4j.LoggerFactory.getLogger;

Expand Down Expand Up @@ -181,10 +180,8 @@ public Value createValue(final ValueFactory valueFactory, final RDFNode data, fi
throws RepositoryException {
assert (valueFactory != null);


if (type == UNDEFINED || type == STRING) {
return valueConverter.reverse().convert(data);
} else if (type == REFERENCE || type == WEAKREFERENCE) {
if (type == REFERENCE || type == WEAKREFERENCE ||
(data.isURIResource() && idTranslator.inDomain(data.asResource()))) {
// reference to another node (by path)
if (!data.isURIResource()) {
throw new ValueFormatException("Reference properties can only refer to URIs, not literals");
Expand All @@ -196,6 +193,8 @@ public Value createValue(final ValueFactory valueFactory, final RDFNode data, fi
} catch (final RepositoryRuntimeException e) {
throw new MalformedRdfException("Unable to find referenced node", e);
}
} else if (type == UNDEFINED || type == STRING) {
return valueConverter.reverse().convert(data);
} else if (data.isResource()) {
LOGGER.debug("Using default JCR value creation for RDF resource: {}",
data);
Expand Down Expand Up @@ -270,17 +269,9 @@ public void addProperty(final FedoraResource resource,
+ node.getPath());
}

final String propertyName =
getPropertyNameFromPredicate(node, predicate, namespaces);

if (value.isURIResource()
&& idTranslator.inDomain(value.asResource())
&& !isReferenceProperty(node, propertyName)) {
nodePropertiesTools.addReferencePlaceholders(idTranslator, node, propertyName, value.asResource());
} else {
final Value v = createValue(node, value, propertyName);
nodePropertiesTools.appendOrReplaceNodeProperty(node, propertyName, v);
}
final String propertyName = getPropertyNameFromPredicate(node, predicate, namespaces);
final Value v = createValue(node, value, propertyName);
nodePropertiesTools.appendOrReplaceNodeProperty(node, propertyName, v);
}

protected boolean repositoryHasType(final Session session, final String mixinName) throws RepositoryException {
Expand Down Expand Up @@ -330,17 +321,8 @@ public void removeProperty(final FedoraResource resource,
+ node.getPath());
}

if (objectNode.isURIResource()
&& idTranslator.inDomain(objectNode.asResource())
&& !isReferenceProperty(node, propertyName)) {
nodePropertiesTools.removeReferencePlaceholders(idTranslator,
node,
propertyName,
objectNode.asResource());
} else {
final Value v = createValue(node, objectNode, propertyName);
nodePropertiesTools.removeNodeProperty(node, propertyName, v);
}
final Value v = createValue(node, objectNode, propertyName);
nodePropertiesTools.removeNodeProperty(node, propertyName, v);
}

/**
Expand Down
Expand Up @@ -34,8 +34,6 @@
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
import static org.fcrepo.kernel.modeshape.rdf.JcrRdfTools.getJcrNamespaceForRDFNamespace;
import static org.fcrepo.kernel.modeshape.rdf.JcrRdfTools.getRDFNamespaceForJcrNamespace;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.getReferencePropertyOriginalName;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.isInternalReferenceProperty;
import static org.fcrepo.kernel.api.utils.NamespaceTools.getNamespaceRegistry;
import static org.slf4j.LoggerFactory.getLogger;

Expand All @@ -56,17 +54,7 @@ protected Property doForward(final javax.jcr.Property property) {
if (property instanceof Namespaced) {
final Namespaced nsProperty = (Namespaced) property;
final String uri = nsProperty.getNamespaceURI();
final String localName = nsProperty.getLocalName();
final String rdfLocalName;

if (isInternalReferenceProperty.test(property)) {
rdfLocalName = getReferencePropertyOriginalName(localName);
} else {
rdfLocalName = localName;
}
return createProperty(
getRDFNamespaceForJcrNamespace(uri),
rdfLocalName);
return createProperty(getRDFNamespaceForJcrNamespace(uri), nsProperty.getLocalName());
}
return createProperty(property.getName());
} catch (final RepositoryException e) {
Expand Down
Expand Up @@ -49,7 +49,6 @@
import static org.fcrepo.kernel.api.utils.UncheckedFunction.uncheck;
import static org.fcrepo.kernel.modeshape.identifiers.NodeResourceConverter.nodeConverter;
import static org.fcrepo.kernel.modeshape.rdf.converters.PropertyConverter.getPropertyNameFromPredicate;
import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.getReferencePropertyName;
import static org.slf4j.LoggerFactory.getLogger;

/**
Expand Down Expand Up @@ -128,16 +127,12 @@ private Iterator<Triple> memberRelations(final FedoraResource container) throws
if (insertedContainerProperty.equals(MEMBER_SUBJECT.getURI())) {
return singletonIterator(create(subject(), memberRelation, childSubject));
}
String insertedContentProperty = getPropertyNameFromPredicate(resource().getNode(),
final String insertedContentProperty = getPropertyNameFromPredicate(resource().getNode(),
createResource(insertedContainerProperty), null);

if (child.hasProperty(insertedContentProperty)) {
// do nothing, insertedContentProperty is good

} else if (child.hasProperty(getReferencePropertyName(insertedContentProperty))) {
// The insertedContentProperty is a pseudo reference property
insertedContentProperty = getReferencePropertyName(insertedContentProperty);

} else {
// No property found!
return emptyIterator();
Expand Down
Expand Up @@ -17,11 +17,16 @@

import static org.fcrepo.kernel.api.FedoraJcrTypes.FEDORA_CONTAINER;
import static org.fcrepo.kernel.api.FedoraJcrTypes.FEDORA_RESOURCE;
import static org.fcrepo.kernel.api.FedoraJcrTypes.JCR_LASTMODIFIED;
import static org.fcrepo.kernel.api.FedoraJcrTypes.LDP_DIRECT_CONTAINER;
import static org.fcrepo.kernel.api.FedoraJcrTypes.LDP_INDIRECT_CONTAINER;
import static org.fcrepo.kernel.api.FedoraJcrTypes.LDP_MEMBER_RESOURCE;
import static org.fcrepo.kernel.modeshape.ContainerImpl.hasMixin;
import static org.modeshape.jcr.api.JcrConstants.JCR_CONTENT;
import static org.modeshape.jcr.api.JcrConstants.NT_FOLDER;
import static org.slf4j.LoggerFactory.getLogger;

import java.util.Calendar;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
Expand Down Expand Up @@ -61,6 +66,13 @@ public Container findOrCreate(final Session session, final String path) {

if (node.isNew()) {
initializeNewObjectProperties(node);

// update membershipResources for Direct/Indirect Containers
if (node.getParent().isNodeType(LDP_DIRECT_CONTAINER) ||
node.getParent().isNodeType(LDP_INDIRECT_CONTAINER)) {
final Node mr = node.getParent().getProperty(LDP_MEMBER_RESOURCE).getNode();
mr.setProperty(JCR_LASTMODIFIED, Calendar.getInstance());
}
}

return new ContainerImpl(node);
Expand Down
Expand Up @@ -49,8 +49,6 @@
*/
public abstract class FedoraTypesUtils implements FedoraJcrTypes {

public static final String REFERENCE_PROPERTY_SUFFIX = "_ref";

private static final Logger LOGGER = getLogger(FedoraTypesUtils.class);

/**
Expand Down Expand Up @@ -82,13 +80,6 @@ public abstract class FedoraTypesUtils implements FedoraJcrTypes {
*/
public static Predicate<Node> isSkolemNode = new AnyTypesPredicate(FEDORA_SKOLEM);

/**
* Check if a property is a reference property.
*/
public static Predicate<Property> isInternalReferenceProperty = uncheck(p -> (p.getType() == REFERENCE ||
p.getType() == WEAKREFERENCE) &&
p.getName().endsWith(REFERENCE_PROPERTY_SUFFIX));

/**
* Check whether a property is protected (ie, cannot be modified directly) but
* is not one we've explicitly chosen to include.
Expand Down Expand Up @@ -206,25 +197,6 @@ public static Optional<PropertyDefinition> getDefinitionForPropertyName(final No
.filter(sameName).findFirst();
}

/**
* When we add certain URI properties, we also want to leave a reference node
* @param propertyName the property name
* @return property name as a reference
*/
public static String getReferencePropertyName(final String propertyName) {
return propertyName + REFERENCE_PROPERTY_SUFFIX;
}

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

/**
* Check if a property definition is a reference property
* @param node the given node
Expand Down

0 comments on commit f152b17

Please sign in to comment.