diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java index c2fc41d1c..d04a49b95 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/jena/RDFUploadController.java @@ -36,6 +36,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils; import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector; import edu.cornell.mannlib.vitro.webapp.dao.jena.VitroJenaSpecialModelMaker; +import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; @@ -225,7 +226,18 @@ public class RDFUploadController extends BaseEditController { String userURI) { mainModel.enterCriticalSection(Lock.WRITE); try { - mainModel.getBaseModel().notifyEvent(new EditEvent(userURI,true)); + + EditEvent startEvent = null, endEvent = null; + + if (remove) { + startEvent = new BulkUpdateEvent(userURI, true); + endEvent = new BulkUpdateEvent(userURI, false); + } else { + startEvent = new EditEvent(userURI, true); + endEvent = new EditEvent(userURI, false); + } + + mainModel.getBaseModel().notifyEvent(startEvent); try { if (makeClassgroups) { Model classgroupModel = @@ -239,7 +251,7 @@ public class RDFUploadController extends BaseEditController { mainModel.add(changesModel); } } finally { - mainModel.getBaseModel().notifyEvent(new EditEvent(userURI,false)); + mainModel.getBaseModel().notifyEvent(endEvent); } } finally { mainModel.leaveCriticalSection(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java index 2c2d4e83c..470e52d17 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java @@ -184,6 +184,7 @@ public class VitroVocabulary { public static final String EDIT_EVENT_AGENT = vitroURI+"editEventAgent"; public static final String EDIT_EVENT_DATETIME = vitroURI+"editEventDateTime"; + public static final String BULK_UPDATE_EVENT = vitroURI+"BulkUpdateEvent"; public static final String INDIVIDUAL_EDIT_EVENT = vitroURI+"IndividualEditEvent"; public static final String INDIVIDUAL_CREATION_EVENT = vitroURI+"IndividualCreationEvent"; public static final String INDIVIDUAL_UPDATE_EVENT = vitroURI+"IndividualUpdateEvent"; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/event/BulkUpdateEvent.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/event/BulkUpdateEvent.java new file mode 100644 index 000000000..ce28e2aef --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/event/BulkUpdateEvent.java @@ -0,0 +1,15 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.dao.jena.event; + +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; + +public class BulkUpdateEvent extends EditEvent { + + private static final String BULK_UPDATE_EVENT = VitroVocabulary.BULK_UPDATE_EVENT; + + public BulkUpdateEvent(String userURI, boolean begin) { + super(userURI, begin); + } + +} 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 88cc95b2a..36918bfcc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java @@ -32,6 +32,9 @@ import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; +import edu.cornell.mannlib.vitro.webapp.dao.jena.CumulativeDeltaModeler; +import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent; + /** * Allows for real-time incremental materialization or retraction of RDFS- * style class and property subsumption based ABox inferences as statements @@ -57,6 +60,10 @@ public class SimpleReasoner extends StatementListener { private AnnotationProperty mostSpecificType = (ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM)).createAnnotationProperty(mostSpecificTypePropertyURI); + private CumulativeDeltaModeler aBoxDeltaModeler1 = null; + private CumulativeDeltaModeler aBoxDeltaModeler2 = null; + private boolean batchMode1, batchMode2; + /** * @param tboxModel - input. This model contains both asserted and inferred TBox axioms * @param aboxModel - input. This model contains asserted ABox statements @@ -70,9 +77,13 @@ public class SimpleReasoner extends StatementListener { this.aboxModel = aboxModel; this.inferenceModel = inferenceModel; this.inferenceRebuildModel = inferenceRebuildModel; - this.scratchpadModel = scratchpadModel; + this.scratchpadModel = scratchpadModel; + this.batchMode1 = false; + this.batchMode2 = false; + aBoxDeltaModeler1 = new CumulativeDeltaModeler(); + aBoxDeltaModeler2 = new CumulativeDeltaModeler(); - aboxModel.register(this); + aboxModel.getBaseModel().register(this); } /** @@ -88,6 +99,10 @@ public class SimpleReasoner extends StatementListener { this.inferenceModel = inferenceModel; this.inferenceRebuildModel = ModelFactory.createDefaultModel(); this.scratchpadModel = ModelFactory.createDefaultModel(); + aBoxDeltaModeler1 = new CumulativeDeltaModeler(); + aBoxDeltaModeler2 = new CumulativeDeltaModeler(); + this.batchMode1 = false; + this.batchMode2 = false; } /* @@ -124,14 +139,21 @@ public class SimpleReasoner extends StatementListener { try { if (stmt.getPredicate().equals(RDF.type)) { - removedABoxTypeAssertion(stmt, inferenceModel); - setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); + if (batchMode1) { + aBoxDeltaModeler1.removedStatement(stmt); + } else if (batchMode2) { + aBoxDeltaModeler2.removedStatement(stmt); + } else { + removedABoxTypeAssertion(stmt, inferenceModel); + setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); + } } /* 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()); @@ -1191,6 +1213,103 @@ public class SimpleReasoner extends StatementListener { return (getSimpleReasonerFromServletContext(ctx) == null); } + @Override + public synchronized void notifyEvent(Model model, Object event) { + + if (event instanceof BulkUpdateEvent) { + if (((BulkUpdateEvent) event).getBegin()) { + + log.info("received BulkUpdateEvent(true)"); + + if (batchMode1 || batchMode2) { + log.error("received a BulkUpdateEvent while already processing one; this event will be ignored and ABox reasoning may not be performed properly"); + return; + } else { + batchMode1 = true; + batchMode2 = false; + aBoxDeltaModeler1.getRetractions().removeAll(); + } + } else { + log.info("received BulkUpdateEvent(false)"); + new Thread(new DeltaComputer()).start(); + } + } + } + + private class DeltaComputer extends Thread { + public DeltaComputer() { + } + + @Override + public void run() { + + Model retractions = aBoxDeltaModeler1.getRetractions(); + boolean finished = (retractions.size() == 0); + String qualifier = "(1)"; + + while (!finished) { + retractions.enterCriticalSection(Lock.READ); + + try { + log.info("started computing inferences for batch " + qualifier + " update"); + StmtIterator iter = retractions.listStatements(); + + int num = 0; + while (iter.hasNext()) { + Statement stmt = iter.next(); + + try { + removedStatement(stmt); + } catch (Exception e) { + log.error("exception while computing inferences for batch " + qualifier + " update: " + e.getMessage()); + } + + num++; + if ((num % 6000) == 0) { + log.info("still computing inferences for batch " + qualifier + " update..."); + } + + } + } finally { + retractions.removeAll(); + retractions.leaveCriticalSection(); + log.info("finished computing inferences for batch " + qualifier + " update"); + } + + + if (batchMode1 && (aBoxDeltaModeler2.getRetractions().size() > 0)) { + retractions = aBoxDeltaModeler2.getRetractions(); + batchMode2 = true; + batchMode1 = false; + qualifier = "(2)"; + log.info("switching from batch mode 1 to batch mode 2"); + } else if (batchMode2 && (aBoxDeltaModeler1.getRetractions().size() > 0)) { + retractions = aBoxDeltaModeler1.getRetractions(); + batchMode1 = true; + batchMode2 = false; + qualifier = "(1)"; + log.info("switching from batch mode 2 to batch mode 1"); + } else { + finished = true; + log.info("finished processing retractions in batch mode"); + } + } + + if (aBoxDeltaModeler1.getRetractions().size() > 0) { + log.warn("Unexpected condition: the aBoxDeltaModeler1 retractions model was not empty at the end of the DeltaComputer.run method"); + aBoxDeltaModeler1.getRetractions().removeAll(); + } + + if (aBoxDeltaModeler2.getRetractions().size() > 0) { + log.warn("Unexpected condition: the aBoxDeltaModeler2 retractions model was not empty at the end of the DeltaComputer.run method"); + aBoxDeltaModeler2.getRetractions().removeAll(); + } + + batchMode1 = false; + batchMode2 = false; + } + } + public static String stmtString(Statement statement) { return " [subject = " + statement.getSubject().getURI() + "] [property = " + statement.getPredicate().getURI() +