Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add the annotation @htmltemplate to allow JAX-B methods to hint at
the appropriate HTML template for the Dataset -> HTML provider (e.g.
when no node-backed dataset is available)
  • Loading branch information
cbeer committed Jun 10, 2013
1 parent 1a76a7b commit 907ddd2
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 146 deletions.
7 changes: 0 additions & 7 deletions fcrepo-http-api/pom.xml
Expand Up @@ -27,13 +27,6 @@
<version>${project.version}</version>
</dependency>

<!-- Apache Velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
Expand Down
25 changes: 13 additions & 12 deletions fcrepo-http-api/src/main/java/org/fcrepo/api/FedoraFieldSearch.java
Expand Up @@ -29,7 +29,7 @@
import org.fcrepo.AbstractResource;
import org.fcrepo.RdfLexicon;
import org.fcrepo.api.rdf.HttpGraphSubjects;
import org.fcrepo.responses.VelocityViewer;
import org.fcrepo.responses.HtmlTemplate;
import org.fcrepo.utils.FedoraJcrTypes;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
Expand All @@ -55,6 +55,7 @@ public class FedoraFieldSearch extends AbstractResource implements

@GET
@Timed
@HtmlTemplate("search:results")
@Produces({TEXT_HTML})
public Dataset searchSubmitHtml(@QueryParam("q")
final String terms, @QueryParam("offset")
Expand All @@ -65,13 +66,7 @@ public Dataset searchSubmitHtml(@QueryParam("q")
final Request request, @Context
final UriInfo uriInfo) throws RepositoryException {


if (terms == null) {
LOGGER.trace("Received search request, but terms was empty. Aborting.");
throw new WebApplicationException(Response.status(Response.Status.OK).entity(new VelocityViewer().getSearchForm()).build());
}

return searchSubmitRdf(terms, offset, limit, request, uriInfo);
return getSearchDataset(terms, offset, limit, uriInfo);
}

@GET
Expand All @@ -94,23 +89,29 @@ public Dataset searchSubmitRdf(@QueryParam("q")
"q parameter is mandatory").build());
}

return getSearchDataset(terms, offset, limit, uriInfo);
}

