/** * 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. */ package org.drools.common; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.drools.FactException; import org.drools.core.util.ObjectHashMap; import org.drools.impl.StatefulKnowledgeSessionImpl; import org.drools.marshalling.impl.MarshallerReaderContext; import org.drools.marshalling.impl.MarshallerWriteContext; import org.drools.rule.Rule; import org.drools.spi.Activation; import org.drools.spi.PropagationContext; /** * The Truth Maintenance System is responsible for tracking two things. Firstly * It maintains a Map to track the classes with the same Equality, using the * EqualityKey. The EqualityKey has an internal datastructure which references * all the handles which are equal. Secondly It maintains another map tracking * the justificiations for logically asserted facts. * * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a> * */ public class TruthMaintenanceSystem { private static final long serialVersionUID = 510l; private AbstractWorkingMemory workingMemory; private ObjectHashMap justifiedMap; private ObjectHashMap assertMap; public TruthMaintenanceSystem() { } public TruthMaintenanceSystem(final AbstractWorkingMemory workingMemory) { this.workingMemory = workingMemory; this.justifiedMap = new ObjectHashMap(); this.assertMap = new ObjectHashMap(); this.assertMap.setComparator( EqualityKeyComparator.getInstance() ); } // public void write(WMSerialisationOutContext context) throws IOException { // ObjectOutputStream stream = context.stream; // // EqualityKey[] keys = new EqualityKey[ this.assertMap.size() ]; // org.drools.util.Iterator it = this.assertMap.iterator(); // int i = 0; // for ( ObjectEntry entry = ( ObjectEntry ) it.next(); entry != null; entry = ( ObjectEntry ) it.next() ) { // EqualityKey key = ( EqualityKey ) entry.getKey(); // keys[i++] = key; // } // // Arrays.sort( keys, EqualityKeySorter.instance ); // // // write the assert map of Equality keys // for ( EqualityKey key : keys ) { // stream.writeInt( PersisterEnums.EQUALITY_KEY ); // stream.writeInt( key.getStatus() ); // InternalFactHandle handle = key.getFactHandle(); // stream.writeInt( handle.getId() ); // context.out.println( "EqualityKey int:" + key.getStatus() + " int:" + handle.getId() ); // if ( key.getOtherFactHandle() != null && !key.getOtherFactHandle().isEmpty() ) { // for ( InternalFactHandle handle2 : key.getOtherFactHandle() ) { // stream.writeInt( PersisterEnums.FACT_HANDLE ); // stream.writeInt( handle2.getId() ); // context.out.println( "OtherHandle int:" + handle2.getId() ); // } // } // stream.writeInt( PersisterEnums.END ); // } // stream.writeInt( PersisterEnums.END ); // } // // public static class EqualityKeySorter implements Comparator<EqualityKey> { // public static final EqualityKeySorter instance = new EqualityKeySorter(); // public int compare(EqualityKey key1, // EqualityKey key2) { // return key1.getFactHandle().getId() - key2.getFactHandle().getId(); // } // } // // public void read(WMSerialisationInContext context) throws IOException { // ObjectInputStream stream = context.stream; // // while ( stream.readInt() == PersisterEnums.EQUALITY_KEY ) { // int status = stream.readInt(); // InternalFactHandle handle = ( InternalFactHandle ) context.handles.get( stream.readInt() ); // EqualityKey key = new EqualityKey(handle, status); // handle.setEqualityKey( key ); // while ( stream.readInt() == PersisterEnums.FACT_HANDLE ) { // handle = ( InternalFactHandle ) context.wm.getFactHandle( stream.readInt() ); // key.addFactHandle( handle ); // handle.setEqualityKey( key ); // } // put( key ); // } // } // public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // workingMemory = (AbstractWorkingMemory)in.readObject(); // justifiedMap = (PrimitiveLongMap)in.readObject(); // assertMap = (ObjectHashMap)in.readObject(); // } // // public void writeExternal(ObjectOutput out) throws IOException { // out.writeObject(workingMemory); // out.writeObject(justifiedMap); // out.writeObject(assertMap); // } public ObjectHashMap getJustifiedMap() { return this.justifiedMap; } public ObjectHashMap getAssertMap() { return this.assertMap; } public Object put(final EqualityKey key) { return this.assertMap.put( key, key, false ); } public EqualityKey get(final EqualityKey key) { return (EqualityKey) this.assertMap.get( key ); } public EqualityKey get(final Object object) { return (EqualityKey) this.assertMap.get( object ); } public EqualityKey remove(final EqualityKey key) { return (EqualityKey) this.assertMap.remove( key ); } /** * An Activation is no longer true so it no longer justifies any of the logical facts * it logically asserted. It iterates over the Activation's LinkedList of DependencyNodes * it retrieves the justitication set for each DependencyNode's FactHandle and removes * itself. If the Set is empty it retracts the FactHandle from the WorkingMemory. * * @param activation * @param context * @param rule * @throws FactException */ public void removeLogicalDependencies(final Activation activation, final PropagationContext context, final Rule rule) throws FactException { final org.drools.core.util.LinkedList list = activation.getLogicalDependencies(); if ( list == null || list.isEmpty() ) { return; } for ( LogicalDependency node = (LogicalDependency) list.getFirst(); node != null; node = (LogicalDependency) node.getNext() ) { removeLogicalDependency( activation, node, context ); } } public void removeLogicalDependency(final Activation activation, final LogicalDependency node, final PropagationContext context) { final InternalFactHandle handle = (InternalFactHandle) node.getFactHandle(); final Set set = (Set) this.justifiedMap.get( handle.getId() ); if ( set != null ) { set.remove( node ); WorkingMemoryAction action = new LogicalRetractCallback( this, node, set, handle, context, activation ); workingMemory.queueWorkingMemoryAction( action ); } } public static class LogicalRetractCallback implements WorkingMemoryAction { private TruthMaintenanceSystem tms; private LogicalDependency node; private Set set; private InternalFactHandle handle; private PropagationContext context; private Activation activation; public LogicalRetractCallback() { } public LogicalRetractCallback(final TruthMaintenanceSystem tms, final LogicalDependency node, final Set set, final InternalFactHandle handle, final PropagationContext context, final Activation activation) { this.tms = tms; this.node = node; this.set = set; this.handle = handle; this.context = context; } public LogicalRetractCallback(MarshallerReaderContext context) throws IOException { this.tms = context.wm.getTruthMaintenanceSystem(); this.handle = context.handles.get( context.readInt() ); this.context = context.propagationContexts.get( context.readLong() ); this.activation = (Activation) context.terminalTupleMap.get( context.readInt() ).getObject(); this.set = ( Set ) this.tms.getJustifiedMap().get( handle.getId() ); for ( Iterator it = this.set.iterator(); it.hasNext(); ) { LogicalDependency node = ( LogicalDependency ) it.next(); if ( node.getJustifier() == this.activation ) { this.node = node; break; } } } public void write(MarshallerWriteContext context) throws IOException { context.writeInt( WorkingMemoryAction.LogicalRetractCallback ); context.writeInt( this.handle.getId() ); context.writeLong( this.context.getPropagationNumber() ); context.writeInt( context.terminalTupleMap.get( this.activation.getTuple() ) ); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { tms = (TruthMaintenanceSystem) in.readObject(); node = (LogicalDependency) in.readObject(); set = (Set) in.readObject(); handle = (InternalFactHandle) in.readObject(); context = (PropagationContext) in.readObject(); activation = (Activation) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject( tms ); out.writeObject( node ); out.writeObject( set ); out.writeObject( handle ); out.writeObject( context ); out.writeObject( activation ); } public void execute(InternalWorkingMemory workingMemory) { if ( set.isEmpty() ) { this.tms.getJustifiedMap().remove( handle.getId() ); // this needs to be scheduled so we don't upset the current // working memory operation workingMemory.retract( this.handle, false, true, (Rule) context.getRuleOrigin(), this.activation ); } } public void execute(InternalKnowledgeRuntime kruntime) { execute(((StatefulKnowledgeSessionImpl) kruntime).getInternalWorkingMemory()); } } /** * The FactHandle is being removed from the system so remove any logical dependencies * between the justified FactHandle and its justifiers. Removes the FactHandle key * from the justifiedMap. It then iterates over all the LogicalDependency nodes, if any, * in the returned Set and removes the LogicalDependency node from the LinkedList maintained * by the Activation. * * @see LogicalDependency * * @param handle - The FactHandle to be removed * @throws FactException */ public void removeLogicalDependencies(final InternalFactHandle handle) throws FactException { final Set set = (Set) this.justifiedMap.remove( handle.getId() ); if ( set != null && !set.isEmpty() ) { for ( final Iterator it = set.iterator(); it.hasNext(); ) { final LogicalDependency node = (LogicalDependency) it.next(); node.getJustifier().getLogicalDependencies().remove( node ); } } } /** * Adds a justification for the FactHandle to the justifiedMap. * * @param handle * @param activation * @param context * @param rule * @throws FactException */ public void addLogicalDependency(final InternalFactHandle handle, final Activation activation, final PropagationContext context, final Rule rule) throws FactException { final LogicalDependency node = new LogicalDependency( activation, handle ); activation.getRule().setHasLogicalDependency( true ); activation.addLogicalDependency( node ); Set set = (Set) this.justifiedMap.get( handle.getId() ); if ( set == null ) { if ( context.getType() == PropagationContext.MODIFICATION ) { // if this was a update, chances are its trying to retract a logical assertion } set = new HashSet(); this.justifiedMap.put( handle.getId(), set ); } set.add( node ); } public void clear() { this.justifiedMap.clear(); this.assertMap.clear(); } }