Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test velocity templates - take 2 #592

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,14 +15,18 @@
*/
package org.fcrepo.http.api.responses;

import static com.google.common.collect.ImmutableMap.builder;
import static com.hp.hpl.jena.graph.Node.ANY;
import static javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML;
import static javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML_TYPE;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.getAllValuesForPredicate;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.getFirstValueForPredicate;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.mixinTypesPredicate;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.primaryTypePredicate;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.ImmutableMap.builder;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
Expand All @@ -33,13 +37,17 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
Expand All @@ -49,8 +57,10 @@
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.rdf.model.Model;
import org.apache.velocity.Template;
Expand Down Expand Up @@ -109,6 +119,27 @@ public class StreamingBaseHtmlProvider implements MessageBodyWriter<RdfStream> {
private static final Logger LOGGER =
getLogger(StreamingBaseHtmlProvider.class);

protected final Predicate<NodeType> acceptWhenTemplateExists = new Predicate<NodeType>() {
@Override
public boolean apply(final NodeType nodeType) {
final String nodeTypeName = nodeType.getName();
if (isBlank(nodeTypeName)) {
return false;
}
return velocity.resourceExists(getTemplateLocation(nodeTypeName));
}
};

protected final Predicate<String> acceptWhenTemplateMapContainsKey = new Predicate<String>() {
@Override
public boolean apply(final String key) {
if (isBlank(key)) {
return false;
}
return templatesMap.containsKey(key);
}
};

@PostConstruct
void init() throws IOException {

Expand All @@ -127,29 +158,28 @@ void init() throws IOException {
final ImmutableMap.Builder<String, Template> templatesMapBuilder = builder();
final Session session = sessionFactory.getInternalSession();
try {
// we search all of the possible node primary types
// we search all of the possible node primary types and mixins
for (final NodeTypeIterator primaryNodeTypes =
session.getWorkspace().getNodeTypeManager()
.getPrimaryNodeTypes(); primaryNodeTypes.hasNext();) {
final NodeType primaryNodeType =
primaryNodeTypes.nextNodeType();
final String primaryNodeTypeName =
primaryNodeTypes.nextNodeType().getName();
// for each node primary type, we try to find a template
final String templateLocation =
templatesLocation + "/" +
primaryNodeTypeName.replace(':', '-') +
templateFilenameExtension;
if (velocity.resourceExists(templateLocation)) {
final Template template =
velocity.getTemplate(templateLocation);
template.setName(templateLocation);
LOGGER.debug("Found template: {}", templateLocation);
templatesMapBuilder.put(primaryNodeTypeName, template);
LOGGER.debug("which we will use for nodes with primary type: {}",
primaryNodeTypeName);
primaryNodeType.getName();

// Create a list of the primary type and all its parents
final List<NodeType> nodeTypesList = new ArrayList<NodeType>();
nodeTypesList.add(primaryNodeType);
nodeTypesList.addAll(Arrays.asList(primaryNodeType.getSupertypes()));

// Find a template that matches the primary type or one of its parents
final NodeType templateMatch = Iterables.find(nodeTypesList, acceptWhenTemplateExists, null);
if (templateMatch != null) {
addTemplate(primaryNodeTypeName, templateMatch.getName(), templatesMapBuilder);
} else {
// No HTML representation available for that kind of node
LOGGER.debug("Didn't find template for nodes with primary type: {} in location: {}",
primaryNodeTypeName, templateLocation);
LOGGER.debug("Didn't find template for nodes with primary type or its parents: {} in location: {}",
primaryNodeTypeName, templatesLocation);
}
}

Expand All @@ -158,9 +188,7 @@ void init() throws IOException {

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

Expand Down Expand Up @@ -235,6 +263,23 @@ private Template getTemplate(final Model rdf, final Node subject,
}
}

if (template == null) {
LOGGER.trace("Attempting to discover the mixin types of the node for the resource in question...");
final Iterator<String> mixinTypes =
getAllValuesForPredicate(rdf, subject,
mixinTypesPredicate);

LOGGER.debug("Found mixins: {}", mixinTypes);
if (mixinTypes != null) {
final ImmutableList<String> copy = copyOf(mixinTypes);
final String mixin = Iterables.find(copy, acceptWhenTemplateMapContainsKey, null);
if (mixin != null) {
LOGGER.debug("Matched mixin type: {}", mixin);
template = templatesMap.get(mixin);
}
}
}

if (template == null) {
LOGGER.trace("Attempting to discover the primary type of the node for the resource in question...");
final String nodeType =
Expand Down Expand Up @@ -271,4 +316,21 @@ public long getSize(final RdfStream t, final Class<?> type,
// we don't know in advance how large the result might be
return -1;
}

private void addTemplate(final String primaryNodeTypeName, final String templateNodeTypeName,
final ImmutableMap.Builder<String, Template> templatesMapBuilder) {
final String templateLocation = getTemplateLocation(templateNodeTypeName);
final Template template =
velocity.getTemplate(templateLocation);
template.setName(templateLocation);
LOGGER.debug("Found template: {}", templateLocation);
templatesMapBuilder.put(primaryNodeTypeName, template);
LOGGER.debug("which we will use for nodes with primary type: {}",
primaryNodeTypeName);
}

private String getTemplateLocation(final String nodeTypeName) {
return templatesLocation + "/" +
nodeTypeName.replace(':', '-') + templateFilenameExtension;
}
}
Expand Up @@ -71,6 +71,7 @@ public class StreamingBaseHtmlProviderTest {
private final StreamingBaseHtmlProvider testProvider = new StreamingBaseHtmlProvider();

private final RdfStream testData = new RdfStream();
private final RdfStream testData2 = new RdfStream();

@Mock
private Session mockSession;
Expand Down Expand Up @@ -100,6 +101,13 @@ public void setup() throws RepositoryException {
new Triple(createURI("test:subject"), primaryTypePredicate,
createLiteral("nt:file")));

testData2.session(mockSession);
testData2.topic(createURI("test:subject2"));
testData2.concat(
new Triple(createURI("test:subject2"),
primaryTypePredicate,
createLiteral("childOf:ntFile")));

final UriInfo info = Mockito.mock(UriInfo.class);
setField(testProvider, "uriInfo", info);
}
Expand Down Expand Up @@ -180,4 +188,31 @@ public Object answer(final InvocationOnMock invocation) {
assertTrue("Got no output from serialization!", results.length > 0);

}

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

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(isA(Context.class), isA(Writer.class));

setField(testProvider, "templatesMap",
of("childOf:ntFile", mockTemplate,
"grandchildOf:ntFile", mockTemplate));
testProvider.writeTo(testData2, RdfStream.class, mock(Type.class),
new Annotation[] {}, MediaType
.valueOf("text/html"),
(MultivaluedMap) new MultivaluedHashMap<>(), outStream);
final byte[] results = outStream.toByteArray();
assertTrue("Got no output from serialization!", results.length > 0);
}
}
Expand Up @@ -15,9 +15,13 @@
*/
package org.fcrepo.integration.http.api;

import static org.apache.commons.lang.StringUtils.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
import org.junit.Test;

/**
Expand Down Expand Up @@ -60,4 +64,17 @@ public void testGetDatastreamNode() throws Exception {
method.addHeader("Accept", "text/html");
assertEquals(200, getStatus(method));
}

@Test
public void testGetTemplate() throws Exception {
final String pid = getRandomUniquePid();
createObject(pid);
addMixin(pid, "http://fedora.info/definitions/v4/rest-api#resource");

final HttpGet method = new HttpGet(serverAddress + pid);
method.addHeader("Accept", "text/html");
final HttpResponse response = execute(method);
final String html = EntityUtils.toString(response.getEntity());
assertTrue(contains(html, "class=\"nt_folder\""));
}
}
Expand Up @@ -15,19 +15,25 @@
*/
package org.fcrepo.http.commons.responses;

import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.Iterables.transform;
import static com.hp.hpl.jena.graph.NodeFactory.createURI;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource;
import static org.fcrepo.kernel.RdfLexicon.JCR_NAMESPACE;
import static org.fcrepo.kernel.impl.rdf.JcrRdfTools.getRDFNamespaceForJcrNamespace;
import static org.slf4j.LoggerFactory.getLogger;

import java.util.Iterator;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import org.slf4j.Logger;

import com.hp.hpl.jena.graph.Node;

/**
* Utilities to help with serializing a graph to an HTTP resource
*
Expand All @@ -50,6 +56,18 @@ private RdfSerializationUtils() {
createURI(getRDFNamespaceForJcrNamespace(JCR_NAMESPACE) +
"primaryType");

/**
* The RDF predicate that will indicate the mixin types.
*/
public static Node mixinTypesPredicate =
createURI(getRDFNamespaceForJcrNamespace(JCR_NAMESPACE) +
"mixinTypes");

public static final Function<RDFNode, String> stringConverter = new Function<RDFNode, String>() {
public String apply(final RDFNode statement) {
return statement.asLiteral().getLexicalForm();
}
};

/**
* Get the very first value for a predicate as a string, or null if the
Expand All @@ -72,4 +90,23 @@ public static String getFirstValueForPredicate(final Model rdf,
return null;
}

/**
* Get all the values for a predicate as a string array, or null if the
* predicate is not used
*
* @param rdf
* @param subject
* @param predicate
* @return first value for the given predicate or null if not found
*/
public static Iterator<String> getAllValuesForPredicate(final Model rdf,
final Node subject, final Node predicate) {
final NodeIterator statements =
rdf.listObjectsOfProperty(createResource(subject.getURI()),
createProperty(predicate.getURI()));

final ImmutableList<RDFNode> copy = copyOf(statements);
return transform(copy, stringConverter).iterator();
}

}
Expand Up @@ -15,14 +15,18 @@
*/
package org.fcrepo.http.commons.responses;

import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.ImmutableList.of;
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.createProperty;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createTypedLiteral;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.getFirstValueForPredicate;
import static org.fcrepo.http.commons.responses.RdfSerializationUtils.getAllValuesForPredicate;
import static org.junit.Assert.assertEquals;

import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -52,6 +56,12 @@ public void setup() {
testData.add(createResource("test:subject"),
createProperty("test:predicate"),
createTypedLiteral("test:object"));
testData.add(createResource("test:subject"),
createProperty("test:anotherPredicate"),
createTypedLiteral("test:object1"));
testData.add(createResource("test:subject"),
createProperty("test:anotherPredicate"),
createTypedLiteral("test:object2"));

final List<PathSegment> segments = new ArrayList<>();
segment = Mockito.mock(PathSegment.class);
Expand All @@ -66,4 +76,14 @@ public void testGetFirstValueForPredicate() {
assertEquals("Didn't find correct value for predicate!", "test:object", foundValue);
}

@Test
public void testGetAllValuesForPredicate() {
final Iterator<String> foundValues =
getAllValuesForPredicate(testData, createURI("test:subject"),
createURI("test:anotherPredicate"));

assertEquals("Didn't find correct values for predicate!", copyOf(foundValues),
of("test:object1", "test:object2"));
}

}