VIVO-1246 improve the ConfigurationBeanLoader:
Add cardinality parameters minOccurs and maxOccurs Create README.md document in the edu.cornell.mannlib.vitro.webapp.utils.configuration package Split large class of unit tests into separate classes by functionality
This commit is contained in:
parent
046d445639
commit
481a215206
12 changed files with 1332 additions and 441 deletions
|
@ -116,6 +116,7 @@ public class ConfigurationBeanLoader {
|
|||
WrappedInstance<T> wrapper = InstanceWrapper.wrap(parsedRdf
|
||||
.getConcreteClass());
|
||||
wrapper.satisfyInterfaces(ctx, req);
|
||||
wrapper.checkCardinality(parsedRdf.getPropertyStatements());
|
||||
wrapper.setProperties(this, parsedRdf.getPropertyStatements());
|
||||
wrapper.validate();
|
||||
return wrapper.getInstance();
|
||||
|
|
|
@ -6,7 +6,9 @@ import java.lang.reflect.Method;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyMethod;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException;
|
||||
|
@ -16,11 +18,17 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.Propert
|
|||
* instance of the class.
|
||||
*/
|
||||
public class InstanceWrapper {
|
||||
private static final Log log = LogFactory.getLog(InstanceWrapper.class);
|
||||
|
||||
public static <T> WrappedInstance<T> wrap(Class<? extends T> concreteClass)
|
||||
throws InstanceWrapperException {
|
||||
return new WrappedInstance<T>(createInstance(concreteClass),
|
||||
parsePropertyAnnotations(concreteClass),
|
||||
parseValidationAnnotations(concreteClass));
|
||||
T instance = createInstance(concreteClass);
|
||||
HashSet<Method> validationMethods = new HashSet<>(
|
||||
parseValidationAnnotations(concreteClass).values());
|
||||
Map<String, PropertyMethod> propertyMethods = new PropertyAnnotationsMap(
|
||||
concreteClass).byUri();
|
||||
return new WrappedInstance<T>(instance, propertyMethods,
|
||||
validationMethods);
|
||||
}
|
||||
|
||||
private static <T> T createInstance(Class<? extends T> concreteClass)
|
||||
|
@ -33,44 +41,22 @@ public class InstanceWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
private static Map<String, PropertyMethod> parsePropertyAnnotations(
|
||||
Class<?> concreteClass) throws InstanceWrapperException {
|
||||
Map<String, PropertyMethod> map = new HashMap<>();
|
||||
for (Method method : concreteClass.getDeclaredMethods()) {
|
||||
Property annotation = method.getAnnotation(Property.class);
|
||||
if (annotation == null) {
|
||||
continue;
|
||||
}
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
throw new InstanceWrapperException("Property method '" + method
|
||||
+ "' should return void.");
|
||||
}
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
throw new InstanceWrapperException("Property method '" + method
|
||||
+ "' must accept exactly one parameter.");
|
||||
}
|
||||
|
||||
String uri = annotation.uri();
|
||||
if (map.containsKey(uri)) {
|
||||
throw new InstanceWrapperException(
|
||||
"Two property methods have the same URI value: "
|
||||
+ map.get(uri).getMethod() + ", and " + method);
|
||||
}
|
||||
try {
|
||||
map.put(uri, PropertyType.createPropertyMethod(method));
|
||||
} catch (PropertyTypeException e) {
|
||||
throw new InstanceWrapperException(
|
||||
"Failed to create the PropertyMethod", e);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Set<Method> parseValidationAnnotations(Class<?> concreteClass)
|
||||
private static Map<String, Method> parseValidationAnnotations(Class<?> clazz)
|
||||
throws InstanceWrapperException {
|
||||
Set<Method> methods = new HashSet<>();
|
||||
for (Method method : concreteClass.getDeclaredMethods()) {
|
||||
if (Object.class.equals(clazz)) {
|
||||
return new HashMap<>();
|
||||
} else {
|
||||
Map<String, Method> methods = parseValidationAnnotations(clazz
|
||||
.getSuperclass());
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
String name = method.getName();
|
||||
if (methods.containsKey(name)) {
|
||||
Method m = methods.get(name);
|
||||
throw new InstanceWrapperException("Method " + name
|
||||
+ " in " + method.getDeclaringClass().getName()
|
||||
+ " overrides a validation method in "
|
||||
+ m.getDeclaringClass().getName());
|
||||
}
|
||||
if (method.getAnnotation(Validation.class) == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -82,10 +68,105 @@ public class InstanceWrapper {
|
|||
throw new InstanceWrapperException("Validation method '"
|
||||
+ method + "' should return void.");
|
||||
}
|
||||
methods.add(method);
|
||||
methods.put(name, method);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertyAnnotationsMap {
|
||||
private Map<String, PropertyMethod> mapByUri = new HashMap<>();
|
||||
private Map<String, PropertyMethod> mapByName = new HashMap<>();
|
||||
|
||||
public PropertyAnnotationsMap(Class<?> clazz)
|
||||
throws InstanceWrapperException {
|
||||
if (!Object.class.equals(clazz)) {
|
||||
populateTheMaps(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateTheMaps(Class<?> clazz)
|
||||
throws InstanceWrapperException {
|
||||
PropertyAnnotationsMap superMap = new PropertyAnnotationsMap(
|
||||
clazz.getSuperclass());
|
||||
mapByUri = superMap.byUri();
|
||||
mapByName = superMap.byName();
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
String name = method.getName();
|
||||
|
||||
Method matchByName = methodByName(name);
|
||||
if (matchByName != null) {
|
||||
throw new InstanceWrapperException("Method " + name
|
||||
+ " in " + clazz.getName()
|
||||
+ " conflicts with a property method in "
|
||||
+ matchByName.getDeclaringClass().getName());
|
||||
}
|
||||
|
||||
Property annotation = method.getAnnotation(Property.class);
|
||||
if (annotation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
throw new InstanceWrapperException("Property method '"
|
||||
+ method + "' should return void.");
|
||||
}
|
||||
|
||||
if (method.getParameterTypes().length != 1) {
|
||||
throw new InstanceWrapperException("Property method '"
|
||||
+ method + "' must accept exactly one parameter.");
|
||||
}
|
||||
|
||||
String uri = annotation.uri();
|
||||
Method matchByUri = methodByUri(uri);
|
||||
if (matchByUri != null) {
|
||||
throw new InstanceWrapperException(
|
||||
"Two property methods have the same URI value: "
|
||||
+ matchByUri + ", and " + method);
|
||||
}
|
||||
|
||||
if (annotation.minOccurs() < 0) {
|
||||
throw new InstanceWrapperException(
|
||||
"minOccurs must not be negative.");
|
||||
}
|
||||
|
||||
if (annotation.maxOccurs() < annotation.minOccurs()) {
|
||||
throw new InstanceWrapperException(
|
||||
"maxOccurs must not be less than minOccurs.");
|
||||
}
|
||||
|
||||
try {
|
||||
PropertyMethod pm = PropertyType.createPropertyMethod(
|
||||
method, annotation);
|
||||
mapByUri.put(uri, pm);
|
||||
mapByName.put(name, pm);
|
||||
} catch (PropertyTypeException e) {
|
||||
throw new InstanceWrapperException(
|
||||
"Failed to create the PropertyMethod", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Method methodByName(String name) {
|
||||
PropertyMethod pm = mapByName.get(name);
|
||||
return (pm == null) ? null : pm.getMethod();
|
||||
}
|
||||
|
||||
private Method methodByUri(String name) {
|
||||
PropertyMethod pm = mapByUri.get(name);
|
||||
return (pm == null) ? null : pm.getMethod();
|
||||
}
|
||||
|
||||
public Map<String, PropertyMethod> byUri() {
|
||||
return mapByUri;
|
||||
}
|
||||
|
||||
public Map<String, PropertyMethod> byName() {
|
||||
return mapByName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class InstanceWrapperException extends Exception {
|
||||
public InstanceWrapperException(String message) {
|
||||
|
|
|
@ -15,4 +15,6 @@ import java.lang.annotation.Target;
|
|||
@Target(ElementType.METHOD)
|
||||
public @interface Property {
|
||||
String uri();
|
||||
int minOccurs() default 0;
|
||||
int maxOccurs() default Integer.MAX_VALUE;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.lang.reflect.Method;
|
|||
|
||||
import com.hp.hpl.jena.datatypes.RDFDatatype;
|
||||
import com.hp.hpl.jena.rdf.model.Literal;
|
||||
import com.hp.hpl.jena.rdf.model.Property;
|
||||
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
|
||||
|
@ -25,38 +24,41 @@ public enum PropertyType {
|
|||
RESOURCE {
|
||||
@Override
|
||||
public PropertyStatement buildPropertyStatement(Statement s) {
|
||||
return new ResourcePropertyStatement(s.getPredicate(), s
|
||||
return new ResourcePropertyStatement(s.getPredicate().getURI(), s
|
||||
.getObject().asResource().getURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMethod buildPropertyMethod(Method method) {
|
||||
return new ResourcePropertyMethod(method);
|
||||
protected PropertyMethod buildPropertyMethod(Method method,
|
||||
Property annotation) {
|
||||
return new ResourcePropertyMethod(method, annotation);
|
||||
}
|
||||
|
||||
},
|
||||
STRING {
|
||||
@Override
|
||||
public PropertyStatement buildPropertyStatement(Statement s) {
|
||||
return new StringPropertyStatement(s.getPredicate(), s.getObject()
|
||||
.asLiteral().getString());
|
||||
return new StringPropertyStatement(s.getPredicate().getURI(), s
|
||||
.getObject().asLiteral().getString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMethod buildPropertyMethod(Method method) {
|
||||
return new StringPropertyMethod(method);
|
||||
protected PropertyMethod buildPropertyMethod(Method method,
|
||||
Property annotation) {
|
||||
return new StringPropertyMethod(method, annotation);
|
||||
}
|
||||
},
|
||||
FLOAT {
|
||||
@Override
|
||||
public PropertyStatement buildPropertyStatement(Statement s) {
|
||||
return new FloatPropertyStatement(s.getPredicate(), s.getObject()
|
||||
.asLiteral().getFloat());
|
||||
return new FloatPropertyStatement(s.getPredicate().getURI(), s
|
||||
.getObject().asLiteral().getFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMethod buildPropertyMethod(Method method) {
|
||||
return new FloatPropertyMethod(method);
|
||||
protected PropertyMethod buildPropertyMethod(Method method,
|
||||
Property annotation) {
|
||||
return new FloatPropertyMethod(method, annotation);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,24 +102,25 @@ public enum PropertyType {
|
|||
return type.buildPropertyStatement(s);
|
||||
}
|
||||
|
||||
public static PropertyMethod createPropertyMethod(Method method)
|
||||
throws PropertyTypeException {
|
||||
public static PropertyMethod createPropertyMethod(Method method,
|
||||
Property annotation) throws PropertyTypeException {
|
||||
Class<?> parameterType = method.getParameterTypes()[0];
|
||||
PropertyType type = PropertyType.typeForParameterType(parameterType);
|
||||
return type.buildPropertyMethod(method);
|
||||
return type.buildPropertyMethod(method, annotation);
|
||||
}
|
||||
|
||||
protected abstract PropertyStatement buildPropertyStatement(Statement s);
|
||||
|
||||
protected abstract PropertyMethod buildPropertyMethod(Method method);
|
||||
protected abstract PropertyMethod buildPropertyMethod(Method method,
|
||||
Property annotation);
|
||||
|
||||
public static abstract class PropertyStatement {
|
||||
private final PropertyType type;
|
||||
private final String predicateUri;
|
||||
|
||||
public PropertyStatement(PropertyType type, Property predicate) {
|
||||
public PropertyStatement(PropertyType type, String predicateUri) {
|
||||
this.type = type;
|
||||
this.predicateUri = predicate.getURI();
|
||||
this.predicateUri = predicateUri;
|
||||
}
|
||||
|
||||
public PropertyType getType() {
|
||||
|
@ -134,8 +137,8 @@ public enum PropertyType {
|
|||
public static class ResourcePropertyStatement extends PropertyStatement {
|
||||
private final String objectUri;
|
||||
|
||||
public ResourcePropertyStatement(Property predicate, String objectUri) {
|
||||
super(RESOURCE, predicate);
|
||||
public ResourcePropertyStatement(String predicateUri, String objectUri) {
|
||||
super(RESOURCE, predicateUri);
|
||||
this.objectUri = objectUri;
|
||||
}
|
||||
|
||||
|
@ -148,8 +151,8 @@ public enum PropertyType {
|
|||
public static class StringPropertyStatement extends PropertyStatement {
|
||||
private final String string;
|
||||
|
||||
public StringPropertyStatement(Property predicate, String string) {
|
||||
super(STRING, predicate);
|
||||
public StringPropertyStatement(String predicateUri, String string) {
|
||||
super(STRING, predicateUri);
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
|
@ -162,8 +165,8 @@ public enum PropertyType {
|
|||
public static class FloatPropertyStatement extends PropertyStatement {
|
||||
private final float f;
|
||||
|
||||
public FloatPropertyStatement(Property predicate, float f) {
|
||||
super(FLOAT, predicate);
|
||||
public FloatPropertyStatement(String predicateUri, float f) {
|
||||
super(FLOAT, predicateUri);
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
|
@ -176,10 +179,23 @@ public enum PropertyType {
|
|||
public static abstract class PropertyMethod {
|
||||
protected final PropertyType type;
|
||||
protected final Method method;
|
||||
protected final String propertyUri;
|
||||
protected final int minOccurs;
|
||||
protected final int maxOccurs;
|
||||
|
||||
public PropertyMethod(PropertyType type, Method method) {
|
||||
// Add cardinality values here! Final, with getters.
|
||||
public PropertyMethod(PropertyType type, Method method,
|
||||
Property annotation) {
|
||||
this.type = type;
|
||||
this.method = method;
|
||||
this.propertyUri = annotation.uri();
|
||||
this.minOccurs = annotation.minOccurs();
|
||||
this.maxOccurs = annotation.maxOccurs();
|
||||
checkCardinalityBounds();
|
||||
}
|
||||
|
||||
private void checkCardinalityBounds() {
|
||||
// This is where we check for negative values or out of order.
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
|
@ -190,6 +206,18 @@ public enum PropertyType {
|
|||
return method.getParameterTypes()[0];
|
||||
}
|
||||
|
||||
public String getPropertyUri() {
|
||||
return propertyUri;
|
||||
}
|
||||
|
||||
public int getMinOccurs() {
|
||||
return minOccurs;
|
||||
}
|
||||
|
||||
public int getMaxOccurs() {
|
||||
return maxOccurs;
|
||||
}
|
||||
|
||||
public void confirmCompatible(PropertyStatement ps)
|
||||
throws PropertyTypeException {
|
||||
if (type != ps.getType()) {
|
||||
|
@ -212,20 +240,20 @@ public enum PropertyType {
|
|||
}
|
||||
|
||||
public static class ResourcePropertyMethod extends PropertyMethod {
|
||||
public ResourcePropertyMethod(Method method) {
|
||||
super(RESOURCE, method);
|
||||
public ResourcePropertyMethod(Method method, Property annotation) {
|
||||
super(RESOURCE, method, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringPropertyMethod extends PropertyMethod {
|
||||
public StringPropertyMethod(Method method) {
|
||||
super(STRING, method);
|
||||
public StringPropertyMethod(Method method, Property annotation) {
|
||||
super(STRING, method, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FloatPropertyMethod extends PropertyMethod {
|
||||
public FloatPropertyMethod(Method method) {
|
||||
super(FLOAT, method);
|
||||
public FloatPropertyMethod(Method method, Property annotation) {
|
||||
super(FLOAT, method, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
# package edu.cornell.mannlib.vitro.webapp.utils.configuration;
|
||||
## Overview
|
||||
### Purpose
|
||||
This package consists of `ConfigurationBeanLoader` and associated classes.
|
||||
`ConfigurationBeanLoader` will instantiate and populate objects according to a
|
||||
description encoded in the triples of an RDF Graph,
|
||||
and annotations within the Java class of the instantiated object.
|
||||
|
||||
The description must include
|
||||
|
||||
+ the URI of exactly one concrete Java class, from which the instance will be created.
|
||||
|
||||
The description may also include
|
||||
|
||||
+ URIs of Java interfaces which the concrete class implements.
|
||||
The description may be use to satisfy a request
|
||||
for any of those interfaces, as well as a request for the concrete class.
|
||||
|
||||
+ Data properties. These will be passed to "property methods" in the instance
|
||||
as part of the creation/initialization. The data value must be an untyped
|
||||
literal (String) or a numeric literal (Float).
|
||||
|
||||
+ Object properties. The URI is assumed to be that of another loader description.
|
||||
The loader will attempt to instantiate the described object, and pass it to
|
||||
the appropriate property method on the original instance. The result may be a
|
||||
network of instances, nested to an arbitrary level.
|
||||
|
||||
The loader also recognizes two special interfaces: `RequestModelsUser` and `ContextModelsUser`.
|
||||
If a created instance implements these interfaces,
|
||||
the loader will provide the appropriate `ModelAccess` object,
|
||||
allowing the instance to access the Vitro triple-stores.
|
||||
|
||||
### Examples of use
|
||||
|
||||
#### ApplicationSetup
|
||||
|
||||
When Vitro starts up, `ApplicationSetup` uses a `ConfigurationBeanLoader` to instantiate the Vitro's component modules.
|
||||
The loader creates an RDF Graph from the file `applicationSetup.n3` and instantiates a `SearchEngine` instance,
|
||||
a `FileStorage` instance, etc.
|
||||
|
||||
Here is some RDF that might be used by `ApplicationSetup`:
|
||||
|
||||
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
|
||||
:application
|
||||
a <java:edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl> ,
|
||||
<java:edu.cornell.mannlib.vitro.webapp.modules.Application> ;
|
||||
:hasSearchEngine :instrumentedSearchEngineWrapper ;
|
||||
:hasFileStorage :ptiFileStorage .
|
||||
|
||||
:ptiFileStorage
|
||||
a <java:edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImplWrapper> ,
|
||||
<java:edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage> .
|
||||
|
||||
:instrumentedSearchEngineWrapper
|
||||
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.InstrumentedSearchEngineWrapper> ,
|
||||
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> ;
|
||||
:wraps :solrSearchEngine .
|
||||
|
||||
:solrSearchEngine
|
||||
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine> ,
|
||||
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> .
|
||||
|
||||
In this case, the `ConfigurationBeanLoader` would be asked to load all instances of
|
||||
`edu.cornell.mannlib.vitro.webapp.modules.Application`.
|
||||
The application individual is declared to be both an `Application` and an `ApplicationImpl`.
|
||||
This is valid because `Application` is an interface, and `ApplicationImpl` implements that interface.
|
||||
An instance of `ApplicationImpl` will be created.
|
||||
|
||||
The application instance has two child objects: a `SearchEngine` and a `FileStorage`.
|
||||
These objects will also be created, and calls will be made to the application's "property methods" (see below).
|
||||
|
||||
The `SearchEngine` in turn has a child object, so that also will be created, and provided to the `SearchEngine`.
|
||||
|
||||
#### SearchIndexer
|
||||
|
||||
When Vitro's `SearchIndexer` is initialized, it uses a `ConfigurationBeanLoader` to create
|
||||
lists of `SearchIndexExcluder`s, `DocumentModifier`s, and `IndexingUriFinder`s.
|
||||
Descriptions of these are taken from Vitro's display model.
|
||||
|
||||
## Specifications
|
||||
|
||||
### ConfigurationBeanLoader
|
||||
The principal methods are:
|
||||
|
||||
+ `public <T> T loadInstance(String uri, Class<T> resultClass) throws ConfigurationBeanLoaderException`
|
||||
+ Search the graph for triples that describe the `uri`.
|
||||
If the description indicates that the individual is of type `resultClass`, create an instance and populate it.
|
||||
Return a reference to the created instance. Throw an exception if the `uri` does not exist in the graph,
|
||||
or if the description does not correctly describe an individual.
|
||||
|
||||
The `resultClass` may be an interface. In that case, each individual must also have a type statement that refers
|
||||
to a concrete class that satisfies the interface. An instance of the concrete class will be created.
|
||||
|
||||
+ `public <T> Set<T> loadAll(Class<T> resultClass) throws ConfigurationBeanLoaderException`
|
||||
+ 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`.
|
||||
|
||||
### Restrictions on instantiated classes.
|
||||
Each class to be instantiated must have a niladic constructor.
|
||||
|
||||
### Property methods
|
||||
When the loader encounters a data property or an object property in a description,
|
||||
it will look in the instantiated class for a method tagged with the
|
||||
`edu.cornell.mannlib.vitro.webapp.utils.configuration.Property` annotation.
|
||||
|
||||
For example:
|
||||
|
||||
@Property(
|
||||
uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource"
|
||||
minOccurs = 1,
|
||||
maxOccurs = 3)
|
||||
public void setTBoxReasonerModule(TBoxReasonerModule module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
|
||||
In more detail:
|
||||
|
||||
+ A class must contain exactly one method that serves each property URI in the description.
|
||||
+ 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.
|
||||
+ The name of the property method is immaterial, except that there must not be another method
|
||||
with the same name in the class.
|
||||
+ Property methods in superclasses will be recognized and accepted, but they may not be
|
||||
overridden in a subclass.
|
||||
+ If `minOccurs` is omitted, the default is `0`. If `minOccurs` is provided, it must be non-negative.
|
||||
+ If `maxOccurs` is omitted, the default is `MAXINT`. If `maxOccurs` is provided, it must not be less than `minOccurs`.
|
||||
|
||||
When instantiating:
|
||||
|
||||
+ The parameter on a property method must match the value supplied in the RDF description.
|
||||
+ If the type of the parameter is `Float`, the object of the triple must be a numeric literal, or
|
||||
an untyped literal that can be parsed as a number.
|
||||
+ If the type of the parameter is `String`, the object of the triple must be a String literal or an untyped literal.
|
||||
+ If the type of the parameter is another class, then the object of the triple must be the URI of
|
||||
another RDF description, from which the loader can create an instance of the required class.
|
||||
+ The number of values for a given property URI must not be less than the `minOccurs` value on the corresponding property method.
|
||||
+ The number of values for a given property URI must not be greater than the `maxOccurs` value on the corresponding property method.
|
||||
|
||||
### Validation methods
|
||||
When the loader has satisfied all of the properties in an instance, it will
|
||||
look in the instantiated class for any methods tagged with the
|
||||
`edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation` annotation.
|
||||
|
||||
For example:
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (baseUri == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a BaseURI.");
|
||||
}
|
||||
}
|
||||
|
||||
Each such method will be called by the loader, and provides a opportunity to
|
||||
confirm that the bean has been properly initialized.
|
||||
|
||||
Again, in detail:
|
||||
|
||||
+ 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
|
||||
+ method with the same name in the lass.
|
||||
+ Validation methods in superclasses will be called, but may not be overridden in a subclass.
|
||||
|
||||
### Life cycle
|
||||
For each instance that the loader creates, the loader will:
|
||||
|
||||
+ Call the appropriate property method for each property in the description.
|
||||
For object properties, this includes recursive calls to create subordinate objects.
|
||||
The order of property method calls is undefined.
|
||||
+ Call the validation methods on the class. The order of validation method calls is undefined.
|
||||
|
||||
If any property method or validation method throws an exception, the process stops,
|
||||
and the exception is propagated upward.
|
|
@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration;
|
|||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -63,6 +64,45 @@ public class WrappedInstance<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The loader provides the distilled property statements from the RDF. Check
|
||||
* that they satisfy the cardinality requested on their methods.
|
||||
*/
|
||||
public void checkCardinality(Set<PropertyStatement> propertyStatements)
|
||||
throws CardinalityException {
|
||||
Map<String, Integer> statementCounts = countPropertyStatementsByPredicateUri(propertyStatements);
|
||||
for (PropertyMethod pm : propertyMethods.values()) {
|
||||
Integer c = statementCounts.get(pm.getPropertyUri());
|
||||
int count = (c == null) ? 0 : c;
|
||||
if (count < pm.getMinOccurs()) {
|
||||
throw new CardinalityException("Expecting at least "
|
||||
+ pm.getMinOccurs() + " values for '"
|
||||
+ pm.getPropertyUri() + "', but found " + count + ".");
|
||||
}
|
||||
if (count > pm.getMaxOccurs()) {
|
||||
throw new CardinalityException("Expecting no more than "
|
||||
+ pm.getMaxOccurs() + " values for '"
|
||||
+ pm.getPropertyUri() + "', but found " + count + ".");
|
||||
}
|
||||
}
|
||||
statementCounts.hashCode();
|
||||
}
|
||||
|
||||
private Map<String, Integer> countPropertyStatementsByPredicateUri(
|
||||
Set<PropertyStatement> propertyStatements) {
|
||||
Map<String, Integer> statementCounts = new HashMap<>();
|
||||
for (String pmPredicateUri : propertyMethods.keySet()) {
|
||||
int count = 0;
|
||||
for (PropertyStatement ps : propertyStatements) {
|
||||
if (ps.getPredicateUri().equals(pmPredicateUri)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
statementCounts.put(pmPredicateUri, count);
|
||||
}
|
||||
return statementCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* The loader provides the distilled property statements from the RDF, to
|
||||
* populate the instance.
|
||||
|
@ -76,7 +116,6 @@ public class WrappedInstance<T> {
|
|||
if (pm == null) {
|
||||
throw new NoSuchPropertyMethodException(ps);
|
||||
}
|
||||
|
||||
pm.confirmCompatible(ps);
|
||||
|
||||
if (ps instanceof ResourcePropertyStatement) {
|
||||
|
@ -132,4 +171,10 @@ public class WrappedInstance<T> {
|
|||
}
|
||||
}
|
||||
|
||||
public static class CardinalityException extends Exception {
|
||||
public CardinalityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -43,6 +43,12 @@ public class ModelUtilitiesTestHelper {
|
|||
createProperty(propertyUri), createPlainLiteral(objectValue));
|
||||
}
|
||||
|
||||
public static Statement dataProperty(String subjectUri, String propertyUri,
|
||||
Float objectValue) {
|
||||
return createStatement(createResource(subjectUri),
|
||||
createProperty(propertyUri), createTypedLiteral(objectValue));
|
||||
}
|
||||
|
||||
public static Statement dataProperty(String subjectUri, String propertyUri,
|
||||
Object objectValue, XSDDatatype dataType) {
|
||||
return createStatement(createResource(subjectUri),
|
||||
|
|
|
@ -5,7 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration;
|
|||
import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDfloat;
|
||||
import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDstring;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement;
|
||||
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
|
||||
|
@ -20,28 +19,19 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import stubs.edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccessFactoryStub;
|
||||
import stubs.javax.servlet.ServletContextStub;
|
||||
import stubs.javax.servlet.http.HttpServletRequestStub;
|
||||
import stubs.javax.servlet.http.HttpSessionStub;
|
||||
|
||||
import com.hp.hpl.jena.rdf.model.Model;
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
|
||||
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ModelAccessFactory;
|
||||
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.InstanceWrapper.InstanceWrapperException;
|
||||
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.ValidationFailedException;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
|
@ -50,47 +40,8 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.Vali
|
|||
* instances by URIs, so if a property refers to a created instance, we just
|
||||
* pass it in.
|
||||
*/
|
||||
public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
||||
private static final String GENERIC_INSTANCE_URI = "http://mytest.edu/some_instance";
|
||||
private static final String GENERIC_PROPERTY_URI = "http://mytest.edu/some_property";
|
||||
|
||||
private static final String SIMPLE_SUCCESS_INSTANCE_URI = "http://mytest.edu/simple_success_instance";
|
||||
|
||||
private static final String FULL_SUCCESS_INSTANCE_URI = "http://mytest.edu/full_success_instance";
|
||||
private static final String FULL_SUCCESS_BOOST_PROPERTY = "http://mydomain.edu/hasBoost";
|
||||
private static final String FULL_SUCCESS_TEXT_PROPERTY = "http://mydomain.edu/hasText";
|
||||
private static final String FULL_SUCCESS_HELPER_PROPERTY = "http://mydomain.edu/hasHelper";
|
||||
private static final String FULL_SUCCESS_HELPER_INSTANCE_URI = "http://mytest.edu/full_success_helper_instance";
|
||||
|
||||
private ServletContextStub ctx;
|
||||
private HttpSessionStub session;
|
||||
private HttpServletRequestStub req;
|
||||
|
||||
private Model model;
|
||||
|
||||
private ConfigurationBeanLoader loader;
|
||||
private ConfigurationBeanLoader noRequestLoader;
|
||||
private ConfigurationBeanLoader noContextLoader;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
ctx = new ServletContextStub();
|
||||
|
||||
session = new HttpSessionStub();
|
||||
session.setServletContext(ctx);
|
||||
|
||||
req = new HttpServletRequestStub();
|
||||
req.setSession(session);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ModelAccessFactory maf = new ModelAccessFactoryStub();
|
||||
|
||||
model = model();
|
||||
|
||||
loader = new ConfigurationBeanLoader(model, req);
|
||||
noRequestLoader = new ConfigurationBeanLoader(model, ctx);
|
||||
noContextLoader = new ConfigurationBeanLoader(model);
|
||||
}
|
||||
public class ConfigurationBeanLoaderTest extends
|
||||
ConfigurationBeanLoaderTestBase {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor tests
|
||||
|
@ -308,276 +259,6 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasNoParameter_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(NoParameterOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
NoParameterOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"must accept exactly one parameter"));
|
||||
}
|
||||
|
||||
public static class NoParameterOnPropertyMethod {
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesNoParameters() {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasMultipleParameters_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(MultipleParametersOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
MultipleParametersOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"must accept exactly one parameter"));
|
||||
}
|
||||
|
||||
public static class MultipleParametersOnPropertyMethod {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesMultipleParameters(String s, Float f) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasInvalidParameter_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(InvalidParameterOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
InvalidParameterOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"Failed to create the PropertyMethod"));
|
||||
}
|
||||
|
||||
public static class InvalidParameterOnPropertyMethod {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesInvalidParameters(byte b) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodDoesNotReturnVoid_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodMustReturnVoid.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodMustReturnVoid.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class, "should return void"));
|
||||
}
|
||||
|
||||
public static class PropertyMethodMustReturnVoid {
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public String methodReturnIsNotVoid(String s) {
|
||||
// Not suitable as a property method.
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodNotAccessible_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodIsPrivate.class)));
|
||||
model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"can't store in a private method."));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodIsPrivate.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(PropertyTypeException.class,
|
||||
"Property method failed."));
|
||||
}
|
||||
|
||||
public static class PropertyMethodIsPrivate {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
private void methodReturnIsNotVoid(String s) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodThrowsException_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodFails.class)));
|
||||
model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"exception while loading."));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodFails.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(PropertyTypeException.class,
|
||||
"Property method failed."));
|
||||
}
|
||||
|
||||
public static class PropertyMethodFails {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodThrowsException(String s) {
|
||||
if (true) {
|
||||
throw new RuntimeException("property method fails.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodDuplicateUri_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(TwoMethodsWithSameUri.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
TwoMethodsWithSameUri.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"methods have the same URI"));
|
||||
}
|
||||
|
||||
public static class TwoMethodsWithSameUri {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void firstProperty(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void secondProperty(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodHasParameters_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodWithParameter.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodWithParameter.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"should not have parameters"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodWithParameter {
|
||||
@SuppressWarnings("unused")
|
||||
@Validation
|
||||
public void validateWithParameter(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodDoesNotReturnVoid_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodShouldReturnVoid.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodShouldReturnVoid.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class, "should return void"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodShouldReturnVoid {
|
||||
@Validation
|
||||
public String validateWithReturnType() {
|
||||
return "Hi there!";
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodNotAccessible_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodIsPrivate.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodIsPrivate.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(ValidationFailedException.class,
|
||||
"Error executing validation method"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodIsPrivate {
|
||||
@Validation
|
||||
private void validateIsPrivate() {
|
||||
// private method
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodThrowsException_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationThrowsException.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationThrowsException.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(ValidationFailedException.class,
|
||||
"Error executing validation method"));
|
||||
}
|
||||
|
||||
public static class ValidationThrowsException {
|
||||
@Validation
|
||||
public void validateFails() {
|
||||
throw new RuntimeException("from validation method");
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void loaderCantSatisfyContextModelsUser_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
|
@ -1041,6 +722,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
// Additional tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO
|
||||
|
@ -1049,6 +731,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
fail("circularReferencesAreNotFatal not implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO deals with circularity.
|
||||
|
@ -1057,6 +740,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
fail("subordinateObjectCantBeLoaded_leavesNoAccessibleInstanceOfParent not implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO deals with circularity.
|
||||
|
@ -1065,42 +749,4 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
fail("parentObjectCantBeLoaded_leavesNoAccessibleInstanceOfSubordinate not implemented");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods for simple failure
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private void expectSimpleFailure(Class<?> failureClass,
|
||||
ExpectedThrowable expected, ExpectedThrowable cause)
|
||||
throws ConfigurationBeanLoaderException {
|
||||
expectException(expected.getClazz(), expected.getMessageSubstring(),
|
||||
cause.getClazz(), cause.getMessageSubstring());
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass);
|
||||
}
|
||||
|
||||
private ExpectedThrowable throwable(Class<? extends Throwable> clazz,
|
||||
String messageSubstring) {
|
||||
return new ExpectedThrowable(clazz, messageSubstring);
|
||||
}
|
||||
|
||||
private static class ExpectedThrowable {
|
||||
private final Class<? extends Throwable> clazz;
|
||||
private final String messageSubstring;
|
||||
|
||||
public ExpectedThrowable(Class<? extends Throwable> clazz,
|
||||
String messageSubstring) {
|
||||
this.clazz = clazz;
|
||||
this.messageSubstring = messageSubstring;
|
||||
}
|
||||
|
||||
public Class<? extends Throwable> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public String getMessageSubstring() {
|
||||
return messageSubstring;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* $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.model;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import stubs.edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccessFactoryStub;
|
||||
import stubs.javax.servlet.ServletContextStub;
|
||||
import stubs.javax.servlet.http.HttpServletRequestStub;
|
||||
import stubs.javax.servlet.http.HttpSessionStub;
|
||||
|
||||
import com.hp.hpl.jena.rdf.model.Model;
|
||||
|
||||
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ModelAccessFactory;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
public class ConfigurationBeanLoaderTestBase extends AbstractTestClass {
|
||||
protected static final String GENERIC_INSTANCE_URI = "http://mytest.edu/some_instance";
|
||||
protected static final String GENERIC_PROPERTY_URI = "http://mytest.edu/some_property";
|
||||
|
||||
protected static final String SIMPLE_SUCCESS_INSTANCE_URI = "http://mytest.edu/simple_success_instance";
|
||||
|
||||
protected static final String FULL_SUCCESS_INSTANCE_URI = "http://mytest.edu/full_success_instance";
|
||||
protected static final String FULL_SUCCESS_BOOST_PROPERTY = "http://mydomain.edu/hasBoost";
|
||||
protected static final String FULL_SUCCESS_TEXT_PROPERTY = "http://mydomain.edu/hasText";
|
||||
protected static final String FULL_SUCCESS_HELPER_PROPERTY = "http://mydomain.edu/hasHelper";
|
||||
protected static final String FULL_SUCCESS_HELPER_INSTANCE_URI = "http://mytest.edu/full_success_helper_instance";
|
||||
|
||||
private ServletContextStub ctx;
|
||||
private HttpSessionStub session;
|
||||
private HttpServletRequestStub req;
|
||||
|
||||
protected Model model;
|
||||
|
||||
protected ConfigurationBeanLoader loader;
|
||||
protected ConfigurationBeanLoader noRequestLoader;
|
||||
protected ConfigurationBeanLoader noContextLoader;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
ctx = new ServletContextStub();
|
||||
|
||||
session = new HttpSessionStub();
|
||||
session.setServletContext(ctx);
|
||||
|
||||
req = new HttpServletRequestStub();
|
||||
req.setSession(session);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
ModelAccessFactory maf = new ModelAccessFactoryStub();
|
||||
|
||||
model = model();
|
||||
|
||||
loader = new ConfigurationBeanLoader(model, req);
|
||||
noRequestLoader = new ConfigurationBeanLoader(model, ctx);
|
||||
noContextLoader = new ConfigurationBeanLoader(model);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods for simple failure
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
protected void expectSimpleFailure(Class<?> failureClass,
|
||||
ExpectedThrowable expected, ExpectedThrowable cause)
|
||||
throws ConfigurationBeanLoaderException {
|
||||
expectException(expected.getClazz(), expected.getMessageSubstring(),
|
||||
cause.getClazz(), cause.getMessageSubstring());
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass);
|
||||
}
|
||||
|
||||
protected ExpectedThrowable throwable(Class<? extends Throwable> clazz,
|
||||
String messageSubstring) {
|
||||
return new ExpectedThrowable(clazz, messageSubstring);
|
||||
}
|
||||
|
||||
private static class ExpectedThrowable {
|
||||
private final Class<? extends Throwable> clazz;
|
||||
private final String messageSubstring;
|
||||
|
||||
public ExpectedThrowable(Class<? extends Throwable> clazz,
|
||||
String messageSubstring) {
|
||||
this.clazz = clazz;
|
||||
this.messageSubstring = messageSubstring;
|
||||
}
|
||||
|
||||
public Class<? extends Throwable> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public String getMessageSubstring() {
|
||||
return messageSubstring;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/* $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.dataProperty;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement;
|
||||
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.CardinalityException;
|
||||
|
||||
/**
|
||||
* Tests for minOccurs, maxOccurs on the @Property annotation.
|
||||
*/
|
||||
public class ConfigurationBeanLoader_Cardinality_Test extends
|
||||
ConfigurationBeanLoaderTestBase {
|
||||
|
||||
private static final Statement[] FOUND_NONE = new Statement[0];
|
||||
|
||||
private static final Statement[] FOUND_ONE = new Statement[] { dataProperty(
|
||||
GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, "value One") };
|
||||
|
||||
private static final Statement[] FOUND_TWO = new Statement[] {
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value One"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Two") };
|
||||
|
||||
private static final Statement[] FOUND_THREE = new Statement[] {
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value One"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Two"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Three") };
|
||||
|
||||
private static final Statement[] FOUND_FOUR = new Statement[] {
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value One"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Two"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Three"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"value Four") };
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void minOccursIsNegative_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(NegativeMinOccurs.class)));
|
||||
model.add(objectProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"http://some.other/uri"));
|
||||
|
||||
expectSimpleFailure(
|
||||
NegativeMinOccurs.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"must not be negative"));
|
||||
}
|
||||
|
||||
public static class NegativeMinOccurs {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI, minOccurs = -1)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void maxOccursLessThanMinOccurs_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(MaxOccursLessThanMinOccurs.class)));
|
||||
model.add(objectProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"http://some.other/uri"));
|
||||
|
||||
expectSimpleFailure(
|
||||
MaxOccursLessThanMinOccurs.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"not be less than minOccurs"));
|
||||
}
|
||||
|
||||
public static class MaxOccursLessThanMinOccurs {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 1)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void expectingSomeFoundNone_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectingSome.class)));
|
||||
model.add(FOUND_NONE);
|
||||
|
||||
expectSimpleFailure(
|
||||
ExpectingSome.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(CardinalityException.class, "Expecting at least 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expectingSomeFoundFewer_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectingSome.class)));
|
||||
model.add(FOUND_ONE);
|
||||
|
||||
expectSimpleFailure(
|
||||
ExpectingSome.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(CardinalityException.class, "Expecting at least 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expectingSomeFoundMore_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectingSome.class)));
|
||||
model.add(FOUND_FOUR);
|
||||
|
||||
expectSimpleFailure(
|
||||
ExpectingSome.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(CardinalityException.class,
|
||||
"Expecting no more than 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expectingSomeFoundSome_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectingSome.class)));
|
||||
model.add(FOUND_THREE);
|
||||
Set<ExpectingSome> instances = loader.loadAll(ExpectingSome.class);
|
||||
assertEquals(1, instances.size());
|
||||
model.removeAll();
|
||||
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectingSome.class)));
|
||||
model.add(FOUND_TWO);
|
||||
instances = loader.loadAll(ExpectingSome.class);
|
||||
assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
public static class ExpectingSome {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 3)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void notSpecifiedFoundNone_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(CardinalityNotSpecified.class)));
|
||||
model.add(FOUND_NONE);
|
||||
|
||||
Set<CardinalityNotSpecified> instances = loader
|
||||
.loadAll(CardinalityNotSpecified.class);
|
||||
assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSpecifiedFoundSome_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(CardinalityNotSpecified.class)));
|
||||
model.add(FOUND_FOUR);
|
||||
|
||||
Set<CardinalityNotSpecified> instances = loader
|
||||
.loadAll(CardinalityNotSpecified.class);
|
||||
assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
public static class CardinalityNotSpecified {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void expectNoneFoundNone_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectNone.class)));
|
||||
model.add(FOUND_NONE);
|
||||
|
||||
Set<ExpectNone> instances = loader.loadAll(ExpectNone.class);
|
||||
assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
public static class ExpectNone {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI, maxOccurs = 0)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void expectExactlyFoundExactly_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ExpectTwo.class)));
|
||||
model.add(FOUND_TWO);
|
||||
|
||||
Set<ExpectTwo> instances = loader.loadAll(ExpectTwo.class);
|
||||
assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
public static class ExpectTwo {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 2)
|
||||
public void setValue(String value) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
/* $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.dataProperty;
|
||||
import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement;
|
||||
import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException;
|
||||
|
||||
/**
|
||||
* Tests of the @Property annotation.
|
||||
*/
|
||||
public class ConfigurationBeanLoader_PropertyTest extends
|
||||
ConfigurationBeanLoaderTestBase {
|
||||
protected static final String OTHER_PROPERTY_URI = "http://mytest.edu/different_property";
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasNoParameter_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(NoParameterOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
NoParameterOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"must accept exactly one parameter"));
|
||||
}
|
||||
|
||||
public static class NoParameterOnPropertyMethod {
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesNoParameters() {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasMultipleParameters_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(MultipleParametersOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
MultipleParametersOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"must accept exactly one parameter"));
|
||||
}
|
||||
|
||||
public static class MultipleParametersOnPropertyMethod {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesMultipleParameters(String s, Float f) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodHasInvalidParameter_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(InvalidParameterOnPropertyMethod.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
InvalidParameterOnPropertyMethod.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"Failed to create the PropertyMethod"));
|
||||
}
|
||||
|
||||
public static class InvalidParameterOnPropertyMethod {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodTakesInvalidParameters(byte b) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodDoesNotReturnVoid_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodMustReturnVoid.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodMustReturnVoid.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class, "should return void"));
|
||||
}
|
||||
|
||||
public static class PropertyMethodMustReturnVoid {
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public String methodReturnIsNotVoid(String s) {
|
||||
// Not suitable as a property method.
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodNotAccessible_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodIsPrivate.class)));
|
||||
model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"can't store in a private method."));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodIsPrivate.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(PropertyTypeException.class,
|
||||
"Property method failed."));
|
||||
}
|
||||
|
||||
public static class PropertyMethodIsPrivate {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
private void methodReturnIsNotVoid(String s) {
|
||||
// Not suitable as a property method.
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodThrowsException_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodFails.class)));
|
||||
model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"exception while loading."));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodFails.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(PropertyTypeException.class,
|
||||
"Property method failed."));
|
||||
}
|
||||
|
||||
public static class PropertyMethodFails {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void methodThrowsException(String s) {
|
||||
if (true) {
|
||||
throw new RuntimeException("property method fails.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void propertyMethodDuplicateUri_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(TwoMethodsWithSameUri.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
TwoMethodsWithSameUri.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"methods have the same URI"));
|
||||
}
|
||||
|
||||
public static class TwoMethodsWithSameUri {
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void firstProperty(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void secondProperty(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void superclassContainsPropertyAnnotation_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(new Statement[] {
|
||||
typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(EmptyPropertyMethodSubclass.class)),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"Value") });
|
||||
|
||||
EmptyPropertyMethodSubclass instance = loader.loadInstance(
|
||||
GENERIC_INSTANCE_URI, EmptyPropertyMethodSubclass.class);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertEquals("Value", instance.value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyMethodOverridesPropertyMethod_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PropertyMethodOverPropertyMethodSubclass.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
PropertyMethodOverPropertyMethodSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"conflicts with a property method"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void plainMethodOverridesPropertyMethod_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PlainOverPropertyMethodSubclass.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
PlainOverPropertyMethodSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"conflicts with a property method"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriConflictsBetweenSubclassAndSuperclassPropertyMethods_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ConflictingUriPropertyMethodSubclass.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ConflictingUriPropertyMethodSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"Two property methods have the same URI"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyMethodSameNameButDoesNotOverride_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(new Statement[] {
|
||||
typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(DistinctPropertyMethodSubclass.class)),
|
||||
dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI,
|
||||
"Value"),
|
||||
dataProperty(GENERIC_INSTANCE_URI, OTHER_PROPERTY_URI,
|
||||
100.0F) });
|
||||
|
||||
expectSimpleFailure(
|
||||
DistinctPropertyMethodSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"conflicts with a property method"));
|
||||
}
|
||||
|
||||
public static class PropertyMethodSuperclass {
|
||||
public String value = null;
|
||||
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void propertySuper(String v) {
|
||||
if (value != null) {
|
||||
throw new RuntimeException("propertySuper has already run.");
|
||||
}
|
||||
value = v;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmptyPropertyMethodSubclass extends
|
||||
PropertyMethodSuperclass {
|
||||
// Just want to see that the superclass method is run.
|
||||
}
|
||||
|
||||
public static class DistinctPropertyMethodSubclass extends
|
||||
PropertyMethodSuperclass {
|
||||
public float fvalue;
|
||||
|
||||
@Property(uri = OTHER_PROPERTY_URI)
|
||||
public void propertySuper(Float f) {
|
||||
if (fvalue != 0.0) {
|
||||
throw new RuntimeException("propertySub has already run.");
|
||||
}
|
||||
fvalue = f;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConflictingUriPropertyMethodSubclass extends
|
||||
PropertyMethodSuperclass {
|
||||
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void propertyConflict(String v) {
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
public static class PropertyMethodOverPropertyMethodSubclass extends
|
||||
EmptyPropertyMethodSubclass {
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
@Property(uri = GENERIC_PROPERTY_URI)
|
||||
public void propertySuper(String v) {
|
||||
// Should fail (two levels down)
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlainOverPropertyMethodSubclass extends
|
||||
PropertyMethodSuperclass {
|
||||
@SuppressWarnings("unused")
|
||||
public void propertySuper(Float f) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/* $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.toJavaUri;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ValidationFailedException;
|
||||
|
||||
/**
|
||||
* Test the @Validation annotation.
|
||||
*/
|
||||
public class ConfigurationBeanLoader_ValidationTest extends
|
||||
ConfigurationBeanLoaderTestBase {
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodHasParameters_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodWithParameter.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodWithParameter.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"should not have parameters"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodWithParameter {
|
||||
@SuppressWarnings("unused")
|
||||
@Validation
|
||||
public void validateWithParameter(String s) {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodDoesNotReturnVoid_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodShouldReturnVoid.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodShouldReturnVoid.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class, "should return void"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodShouldReturnVoid {
|
||||
@Validation
|
||||
public String validateWithReturnType() {
|
||||
return "Hi there!";
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodNotAccessible_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationMethodIsPrivate.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationMethodIsPrivate.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(ValidationFailedException.class,
|
||||
"Error executing validation method"));
|
||||
}
|
||||
|
||||
public static class ValidationMethodIsPrivate {
|
||||
@Validation
|
||||
private void validateIsPrivate() {
|
||||
// private method
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void validationMethodThrowsException_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationThrowsException.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationThrowsException.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(ValidationFailedException.class,
|
||||
"Error executing validation method"));
|
||||
}
|
||||
|
||||
public static class ValidationThrowsException {
|
||||
@Validation
|
||||
public void validateFails() {
|
||||
throw new RuntimeException("from validation method");
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
@Test
|
||||
public void superclassContainsValidationMethod_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(EmptyValidationSubclass.class)));
|
||||
|
||||
EmptyValidationSubclass instance = loader.loadInstance(
|
||||
GENERIC_INSTANCE_URI, EmptyValidationSubclass.class);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertTrue(instance.validatorSuperHasRun);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void superclassAndSubclassContainValidationMethods_success()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(AdditionalValidationSubclass.class)));
|
||||
|
||||
AdditionalValidationSubclass instance = loader.loadInstance(
|
||||
GENERIC_INSTANCE_URI, AdditionalValidationSubclass.class);
|
||||
|
||||
assertNotNull(instance);
|
||||
assertTrue(instance.validatorSuperHasRun);
|
||||
assertTrue(instance.validatorSubHasRun);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationMethodOverridesValidationMethod_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(ValidationOverValidationSubclass.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
ValidationOverValidationSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"overrides a validation method"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void plainMethodOverridesValidationMethod_throwsException()
|
||||
throws ConfigurationBeanLoaderException {
|
||||
model.add(typeStatement(GENERIC_INSTANCE_URI,
|
||||
toJavaUri(PlainOverValidationSubclass.class)));
|
||||
|
||||
expectSimpleFailure(
|
||||
PlainOverValidationSubclass.class,
|
||||
throwable(ConfigurationBeanLoaderException.class,
|
||||
"Failed to load"),
|
||||
throwable(InstanceWrapperException.class,
|
||||
"overrides a validation method"));
|
||||
}
|
||||
|
||||
public static class ValidationSuperclass {
|
||||
public boolean validatorSuperHasRun = false;
|
||||
|
||||
@Validation
|
||||
public void validatorSuper() {
|
||||
if (validatorSuperHasRun) {
|
||||
throw new RuntimeException("validatorSuper has already run.");
|
||||
}
|
||||
validatorSuperHasRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmptyValidationSubclass extends ValidationSuperclass {
|
||||
// Just want to see that the superclass validation is run.
|
||||
}
|
||||
|
||||
public static class AdditionalValidationSubclass extends
|
||||
ValidationSuperclass {
|
||||
public boolean validatorSubHasRun = false;
|
||||
|
||||
@Validation
|
||||
public void validatorSub() {
|
||||
if (validatorSubHasRun) {
|
||||
throw new RuntimeException("validatorSub has already run.");
|
||||
}
|
||||
validatorSubHasRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValidationOverValidationSubclass extends
|
||||
EmptyValidationSubclass {
|
||||
@Override
|
||||
@Validation
|
||||
public void validatorSuper() {
|
||||
// Should fail (two levels down)
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlainOverValidationSubclass extends
|
||||
ValidationSuperclass {
|
||||
@Override
|
||||
public void validatorSuper() {
|
||||
// Should fail
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue