Merge pull request #65 from j2blake/feature/VIVO-1408_java-rdf-namespaces

VIVO-1408 ConfigurationBeanLoader: java rdf namespaces
This commit is contained in:
hudajkhan 2018-04-03 15:42:07 -04:00 committed by GitHub
commit 07686ebdf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 829 additions and 218 deletions

View file

@ -8,6 +8,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -34,16 +35,42 @@ public class ConfigurationBeanLoader {
return JAVA_URI_PREFIX + clazz.getName(); return JAVA_URI_PREFIX + clazz.getName();
} }
public static String toCanonicalJavaUri(String uri) {
return uri.replace("#", ".");
}
public static boolean isJavaUri(String uri) { public static boolean isJavaUri(String uri) {
return uri.startsWith(JAVA_URI_PREFIX); return uri.startsWith(JAVA_URI_PREFIX);
} }
public static String fromJavaUri(String uri) { public static Set<String> toPossibleJavaUris(Class<?> clazz) {
if (!isJavaUri(uri)) { Set<String> set = new TreeSet<>();
throw new IllegalArgumentException("Not a java class URI: '" + uri String[] uriPieces = toJavaUri(clazz).split("\\.");
+ "'"); for (int hashIndex = 0; hashIndex < uriPieces.length; hashIndex++) {
set.add(joinWithPeriodsAndAHash(uriPieces, hashIndex));
} }
return uri.substring(JAVA_URI_PREFIX.length()); return set;
}
private static String joinWithPeriodsAndAHash(String[] pieces,
int hashIndex) {
StringBuilder buffer = new StringBuilder(pieces[0]);
for (int i = 1; i < pieces.length; i++) {
buffer.append(i == hashIndex ? '#' : '.').append(pieces[i]);
}
return buffer.toString();
}
public static String classnameFromJavaUri(String uri) {
if (!isJavaUri(uri)) {
throw new IllegalArgumentException(
"Not a java class URI: '" + uri + "'");
}
return toCanonicalJavaUri(uri).substring(JAVA_URI_PREFIX.length());
}
public static boolean isMatchingJavaUri(String uri1, String uri2) {
return toCanonicalJavaUri(uri1).equals(toCanonicalJavaUri(uri2));
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -85,9 +112,11 @@ public class ConfigurationBeanLoader {
this(new LockableModel(model), req); this(new LockableModel(model), req);
} }
public ConfigurationBeanLoader(LockableModel locking, HttpServletRequest req) { public ConfigurationBeanLoader(LockableModel locking,
this(locking, (req == null) ? null : req.getSession() HttpServletRequest req) {
.getServletContext(), req); this(locking,
(req == null) ? null : req.getSession().getServletContext(),
req);
} }
private ConfigurationBeanLoader(LockableModel locking, ServletContext ctx, private ConfigurationBeanLoader(LockableModel locking, ServletContext ctx,
@ -111,18 +140,18 @@ public class ConfigurationBeanLoader {
} }
try { try {
ConfigurationRdf<T> parsedRdf = ConfigurationRdfParser.parse( ConfigurationRdf<T> parsedRdf = ConfigurationRdfParser
locking, uri, resultClass); .parse(locking, uri, resultClass);
WrappedInstance<T> wrapper = InstanceWrapper.wrap(parsedRdf WrappedInstance<T> wrapper = InstanceWrapper
.getConcreteClass()); .wrap(parsedRdf.getConcreteClass());
wrapper.satisfyInterfaces(ctx, req); wrapper.satisfyInterfaces(ctx, req);
wrapper.checkCardinality(parsedRdf.getPropertyStatements()); wrapper.checkCardinality(parsedRdf.getPropertyStatements());
wrapper.setProperties(this, parsedRdf.getPropertyStatements()); wrapper.setProperties(this, parsedRdf.getPropertyStatements());
wrapper.validate(); wrapper.validate();
return wrapper.getInstance(); return wrapper.getInstance();
} catch (Exception e) { } catch (Exception e) {
throw new ConfigurationBeanLoaderException("Failed to load '" + uri throw new ConfigurationBeanLoaderException(
+ "'", e); "Failed to load '" + uri + "'", e);
} }
} }
@ -133,14 +162,16 @@ public class ConfigurationBeanLoader {
throws ConfigurationBeanLoaderException { throws ConfigurationBeanLoaderException {
Set<String> uris = new HashSet<>(); Set<String> uris = new HashSet<>();
try (LockedModel m = locking.read()) { try (LockedModel m = locking.read()) {
for (String typeUri : toPossibleJavaUris(resultClass)) {
List<Resource> resources = m.listResourcesWithProperty(RDF.type, List<Resource> resources = m.listResourcesWithProperty(RDF.type,
createResource(toJavaUri(resultClass))).toList(); createResource(typeUri)).toList();
for (Resource r : resources) { for (Resource r : resources) {
if (r.isURIResource()) { if (r.isURIResource()) {
uris.add(r.getURI()); uris.add(r.getURI());
} }
} }
} }
}
Set<T> instances = new HashSet<>(); Set<T> instances = new HashSet<>();
for (String uri : uris) { for (String uri : uris) {

View file

@ -2,16 +2,18 @@
package edu.cornell.mannlib.vitro.webapp.utils.configuration; package edu.cornell.mannlib.vitro.webapp.utils.configuration;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.classnameFromJavaUri;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.isJavaUri;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.isMatchingJavaUri;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
import static org.apache.jena.rdf.model.ResourceFactory.createResource; import static org.apache.jena.rdf.model.ResourceFactory.createResource;
import static org.apache.jena.rdf.model.ResourceFactory.createStatement; import static org.apache.jena.rdf.model.ResourceFactory.createStatement;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.fromJavaUri;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.isJavaUri;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Property;
@ -64,23 +66,30 @@ public class ConfigurationRdfParser {
private static void confirmEligibilityForResultClass(LockableModel locking, private static void confirmEligibilityForResultClass(LockableModel locking,
String uri, Class<?> resultClass) String uri, Class<?> resultClass)
throws InvalidConfigurationRdfException { throws InvalidConfigurationRdfException {
Statement s = createStatement(createResource(uri), RDF.type, String resultClassUri = toJavaUri(resultClass);
createResource(toJavaUri(resultClass)));
try (LockedModel m = locking.read()) { try (LockedModel m = locking.read()) {
if (!m.contains(s)) { Set<RDFNode> types = //
throw noTypeStatementForResultClass(s); m.listObjectsOfProperty(createResource(uri), RDF.type)
.toSet();
for (RDFNode typeNode : types) {
if (typeNode.isURIResource()) {
String typeUri = typeNode.asResource().getURI();
if (isMatchingJavaUri(resultClassUri, typeUri)) {
return;
} }
} }
} }
throw noTypeStatementForResultClass(uri, resultClassUri);
}
}
private static Set<PropertyStatement> loadProperties(LockableModel locking, private static Set<PropertyStatement> loadProperties(LockableModel locking,
String uri) throws InvalidConfigurationRdfException { String uri) throws InvalidConfigurationRdfException {
Set<PropertyStatement> set = new HashSet<>(); Set<PropertyStatement> set = new HashSet<>();
try (LockedModel m = locking.read()) { try (LockedModel m = locking.read()) {
List<Statement> rawStatements = m.listStatements( List<Statement> rawStatements = m.listStatements(m.getResource(uri),
m.getResource(uri), (Property) null, (RDFNode) null) (Property) null, (RDFNode) null).toList();
.toList();
if (rawStatements.isEmpty()) { if (rawStatements.isEmpty()) {
throw noRdfStatements(uri); throw noRdfStatements(uri);
} }
@ -108,8 +117,9 @@ public class ConfigurationRdfParser {
Set<Class<? extends T>> concreteClasses = new HashSet<>(); Set<Class<? extends T>> concreteClasses = new HashSet<>();
try (LockedModel m = locking.read()) { try (LockedModel m = locking.read()) {
for (RDFNode node : m.listObjectsOfProperty(createResource(uri), for (RDFNode node : m
RDF.type).toSet()) { .listObjectsOfProperty(createResource(uri), RDF.type)
.toSet()) {
if (!node.isURIResource()) { if (!node.isURIResource()) {
throw typeMustBeUriResource(node); throw typeMustBeUriResource(node);
} }
@ -140,7 +150,7 @@ public class ConfigurationRdfParser {
if (!isJavaUri(typeUri)) { if (!isJavaUri(typeUri)) {
return false; return false;
} }
Class<?> clazz = Class.forName(fromJavaUri(typeUri)); Class<?> clazz = Class.forName(classnameFromJavaUri(typeUri));
if (clazz.isInterface()) { if (clazz.isInterface()) {
return false; return false;
} }
@ -157,7 +167,7 @@ public class ConfigurationRdfParser {
private static <T> Class<? extends T> processTypeUri(String typeUri, private static <T> Class<? extends T> processTypeUri(String typeUri,
Class<T> resultClass) throws InvalidConfigurationRdfException { Class<T> resultClass) throws InvalidConfigurationRdfException {
try { try {
Class<?> clazz = Class.forName(fromJavaUri(typeUri)); Class<?> clazz = Class.forName(classnameFromJavaUri(typeUri));
if (!resultClass.isAssignableFrom(clazz)) { if (!resultClass.isAssignableFrom(clazz)) {
throw notAssignable(resultClass, clazz); throw notAssignable(resultClass, clazz);
} }
@ -180,22 +190,23 @@ public class ConfigurationRdfParser {
"The model contains no statements about '" + uri + "'"); "The model contains no statements about '" + uri + "'");
} }
private static InvalidConfigurationRdfException noConcreteClasses(String uri) { private static InvalidConfigurationRdfException noConcreteClasses(
String uri) {
return new InvalidConfigurationRdfException( return new InvalidConfigurationRdfException(
"No concrete class is declared for '" + uri + "'"); "No concrete class is declared for '" + uri + "'");
} }
private static InvalidConfigurationRdfException tooManyConcreteClasses( private static InvalidConfigurationRdfException tooManyConcreteClasses(
String uri, Set<?> concreteClasses) { String uri, Set<?> concreteClasses) {
return new InvalidConfigurationRdfException("'" + uri return new InvalidConfigurationRdfException(
+ "' is declared with more than one " + "concrete class: " "'" + uri + "' is declared with more than one "
+ concreteClasses); + "concrete class: " + concreteClasses);
} }
private static InvalidConfigurationRdfException notAssignable( private static InvalidConfigurationRdfException notAssignable(
Class<?> resultClass, Class<?> clazz) { Class<?> resultClass, Class<?> clazz) {
return new InvalidConfigurationRdfException(clazz return new InvalidConfigurationRdfException(
+ " cannot be assigned to " + resultClass); clazz + " cannot be assigned to " + resultClass);
} }
private static InvalidConfigurationRdfException noZeroArgumentConstructor( private static InvalidConfigurationRdfException noZeroArgumentConstructor(
@ -212,8 +223,8 @@ public class ConfigurationRdfParser {
private static InvalidConfigurationRdfException failedToLoadClass( private static InvalidConfigurationRdfException failedToLoadClass(
String typeUri, Throwable e) { String typeUri, Throwable e) {
return new InvalidConfigurationRdfException("Can't load this type: '" return new InvalidConfigurationRdfException(
+ typeUri + "'", e); "Can't load this type: '" + typeUri + "'", e);
} }
private static InvalidConfigurationRdfException typeMustBeUriResource( private static InvalidConfigurationRdfException typeMustBeUriResource(
@ -223,14 +234,17 @@ public class ConfigurationRdfParser {
} }
private static InvalidConfigurationRdfException noTypeStatementForResultClass( private static InvalidConfigurationRdfException noTypeStatementForResultClass(
Statement s) { String uri, String resultClassUri) {
return new InvalidConfigurationRdfException( return new InvalidConfigurationRdfException(
"A type statement is required: '" + s); "A type statement is required: '"
+ createStatement(createResource(uri), RDF.type,
createResource(resultClassUri)));
} }
private static InvalidConfigurationRdfException noRdfStatements(String uri) { private static InvalidConfigurationRdfException noRdfStatements(
return new InvalidConfigurationRdfException("'" + uri String uri) {
+ "' does not appear as the subject of any " return new InvalidConfigurationRdfException(
"'" + uri + "' does not appear as the subject of any "
+ "statements in the model."); + "statements in the model.");
} }
@ -239,7 +253,8 @@ public class ConfigurationRdfParser {
super(message); super(message);
} }
public InvalidConfigurationRdfException(String message, Throwable cause) { public InvalidConfigurationRdfException(String message,
Throwable cause) {
super(message, cause); super(message, cause);
} }
} }

View file

@ -95,8 +95,36 @@ The principal methods are:
+ Search the graph for all individuals of type `resultClass`. For each such individual, call `loadInstance`. + Search the graph for all individuals of type `resultClass`. For each such individual, call `loadInstance`.
Return a set containing the created instances. If no individuals are found, return an empty `Set`. Return a set containing the created instances. If no individuals are found, return an empty `Set`.
### Specifying Java class URIs
Java classes are specified as types in the configurations. The type URIs consist of `java:` and the fully-qualified class path and name. For example,
```
:application
a <java:edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl> .
```
It would be nice to use prefixes to make URIs more readable. This doesn't
work with the scheme above, since none of the characters in the URI are valid
as delimiters of a prefix.
For this reason, the loader will also recognize a type URI if one of the periods is replaced by a hash (`#`). So, this is equivalent to the previous example (note the `#` after `webapp`):
```
:application
a <java:edu.cornell.mannlib.vitro.webapp#application.ApplicationImpl> .
```
which implies that this is equivalent also:
```
@prefix javaWebapp: <java:edu.cornell.mannlib.vitro.webapp#>
:application a javaWebapp:application.ApplicationImpl .
```
### Restrictions on instantiated classes. ### Restrictions on instantiated classes.
Each class to be instantiated must have a niladic constructor. Each class to be instantiated must have a public niladic constructor.
### Property methods ### Property methods
When the loader encounters a data property or an object property in a description, When the loader encounters a data property or an object property in a description,
@ -116,7 +144,8 @@ For example:
In more detail: In more detail:
+ A class must contain exactly one method that serves each property URI in the description. + Each property URI in the description may be served by only one method in the class.
+ If a property URI in the description is not served by any method in the class, the loader will ignore that property.
+ The description need not include properies for all of the property methods in the class. + The description need not include properies for all of the property methods in the class.
+ Each property method must be public, must have exactly one parameter, and must return null. + Each property method must be public, must have exactly one parameter, and must return null.
+ The name of the property method is immaterial, except that there must not be another method + The name of the property method is immaterial, except that there must not be another method
@ -159,7 +188,7 @@ Again, in detail:
+ Each validation method must be public, must accept no parameters, and must return null. + Each validation method must be public, must accept no parameters, and must return null.
+ The name of the validation method is immaterial, except that there must not be another + The name of the validation method is immaterial, except that there must not be another
+ method with the same name in the lass. + method with the same name in the class.
+ Validation methods in superclasses will be called, but may not be overridden in a subclass. + Validation methods in superclasses will be called, but may not be overridden in a subclass.
### Life cycle ### Life cycle

View file

@ -70,7 +70,8 @@ public class WrappedInstance<T> {
*/ */
public void checkCardinality(Set<PropertyStatement> propertyStatements) public void checkCardinality(Set<PropertyStatement> propertyStatements)
throws CardinalityException { throws CardinalityException {
Map<String, Integer> statementCounts = countPropertyStatementsByPredicateUri(propertyStatements); Map<String, Integer> statementCounts = countPropertyStatementsByPredicateUri(
propertyStatements);
for (PropertyMethod pm : propertyMethods.values()) { for (PropertyMethod pm : propertyMethods.values()) {
Integer c = statementCounts.get(pm.getPropertyUri()); Integer c = statementCounts.get(pm.getPropertyUri());
int count = (c == null) ? 0 : c; int count = (c == null) ? 0 : c;
@ -109,12 +110,11 @@ public class WrappedInstance<T> {
*/ */
public void setProperties(ConfigurationBeanLoader loader, public void setProperties(ConfigurationBeanLoader loader,
Collection<PropertyStatement> propertyStatements) Collection<PropertyStatement> propertyStatements)
throws PropertyTypeException, NoSuchPropertyMethodException, throws PropertyTypeException, ConfigurationBeanLoaderException {
ConfigurationBeanLoaderException {
for (PropertyStatement ps : propertyStatements) { for (PropertyStatement ps : propertyStatements) {
PropertyMethod pm = propertyMethods.get(ps.getPredicateUri()); PropertyMethod pm = propertyMethods.get(ps.getPredicateUri());
if (pm == null) { if (pm == null) {
throw new NoSuchPropertyMethodException(ps); continue; // No method for this property? Ignore it.
} }
pm.confirmCompatible(ps); pm.confirmCompatible(ps);
@ -141,7 +141,8 @@ public class WrappedInstance<T> {
} catch (IllegalAccessException | IllegalArgumentException } catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) { | InvocationTargetException e) {
throw new ValidationFailedException( throw new ValidationFailedException(
"Error executing validation method '" + method + "'", e); "Error executing validation method '" + method + "'",
e);
} }
} }
} }
@ -165,12 +166,6 @@ public class WrappedInstance<T> {
} }
} }
public static class NoSuchPropertyMethodException extends Exception {
public NoSuchPropertyMethodException(PropertyStatement ps) {
super("No property method for '" + ps.getPredicateUri() + "'");
}
}
public static class CardinalityException extends Exception { public static class CardinalityException extends Exception {
public CardinalityException(String message) { public CardinalityException(String message) {
super(message); super(message);

View file

@ -2,19 +2,43 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels; package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
import java.io.File;
import java.lang.reflect.Method;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import freemarker.ext.beans.MethodAppearanceFineTuner;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.TemplateModel; import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelException;
/**
* Provides a mechanism for Freemarker templates (main or included, parent or
* child) to add to the lists of scripts and style sheets for the current page.
*
* Each page uses 3 instances of Tags, exposed as ${scripts}, ${headScripts} and
* ${stylesheets}. A template may add a complete &lt;script/$gt; element (for
* scripts or headScripts) or a &lt;link&gt; tag (for stylesheets), and these
* elements will appear at the proper location in the rendered HTML for the
* page.
*
* VIVO-1405: This process is augmented by the TagVersionInfo inner class, which
* attempts to add a "version=" query string to the URL in the supplied element.
* The version number is derived from the last-modified date of the specified
* script or stylesheet on the server. The effect is that a user's browser cache
* is effectively invalidated each time a new version of the script or
* stylesheet is deployed.
*/
public class Tags extends BaseTemplateModel { public class Tags extends BaseTemplateModel {
private static final Log log = LogFactory.getLog(Tags.class); private static final Log log = LogFactory.getLog(Tags.class);
protected final LinkedHashSet<String> tags; protected final LinkedHashSet<String> tags;
@ -36,35 +60,44 @@ public class Tags extends BaseTemplateModel {
} }
} }
/** Script and stylesheet lists are wrapped with a specialized BeansWrapper /**
* that exposes certain write methods, instead of the configuration's object wrapper, * Script and stylesheet lists are wrapped with a specialized BeansWrapper
* which doesn't. The templates can then add stylesheets and scripts to the lists * that exposes certain write methods, instead of the configuration's object
* by calling their add() methods. * wrapper, which doesn't. The templates can then add stylesheets and
* scripts to the lists by calling their add() methods.
*
* @param Tags
* tags
* @return TemplateModel
*/ */
static public class TagsWrapper extends BeansWrapper { static public class TagsWrapper extends BeansWrapper {
public TagsWrapper() { public TagsWrapper() {
super(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// Start by exposing all safe methods. // Start by exposing all safe methods.
setExposureLevel(EXPOSE_SAFE); setExposureLevel(EXPOSE_SAFE);
setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() { }
@SuppressWarnings("rawtypes")
@Override @Override
public void process(MethodAppearanceDecisionInput methodAppearanceDecisionInput, MethodAppearanceDecision methodAppearanceDecision) { protected void finetuneMethodAppearance(Class cls, Method method,
MethodAppearanceDecision decision) {
try { try {
String methodName = methodAppearanceDecisionInput.getMethod().getName(); String methodName = method.getName();
if ( ! ( methodName.equals("add") || methodName.equals("list")) ) { if (!(methodName.equals("add") || methodName.equals("list"))) {
methodAppearanceDecision.setExposeMethodAs(null); decision.setExposeMethodAs(null);
} }
} catch (Exception e) { } catch (Exception e) {
log.error(e, e); log.error(e, e);
} }
} }
});
} }
}
/* Template methods */ /* Template methods */
@SuppressWarnings("hiding")
public void add(String... tags) { public void add(String... tags) {
for (String tag : tags) { for (String tag : tags) {
add(tag); add(tag);
@ -72,12 +105,167 @@ public class Tags extends BaseTemplateModel {
} }
public void add(String tag) { public void add(String tag) {
TagVersionInfo info = new TagVersionInfo(tag);
if (info.hasVersion()) {
tags.add(TagVersionInfo.addVersionNumber(tag, info));
} else {
tags.add(tag); tags.add(tag);
} }
}
public String list() { public String list() {
return StringUtils.join(tags, "\n"); return StringUtils.join(tags, "\n");
} }
/**
* Find the value of "href" or "src".
*
* If there is such a value, and it doesn't have a query string already, and
* it represents a local URL, and we can locate the file that is served by
* the URL and get the last modified date, then we have found a "version
* number" that we can add to the attribute value.
*
* Reference for parsing attributes:
* https://www.w3.org/TR/html/syntax.html#elements-attributes
*/
protected static class TagVersionInfo {
private static final Pattern PATTERN_DOUBLE_QUOTES = Pattern
.compile("(href|src)\\s*=\\s*\"([^\"]+)\"[\\s|>]");
private static final int GROUP_INDEX_DOUBLE_QUOTES = 2;
private static final Pattern PATTERN_SINGLE_QUOTES = Pattern
.compile("(href|src)\\s*=\\s*'([^']+)'[\\s|>]");
private static final int GROUP_INDEX_SINGLE_QUOTES = 2;
private static final Pattern PATTERN_NO_QUOTES = Pattern
.compile("(href|src)\\s*=\\s*([^\"'<=>\\s]+)[\\s|>]");
private static final int GROUP_INDEX_NO_QUOTES = 2;
public static String addVersionNumber(String rawTag,
TagVersionInfo info) {
String versionString = (info.match.style == MatchResult.Style.NO_QUOTES)
? "?version&eq;"
: "?version=";
return rawTag.substring(0, info.match.start) + info.match.group
+ versionString + smushTimeStamp(info)
+ rawTag.substring(info.match.end);
}
private static String smushTimeStamp(TagVersionInfo info) {
int smushed = (((char) (info.timestamp >> 48))
^ ((char) (info.timestamp >> 32))
^ ((char) (info.timestamp >> 16))
^ ((char) info.timestamp));
return String.format("%04x", smushed);
}
private MatchResult match;
private long timestamp = 0L;
public TagVersionInfo(String rawTag) {
try {
match = findUrlValue(rawTag);
if (match != null && !hasQueryString(match.group)) {
String stripped = stripContextPath(match.group);
if (stripped != null) {
String realPath = locateRealPath(stripped);
if (realPath != null) {
timestamp = getLastModified(realPath);
}
}
}
} catch (Exception e) {
log.debug("Failed to add version info to tag: " + rawTag, e);
timestamp = 0L;
}
}
public boolean hasVersion() {
return timestamp != 0L;
}
private static MatchResult findUrlValue(String rawTag) {
Matcher mDouble = PATTERN_DOUBLE_QUOTES.matcher(rawTag);
if (mDouble.find()) {
return new MatchResult(mDouble, GROUP_INDEX_DOUBLE_QUOTES,
MatchResult.Style.DOUBLE_QUOTES);
}
Matcher mSingle = PATTERN_SINGLE_QUOTES.matcher(rawTag);
if (mSingle.find()) {
return new MatchResult(mSingle, GROUP_INDEX_SINGLE_QUOTES,
MatchResult.Style.SINGLE_QUOTES);
}
Matcher mNo = PATTERN_NO_QUOTES.matcher(rawTag);
if (mNo.find()) {
return new MatchResult(mNo, GROUP_INDEX_NO_QUOTES,
MatchResult.Style.NO_QUOTES);
}
log.debug(rawTag + " no match");
return null;
}
private static boolean hasQueryString(String group) {
if (group.indexOf('?') > -1) {
log.debug(group + " has query string already");
return true;
} else {
return false;
}
}
private static String stripContextPath(String group) {
String contextPath = UrlBuilder.getBaseUrl();
if (contextPath.isEmpty() || group.startsWith(contextPath)) {
return group.substring(contextPath.length());
} else {
log.debug(group + " doesn't match context path");
return null;
}
}
private static String locateRealPath(String stripped) {
ServletContext ctx = ApplicationUtils.instance()
.getServletContext();
String realPath = ctx.getRealPath(stripped);
if (realPath == null) {
log.debug(stripped + " has no real path");
}
return realPath;
}
private static long getLastModified(String realPath) {
return new File(realPath).lastModified();
}
protected static class MatchResult {
public enum Style {
SINGLE_QUOTES, DOUBLE_QUOTES, NO_QUOTES
}
public final String group;
public final int start;
public final int end;
public final Style style;
public MatchResult(Matcher matcher, int group, Style style) {
this.group = matcher.group(group);
this.start = matcher.start(group);
this.end = matcher.end(group);
this.style = style;
log.debug(this);
}
@Override
public String toString() {
return "MatchResult[start=" + start + ", end=" + end
+ ", group=" + group + ", style=" + style + "]";
}
}
}
} }

View file

@ -2,12 +2,12 @@
package edu.cornell.mannlib.vitro.webapp.utils.configuration; package edu.cornell.mannlib.vitro.webapp.utils.configuration;
import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat;
import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring;
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty;
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty;
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat;
import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -19,18 +19,16 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.Statement;
import org.junit.Ignore;
import org.junit.Test;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess;
import edu.cornell.mannlib.vitro.webapp.modelaccess.RequestModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.RequestModelAccess;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationRdfParser.InvalidConfigurationRdfException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationRdfParser.InvalidConfigurationRdfException;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.NoSuchPropertyMethodException;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ResourceUnavailableException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ResourceUnavailableException;
/** /**
@ -309,24 +307,6 @@ public class ConfigurationBeanLoaderTest extends
// -------------------------------------------- // --------------------------------------------
@Test
public void tripleHasUnrecognizedProperty_throwsException()
throws ConfigurationBeanLoaderException {
model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(SimpleSuccess.class)));
model.add(dataProperty(GENERIC_INSTANCE_URI,
"http://bogus.property/name", "No place to put it."));
expectSimpleFailure(
SimpleSuccess.class,
throwable(ConfigurationBeanLoaderException.class,
"Failed to load"),
throwable(NoSuchPropertyMethodException.class,
"No property method"));
}
// --------------------------------------------
@Test @Test
public void valueTypeDoesNotMatchArgumentOfPropertyMethod_throwsException() public void valueTypeDoesNotMatchArgumentOfPropertyMethod_throwsException()
throws ConfigurationBeanLoaderException { throws ConfigurationBeanLoaderException {
@ -395,6 +375,22 @@ public class ConfigurationBeanLoaderTest extends
assertNotNull(instance); assertNotNull(instance);
} }
/**
* Ignores unexpected properties
*/
@Test
public void simpleSuccessIgnoringExtraProperties() throws ConfigurationBeanLoaderException {
model.add(typeStatement(SIMPLE_SUCCESS_INSTANCE_URI,
toJavaUri(SimpleSuccess.class)));
model.add(dataProperty(SIMPLE_SUCCESS_INSTANCE_URI,
"http://surprise.property/name", "No matching method."));
SimpleSuccess instance = loader.loadInstance(
SIMPLE_SUCCESS_INSTANCE_URI, SimpleSuccess.class);
assertNotNull(instance);
}
public static class SimpleSuccess { public static class SimpleSuccess {
// Nothing of interest. // Nothing of interest.
} }

View file

@ -0,0 +1,111 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.utils.configuration;
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement;
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toPossibleJavaUris;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import org.junit.Test;
/**
* Assure that we can use "namespaces" for Java URIs. The namespace must end
* with a '#'.
*/
public class ConfigurationBeanLoader_NamespacesTest
extends ConfigurationBeanLoaderTestBase {
// ----------------------------------------------------------------------
// toPossibleJavaUris()
// ----------------------------------------------------------------------
@Test
public void possibleForJavaLangString() {
Set<String> expected = new HashSet<>();
expected.add("java:java.lang.String");
expected.add("java:java#lang.String");
expected.add("java:java.lang#String");
assertEquals(expected, toPossibleJavaUris(String.class));
}
// ----------------------------------------------------------------------
// loadAll()
// ----------------------------------------------------------------------
@Test
public void loadAllForJavaUtilRandom()
throws ConfigurationBeanLoaderException {
model.add(typeStatement("http://noPound", "java:java.util.Random"));
model.add(typeStatement("http://firstPound", "java:java#util.Random"));
model.add(typeStatement("http://secondPound", "java:java.util#Random"));
model.add(typeStatement("http://notARandom", "java:java.util.Set"));
Set<Random> instances = loader.loadAll(Random.class);
assertEquals(3, instances.size());
}
@Test
public void loadAlForCustomInnerClass()
throws ConfigurationBeanLoaderException {
Set<String> typeUris = toPossibleJavaUris(ExampleClassForLoadAll.class);
for (String typeUri : typeUris) {
model.add(typeStatement("http://testUri" + model.size(), typeUri));
}
Set<ExampleClassForLoadAll> instances = loader
.loadAll(ExampleClassForLoadAll.class);
assertEquals(typeUris.size(), instances.size());
}
public static class ExampleClassForLoadAll {
// Nothing of interest
}
// ----------------------------------------------------------------------
// loadInstance()
// ----------------------------------------------------------------------
@Test
public void loadInstanceVariationsForJavaUtilRandom()
throws ConfigurationBeanLoaderException {
model.add(typeStatement("http://noPound", "java:java.util.Random"));
model.add(typeStatement("http://firstPound", "java:java#util.Random"));
model.add(typeStatement("http://secondPound", "java:java.util#Random"));
model.add(typeStatement("http://notARandom", "java:java.util.Set"));
assertNotNull(loader.loadInstance("http://noPound", Random.class));
assertNotNull(loader.loadInstance("http://firstPound", Random.class));
assertNotNull(loader.loadInstance("http://secondPound", Random.class));
try {
loader.loadInstance("http://notARandom", Random.class);
fail("Should not be a Random");
} catch (Exception e) {
// Expected it
}
}
@Test
public void loadInstanceVariationsForCustomInnerClass()
throws ConfigurationBeanLoaderException {
Set<String> typeUris = toPossibleJavaUris(
ExampleClassForLoadInstance.class);
for (String typeUri : typeUris) {
model.add(typeStatement("http://testUri" + model.size(), typeUri));
}
for (int i = 0; i < model.size(); i++) {
String instanceUri = "http://testUri" + i;
assertNotNull("No instance for " + instanceUri, loader.loadInstance(
instanceUri, ExampleClassForLoadInstance.class));
}
}
public static class ExampleClassForLoadInstance {
// Nothing of interest
}
}

View file

@ -15,8 +15,8 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.Vali
/** /**
* Test the @Validation annotation. * Test the @Validation annotation.
*/ */
public class ConfigurationBeanLoader_ValidationTest extends public class ConfigurationBeanLoader_ValidationTest
ConfigurationBeanLoaderTestBase { extends ConfigurationBeanLoaderTestBase {
// -------------------------------------------- // --------------------------------------------
@Test @Test
@ -25,8 +25,7 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(ValidationMethodWithParameter.class))); toJavaUri(ValidationMethodWithParameter.class)));
expectSimpleFailure( expectSimpleFailure(ValidationMethodWithParameter.class,
ValidationMethodWithParameter.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(InstanceWrapperException.class, throwable(InstanceWrapperException.class,
@ -49,11 +48,11 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(ValidationMethodShouldReturnVoid.class))); toJavaUri(ValidationMethodShouldReturnVoid.class)));
expectSimpleFailure( expectSimpleFailure(ValidationMethodShouldReturnVoid.class,
ValidationMethodShouldReturnVoid.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(InstanceWrapperException.class, "should return void")); throwable(InstanceWrapperException.class,
"should return void"));
} }
public static class ValidationMethodShouldReturnVoid { public static class ValidationMethodShouldReturnVoid {
@ -71,8 +70,7 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(ValidationMethodIsPrivate.class))); toJavaUri(ValidationMethodIsPrivate.class)));
expectSimpleFailure( expectSimpleFailure(ValidationMethodIsPrivate.class,
ValidationMethodIsPrivate.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(ValidationFailedException.class, throwable(ValidationFailedException.class,
@ -94,8 +92,7 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(ValidationThrowsException.class))); toJavaUri(ValidationThrowsException.class)));
expectSimpleFailure( expectSimpleFailure(ValidationThrowsException.class,
ValidationThrowsException.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(ValidationFailedException.class, throwable(ValidationFailedException.class,
@ -144,8 +141,7 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(ValidationOverValidationSubclass.class))); toJavaUri(ValidationOverValidationSubclass.class)));
expectSimpleFailure( expectSimpleFailure(ValidationOverValidationSubclass.class,
ValidationOverValidationSubclass.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(InstanceWrapperException.class, throwable(InstanceWrapperException.class,
@ -158,8 +154,7 @@ public class ConfigurationBeanLoader_ValidationTest extends
model.add(typeStatement(GENERIC_INSTANCE_URI, model.add(typeStatement(GENERIC_INSTANCE_URI,
toJavaUri(PlainOverValidationSubclass.class))); toJavaUri(PlainOverValidationSubclass.class)));
expectSimpleFailure( expectSimpleFailure(PlainOverValidationSubclass.class,
PlainOverValidationSubclass.class,
throwable(ConfigurationBeanLoaderException.class, throwable(ConfigurationBeanLoaderException.class,
"Failed to load"), "Failed to load"),
throwable(InstanceWrapperException.class, throwable(InstanceWrapperException.class,
@ -182,8 +177,8 @@ public class ConfigurationBeanLoader_ValidationTest extends
// Just want to see that the superclass validation is run. // Just want to see that the superclass validation is run.
} }
public static class AdditionalValidationSubclass extends public static class AdditionalValidationSubclass
ValidationSuperclass { extends ValidationSuperclass {
public boolean validatorSubHasRun = false; public boolean validatorSubHasRun = false;
@Validation @Validation
@ -195,8 +190,8 @@ public class ConfigurationBeanLoader_ValidationTest extends
} }
} }
public static class ValidationOverValidationSubclass extends public static class ValidationOverValidationSubclass
EmptyValidationSubclass { extends EmptyValidationSubclass {
@Override @Override
@Validation @Validation
public void validatorSuper() { public void validatorSuper() {
@ -204,8 +199,8 @@ public class ConfigurationBeanLoader_ValidationTest extends
} }
} }
public static class PlainOverValidationSubclass extends public static class PlainOverValidationSubclass
ValidationSuperclass { extends ValidationSuperclass {
@Override @Override
public void validatorSuper() { public void validatorSuper() {
// Should fail // Should fail

View file

@ -0,0 +1,248 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import org.apache.log4j.Level;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.Tags.TagVersionInfo;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.Tags.TagVersionInfo.MatchResult;
import stubs.edu.cornell.mannlib.vitro.webapp.modules.ApplicationStub;
import stubs.javax.servlet.ServletContextStub;
public class TagsTest extends AbstractTestClass {
private ServletContextStub ctx;
private File resource;
@Before
public void setup() throws IOException {
resource = File.createTempFile("resource", "");
ctx = new ServletContextStub();
ctx.setRealPath("/base/sub/file.js", resource.getPath());
ctx.setRealPath("/base/sub/file.css", resource.getPath());
ApplicationStub.setup(ctx, null);
setContextPath("/context");
}
// ----------------------------------------------------------------------
// Parsing tests
//
// Reference for parsing attributes:
// https://www.w3.org/TR/html/syntax.html#elements-attributes
// ----------------------------------------------------------------------
@Test
public void noAttribute_failure() {
assertNoMatch("<div height='value'></div>");
}
@Test
public void singleQuote_noTerminator_failure() {
assertNoMatch("<link rel='stylesheet' href='problem></link>");
}
@Test
public void singleQuotes_embeddedSingleQuote_failure() {
assertNoMatch("<script src='value'problem'></script");
}
@Test
public void singleQuotes_embeddedDoubleQuote_success() {
assertMatch("<script src='value\"noproblem'></script",
"value\"noproblem");
}
@Test
public void doubleQuote_noTerminator_failure() {
assertNoMatch("<link rel=\"stylesheet\" href=\"problem ></link>");
}
@Test
public void doubleQuotes_embeddedDoubleQuote_failure() {
assertNoMatch("<link href=\"value\"problem\"></link>");
}
@Test
public void doubleQuotes_embeddedSingleQuote_success() {
assertMatch("<link href=\"value'noproblem\"></link>",
"value'noproblem");
}
@Test
public void unquotedBadTerminator_failure() {
assertNoMatch("<script src=problem");
}
@Test
public void unquoted_embeddedEquals_failure() {
assertNoMatch("<script src=value=problem>");
}
@Test
public void unquoted_embeddedSingleQuote_failure() {
assertNoMatch("<script src=value'problem>");
}
@Test
public void unquoted_embeddedDoubleQuote_failure() {
assertNoMatch("<script src=value\"problem>");
}
@Test
public void unquoted_embeddedBackTick_failure() {
assertNoMatch("<script src=value`problem>");
}
@Test
public void unquoted_embeddedLessThan_failure() {
assertNoMatch("<script src=value<problem>");
}
@Test
public void spacesBeforeEquals_success() {
assertMatch("<link href =value rel='stylesheet'>", "value");
}
@Test
public void spacesAfterEquals_success() {
assertMatch("<script src= 'value'></script>", "value");
}
@Test
public void noSpacesAroundEquals_success() {
assertMatch("<script src=\"value\" ></script>", "value");
}
// ----------------------------------------------------------------------
// Substitution tests
// ----------------------------------------------------------------------
@Test
public void noMatch_noChange() {
assertVersionNotAdded(
"<script junk='/context/base/sub/file.js' ></script>",
"no match");
}
@Test
public void alreadyHasQueryString_noChange() {
assertVersionNotAdded(
"<script src='/context/base/sub/file.js?why' ></script>",
"has query");
}
@Test
public void doesntStartWithContextPath_noChange() {
assertVersionNotAdded(
"<script src='/notContext/base/sub/file.js' ></script>",
"context path");
}
@Test
public void noRealPath_noChange() {
assertVersionNotAdded(
"<script src='/context/base/sub/nofile.js' ></script>",
"real path");
}
@Test
@Ignore
public void exception_noChange() {
fail("exception_noChange not implemented");
}
@Test
public void doubleQuotes_substitution() {
assertVersionAdded( //
"<link href=\"/context/base/sub/file.css\" rel=stylesheet></link>", //
"<link href=\"/context/base/sub/file.css?version=9999\" rel=stylesheet></link>");
}
@Test
public void singleQuotes_substitution() {
assertVersionAdded( //
"<script src='/context/base/sub/file.js' ></script>", //
"<script src='/context/base/sub/file.js?version=9999' ></script>");
}
@Test
public void unquoted_substitution() {
assertVersionAdded( //
"<script type=text/javascript src=/context/base/sub/file.js ></script>", //
"<script type=text/javascript src=/context/base/sub/file.js?version&eq;9999 ></script>");
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void setContextPath(String contextPath) {
try {
Field f = UrlBuilder.class.getDeclaredField("contextPath");
f.setAccessible(true);
f.set(null, contextPath);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void assertMatch(String tag, String expected) {
TagVersionInfo info = new TagVersionInfo(tag);
try {
Field f = TagVersionInfo.class.getDeclaredField("match");
f.setAccessible(true);
MatchResult match = (MatchResult) f.get(info);
assertEquals(expected, match.group);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void assertNoMatch(String tag) {
TagVersionInfo info = new TagVersionInfo(tag);
assertFalse(info.hasVersion());
}
private void assertVersionAdded(String rawTag, String expected) {
String actual = createTag(rawTag);
String canonicalActual = actual.replaceAll("=[0-9a-f]{4}", "=9999")
.replaceAll("&eq;[0-9a-f]{4}", "&eq;9999");
assertEquals(expected, canonicalActual);
}
private void assertVersionNotAdded(String rawTag, String debugMessage) {
StringWriter writer = new StringWriter();
captureLogOutput(Tags.class, writer, true);
setLoggerLevel(Tags.class, Level.DEBUG);
String actual = createTag(rawTag);
assertEquals(rawTag, actual);
assertThat(writer.toString(), containsString(debugMessage));
}
private String createTag(String rawTag) {
Tags t = new Tags();
t.add(rawTag);
return t.list();
}
}

View file

@ -11,6 +11,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> . @prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
@prefix vitroWebapp: <java:edu.cornell.mannlib.vitro.webapp#> .
# ---------------------------- # ----------------------------
# #
@ -19,8 +20,8 @@
# #
:application :application
a <java:edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl> , a vitroWebapp:application.ApplicationImpl ,
<java:edu.cornell.mannlib.vitro.webapp.modules.Application> ; vitroWebapp:modules.Application ;
:hasSearchEngine :instrumentedSearchEngineWrapper ; :hasSearchEngine :instrumentedSearchEngineWrapper ;
:hasSearchIndexer :basicSearchIndexer ; :hasSearchIndexer :basicSearchIndexer ;
:hasImageProcessor :iioImageProcessor ; :hasImageProcessor :iioImageProcessor ;
@ -35,8 +36,8 @@
# #
:iioImageProcessor :iioImageProcessor
a <java:edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio.IIOImageProcessor> , a vitroWebapp:imageprocessor.imageio.IIOImageProcessor ,
<java:edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor> . vitroWebapp:modules.imageProcessor.ImageProcessor .
# ---------------------------- # ----------------------------
# #
@ -46,8 +47,8 @@
# #
:ptiFileStorage :ptiFileStorage
a <java:edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImplWrapper> , a vitroWebapp:filestorage.impl.FileStorageImplWrapper ,
<java:edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage> . vitroWebapp:modules.fileStorage.FileStorage .
# ---------------------------- # ----------------------------
# #
@ -58,13 +59,13 @@
# #
:instrumentedSearchEngineWrapper :instrumentedSearchEngineWrapper
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.InstrumentedSearchEngineWrapper> , a vitroWebapp:searchengine.InstrumentedSearchEngineWrapper ,
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> ; vitroWebapp:modules.searchEngine.SearchEngine ;
:wraps :solrSearchEngine . :wraps :solrSearchEngine .
:solrSearchEngine :solrSearchEngine
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine> , a vitroWebapp:searchengine.solr.SolrSearchEngine ,
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> . vitroWebapp:modules.searchEngine.SearchEngine .
# ---------------------------- # ----------------------------
# #
@ -74,8 +75,8 @@
# #
:basicSearchIndexer :basicSearchIndexer
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.SearchIndexerImpl> , a vitroWebapp:searchindex.SearchIndexerImpl ,
<java:edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer> ; vitroWebapp:modules.searchIndexer.SearchIndexer ;
:threadPoolSize "10" . :threadPoolSize "10" .
# ---------------------------- # ----------------------------
@ -89,26 +90,26 @@
# #
:sdbContentTripleSource :sdbContentTripleSource
a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.sdb.ContentTripleSourceSDB> , a vitroWebapp:triplesource.impl.sdb.ContentTripleSourceSDB ,
<java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource> . vitroWebapp:modules.tripleSource.ContentTripleSource .
#:tdbContentTripleSource #:tdbContentTripleSource
# a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.tdb.ContentTripleSourceTDB> , # a vitroWebapp:triplesource.impl.tdb.ContentTripleSourceTDB ,
# <java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource> ; # vitroWebapp:modules.tripleSource.ContentTripleSource ;
# # May be an absolute path, or relative to the Vitro home directory. # # May be an absolute path, or relative to the Vitro home directory.
# :hasTdbDirectory "tdbContentModels" . # :hasTdbDirectory "tdbContentModels" .
#:sparqlContentTripleSource #:sparqlContentTripleSource
# a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.virtuoso.ContentTripleSourceSPARQL> , # a vitroWebapp:triplesource.impl.virtuoso.ContentTripleSourceSPARQL ,
# <java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource> ; # vitroWebapp:modules.tripleSource.ContentTripleSource ;
# # The URI of the SPARQL endpoint for your triple-store. # # The URI of the SPARQL endpoint for your triple-store.
# :hasEndpointURI "PUT_YOUR_SPARQL_ENDPOINT_URI_HERE" ; # :hasEndpointURI "PUT_YOUR_SPARQL_ENDPOINT_URI_HERE" ;
# # The URI to use for SPARQL UPDATE calls against your triple-store. # # The URI to use for SPARQL UPDATE calls against your triple-store.
# :hasUpdateEndpointURI "PUT_THE UPDATE_URI_HERE" . # :hasUpdateEndpointURI "PUT_THE UPDATE_URI_HERE" .
#:virtuosoContentTripleSource #:virtuosoContentTripleSource
# a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.virtuoso.ContentTripleSourceVirtuoso> , # a vitroWebapp:triplesource.impl.virtuoso.ContentTripleSourceVirtuoso ,
# <java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource> ; # vitroWebapp:modules.tripleSource.ContentTripleSource ;
# # The URI of Virtuoso's SPARQL endpoint. # # The URI of Virtuoso's SPARQL endpoint.
# :hasEndpointURI "PUT_YOUR_VIRTUOSO_URI_HERE" ; # :hasEndpointURI "PUT_YOUR_VIRTUOSO_URI_HERE" ;
# # The URI to use for SPARQL UPDATE calls against Virtuoso. # # The URI to use for SPARQL UPDATE calls against Virtuoso.
@ -123,8 +124,8 @@
# #
:tdbConfigurationTripleSource :tdbConfigurationTripleSource
a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.tdb.ConfigurationTripleSourceTDB> , a vitroWebapp:triplesource.impl.tdb.ConfigurationTripleSourceTDB ,
<java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource> . vitroWebapp:modules.tripleSource.ConfigurationTripleSource .
# ---------------------------- # ----------------------------
# #
@ -134,5 +135,5 @@
# #
:jfactTBoxReasonerModule :jfactTBoxReasonerModule
a <java:edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.jfact.JFactTBoxReasonerModule> , a vitroWebapp:tboxreasoner.impl.jfact.JFactTBoxReasonerModule ,
<java:edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule> . vitroWebapp:modules.tboxreasoner.TBoxReasonerModule .

View file

@ -1,6 +1,8 @@
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> . @prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix searchIndex: <java:edu.cornell.mannlib.vitro.webapp.searchindex#> .
# #
# configure the SearchIndexer # configure the SearchIndexer
@ -8,8 +10,8 @@
# Individuals with these types will be excluded from the search index # Individuals with these types will be excluded from the search index
:searchExcluder_typeExcluder :searchExcluder_typeExcluder
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.ExcludeBasedOnType> , a searchIndex:exclusions.ExcludeBasedOnType ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder> ; searchIndex:exclusions.SearchIndexExcluder ;
:excludes :excludes
"http://www.w3.org/2002/07/owl#AnnotationProperty" , "http://www.w3.org/2002/07/owl#AnnotationProperty" ,
"http://www.w3.org/2002/07/owl#DatatypeProperty" , "http://www.w3.org/2002/07/owl#DatatypeProperty" ,
@ -17,8 +19,8 @@
# Individuals with types from these namespaces will be excluded from the search index. # Individuals with types from these namespaces will be excluded from the search index.
:searchExcluder_namespaceExcluder :searchExcluder_namespaceExcluder
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.ExcludeBasedOnNamespace> , a searchIndex:exclusions.ExcludeBasedOnNamespace ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder> ; searchIndex:exclusions.SearchIndexExcluder ;
:excludes :excludes
"http://vitro.mannlib.cornell.edu/ns/vitro/0.7#" , "http://vitro.mannlib.cornell.edu/ns/vitro/0.7#" ,
"http://vitro.mannlib.cornell.edu/ns/vitro/public#" , "http://vitro.mannlib.cornell.edu/ns/vitro/public#" ,
@ -27,38 +29,38 @@
# Individuals with URIs in these namespaces will be excluded from the search index. # Individuals with URIs in these namespaces will be excluded from the search index.
:searchExcluder_typeNamespaceExcluder :searchExcluder_typeNamespaceExcluder
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.ExcludeBasedOnTypeNamespace> , a searchIndex:exclusions.ExcludeBasedOnTypeNamespace ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder> ; searchIndex:exclusions.SearchIndexExcluder ;
:excludes :excludes
"http://vitro.mannlib.cornell.edu/ns/vitro/role#public" . "http://vitro.mannlib.cornell.edu/ns/vitro/role#public" .
:searchExcluder_syncingTypeExcluder :searchExcluder_syncingTypeExcluder
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SyncingExcludeBasedOnType> , a searchIndex:exclusions.SyncingExcludeBasedOnType ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder> . searchIndex:exclusions.SearchIndexExcluder .
# ------------------------------------ # ------------------------------------
:uriFinder_forDataProperties :uriFinder_forDataProperties
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.IndexingUriFinder> , a searchIndex:indexing.IndexingUriFinder ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.AdditionalURIsForDataProperties> . searchIndex:indexing.AdditionalURIsForDataProperties .
:uriFinder_forObjectProperties :uriFinder_forObjectProperties
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.IndexingUriFinder> , a searchIndex:indexing.IndexingUriFinder ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.AdditionalURIsForObjectProperties> . searchIndex:indexing.AdditionalURIsForObjectProperties .
:uriFinder_forTypeStatements :uriFinder_forTypeStatements
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.IndexingUriFinder> , a searchIndex:indexing.IndexingUriFinder ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.AdditionalURIsForTypeStatements> . searchIndex:indexing.AdditionalURIsForTypeStatements .
:uriFinder_forClassGroupChange :uriFinder_forClassGroupChange
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.IndexingUriFinder> , a searchIndex:indexing.IndexingUriFinder ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.indexing.URIsForClassGroupChange> . searchIndex:indexing.URIsForClassGroupChange .
# ------------------------------------ # ------------------------------------
:documentModifier_AllNames :documentModifier_AllNames
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.SelectQueryDocumentModifier> , a searchIndex:documentBuilding.SelectQueryDocumentModifier ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentModifier> ; searchIndex:documentBuilding.DocumentModifier ;
rdfs:label "All labels are added to name fields." ; rdfs:label "All labels are added to name fields." ;
:hasTargetField "nameRaw" ; :hasTargetField "nameRaw" ;
:hasSelectQuery """ :hasSelectQuery """
@ -70,8 +72,8 @@
""" . """ .
:documentModifier_NameFieldBooster :documentModifier_NameFieldBooster
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.FieldBooster> , a searchIndex:documentBuilding.FieldBooster ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentModifier> ; searchIndex:documentBuilding.DocumentModifier ;
:hasTargetField "nameRaw" ; :hasTargetField "nameRaw" ;
:hasTargetField "nameLowercase" ; :hasTargetField "nameLowercase" ;
:hasTargetField "nameUnstemmed" ; :hasTargetField "nameUnstemmed" ;
@ -79,5 +81,5 @@
:hasBoost "1.2"^^xsd:float . :hasBoost "1.2"^^xsd:float .
:documentModifier_thumbnailImageUrl :documentModifier_thumbnailImageUrl
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.ThumbnailImageURL> , a searchIndex:documentBuilding.ThumbnailImageURL ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentModifier> . searchIndex:documentBuilding.DocumentModifier .