VIVO-1246 Improve the ConfigurationBeanLoader (#52)
* 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 * VIVO-1247, remove duplicate code used with ConfigurationBeanLoader. Now that the @Property annotation includes cardinality parameters, we can remove a lot of duplicate code. * VIVO-1246 Move unit tests to the new location. * VIVO-1246 The documentation was in the wrong place.
This commit is contained in:
parent
fc83c9f48d
commit
a8ec633f71
22 changed files with 1414 additions and 703 deletions
|
@ -25,7 +25,6 @@ import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
|
|||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.BasicCombinedTripleSource;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||
|
||||
/**
|
||||
* The basic implementation of the Application interface.
|
||||
|
@ -69,15 +68,9 @@ public class ApplicationImpl implements Application {
|
|||
return searchEngine;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchEngine")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchEngine", minOccurs = 1, maxOccurs = 1)
|
||||
public void setSearchEngine(SearchEngine se) {
|
||||
if (searchEngine == null) {
|
||||
searchEngine = se;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple SearchEngine instances: "
|
||||
+ searchEngine + ", and " + se);
|
||||
}
|
||||
searchEngine = se;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,15 +78,9 @@ public class ApplicationImpl implements Application {
|
|||
return searchIndexer;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchIndexer")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchIndexer", minOccurs = 1, maxOccurs = 1)
|
||||
public void setSearchIndexer(SearchIndexer si) {
|
||||
if (searchIndexer == null) {
|
||||
searchIndexer = si;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple SearchIndexer instances: "
|
||||
+ searchIndexer + ", and " + si);
|
||||
}
|
||||
searchIndexer = si;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,15 +88,9 @@ public class ApplicationImpl implements Application {
|
|||
return imageProcessor;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasImageProcessor")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasImageProcessor", minOccurs = 1, maxOccurs = 1)
|
||||
public void setImageProcessor(ImageProcessor ip) {
|
||||
if (imageProcessor == null) {
|
||||
imageProcessor = ip;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple ImageProcessor instances: "
|
||||
+ imageProcessor + ", and " + ip);
|
||||
}
|
||||
imageProcessor = ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,15 +98,9 @@ public class ApplicationImpl implements Application {
|
|||
return fileStorage;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasFileStorage")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasFileStorage", minOccurs = 1, maxOccurs = 1)
|
||||
public void setFileStorage(FileStorage fs) {
|
||||
if (fileStorage == null) {
|
||||
fileStorage = fs;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple FileStorage instances: "
|
||||
+ fileStorage + ", and " + fs);
|
||||
}
|
||||
fileStorage = fs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,15 +108,9 @@ public class ApplicationImpl implements Application {
|
|||
return contentTripleSource;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasContentTripleSource")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasContentTripleSource", minOccurs = 1, maxOccurs = 1)
|
||||
public void setContentTripleSource(ContentTripleSource source) {
|
||||
if (contentTripleSource == null) {
|
||||
contentTripleSource = source;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of ContentTripleSource: "
|
||||
+ contentTripleSource + ", and " + source);
|
||||
}
|
||||
contentTripleSource = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,15 +118,9 @@ public class ApplicationImpl implements Application {
|
|||
return configurationTripleSource;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource", minOccurs = 1, maxOccurs = 1)
|
||||
public void setConfigurationTripleSource(ConfigurationTripleSource source) {
|
||||
if (configurationTripleSource == null) {
|
||||
configurationTripleSource = source;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of ConfigurationTripleSource: "
|
||||
+ configurationTripleSource + ", and " + source);
|
||||
}
|
||||
configurationTripleSource = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,47 +128,9 @@ public class ApplicationImpl implements Application {
|
|||
return tboxReasonerModule;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTBoxReasonerModule")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTBoxReasonerModule", minOccurs = 1, maxOccurs = 1)
|
||||
public void setTBoxReasonerModule(TBoxReasonerModule module) {
|
||||
if (tboxReasonerModule == null) {
|
||||
tboxReasonerModule = module;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of TBoxReasonerModule: "
|
||||
+ tboxReasonerModule + ", and " + module);
|
||||
}
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (searchEngine == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a SearchEngine.");
|
||||
}
|
||||
if (searchIndexer == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a SearchIndexer.");
|
||||
}
|
||||
if (imageProcessor == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include an ImageProcessor.");
|
||||
}
|
||||
if (fileStorage == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a FileStorage.");
|
||||
}
|
||||
if (contentTripleSource == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a ContentTripleSource.");
|
||||
}
|
||||
if (configurationTripleSource == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a ConfigurationTripleSource.");
|
||||
}
|
||||
if (tboxReasonerModule == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a TBoxReasonerModule.");
|
||||
}
|
||||
tboxReasonerModule = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,6 @@ import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
|
|||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocument;
|
||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocumentList;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||
|
||||
/**
|
||||
* Manages the life-cycle of the SearchEngine. Adds logging, controlled by
|
||||
|
@ -40,26 +39,11 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine {
|
|||
|
||||
private volatile LifecycleState lifecycleState = NEW;
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#wraps")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#wraps", minOccurs = 1, maxOccurs = 1)
|
||||
public void setInnerEngine(SearchEngine inner) {
|
||||
if (innerEngine == null) {
|
||||
innerEngine = inner;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple SearchEngine instancess: "
|
||||
+ innerEngine + ", and " + inner);
|
||||
}
|
||||
innerEngine = inner;
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (innerEngine == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a wrapped SearchEngine.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Complain unless ACTIVE.
|
||||
*/
|
||||
|
@ -222,13 +206,13 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine {
|
|||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
||||
private static class SearchResponseForDocumentCount implements SearchResponse {
|
||||
private static class SearchResponseForDocumentCount implements
|
||||
SearchResponse {
|
||||
private final int count;
|
||||
|
||||
public SearchResponseForDocumentCount(int count) {
|
||||
|
@ -254,28 +238,29 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine {
|
|||
public List<SearchFacetField> getFacetFields() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private class EmptyDocumentListWithCount implements SearchResultDocumentList {
|
||||
@Override
|
||||
public Iterator<SearchResultDocument> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumFound() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchResultDocument get(int i) {
|
||||
throw new ArrayIndexOutOfBoundsException(i);
|
||||
}
|
||||
|
||||
private class EmptyDocumentListWithCount implements
|
||||
SearchResultDocumentList {
|
||||
@Override
|
||||
public Iterator<SearchResultDocument> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumFound() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchResultDocument get(int i) {
|
||||
throw new ArrayIndexOutOfBoundsException(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
private Set<IndexingUriFinder> uriFinders;
|
||||
private WebappDaoFactory wadf;
|
||||
|
||||
private boolean rebuildOnUnpause = false;
|
||||
private boolean rebuildOnUnpause = false;
|
||||
|
||||
private volatile int paused = 0;
|
||||
|
||||
|
@ -110,25 +110,14 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
// ConfigurationBeanLoader methods.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#threadPoolSize")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#threadPoolSize", minOccurs = 1, maxOccurs = 1)
|
||||
public void setThreadPoolSize(String size) {
|
||||
if (threadPoolSize == null) {
|
||||
threadPoolSize = Integer.parseInt(size);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple values for threadPoolSize: "
|
||||
+ threadPoolSize + ", and " + size);
|
||||
}
|
||||
threadPoolSize = Integer.parseInt(size);
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (threadPoolSize == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a value for threadPoolSize.");
|
||||
} else {
|
||||
this.pool = new WorkerThreadPool(threadPoolSize);
|
||||
}
|
||||
this.pool = new WorkerThreadPool(threadPoolSize);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
@ -241,7 +230,7 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized void schedulePendingUris() {
|
||||
private synchronized void schedulePendingUris() {
|
||||
if (paused == 0 && pendingUris.size() > 0) {
|
||||
scheduleUpdatesForUris(pendingUris);
|
||||
pendingUris = new ArrayList<>();
|
||||
|
@ -278,13 +267,14 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
if (changes == null || changes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (paused > 0) {
|
||||
if (paused > 0) {
|
||||
if (addToPendingStatements(changes)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.scheduleTask(new UpdateStatementsTask(new IndexerConfigImpl(this), changes));
|
||||
scheduler.scheduleTask(new UpdateStatementsTask(new IndexerConfigImpl(
|
||||
this), changes));
|
||||
log.debug("Scheduled updates for " + changes.size() + " statements.");
|
||||
}
|
||||
|
||||
|
@ -306,13 +296,14 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
if (uris == null || uris.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (paused > 0) {
|
||||
if (paused > 0) {
|
||||
if (pendingUris.addAll(uris)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.scheduleTask(new UpdateUrisTask(new IndexerConfigImpl(this), uris));
|
||||
scheduler.scheduleTask(new UpdateUrisTask(new IndexerConfigImpl(this),
|
||||
uris));
|
||||
log.debug("Scheduled updates for " + uris.size() + " uris.");
|
||||
}
|
||||
|
||||
|
@ -332,13 +323,14 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
return;
|
||||
}
|
||||
fireEvent(REBUILD_REQUESTED);
|
||||
if (paused > 0) {
|
||||
if (paused > 0) {
|
||||
// Make sure that we are rebuilding when we unpause
|
||||
// and don't bother noting any other changes until unpaused
|
||||
rebuildOnUnpause = true;
|
||||
return;
|
||||
}
|
||||
scheduler.scheduleTask(new RebuildIndexTask(new IndexerConfigImpl(this)));
|
||||
rebuildOnUnpause = true;
|
||||
return;
|
||||
}
|
||||
scheduler
|
||||
.scheduleTask(new RebuildIndexTask(new IndexerConfigImpl(this)));
|
||||
log.debug("Scheduled a full rebuild.");
|
||||
}
|
||||
|
||||
|
@ -447,13 +439,13 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
}
|
||||
|
||||
public synchronized void scheduleTask(Task task) {
|
||||
if (!started) {
|
||||
deferredQueue.add(task);
|
||||
log.debug("added task to deferred queue: " + task);
|
||||
} else {
|
||||
if (!started) {
|
||||
deferredQueue.add(task);
|
||||
log.debug("added task to deferred queue: " + task);
|
||||
} else {
|
||||
taskQueue.scheduleTask(task);
|
||||
log.debug("added task to task queue: " + task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
|
@ -463,8 +455,9 @@ public class SearchIndexerImpl implements SearchIndexer {
|
|||
|
||||
private void processDeferredTasks() {
|
||||
for (Task task : deferredQueue) {
|
||||
taskQueue.scheduleTask(task);
|
||||
log.debug("moved task from deferred queue to task queue: " + task);
|
||||
taskQueue.scheduleTask(task);
|
||||
log.debug("moved task from deferred queue to task queue: "
|
||||
+ task);
|
||||
}
|
||||
deferredQueue.clear();
|
||||
}
|
||||
|
|
|
@ -17,27 +17,18 @@ public class FieldBooster implements DocumentModifier {
|
|||
private final List<String> fieldNames = new ArrayList<>();
|
||||
private volatile Float boost;
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTargetField")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTargetField", minOccurs = 1)
|
||||
public void addTargetField(String fieldName) {
|
||||
fieldNames.add(fieldName);
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBoost")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBoost", minOccurs = 1)
|
||||
public void setBoost(float boost) {
|
||||
this.boost = boost;
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() {
|
||||
if (boost == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a boost value.");
|
||||
}
|
||||
if (fieldNames.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a target field.");
|
||||
}
|
||||
|
||||
Set<String> uniqueFieldNames = new HashSet<>(fieldNames);
|
||||
List<String> duplicateFieldNames = new ArrayList<>(fieldNames);
|
||||
for (String fn : uniqueFieldNames) {
|
||||
|
|
|
@ -74,7 +74,7 @@ public class SelectQueryUriFinder implements IndexingUriFinder,
|
|||
label = l;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSelectQuery")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSelectQuery", minOccurs = 1)
|
||||
public void addQuery(String query) {
|
||||
queries.add(query);
|
||||
}
|
||||
|
@ -89,10 +89,6 @@ public class SelectQueryUriFinder implements IndexingUriFinder,
|
|||
if (label == null) {
|
||||
label = this.getClass().getSimpleName() + ":" + this.hashCode();
|
||||
}
|
||||
if (queries.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration contains no queries for " + label);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceFactorySingle;
|
|||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging.LoggingRDFServiceFactory;
|
||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql.RDFServiceSparql;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||
|
||||
/**
|
||||
|
@ -41,34 +40,14 @@ public class ContentTripleSourceSPARQL extends ContentTripleSource {
|
|||
private Dataset dataset;
|
||||
private ModelMaker modelMaker;
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasEndpointURI")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasEndpointURI", minOccurs = 1, maxOccurs = 1)
|
||||
public void setEndpointURI(String eUri) {
|
||||
if (endpointURI == null) {
|
||||
endpointURI = eUri;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of EndpointURI: "
|
||||
+ endpointURI + ", and " + eUri);
|
||||
}
|
||||
endpointURI = eUri;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUpdateEndpointURI")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUpdateEndpointURI", maxOccurs = 1)
|
||||
public void setUpdateEndpointURI(String ueUri) {
|
||||
if (updateEndpointURI == null) {
|
||||
updateEndpointURI = ueUri;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of UpdateEndpointURI: "
|
||||
+ updateEndpointURI + ", and " + ueUri);
|
||||
}
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (endpointURI == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include an EndpointURI.");
|
||||
}
|
||||
updateEndpointURI = ueUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.tdb.RDFServiceTDB;
|
|||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging.LoggingRDFServiceFactory;
|
||||
import edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaDataSourceSetupBase;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||
|
||||
/**
|
||||
|
@ -49,23 +48,9 @@ public class ContentTripleSourceTDB extends ContentTripleSource {
|
|||
private Dataset dataset;
|
||||
private ModelMaker modelMaker;
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTdbDirectory")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTdbDirectory", minOccurs = 1, maxOccurs = 1)
|
||||
public void setTdbPath(String path) {
|
||||
if (tdbPath == null) {
|
||||
tdbPath = path;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of TdbDirectory: "
|
||||
+ tdbPath + ", and " + path);
|
||||
}
|
||||
}
|
||||
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (tdbPath == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a TdbDirectory.");
|
||||
}
|
||||
tdbPath = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,7 +91,8 @@ public class ContentTripleSourceTDB extends ContentTripleSource {
|
|||
}
|
||||
|
||||
private void checkForFirstTimeStartup() {
|
||||
if (this.dataset.getNamedModel(ModelNames.TBOX_ASSERTIONS).getGraph().isEmpty()) {
|
||||
if (this.dataset.getNamedModel(ModelNames.TBOX_ASSERTIONS).getGraph()
|
||||
.isEmpty()) {
|
||||
JenaDataSourceSetupBase.thisIsFirstStartup();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
|||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso.RDFServiceVirtuoso;
|
||||
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.sparql.ContentTripleSourceSPARQL;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||
|
||||
/**
|
||||
|
@ -19,54 +18,19 @@ public class ContentTripleSourceVirtuoso extends ContentTripleSourceSPARQL {
|
|||
private String username;
|
||||
private String password;
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBaseURI")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBaseURI", minOccurs = 1, maxOccurs = 1)
|
||||
public void setBaseUri(String uri) {
|
||||
if (baseUri == null) {
|
||||
baseUri = uri;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of BaseURI: "
|
||||
+ baseUri + ", and " + uri);
|
||||
}
|
||||
baseUri = uri;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUsername")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUsername", minOccurs = 1, maxOccurs = 1)
|
||||
public void setUsername(String user) {
|
||||
if (username == null) {
|
||||
username = user;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of Username: "
|
||||
+ username + ", and " + user);
|
||||
}
|
||||
username = user;
|
||||
}
|
||||
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasPassword")
|
||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasPassword", minOccurs = 1, maxOccurs = 1)
|
||||
public void setPassword(String pass) {
|
||||
if (password == null) {
|
||||
password = pass;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Configuration includes multiple instances of Password: "
|
||||
+ password + ", and " + pass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Validation
|
||||
public void validate() throws Exception {
|
||||
if (baseUri == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a BaseURI.");
|
||||
}
|
||||
if (username == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a Username.");
|
||||
}
|
||||
if (password == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a Password.");
|
||||
}
|
||||
password = pass;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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,58 +41,131 @@ 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);
|
||||
private static Map<String, Method> parseValidationAnnotations(Class<?> clazz)
|
||||
throws InstanceWrapperException {
|
||||
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;
|
||||
}
|
||||
if (method.getParameterTypes().length > 0) {
|
||||
throw new InstanceWrapperException("Validation method '"
|
||||
+ method + "' should not have parameters.");
|
||||
}
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
throw new InstanceWrapperException("Validation method '"
|
||||
+ method + "' should return void.");
|
||||
}
|
||||
methods.put(name, method);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Set<Method> parseValidationAnnotations(Class<?> concreteClass)
|
||||
throws InstanceWrapperException {
|
||||
Set<Method> methods = new HashSet<>();
|
||||
for (Method method : concreteClass.getDeclaredMethods()) {
|
||||
if (method.getAnnotation(Validation.class) == null) {
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
if (method.getParameterTypes().length > 0) {
|
||||
throw new InstanceWrapperException("Validation method '"
|
||||
+ method + "' should not have parameters.");
|
||||
}
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
throw new InstanceWrapperException("Validation method '"
|
||||
+ method + "' should return void.");
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
return methods;
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.lang.reflect.Method;
|
|||
import org.apache.jena.datatypes.RDFDatatype;
|
||||
import org.apache.jena.datatypes.xsd.impl.RDFLangString;
|
||||
import org.apache.jena.rdf.model.Literal;
|
||||
import org.apache.jena.rdf.model.Property;
|
||||
import org.apache.jena.rdf.model.RDFNode;
|
||||
import org.apache.jena.rdf.model.Statement;
|
||||
|
||||
|
@ -26,38 +25,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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -101,24 +103,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() {
|
||||
|
@ -135,8 +138,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;
|
||||
}
|
||||
|
||||
|
@ -149,8 +152,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;
|
||||
}
|
||||
|
||||
|
@ -163,8 +166,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;
|
||||
}
|
||||
|
||||
|
@ -177,10 +180,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() {
|
||||
|
@ -191,6 +207,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()) {
|
||||
|
@ -213,20 +241,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 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.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,7 +19,6 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -32,16 +30,13 @@ import stubs.javax.servlet.http.HttpSessionStub;
|
|||
import org.apache.jena.rdf.model.Model;
|
||||
import org.apache.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 +45,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 +264,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 +727,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
// Additional tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO
|
||||
|
@ -1049,6 +736,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
fail("circularReferencesAreNotFatal not implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO deals with circularity.
|
||||
|
@ -1057,6 +745,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
|
|||
fail("subordinateObjectCantBeLoaded_leavesNoAccessibleInstanceOfParent not implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO deals with circularity.
|
||||
|
@ -1065,42 +754,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,100 @@
|
|||
/* $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.apache.jena.rdf.model.Model;
|
||||
import org.junit.Before;
|
||||
|
||||
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ModelAccessFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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,251 @@
|
|||
/* $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.apache.jena.rdf.model.Statement;
|
||||
import org.junit.Test;
|
||||
|
||||
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,338 @@
|
|||
/* $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.apache.jena.rdf.model.Statement;
|
||||
import org.junit.Test;
|
||||
|
||||
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