diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java index c0765854b..f6e2f92ea 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java @@ -23,6 +23,8 @@ import com.hp.hpl.jena.ontology.OntResource; import com.hp.hpl.jena.ontology.UnionClass; import com.hp.hpl.jena.rdf.model.AnonId; import com.hp.hpl.jena.rdf.model.Literal; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.ResIterator; @@ -285,6 +287,13 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao { com.hp.hpl.jena.ontology.Individual ind = ontModel.getIndividual(ent.getURI()); if (ind != null) { if (ent.getName() != null && ( (ind.getLabel(getDefaultLanguage())==null) || (ind.getLabel(getDefaultLanguage())!=null && ent.getName()!=null && !ent.getName().equals(ind.getLabel(getDefaultLanguage())) ) ) ) { + + // removal of existing values done this odd way to trigger + // the change listeners + Model temp = ModelFactory.createDefaultModel(); + temp.add(ontModel.listStatements(ind, RDFS.label, (RDFNode) null)); + ontModel.remove(temp); + ind.setLabel(ent.getName(), getDefaultLanguage()); } Set oldTypeURIsSet = new HashSet(); @@ -295,7 +304,9 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao { } } Set newTypeURIsSet = new HashSet(); - newTypeURIsSet.add(ent.getVClassURI()); + if (ent.getVClassURI() != null) { + newTypeURIsSet.add(ent.getVClassURI()); + } boolean conservativeTypeDeletion = false; try { List vcl = ent.getVClasses(false); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/ReasonerPlugin.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/ReasonerPlugin.java new file mode 100644 index 000000000..14470d762 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/ReasonerPlugin.java @@ -0,0 +1,23 @@ +package edu.cornell.mannlib.vitro.webapp.reasoner; + +import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.Statement; + +public interface ReasonerPlugin { + + public boolean isInterestedInAddedStatement(Statement stmt); + + public boolean isInterestedInRemovedStatement(Statement stmt); + + public void addedABoxStatement(Statement stmt, + Model aboxAssertionsModel, + Model aboxInferencesModel, + OntModel TBoxInferencesModel); + + public void removedABoxStatement(Statement stmt, + Model aboxAssertionsModel, + Model aboxInferencesModel, + OntModel TBoxInferencesModel); + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java index a68cf4a5d..456699436 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java @@ -70,6 +70,8 @@ public class SimpleReasoner extends StatementListener { private CumulativeDeltaModeler aBoxDeltaModeler2 = null; private boolean batchMode1 = false, batchMode2 = false; private boolean stopRequested = false; + + private List pluginList = new ArrayList(); /** * @param tboxModel - input. This model contains both asserted and inferred TBox axioms @@ -114,6 +116,14 @@ public class SimpleReasoner extends StatementListener { stopRequested = false; } + public void setPluginList(List pluginList) { + this.pluginList = pluginList; + } + + public List getPluginList() { + return this.pluginList; + } + /* * Performs selected incremental ABox reasoning based * on the addition of a new statement (aka assertion) @@ -127,11 +137,19 @@ public class SimpleReasoner extends StatementListener { addedABoxTypeAssertion(stmt, inferenceModel, new HashSet()); setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); } - /* uncomment this to enable subproperty/equivalent property inferencing. sjm222 5/13/2011 - else { - addedABoxAssertion(stmt,inferenceModel); + // uncomment this to enable subproperty/equivalent property inferencing. sjm222 5/13/2011 + // addedABoxAssertion(stmt,inferenceModel); + + for (ReasonerPlugin plugin : getPluginList()) { + try { + if (plugin.isInterestedInAddedStatement(stmt)) { + plugin.addedABoxStatement( + stmt, aboxModel, inferenceModel, tboxModel); + } + } catch (Throwable t) { + log.error(t, t); + } } - */ } catch (Exception e) { // don't stop the edit if there's an exception log.error("Exception while computing inferences: " + e.getMessage()); @@ -141,30 +159,46 @@ public class SimpleReasoner extends StatementListener { /* * Performs selected incremental ABox reasoning based * on the retraction of a statement (aka assertion) - * from the ABox. + * from the ABox. */ @Override public void removedStatement(Statement stmt) { try { - if (stmt.getPredicate().equals(RDF.type)) { - if (batchMode1) { - aBoxDeltaModeler1.removedStatement(stmt); - } else if (batchMode2) { - aBoxDeltaModeler2.removedStatement(stmt); - } else { - removedABoxTypeAssertion(stmt, inferenceModel); - setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); + + // The delta modeler could optionally record only statements relevant + // to reasoning by checking the .isInterestedInRemovedStatement() + // methods on the plugins in addition to recording rdf:type + // statements. If property reasoning were uncommented, however, + // almost all statements would be relevant. + + if (batchMode1) { + aBoxDeltaModeler1.removedStatement(stmt); + } else if (batchMode2) { + aBoxDeltaModeler2.removedStatement(stmt); + } else { + if (stmt.getPredicate().equals(RDF.type)) { + removedABoxTypeAssertion(stmt, inferenceModel); + setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); + } + + // uncomment this to enable subproperty/equivalent property inferencing. sjm222 5/13/2011 + // removedABoxAssertion(stmt, inferenceModel); + + for (ReasonerPlugin plugin : getPluginList()) { + try { + if (plugin.isInterestedInRemovedStatement(stmt)) { + plugin.removedABoxStatement( + stmt, aboxModel, inferenceModel, tboxModel); + } + } catch (Throwable t) { + log.error(t, t); + } } } - /* uncomment this to enable subproperty/equivalent property inferencing. sjm222 5/13/2011 - else { - removedABoxAssertion(stmt, inferenceModel); - } - */ } catch (Exception e) { // don't stop the edit if there's an exception - log.error("Exception while retracting inferences: " + e.getMessage()); + log.error("Exception while retracting inferences: ", e); } } @@ -1013,17 +1047,24 @@ public class SimpleReasoner extends StatementListener { try { addedABoxTypeAssertion(individual, inferenceRebuildModel, unknownTypes); setMostSpecificTypes(individual, inferenceRebuildModel, unknownTypes); + StmtIterator sit = aboxModel.listStatements(individual, null, (RDFNode) null); + while (sit.hasNext()) { + Statement s = sit.nextStatement(); + for (ReasonerPlugin plugin : getPluginList()) { + plugin.addedABoxStatement(s, aboxModel, inferenceRebuildModel, tboxModel); + } + } } catch (NullPointerException npe) { log.error("a NullPointerException was received while recomputing the ABox inferences. Halting inference computation."); return; } catch (JenaException je) { if (je.getMessage().equals("Statement models must no be null")) { - log.error("Exception while recomputing ABox inference model: " + je.getMessage() + ". Halting inference computation."); + log.error("Exception while recomputing ABox inference model. Halting inference computation.", je); return; } - log.error("Exception while recomputing ABox inference model: " + je.getMessage()); + log.error("Exception while recomputing ABox inference model: ", je); } catch (Exception e) { - log.error("Exception while recomputing ABox inference model: " + e.getMessage()); + log.error("Exception while recomputing ABox inference model: ", e); } numStmts++; @@ -1391,6 +1432,16 @@ public class SimpleReasoner extends StatementListener { try { removedABoxTypeAssertion(stmt, inferenceModel); + for (ReasonerPlugin plugin : getPluginList()) { + try { + if (plugin.isInterestedInRemovedStatement(stmt)) { + plugin.removedABoxStatement( + stmt, aboxModel, inferenceModel, tboxModel); + } + } catch (Throwable t) { + log.error(t, t); + } + } setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); //TODO update this part when subproperty inferencing is added. } catch (NullPointerException npe) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java index 38e1e9a5b..c5482cab1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java @@ -2,6 +2,13 @@ package edu.cornell.mannlib.vitro.webapp.servlet.setup; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -20,14 +27,18 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector; import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena; import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener; import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.ReasonerConfiguration; +import edu.cornell.mannlib.vitro.webapp.reasoner.ReasonerPlugin; import edu.cornell.mannlib.vitro.webapp.reasoner.SimpleReasoner; import edu.cornell.mannlib.vitro.webapp.reasoner.support.SimpleReasonerTBoxListener; import edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaDataSourceSetupBase.TripleStoreType; +import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; public class SimpleReasonerSetup implements ServletContextListener { private static final Log log = LogFactory.getLog(SimpleReasonerSetup.class.getName()); + public static final String FILE_OF_PLUGINS = "/WEB-INF/resources/reasoner_plugins.txt"; + // Models used during a full recompute of the ABox static final String JENA_INF_MODEL_REBUILD = "http://vitro.mannlib.cornell.edu/default/vitro-kb-inf-rebuild"; static final String JENA_INF_MODEL_SCRATCHPAD = "http://vitro.mannlib.cornell.edu/default/vitro-kb-inf-scratchpad"; @@ -93,6 +104,21 @@ public class SimpleReasonerSetup implements ServletContextListener { SimpleReasoner simpleReasoner = new SimpleReasoner(unionOms.getTBoxModel(), assertionsOms.getABoxModel(), inferencesOms.getABoxModel(), rebuildModel, scratchModel); sce.getServletContext().setAttribute(SimpleReasoner.class.getName(),simpleReasoner); + StartupStatus ss = StartupStatus.getBean(ctx); + List pluginList = new ArrayList(); + List pluginClassnameList = this.readFileOfListeners(ctx); + for (String classname : pluginClassnameList) { + try { + ReasonerPlugin plugin = (ReasonerPlugin) Class.forName( + classname).getConstructors()[0].newInstance(); + pluginList.add(plugin); + } catch(Throwable t) { + ss.info(this, "Could not instantiate reasoner plugin " + classname); + } + } + simpleReasoner.setPluginList(pluginList); + + if (isRecomputeRequired(sce.getServletContext())) { log.info("ABox inference recompute required."); @@ -204,4 +230,55 @@ public class SimpleReasonerSetup implements ServletContextListener { simpleReasoner.computeMostSpecificType(); } } + + /** + * Read the names of the plugin classes classes. + * + * If there is a problem, set a fatal error, and return an empty list. + */ + private List readFileOfListeners(ServletContext ctx) { + List list = new ArrayList(); + + StartupStatus ss = StartupStatus.getBean(ctx); + + InputStream is = null; + BufferedReader br = null; + try { + is = ctx.getResourceAsStream(FILE_OF_PLUGINS); + br = new BufferedReader(new InputStreamReader(is)); + + String line; + while (null != (line = br.readLine())) { + String trimmed = line.trim(); + if (!trimmed.isEmpty() && !trimmed.startsWith("#")) { + list.add(trimmed); + } + } + } catch (NullPointerException e) { + // ignore the lack of file + } catch (IOException e) { + ss.fatal(this, + "Failed while processing the list of startup listeners: " + + FILE_OF_PLUGINS, e); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + log.error(e); + } + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + log.error(e); + } + } + } + + log.debug("Classnames of reasoner plugins = " + list); + return list; + + } }