/******************************************************************************* * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Gabor Bergmann - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.runtime.rete.matcher; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.Callable; import org.eclipse.incquery.runtime.rete.boundary.Disconnectable; import org.eclipse.incquery.runtime.rete.boundary.IManipulationListener; import org.eclipse.incquery.runtime.rete.boundary.IPredicateTraceListener; import org.eclipse.incquery.runtime.rete.boundary.ReteBoundary; import org.eclipse.incquery.runtime.rete.collections.CollectionsFactory; import org.eclipse.incquery.runtime.rete.construction.IRetePatternBuilder; import org.eclipse.incquery.runtime.rete.construction.RetePatternBuildException; import org.eclipse.incquery.runtime.rete.index.Indexer; import org.eclipse.incquery.runtime.rete.network.Library; import org.eclipse.incquery.runtime.rete.network.Network; import org.eclipse.incquery.runtime.rete.network.Production; import org.eclipse.incquery.runtime.rete.network.Receiver; import org.eclipse.incquery.runtime.rete.network.Supplier; import org.eclipse.incquery.runtime.rete.remote.Address; import org.eclipse.incquery.runtime.rete.tuple.TupleMask; /** * @author Gabor Bergmann * */ public class ReteEngine<PatternDescription> { protected Network reteNet; protected final int reteThreads; protected ReteBoundary<PatternDescription> boundary; protected IPatternMatcherRuntimeContext<PatternDescription> context; protected Collection<Disconnectable> disconnectables; protected IManipulationListener manipulationListener; protected IPredicateTraceListener traceListener; // protected MachineListener machineListener; protected Map<PatternDescription, RetePatternMatcher> matchers; // protected Map<GTPattern, Map<Map<Integer, Scope>, RetePatternMatcher>> matchersScoped; // (pattern, scopemap) -> // matcher protected IRetePatternBuilder<PatternDescription, Address<? extends Supplier>, Address<? extends Receiver>> builder; protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on // while RETE does its job. // protected BlockingQueue<Throwable> caughtExceptions; /** * @param context * the context of the pattern matcher, conveying all information from the outside world. * @param reteThreads * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers. */ public ReteEngine(IPatternMatcherRuntimeContext<PatternDescription> context, int reteThreads) { super(); this.context = context; this.reteThreads = reteThreads; this.parallelExecutionEnabled = reteThreads > 0; // this.framework = new WeakReference<IFramework>(context.getFramework()); initEngine(); this.builder = null; } /** * initializes engine components */ synchronized private void initEngine() { this.disconnectables = new LinkedList<Disconnectable>(); // this.caughtExceptions = new LinkedBlockingQueue<Throwable>(); this.reteNet = new Network(reteThreads, context); this.boundary = new ReteBoundary<PatternDescription>(this); // prerequisite: network this.matchers = //new HashMap<PatternDescription, RetePatternMatcher>(); CollectionsFactory.getMap(); /* this.matchersScoped = new HashMap<PatternDescription, Map<Map<Integer,Scope>,RetePatternMatcher>>(); */ // prerequisite: network, framework, boundary, disconnectables this.manipulationListener = context.subscribePatternMatcherForUpdates(this); // prerequisite: boundary, disconnectables this.traceListener = context.subscribePatternMatcherForTraceInfluences(this); } /** * deconstructs engine components */ synchronized private void deconstructEngine() { reteNet.kill(); for (Disconnectable disc : disconnectables) { disc.disconnect(); } this.matchers = null; this.disconnectables = null; this.reteNet = null; this.boundary = null; // this.machineListener = new MachineListener(this); // prerequisite: // framework, disconnectables this.manipulationListener = null; this.traceListener = null; } /** * Deconstructs the engine to get rid of it finally */ public void killEngine() { deconstructEngine(); // this.framework = null; this.builder = null; } /** * Resets the engine to an after-initialization phase * */ public void reset() { deconstructEngine(); initEngine(); builder.refresh(); } /** * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet. * * @pre: builder is set. * @param gtPattern * the pattern to be matched. * @return a patternmatcher object that can match occurences of the given pattern. * @throws RetePatternBuildException * if construction fails. */ public synchronized RetePatternMatcher accessMatcher(final PatternDescription gtPattern) throws RetePatternBuildException { RetePatternMatcher matcher; // String namespace = gtPattern.getNamespace().getName(); // String name = gtPattern.getName(); // String fqn = namespace + "." + name; matcher = matchers.get(gtPattern); if (matcher == null) { context.modelReadLock(); try { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().lock(); try { try { context.coalesceTraversals(new Callable<Void>() { @Override public Void call() throws RetePatternBuildException { Address<? extends Production> prodNode; prodNode = boundary.accessProduction(gtPattern); RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this, prodNode); retePatternMatcher.setTag(gtPattern); matchers.put(gtPattern, retePatternMatcher); return null; } }); } catch (InvocationTargetException ex) { final Throwable cause = ex.getCause(); if (cause instanceof RetePatternBuildException) throw (RetePatternBuildException) cause; if (cause instanceof RuntimeException) throw (RuntimeException) cause; assert (false); } } finally { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().unlock(); settle(); } } finally { context.modelReadUnLock(); } // reteNet.flushUpdates(); matcher = matchers.get(gtPattern); } return matcher; } /** * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal * during the whole construction period is coalesced (which may have an effect on performance, depending on the * matcher context). * * @pre: builder is set. * @param patterns * the patterns to be matched. * @throws RetePatternBuildException * if construction fails. */ public synchronized void buildMatchersCoalesced(final Collection<PatternDescription> patterns) throws RetePatternBuildException { context.modelReadLock(); try { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().lock(); try { try { context.coalesceTraversals(new Callable<Void>() { @Override public Void call() throws RetePatternBuildException { for (PatternDescription gtPattern : patterns) { boundary.accessProduction(gtPattern); } return null; } }); } catch (InvocationTargetException ex) { final Throwable cause = ex.getCause(); if (cause instanceof RetePatternBuildException) throw (RetePatternBuildException) cause; if (cause instanceof RuntimeException) throw (RuntimeException) cause; assert (false); } } finally { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().unlock(); } settle(); } finally { context.modelReadUnLock(); } } // /** // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if // * a matcher is not available yet. // * // * @param gtPattern // * the pattern to be matched. // * @param additionalScopeMap // * additional, optional scopes for the symbolic parameters // * maps the position of the symbolic parameter to its additional scope (if any) // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature // * @return a patternmatcher object that can match occurences of the given // * pattern. // * @throws PatternMatcherCompileTimeException // * if construction fails. // */ // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map<Integer, Scope> // additionalScopeMap) // throws PatternMatcherCompileTimeException { // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern); // // RetePatternMatcher matcher; // // Map<Map<Integer, Scope>, RetePatternMatcher> scopes = matchersScoped.get(gtPattern); // if (scopes == null) { // scopes = new HashMap<Map<Integer, Scope>, RetePatternMatcher>(); // matchersScoped.put(gtPattern, scopes); // } // // matcher = scopes.get(additionalScopeMap); // if (matcher == null) { // context.modelReadLock(); // try { // reteNet.getStructuralChangeLock().lock(); // try { // Address<? extends Production> prodNode; // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap); // // matcher = new RetePatternMatcher(this, prodNode); // scopes.put(additionalScopeMap, matcher); // } finally { // reteNet.getStructuralChangeLock().unlock(); // } // } finally { // context.modelReadUnLock(); // } // // reteNet.flushUpdates(); // } // // return matcher; // } /** * Returns an indexer that groups the contents of this Production node by their projections to a given mask. * Designed to be called by a RetePatternMatcher. * * @param production * the production node to be indexed. * @param mask * the mask that defines the projection. * @return the Indexer. */ synchronized Indexer accessProjection(Production production, TupleMask mask) { Library library = reteNet.getHeadContainer().getLibrary(); Indexer result = library.peekProjectionIndexer(production, mask); if (result == null) { context.modelReadLock(); try { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().lock(); try { result = library.accessProjectionIndexerOnetime(production, mask); } finally { if (parallelExecutionEnabled) reteNet.getStructuralChangeLock().unlock(); } } finally { context.modelReadUnLock(); } } return result; } // /** // * Retrieves the patternmatcher for a given pattern fqn, returns null if // the matching network hasn't been constructed yet. // * // * @param fqn the fully qualified name of the pattern to be matched. // * @return the previously constructed patternmatcher object that can match // occurences of the given pattern, or null if it doesn't exist. // */ // public RetePatternMatcher getMatcher(String fqn) // { // RetePatternMatcher matcher = matchersByFqn.get(fqn); // if (matcher == null) // { // Production prodNode = boundary.getProduction(fqn); // // matcher = new RetePatternMatcher(this, prodNode); // matchersByFqn.put(fqn, matcher); // } // // return matcher; // } /** * Waits until the pattern matcher is in a steady state and output can be retrieved. */ public void settle() { reteNet.waitForReteTermination(); } /** * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a * retrieval action is executed before the steady state ceases. * * @param action * the action to be run when reaching the steady-state. */ public void settle(Runnable action) { reteNet.waitForReteTermination(action); } // /** // * @return the framework // */ // public IFramework getFramework() { // return framework.get(); // } /** * @return the reteNet */ public Network getReteNet() { return reteNet; } /** * @return the boundary */ public ReteBoundary<PatternDescription> getBoundary() { return boundary; } // /** // * @return the pattern matcher builder // */ // public IRetePatternBuilder getBuilder() { // return builder; // } /** * @param builder * the pattern matcher builder to set */ public void setBuilder( IRetePatternBuilder<PatternDescription, Address<? extends Supplier>, Address<? extends Receiver>> builder) { this.builder = builder; } /** * @return the manipulationListener */ public IManipulationListener getManipulationListener() { return manipulationListener; } /** * @return the traceListener */ public IPredicateTraceListener geTraceListener() { return traceListener; } /** * @param disc * the new Disconnectable adapter. */ public void addDisconnectable(Disconnectable disc) { disconnectables.add(disc); } /** * @return the parallelExecutionEnabled */ public boolean isParallelExecutionEnabled() { return parallelExecutionEnabled; } /** * @return the context */ public IPatternMatcherRuntimeContext<PatternDescription> getContext() { return context; } public IRetePatternBuilder<PatternDescription, Address<? extends Supplier>, Address<? extends Receiver>> getBuilder() { return builder; } // /** // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net. // * @param e // */ // public void logEvaluatorException(Throwable e) { // try { // caughtExceptions.put(e); // } catch (InterruptedException e1) { // logEvaluatorException(e); // } // } // /** // * Polls the exceptions caught and logged during term evaluation by this RETE engine. // * Recommended usage: iterate polling until null is returned. // * // * @return the next caught exception, or null if there are no more. // */ // public Throwable getNextLoggedEvaluatorException() { // return caughtExceptions.poll(); // } }