From f6f12efe6d75fa5ded983e3c780c41d5b371d644 Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Fri, 31 Oct 2014 18:20:18 -0400 Subject: [PATCH 1/2] Repair logging queries from the developer panel. the DataStructuresProviders must wrap their RDFServiceFactory in LoggingRDFServiceFactory, and insure that their RDFService comes from that wrapped factory. --- .../rdfservice/impl/logging/LoggingRDFService.java | 5 +++++ .../impl/logging/LoggingRDFServiceFactory.java | 5 +++++ .../impl/sdb/ContentDataStructuresProviderSDB.java | 4 +++- .../sparql/ContentDataStructuresProviderSPARQL.java | 10 ++++++---- .../tdb/ConfigurationDataStructuresProviderTDB.java | 11 +++++++---- .../impl/tdb/ContentDataStructuresProviderTDB.java | 4 +++- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java index 9d98722ec..99d50b095 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java @@ -126,4 +126,9 @@ public class LoggingRDFService implements RDFService { public void close() { innerService.close(); } + + @Override + public String toString() { + return "LoggingRDFService[inner=" + innerService + "]"; + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java index 894b208d1..fec57486d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFServiceFactory.java @@ -40,4 +40,9 @@ public class LoggingRDFServiceFactory implements RDFServiceFactory { factory.unregisterListener(changeListener); } + @Override + public String toString() { + return "LoggingRDFServiceFactory[factory=" + factory + "]"; + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sdb/ContentDataStructuresProviderSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sdb/ContentDataStructuresProviderSDB.java index ba6d249de..2bd411900 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sdb/ContentDataStructuresProviderSDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sdb/ContentDataStructuresProviderSDB.java @@ -39,6 +39,7 @@ import edu.cornell.mannlib.vitro.webapp.modelaccess.ontmodels.UnionModelsOntMode import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.sdb.RDFServiceFactorySDB; +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.servlet.setup.rdfsetup.impl.ContentDataStructuresProvider; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; @@ -122,7 +123,8 @@ public class ContentDataStructuresProviderSDB extends setupSDB(store); } - return new RDFServiceFactorySDB(ds, storeDesc); + return new LoggingRDFServiceFactory(new RDFServiceFactorySDB(ds, + storeDesc)); } /** diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sparql/ContentDataStructuresProviderSPARQL.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sparql/ContentDataStructuresProviderSPARQL.java index 9b43cbcbe..a0917dae6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sparql/ContentDataStructuresProviderSPARQL.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/sparql/ContentDataStructuresProviderSPARQL.java @@ -17,6 +17,7 @@ import edu.cornell.mannlib.vitro.webapp.modelaccess.ontmodels.OntModelCache; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; 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.servlet.setup.rdfsetup.impl.ContentDataStructuresProvider; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; @@ -58,8 +59,8 @@ public class ContentDataStructuresProviderSPARQL extends this.updateEndpointURI = props .getProperty(PROPERTY_SPARQL_UPDATE_ENDPOINT_URI); - this.rdfService = createRDFService(); - this.rdfServiceFactory = createRDFServiceFactory(); + this.rdfServiceFactory = createRDFServiceFactory(createRDFService()); + this.rdfService = this.rdfServiceFactory.getRDFService(); this.dataset = createDataset(); this.modelMaker = createModelMaker(); } @@ -75,8 +76,9 @@ public class ContentDataStructuresProviderSPARQL extends } } - private RDFServiceFactory createRDFServiceFactory() { - return new RDFServiceFactorySingle(this.rdfService); + private RDFServiceFactory createRDFServiceFactory(RDFService service) { + return new LoggingRDFServiceFactory( + new RDFServiceFactorySingle(service)); } private Dataset createDataset() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ConfigurationDataStructuresProviderTDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ConfigurationDataStructuresProviderTDB.java index 003cdb1f6..e65363904 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ConfigurationDataStructuresProviderTDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ConfigurationDataStructuresProviderTDB.java @@ -22,6 +22,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceFactorySingle; 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.rdfsetup.impl.ConfigurationDataStructuresProvider; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; @@ -60,8 +61,8 @@ public class ConfigurationDataStructuresProviderTDB extends + DIRECTORY_TDB; try { - this.rdfService = new RDFServiceTDB(tdbPath); - this.rdfServiceFactory = createRDFServiceFactory(); + this.rdfServiceFactory = createRDFServiceFactory(tdbPath); + this.rdfService = this.rdfServiceFactory.getRDFService(); this.dataset = new RDFServiceDataset(this.rdfService); this.modelMaker = createModelMaker(); ss.info(ctxListener, "Initialized the RDF source for TDB"); @@ -75,8 +76,10 @@ public class ConfigurationDataStructuresProviderTDB extends TDB.getContext().setTrue(TDB.symUnionDefaultGraph); } - private RDFServiceFactory createRDFServiceFactory() { - return new RDFServiceFactorySingle(this.rdfService); + private RDFServiceFactory createRDFServiceFactory(String tdbPath) + throws IOException { + return new LoggingRDFServiceFactory(new RDFServiceFactorySingle( + new RDFServiceTDB(tdbPath))); } private ModelMaker createModelMaker() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ContentDataStructuresProviderTDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ContentDataStructuresProviderTDB.java index 641f2d02c..bcd95680f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ContentDataStructuresProviderTDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/rdfsetup/impl/tdb/ContentDataStructuresProviderTDB.java @@ -21,6 +21,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceFactorySingle; 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.rdfsetup.impl.ContentDataStructuresProvider; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; @@ -74,7 +75,8 @@ public class ContentDataStructuresProviderTDB extends } private RDFServiceFactory createRDFServiceFactory() { - return new RDFServiceFactorySingle(this.rdfService); + return new LoggingRDFServiceFactory(new RDFServiceFactorySingle( + this.rdfService)); } private ModelMaker createModelMaker() { From 72b86bd4d55f3278b10169e254e65b527dab74cf Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Sun, 9 Nov 2014 16:05:06 -0500 Subject: [PATCH 2/2] Improve handling of critical sections Use LockableOntModel, etc., instead of Critical. --- .../ConfigurationBeanLoader.java | 51 +++++++++------ .../configuration/ConfigurationRdfParser.java | 64 +++++++++---------- .../vitro/webapp/utils/jena/Critical.java | 39 ----------- .../jena/criticalsection/LockableModel.java | 39 +++++++++++ .../criticalsection/LockableOntModel.java | 49 ++++++++++++++ .../LockableOntModelSelector.java | 53 +++++++++++++++ .../jena/criticalsection/LockedModel.java | 40 ++++++++++++ .../jena/criticalsection/LockedOntModel.java | 32 ++++++++++ .../ConfigurationBeanLoaderTest.java | 2 +- 9 files changed, 275 insertions(+), 94 deletions(-) delete mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/Critical.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableModel.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModel.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockableOntModelSelector.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedModel.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/utils/jena/criticalsection/LockedOntModel.java 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); } // ----------------------------------------------------------------------