private Dataset getSearchDataset(final String terms, final long offset, final int limit, final UriInfo uriInfo) throws RepositoryException {
final Session session = getAuthenticatedSession();
try {
LOGGER.debug("Received search request with search terms {}, offset {}, and limit {}", terms, offset, limit);

final Resource searchResult =
ResourceFactory.createResource(uriInfo.getRequestUri()
.toASCIIString());
.toASCIIString());

final Dataset dataset =
nodeService.searchRepository(new HttpGraphSubjects(
FedoraNodes.class, uriInfo), searchResult, session,
terms, limit, offset);

final Model searchModel = ModelFactory.createDefaultModel();
Map<String, ?> pathMap = ImmutableBiMap.of("q", terms, "offset", offset + limit, "limit", limit);
searchModel.add(searchResult, RdfLexicon.SEARCH_NEXT_PAGE, searchModel.createResource(uriInfo.getRequestUriBuilder().path(FedoraFieldSearch.class).buildFromMap(pathMap).toString()));
dataset.addNamedModel("search-pagination", searchModel);
if (terms != null && dataset.getDefaultModel().contains(searchResult, RdfLexicon.SEARCH_HAS_MORE, searchModel.createTypedLiteral(true))) {
Map<String, ?> pathMap = ImmutableBiMap.of("q", terms, "offset", offset + limit, "limit", limit);
searchModel.add(searchResult, RdfLexicon.SEARCH_NEXT_PAGE, searchModel.createResource(uriInfo.getBaseUriBuilder().path(FedoraFieldSearch.class).buildFromMap(pathMap).toString()));
dataset.addNamedModel("search-pagination", searchModel);
}

return dataset;

Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.fcrepo.AbstractResource;
import org.fcrepo.responses.HtmlTemplate;
import org.springframework.stereotype.Component;

import com.codahale.metrics.annotation.Timed;
Expand Down Expand Up @@ -81,6 +82,7 @@ public Response updateNamespaces(final InputStream requestBodyStream)
@Timed
@Produces({N3, N3_ALT1, N3_ALT2, TURTLE, RDF_XML, RDF_JSON,
NTRIPLES, TEXT_HTML})
@HtmlTemplate("jcr:namespaces")
public Dataset getNamespaces() throws RepositoryException,
IOException {

Expand Down
Expand Up @@ -38,6 +38,22 @@ public void testSearchHtml() throws Exception {

}

@Test
public void testSearchResultsHtml() throws Exception {
final HttpGet method = new HttpGet(serverAddress + "fcr:search");
method.setHeader("Accept", "text/html");
URI uri = new URIBuilder(method.getURI()).addParameter("q", "testobj").addParameter("offset", "0").addParameter("limit", "1").build();

method.setURI(uri);

HttpResponse resp = execute(method);

String content = IOUtils.toString(resp.getEntity().getContent());
logger.debug("Got search results: {}", content);
assertEquals(200, resp.getStatusLine().getStatusCode());

}

@Test
public void testSearchRdf() throws Exception {
/* first post an object which can be used for the search */
Expand Down
Expand Up @@ -19,6 +19,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;

Expand All @@ -32,6 +33,7 @@
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import com.google.common.collect.ImmutableList;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
Expand Down Expand Up @@ -129,8 +131,18 @@ void init() throws IOException, RepositoryException {
* available for that kind of node
*/
}
templatesMap = templatesMapBuilder.build();

}

List<String> otherTemplates = ImmutableList.of("search:results", "jcr:namespaces");

for ( String key : otherTemplates) {
final Template template = velocity.getTemplate(templatesLocation + "/" + key.replace(':', '-') + templateFilenameExtension);
templatesMapBuilder.put(key, template);
}

templatesMap = templatesMapBuilder.build();

} finally {
session.logout();
}
Expand All @@ -154,12 +166,7 @@ public void writeTo(final Dataset rdf, final Class<?> type,
httpHeaders.put("Content-type", of((Object) TEXT_HTML));
setCachingHeaders(httpHeaders, rdf);

LOGGER.trace("Attempting to discover the primary type of the node for the resource in question...");
final String nodeType =
getFirstValueForPredicate(rdf, subject, primaryTypePredicate);
LOGGER.debug("Found primary node type: {}", nodeType);
final Template nodeTypeTemplate = templatesMap.get(nodeType);
LOGGER.debug("Choosing template: {}", nodeTypeTemplate.getName());
final Template nodeTypeTemplate = getTemplate(rdf, subject, annotations);

final Context context = new VelocityContext();
context.put("rdf", rdf.asDatasetGraph());
Expand All @@ -175,6 +182,31 @@ public void writeTo(final Dataset rdf, final Class<?> type,

}

private Template getTemplate(final Dataset rdf, final Node subject, final Annotation[] annotations) {
Template template = null;

for (Annotation a : annotations) {
if (a instanceof HtmlTemplate) {
final String value = ((HtmlTemplate) a).value();
LOGGER.debug("Found an HtmlTemplate annotation {}", value);
template = templatesMap.get(value);
break;
}
}

if (template == null) {
LOGGER.trace("Attempting to discover the primary type of the node for the resource in question...");
final String nodeType =
getFirstValueForPredicate(rdf, subject, primaryTypePredicate);

LOGGER.debug("Found primary node type: {}", nodeType);
template = templatesMap.get(nodeType);
}

LOGGER.debug("Choosing template: {}", template.getName());
return template;
}

public void setTemplatesMap(final Map<String, Template> templatesMap) {
this.templatesMap = templatesMap;
}
Expand Down
@@ -0,0 +1,12 @@
package org.fcrepo.responses;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HtmlTemplate {
String value();
}

This file was deleted.

19 changes: 19 additions & 0 deletions fcrepo-http-commons/src/main/resources/views/jcr-namespaces.vsl
@@ -0,0 +1,19 @@
#* @vtlvariable name="rdf" type="com.hp.hpl.jena.sparql.core.DatasetGraph" *#
#* @vtlvariable name="subjects" type="com.hp.hpl.jena.rdf.model.ResIterator" *#
#* @vtlvariable name="nodeany" type="com.hp.hpl.jena.graph.Node" *#
#* @vtlvariable name="topic" type="com.hp.hpl.jena.graph.Node" *#
#parse("views/common.vsl")
<html>
<head>
<title>registered namespaces</title>
#parse("views/common-header.vsl")
</head>
<body>
<h1>namespaces</h1>

## output other nodes
#foreach($subject in $subjects)
#triples($subject.asNode())
#end
</body>
</html>
33 changes: 33 additions & 0 deletions fcrepo-http-commons/src/main/resources/views/search-results.vsl
@@ -0,0 +1,33 @@
#* @vtlvariable name="rdf" type="com.hp.hpl.jena.sparql.core.DatasetGraph" *#
#* @vtlvariable name="subjects" type="com.hp.hpl.jena.rdf.model.ResIterator" *#
#* @vtlvariable name="nodeany" type="com.hp.hpl.jena.graph.Node" *#
#* @vtlvariable name="topic" type="com.hp.hpl.jena.graph.Node" *#
#parse("views/common.vsl")
<html>
<head>
<title>Search results</title>
#parse("views/common-header.vsl")
</head>
<body>
<h1>Search Results: $topic</h1>


## output actions
<div class="actions">
<form method="GET">
<input type="text" name="q" />
<input type="submit" value="search"/>
</form>
</div>

## output triples for the topic node
#triples($topic)

## output other nodes
#foreach($subject in $subjects)
#if( $subject != $topic )
#triples($subject.asNode())
#end
#end
</body>
</html>
Expand Up @@ -12,10 +12,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.WebApplicationException;
Expand Down Expand Up @@ -91,11 +93,39 @@ public Object answer(final InvocationOnMock invocation) {
}).when(mockTemplate).merge(Mockito.isA(Context.class),
Mockito.isA(Writer.class));
htmlProvider.setTemplatesMap(of("nt:file", mockTemplate));
htmlProvider.writeTo(testData, Dataset.class, mock(Type.class), null,
htmlProvider.writeTo(testData, Dataset.class, mock(Type.class), new Annotation[] {},
MediaType.valueOf("text/html"),
(MultivaluedMap) new MultivaluedMapImpl(), outStream);
final byte[] results = outStream.toByteArray();
assertTrue("Got no output from serialization!", results.length > 0);

}


@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void testWriteToWithAnnotation() throws WebApplicationException,
IllegalArgumentException, IOException {
final Template mockTemplate = mock(Template.class);
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();

Mockito.doAnswer(new Answer<Object>() {

@Override
public Object answer(final InvocationOnMock invocation) {
outStream.write("abcdefighijk".getBytes(), 0, 10);
return "I am pretending to merge a template for you.";
}
}).when(mockTemplate).merge(Mockito.isA(Context.class),
Mockito.isA(Writer.class));
htmlProvider.setTemplatesMap(of("some:file", mockTemplate));
HtmlTemplate mockAnnotation = mock(HtmlTemplate.class);
when(mockAnnotation.value()).thenReturn("some:file");
htmlProvider.writeTo(testData, Dataset.class, mock(Type.class), new Annotation[] { mockAnnotation },
MediaType.valueOf("text/html"),
(MultivaluedMap) new MultivaluedMapImpl(), outStream);
final byte[] results = outStream.toByteArray();
assertTrue("Got no output from serialization!", results.length > 0);

}
}

0 comments on commit 907ddd2

Please sign in to comment.