package org.drools.common;
/*
* Copyright 2005 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.drools.PackageIntegrationException;
import org.drools.RuleBase;
import org.drools.RuleBaseConfiguration;
import org.drools.RuntimeDroolsException;
import org.drools.SessionConfiguration;
import org.drools.StatefulSession;
import org.drools.base.ClassFieldAccessorCache;
import org.drools.definition.type.FactType;
import org.drools.event.RuleBaseEventListener;
import org.drools.event.RuleBaseEventSupport;
import org.drools.impl.EnvironmentFactory;
import org.drools.management.DroolsManagementAgent;
import org.drools.process.core.Process;
import org.drools.rule.CompositeClassLoader;
import org.drools.rule.DialectRuntimeRegistry;
import org.drools.rule.Function;
import org.drools.rule.ImportDeclaration;
import org.drools.rule.InvalidPatternException;
import org.drools.rule.Package;
import org.drools.rule.Rule;
import org.drools.rule.TypeDeclaration;
import org.drools.spi.FactHandleFactory;
import org.drools.util.ObjectHashSet;
/**
* Implementation of <code>RuleBase</code>.
*
* @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
* @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
* @version $Id: RuleBaseImpl.java,v 1.5 2005/08/14 22:44:12 mproctor Exp $
*/
abstract public class AbstractRuleBase
implements
InternalRuleBase,
Externalizable {
// ------------------------------------------------------------
// Instance members
// ------------------------------------------------------------
private String id;
private int workingMemoryCounter;
private RuleBaseConfiguration config;
protected Map<String, Package> pkgs;
private Map processes;
private Map agendaGroupRuleTotals;
private transient CompositeClassLoader rootClassLoader;
/**
* The fact handle factory.
*/
private FactHandleFactory factHandleFactory;
private transient Map<String, Class< ? >> globals;
private ReloadPackageCompilationData reloadPackageCompilationData = null;
private RuleBaseEventSupport eventSupport = new RuleBaseEventSupport( this );
private transient ObjectHashSet statefulSessions;
// indexed used to track invariant lock
private int lastAquiredLock;
// lock for entire rulebase, used for dynamic updates
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* This lock is used when adding to, or reading the <field>statefulSessions</field>
*/
private final ReentrantLock statefuleSessionLock = new ReentrantLock();
private int additionsSinceLock;
private int removalsSinceLock;
private transient Map<Class< ? >, TypeDeclaration> classTypeDeclaration;
private List<RuleBasePartitionId> partitionIDs;
private ClassFieldAccessorCache classFieldAccessorCache;
/**
* Default constructor - for Externalizable. This should never be used by a user, as it
* will result in an invalid state for the instance.
*/
public AbstractRuleBase() {
}
public synchronized int nextWorkingMemoryCounter() {
return this.workingMemoryCounter++;
}
public synchronized long getWorkingMemoryCounter() {
return this.workingMemoryCounter;
}
/**
* Construct.
*
* @param id The rete network.
*/
public AbstractRuleBase(final String id,
final RuleBaseConfiguration config,
final FactHandleFactory factHandleFactory) {
this.config = (config != null) ? config : new RuleBaseConfiguration();
this.config.makeImmutable();
createRulebaseId( id );
this.factHandleFactory = factHandleFactory;
if ( this.config.isSequential() ) {
this.agendaGroupRuleTotals = new HashMap();
}
this.rootClassLoader = new CompositeClassLoader( this.config.getClassLoader() );
this.pkgs = new HashMap<String, Package>();
this.processes = new HashMap();
this.globals = new HashMap<String, Class< ? >>();
this.statefulSessions = new ObjectHashSet();
this.classTypeDeclaration = new HashMap<Class< ? >, TypeDeclaration>();
this.partitionIDs = new ArrayList<RuleBasePartitionId>();
this.classFieldAccessorCache = new ClassFieldAccessorCache( this.rootClassLoader );
}
private void createRulebaseId(final String id) {
if( id != null ) {
this.id = id;
} else {
String key = "";
if( config.isMBeansEnabled() ) {
DroolsManagementAgent agent = DroolsManagementAgent.getInstance();
key = String.valueOf( agent.getNextKnowledgeBaseId() );
}
this.id = "default"+key;
}
}
// ------------------------------------------------------------
// Instance methods
// ------------------------------------------------------------
/**
* Handles the write serialization of the Package. Patterns in Rules may reference generated data which cannot be serialized by default methods.
* The Package uses PackageCompilationData to hold a reference to the generated bytecode. The generated bytecode must be restored before any Rules.
*/
public void writeExternal(final ObjectOutput out) throws IOException {
ObjectOutput droolsStream;
boolean isDrools = out instanceof DroolsObjectOutputStream;
ByteArrayOutputStream bytes;
if ( isDrools ) {
droolsStream = out;
bytes = null;
} else {
bytes = new ByteArrayOutputStream();
droolsStream = new DroolsObjectOutputStream( bytes );
}
droolsStream.writeObject( this.config );
droolsStream.writeObject( this.pkgs );
// Rules must be restored by an ObjectInputStream that can resolve using a given ClassLoader to handle seaprately by storing as
// a byte[]
droolsStream.writeObject( this.id );
droolsStream.writeInt( this.workingMemoryCounter );
droolsStream.writeObject( this.processes );
droolsStream.writeObject( this.agendaGroupRuleTotals );
droolsStream.writeUTF( this.factHandleFactory.getClass().getName() );
droolsStream.writeObject( buildGlobalMapForSerialization() );
droolsStream.writeObject( this.partitionIDs );
this.eventSupport.removeEventListener( RuleBaseEventListener.class );
droolsStream.writeObject( this.eventSupport );
if ( !isDrools ) {
droolsStream.flush();
droolsStream.close();
bytes.close();
out.writeObject( bytes.toByteArray() );
}
}
private Map<String, String> buildGlobalMapForSerialization() {
Map<String, String> gl = new HashMap<String, String>();
for ( Map.Entry<String, Class< ? >> entry : this.globals.entrySet() ) {
gl.put( entry.getKey(),
entry.getValue().getName() );
}
return gl;
}
/**
* Handles the read serialization of the Package. Patterns in Rules may reference generated data which cannot be serialized by default methods.
* The Package uses PackageCompilationData to hold a reference to the generated bytecode; which must be restored before any Rules.
* A custom ObjectInputStream, able to resolve classes against the bytecode in the PackageCompilationData, is used to restore the Rules.
*/
public void readExternal(final ObjectInput in) throws IOException,
ClassNotFoundException {
// PackageCompilationData must be restored before Rules as it has the ClassLoader needed to resolve the generated code references in Rules
DroolsObjectInput droolsStream;
boolean isDrools = in instanceof DroolsObjectInputStream;
ByteArrayInputStream bytes = null;
if ( isDrools ) {
droolsStream = (DroolsObjectInput) in;
} else {
bytes = new ByteArrayInputStream( (byte[]) in.readObject() );
droolsStream = new DroolsObjectInputStream( bytes );
}
this.rootClassLoader = new CompositeClassLoader( droolsStream.getParentClassLoader() );
droolsStream.setClassLoader( this.rootClassLoader );
droolsStream.setRuleBase( this );
this.classFieldAccessorCache = new ClassFieldAccessorCache( this.rootClassLoader );
this.config = (RuleBaseConfiguration) droolsStream.readObject();
this.config.setClassLoader( droolsStream.getParentClassLoader() );
this.pkgs = (Map<String, Package>) droolsStream.readObject();
for ( final Object object : this.pkgs.values() ) {
((Package) object).getDialectRuntimeRegistry().onAdd( this.rootClassLoader );
}
// PackageCompilationData must be restored before Rules as it has the ClassLoader needed to resolve the generated code references in Rules
this.id = (String) droolsStream.readObject();
this.workingMemoryCounter = droolsStream.readInt();
this.processes = (Map) droolsStream.readObject();
this.agendaGroupRuleTotals = (Map) droolsStream.readObject();
Class cls = null;
try {
cls = droolsStream.getParentClassLoader().loadClass( droolsStream.readUTF() );
this.factHandleFactory = (FactHandleFactory) cls.newInstance();
} catch ( InstantiationException e ) {
DroolsObjectInputStream.newInvalidClassException( cls,
e );
} catch ( IllegalAccessException e ) {
DroolsObjectInputStream.newInvalidClassException( cls,
e );
}
for ( final Object object : this.pkgs.values() ) {
((Package) object).getDialectRuntimeRegistry().onBeforeExecute();
((Package) object).getClassFieldAccessorStore().setClassFieldAccessorCache( this.classFieldAccessorCache );
((Package) object).getClassFieldAccessorStore().wire();
}
this.populateTypeDeclarationMaps();
// read globals
Map<String, String> globs = (Map<String, String>) droolsStream.readObject();
populateGlobalsMap( globs );
this.partitionIDs = (List<RuleBasePartitionId>) droolsStream.readObject();
this.eventSupport = (RuleBaseEventSupport) droolsStream.readObject();
this.eventSupport.setRuleBase( this );
this.statefulSessions = new ObjectHashSet();
if ( !isDrools ) {
droolsStream.close();
}
}
/**
* globals class types must be re-wired after serialization
*
* @param globs
* @throws ClassNotFoundException
*/
private void populateGlobalsMap(Map<String, String> globs) throws ClassNotFoundException {
this.globals = new HashMap<String, Class< ? >>();
for ( Map.Entry<String, String> entry : globs.entrySet() ) {
this.globals.put( entry.getKey(),
this.rootClassLoader.loadClass( entry.getValue() ) );
}
}
/**
* type classes must be re-wired after serialization
*
* @throws ClassNotFoundException
*/
private void populateTypeDeclarationMaps() throws ClassNotFoundException {
this.classTypeDeclaration = new HashMap<Class< ? >, TypeDeclaration>();
for ( Package pkg : this.pkgs.values() ) {
for ( TypeDeclaration type : pkg.getTypeDeclarations().values() ) {
type.setTypeClass( this.rootClassLoader.loadClass( type.getTypeClassName() ) );
this.classTypeDeclaration.put( type.getTypeClass(),
type );
}
}
}
/**
* @return the id
*/
public String getId() {
return this.id;
}
public RuleBaseConfiguration getConfig() {
return config;
}
/**
* @see RuleBase
*/
public StatefulSession newStatefulSession() {
return newStatefulSession( new SessionConfiguration(),
EnvironmentFactory.newEnvironment() );
}
public void disposeStatefulSession(final StatefulSession statefulSession) {
try {
statefuleSessionLock.lock();
this.statefulSessions.remove( statefulSession );
for ( Object listener : statefulSession.getRuleBaseUpdateListeners() ) {
this.removeEventListener( (RuleBaseEventListener) listener );
}
} finally {
statefuleSessionLock.unlock();
}
}
/**
* @see RuleBase
*/
public FactHandleFactory getFactHandleFactory() {
return this.factHandleFactory;
}
public FactHandleFactory newFactHandleFactory() {
return this.factHandleFactory.newInstance();
}
public FactHandleFactory newFactHandleFactory(int id,
long counter) {
return this.factHandleFactory.newInstance( id,
counter );
}
public Process[] getProcesses() {
return (Process[]) this.processes.values().toArray( new Process[this.processes.size()] );
}
public Package[] getPackages() {
return this.pkgs.values().toArray( new Package[this.pkgs.size()] );
}
public Map<String, Package> getPackagesMap() {
return this.pkgs;
}
public Map getGlobals() {
return this.globals;
}
public Map getAgendaGroupRuleTotals() {
return this.agendaGroupRuleTotals;
}
public int getAdditionsSinceLock() {
return additionsSinceLock;
}
public int getRemovalsSinceLock() {
return removalsSinceLock;
}
public void lock() {
this.additionsSinceLock = 0;
this.removalsSinceLock = 0;
this.eventSupport.fireBeforeRuleBaseLocked();
this.lock.writeLock().lock();
this.eventSupport.fireAfterRuleBaseLocked();
}
public void unlock() {
this.eventSupport.fireBeforeRuleBaseUnlocked();
this.lock.writeLock().unlock();
this.eventSupport.fireAfterRuleBaseUnlocked();
}
public void readLock() {
this.lock.readLock().lock();
}
public void readUnlock() {
this.lock.readLock().unlock();
}
/**
* Add a <code>Package</code> to the network. Iterates through the
* <code>Package</code> adding Each individual <code>Rule</code> to the
* network. Before update network each referenced <code>WorkingMemory</code>
* is locked.
*
* @param newPkg The package to add.
*/
public void addPackages(final Collection<Package> newPkgs) {
try {
lock();
// we need to merge all byte[] first, so that the root classloader can resolve classes
for ( Package newPkg : newPkgs ) {
newPkg.checkValidity();
this.additionsSinceLock++;
this.eventSupport.fireBeforePackageAdded( newPkg );
Package pkg = this.pkgs.get( newPkg.getName() );
if ( pkg == null ) {
pkg = new Package( newPkg.getName() );
// @TODO we really should have a single root cache
pkg.setClassFieldAccessorCache( this.classFieldAccessorCache );
pkgs.put( pkg.getName(),
pkg );
}
// first merge anything related to classloader re-wiring
pkg.getDialectRuntimeRegistry().merge( newPkg.getDialectRuntimeRegistry(),
this.rootClassLoader );
}
// now iterate again, this time onBeforeExecute will handle any wiring or cloader re-creating that needs to be done as part of the merge
for ( Package newPkg : newPkgs ) {
Package pkg = this.pkgs.get( newPkg.getName() );
// this needs to go here, as functions will set a java dialect to dirty
if ( newPkg.getFunctions() != null ) {
for ( Map.Entry<String, Function> entry : newPkg.getFunctions().entrySet() ) {
pkg.addFunction( entry.getValue() );
}
}
pkg.getDialectRuntimeRegistry().onBeforeExecute();
// with the classloader recreated for all byte[] classes, we should now merge and wire any new accessors
pkg.getClassFieldAccessorStore().merge( newPkg.getClassFieldAccessorStore() );
}
for ( Package newPkg : newPkgs ) {
Package pkg = this.pkgs.get( newPkg.getName() );
// we have to do this before the merging, as it does some classloader resolving
TypeDeclaration lastType = null;
try {
// Add the type declarations to the RuleBase
if ( newPkg.getTypeDeclarations() != null ) {
// add type declarations
for ( TypeDeclaration type : newPkg.getTypeDeclarations().values() ) {
lastType = type;
type.setTypeClass( this.rootClassLoader.loadClass( type.getTypeClassName() ) );
// @TODO should we allow overrides? only if the class is not in use.
if ( !this.classTypeDeclaration.containsKey( type.getTypeClass() ) ) {
// add to rulebase list of type declarations
this.classTypeDeclaration.put( type.getTypeClass(),
type );
}
}
}
} catch ( ClassNotFoundException e ) {
throw new RuntimeDroolsException( "unable to resolve Type Declaration class '" + lastType.getTypeName() + "'" );
}
// now merge the new package into the existing one
mergePackage( pkg,
newPkg );
// add the rules to the RuleBase
final Rule[] rules = newPkg.getRules();
for ( int i = 0; i < rules.length; ++i ) {
addRule( newPkg,
rules[i] );
}
// add the flows to the RuleBase
if ( newPkg.getRuleFlows() != null ) {
final Map flows = newPkg.getRuleFlows();
for ( final Object object : flows.entrySet() ) {
final Entry flow = (Entry) object;
this.processes.put( flow.getKey(),
flow.getValue() );
}
}
this.eventSupport.fireAfterPackageAdded( newPkg );
}
} finally {
unlock();
}
}
/**
* Merge a new package with an existing package.
* Most of the work is done by the concrete implementations,
* but this class does some work (including combining imports, compilation data, globals,
* and the actual Rule objects into the package).
*/
private void mergePackage(final Package pkg,
final Package newPkg) {
// Merge imports
final Map<String, ImportDeclaration> imports = pkg.getImports();
imports.putAll( newPkg.getImports() );
String lastType = null;
try {
// merge globals
if ( newPkg.getGlobals() != null && newPkg.getGlobals() != Collections.EMPTY_MAP ) {
Map<String, String> globals = pkg.getGlobals();
// Add globals
for ( final Map.Entry<String, String> entry : newPkg.getGlobals().entrySet() ) {
final String identifier = entry.getKey();
final String type = entry.getValue();
lastType = type;
if ( globals.containsKey( identifier ) && !globals.get( identifier ).equals( type ) ) {
throw new PackageIntegrationException( pkg );
} else {
pkg.addGlobal( identifier,
this.rootClassLoader.loadClass( type ) );
// this isn't a package merge, it's adding to the rulebase, but I've put it here for convienience
this.globals.put( identifier,
this.rootClassLoader.loadClass( type ) );
}
}
}
} catch ( ClassNotFoundException e ) {
throw new RuntimeDroolsException( "Unable to resolve class '" + lastType + "'" );
}
// merge the type declarations
if ( newPkg.getTypeDeclarations() != null ) {
// add type declarations
for ( TypeDeclaration type : newPkg.getTypeDeclarations().values() ) {
// @TODO should we allow overrides? only if the class is not in use.
if ( !pkg.getTypeDeclarations().containsKey( type.getTypeName() ) ) {
// add to package list of type declarations
pkg.addTypeDeclaration( type );
}
}
}
//Merge rules into the RuleBase package
//as this is needed for individual rule removal later on
final Rule[] newRules = newPkg.getRules();
for ( int i = 0; i < newRules.length; i++ ) {
final Rule newRule = newRules[i];
// remove the rule if it already exists
if ( pkg.getRule( newRule.getName() ) != null ) {
removeRule( pkg,
pkg.getRule( newRule.getName() ) );
}
pkg.addRule( newRule );
}
//Merge The Rule Flows
if ( newPkg.getRuleFlows() != null ) {
final Map flows = newPkg.getRuleFlows();
for ( final Iterator iter = flows.values().iterator(); iter.hasNext(); ) {
final Process flow = (Process) iter.next();
pkg.addProcess( flow );
}
}
// // this handles re-wiring any dirty Packages, it's done lazily to allow incremental
// // additions without incurring the repeated cost.
// if ( this.reloadPackageCompilationData == null ) {
// this.reloadPackageCompilationData = new ReloadPackageCompilationData();
// }
// this.reloadPackageCompilationData.addDialectDatas( pkg.getDialectRuntimeRegistry() );
}
public TypeDeclaration getTypeDeclaration(Class< ? > clazz) {
return this.classTypeDeclaration.get( clazz );
}
public Collection<TypeDeclaration> getTypeDeclarations() {
return this.classTypeDeclaration.values();
}
public void addRule(final Package pkg,
final Rule rule) throws InvalidPatternException {
synchronized ( this.pkgs ) {
this.eventSupport.fireBeforeRuleAdded( pkg,
rule );
// if ( !rule.isValid() ) {
// throw new IllegalArgumentException( "The rule called " + rule.getName() + " is not valid. Check for compile errors reported." );
// }
addRule( rule );
this.eventSupport.fireAfterRuleAdded( pkg,
rule );
}
}
protected abstract void addRule(final Rule rule) throws InvalidPatternException;
public void removePackage(final String packageName) {
try {
lock();
final Package pkg = this.pkgs.get( packageName );
if ( pkg == null ) {
throw new IllegalArgumentException( "Package name '" + packageName + "' does not exist for this Rule Base." );
}
this.removalsSinceLock++;
this.eventSupport.fireBeforePackageRemoved( pkg );
final Rule[] rules = pkg.getRules();
for ( int i = 0; i < rules.length; ++i ) {
removeRule( pkg,
rules[i] );
}
// getting the list of referenced globals
final Set referencedGlobals = new HashSet();
for ( final Iterator it = this.pkgs.values().iterator(); it.hasNext(); ) {
final org.drools.rule.Package pkgref = (org.drools.rule.Package) it.next();
if ( pkgref != pkg ) {
referencedGlobals.addAll( pkgref.getGlobals().keySet() );
}
}
// removing globals declared inside the package that are not shared
for ( final Iterator it = pkg.getGlobals().keySet().iterator(); it.hasNext(); ) {
final String globalName = (String) it.next();
if ( !referencedGlobals.contains( globalName ) ) {
this.globals.remove( globalName );
}
}
//and now the rule flows
final Map flows = pkg.getRuleFlows();
for ( final Iterator iter = flows.keySet().iterator(); iter.hasNext(); ) {
removeProcess( (String) iter.next() );
}
// removing the package itself from the list
this.pkgs.remove( pkg.getName() );
pkg.getDialectRuntimeRegistry().onRemove();
//clear all members of the pkg
pkg.clear();
this.eventSupport.fireAfterPackageRemoved( pkg );
} finally {
unlock();
}
}
public void removeRule(final String packageName,
final String ruleName) {
try {
lock();
final Package pkg = this.pkgs.get( packageName );
if ( pkg == null ) {
throw new IllegalArgumentException( "Package name '" + packageName + "' does not exist for this Rule Base." );
}
final Rule rule = pkg.getRule( ruleName );
if ( rule == null ) {
throw new IllegalArgumentException( "Rule name '" + ruleName + "' does not exist in the Package '" + packageName + "'." );
}
this.removalsSinceLock++;
removeRule( pkg,
rule );
pkg.removeRule( rule );
if ( this.reloadPackageCompilationData == null ) {
this.reloadPackageCompilationData = new ReloadPackageCompilationData();
}
this.reloadPackageCompilationData.addDialectDatas( pkg.getDialectRuntimeRegistry() );
} finally {
unlock();
}
}
public void removeRule(final Package pkg,
final Rule rule) {
try {
lock();
this.eventSupport.fireBeforeRuleRemoved( pkg,
rule );
removeRule( rule );
this.eventSupport.fireAfterRuleRemoved( pkg,
rule );
} finally {
unlock();
}
}
protected abstract void removeRule(Rule rule);
public void removeFunction(final String packageName,
final String functionName) {
synchronized ( this.pkgs ) {
final Package pkg = this.pkgs.get( packageName );
if ( pkg == null ) {
throw new IllegalArgumentException( "Package name '" + packageName + "' does not exist for this Rule Base." );
}
this.eventSupport.fireBeforeFunctionRemoved( pkg,
functionName );
if ( !pkg.getFunctions().containsKey( functionName ) ) {
throw new IllegalArgumentException( "function name '" + packageName + "' does not exist in the Package '" + packageName + "'." );
}
pkg.removeFunction( functionName );
if ( this.reloadPackageCompilationData == null ) {
this.reloadPackageCompilationData = new ReloadPackageCompilationData();
}
this.reloadPackageCompilationData.addDialectDatas( pkg.getDialectRuntimeRegistry() );
this.eventSupport.fireAfterFunctionRemoved( pkg,
functionName );
}
}
public void addProcess(final Process process) {
synchronized ( this.pkgs ) {
this.processes.put( process.getId(),
process );
}
}
public void removeProcess(final String id) {
synchronized ( this.pkgs ) {
this.processes.remove( id );
}
}
public Process getProcess(final String id) {
Process process;
synchronized ( this.pkgs ) {
process = (Process) this.processes.get( id );
}
return process;
}
public void addStatefulSession(final StatefulSession statefulSession) {
try {
statefuleSessionLock.lock();
this.statefulSessions.add( statefulSession );
} finally {
statefuleSessionLock.unlock();
}
}
public Package getPackage(final String name) {
return this.pkgs.get( name );
}
public StatefulSession[] getStatefulSessions() {
final StatefulSession[] copyOfSessions;
try {
statefuleSessionLock.lock();
copyOfSessions = new StatefulSession[this.statefulSessions.size()];
this.statefulSessions.toArray( copyOfSessions );
} finally {
statefuleSessionLock.unlock();
}
return copyOfSessions;
}
public InternalWorkingMemory[] getWorkingMemories() {
final InternalWorkingMemory[] copyOfMemories;
try {
statefuleSessionLock.lock();
copyOfMemories = new InternalWorkingMemory[this.statefulSessions.size()];
this.statefulSessions.toArray( copyOfMemories );
} finally {
statefuleSessionLock.unlock();
}
return copyOfMemories;
}
public RuleBaseConfiguration getConfiguration() {
return this.config;
}
public CompositeClassLoader getRootClassLoader() {
return this.rootClassLoader;
}
public void executeQueuedActions() {
synchronized ( this.pkgs ) {
if ( this.reloadPackageCompilationData != null ) {
this.reloadPackageCompilationData.execute( this );
this.reloadPackageCompilationData = null;
}
}
}
public RuleBasePartitionId createNewPartitionId() {
RuleBasePartitionId p;
synchronized ( this.partitionIDs ) {
p = new RuleBasePartitionId( "P-" + this.partitionIDs.size() );
this.partitionIDs.add( p );
}
return p;
}
public List<RuleBasePartitionId> getPartitionIds() {
return this.partitionIDs;
}
public void addEventListener(final RuleBaseEventListener listener) {
// no need for synchonization or locking because eventSupport is thread-safe
this.eventSupport.addEventListener( listener );
}
public void removeEventListener(final RuleBaseEventListener listener) {
// no need for synchonization or locking because eventSupport is thread-safe
this.eventSupport.removeEventListener( listener );
}
public List<RuleBaseEventListener> getRuleBaseEventListeners() {
// no need for synchonization or locking because eventSupport is thread-safe
return this.eventSupport.getEventListeners();
}
public boolean isEvent(Class clazz) {
for ( Package pkg : this.pkgs.values() ) {
if ( pkg.isEvent( clazz ) ) {
return true;
}
}
return false;
}
public FactType getFactType(final String name) {
for ( Package pkg : this.pkgs.values() ) {
FactType type = pkg.getFactType( name );
if ( type != null ) {
return type;
}
}
return null;
}
public static class ReloadPackageCompilationData
implements
RuleBaseAction {
private static final long serialVersionUID = 1L;
private Set<DialectRuntimeRegistry> set;
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
set = (Set<DialectRuntimeRegistry>) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( set );
}
public void addDialectDatas(final DialectRuntimeRegistry registry) {
if ( this.set == null ) {
this.set = new HashSet<DialectRuntimeRegistry>();
}
if ( !this.set.contains( registry ) ) this.set.add( registry );
}
public void execute(final InternalRuleBase ruleBase) {
for ( final DialectRuntimeRegistry registry : this.set ) {
registry.onBeforeExecute();
}
}
}
public static interface RuleBaseAction
extends
Externalizable {
public void execute(InternalRuleBase ruleBase);
}
public ClassFieldAccessorCache getClassFieldAccessorCache() {
return this.classFieldAccessorCache;
}
}