diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index df6760e4f..fa7249297 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -2,19 +2,22 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; +import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource; + import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; -import static com.hp.hpl.jena.rdf.model.ResourceFactory.*; + import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.vocabulary.RDF; -import edu.cornell.mannlib.vitro.webapp.utils.jena.Critical; +import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel; +import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedModel; /** * Load one or more Configuration beans from a specified model. @@ -48,7 +51,7 @@ public class ConfigurationBeanLoader { // ---------------------------------------------------------------------- /** Must not be null. */ - private final Model model; + private final LockableModel locking; /** * May be null, but the loader will be unable to satisfy instances of @@ -63,26 +66,34 @@ public class ConfigurationBeanLoader { private final HttpServletRequest req; public ConfigurationBeanLoader(Model model) { - this(model, null, null); + this(new LockableModel(model), null, null); + } + + public ConfigurationBeanLoader(LockableModel locking) { + this(locking, null, null); } public ConfigurationBeanLoader(Model model, ServletContext ctx) { - this(model, ctx, null); + this(new LockableModel(model), ctx, null); + } + + public ConfigurationBeanLoader(LockableModel locking, ServletContext ctx) { + this(locking, ctx, null); } public ConfigurationBeanLoader(Model model, HttpServletRequest req) { - this(model, - (req == null) ? null : req.getSession().getServletContext(), - req); + this(new LockableModel(model), req); } - private ConfigurationBeanLoader(Model model, ServletContext ctx, - HttpServletRequest req) { - if (model == null) { - throw new NullPointerException("model may not be null."); - } + public ConfigurationBeanLoader(LockableModel locking, HttpServletRequest req) { + this(locking, (req == null) ? null : req.getSession() + .getServletContext(), req); + } - this.model = model; + private ConfigurationBeanLoader(LockableModel locking, ServletContext ctx, + HttpServletRequest req) { + this.locking = Objects.requireNonNull(locking, + "locking may not be null."); this.req = req; this.ctx = ctx; } @@ -100,8 +111,8 @@ public class ConfigurationBeanLoader { } try { - ConfigurationRdf parsedRdf = ConfigurationRdfParser.parse(model, - uri, resultClass); + ConfigurationRdf parsedRdf = ConfigurationRdfParser.parse( + locking, uri, resultClass); WrappedInstance wrapper = InstanceWrapper.wrap(parsedRdf .getConcreteClass()); wrapper.satisfyInterfaces(ctx, req); @@ -120,9 +131,9 @@ public class ConfigurationBeanLoader { public Set loadAll(Class resultClass) throws ConfigurationBeanLoaderException { Set uris = new HashSet<>(); - try (Critical section = Critical.read(model)) { - List resources = model.listResourcesWithProperty( - RDF.type, createResource(toJavaUri(resultClass))).toList(); + try (LockedModel m = locking.read()) { + List resources = m.listResourcesWithProperty(RDF.type, + createResource(toJavaUri(resultClass))).toList(); for (Resource r : resources) { if (r.isURIResource()) { uris.add(r.getURI()); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java index 507e71319..72dc8223a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java @@ -11,9 +11,9 @@ import static edu.cornell.mannlib.vitro.webapp.utils.configuration.Configuration import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; -import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Selector; @@ -23,67 +23,63 @@ import com.hp.hpl.jena.vocabulary.RDF; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyStatement; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; -import edu.cornell.mannlib.vitro.webapp.utils.jena.Critical; +import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel; +import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedModel; /** * Parse the RDF for a single individual in the model to create a * ConfigurationRdf object. */ public class ConfigurationRdfParser { - public static ConfigurationRdf parse(Model model, String uri, - Class resultClass) throws InvalidConfigurationRdfException { - if (model == null) { - throw new NullPointerException("model may not be null."); - } - if (uri == null) { - throw new NullPointerException("uri may not be null."); - } - if (resultClass == null) { - throw new NullPointerException("resultClass may not be null."); - } + public static ConfigurationRdf parse(LockableModel locking, + String uri, Class resultClass) + throws InvalidConfigurationRdfException { + Objects.requireNonNull(locking, "locking may not be null."); + Objects.requireNonNull(uri, "uri may not be null."); + Objects.requireNonNull(resultClass, "resultClass may not be null."); - confirmExistenceInModel(model, uri); + confirmExistenceInModel(locking, uri); - confirmEligibilityForResultClass(model, uri, resultClass); + confirmEligibilityForResultClass(locking, uri, resultClass); - Set properties = loadProperties(model, uri); + Set properties = loadProperties(locking, uri); - Class concreteClass = determineConcreteClass(model, uri, + Class concreteClass = determineConcreteClass(locking, uri, resultClass); return new ConfigurationRdf(concreteClass, properties); } - private static void confirmExistenceInModel(Model model, String uri) - throws InvalidConfigurationRdfException { + private static void confirmExistenceInModel(LockableModel locking, + String uri) throws InvalidConfigurationRdfException { Selector s = new SimpleSelector(createResource(uri), null, (RDFNode) null); - try (Critical section = Critical.read(model)) { - if (model.listStatements(s).toList().isEmpty()) { + try (LockedModel m = locking.read()) { + if (m.listStatements(s).toList().isEmpty()) { throw individualDoesNotAppearInModel(uri); } } } - private static void confirmEligibilityForResultClass(Model model, + private static void confirmEligibilityForResultClass(LockableModel locking, String uri, Class resultClass) throws InvalidConfigurationRdfException { Statement s = createStatement(createResource(uri), RDF.type, createResource(toJavaUri(resultClass))); - try (Critical section = Critical.read(model)) { - if (!model.contains(s)) { + try (LockedModel m = locking.read()) { + if (!m.contains(s)) { throw noTypeStatementForResultClass(s); } } } - private static Set loadProperties(Model model, String uri) - throws InvalidConfigurationRdfException { + private static Set loadProperties(LockableModel locking, + String uri) throws InvalidConfigurationRdfException { Set set = new HashSet<>(); - try (Critical section = Critical.read(model)) { - List rawStatements = model.listStatements( - model.getResource(uri), (Property) null, (RDFNode) null) + try (LockedModel m = locking.read()) { + List rawStatements = m.listStatements( + m.getResource(uri), (Property) null, (RDFNode) null) .toList(); if (rawStatements.isEmpty()) { throw noRdfStatements(uri); @@ -106,14 +102,14 @@ public class ConfigurationRdfParser { } } - private static Class determineConcreteClass(Model model, - String uri, Class resultClass) + private static Class determineConcreteClass( + LockableModel locking, String uri, Class resultClass) throws InvalidConfigurationRdfException { Set> concreteClasses = new HashSet<>(); - try (Critical section = Critical.read(model)) { - for (RDFNode node : model.listObjectsOfProperty( - createResource(uri), RDF.type).toSet()) { + try (LockedModel m = locking.read()) { + for (RDFNode node : m.listObjectsOfProperty(createResource(uri), + RDF.type).toSet()) { if (!node.isURIResource()) { throw typeMustBeUriResource(node); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/Critical.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/Critical.java deleted file mode 100644 index 113645909..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/Critical.java +++ /dev/null @@ -1,39 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.jena; - -import static com.hp.hpl.jena.shared.Lock.READ; -import static com.hp.hpl.jena.shared.Lock.WRITE; - -import com.hp.hpl.jena.rdf.model.Model; - -/** - * Use this in a try-with-resources block. - * - *
- * try (Critical section = Critical.read(model)) {
- * }
- * 
- */ -public class Critical implements AutoCloseable { - public static Critical read(Model model) { - return new Critical(model, READ); - } - - public static Critical write(Model model) { - return new Critical(model, WRITE); - } - - private final Model model; - - private Critical(Model model, boolean readLockRequested) { - this.model = model; - this.model.enterCriticalSection(readLockRequested); - } - - @Override - public void close() { - this.model.leaveCriticalSection(); - } - -} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableModel.java new file mode 100644 index 000000000..e66f65578 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableModel.java @@ -0,0 +1,39 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection; + +import java.util.Objects; + +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.shared.Lock; + +/** + * Makes it easy to use a Jena Model in a try-with-resources block. At the end + * of the block, the close() method will not close the model, but will merely + * release the lock. + * + * Wraps around a bare Model. Cannot be used without locking. + * + *
+ * try (LockedModel m = new LockableModel(model).read()) {
+ *    ...
+ * }
+ * 
+ */ +public class LockableModel { + private final Model model; + + public LockableModel(Model model) { + this.model = Objects.requireNonNull(model, "model may not be null."); + } + + public LockedModel read() { + model.enterCriticalSection(Lock.READ); + return new LockedModel(model); + } + + public LockedModel write() { + model.enterCriticalSection(Lock.WRITE); + return new LockedModel(model); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModel.java new file mode 100644 index 000000000..75a05c82d --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModel.java @@ -0,0 +1,49 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection; + +import java.util.Objects; + +import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.shared.Lock; + +/** + * Makes it easy to use a Jena OntModel in a try-with-resources block. At the + * end of the block, the close() method will not close the model, but will + * merely release the lock. + * + * Returned by the LockableOntModelSelector, or it can be wrapped around a bare + * OntModel. Cannot be used without locking. + * + *
+ * try (LockedOntModel m = lockableOms.getDisplayModel.read()) {
+ *    ...
+ * }
+ * 
+ * + * or + * + *
+ * try (LockedOntModel m = new LockableOntModel(ontModel).read()) {
+ *    ...
+ * }
+ * 
+ */ +public class LockableOntModel { + private final OntModel ontModel; + + public LockableOntModel(OntModel ontModel) { + this.ontModel = Objects.requireNonNull(ontModel, + "ontModel may not be null."); + } + + public LockedOntModel read() { + ontModel.enterCriticalSection(Lock.READ); + return new LockedOntModel(ontModel); + } + + public LockedOntModel write() { + ontModel.enterCriticalSection(Lock.WRITE); + return new LockedOntModel(ontModel); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModelSelector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModelSelector.java new file mode 100644 index 000000000..2a22e1521 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModelSelector.java @@ -0,0 +1,53 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection; + +import java.util.Objects; + +import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector; + +/** + * Makes it easy to use a Jena OntModel with a try-with-resources block. If you + * have access to an OntModelSelector, you can wrap it and then use it obtain + * LockableOntModels. + * + *
+ * LockableOntModelSelector lockableOms = new LockingOntModelSelector(oms);
+ *  
+ * try (LockedOntModel m = lockableOms.getDisplayModel.read()) {
+ *    ...
+ * }
+ * 
+ */ +public class LockableOntModelSelector { + private final OntModelSelector oms; + + public LockableOntModelSelector(OntModelSelector oms) { + this.oms = Objects.requireNonNull(oms, "oms may not be null."); + } + + public LockableOntModel getFullModel() { + return new LockableOntModel(oms.getFullModel()); + } + + public LockableOntModel getABoxModel() { + return new LockableOntModel(oms.getABoxModel()); + } + + public LockableOntModel getTBoxModel() { + return new LockableOntModel(oms.getTBoxModel()); + } + + public LockableOntModel getApplicationMetadataModel() { + return new LockableOntModel(oms.getApplicationMetadataModel()); + } + + public LockableOntModel getUserAccountsModel() { + return new LockableOntModel(oms.getUserAccountsModel()); + } + + public LockableOntModel getDisplayModel() { + return new LockableOntModel(oms.getDisplayModel()); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedModel.java new file mode 100644 index 000000000..7d9cdec98 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedModel.java @@ -0,0 +1,40 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection; + +import com.hp.hpl.jena.rdf.model.Model; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.AbstractModelDecorator; + +/** + * A model that is easy to use in a try-with-resources code block. It can only + * be created by locking a LockableModel. + * + *
+ * try (LockedModel m = lockableModel.read()) {
+ *    ...
+ * }
+ * 
+ * + * The close method has been hijacked to simply release the lock, and not to + * actually close the wrapped model. + */ +public class LockedModel extends AbstractModelDecorator implements + AutoCloseable { + + /** + * Should only be created by LockableModel. + */ + LockedModel(Model m) { + super(m); + } + + /** + * Just unlocks the model. Doesn't actually close it, because we may want to + * use it again. + */ + @Override + public void close() { + super.leaveCriticalSection(); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedOntModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedOntModel.java new file mode 100644 index 000000000..5f310d84a --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedOntModel.java @@ -0,0 +1,32 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection; + +import com.hp.hpl.jena.ontology.OntModel; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.AbstractOntModelDecorator; + +/** + * A simple OntModel, except that it can only be created by locking a + * LockableOntModel. It is AutoCloseable, but the close method has been hijacked + * to simply release the lock, and not to actually close the wrapped model. + */ +public class LockedOntModel extends AbstractOntModelDecorator implements + AutoCloseable { + + /** + * Can only be created by LockableOntModel. + */ + LockedOntModel(OntModel m) { + super(m); + } + + /** + * Just unlocks the model. Doesn't actually close it, because we may want to + * use it again. + */ + @Override + public void close() { + super.leaveCriticalSection(); + } +} diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index a6b17e116..b1e8ccb4b 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -101,7 +101,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { expectException(NullPointerException.class, "model may not be null"); @SuppressWarnings("unused") - Object unused = new ConfigurationBeanLoader(null); + Object unused = new ConfigurationBeanLoader((Model) null); } // ----------------------------------------------------------------------