Skip to content

Commit

Permalink
add a way for modules to inject triples into the describe responses f…
Browse files Browse the repository at this point in the history
…or a resource (to add e.g. HATEOS responsess)
  • Loading branch information
cbeer committed May 30, 2013
1 parent dfd4c12 commit 4f78eb8
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 15 deletions.
6 changes: 6 additions & 0 deletions fcrepo-http-api/pom.xml
Expand Up @@ -26,6 +26,12 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.fcrepo</groupId>
<artifactId>fcrepo-kernel</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Apache Velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
Expand Down
Expand Up @@ -95,7 +95,11 @@ public Dataset describe(@PathParam("path")
throw new WebApplicationException(builder.cacheControl(cc)
.lastModified(date).build());
}
return resource.getPropertiesDataset(new HttpGraphSubjects(FedoraNodes.class, uriInfo));
final HttpGraphSubjects subjects = new HttpGraphSubjects(FedoraNodes.class, uriInfo);
final Dataset propertiesDataset = resource.getPropertiesDataset(subjects);
addResponseInformationToDataset(resource, propertiesDataset, uriInfo, subjects);

return propertiesDataset;

} finally {
session.logout();
Expand Down
62 changes: 62 additions & 0 deletions fcrepo-http-api/src/main/java/org/fcrepo/url/HttpApiResources.java
@@ -0,0 +1,62 @@
package org.fcrepo.url;

import com.google.common.collect.ImmutableBiMap;
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 org.fcrepo.FedoraResource;
import org.fcrepo.api.FedoraExport;
import org.fcrepo.api.FedoraFieldSearch;
import org.fcrepo.api.FedoraFixity;
import org.fcrepo.api.FedoraSitemap;
import org.fcrepo.api.FedoraTransactions;
import org.fcrepo.api.FedoraVersions;
import org.fcrepo.api.rdf.UriAwareResourceModelFactory;
import org.fcrepo.api.repository.FedoraRepositoryNamespaces;
import org.fcrepo.rdf.GraphSubjects;
import org.fcrepo.serialization.FedoraObjectSerializer;
import org.springframework.stereotype.Component;

import javax.jcr.RepositoryException;
import javax.ws.rs.core.UriInfo;

import java.util.Map;

@Component
public class HttpApiResources implements UriAwareResourceModelFactory {

@javax.annotation.Resource
protected Map<String, FedoraObjectSerializer> serializers;

@Override
public Model createModelForResource(FedoraResource resource, UriInfo uriInfo, GraphSubjects graphSubjects) throws RepositoryException {

final Model model = ModelFactory.createDefaultModel();
final Resource s = graphSubjects.getGraphSubject(resource.getNode());

if (resource.getNode().getPrimaryNodeType().isNodeType("mode:root")) {
model.add(s, model.createProperty("http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#link-type-search"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraFieldSearch.class).build().toASCIIString()));
model.add(s, model.createProperty("http://microformats.org/wiki/rel-sitemap"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraSitemap.class).build().toASCIIString()));
model.add(s, model.createProperty("info:fedora/hasTransactionProvider"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraTransactions.class).build().toASCIIString()));
model.add(s, model.createProperty("info:fedora/hasNamespaces"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraRepositoryNamespaces.class).build().toASCIIString()));

} else {

for (String key : serializers.keySet()) {
final Map<String, String> pathMap = ImmutableBiMap.of("path", resource.getPath().substring(1), "format", key);
model.add(s, model.createProperty("info:fedora/exportsAs"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraExport.class).buildFromMap(pathMap).toASCIIString()));
}

final Map<String, String> pathMap = ImmutableBiMap.of("path", resource.getPath().substring(1));
model.add(s, model.createProperty("info:fedora/hasVersions"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraVersions.class).buildFromMap(pathMap).toASCIIString()));

}

if (resource.hasContent()) {
final Map<String, String> pathMap = ImmutableBiMap.of("path", resource.getPath().substring(1));
model.add(s, model.createProperty("info:fedora/runFixityCheck"), model.createResource(uriInfo.getBaseUriBuilder().path(FedoraFixity.class).buildFromMap(pathMap).toASCIIString()));
}

return model;
}
}
2 changes: 1 addition & 1 deletion fcrepo-http-api/src/test/resources/spring-test/rest.xml
Expand Up @@ -14,7 +14,7 @@
<context:annotation-config/>

<context:component-scan
base-package="org.fcrepo.api, org.fcrepo.serialization, org.fcrepo.responses, org.fcrepo.exceptionhandlers"/>
base-package="org.fcrepo.api, org.fcrepo.serialization, org.fcrepo.responses, org.fcrepo.exceptionhandlers, org.fcrepo.url"/>

<util:map id="serializers" key-type="java.lang.String" map-class="java.util.HashMap"
value-type="org.fcrepo.serialization.FedoraObjectSerializer">
Expand Down
14 changes: 12 additions & 2 deletions fcrepo-http-commons/src/main/java/org/fcrepo/AbstractResource.java
Expand Up @@ -2,7 +2,6 @@
package org.fcrepo;

