Java tutorial
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl; import static edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel.IDLE; import static edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel.WORKING; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.InferenceModelUpdater; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.PatternListBuilder; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner.Status; import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel; import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread; /** * The basic implementation of the TBoxReasonerDriver. It gets help from a * listener, an executor, and a reasoner. * * Create a listener that listens for changes to the TBox, but filters them * according to a ReasonerConfiguration object. The listener accumulates the * changes it likes, until it detects an ending EditEvent. Then it passes the * change set back to the driver. * * Each time a change set is received, a task is created and given to the * executor to run. The executor is single-threaded, so the change sets are * processed in sequence. * * Processing involves the following steps: * * 1. Telling the reasoner about the changes, so it can update its own internal * ontology model. * * 2. Telling the reasoner to re-inference its model. A status is returned. * * 3. Asking the reasoner for the inferences from its model. As with the initial * changes, these inferences are filtered according to the * ReasonerConfiguration. * * 4. Synchronizing the applications TBox inferences model with the inferences * obtained from the reasoner. * * ---------------------- * * Possible optimization: if change sets come in quickly enough that the third * set is received while the first is still being processed, it would be * reasonable to merge the second and third sets into one. */ public class BasicTBoxReasonerDriver implements TBoxReasonerDriver { private static final Log log = LogFactory.getLog(BasicTBoxReasonerDriver.class); private final LockableOntModel lockableAssertionsModel; private final LockableModel lockableInferencesModel; private final LockableOntModel lockableFullModel; private final ReasonerConfiguration reasonerConfiguration; private final ConfiguredReasonerListener listener; private final Set<TBoxChanges> pendingChangeSets; private final ExecutorService executorService; private final TBoxReasoner reasoner; private TBoxReasoner.Status innerStatus; public BasicTBoxReasonerDriver(OntModel assertionsModel, Model inferencesModel, OntModel fullModel, TBoxReasoner reasoner, ReasonerConfiguration reasonerConfiguration) { this.lockableAssertionsModel = new LockableOntModel(assertionsModel); this.lockableInferencesModel = new LockableModel(inferencesModel); this.lockableFullModel = new LockableOntModel(fullModel); this.reasoner = reasoner; this.reasonerConfiguration = reasonerConfiguration; this.listener = new ConfiguredReasonerListener(reasonerConfiguration, this); this.pendingChangeSets = Collections.synchronizedSet(new HashSet<TBoxChanges>()); this.executorService = Executors.newFixedThreadPool(1, new VitroBackgroundThread.Factory("TBoxReasoner")); assertionsModel.getBaseModel().register(listener); fullModel.getBaseModel().register(listener); doInitialReasoning(); } private void doInitialReasoning() { try (LockedOntModel assertionsModel = lockableAssertionsModel.read()) { for (ReasonerStatementPattern pat : reasonerConfiguration.getInferenceDrivingPatternAllowSet()) { listener.addedStatements( assertionsModel.listStatements((Resource) null, pat.getPredicate(), (RDFNode) null)); } } listener.notifyEvent(null, new EditEvent(null, false)); } @Override public TBoxReasonerStatus getStatus() { return new FullStatus(innerStatus, !pendingChangeSets.isEmpty()); } @Override public void runSynchronizer(TBoxChanges changeSet) { if (!changeSet.isEmpty()) { executorService.execute(new ReasoningTask(changeSet)); } } /** * Shut down the thread that runs the reasoning tasks. Don't wait longer * than 1 minute. */ public void shutdown() { executorService.shutdown(); int waited = 0; while (waited < 60 && !executorService.isTerminated()) { try { log.info("Waiting for TBox reasoner to terminate."); executorService.awaitTermination(5, TimeUnit.SECONDS); waited += 5; } catch (InterruptedException e) { // Should never happen. e.printStackTrace(); break; } } if (!executorService.isTerminated()) { log.warn("Forcing TBox reasoner to terminate."); executorService.shutdownNow(); } if (!executorService.isTerminated()) { log.error("TBox reasoner did not terminate."); } } private class ReasoningTask implements Runnable { private final TBoxChanges changes; private List<ReasonerStatementPattern> patternList; public ReasoningTask(TBoxChanges changes) { this.changes = changes; pendingChangeSets.add(changes); } @Override public void run() { try { setWorking(); reasoner.updateReasonerModel(changes); innerStatus = reasoner.performReasoning(); buildPatternList(); updateInferencesModel(); setIdle(); } finally { pendingChangeSets.remove(changes); } } private void setWorking() { Thread current = Thread.currentThread(); if (current instanceof VitroBackgroundThread) { ((VitroBackgroundThread) current).setWorkLevel(WORKING); } } private void setIdle() { Thread current = Thread.currentThread(); if (current instanceof VitroBackgroundThread) { ((VitroBackgroundThread) current).setWorkLevel(IDLE); } } private void buildPatternList() { PatternListBuilder patternListBuilder = new PatternListBuilder(reasonerConfiguration, reasoner, changes); this.patternList = patternListBuilder.build(); } private void updateInferencesModel() { InferenceModelUpdater inferenceModelUpdater = new InferenceModelUpdater(reasoner, lockableInferencesModel, lockableFullModel, listener); inferenceModelUpdater.update(patternList); } } private static class FullStatus implements TBoxReasonerStatus { private final TBoxReasoner.Status reasonerStatus; private final boolean reasoning; public FullStatus(Status reasonerStatus, boolean reasoning) { this.reasonerStatus = reasonerStatus; this.reasoning = reasoning; } @Override public boolean isReasoning() { return reasoning; } @Override public boolean isConsistent() { return reasonerStatus.isConsistent(); } @Override public boolean isInErrorState() { return reasonerStatus.isInErrorState(); } @Override public String getExplanation() { String explanation = reasonerStatus.getExplanation(); return explanation == null ? "" : explanation; } } }