Skip to content

Commit

Permalink
All tests pass, proper output displayed in browser/via curl
Browse files Browse the repository at this point in the history
  • Loading branch information
ajs6f committed May 19, 2013
1 parent 0508b35 commit c71c6ba
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 60 deletions.
Expand Up @@ -2,6 +2,7 @@
package org.fcrepo.responses;

import static org.apache.jena.riot.WebContent.contentTypeToLang;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
import java.io.OutputStream;
Expand All @@ -11,14 +12,12 @@
import javax.ws.rs.core.StreamingOutput;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.hpl.jena.update.GraphStore;

public class GraphStoreStreamingOutput implements StreamingOutput {

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

private final GraphStore m_graphStore;

Expand All @@ -28,8 +27,7 @@ public GraphStoreStreamingOutput(final GraphStore graphStore,
final MediaType mediaType) {
m_graphStore = graphStore;
m_format =
contentTypeToLang(mediaType.toString()).getName()
.toUpperCase();
contentTypeToLang(mediaType.toString()).getName().toUpperCase();
}

@Override
Expand Down
Expand Up @@ -14,6 +14,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
Expand Down Expand Up @@ -41,67 +43,95 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.sparql.core.Quad;

/**
* A simple JAX-RS Entity Provider that can accept RDF datasets
* that represent Fedora resources and merge them into templates
* chosen based on the primary node type of the backing JCR node
* for that Fedora resource.
*
* @author ajs6f
* @date May 19, 2013
*/
@Provider
@Component
public class HtmlProvider implements MessageBodyWriter<Dataset> {

@Autowired
SessionFactory sessionFactory;

static Node primaryTypePredicate =
/**
* The RDF predicate that will indicate the primary node type.
*/
public static Node primaryTypePredicate =
createURI(getRDFNamespaceForJcrNamespace("http://www.jcp.org/jcr/1.0") +
"primaryType");

VelocityEngine velocity = new VelocityEngine();
protected VelocityEngine velocity = new VelocityEngine();

static final String templatesLocation = "/views";
/**
* Location in the classpath where Velocity templates are to be found.
*/
public static final String templatesLocation = "/views";

Map<String, Template> templatesMap;
/**
* A map from String names for primary node types to the
* Velocity templates that should be used for those node types.
*/
protected Map<String, Template> templatesMap;

static final String templateFilenameExtension = ".vsl";
public static final String templateFilenameExtension = ".vsl";

private static final Logger logger = getLogger(HtmlProvider.class);

public static final String velocityPropertiesLocation =
"/velocity.properties";

@PostConstruct
void init() throws IOException, RepositoryException {

logger.trace("Velocity engine initializing...");
final Properties properties = new Properties();
final URL propertiesUrl =
getClass().getResource("/velocity.properties");
getClass().getResource(velocityPropertiesLocation);
logger.debug("Using Velocity configuration from {}", propertiesUrl);
try (final InputStream propertiesStream = propertiesUrl.openStream()) {
properties.load(propertiesStream);
}
velocity.init(properties);
logger.trace("Velocity engine initialized.");

logger.trace("Assembling a map of node primary types -> templates...");
final Builder<String, Template> templatesMapBuilder = builder();
final Session session = sessionFactory.getSession();
ImmutableMap.Builder<String, Template> templatesMapBuilder = builder();
try {
// we search all of the possible node primary types
for (final NodeTypeIterator primaryNodeTypes =
session.getWorkspace().getNodeTypeManager()
.getPrimaryNodeTypes(); primaryNodeTypes.hasNext();) {
final String primaryNodeTypeName =
primaryNodeTypes.nextNodeType().getName();
// for each node primary type, we try to find a template
final String templateLocation =
templatesLocation + "/" + primaryNodeTypeName +
templateFilenameExtension;
try {
final String templateLocation =
templatesLocation + "/" + primaryNodeTypeName +
templateFilenameExtension;
final Template template =
velocity.getTemplate(templateLocation);
template.setName(primaryNodeTypeName);
template.setName(templateLocation);
logger.debug("Found template: {}", templateLocation);
templatesMapBuilder.put(primaryNodeTypeName, template);
logger.debug(
"which we will use for nodes with primary type: {}",
primaryNodeTypeName);
} catch (ResourceNotFoundException e) {
} catch (final ResourceNotFoundException e) {
logger.debug(
"Didn't find template for nodes with primary type: {}",
primaryNodeTypeName);
"Didn't find template for nodes with primary type: {} in location: {}",
primaryNodeTypeName, templateLocation);
/*
* we don't care-- just means we don't have an HTML
* representation
Expand All @@ -110,10 +140,11 @@ void init() throws IOException, RepositoryException {
}
templatesMap = templatesMapBuilder.build();
}

} finally {
session.logout();
}
logger.trace("Assembled template map.");
logger.trace("HtmlProvider initialization complete.");
}

@Override
Expand All @@ -129,28 +160,38 @@ public void writeTo(final Dataset rdf, final Class<?> type,
httpHeaders.put("Content-type", of((Object) TEXT_HTML));

final Context context = new VelocityContext();
context.put("rdf", rdf);

for (final Iterator<Quad> typeStatements =
rdf.asDatasetGraph().find(ANY, ANY, primaryTypePredicate, ANY); typeStatements
.hasNext();) {
final Quad statement = typeStatements.next();
final String nodeType =
(String) statement.asTriple().getObject().getLiteral()
.getValue();
final Template nodeTypeTemplate = templatesMap.get(nodeType);
// the contract of MessageBodyWriter is not to close the stream
// after writing to it
nodeTypeTemplate.merge(context,
new OutputStreamWriter(entityStream));
context.put("rdf", rdf.asDatasetGraph());

logger.trace("Attempting to discover the primary type of the node for the resource in question.");
final Iterator<Quad> typeStatements =
rdf.asDatasetGraph().find(ANY, ANY, primaryTypePredicate, ANY);
// we'll take the first one we get
final Quad statement = typeStatements.next();
logger.trace("Checking statement: {}", statement);
final String nodeType =
(String) statement.asTriple().getObject().getLiteral()
.getValue();
logger.debug("Found primary node type: {}", nodeType);
final Template nodeTypeTemplate = templatesMap.get(nodeType);
logger.debug("Choosing template: {}", nodeTypeTemplate.getName());
try (final Writer loggingWriter = new StringWriter()) {
nodeTypeTemplate.merge(context, loggingWriter);
logger.trace("Created HTML response: \n{}", loggingWriter);
}
// the contract of MessageBodyWriter<T> is _not_ to close the stream
// after writing to it
final Writer outWriter = new OutputStreamWriter(entityStream);
nodeTypeTemplate.merge(context, outWriter);
outWriter.flush();

}

@Override
public boolean isWriteable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return mediaType.equals(TEXT_HTML_TYPE) &&
Dataset.class.isAssignableFrom(type);
(Dataset.class.isAssignableFrom(type) || Dataset.class
.isAssignableFrom(genericType.getClass()));
}

@Override
Expand Down
Expand Up @@ -2,28 +2,20 @@
package org.fcrepo.responses;

import static com.google.common.collect.ImmutableList.of;
import static com.google.common.collect.ImmutableSet.copyOf;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import static org.fcrepo.http.RDFMediaType.N3;
import static org.fcrepo.http.RDFMediaType.N3_ALT1;
import static org.fcrepo.http.RDFMediaType.N3_ALT2;
import static org.fcrepo.http.RDFMediaType.NTRIPLES;
import static org.fcrepo.http.RDFMediaType.RDF_JSON;
import static org.fcrepo.http.RDFMediaType.RDF_XML;
import static org.fcrepo.http.RDFMediaType.TURTLE;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import org.apache.jena.riot.WebContent;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import com.hp.hpl.jena.query.Dataset;
Expand All @@ -33,9 +25,7 @@
@Component
public class RdfProvider implements MessageBodyWriter<Dataset> {

private static final Set<String> rdfMimeTypes = copyOf(new String[] {
TEXT_HTML, N3, N3_ALT1, N3_ALT2, TURTLE, RDF_XML, RDF_JSON,
NTRIPLES});
private static final Logger logger = getLogger(RdfProvider.class);

@Override
public void writeTo(final Dataset rdf, final Class<?> type,
Expand All @@ -45,20 +35,26 @@ public void writeTo(final Dataset rdf, final Class<?> type,
final OutputStream entityStream) throws IOException,
WebApplicationException {

logger.debug("Writing a response for: {} with MIMEtype: {}", rdf,
mediaType);

// add a Content-type header
httpHeaders.put("Content-type", of((Object) mediaType.toString()));

new GraphStoreStreamingOutput(new GraphStoreBasic(rdf), mediaType)
.write(entityStream);

}

@Override
public boolean isWriteable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {

return rdfMimeTypes.contains(mediaType.toString()) &&
Dataset.class.isAssignableFrom(type);
// we can return a result for any MIME type that Jena can serialize
final Boolean appropriateMimeType =
(WebContent.contentTypeToLang(mediaType.toString()) != null);
return appropriateMimeType &&
(Dataset.class.isAssignableFrom(type) || Dataset.class
.isAssignableFrom(genericType.getClass()));
}

@Override
Expand Down
6 changes: 3 additions & 3 deletions fcrepo-http-commons/src/main/resources/views/nt:file.vsl
@@ -1,7 +1,7 @@
<html>
#set($quads = $rdf.asDatasetGraph().find())
#set($quads = $rdf.find())
<head>
<title>nt:folder profile</title>
<title>nt:file profile</title>
</head>

<body>
Expand All @@ -12,4 +12,4 @@
#end
</body>

</htmL>
</html>
4 changes: 2 additions & 2 deletions fcrepo-http-commons/src/main/resources/views/nt:folder.vsl
@@ -1,5 +1,5 @@
<html>
#set($quads = $rdf.asDatasetGraph().find())
#set($quads = $rdf.find())
<head>
<title>nt:folder profile</title>
</head>
Expand All @@ -12,4 +12,4 @@
#end
</body>

</htmL>
</html>
15 changes: 15 additions & 0 deletions fcrepo-http-commons/src/main/resources/views/nt:resource.vsl
@@ -0,0 +1,15 @@
<html>
#set($quads = $rdf.find())
<head>
<title>nt:resource profile</title>
</head>

<body>
#foreach($quad in $quads)
<div>
<span>$quad.asTriple()</span>
</div>
#end
</body>

</html>
2 changes: 1 addition & 1 deletion fcrepo-webapp/src/main/resources/logback.xml
Expand Up @@ -6,7 +6,7 @@
<pattern>%p %d{HH:mm:ss.SSS} \(%c{0}\) %m%n</pattern>
</encoder>
</appender>
<logger name="org.fcrepo" additivity="false" level="DEBUG">
<logger name="org.fcrepo" additivity="false" level="TRACE">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.modeshape" additivity="false" level="INFO">
Expand Down

0 comments on commit c71c6ba

Please sign in to comment.