/** * Copyright 2010 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. */ /** * */ package org.drools.common; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.concurrent.locks.ReentrantLock; import org.drools.FactException; import org.drools.FactHandle; import org.drools.RuleBase; import org.drools.RuntimeDroolsException; import org.drools.WorkingMemory; import org.drools.WorkingMemoryEntryPoint; import org.drools.RuleBaseConfiguration.AssertBehaviour; import org.drools.impl.StatefulKnowledgeSessionImpl.ObjectStoreWrapper; import org.drools.reteoo.EntryPointNode; import org.drools.reteoo.LeftTuple; import org.drools.reteoo.ObjectTypeConf; import org.drools.rule.EntryPoint; import org.drools.rule.Rule; import org.drools.spi.Activation; import org.drools.spi.FactHandleFactory; import org.drools.spi.PropagationContext; public class NamedEntryPoint implements InternalWorkingMemoryEntryPoint, WorkingMemoryEntryPoint { /** The arguments used when adding/removing a property change listener. */ protected final Object[] addRemovePropertyChangeListenerArgs = new Object[]{this}; private static final long serialVersionUID = 510l; protected ObjectStore objectStore; protected transient InternalRuleBase ruleBase; protected EntryPoint entryPoint; protected EntryPointNode entryPointNode; private ObjectTypeConfigurationRegistry typeConfReg; private final AbstractWorkingMemory wm; private FactHandleFactory handleFactory; protected final ReentrantLock lock; public NamedEntryPoint(EntryPoint entryPoint, EntryPointNode entryPointNode, AbstractWorkingMemory wm) { this( entryPoint, entryPointNode, wm, new ReentrantLock() ); } public NamedEntryPoint(EntryPoint entryPoint, EntryPointNode entryPointNode, AbstractWorkingMemory wm, ReentrantLock lock) { this.entryPoint = entryPoint; this.entryPointNode = entryPointNode; this.wm = wm; this.ruleBase = (InternalRuleBase) this.wm.getRuleBase(); this.lock = lock; this.typeConfReg = new ObjectTypeConfigurationRegistry( this.ruleBase ); this.handleFactory = this.wm.getFactHandleFactory(); this.objectStore = new SingleThreadedObjectStore( this.ruleBase.getConfiguration(), this.lock ); } /** * @see WorkingMemory */ public FactHandle insert(final Object object) throws FactException { return insert( object, /* Not-Dynamic */ false, false, null, null ); } public FactHandle insert(final Object object, final boolean dynamic) throws FactException { return insert( object, dynamic, false, null, null ); } protected FactHandle insert(final Object object, final boolean dynamic, boolean logical, final Rule rule, final Activation activation) throws FactException { if ( object == null ) { // you cannot assert a null object return null; } try { this.ruleBase.readLock(); this.lock.lock(); this.wm.startOperation(); ObjectTypeConf typeConf = this.typeConfReg.getObjectTypeConf( this.entryPoint, object ); // check if the object already exists in the WM InternalFactHandle handle = (InternalFactHandle) this.objectStore.getHandleForObject( object ); if ( handle != null ) { // already inserted, so return the same handle return handle; } handle = this.handleFactory.newFactHandle( object, typeConf, wm, this ); this.objectStore.addHandle( handle, object ); if ( dynamic ) { addPropertyChangeListener( object ); } insert( handle, object, rule, activation ); return handle; } finally { this.wm.endOperation(); this.ruleBase.readUnlock(); this.lock.unlock(); } } protected void insert(final InternalFactHandle handle, final Object object, final Rule rule, final Activation activation) { this.ruleBase.executeQueuedActions(); if ( activation != null ) { // release resources so that they can be GC'ed activation.getPropagationContext().releaseResources(); } final PropagationContext propagationContext = new PropagationContextImpl( this.wm.getNextPropagationIdCounter(), PropagationContext.ASSERTION, rule, (activation == null) ? null : (LeftTuple) activation.getTuple(), handle, -1, -1, this.entryPoint ); this.entryPointNode.assertObject( handle, propagationContext, this.typeConfReg.getObjectTypeConf( this.entryPoint, object ), this.wm ); this.wm.executeQueuedActions(); this.wm.getWorkingMemoryEventSupport().fireObjectInserted( propagationContext, handle, object, wm ); } public void update(final org.drools.runtime.rule.FactHandle handle, final Object object) throws FactException { update( handle, object, null, null ); } public void update(final org.drools.runtime.rule.FactHandle factHandle, final Object object, final Rule rule, final Activation activation) throws FactException { try { this.ruleBase.readLock(); this.lock.lock(); this.ruleBase.executeQueuedActions(); this.wm.startOperation(); final InternalFactHandle handle = (InternalFactHandle) factHandle; final Object originalObject = handle.getObject(); if ( handle.getId() == -1 || object == null || (handle.isEvent() && ((EventFactHandle) handle).isExpired()) ) { // the handle is invalid, most likely already retracted, so // return // and we cannot assert a null object return; } ObjectTypeConf typeConf = this.typeConfReg.getObjectTypeConf( this.entryPoint, object ); if ( activation != null ) { // release resources so that they can be GC'ed activation.getPropagationContext().releaseResources(); } // Nowretract any trace of the original fact final PropagationContext propagationContext = new PropagationContextImpl( this.wm.getNextPropagationIdCounter(), PropagationContext.MODIFICATION, rule, (activation == null) ? null : (LeftTuple) activation.getTuple(), handle, -1, -1, this.entryPoint ); this.entryPointNode.retractObject( handle, propagationContext, typeConf, this.wm ); if ( (originalObject != object) || (this.ruleBase.getConfiguration().getAssertBehaviour() != AssertBehaviour.IDENTITY) ) { this.objectStore.removeHandle( handle ); // set anyway, so that it updates the hashCodes handle.setObject( object ); this.objectStore.addHandle( handle, object ); } this.handleFactory.increaseFactHandleRecency( handle ); this.entryPointNode.assertObject( handle, propagationContext, typeConf, this.wm ); this.wm.getWorkingMemoryEventSupport().fireObjectUpdated( propagationContext, (org.drools.FactHandle) factHandle, originalObject, object, this.wm ); this.wm.executeQueuedActions(); } finally { this.wm.endOperation(); this.ruleBase.readUnlock(); this.lock.unlock(); } } public void retract(final org.drools.runtime.rule.FactHandle handle) throws FactException { retract( (org.drools.FactHandle) handle, true, true, null, null ); } public void retract(final FactHandle factHandle, final boolean removeLogical, final boolean updateEqualsMap, final Rule rule, final Activation activation) throws FactException { try { this.ruleBase.readLock(); this.lock.lock(); this.ruleBase.executeQueuedActions(); this.wm.startOperation(); final InternalFactHandle handle = (InternalFactHandle) factHandle; if ( handle.getId() == -1 ) { // can't retract an already retracted handle return; } removePropertyChangeListener( handle ); if ( activation != null ) { // release resources so that they can be GC'ed activation.getPropagationContext().releaseResources(); } final PropagationContext propagationContext = new PropagationContextImpl( this.wm.getNextPropagationIdCounter(), PropagationContext.RETRACTION, rule, (activation == null) ? null : (LeftTuple) activation.getTuple(), handle, -1, -1, this.entryPoint ); ObjectTypeConf typeConf = this.typeConfReg.getObjectTypeConf( this.entryPoint, handle.getObject() ); this.entryPointNode.retractObject( handle, propagationContext, typeConf, this.wm ); final Object object = handle.getObject(); this.wm.getWorkingMemoryEventSupport().fireObjectRetracted( propagationContext, handle, object, this.wm ); this.objectStore.removeHandle( handle ); this.handleFactory.destroyFactHandle( handle ); this.wm.executeQueuedActions(); } finally { this.wm.endOperation(); this.ruleBase.readUnlock(); this.lock.unlock(); } } public void modifyRetract(final FactHandle factHandle) { modifyRetract( factHandle, null, null ); } public void modifyRetract(final FactHandle factHandle, final Rule rule, final Activation activation) { try { this.ruleBase.readLock(); this.lock.lock(); this.ruleBase.executeQueuedActions(); this.wm.startOperation(); final InternalFactHandle handle = (InternalFactHandle) factHandle; // final Object originalObject = (handle.isShadowFact()) ? // ((ShadowProxy) handle.getObject()).getShadowedObject() : // handle.getObject(); if ( handle.getId() == -1 || (handle.isEvent() && ((EventFactHandle) handle).isExpired()) ) { // the handle is invalid, most likely already retracted, so // return return; } if ( activation != null ) { // release resources so that they can be GC'ed activation.getPropagationContext().releaseResources(); } // Nowretract any trace of the original fact final PropagationContext propagationContext = new PropagationContextImpl( this.wm.getNextPropagationIdCounter(), PropagationContext.MODIFICATION, rule, (activation == null) ? null : (LeftTuple) activation.getTuple(), handle, -1, -1, entryPoint ); ObjectTypeConf typeConf = this.typeConfReg.getObjectTypeConf( this.entryPoint, handle.getObject() ); this.entryPointNode.retractObject( handle, propagationContext, typeConf, this.wm ); } finally { this.wm.endOperation(); this.ruleBase.readUnlock(); this.lock.unlock(); } } public void modifyInsert(final FactHandle factHandle, final Object object) { modifyInsert( factHandle, object, null, null ); } public void modifyInsert(final FactHandle factHandle, final Object object, final Rule rule, final Activation activation) { this.modifyInsert( EntryPoint.DEFAULT, factHandle, object, rule, activation ); } protected void modifyInsert(final EntryPoint entryPoint, final FactHandle factHandle, final Object object, final Rule rule, final Activation activation) { try { this.ruleBase.readLock(); this.lock.lock(); this.ruleBase.executeQueuedActions(); this.wm.startOperation(); final InternalFactHandle handle = (InternalFactHandle) factHandle; final Object originalObject = handle.getObject(); if ( handle.getId() == -1 || (handle.isEvent() && ((EventFactHandle) handle).isExpired()) ) { // the handle is invalid, most likely already retracted, so // return return; } this.handleFactory.increaseFactHandleRecency( handle ); if ( activation != null ) { // release resources so that they can be GC'ed activation.getPropagationContext().releaseResources(); } // Nowretract any trace of the original fact final PropagationContext propagationContext = new PropagationContextImpl( this.wm.getNextPropagationIdCounter(), PropagationContext.MODIFICATION, rule, (activation == null) ? null : (LeftTuple) activation.getTuple(), handle, -1, -1, entryPoint ); ObjectTypeConf typeConf = this.typeConfReg.getObjectTypeConf( this.entryPoint, handle.getObject() ); this.entryPointNode.assertObject( handle, propagationContext, typeConf, this.wm ); this.wm.getWorkingMemoryEventSupport().fireObjectUpdated( propagationContext, factHandle, originalObject, object, this.wm ); this.wm.executeQueuedActions(); } finally { this.wm.endOperation(); this.ruleBase.readUnlock(); this.lock.unlock(); } } protected void addPropertyChangeListener(final Object object) { try { final Method method = object.getClass().getMethod( "addPropertyChangeListener", AbstractWorkingMemory.ADD_REMOVE_PROPERTY_CHANGE_LISTENER_ARG_TYPES ); method.invoke( object, this.addRemovePropertyChangeListenerArgs ); } catch ( final NoSuchMethodException e ) { System.err.println( "Warning: Method addPropertyChangeListener not found" + " on the class " + object.getClass() + " so Drools will be unable to process JavaBean" + " PropertyChangeEvents on the asserted Object" ); } catch ( final IllegalArgumentException e ) { System.err.println( "Warning: The addPropertyChangeListener method" + " on the class " + object.getClass() + " does not take" + " a simple PropertyChangeListener argument" + " so Drools will be unable to process JavaBean" + " PropertyChangeEvents on the asserted Object" ); } catch ( final IllegalAccessException e ) { System.err.println( "Warning: The addPropertyChangeListener method" + " on the class " + object.getClass() + " is not public" + " so Drools will be unable to process JavaBean" + " PropertyChangeEvents on the asserted Object" ); } catch ( final InvocationTargetException e ) { System.err.println( "Warning: The addPropertyChangeListener method" + " on the class " + object.getClass() + " threw an InvocationTargetException" + " so Drools will be unable to process JavaBean" + " PropertyChangeEvents on the asserted Object: " + e.getMessage() ); } catch ( final SecurityException e ) { System.err.println( "Warning: The SecurityManager controlling the class " + object.getClass() + " did not allow the lookup of a" + " addPropertyChangeListener method" + " so Drools will be unable to process JavaBean" + " PropertyChangeEvents on the asserted Object: " + e.getMessage() ); } } protected void removePropertyChangeListener(final FactHandle handle) { Object object = null; try { object = ((InternalFactHandle) handle).getObject(); if ( object != null ) { final Method mehod = object.getClass().getMethod( "removePropertyChangeListener", AbstractWorkingMemory.ADD_REMOVE_PROPERTY_CHANGE_LISTENER_ARG_TYPES ); mehod.invoke( object, this.addRemovePropertyChangeListenerArgs ); } } catch ( final NoSuchMethodException e ) { // The removePropertyChangeListener method on the class // was not found so Drools will be unable to // stop processing JavaBean PropertyChangeEvents // on the retracted Object } catch ( final IllegalArgumentException e ) { throw new RuntimeDroolsException( "Warning: The removePropertyChangeListener method on the class " + object.getClass() + " does not take a simple PropertyChangeListener argument so Drools will be unable to stop processing JavaBean" + " PropertyChangeEvents on the retracted Object" ); } catch ( final IllegalAccessException e ) { throw new RuntimeDroolsException( "Warning: The removePropertyChangeListener method on the class " + object.getClass() + " is not public so Drools will be unable to stop processing JavaBean PropertyChangeEvents on the retracted Object" ); } catch ( final InvocationTargetException e ) { throw new RuntimeDroolsException( "Warning: The removePropertyChangeL istener method on the class " + object.getClass() + " threw an InvocationTargetException so Drools will be unable to stop processing JavaBean" + " PropertyChangeEvents on the retracted Object: " + e.getMessage() ); } catch ( final SecurityException e ) { throw new RuntimeDroolsException( "Warning: The SecurityManager controlling the class " + object.getClass() + " did not allow the lookup of a removePropertyChangeListener method so Drools will be unable to stop processing JavaBean" + " PropertyChangeEvents on the retracted Object: " + e.getMessage() ); } } public WorkingMemoryEntryPoint getWorkingMemoryEntryPoint(String name) { return this.wm.getWorkingMemoryEntryPoint( name ); } public ObjectTypeConfigurationRegistry getObjectTypeConfigurationRegistry() { return this.typeConfReg; } public RuleBase getRuleBase() { return this.ruleBase; } public FactHandle getFactHandle(Object object) { return this.objectStore.getHandleForObject( object ); } public EntryPoint getEntryPoint() { return this.entryPoint; } public InternalWorkingMemory getInternalWorkingMemory() { return this.wm; } public FactHandle getFactHandleByIdentity(final Object object) { return this.objectStore.getHandleForObjectIdentity( object ); } public Object getObject(org.drools.runtime.rule.FactHandle factHandle) { return this.objectStore.getObjectForHandle( (InternalFactHandle) factHandle ); } @SuppressWarnings("unchecked") public <T extends org.drools.runtime.rule.FactHandle> Collection<T> getFactHandles() { return new ObjectStoreWrapper( this.objectStore, null, ObjectStoreWrapper.FACT_HANDLE ); } @SuppressWarnings("unchecked") public <T extends org.drools.runtime.rule.FactHandle> Collection<T> getFactHandles(org.drools.runtime.ObjectFilter filter) { return new ObjectStoreWrapper( this.objectStore, filter, ObjectStoreWrapper.FACT_HANDLE ); } @SuppressWarnings("unchecked") public Collection<Object> getObjects() { return new ObjectStoreWrapper( this.objectStore, null, ObjectStoreWrapper.OBJECT ); } @SuppressWarnings("unchecked") public Collection<Object> getObjects(org.drools.runtime.ObjectFilter filter) { return new ObjectStoreWrapper( this.objectStore, filter, ObjectStoreWrapper.OBJECT ); } public String getEntryPointId() { return this.entryPoint.getEntryPointId(); } public long getFactCount() { return this.objectStore.size(); } }