import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.Response.noContent;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
Expand All @@ -17,15 +16,17 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;

import com.hp.hpl.jena.query.Dataset;
import org.apache.commons.io.IOUtils;
import org.apache.jena.riot.WebContent;
import org.fcrepo.api.rdf.HttpTripleUtil;
import org.fcrepo.api.rdf.HttpGraphSubjects;
import org.fcrepo.exception.InvalidChecksumException;
import org.fcrepo.identifiers.PidMinter;
import org.fcrepo.rdf.GraphSubjects;
import org.fcrepo.services.DatastreamService;
import org.fcrepo.services.NodeService;
import org.fcrepo.services.ObjectService;
Expand Down Expand Up @@ -79,6 +80,9 @@ public abstract class AbstractResource {
@Autowired
protected DatastreamService datastreamService;

@Autowired(required=false)
private HttpTripleUtil httpTripleUtil;

/**
* A resource that can mint new Fedora PIDs.
*/
Expand Down Expand Up @@ -183,6 +187,12 @@ protected FedoraResource createObjectOrDatastreamFromRequestContent(
return result;
}

protected void addResponseInformationToDataset(final FedoraResource resource, final Dataset dataset, final UriInfo uriInfo, GraphSubjects subjects) throws RepositoryException {
if (httpTripleUtil != null) {
httpTripleUtil.addHttpComponentModelsForResource(dataset, resource, uriInfo, subjects);
}
}

/**
* A testing convenience setter for otherwise injected resources
* @param repo
Expand Down
@@ -0,0 +1,58 @@
package org.fcrepo.api.rdf;

import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model;
import org.fcrepo.FedoraResource;
import org.fcrepo.rdf.GraphSubjects;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.jcr.RepositoryException;
import javax.ws.rs.core.UriInfo;
import java.util.Map;

import static org.slf4j.LoggerFactory.getLogger;

/**
* Utility for injecting HTTP-contextual data into a Dataset
*/
@Component
public class HttpTripleUtil implements ApplicationContextAware {
private static final Logger LOGGER = getLogger(HttpTripleUtil.class);
private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

/**
* Add additional models to the RDF dataset for the given resource
* @param dataset the source dataset we'll add named models to
* @param resource the FedoraResource in question
* @param uriInfo a JAX-RS UriInfo object to build URIs to resources
* @param graphSubjects
* @throws RepositoryException
*/
public void addHttpComponentModelsForResource(Dataset dataset, FedoraResource resource, UriInfo uriInfo, GraphSubjects graphSubjects) throws RepositoryException {

LOGGER.debug("Adding additional HTTP context triples to dataset");
for (final Map.Entry<String, UriAwareResourceModelFactory> e : getUriAwareTripleFactories().entrySet()) {
final String beanName = e.getKey();
final UriAwareResourceModelFactory uriAwareResourceModelFactory = e.getValue();
LOGGER.debug("Adding response information using {}", beanName);

final Model m = uriAwareResourceModelFactory.createModelForResource(resource, uriInfo, graphSubjects);
dataset.addNamedModel(beanName, m);
}

}

private Map<String, UriAwareResourceModelFactory> getUriAwareTripleFactories() {
return applicationContext.getBeansOfType(UriAwareResourceModelFactory.class);

}
}
@@ -0,0 +1,16 @@
package org.fcrepo.api.rdf;

import com.hp.hpl.jena.rdf.model.Model;
import org.fcrepo.FedoraResource;
import org.fcrepo.rdf.GraphSubjects;

import javax.jcr.RepositoryException;
import javax.ws.rs.core.UriInfo;

/**
* Helper to generate an RDF model for a FedoraResource that (likely) creates relations
* from our resource to other HTTP components
*/
public interface UriAwareResourceModelFactory {
Model createModelForResource(final FedoraResource resource, final UriInfo uriInfo, GraphSubjects graphSubjects) throws RepositoryException;
}
Expand Up @@ -6,6 +6,7 @@

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
Expand All @@ -21,7 +22,7 @@ public class GraphStoreStreamingOutput implements StreamingOutput {
private static final Logger LOGGER =
getLogger(GraphStoreStreamingOutput.class);

private final Model model;
private final Dataset dataset;

private final String format;

Expand All @@ -33,14 +34,7 @@ public GraphStoreStreamingOutput(final GraphStore graphStore,

public GraphStoreStreamingOutput(final Dataset dataset,
final MediaType mediaType) {
this.model = dataset.getDefaultModel();
format =
contentTypeToLang(mediaType.toString()).getName().toUpperCase();
}

public GraphStoreStreamingOutput(final Model model,
final MediaType mediaType) {
this.model = model;
this.dataset = dataset;
format =
contentTypeToLang(mediaType.toString()).getName().toUpperCase();
}
Expand All @@ -49,7 +43,14 @@ public GraphStoreStreamingOutput(final Model model,
public void write(final OutputStream out) throws IOException,
WebApplicationException {
LOGGER.debug("Serializing graph model as {}", format);
model.write(out, format);
final Iterator<String> iterator = dataset.listNames();

while (iterator.hasNext()) {
final Model model = dataset.getNamedModel(iterator.next());
model.write(out, format);
}

dataset.getDefaultModel().write(out, format);

}

Expand Down
@@ -0,0 +1,60 @@
package org.fcrepo.api.rdf;

import com.google.common.collect.ImmutableBiMap;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.query.DatasetFactory;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import org.fcrepo.FedoraResource;
import org.fcrepo.rdf.GraphSubjects;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

import javax.jcr.RepositoryException;
import javax.ws.rs.core.UriInfo;

import java.util.Map;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class HttpTripleUtilTest {

private HttpTripleUtil testObj;
private Dataset dataset;
private UriInfo mockUriInfo;
private GraphSubjects mockSubjects;
private ApplicationContext mockContext;

@Before
public void setUp() {
mockContext = mock(ApplicationContext.class);
testObj = new HttpTripleUtil();
testObj.setApplicationContext(mockContext);

dataset = DatasetFactory.create(ModelFactory.createDefaultModel());
mockUriInfo = mock(UriInfo.class);
mockSubjects = mock(GraphSubjects.class);


}

@Test
public void shouldAddTriplesFromRegisteredBeans() throws RepositoryException {
final FedoraResource mockResource = mock(FedoraResource.class);

UriAwareResourceModelFactory mockBean1 = mock(UriAwareResourceModelFactory.class);
UriAwareResourceModelFactory mockBean2 = mock(UriAwareResourceModelFactory.class);
Map<String, UriAwareResourceModelFactory> mockBeans = ImmutableBiMap.of("doesnt", mockBean1, "matter", mockBean2);
when(mockContext.getBeansOfType(UriAwareResourceModelFactory.class)).thenReturn(mockBeans);
when(mockBean1.createModelForResource(eq(mockResource), eq(mockUriInfo), eq(mockSubjects))).thenReturn(ModelFactory.createDefaultModel());
when(mockBean2.createModelForResource(eq(mockResource), eq(mockUriInfo), eq(mockSubjects))).thenReturn(ModelFactory.createDefaultModel());

testObj.addHttpComponentModelsForResource(dataset, mockResource, mockUriInfo, mockSubjects);
verify(mockBean1).createModelForResource(eq(mockResource), eq(mockUriInfo), eq(mockSubjects));
verify(mockBean2).createModelForResource(eq(mockResource), eq(mockUriInfo), eq(mockSubjects));
}
}
6 changes: 6 additions & 0 deletions fcrepo-jcr/pom.xml
Expand Up @@ -17,6 +17,12 @@
<artifactId>modeshape-jcr</artifactId>
</dependency>


<dependency>
<groupId>org.modeshape</groupId>
<artifactId>modeshape-common</artifactId>
</dependency>

<dependency>
<groupId>org.jboss.jbossts</groupId>
<artifactId>jbossjta</artifactId>
Expand Down
26 changes: 26 additions & 0 deletions fcrepo-rss/src/main/java/org/fcrepo/syndication/RssResources.java
@@ -0,0 +1,26 @@
package org.fcrepo.syndication;

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 org.fcrepo.FedoraResource;
import org.fcrepo.api.rdf.UriAwareResourceModelFactory;
import org.fcrepo.rdf.GraphSubjects;

import javax.jcr.RepositoryException;
import javax.ws.rs.core.UriInfo;

public class RssResources implements UriAwareResourceModelFactory {
@Override
public Model createModelForResource(FedoraResource resource, UriInfo uriInfo, GraphSubjects graphSubjects) throws RepositoryException {

final Model model = ModelFactory.createDefaultModel();
final Resource s = graphSubjects.getGraphSubject(resource.getNode());

if (resource.getNode().getPrimaryNodeType().isNodeType("mode:root")) {
model.add(s, model.createProperty("http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#link-type-feed"), model.createResource(uriInfo.getBaseUriBuilder().path(RSSPublisher.class).build().toASCIIString()));
}

return model;
}
}
2 changes: 1 addition & 1 deletion fcrepo-webapp/src/main/resources/spring/rest.xml
Expand Up @@ -17,7 +17,7 @@
<bean class="org.fcrepo.identifiers.UUIDPidMinter"/>
<bean class="org.fcrepo.session.SessionFactory"/>

<context:component-scan base-package="org.modeshape.web.jcr.rest, org.fcrepo.api, org.fcrepo.serialization, org.fcrepo.responses, org.fcrepo.exceptionhandlers, org.fcrepo.audit"/>
<context:component-scan base-package="org.modeshape.web.jcr.rest, org.fcrepo.api, org.fcrepo.serialization, org.fcrepo.responses, org.fcrepo.exceptionhandlers, org.fcrepo.audit, org.fcrepo.url"/>

<util:map id="serializers" key-type="java.lang.String" map-class="java.util.HashMap"
value-type="org.fcrepo.serialization.FedoraObjectSerializer">
Expand Down

0 comments on commit 4f78eb8

Please sign in to comment.