0001: package org.drools.common;
0002:
0003: /*
0004: * Copyright 2005 JBoss Inc
0005: *
0006: * Licensed under the Apache License, Version 2.0 (the "License");
0007: * you may not use this file except in compliance with the License.
0008: * You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing, software
0013: * distributed under the License is distributed on an "AS IS" BASIS,
0014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: * See the License for the specific language governing permissions and
0016: * limitations under the License.
0017: */
0018:
0019: import java.beans.PropertyChangeEvent;
0020: import java.beans.PropertyChangeListener;
0021: import java.io.Serializable;
0022: import java.lang.reflect.InvocationTargetException;
0023: import java.lang.reflect.Method;
0024: import java.util.ArrayList;
0025: import java.util.Collections;
0026: import java.util.HashMap;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Map;
0030: import java.util.Map.Entry;
0031:
0032: import org.drools.Agenda;
0033: import org.drools.FactException;
0034: import org.drools.FactHandle;
0035: import org.drools.ObjectFilter;
0036: import org.drools.Otherwise;
0037: import org.drools.QueryResults;
0038: import org.drools.RuleBase;
0039: import org.drools.RuleBaseConfiguration;
0040: import org.drools.RuntimeDroolsException;
0041: import org.drools.WorkingMemory;
0042: import org.drools.RuleBaseConfiguration.AssertBehaviour;
0043: import org.drools.RuleBaseConfiguration.LogicalOverride;
0044: import org.drools.base.MapGlobalResolver;
0045: import org.drools.base.ShadowProxy;
0046: import org.drools.event.AgendaEventListener;
0047: import org.drools.event.AgendaEventSupport;
0048: import org.drools.event.RuleFlowEventListener;
0049: import org.drools.event.RuleFlowEventSupport;
0050: import org.drools.event.WorkingMemoryEventListener;
0051: import org.drools.event.WorkingMemoryEventSupport;
0052: import org.drools.reteoo.LIANodePropagation;
0053: import org.drools.rule.Declaration;
0054: import org.drools.rule.Rule;
0055: import org.drools.ruleflow.common.core.Process;
0056: import org.drools.ruleflow.common.instance.ProcessInstance;
0057: import org.drools.ruleflow.core.RuleFlowProcess;
0058: import org.drools.ruleflow.instance.RuleFlowProcessInstance;
0059: import org.drools.ruleflow.instance.impl.RuleFlowProcessInstanceImpl;
0060: import org.drools.spi.Activation;
0061: import org.drools.spi.AgendaFilter;
0062: import org.drools.spi.AgendaGroup;
0063: import org.drools.spi.AsyncExceptionHandler;
0064: import org.drools.spi.FactHandleFactory;
0065: import org.drools.spi.GlobalResolver;
0066: import org.drools.spi.PropagationContext;
0067: import org.drools.util.JavaIteratorAdapter;
0068: import org.drools.util.ObjectHashMap;
0069: import org.drools.util.PrimitiveLongMap;
0070: import org.drools.util.AbstractHashTable.HashTableIterator;
0071: import org.drools.util.concurrent.locks.Lock;
0072: import org.drools.util.concurrent.locks.ReentrantLock;
0073:
0074: /**
0075: * Implementation of <code>WorkingMemory</code>.
0076: *
0077: * @author <a href="mailto:bob@werken.com">bob mcwhirter </a>
0078: * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
0079: * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris </a>
0080: */
0081: public abstract class AbstractWorkingMemory implements
0082: InternalWorkingMemoryActions, EventSupport,
0083: PropertyChangeListener {
0084: // ------------------------------------------------------------
0085: // Constants
0086: // ------------------------------------------------------------
0087: protected static final Class[] ADD_REMOVE_PROPERTY_CHANGE_LISTENER_ARG_TYPES = new Class[] { PropertyChangeListener.class };
0088:
0089: // ------------------------------------------------------------
0090: // Instance members
0091: // ------------------------------------------------------------
0092: protected final long id;
0093:
0094: /** The arguments used when adding/removing a property change listener. */
0095: protected final Object[] addRemovePropertyChangeListenerArgs = new Object[] { this };
0096:
0097: /** The actual memory for the <code>JoinNode</code>s. */
0098: protected final PrimitiveLongMap nodeMemories = new PrimitiveLongMap(
0099: 32, 8);
0100: /** Object-to-handle mapping. */
0101: private final ObjectHashMap assertMap;
0102: private final ObjectHashMap identityMap;
0103:
0104: protected Map queryResults = Collections.EMPTY_MAP;
0105:
0106: /** Global values which are associated with this memory. */
0107: protected GlobalResolver globalResolver;
0108:
0109: protected static final Object NULL = new Serializable() {
0110: private static final long serialVersionUID = 400L;
0111: };
0112:
0113: /** The eventSupport */
0114: protected WorkingMemoryEventSupport workingMemoryEventSupport = new WorkingMemoryEventSupport();
0115:
0116: protected AgendaEventSupport agendaEventSupport = new AgendaEventSupport();
0117:
0118: protected RuleFlowEventSupport ruleFlowEventSupport = new RuleFlowEventSupport();
0119:
0120: /** The <code>RuleBase</code> with which this memory is associated. */
0121: protected transient InternalRuleBase ruleBase;
0122:
0123: protected final FactHandleFactory handleFactory;
0124:
0125: protected final TruthMaintenanceSystem tms;
0126:
0127: /** Rule-firing agenda. */
0128: protected DefaultAgenda agenda;
0129:
0130: protected final List actionQueue = new ArrayList();
0131:
0132: protected final ReentrantLock lock = new ReentrantLock();
0133:
0134: protected final boolean discardOnLogicalOverride;
0135:
0136: protected long propagationIdCounter;
0137:
0138: private final boolean maintainTms;
0139:
0140: private final boolean sequential;
0141:
0142: private List liaPropagations = Collections.EMPTY_LIST;
0143:
0144: /** Flag to determine if a rule is currently being fired. */
0145: protected boolean firing;
0146:
0147: protected boolean halt;
0148:
0149: private int processCounter;
0150:
0151: // ------------------------------------------------------------
0152: // Constructors
0153: // ------------------------------------------------------------
0154:
0155: /**
0156: * Construct.
0157: *
0158: * @param ruleBase
0159: * The backing rule-base.
0160: */
0161: public AbstractWorkingMemory(final int id,
0162: final InternalRuleBase ruleBase,
0163: final FactHandleFactory handleFactory) {
0164: this .id = id;
0165: this .ruleBase = ruleBase;
0166: this .handleFactory = handleFactory;
0167: this .globalResolver = new MapGlobalResolver();
0168: this .maintainTms = this .ruleBase.getConfiguration()
0169: .isMaintainTms();
0170: this .sequential = this .ruleBase.getConfiguration()
0171: .isSequential();
0172:
0173: if (this .maintainTms) {
0174: this .tms = new TruthMaintenanceSystem(this );
0175: } else {
0176: this .tms = null;
0177: }
0178:
0179: this .assertMap = new ObjectHashMap();
0180: final RuleBaseConfiguration conf = this .ruleBase
0181: .getConfiguration();
0182:
0183: if (conf.getAssertBehaviour() == AssertBehaviour.IDENTITY) {
0184: this .assertMap
0185: .setComparator(new IdentityAssertMapComparator());
0186: this .identityMap = assertMap;
0187: } else {
0188: this .assertMap
0189: .setComparator(new EqualityAssertMapComparator());
0190: this .identityMap = new ObjectHashMap();
0191: this .identityMap
0192: .setComparator(new IdentityAssertMapComparator());
0193: }
0194:
0195: // Only takes effect if are using idententity behaviour for assert
0196: if (conf.getLogicalOverride() == LogicalOverride.DISCARD) {
0197: this .discardOnLogicalOverride = true;
0198: } else {
0199: this .discardOnLogicalOverride = false;
0200: }
0201: }
0202:
0203: // ------------------------------------------------------------
0204: // Instance methods
0205: // ------------------------------------------------------------
0206:
0207: void setRuleBase(final InternalRuleBase ruleBase) {
0208: this .ruleBase = ruleBase;
0209: }
0210:
0211: public void setWorkingMemoryEventSupport(
0212: WorkingMemoryEventSupport workingMemoryEventSupport) {
0213: this .workingMemoryEventSupport = workingMemoryEventSupport;
0214: }
0215:
0216: public void setAgendaEventSupport(
0217: AgendaEventSupport agendaEventSupport) {
0218: this .agendaEventSupport = agendaEventSupport;
0219: }
0220:
0221: public void setRuleFlowEventSupport(
0222: RuleFlowEventSupport ruleFlowEventSupport) {
0223: this .ruleFlowEventSupport = ruleFlowEventSupport;
0224: }
0225:
0226: public boolean isSequential() {
0227: return this .sequential;
0228: }
0229:
0230: public void addLIANodePropagation(
0231: LIANodePropagation liaNodePropagation) {
0232: if (this .liaPropagations == Collections.EMPTY_LIST) {
0233: this .liaPropagations = new ArrayList();
0234: }
0235: this .liaPropagations.add(liaNodePropagation);
0236: }
0237:
0238: public void addEventListener(
0239: final WorkingMemoryEventListener listener) {
0240: try {
0241: this .lock.lock();
0242: this .workingMemoryEventSupport.addEventListener(listener);
0243: } finally {
0244: this .lock.unlock();
0245: }
0246: }
0247:
0248: public void removeEventListener(
0249: final WorkingMemoryEventListener listener) {
0250: try {
0251: this .lock.lock();
0252: this .workingMemoryEventSupport
0253: .removeEventListener(listener);
0254: } finally {
0255: this .lock.unlock();
0256: }
0257: }
0258:
0259: public List getWorkingMemoryEventListeners() {
0260: try {
0261: this .lock.lock();
0262: return this .workingMemoryEventSupport.getEventListeners();
0263: } finally {
0264: this .lock.unlock();
0265: }
0266: }
0267:
0268: public void addEventListener(final AgendaEventListener listener) {
0269: try {
0270: this .lock.lock();
0271: this .agendaEventSupport.addEventListener(listener);
0272: } finally {
0273: this .lock.unlock();
0274: }
0275: }
0276:
0277: public void removeEventListener(final AgendaEventListener listener) {
0278: try {
0279: this .lock.lock();
0280: this .agendaEventSupport.removeEventListener(listener);
0281: } finally {
0282: this .lock.unlock();
0283: }
0284: }
0285:
0286: public List getAgendaEventListeners() {
0287: try {
0288: this .lock.lock();
0289: return this .agendaEventSupport.getEventListeners();
0290: } finally {
0291: this .lock.unlock();
0292: }
0293: }
0294:
0295: public void addEventListener(final RuleFlowEventListener listener) {
0296: try {
0297: this .lock.lock();
0298: this .ruleFlowEventSupport.addEventListener(listener);
0299: } finally {
0300: this .lock.unlock();
0301: }
0302: }
0303:
0304: public void removeEventListener(final RuleFlowEventListener listener) {
0305: try {
0306: this .lock.lock();
0307: this .ruleFlowEventSupport.removeEventListener(listener);
0308: } finally {
0309: this .lock.unlock();
0310: }
0311: }
0312:
0313: public List getRuleFlowEventListeners() {
0314: try {
0315: this .lock.lock();
0316: return this .ruleFlowEventSupport.getEventListeners();
0317: } finally {
0318: this .lock.unlock();
0319: }
0320: }
0321:
0322: public FactHandleFactory getFactHandleFactory() {
0323: return this .handleFactory;
0324: }
0325:
0326: public void setGlobal(final String identifier, final Object value) {
0327: // Cannot set null values
0328: if (value == null) {
0329: return;
0330: }
0331:
0332: try {
0333: this .lock.lock();
0334: // Make sure the global has been declared in the RuleBase
0335: final Map globalDefintions = this .ruleBase.getGlobals();
0336: final Class type = (Class) globalDefintions.get(identifier);
0337: if ((type == null)) {
0338: throw new RuntimeException("Unexpected global ["
0339: + identifier + "]");
0340: } else if (!type.isInstance(value)) {
0341: throw new RuntimeException("Illegal class for global. "
0342: + "Expected [" + type.getName() + "], "
0343: + "found [" + value.getClass().getName() + "].");
0344:
0345: } else {
0346: this .globalResolver.setGlobal(identifier, value);
0347: }
0348: } finally {
0349: this .lock.unlock();
0350: }
0351: }
0352:
0353: public void setGlobalResolver(final GlobalResolver globalResolver) {
0354: try {
0355: this .lock.lock();
0356: this .globalResolver = globalResolver;
0357: } finally {
0358: this .lock.unlock();
0359: }
0360: }
0361:
0362: public GlobalResolver getGlobalResolver() {
0363: return this .globalResolver;
0364: }
0365:
0366: public long getId() {
0367: return this .id;
0368: }
0369:
0370: public Object getGlobal(final String identifier) {
0371: try {
0372: this .lock.lock();
0373: return this .globalResolver.resolveGlobal(identifier);
0374: } finally {
0375: this .lock.unlock();
0376: }
0377: }
0378:
0379: public Agenda getAgenda() {
0380: return this .agenda;
0381: }
0382:
0383: public void clearAgenda() {
0384: this .agenda.clearAgenda();
0385: }
0386:
0387: public void clearAgendaGroup(final String group) {
0388: this .agenda.clearAgendaGroup(group);
0389: }
0390:
0391: public void clearActivationGroup(final String group) {
0392: this .agenda.clearActivationGroup(group);
0393: }
0394:
0395: public void clearRuleFlowGroup(final String group) {
0396: this .agenda.clearRuleFlowGroup(group);
0397: }
0398:
0399: public RuleBase getRuleBase() {
0400: return this .ruleBase;
0401: }
0402:
0403: public void halt() {
0404: this .halt = true;
0405: }
0406:
0407: public synchronized void fireAllRules() throws FactException {
0408: fireAllRules(null, -1);
0409: }
0410:
0411: public synchronized void fireAllRules(int fireLimit)
0412: throws FactException {
0413: fireAllRules(null, fireLimit);
0414: }
0415:
0416: public synchronized void fireAllRules(
0417: final AgendaFilter agendaFilter) throws FactException {
0418: fireAllRules(agendaFilter, -1);
0419: }
0420:
0421: public synchronized void fireAllRules(
0422: final AgendaFilter agendaFilter, int fireLimit)
0423: throws FactException {
0424: // If we're already firing a rule, then it'll pick up
0425: // the firing for any other assertObject(..) that get
0426: // nested inside, avoiding concurrent-modification
0427: // exceptions, depending on code paths of the actions.
0428: this .halt = false;
0429:
0430: if (isSequential()) {
0431: for (Iterator it = this .liaPropagations.iterator(); it
0432: .hasNext();) {
0433: ((LIANodePropagation) it.next()).doPropagation(this );
0434: }
0435: }
0436:
0437: if (!this .actionQueue.isEmpty()) {
0438: executeQueuedActions();
0439: }
0440:
0441: boolean noneFired = true;
0442:
0443: if (!this .firing) {
0444: try {
0445: this .firing = true;
0446:
0447: while (continueFiring(fireLimit)
0448: && this .agenda.fireNextItem(agendaFilter)) {
0449: fireLimit = updateFireLimit(fireLimit);
0450: noneFired = false;
0451: if (!this .actionQueue.isEmpty()) {
0452: executeQueuedActions();
0453: }
0454: }
0455: } finally {
0456: this .firing = false;
0457: // @todo (mproctor) disabling Otherwise management for now, not happy with the current implementation
0458: // if ( noneFired ) {
0459: // doOtherwise( agendaFilter,
0460: // fireLimit );
0461: // }
0462:
0463: }
0464: }
0465: }
0466:
0467: private final boolean continueFiring(final int fireLimit) {
0468: return (!halt) && (fireLimit != 0);
0469: }
0470:
0471: private final int updateFireLimit(final int fireLimit) {
0472: return fireLimit > 0 ? fireLimit - 1 : fireLimit;
0473: }
0474:
0475: /**
0476: * This does the "otherwise" phase of processing.
0477: * If no items are fired, then it will assert a temporary "Otherwise"
0478: * fact and allow any rules to fire to handle "otherwise" cases.
0479: */
0480: private void doOtherwise(final AgendaFilter agendaFilter,
0481: int fireLimit) {
0482: final FactHandle handle = this .insert(new Otherwise());
0483: if (!this .actionQueue.isEmpty()) {
0484: executeQueuedActions();
0485: }
0486:
0487: while (continueFiring(fireLimit)
0488: && this .agenda.fireNextItem(agendaFilter)) {
0489: fireLimit = updateFireLimit(fireLimit);
0490: }
0491:
0492: this .retract(handle);
0493: }
0494:
0495: //
0496: // MN: The following is the traditional fireAllRules (without otherwise).
0497: // Purely kept here as this implementation of otherwise is still experimental.
0498: //
0499: // public synchronized void fireAllRules(final AgendaFilter agendaFilter) throws FactException {
0500: // // If we're already firing a rule, then it'll pick up
0501: // // the firing for any other assertObject(..) that get
0502: // // nested inside, avoiding concurrent-modification
0503: // // exceptions, depending on code paths of the actions.
0504: //
0505: // if ( !this.factQueue.isEmpty() ) {
0506: // propagateQueuedActions();
0507: // }
0508: //
0509: // if ( !this.firing ) {
0510: // try {
0511: // this.firing = true;
0512: //
0513: // while ( this.agenda.fireNextItem( agendaFilter ) ) {
0514: // ;
0515: // }
0516: // } finally {
0517: // this.firing = false;
0518: // }
0519: // }
0520: // }
0521:
0522: /**
0523: * Returns the fact Object for the given <code>FactHandle</code>. It
0524: * actually attemps to return the value from the handle, before retrieving
0525: * it from objects map.
0526: *
0527: * @see WorkingMemory
0528: *
0529: * @param handle
0530: * The <code>FactHandle</code> reference for the
0531: * <code>Object</code> lookup
0532: *
0533: */
0534: public Object getObject(final FactHandle handle) {
0535: try {
0536: this .lock.lock();
0537:
0538: // Make sure the FactHandle is from this WorkingMemory
0539: final InternalFactHandle internalHandle = (InternalFactHandle) this .assertMap
0540: .get(handle);
0541: if (internalHandle == null) {
0542: return null;
0543: }
0544:
0545: Object object = internalHandle.getObject();
0546:
0547: if (object != null && internalHandle.isShadowFact()) {
0548: object = ((ShadowProxy) object).getShadowedObject();
0549: }
0550:
0551: return object;
0552: } finally {
0553: this .lock.unlock();
0554: }
0555:
0556: }
0557:
0558: /**
0559: * @see WorkingMemory
0560: */
0561: public FactHandle getFactHandle(final Object object) {
0562: try {
0563: this .lock.lock();
0564: final FactHandle factHandle = (FactHandle) this .identityMap
0565: .get(object);
0566:
0567: return factHandle;
0568: } finally {
0569: this .lock.unlock();
0570: }
0571: }
0572:
0573: /**
0574: * This is an internal method, used to avoid java.util.Iterator adaptors
0575: */
0576: public ObjectHashMap getFactHandleMap() {
0577: return this .assertMap;
0578: }
0579:
0580: /**
0581: * This class is not thread safe, changes to the working memory during iteration may give unexpected results
0582: */
0583: public Iterator iterateObjects() {
0584: HashTableIterator iterator = new HashTableIterator(
0585: this .assertMap);
0586: iterator.reset();
0587: return new JavaIteratorAdapter(iterator,
0588: JavaIteratorAdapter.OBJECT);
0589: }
0590:
0591: /**
0592: * This class is not thread safe, changes to the working memory during iteration may give unexpected results
0593: */
0594: public Iterator iterateObjects(ObjectFilter filter) {
0595: HashTableIterator iterator = new HashTableIterator(
0596: this .assertMap);
0597: iterator.reset();
0598: return new JavaIteratorAdapter(iterator,
0599: JavaIteratorAdapter.OBJECT, filter);
0600: }
0601:
0602: /**
0603: * This class is not thread safe, changes to the working memory during iteration may give unexpected results
0604: */
0605: public Iterator iterateFactHandles() {
0606: HashTableIterator iterator = new HashTableIterator(
0607: this .assertMap);
0608: iterator.reset();
0609: return new JavaIteratorAdapter(iterator,
0610: JavaIteratorAdapter.FACT_HANDLE);
0611: }
0612:
0613: /**
0614: * This class is not thread safe, changes to the working memory during iteration may give unexpected results
0615: */
0616: public Iterator iterateFactHandles(ObjectFilter filter) {
0617: HashTableIterator iterator = new HashTableIterator(
0618: this .assertMap);
0619: iterator.reset();
0620: return new JavaIteratorAdapter(iterator,
0621: JavaIteratorAdapter.FACT_HANDLE, filter);
0622: }
0623:
0624: public abstract QueryResults getQueryResults(String query);
0625:
0626: public AgendaGroup getFocus() {
0627: return this .agenda.getFocus();
0628: }
0629:
0630: public void setFocus(final String focus) {
0631: this .agenda.setFocus(focus);
0632: }
0633:
0634: public void setFocus(final AgendaGroup focus) {
0635: this .agenda.setFocus(focus);
0636: }
0637:
0638: public TruthMaintenanceSystem getTruthMaintenanceSystem() {
0639: return this .tms;
0640: }
0641:
0642: /**
0643: * @see WorkingMemory
0644: */
0645: public FactHandle insert(final Object object) throws FactException {
0646: return insert(object, /* Not-Dynamic */
0647: false, false, null, null);
0648: }
0649:
0650: /**
0651: * @see WorkingMemory
0652: */
0653: public FactHandle insertLogical(final Object object)
0654: throws FactException {
0655: return insert(object, /* Not-Dynamic */
0656: false, true, null, null);
0657: }
0658:
0659: public FactHandle insert(final Object object, final boolean dynamic)
0660: throws FactException {
0661: return insert(object, dynamic, false, null, null);
0662: }
0663:
0664: public FactHandle insertLogical(final Object object,
0665: final boolean dynamic) throws FactException {
0666: return insert(object, dynamic, true, null, null);
0667: }
0668:
0669: public FactHandle insert(final Object object,
0670: final boolean dynamic, boolean logical, final Rule rule,
0671: final Activation activation) throws FactException {
0672: if (object == null) {
0673: // you cannot assert a null object
0674: return null;
0675: }
0676:
0677: InternalFactHandle handle = null;
0678:
0679: if (isSequential()) {
0680: handle = this .handleFactory.newFactHandle(object);
0681: addHandleToMaps(handle);
0682: insert(handle, object, rule, activation);
0683: return handle;
0684: }
0685:
0686: try {
0687: this .lock.lock();
0688: // check if the object already exists in the WM
0689: handle = (InternalFactHandle) this .assertMap.get(object);
0690:
0691: if (this .maintainTms) {
0692:
0693: EqualityKey key = null;
0694:
0695: if (handle == null) {
0696: // lets see if the object is already logical asserted
0697: key = this .tms.get(object);
0698: } else {
0699: // Object is already asserted, so check and possibly correct its
0700: // status and then return the handle
0701: key = handle.getEqualityKey();
0702:
0703: if (key.getStatus() == EqualityKey.STATED) {
0704: // return null as you cannot justify a stated object.
0705: return handle;
0706: }
0707:
0708: if (!logical) {
0709: // this object was previously justified, so we have to
0710: // override it to stated
0711: key.setStatus(EqualityKey.STATED);
0712: this .tms.removeLogicalDependencies(handle);
0713: } else {
0714: // this was object is already justified, so just add new
0715: // logical dependency
0716: this .tms.addLogicalDependency(handle,
0717: activation, activation
0718: .getPropagationContext(), rule);
0719: }
0720:
0721: return handle;
0722: }
0723:
0724: // At this point we know the handle is null
0725: if (key == null) {
0726: // key is also null, so treat as a totally new stated/logical
0727: // assert
0728: handle = this .handleFactory.newFactHandle(object);
0729: addHandleToMaps(handle);
0730:
0731: key = new EqualityKey(handle);
0732: handle.setEqualityKey(key);
0733: this .tms.put(key);
0734: if (!logical) {
0735: key.setStatus(EqualityKey.STATED);
0736: } else {
0737: key.setStatus(EqualityKey.JUSTIFIED);
0738: this .tms.addLogicalDependency(handle,
0739: activation, activation
0740: .getPropagationContext(), rule);
0741: }
0742: } else if (!logical) {
0743: if (key.getStatus() == EqualityKey.JUSTIFIED) {
0744: // Its previous justified, so switch to stated and remove
0745: // logical dependencies
0746: final InternalFactHandle justifiedHandle = key
0747: .getFactHandle();
0748: this .tms
0749: .removeLogicalDependencies(justifiedHandle);
0750:
0751: if (this .discardOnLogicalOverride) {
0752: // override, setting to new instance, and return
0753: // existing handle
0754: key.setStatus(EqualityKey.STATED);
0755: handle = key.getFactHandle();
0756:
0757: if (this .ruleBase.getConfiguration()
0758: .getAssertBehaviour() == AssertBehaviour.IDENTITY) {
0759: // as assertMap may be using an "identity" equality comparator,
0760: // we need to remove the handle from the map, before replacing the object
0761: // and then re-add the handle. Otherwise we may end up with a leak.
0762: this .assertMap.remove(handle);
0763: Object oldObject = handle.getObject();
0764: if (oldObject instanceof ShadowProxy) {
0765: ((ShadowProxy) oldObject)
0766: .setShadowedObject(object);
0767: } else {
0768: handle.setObject(object);
0769: }
0770: this .assertMap.put(handle, handle,
0771: false);
0772: } else {
0773: Object oldObject = handle.getObject();
0774: if (oldObject instanceof ShadowProxy) {
0775: ((ShadowProxy) oldObject)
0776: .setShadowedObject(object);
0777: } else {
0778: handle.setObject(object);
0779: }
0780: }
0781: return handle;
0782: } else {
0783: // override, then instantiate new handle for assertion
0784: key.setStatus(EqualityKey.STATED);
0785: handle = this .handleFactory
0786: .newFactHandle(object);
0787: handle.setEqualityKey(key);
0788: key.addFactHandle(handle);
0789: addHandleToMaps(handle);
0790:
0791: }
0792:
0793: } else {
0794: handle = this .handleFactory
0795: .newFactHandle(object);
0796: addHandleToMaps(handle);
0797: key.addFactHandle(handle);
0798: handle.setEqualityKey(key);
0799:
0800: }
0801:
0802: } else {
0803: if (key.getStatus() == EqualityKey.JUSTIFIED) {
0804: // only add as logical dependency if this wasn't previously
0805: // stated
0806: this .tms.addLogicalDependency(key
0807: .getFactHandle(), activation,
0808: activation.getPropagationContext(),
0809: rule);
0810: return key.getFactHandle();
0811: } else {
0812: // You cannot justify a previously stated equality equal
0813: // object, so return null
0814: return null;
0815: }
0816: }
0817:
0818: } else {
0819: if (handle != null) {
0820: return handle;
0821: }
0822: handle = this .handleFactory.newFactHandle(object);
0823: addHandleToMaps(handle);
0824:
0825: }
0826:
0827: if (dynamic) {
0828: addPropertyChangeListener(object);
0829: }
0830:
0831: insert(handle, object, rule, activation);
0832:
0833: } finally {
0834: this .lock.unlock();
0835: }
0836: return handle;
0837: }
0838:
0839: private void insert(InternalFactHandle handle, Object object,
0840: Rule rule, Activation activation) {
0841: this .ruleBase.executeQueuedActions();
0842:
0843: if (activation != null) {
0844: // release resources so that they can be GC'ed
0845: activation.getPropagationContext().releaseResources();
0846: }
0847: final PropagationContext propagationContext = new PropagationContextImpl(
0848: this .propagationIdCounter++,
0849: PropagationContext.ASSERTION, rule, activation,
0850: this .agenda.getActiveActivations(), this .agenda
0851: .getDormantActivations());
0852:
0853: doInsert(handle, object, propagationContext);
0854:
0855: this .workingMemoryEventSupport.fireObjectInserted(
0856: propagationContext, handle, object, this );
0857:
0858: if (!this .actionQueue.isEmpty()) {
0859: executeQueuedActions();
0860: }
0861: }
0862:
0863: protected void addPropertyChangeListener(final Object object) {
0864: try {
0865: final Method method = object
0866: .getClass()
0867: .getMethod(
0868: "addPropertyChangeListener",
0869: AbstractWorkingMemory.ADD_REMOVE_PROPERTY_CHANGE_LISTENER_ARG_TYPES);
0870:
0871: method.invoke(object,
0872: this .addRemovePropertyChangeListenerArgs);
0873: } catch (final NoSuchMethodException e) {
0874: System.err
0875: .println("Warning: Method addPropertyChangeListener not found"
0876: + " on the class "
0877: + object.getClass()
0878: + " so Drools will be unable to process JavaBean"
0879: + " PropertyChangeEvents on the asserted Object");
0880: } catch (final IllegalArgumentException e) {
0881: System.err
0882: .println("Warning: The addPropertyChangeListener method"
0883: + " on the class "
0884: + object.getClass()
0885: + " does not take"
0886: + " a simple PropertyChangeListener argument"
0887: + " so Drools will be unable to process JavaBean"
0888: + " PropertyChangeEvents on the asserted Object");
0889: } catch (final IllegalAccessException e) {
0890: System.err
0891: .println("Warning: The addPropertyChangeListener method"
0892: + " on the class "
0893: + object.getClass()
0894: + " is not public"
0895: + " so Drools will be unable to process JavaBean"
0896: + " PropertyChangeEvents on the asserted Object");
0897: } catch (final InvocationTargetException e) {
0898: System.err
0899: .println("Warning: The addPropertyChangeListener method"
0900: + " on the class "
0901: + object.getClass()
0902: + " threw an InvocationTargetException"
0903: + " so Drools will be unable to process JavaBean"
0904: + " PropertyChangeEvents on the asserted Object: "
0905: + e.getMessage());
0906: } catch (final SecurityException e) {
0907: System.err
0908: .println("Warning: The SecurityManager controlling the class "
0909: + object.getClass()
0910: + " did not allow the lookup of a"
0911: + " addPropertyChangeListener method"
0912: + " so Drools will be unable to process JavaBean"
0913: + " PropertyChangeEvents on the asserted Object: "
0914: + e.getMessage());
0915: }
0916: }
0917:
0918: public abstract void doInsert(InternalFactHandle factHandle,
0919: Object object, PropagationContext propagationContext)
0920: throws FactException;
0921:
0922: protected void removePropertyChangeListener(final FactHandle handle) {
0923: Object object = null;
0924: try {
0925: object = getObject(handle);
0926:
0927: if (object != null) {
0928: final Method mehod = object
0929: .getClass()
0930: .getMethod(
0931: "removePropertyChangeListener",
0932: AbstractWorkingMemory.ADD_REMOVE_PROPERTY_CHANGE_LISTENER_ARG_TYPES);
0933:
0934: mehod.invoke(object,
0935: this .addRemovePropertyChangeListenerArgs);
0936: }
0937: } catch (final NoSuchMethodException e) {
0938: // The removePropertyChangeListener method on the class
0939: // was not found so Drools will be unable to
0940: // stop processing JavaBean PropertyChangeEvents
0941: // on the retracted Object
0942: } catch (final IllegalArgumentException e) {
0943: throw new RuntimeDroolsException(
0944: "Warning: The removePropertyChangeListener method on the class "
0945: + object.getClass()
0946: + " does not take a simple PropertyChangeListener argument so Drools will be unable to stop processing JavaBean"
0947: + " PropertyChangeEvents on the retracted Object");
0948: } catch (final IllegalAccessException e) {
0949: throw new RuntimeDroolsException(
0950: "Warning: The removePropertyChangeListener method on the class "
0951: + object.getClass()
0952: + " is not public so Drools will be unable to stop processing JavaBean PropertyChangeEvents on the retracted Object");
0953: } catch (final InvocationTargetException e) {
0954: throw new RuntimeDroolsException(
0955: "Warning: The removePropertyChangeL istener method on the class "
0956: + object.getClass()
0957: + " threw an InvocationTargetException so Drools will be unable to stop processing JavaBean"
0958: + " PropertyChangeEvents on the retracted Object: "
0959: + e.getMessage());
0960: } catch (final SecurityException e) {
0961: throw new RuntimeDroolsException(
0962: "Warning: The SecurityManager controlling the class "
0963: + object.getClass()
0964: + " did not allow the lookup of a removePropertyChangeListener method so Drools will be unable to stop processing JavaBean"
0965: + " PropertyChangeEvents on the retracted Object: "
0966: + e.getMessage());
0967: }
0968: }
0969:
0970: public void retract(final FactHandle handle) throws FactException {
0971: retract(handle, true, true, null, null);
0972: }
0973:
0974: public abstract void doRetract(InternalFactHandle factHandle,
0975: PropagationContext propagationContext);
0976:
0977: /**
0978: * @see WorkingMemory
0979: */
0980: public void retract(final FactHandle factHandle,
0981: final boolean removeLogical, final boolean updateEqualsMap,
0982: final Rule rule, final Activation activation)
0983: throws FactException {
0984: try {
0985: this .lock.lock();
0986: this .ruleBase.executeQueuedActions();
0987:
0988: final InternalFactHandle handle = (InternalFactHandle) factHandle;
0989: if (handle.getId() == -1) {
0990: // can't retract an already retracted handle
0991: return;
0992: }
0993: removePropertyChangeListener(handle);
0994:
0995: if (activation != null) {
0996: // release resources so that they can be GC'ed
0997: activation.getPropagationContext().releaseResources();
0998: }
0999: final PropagationContext propagationContext = new PropagationContextImpl(
1000: this .propagationIdCounter++,
1001: PropagationContext.RETRACTION, rule, activation,
1002: this .agenda.getActiveActivations(), this .agenda
1003: .getDormantActivations());
1004:
1005: doRetract(handle, propagationContext);
1006:
1007: if (this .maintainTms) {
1008: // Update the equality key, which maintains a list of stated
1009: // FactHandles
1010: final EqualityKey key = handle.getEqualityKey();
1011:
1012: // Its justified so attempt to remove any logical dependencies for
1013: // the handle
1014: if (key.getStatus() == EqualityKey.JUSTIFIED) {
1015: this .tms.removeLogicalDependencies(handle);
1016: }
1017:
1018: key.removeFactHandle(handle);
1019: handle.setEqualityKey(null);
1020:
1021: // If the equality key is now empty, then remove it
1022: if (key.isEmpty()) {
1023: this .tms.remove(key);
1024: }
1025: }
1026:
1027: final Object object = handle.getObject();
1028:
1029: this .workingMemoryEventSupport.fireObjectRetracted(
1030: propagationContext, handle, object, this );
1031:
1032: removeHandleFromMaps(handle);
1033:
1034: this .handleFactory.destroyFactHandle(handle);
1035:
1036: if (!this .actionQueue.isEmpty()) {
1037: executeQueuedActions();
1038: }
1039: } finally {
1040: this .lock.unlock();
1041: }
1042: }
1043:
1044: private void addHandleToMaps(InternalFactHandle handle) {
1045: this .assertMap.put(handle, handle, false);
1046: if (this .ruleBase.getConfiguration().getAssertBehaviour() == AssertBehaviour.EQUALITY) {
1047: this .identityMap.put(handle, handle, false);
1048: }
1049: }
1050:
1051: private void removeHandleFromMaps(final InternalFactHandle handle) {
1052: this .assertMap.remove(handle);
1053: if (this .ruleBase.getConfiguration().getAssertBehaviour() == AssertBehaviour.EQUALITY) {
1054: this .identityMap.remove(handle);
1055: }
1056: }
1057:
1058: public void modifyRetract(final FactHandle factHandle) {
1059: modifyRetract(factHandle, null, null);
1060: }
1061:
1062: public void modifyRetract(final FactHandle factHandle,
1063: final Rule rule, final Activation activation) {
1064: try {
1065: this .lock.lock();
1066: this .ruleBase.executeQueuedActions();
1067:
1068: // only needed if we maintain tms, but either way we must get it before we do the retract
1069: int status = -1;
1070: if (this .maintainTms) {
1071: status = ((InternalFactHandle) factHandle)
1072: .getEqualityKey().getStatus();
1073: }
1074: final InternalFactHandle handle = (InternalFactHandle) factHandle;
1075: //final Object originalObject = (handle.isShadowFact()) ? ((ShadowProxy) handle.getObject()).getShadowedObject() : handle.getObject();
1076:
1077: if (handle.getId() == -1) {
1078: // the handle is invalid, most likely already retracted, so return
1079: return;
1080: }
1081:
1082: if (activation != null) {
1083: // release resources so that they can be GC'ed
1084: activation.getPropagationContext().releaseResources();
1085: }
1086: // Nowretract any trace of the original fact
1087: final PropagationContext propagationContext = new PropagationContextImpl(
1088: this .propagationIdCounter++,
1089: PropagationContext.MODIFICATION, rule, activation,
1090: this .agenda.getActiveActivations(), this .agenda
1091: .getDormantActivations());
1092: doRetract(handle, propagationContext);
1093:
1094: if (this .maintainTms) {
1095:
1096: // the hashCode and equality has changed, so we must update the EqualityKey
1097: EqualityKey key = handle.getEqualityKey();
1098: key.removeFactHandle(handle);
1099:
1100: // If the equality key is now empty, then remove it
1101: if (key.isEmpty()) {
1102: this .tms.remove(key);
1103: }
1104: }
1105: } finally {
1106: this .lock.unlock();
1107: }
1108: }
1109:
1110: public void modifyInsert(final FactHandle factHandle,
1111: final Object object) {
1112: modifyInsert(factHandle, object, null, null);
1113: }
1114:
1115: public void modifyInsert(final FactHandle factHandle,
1116: final Object object, final Rule rule,
1117: final Activation activation) {
1118: try {
1119: this .lock.lock();
1120: this .ruleBase.executeQueuedActions();
1121:
1122: final InternalFactHandle handle = (InternalFactHandle) factHandle;
1123: final Object originalObject = (handle.isShadowFact()) ? ((ShadowProxy) handle
1124: .getObject()).getShadowedObject()
1125: : handle.getObject();
1126:
1127: if (this .maintainTms) {
1128: EqualityKey key = handle.getEqualityKey();
1129:
1130: // now use an existing EqualityKey, if it exists, else create a new one
1131: key = this .tms.get(object);
1132: if (key == null) {
1133: key = new EqualityKey(handle, 0);
1134: this .tms.put(key);
1135: } else {
1136: key.addFactHandle(handle);
1137: }
1138:
1139: handle.setEqualityKey(key);
1140: }
1141:
1142: this .handleFactory.increaseFactHandleRecency(handle);
1143:
1144: if (activation != null) {
1145: // release resources so that they can be GC'ed
1146: activation.getPropagationContext().releaseResources();
1147: }
1148: // Nowretract any trace of the original fact
1149: final PropagationContext propagationContext = new PropagationContextImpl(
1150: this .propagationIdCounter++,
1151: PropagationContext.MODIFICATION, rule, activation,
1152: this .agenda.getActiveActivations(), this .agenda
1153: .getDormantActivations());
1154:
1155: doInsert(handle, object, propagationContext);
1156:
1157: this .workingMemoryEventSupport.fireObjectUpdated(
1158: propagationContext, factHandle, originalObject,
1159: object, this );
1160:
1161: propagationContext.clearRetractedTuples();
1162:
1163: if (!this .actionQueue.isEmpty()) {
1164: executeQueuedActions();
1165: }
1166: } finally {
1167: this .lock.unlock();
1168: }
1169: }
1170:
1171: public void update(final FactHandle handle, final Object object)
1172: throws FactException {
1173: update(handle, object, null, null);
1174: }
1175:
1176: /**
1177: * modify is implemented as half way retract / assert due to the truth
1178: * maintenance issues.
1179: *
1180: * @see WorkingMemory
1181: */
1182: public void update(final FactHandle factHandle,
1183: final Object object, final Rule rule,
1184: final Activation activation) throws FactException {
1185: try {
1186: this .lock.lock();
1187: this .ruleBase.executeQueuedActions();
1188:
1189: // only needed if we maintain tms, but either way we must get it before we do the retract
1190: int status = -1;
1191: if (this .maintainTms) {
1192: status = ((InternalFactHandle) factHandle)
1193: .getEqualityKey().getStatus();
1194: }
1195: final InternalFactHandle handle = (InternalFactHandle) factHandle;
1196: final Object originalObject = (handle.isShadowFact()) ? ((ShadowProxy) handle
1197: .getObject()).getShadowedObject()
1198: : handle.getObject();
1199:
1200: if (handle.getId() == -1 || object == null) {
1201: // the handle is invalid, most likely already retracted, so return
1202: // and we cannot assert a null object
1203: return;
1204: }
1205:
1206: if (activation != null) {
1207: // release resources so that they can be GC'ed
1208: activation.getPropagationContext().releaseResources();
1209: }
1210: // Nowretract any trace of the original fact
1211: final PropagationContext propagationContext = new PropagationContextImpl(
1212: this .propagationIdCounter++,
1213: PropagationContext.MODIFICATION, rule, activation,
1214: this .agenda.getActiveActivations(), this .agenda
1215: .getDormantActivations());
1216: doRetract(handle, propagationContext);
1217:
1218: if ((originalObject != object)
1219: || (this .ruleBase.getConfiguration()
1220: .getAssertBehaviour() != AssertBehaviour.IDENTITY)) {
1221: removeHandleFromMaps(handle);
1222:
1223: // set anyway, so that it updates the hashCodes
1224: handle.setObject(object);
1225: addHandleToMaps(handle);
1226: }
1227:
1228: if (this .maintainTms) {
1229:
1230: // the hashCode and equality has changed, so we must update the EqualityKey
1231: EqualityKey key = handle.getEqualityKey();
1232: key.removeFactHandle(handle);
1233:
1234: // If the equality key is now empty, then remove it
1235: if (key.isEmpty()) {
1236: this .tms.remove(key);
1237: }
1238:
1239: // now use an existing EqualityKey, if it exists, else create a new one
1240: key = this .tms.get(object);
1241: if (key == null) {
1242: key = new EqualityKey(handle, status);
1243: this .tms.put(key);
1244: } else {
1245: key.addFactHandle(handle);
1246: }
1247:
1248: handle.setEqualityKey(key);
1249: }
1250:
1251: this .handleFactory.increaseFactHandleRecency(handle);
1252:
1253: doInsert(handle, object, propagationContext);
1254:
1255: this .workingMemoryEventSupport.fireObjectUpdated(
1256: propagationContext, factHandle, originalObject,
1257: object, this );
1258:
1259: propagationContext.clearRetractedTuples();
1260:
1261: if (!this .actionQueue.isEmpty()) {
1262: executeQueuedActions();
1263: }
1264: } finally {
1265: this .lock.unlock();
1266: }
1267: }
1268:
1269: public void executeQueuedActions() {
1270: while (!actionQueue.isEmpty()) {
1271: final WorkingMemoryAction action = (WorkingMemoryAction) actionQueue
1272: .get(0);
1273: actionQueue.remove(0);
1274: action.execute(this );
1275: }
1276: // for ( final Iterator it = this.actionQueue.iterator(); it.hasNext(); ) {
1277: // final WorkingMemoryAction action = (WorkingMemoryAction) it.next();
1278: // it.remove();
1279: // action.execute( this );
1280: // }
1281: }
1282:
1283: public void queueWorkingMemoryAction(
1284: final WorkingMemoryAction action) {
1285: this .actionQueue.add(action);
1286: }
1287:
1288: public void removeLogicalDependencies(final Activation activation,
1289: final PropagationContext context, final Rule rule)
1290: throws FactException {
1291: if (this .maintainTms) {
1292: this .tms.removeLogicalDependencies(activation, context,
1293: rule);
1294: }
1295: }
1296:
1297: /**
1298: * Retrieve the <code>JoinMemory</code> for a particular
1299: * <code>JoinNode</code>.
1300: *
1301: * @param node
1302: * The <code>JoinNode</code> key.
1303: *
1304: * @return The node's memory.
1305: */
1306: public Object getNodeMemory(final NodeMemory node) {
1307: Object memory = this .nodeMemories.get(node.getId());
1308:
1309: if (memory == null) {
1310: memory = node
1311: .createMemory(this .ruleBase.getConfiguration());
1312:
1313: this .nodeMemories.put(node.getId(), memory);
1314: }
1315:
1316: return memory;
1317: }
1318:
1319: public void clearNodeMemory(final NodeMemory node) {
1320: this .nodeMemories.remove(node.getId());
1321: }
1322:
1323: public WorkingMemoryEventSupport getWorkingMemoryEventSupport() {
1324: return this .workingMemoryEventSupport;
1325: }
1326:
1327: public AgendaEventSupport getAgendaEventSupport() {
1328: return this .agendaEventSupport;
1329: }
1330:
1331: public RuleFlowEventSupport getRuleFlowEventSupport() {
1332: return this .ruleFlowEventSupport;
1333: }
1334:
1335: /**
1336: * Sets the AsyncExceptionHandler to handle exceptions thrown by the Agenda
1337: * Scheduler used for duration rules.
1338: *
1339: * @param handler
1340: */
1341: public void setAsyncExceptionHandler(
1342: final AsyncExceptionHandler handler) {
1343: // this.agenda.setAsyncExceptionHandler( handler );
1344: }
1345:
1346: /*
1347: * public void dumpMemory() { Iterator it = this.joinMemories.keySet(
1348: * ).iterator( ); while ( it.hasNext( ) ) { ((JoinMemory)
1349: * this.joinMemories.get( it.next( ) )).dump( ); } }
1350: */
1351:
1352: public void propertyChange(final PropertyChangeEvent event) {
1353: final Object object = event.getSource();
1354:
1355: try {
1356: update(getFactHandle(object), object);
1357: } catch (final FactException e) {
1358: throw new RuntimeException(e.getMessage());
1359: }
1360: }
1361:
1362: public long getNextPropagationIdCounter() {
1363: return this .propagationIdCounter++;
1364: }
1365:
1366: public Lock getLock() {
1367: return this .lock;
1368: }
1369:
1370: public class RuleFlowDeactivateEvent {
1371:
1372: public void propagate() {
1373:
1374: }
1375: }
1376:
1377: public ProcessInstance startProcess(final String processId) {
1378: final Process process = ((InternalRuleBase) getRuleBase())
1379: .getProcess(processId);
1380: if (process == null) {
1381: throw new IllegalArgumentException("Unknown process ID: "
1382: + processId);
1383: }
1384: if (process instanceof RuleFlowProcess) {
1385: final RuleFlowProcessInstance processInstance = new RuleFlowProcessInstanceImpl();
1386: processInstance.setWorkingMemory(this );
1387: processInstance.setProcess(process);
1388: processInstance.setId(++processCounter);
1389: processInstance.start();
1390:
1391: getRuleFlowEventSupport().fireRuleFlowProcessStarted(
1392: processInstance, this );
1393:
1394: return processInstance;
1395: } else {
1396: throw new IllegalArgumentException("Unknown process type: "
1397: + process.getClass());
1398: }
1399: }
1400:
1401: public List iterateObjectsToList() {
1402: List result = new ArrayList();
1403: Iterator iterator = iterateObjects();
1404: for (; iterator.hasNext();) {
1405: result.add(iterator.next());
1406: }
1407: return result;
1408: }
1409:
1410: public Entry[] getActivationParameters(long activationId) {
1411: Activation[] activations = getAgenda().getActivations();
1412: for (int i = 0; i < activations.length; i++) {
1413: if (activations[i].getActivationNumber() == activationId) {
1414: Map params = getActivationParameters(activations[i]);
1415: return (Entry[]) params.entrySet().toArray(
1416: new Entry[params.size()]);
1417: }
1418: }
1419: return new Entry[0];
1420: }
1421:
1422: /**
1423: * Helper method
1424: */
1425: public Map getActivationParameters(Activation activation) {
1426: Map result = new HashMap();
1427: Declaration[] declarations = activation.getRule()
1428: .getDeclarations();
1429: for (int i = 0; i < declarations.length; i++) {
1430: FactHandle handle = activation.getTuple().get(
1431: declarations[i]);
1432: if (handle instanceof InternalFactHandle) {
1433: result.put(declarations[i].getIdentifier(),
1434: declarations[i].getValue(this ,
1435: ((InternalFactHandle) handle)
1436: .getObject()));
1437: }
1438: }
1439: return result;
1440: }
1441:
1442: }
|