[jboss-svn-commits] JBL Code SVN: r36014 - in labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src: main/java/org/drools/common and 9 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Sun Nov 21 18:41:00 EST 2010


Author: leo_gomes
Date: 2010-11-21 18:40:59 -0500 (Sun, 21 Nov 2010)
New Revision: 36014

Added:
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/Unlinkable.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/ConfigStep.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingComplex.nodeTestCase
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingModify.nodeTestCase
Modified:
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/RuleBaseConfiguration.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/common/PropagationContextImpl.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/ConcurrentRightTupleList.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleIndexHashTable.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleList.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleIndexHashTable.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleList.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/marshalling/impl/InputMarshaller.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/AbstractObjectSinkAdapter.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaMemory.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaNode.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/EmptyObjectSinkAdapter.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/JoinNode.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTuple.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTupleMemory.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectSinkPropagator.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/RightTupleMemory.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/spi/PropagationContext.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/RuleBaseConfigurationTest.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/MockObjectSource.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/JUnitNodeTestRunner.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/NodeSuiteTest.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngine.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngineTest.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/SingleTestCase.java
   labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/NodeTestCase.java
Log:
Initial implementation of L&R Unlinking. Only the right side is unlinked and once it's linked it's not unlinked again.

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/RuleBaseConfiguration.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/RuleBaseConfiguration.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/RuleBaseConfiguration.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -27,6 +27,7 @@
 import java.util.Properties;
 
 import org.drools.builder.conf.ClassLoaderCacheOption;
+import org.drools.builder.conf.LRUnlinkingOption;
 import org.drools.common.AgendaGroupFactory;
 import org.drools.common.ArrayAgendaGroupFactory;
 import org.drools.common.PriorityQueueAgendaGroupFactory;
@@ -100,7 +101,8 @@
  * drools.maxThreads = <-1|1..n>
  * drools.multithreadEvaluation = <true|false>
  * drools.mbeans = <enabled|disabled>
- * drools.classLoaderCacheEnabled = <true|false> 
+ * drools.classLoaderCacheEnabled = <true|false>
+ * drools.lrUnlinkingEnabled = <true|false> 
  * </pre>
  */
 public class RuleBaseConfiguration
@@ -132,6 +134,7 @@
     private String                         consequenceExceptionHandler;
     private String                         ruleBaseUpdateHandler;
     private boolean                        classLoaderCacheEnabled;
+    private boolean                        lrUnlinkingEnabled;
 
     private EventProcessingOption          eventProcessingMode;
 
@@ -175,6 +178,7 @@
         out.writeInt( maxThreads );
         out.writeObject( eventProcessingMode );
         out.writeBoolean( classLoaderCacheEnabled );
+        out.writeBoolean( lrUnlinkingEnabled );
     }
 
     public void readExternal(ObjectInput in) throws IOException,
@@ -202,6 +206,7 @@
         maxThreads = in.readInt();
         eventProcessingMode = (EventProcessingOption) in.readObject();
         classLoaderCacheEnabled = in.readBoolean();
+        lrUnlinkingEnabled = in.readBoolean();
     }
 
     /**
@@ -294,6 +299,8 @@
             setMBeansEnabled( MBeansOption.isEnabled( value ) );
         } else if ( name.equals( ClassLoaderCacheOption.PROPERTY_NAME ) ) {
             setClassLoaderCacheEnabled( StringUtils.isEmpty( value ) ? true : Boolean.valueOf( value ) );
+        } else if ( name.equals( LRUnlinkingOption.PROPERTY_NAME ) ) {
+            setLRUnlinkingEnabled( StringUtils.isEmpty( value ) ? false : Boolean.valueOf( value ) );
         }
     }
 
@@ -347,6 +354,8 @@
             return isMBeansEnabled() ? "enabled" : "disabled";
         } else if ( name.equals( ClassLoaderCacheOption.PROPERTY_NAME ) ) {
             return Boolean.toString( isClassLoaderCacheEnabled() );
+        } else if ( name.equals( LRUnlinkingOption.PROPERTY_NAME ) ) {
+            return Boolean.toString( isLRUnlinkingEnabled() );
         }
 
         return null;
@@ -443,6 +452,10 @@
 
         setClassLoaderCacheEnabled( Boolean.valueOf( this.chainedProperties.getProperty( ClassLoaderCacheOption.PROPERTY_NAME,
                                                                                          "true" ) ) );
+        
+        setLRUnlinkingEnabled( Boolean.valueOf( this.chainedProperties.getProperty( LRUnlinkingOption.PROPERTY_NAME,
+                                                                                        "false" ) ) );
+
     }
 
     /**
@@ -685,7 +698,23 @@
         this.classLoaderCacheEnabled = classLoaderCacheEnabled;
         this.classLoader.setCachingEnabled( this.classLoaderCacheEnabled );
     }
+    
+    /**
+     * @return whether or not Left & Right Unlinking is enabled.
+     */
+    public boolean isLRUnlinkingEnabled() {
+        return this.lrUnlinkingEnabled;
+    }
+    
+    /**
+     * Enable Left & Right Unlinking. 
+     * @param enabled
+     */
+    public void setLRUnlinkingEnabled(boolean enabled) {
+        this.lrUnlinkingEnabled = enabled;
+    }
 
+
     public List<Map<String, Object>> getWorkDefinitions() {
         if ( this.workDefinitions == null ) {
             initWorkDefinitions();
@@ -1046,6 +1075,8 @@
             return (T) (this.isMBeansEnabled() ? MBeansOption.ENABLED : MBeansOption.DISABLED);
         } else if ( ClassLoaderCacheOption.class.equals( option ) ) {
             return (T) (this.isClassLoaderCacheEnabled() ? ClassLoaderCacheOption.ENABLED : ClassLoaderCacheOption.DISABLED);
+        } else if ( LRUnlinkingOption.class.equals( option ) ) {
+            return (T) (this.isLRUnlinkingEnabled() ? ClassLoaderCacheOption.ENABLED : ClassLoaderCacheOption.DISABLED);
         }
         return null;
 
@@ -1088,6 +1119,8 @@
             setMBeansEnabled( ((MBeansOption) option).isEnabled() );
         } else if ( option instanceof ClassLoaderCacheOption ) {
             setClassLoaderCacheEnabled( ((ClassLoaderCacheOption) option).isClassLoaderCacheEnabled() );
+        } else if ( option instanceof LRUnlinkingOption ) {
+            setLRUnlinkingEnabled( ((LRUnlinkingOption) option).isLRUnlinkingEnabled() );
         }
 
     }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/common/PropagationContextImpl.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/common/PropagationContextImpl.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/common/PropagationContextImpl.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -49,6 +49,8 @@
     private EntryPoint         entryPoint;
     
     private int                originOffset;
+    
+    private int                 latestPropagationAttempt;
 
     public PropagationContextImpl() {
 
@@ -68,6 +70,7 @@
         this.dormantActivations = 0;
         this.entryPoint = EntryPoint.DEFAULT;
         this.originOffset = -1;
+        resetLatestPropagationAttempt();
     }
 
     public PropagationContextImpl(final long number,
@@ -87,6 +90,7 @@
         this.dormantActivations = dormantActivations;
         this.entryPoint = entryPoint;
         this.originOffset = -1;
+        resetLatestPropagationAttempt();
     }
 
     public void readExternal(ObjectInput in) throws IOException,
@@ -99,6 +103,7 @@
         this.leftTuple = (LeftTuple) in.readObject();
         this.entryPoint = (EntryPoint) in.readObject();
         this.originOffset = in.readInt();
+        this.latestPropagationAttempt = in.readInt();
     }
 
     public void writeExternal(ObjectOutput out) throws IOException {
@@ -110,6 +115,7 @@
         out.writeObject( this.leftTuple );
         out.writeObject( this.entryPoint );
         out.writeInt( this.originOffset );
+        out.writeInt( this.latestPropagationAttempt );
     }
 
     public long getPropagationNumber() {
@@ -189,4 +195,20 @@
         this.originOffset = originOffset;
     }
 
+    public int getLatestPropagationAttempt() {
+        return latestPropagationAttempt;
+    }
+
+    public void setLatestPropagationAttempt() {
+        this.latestPropagationAttempt = this.factHandle.getId();
+    }
+
+    public void resetLatestPropagationAttempt() {
+        this.latestPropagationAttempt = -1;
+    }
+    
+    
+    
+    
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/ConcurrentRightTupleList.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/ConcurrentRightTupleList.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/ConcurrentRightTupleList.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -202,4 +202,13 @@
     public Entry[] toArray() {
         throw new UnsupportedOperationException( "method is not implemented yet" );
     }    
+    
+    public void clear() {
+        
+        RightTuple rightTuple = null; 
+        
+        while((rightTuple = (RightTuple) iterator().next()) != null) {
+            remove(rightTuple);
+        }
+    }
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleIndexHashTable.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleIndexHashTable.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleIndexHashTable.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -397,4 +397,16 @@
         return builder.toString();
     }
 
+    
+    public void clear() {
+        
+        LeftTuple leftTuple = null; 
+        
+        while((leftTuple = (LeftTuple) iterator().next()) != null) {
+            remove(leftTuple);
+        }
+    }
+    
+    
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleList.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleList.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/LeftTupleList.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -217,4 +217,14 @@
 
         return builder.toString();
     }
+    
+    public void clear() {
+        
+        LeftTuple leftTuple = null; 
+        
+        while((leftTuple = (LeftTuple) iterator().next()) != null) {
+            remove(leftTuple);
+        }
+    }
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleIndexHashTable.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleIndexHashTable.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleIndexHashTable.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -259,7 +259,7 @@
                                            this.table.length );
                 RightTupleList previous = null;
                 RightTupleList current = (RightTupleList) this.table[index];
-                while ( current != memory ) {
+                while (current != memory ) {
                     previous = current;
                     current = (RightTupleList) current.getNext();
                 }
@@ -417,4 +417,15 @@
 
         return builder.toString();
     }
+    
+    public void clear() {
+
+        Iterator it = iterator();
+        
+        for ( RightTuple rightTuple = (RightTuple) it.next(); rightTuple != null; rightTuple = (RightTuple) it.next() ) {
+            remove(rightTuple);
+            rightTuple.setMemory(null);
+        }
+    }
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleList.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleList.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/core/util/RightTupleList.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -266,4 +266,14 @@
 
         return builder.toString();
     }
+    
+    public void clear() {
+        
+        RightTuple rightTuple = null; 
+        
+        while((rightTuple = (RightTuple) iterator().next()) != null) {
+            remove(rightTuple);
+        }
+    }
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/marshalling/impl/InputMarshaller.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/marshalling/impl/InputMarshaller.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/marshalling/impl/InputMarshaller.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -1,841 +1,849 @@
-/**
- * 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.marshalling.impl;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.Serializable;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Queue;
-
-import org.drools.RuntimeDroolsException;
-import org.drools.SessionConfiguration;
-import org.drools.base.ClassObjectType;
-import org.drools.common.AbstractWorkingMemory;
-import org.drools.common.AgendaItem;
-import org.drools.common.BaseNode;
-import org.drools.common.BinaryHeapQueueAgendaGroup;
-import org.drools.common.DefaultAgenda;
-import org.drools.common.DefaultFactHandle;
-import org.drools.common.EqualityKey;
-import org.drools.common.InternalAgendaGroup;
-import org.drools.common.InternalFactHandle;
-import org.drools.common.InternalRuleBase;
-import org.drools.common.InternalRuleFlowGroup;
-import org.drools.common.InternalWorkingMemory;
-import org.drools.common.NodeMemory;
-import org.drools.common.PropagationContextImpl;
-import org.drools.common.RuleFlowGroupImpl;
-import org.drools.common.TruthMaintenanceSystem;
-import org.drools.common.WorkingMemoryAction;
-import org.drools.concurrent.ExecutorService;
-import org.drools.core.util.ObjectHashMap;
-import org.drools.core.util.ObjectHashSet;
-import org.drools.impl.EnvironmentFactory;
-import org.drools.impl.StatefulKnowledgeSessionImpl;
-import org.drools.marshalling.ObjectMarshallingStrategy;
-import org.drools.process.instance.WorkItem;
-import org.drools.process.instance.impl.WorkItemImpl;
-import org.drools.reteoo.BetaMemory;
-import org.drools.reteoo.BetaNode;
-import org.drools.reteoo.EntryPointNode;
-import org.drools.reteoo.InitialFactImpl;
-import org.drools.reteoo.LeftTuple;
-import org.drools.reteoo.LeftTupleSink;
-import org.drools.reteoo.NodeTypeEnums;
-import org.drools.reteoo.ObjectTypeConf;
-import org.drools.reteoo.ObjectTypeNode;
-import org.drools.reteoo.ReteooStatefulSession;
-import org.drools.reteoo.ReteooWorkingMemory;
-import org.drools.reteoo.RightTuple;
-import org.drools.reteoo.RightTupleSink;
-import org.drools.reteoo.RuleTerminalNode;
-import org.drools.reteoo.AccumulateNode.AccumulateContext;
-import org.drools.reteoo.AccumulateNode.AccumulateMemory;
-import org.drools.reteoo.FromNode.FromMemory;
-import org.drools.rule.EntryPoint;
-import org.drools.rule.GroupElement;
-import org.drools.rule.Package;
-import org.drools.rule.Rule;
-import org.drools.runtime.Environment;
-import org.drools.runtime.rule.WorkingMemoryEntryPoint;
-import org.drools.spi.Activation;
-import org.drools.spi.AgendaGroup;
-import org.drools.spi.FactHandleFactory;
-import org.drools.spi.ObjectType;
-import org.drools.spi.PropagationContext;
-import org.drools.spi.RuleFlowGroup;
-
-public class InputMarshaller {
-
-	private static ProcessMarshaller processMarshaller = createProcessMarshaller();
-	
-    private static ProcessMarshaller createProcessMarshaller() {
-		try {
-			return ProcessMarshallerFactory.newProcessMarshaller();
-		} catch (IllegalArgumentException e) {
-			return null;
-		}
-    }
-	
-	/**
-     * Stream the data into an existing session
-     * 
-     * @param session
-     * @param context
-     * @param id
-     * @param executor
-     * @return
-     * @throws IOException
-     * @throws ClassNotFoundException
-     */
-    public static ReteooStatefulSession readSession(ReteooStatefulSession session,
-                                                    MarshallerReaderContext context) throws IOException,
-                                                                                    ClassNotFoundException {
-        boolean multithread = context.readBoolean();
-        int handleId = context.readInt();
-        long handleCounter = context.readLong();
-        long propagationCounter = context.readLong();
-
-        // these are for the InitialFactHandle, on a reset we just ignore
-        context.readInt();
-        context.readLong();
-
-        session.reset( handleId,
-                       handleCounter,
-                       propagationCounter );
-        DefaultAgenda agenda = (DefaultAgenda) session.getAgenda();
-
-        readAgenda( context,
-                    agenda );
-
-        context.wm = session;
-        
-        context.handles.put( context.wm.getInitialFactHandle().getId(),  context.wm.getInitialFactHandle() );
-
-        readFactHandles( context );       
-
-        readActionQueue( context );
-
-        if ( context.readBoolean() ) {
-            readTruthMaintenanceSystem( context );
-        }
-
-        if ( context.marshalProcessInstances && processMarshaller != null) {
-            processMarshaller.readProcessInstances( context );
-        }
-
-        if ( context.marshalWorkItems ) {
-        	processMarshaller.readWorkItems( context );
-        }
-
-        if (processMarshaller != null) {
-        	processMarshaller.readProcessTimers( context );
-        }
-        
-        if( multithread ) {
-            session.startPartitionManagers();
-        }
-
-        return session;
-    }
-
-    /**
-     * Create a new session into which to read the stream data
-     * @param context
-     * @param id
-     * @param executor
-     * @return
-     * @throws IOException
-     * @throws ClassNotFoundException
-     */
-    public static ReteooStatefulSession readSession(MarshallerReaderContext context,
-                                                    int id,
-                                                    ExecutorService executor) throws IOException,
-                                                                             ClassNotFoundException {
-        return readSession( context, id, executor, EnvironmentFactory.newEnvironment(), new SessionConfiguration() );
-    }
-    
-    public static ReteooStatefulSession readSession(MarshallerReaderContext context,
-                                                    int id,
-                                                    ExecutorService executor,
-                                                    Environment environment,
-                                                    SessionConfiguration config) throws IOException,
-                                                                             ClassNotFoundException {
-
-        boolean multithread = context.readBoolean();
-        
-        FactHandleFactory handleFactory = context.ruleBase.newFactHandleFactory( context.readInt(),
-                                                                                 context.readLong() );
-
-        InternalFactHandle initialFactHandle = new DefaultFactHandle( context.readInt(), //id
-                                                                      InitialFactImpl.getInstance(),
-                                                                      context.readLong(),
-                                                                      null);
-        
-        context.handles.put( initialFactHandle.getId(),
-                             initialFactHandle );
-
-        long propagationCounter = context.readLong();
-
-        DefaultAgenda agenda = new DefaultAgenda( context.ruleBase,
-                                                  false );
-        readAgenda( context,
-                    agenda );
-        ReteooStatefulSession session = new ReteooStatefulSession( id,
-                                                                   context.ruleBase,
-                                                                   executor,
-                                                                   handleFactory,
-                                                                   initialFactHandle,
-                                                                   propagationCounter,
-                                                                   config,  
-                                                                   agenda,
-                                                                   environment );
-        session.setKnowledgeRuntime(new StatefulKnowledgeSessionImpl(session));
-        
-        initialFactHandle.setEntryPoint( session.getEntryPoints().get( EntryPoint.DEFAULT.getEntryPointId() ) );
-
-        // RuleFlowGroups need to reference the session
-        for ( RuleFlowGroup group : agenda.getRuleFlowGroupsMap().values() ) {
-            ((RuleFlowGroupImpl) group).setWorkingMemory( session );
-        }
-        context.wm = session;
-
-        readFactHandles( context );
-
-        readActionQueue( context );
-
-        readTruthMaintenanceSystem( context );
-                
-        if ( context.marshalProcessInstances && processMarshaller != null ) {
-            processMarshaller.readProcessInstances( context );
-        }
-
-        if ( context.marshalWorkItems  && processMarshaller != null ) {
-        	processMarshaller.readWorkItems( context );
-        }
-
-        if (  processMarshaller != null ) {
-        	processMarshaller.readProcessTimers( context );
-        }
-
-        if( multithread ) {
-            session.startPartitionManagers();
-        }
-
-        return session;
-    }    
-
-    public static void readAgenda(MarshallerReaderContext context,
-                                  DefaultAgenda agenda) throws IOException {
-        ObjectInputStream stream = context.stream;
-        while ( stream.readShort() == PersisterEnums.AGENDA_GROUP ) {
-            BinaryHeapQueueAgendaGroup group = new BinaryHeapQueueAgendaGroup( stream.readUTF(),
-                                                                               context.ruleBase );
-            group.setActive( stream.readBoolean() );
-            agenda.getAgendaGroupsMap().put( group.getName(),
-                                             group );
-        }
-
-        while ( stream.readShort() == PersisterEnums.AGENDA_GROUP ) {
-            String agendaGroupName = stream.readUTF();
-            agenda.getStackList().add( agenda.getAgendaGroup( agendaGroupName ) );
-        }
-
-        while ( stream.readShort() == PersisterEnums.RULE_FLOW_GROUP ) {
-            String rfgName = stream.readUTF();
-            boolean active = stream.readBoolean();
-            boolean autoDeactivate = stream.readBoolean();
-            RuleFlowGroupImpl rfg = new RuleFlowGroupImpl( rfgName,
-                                                       active,
-                                                       autoDeactivate );
-            agenda.getRuleFlowGroupsMap().put( rfgName,
-                                               rfg );
-            int nbNodeInstances = stream.readInt();
-            for (int i = 0; i < nbNodeInstances; i++) {
-            	Long processInstanceId = stream.readLong();
-            	String nodeInstanceId = stream.readUTF();
-            	rfg.addNodeInstance(processInstanceId, nodeInstanceId);
-            }
-        }
-
-    }
-
-    public static void readActionQueue(MarshallerReaderContext context) throws IOException, ClassNotFoundException {
-        ReteooWorkingMemory wm = (ReteooWorkingMemory) context.wm;
-        Queue<WorkingMemoryAction> actionQueue = wm.getActionQueue();
-        while ( context.readShort() == PersisterEnums.WORKING_MEMORY_ACTION ) {
-            actionQueue.offer( PersisterHelper.readWorkingMemoryAction( context ) );
-        }
-    }
-
-    public static void readTruthMaintenanceSystem(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-
-        TruthMaintenanceSystem tms = context.wm.getTruthMaintenanceSystem();
-        while ( stream.readShort() == PersisterEnums.EQUALITY_KEY ) {
-            int status = stream.readInt();
-            int factHandleId = stream.readInt();
-            InternalFactHandle handle = (InternalFactHandle) context.handles.get( factHandleId );
-            
-            // ObjectTypeConf state is not marshalled, so it needs to be re-determined
-            ObjectTypeConf typeConf = context.wm.getObjectTypeConfigurationRegistry().getObjectTypeConf( context.wm.getEntryPoint(), handle.getObject() );
-            if ( !typeConf.isTMSEnabled() ) {
-                typeConf.enableTMS();
-            }
-            
-            EqualityKey key = new EqualityKey( handle,
-                                               status );
-            handle.setEqualityKey( key );
-            while ( stream.readShort() == PersisterEnums.FACT_HANDLE ) {
-                factHandleId = stream.readInt();
-                handle = (InternalFactHandle) context.handles.get( factHandleId );
-                key.addFactHandle( handle );
-                handle.setEqualityKey( key );
-            }
-            tms.put( key );
-        }
-    }
-
-    public static void readFactHandles(MarshallerReaderContext context) throws IOException,
-                                                                       ClassNotFoundException {
-        ObjectInputStream stream = context.stream;
-        InternalRuleBase ruleBase = context.ruleBase;
-        InternalWorkingMemory wm = context.wm;
-
-        if ( stream.readBoolean() ) {
-            InternalFactHandle initialFactHandle = wm.getInitialFactHandle();
-            int sinkId = stream.readInt();
-            ObjectTypeNode initialFactNode = (ObjectTypeNode) context.sinks.get( sinkId );
-            ObjectHashSet initialFactMemory = (ObjectHashSet) context.wm.getNodeMemory( initialFactNode );
-
-            initialFactMemory.add( initialFactHandle );
-            readRightTuples( initialFactHandle,
-                             context );
-        }
-
-        int size = stream.readInt();
-
-        // load the handles
-        InternalFactHandle[] handles = new InternalFactHandle[size];
-        for ( int i = 0; i < size; i++ ) {
-            InternalFactHandle handle = readFactHandle( context );
-
-            context.handles.put( handle.getId(),
-                                 handle );
-            handles[i] = handle;
-
-            context.wm.getObjectStore().addHandle( handle,
-                                                   handle.getObject() );
-
-            readRightTuples( handle,
-                             context );
-        }
-
-        EntryPointNode node = ruleBase.getRete().getEntryPointNode( EntryPoint.DEFAULT );
-        Map<ObjectType, ObjectTypeNode> objectTypeNodes = node.getObjectTypeNodes();
-
-        // add handles to object type nodes
-        for ( InternalFactHandle handle : handles ) {
-            Object object = handle.getObject();
-            ClassObjectType objectType = new ClassObjectType( object.getClass() );
-            ObjectTypeNode objectTypeNode = objectTypeNodes.get( objectType );
-            if (objectTypeNode != null) {
-	            ObjectHashSet set = (ObjectHashSet) context.wm.getNodeMemory( objectTypeNode );
-	            set.add( handle,
-	                     false );
-            }
-        }
-
-        InternalFactHandle handle = wm.getInitialFactHandle();
-        while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
-            LeftTupleSink sink = (LeftTupleSink) context.sinks.get( stream.readInt() );
-            LeftTuple leftTuple = new LeftTuple( handle,
-                                                 sink,
-                                                 true );
-            readLeftTuple( leftTuple,
-                           context );
-        }
-
-        readLeftTuples( context );
- 
-        readPropagationContexts( context );
-
-        readActivations( context );
-    }
-
-    public static InternalFactHandle readFactHandle(MarshallerReaderContext context) throws IOException,
-                                                                                    ClassNotFoundException {
-        int id = context.stream.readInt();
-        long recency = context.stream.readLong();
-
-        int strategyIndex = context.stream.readInt();
-        ObjectMarshallingStrategy strategy = context.resolverStrategyFactory.getStrategy( strategyIndex );
-        Object object = strategy.read( context.stream );
-        
-        WorkingMemoryEntryPoint entryPoint = null;
-        if(context.readBoolean()){
-            String entryPointId = context.readUTF();
-            if(entryPointId != null && !entryPointId.equals("")){
-                entryPoint = context.wm.getEntryPoints().get(entryPointId);
-            } 
-        }        
-
-        InternalFactHandle handle = new DefaultFactHandle( id,
-                                                           object,
-                                                           recency,
-                                                           entryPoint );
-
-        return handle;
-    }
-
-    public static void readRightTuples(InternalFactHandle factHandle,
-                                       MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-        while ( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
-            readRightTuple( context,
-                            factHandle );
-        }
-    }
-
-    public static void readRightTuple(MarshallerReaderContext context,
-                                      InternalFactHandle factHandle) throws IOException {
-        ObjectInputStream stream = context.stream;
-
-        int sinkId = stream.readInt();
-        RightTupleSink sink = (sinkId >= 0) ? (RightTupleSink) context.sinks.get( sinkId ) : null;
-
-        RightTuple rightTuple = new RightTuple( factHandle,
-                                                sink );
-        context.rightTuples.put( new RightTupleKey( factHandle.getId(),
-                                                    sink ),
-                                 rightTuple );
-
-        if( sink != null ) {
-            BetaMemory memory = null;
-            switch ( sink.getType() ) {
-                case NodeTypeEnums.AccumulateNode : {
-                    memory = ((AccumulateMemory) context.wm.getNodeMemory( (BetaNode) sink )).betaMemory;
-                    break;
-                }
-                default : {
-                    memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
-                    break;
-                }
-            }
-            memory.getRightTupleMemory().add( rightTuple );
-        }
-    }
-
-    public static void readLeftTuples(MarshallerReaderContext context) throws IOException,
-                                                                      ClassNotFoundException {
-        ObjectInputStream stream = context.stream;
-
-        while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
-            int nodeId = stream.readInt();
-            LeftTupleSink sink = (LeftTupleSink) context.sinks.get( nodeId );
-            int factHandleId = stream.readInt();
-            LeftTuple leftTuple = new LeftTuple( context.handles.get( factHandleId ),
-                                                 sink,
-                                                 true );
-            readLeftTuple( leftTuple,
-                           context );
-        }
-    }
-
-    public static void readLeftTuple(LeftTuple parentLeftTuple,
-                                     MarshallerReaderContext context) throws IOException,
-                                                                     ClassNotFoundException {
-        ObjectInputStream stream = context.stream;
-        Map<Integer, BaseNode> sinks = context.sinks;
-
-        LeftTupleSink sink = parentLeftTuple.getLeftTupleSink();
-
-        switch ( sink.getType() ) {
-            case NodeTypeEnums.JoinNode : {
-                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
-                memory.getLeftTupleMemory().add( parentLeftTuple );
-
-                while ( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
-                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                    int factHandleId = stream.readInt();
-                    RightTupleKey key = new RightTupleKey( factHandleId,
-                                                           sink );
-                    RightTuple rightTuple = context.rightTuples.get( key );
-                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                              rightTuple,
-                                                              childSink,
-                                                              true );
-                    readLeftTuple( childLeftTuple,
-                                   context );
-                }
-                break;
-
-            }
-            case NodeTypeEnums.EvalConditionNode : {
-                while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
-                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                              childSink,
-                                                              true );
-                    readLeftTuple( childLeftTuple,
-                                   context );
-                }
-                break;
-            }
-            case NodeTypeEnums.NotNode : 
-            case NodeTypeEnums.ForallNotNode : {
-                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
-                int type = stream.readShort();
-                if ( type == PersisterEnums.LEFT_TUPLE_NOT_BLOCKED ) {
-                    memory.getLeftTupleMemory().add( parentLeftTuple );
-
-                    while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
-                        LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                        LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                                  childSink,
-                                                                  true );
-                        readLeftTuple( childLeftTuple,
-                                       context );
-                    }
-
-                } else {
-                    int factHandleId = stream.readInt();
-                    RightTupleKey key = new RightTupleKey( factHandleId,
-                                                           sink );
-                    RightTuple rightTuple = context.rightTuples.get( key );
-
-                    parentLeftTuple.setBlocker( rightTuple );
-                    rightTuple.addBlocked( parentLeftTuple );
-                }
-                break;
-            }
-            case NodeTypeEnums.ExistsNode : {
-                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
-                int type = stream.readShort();
-                if ( type == PersisterEnums.LEFT_TUPLE_NOT_BLOCKED ) {
-                    memory.getLeftTupleMemory().add( parentLeftTuple );
-                } else {
-                    int factHandleId = stream.readInt();
-                    RightTupleKey key = new RightTupleKey( factHandleId,
-                                                           sink );
-                    RightTuple rightTuple = context.rightTuples.get( key );
-
-                    parentLeftTuple.setBlocker( rightTuple );
-                    rightTuple.addBlocked( parentLeftTuple );
-
-                    while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
-                        LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                        LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                                  childSink,
-                                                                  true );
-                        readLeftTuple( childLeftTuple,
-                                       context );
-                    }
-                }
-                break;
-            }
-            case NodeTypeEnums.AccumulateNode : {
-                // accumulate nodes generate new facts on-demand and need special procedures when de-serializing from persistent storage
-                AccumulateMemory memory = (AccumulateMemory) context.wm.getNodeMemory( (BetaNode) sink );
-                memory.betaMemory.getLeftTupleMemory().add( parentLeftTuple );
-
-                AccumulateContext accctx = new AccumulateContext();
-                memory.betaMemory.getCreatedHandles().put( parentLeftTuple,
-                                                           accctx,
-                                                           false );
-                // first we de-serialize the generated fact handle
-                InternalFactHandle handle = readFactHandle( context );
-                accctx.result = new RightTuple( handle,
-                                                (RightTupleSink) sink );
-
-                // then we de-serialize the associated accumulation context
-                accctx.context = (Serializable) stream.readObject();
-                // then we de-serialize the boolean propagated flag
-                accctx.propagated = stream.readBoolean();
-
-                // then we de-serialize all the propagated tuples
-                short head = -1;
-                while ( (head = stream.readShort()) != PersisterEnums.END ) {
-                    switch ( head ) {
-                        case PersisterEnums.RIGHT_TUPLE : {
-                            int factHandleId = stream.readInt();
-                            RightTupleKey key = new RightTupleKey( factHandleId,
-                                                                   sink );
-                            RightTuple rightTuple = context.rightTuples.get( key );
-                            // just wiring up the match record
-                            new LeftTuple( parentLeftTuple,
-                                           rightTuple,
-                                           sink,
-                                           true );
-                            break;
-                        }
-                        case PersisterEnums.LEFT_TUPLE : {
-                            LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                            LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                                      accctx.result,
-                                                                      childSink,
-                                                                      true );
-                            readLeftTuple( childLeftTuple,
-                                           context );
-                            break;
-                        }
-                        default : {
-                            throw new RuntimeDroolsException( "Marshalling error. This is a bug. Please contact the development team." );
-                        }
-                    }
-                }
-                break;
-            }
-            case NodeTypeEnums.RightInputAdaterNode : {
-                // RIANs generate new fact handles on-demand to wrap tuples and need special procedures when de-serializing from persistent storage
-                ObjectHashMap memory = (ObjectHashMap) context.wm.getNodeMemory( (NodeMemory) sink );
-                // create fact handle
-                int id = stream.readInt();
-                long recency = stream.readLong();
-                InternalFactHandle handle = new DefaultFactHandle( id,
-                                                                   parentLeftTuple,
-                                                                   recency,
-                                                                   context.wm.getEntryPoints().get( EntryPoint.DEFAULT.getEntryPointId() ) );
-                memory.put( parentLeftTuple, handle );
-                
-                readRightTuples( handle, context );
-                
-                stream.readShort(); // Persistence.END
-                break;
-            }
-            case NodeTypeEnums.FromNode: {
-//              context.out.println( "FromNode" );
-                // FNs generate new fact handles on-demand to wrap objects and need special procedures when serializing to persistent storage
-                FromMemory memory = (FromMemory) context.wm.getNodeMemory( (NodeMemory) sink );
-                
-                memory.betaMemory.getLeftTupleMemory().add( parentLeftTuple );                
-                Map<Object, RightTuple> matches =  new LinkedHashMap<Object, RightTuple>();
-                memory.betaMemory.getCreatedHandles().put( parentLeftTuple, matches );
-                
-                while( stream.readShort() == PersisterEnums.FACT_HANDLE ) {
-                    // we de-serialize the generated fact handle ID
-                    InternalFactHandle handle = readFactHandle( context );
-                    context.handles.put( handle.getId(),
-                                         handle );
-                    readRightTuples( handle, 
-                                     context );
-                    matches.put( handle.getObject(), handle.getFirstRightTuple() );
-                }
-                while( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
-                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
-                    int factHandleId = stream.readInt();
-                    RightTupleKey key = new RightTupleKey( factHandleId,
-                                                           null ); // created tuples in from node always use null sink
-                    RightTuple rightTuple = context.rightTuples.get( key );
-                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
-                                                              rightTuple,
-                                                              childSink,
-                                                              true );
-                    readLeftTuple( childLeftTuple,
-                                   context );
-                }
-//                context.out.println( "FromNode   ---   END" );
-                break;
-            }
-            case NodeTypeEnums.RuleTerminalNode : {
-                int pos = context.terminalTupleMap.size();
-                context.terminalTupleMap.put( pos,
-                                              parentLeftTuple );
-                break;
-            }
-        }
-    }
-
-    public static void readActivations(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-
-        while ( stream.readShort() == PersisterEnums.ACTIVATION ) {
-            readActivation( context );
-        }
-    }
-
-    public static Activation readActivation(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-        InternalRuleBase ruleBase = context.ruleBase;
-        InternalWorkingMemory wm = context.wm;
-
-        long activationNumber = stream.readLong();
-
-        int pos = stream.readInt();
-        LeftTuple leftTuple = context.terminalTupleMap.get( pos );
-
-        int salience = stream.readInt();
-
-        //PropagationContext context,
-        String pkgName = stream.readUTF();
-        String ruleName = stream.readUTF();
-        Package pkg = ruleBase.getPackage( pkgName );
-        Rule rule = pkg.getRule( ruleName );
-
-        RuleTerminalNode ruleTerminalNode = (RuleTerminalNode) leftTuple.getLeftTupleSink();
-        GroupElement subRule = ruleTerminalNode.getSubRule();
-
-        PropagationContext pc = context.propagationContexts.get( stream.readLong() );
-
-        AgendaItem activation = new AgendaItem( activationNumber,
-                                                leftTuple,
-                                                salience,
-                                                pc,
-                                                rule,
-                                                subRule );
-
-        leftTuple.setObject( activation );
-
-        if ( stream.readBoolean() ) {
-            String activationGroupName = stream.readUTF();
-            ((DefaultAgenda) wm.getAgenda()).getActivationGroup( activationGroupName ).addActivation( activation );
-        }
-
-        boolean activated = stream.readBoolean();
-        activation.setActivated( activated );
-
-        InternalAgendaGroup agendaGroup;
-        if ( rule.getAgendaGroup() == null || rule.getAgendaGroup().equals( "" ) || rule.getAgendaGroup().equals( AgendaGroup.MAIN ) ) {
-            // Is the Rule AgendaGroup undefined? If it is use MAIN,
-            // which is added to the Agenda by default
-            agendaGroup = (InternalAgendaGroup) ((DefaultAgenda) wm.getAgenda()).getAgendaGroup( AgendaGroup.MAIN );
-        } else {
-            // AgendaGroup is defined, so try and get the AgendaGroup
-            // from the Agenda
-            agendaGroup = (InternalAgendaGroup) ((DefaultAgenda) wm.getAgenda()).getAgendaGroup( rule.getAgendaGroup() );
-        }
-
-        activation.setAgendaGroup( agendaGroup );
-
-        if ( activated ) {
-            if ( rule.getRuleFlowGroup() == null ) {
-                agendaGroup.add( activation );
-            } else {
-                InternalRuleFlowGroup rfg = (InternalRuleFlowGroup) ((DefaultAgenda) wm.getAgenda()).getRuleFlowGroup( rule.getRuleFlowGroup() );
-                rfg.addActivation( activation );
-            }
-        }
-
-        TruthMaintenanceSystem tms = context.wm.getTruthMaintenanceSystem();
-        while ( stream.readShort() == PersisterEnums.LOGICAL_DEPENDENCY ) {
-            int factHandleId = stream.readInt();
-            InternalFactHandle handle = (InternalFactHandle) context.handles.get( factHandleId );
-            tms.addLogicalDependency( handle,
-                                      activation,
-                                      pc,
-                                      rule );
-        }
-
-        return activation;
-    }
-
-    public static void readPropagationContexts(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-
-        while ( stream.readShort() == PersisterEnums.PROPAGATION_CONTEXT ) {
-            readPropagationContext( context );
-        }
-
-    }
-
-    public static void readPropagationContext(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-        InternalRuleBase ruleBase = context.ruleBase;
-
-        int type = stream.readInt();
-
-        Rule rule = null;
-        if ( stream.readBoolean() ) {
-            String pkgName = stream.readUTF();
-            String ruleName = stream.readUTF();
-            Package pkg = ruleBase.getPackage( pkgName );
-            rule = pkg.getRule( ruleName );
-        }
-
-        LeftTuple leftTuple = null;
-        if ( stream.readBoolean() ) {
-            int tuplePos = stream.readInt();
-            leftTuple = (LeftTuple) context.terminalTupleMap.get( tuplePos );
-        }
-
-        long propagationNumber = stream.readLong();
-
-        int factHandleId = stream.readInt();
-        InternalFactHandle factHandle = context.handles.get( factHandleId );
-
-        int activeActivations = stream.readInt();
-        int dormantActivations = stream.readInt();
-        String entryPointId = stream.readUTF();
-
-        EntryPoint entryPoint = context.entryPoints.get( entryPointId );
-        if ( entryPoint == null ) {
-            entryPoint = new EntryPoint( entryPointId );
-            context.entryPoints.put( entryPointId,
-                                     entryPoint );
-        }
-
-        PropagationContext pc = new PropagationContextImpl( propagationNumber,
-                                                            type,
-                                                            rule,
-                                                            leftTuple,
-                                                            factHandle,
-                                                            activeActivations,
-                                                            dormantActivations,
-                                                            entryPoint );
-        context.propagationContexts.put( propagationNumber,
-                                         pc );
-    }
-
-    public static WorkItem readWorkItem(MarshallerReaderContext context) throws IOException {
-        ObjectInputStream stream = context.stream;
-
-        WorkItemImpl workItem = new WorkItemImpl();
-        workItem.setId( stream.readLong() );
-        workItem.setProcessInstanceId( stream.readLong() );
-        workItem.setName( stream.readUTF() );
-        workItem.setState( stream.readInt() );
-
-        //WorkItem Paramaters
-        int nbVariables = stream.readInt();
-            if (nbVariables > 0) {
-                
-                for (int i = 0; i < nbVariables; i++) {
-                    String name = stream.readUTF();
-                    try {
-                        int index = stream.readInt();
-                        ObjectMarshallingStrategy strategy = context.resolverStrategyFactory.getStrategy(index);
-                        
-                        Object value = strategy.read(stream);
-                        workItem.setParameter(name, value);
-                    } catch (ClassNotFoundException e) {
-                        throw new IllegalArgumentException(
-                                "Could not reload variable " + name);
-                    }
-                }
-            }
-        
-      
-        
-
-        return workItem;
-    }
-
-   
-
-}
+/**
+ * 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.marshalling.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Queue;
+
+import org.drools.RuntimeDroolsException;
+import org.drools.SessionConfiguration;
+import org.drools.base.ClassObjectType;
+import org.drools.common.AbstractWorkingMemory;
+import org.drools.common.AgendaItem;
+import org.drools.common.BaseNode;
+import org.drools.common.BinaryHeapQueueAgendaGroup;
+import org.drools.common.DefaultAgenda;
+import org.drools.common.DefaultFactHandle;
+import org.drools.common.EqualityKey;
+import org.drools.common.InternalAgendaGroup;
+import org.drools.common.InternalFactHandle;
+import org.drools.common.InternalRuleBase;
+import org.drools.common.InternalRuleFlowGroup;
+import org.drools.common.InternalWorkingMemory;
+import org.drools.common.NodeMemory;
+import org.drools.common.PropagationContextImpl;
+import org.drools.common.RuleFlowGroupImpl;
+import org.drools.common.TruthMaintenanceSystem;
+import org.drools.common.WorkingMemoryAction;
+import org.drools.concurrent.ExecutorService;
+import org.drools.core.util.ObjectHashMap;
+import org.drools.core.util.ObjectHashSet;
+import org.drools.impl.EnvironmentFactory;
+import org.drools.impl.StatefulKnowledgeSessionImpl;
+import org.drools.marshalling.ObjectMarshallingStrategy;
+import org.drools.process.instance.WorkItem;
+import org.drools.process.instance.impl.WorkItemImpl;
+import org.drools.reteoo.BetaMemory;
+import org.drools.reteoo.BetaNode;
+import org.drools.reteoo.EntryPointNode;
+import org.drools.reteoo.InitialFactImpl;
+import org.drools.reteoo.LeftTuple;
+import org.drools.reteoo.LeftTupleSink;
+import org.drools.reteoo.NodeTypeEnums;
+import org.drools.reteoo.ObjectTypeConf;
+import org.drools.reteoo.ObjectTypeNode;
+import org.drools.reteoo.ReteooStatefulSession;
+import org.drools.reteoo.ReteooWorkingMemory;
+import org.drools.reteoo.RightTuple;
+import org.drools.reteoo.RightTupleSink;
+import org.drools.reteoo.RuleTerminalNode;
+import org.drools.reteoo.AccumulateNode.AccumulateContext;
+import org.drools.reteoo.AccumulateNode.AccumulateMemory;
+import org.drools.reteoo.FromNode.FromMemory;
+import org.drools.rule.EntryPoint;
+import org.drools.rule.GroupElement;
+import org.drools.rule.Package;
+import org.drools.rule.Rule;
+import org.drools.runtime.Environment;
+import org.drools.runtime.rule.WorkingMemoryEntryPoint;
+import org.drools.spi.Activation;
+import org.drools.spi.AgendaGroup;
+import org.drools.spi.FactHandleFactory;
+import org.drools.spi.ObjectType;
+import org.drools.spi.PropagationContext;
+import org.drools.spi.RuleFlowGroup;
+
+public class InputMarshaller {
+
+    private static ProcessMarshaller processMarshaller = createProcessMarshaller();
+    
+    private static ProcessMarshaller createProcessMarshaller() {
+        try {
+            return ProcessMarshallerFactory.newProcessMarshaller();
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+    
+    /**
+     * Stream the data into an existing session
+     * 
+     * @param session
+     * @param context
+     * @param id
+     * @param executor
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public static ReteooStatefulSession readSession(ReteooStatefulSession session,
+                                                    MarshallerReaderContext context) throws IOException,
+                                                                                    ClassNotFoundException {
+        boolean multithread = context.readBoolean();
+        int handleId = context.readInt();
+        long handleCounter = context.readLong();
+        long propagationCounter = context.readLong();
+
+        // these are for the InitialFactHandle, on a reset we just ignore
+        context.readInt();
+        context.readLong();
+
+        session.reset( handleId,
+                       handleCounter,
+                       propagationCounter );
+        DefaultAgenda agenda = (DefaultAgenda) session.getAgenda();
+
+        readAgenda( context,
+                    agenda );
+
+        context.wm = session;
+        
+        context.handles.put( context.wm.getInitialFactHandle().getId(),  context.wm.getInitialFactHandle() );
+
+        readFactHandles( context );       
+
+        readActionQueue( context );
+
+        if ( context.readBoolean() ) {
+            readTruthMaintenanceSystem( context );
+        }
+
+        if ( context.marshalProcessInstances && processMarshaller != null) {
+            processMarshaller.readProcessInstances( context );
+        }
+
+        if ( context.marshalWorkItems ) {
+            processMarshaller.readWorkItems( context );
+        }
+
+        if (processMarshaller != null) {
+            processMarshaller.readProcessTimers( context );
+        }
+        
+        if( multithread ) {
+            session.startPartitionManagers();
+        }
+
+        return session;
+    }
+
+    /**
+     * Create a new session into which to read the stream data
+     * @param context
+     * @param id
+     * @param executor
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public static ReteooStatefulSession readSession(MarshallerReaderContext context,
+                                                    int id,
+                                                    ExecutorService executor) throws IOException,
+                                                                             ClassNotFoundException {
+        return readSession( context, id, executor, EnvironmentFactory.newEnvironment(), new SessionConfiguration() );
+    }
+    
+    public static ReteooStatefulSession readSession(MarshallerReaderContext context,
+                                                    int id,
+                                                    ExecutorService executor,
+                                                    Environment environment,
+                                                    SessionConfiguration config) throws IOException,
+                                                                             ClassNotFoundException {
+
+        boolean multithread = context.readBoolean();
+        
+        FactHandleFactory handleFactory = context.ruleBase.newFactHandleFactory( context.readInt(),
+                                                                                 context.readLong() );
+
+        InternalFactHandle initialFactHandle = new DefaultFactHandle( context.readInt(), //id
+                                                                      InitialFactImpl.getInstance(),
+                                                                      context.readLong(),
+                                                                      null);
+        
+        context.handles.put( initialFactHandle.getId(),
+                             initialFactHandle );
+
+        long propagationCounter = context.readLong();
+
+        DefaultAgenda agenda = new DefaultAgenda( context.ruleBase,
+                                                  false );
+        readAgenda( context,
+                    agenda );
+        ReteooStatefulSession session = new ReteooStatefulSession( id,
+                                                                   context.ruleBase,
+                                                                   executor,
+                                                                   handleFactory,
+                                                                   initialFactHandle,
+                                                                   propagationCounter,
+                                                                   config,  
+                                                                   agenda,
+                                                                   environment );
+        session.setKnowledgeRuntime(new StatefulKnowledgeSessionImpl(session));
+        
+        initialFactHandle.setEntryPoint( session.getEntryPoints().get( EntryPoint.DEFAULT.getEntryPointId() ) );
+
+        // RuleFlowGroups need to reference the session
+        for ( RuleFlowGroup group : agenda.getRuleFlowGroupsMap().values() ) {
+            ((RuleFlowGroupImpl) group).setWorkingMemory( session );
+        }
+        context.wm = session;
+
+        readFactHandles( context );
+
+        readActionQueue( context );
+
+        readTruthMaintenanceSystem( context );
+                
+        if ( context.marshalProcessInstances && processMarshaller != null ) {
+            processMarshaller.readProcessInstances( context );
+        }
+
+        if ( context.marshalWorkItems  && processMarshaller != null ) {
+            processMarshaller.readWorkItems( context );
+        }
+
+        if (  processMarshaller != null ) {
+            processMarshaller.readProcessTimers( context );
+        }
+
+        if( multithread ) {
+            session.startPartitionManagers();
+        }
+
+        return session;
+    }    
+
+    public static void readAgenda(MarshallerReaderContext context,
+                                  DefaultAgenda agenda) throws IOException {
+        ObjectInputStream stream = context.stream;
+        while ( stream.readShort() == PersisterEnums.AGENDA_GROUP ) {
+            BinaryHeapQueueAgendaGroup group = new BinaryHeapQueueAgendaGroup( stream.readUTF(),
+                                                                               context.ruleBase );
+            group.setActive( stream.readBoolean() );
+            agenda.getAgendaGroupsMap().put( group.getName(),
+                                             group );
+        }
+
+        while ( stream.readShort() == PersisterEnums.AGENDA_GROUP ) {
+            String agendaGroupName = stream.readUTF();
+            agenda.getStackList().add( agenda.getAgendaGroup( agendaGroupName ) );
+        }
+
+        while ( stream.readShort() == PersisterEnums.RULE_FLOW_GROUP ) {
+            String rfgName = stream.readUTF();
+            boolean active = stream.readBoolean();
+            boolean autoDeactivate = stream.readBoolean();
+            RuleFlowGroupImpl rfg = new RuleFlowGroupImpl( rfgName,
+                                                       active,
+                                                       autoDeactivate );
+            agenda.getRuleFlowGroupsMap().put( rfgName,
+                                               rfg );
+            int nbNodeInstances = stream.readInt();
+            for (int i = 0; i < nbNodeInstances; i++) {
+                Long processInstanceId = stream.readLong();
+                String nodeInstanceId = stream.readUTF();
+                rfg.addNodeInstance(processInstanceId, nodeInstanceId);
+            }
+        }
+
+    }
+
+    public static void readActionQueue(MarshallerReaderContext context) throws IOException, ClassNotFoundException {
+        ReteooWorkingMemory wm = (ReteooWorkingMemory) context.wm;
+        Queue<WorkingMemoryAction> actionQueue = wm.getActionQueue();
+        while ( context.readShort() == PersisterEnums.WORKING_MEMORY_ACTION ) {
+            actionQueue.offer( PersisterHelper.readWorkingMemoryAction( context ) );
+        }
+    }
+
+    public static void readTruthMaintenanceSystem(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+
+        TruthMaintenanceSystem tms = context.wm.getTruthMaintenanceSystem();
+        while ( stream.readShort() == PersisterEnums.EQUALITY_KEY ) {
+            int status = stream.readInt();
+            int factHandleId = stream.readInt();
+            InternalFactHandle handle = (InternalFactHandle) context.handles.get( factHandleId );
+            
+            // ObjectTypeConf state is not marshalled, so it needs to be re-determined
+            ObjectTypeConf typeConf = context.wm.getObjectTypeConfigurationRegistry().getObjectTypeConf( context.wm.getEntryPoint(), handle.getObject() );
+            if ( !typeConf.isTMSEnabled() ) {
+                typeConf.enableTMS();
+            }
+            
+            EqualityKey key = new EqualityKey( handle,
+                                               status );
+            handle.setEqualityKey( key );
+            while ( stream.readShort() == PersisterEnums.FACT_HANDLE ) {
+                factHandleId = stream.readInt();
+                handle = (InternalFactHandle) context.handles.get( factHandleId );
+                key.addFactHandle( handle );
+                handle.setEqualityKey( key );
+            }
+            tms.put( key );
+        }
+    }
+
+    public static void readFactHandles(MarshallerReaderContext context) throws IOException,
+                                                                       ClassNotFoundException {
+        ObjectInputStream stream = context.stream;
+        InternalRuleBase ruleBase = context.ruleBase;
+        InternalWorkingMemory wm = context.wm;
+
+        if ( stream.readBoolean() ) {
+            InternalFactHandle initialFactHandle = wm.getInitialFactHandle();
+            int sinkId = stream.readInt();
+            ObjectTypeNode initialFactNode = (ObjectTypeNode) context.sinks.get( sinkId );
+            ObjectHashSet initialFactMemory = (ObjectHashSet) context.wm.getNodeMemory( initialFactNode );
+
+            initialFactMemory.add( initialFactHandle );
+            readRightTuples( initialFactHandle,
+                             context );
+        }
+
+        int size = stream.readInt();
+
+        // load the handles
+        InternalFactHandle[] handles = new InternalFactHandle[size];
+        for ( int i = 0; i < size; i++ ) {
+            InternalFactHandle handle = readFactHandle( context );
+
+            context.handles.put( handle.getId(),
+                                 handle );
+            handles[i] = handle;
+
+            context.wm.getObjectStore().addHandle( handle,
+                                                   handle.getObject() );
+
+            readRightTuples( handle,
+                             context );
+        }
+
+        EntryPointNode node = ruleBase.getRete().getEntryPointNode( EntryPoint.DEFAULT );
+        Map<ObjectType, ObjectTypeNode> objectTypeNodes = node.getObjectTypeNodes();
+
+        // add handles to object type nodes
+        for ( InternalFactHandle handle : handles ) {
+            Object object = handle.getObject();
+            ClassObjectType objectType = new ClassObjectType( object.getClass() );
+            ObjectTypeNode objectTypeNode = objectTypeNodes.get( objectType );
+            if (objectTypeNode != null) {
+                ObjectHashSet set = (ObjectHashSet) context.wm.getNodeMemory( objectTypeNode );
+                set.add( handle,
+                         false );
+            }
+        }
+
+        InternalFactHandle handle = wm.getInitialFactHandle();
+        while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
+            LeftTupleSink sink = (LeftTupleSink) context.sinks.get( stream.readInt() );
+            LeftTuple leftTuple = new LeftTuple( handle,
+                                                 sink,
+                                                 true );
+            readLeftTuple( leftTuple,
+                           context );
+        }
+
+        readLeftTuples( context );
+ 
+        readPropagationContexts( context );
+
+        readActivations( context );
+    }
+
+    public static InternalFactHandle readFactHandle(MarshallerReaderContext context) throws IOException,
+                                                                                    ClassNotFoundException {
+        int id = context.stream.readInt();
+        long recency = context.stream.readLong();
+
+        int strategyIndex = context.stream.readInt();
+        ObjectMarshallingStrategy strategy = context.resolverStrategyFactory.getStrategy( strategyIndex );
+        Object object = strategy.read( context.stream );
+        
+        WorkingMemoryEntryPoint entryPoint = null;
+        if(context.readBoolean()){
+            String entryPointId = context.readUTF();
+            if(entryPointId != null && !entryPointId.equals("")){
+                entryPoint = context.wm.getEntryPoints().get(entryPointId);
+            } 
+        }        
+
+        InternalFactHandle handle = new DefaultFactHandle( id,
+                                                           object,
+                                                           recency,
+                                                           entryPoint );
+
+        return handle;
+    }
+
+    public static void readRightTuples(InternalFactHandle factHandle,
+                                       MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+        while ( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
+            readRightTuple( context,
+                            factHandle );
+        }
+    }
+
+    public static void readRightTuple(MarshallerReaderContext context,
+                                      InternalFactHandle factHandle) throws IOException {
+        ObjectInputStream stream = context.stream;
+
+        int sinkId = stream.readInt();
+        RightTupleSink sink = (sinkId >= 0) ? (RightTupleSink) context.sinks.get( sinkId ) : null;
+
+        RightTuple rightTuple = new RightTuple( factHandle,
+                                                sink );
+        context.rightTuples.put( new RightTupleKey( factHandle.getId(),
+                                                    sink ),
+                                 rightTuple );
+
+        if( sink != null ) {
+            BetaMemory memory = null;
+            switch ( sink.getType() ) {
+                case NodeTypeEnums.AccumulateNode : {
+                    memory = ((AccumulateMemory) context.wm.getNodeMemory( (BetaNode) sink )).betaMemory;
+                    break;
+                }
+                default : {
+                    memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
+                    break;
+                }
+            }
+            memory.getRightTupleMemory().add( rightTuple );
+            memory.linkLeft();
+        }
+    }
+
+    public static void readLeftTuples(MarshallerReaderContext context) throws IOException,
+                                                                      ClassNotFoundException {
+        ObjectInputStream stream = context.stream;
+
+        while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
+            int nodeId = stream.readInt();
+            LeftTupleSink sink = (LeftTupleSink) context.sinks.get( nodeId );
+            int factHandleId = stream.readInt();
+            LeftTuple leftTuple = new LeftTuple( context.handles.get( factHandleId ),
+                                                 sink,
+                                                 true );
+            readLeftTuple( leftTuple,
+                           context );
+        }
+    }
+
+    public static void readLeftTuple(LeftTuple parentLeftTuple,
+                                     MarshallerReaderContext context) throws IOException,
+                                                                     ClassNotFoundException {
+        ObjectInputStream stream = context.stream;
+        Map<Integer, BaseNode> sinks = context.sinks;
+
+        LeftTupleSink sink = parentLeftTuple.getLeftTupleSink();
+
+        switch ( sink.getType() ) {
+            case NodeTypeEnums.JoinNode : {
+                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
+                addToLeftMemory(parentLeftTuple, memory);
+
+                while ( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
+                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                    int factHandleId = stream.readInt();
+                    RightTupleKey key = new RightTupleKey( factHandleId,
+                                                           sink );
+                    RightTuple rightTuple = context.rightTuples.get( key );
+                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                              rightTuple,
+                                                              childSink,
+                                                              true );
+                    readLeftTuple( childLeftTuple,
+                                   context );
+                }
+                break;
+
+            }
+            case NodeTypeEnums.EvalConditionNode : {
+                while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
+                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                              childSink,
+                                                              true );
+                    readLeftTuple( childLeftTuple,
+                                   context );
+                }
+                break;
+            }
+            case NodeTypeEnums.NotNode : 
+            case NodeTypeEnums.ForallNotNode : {
+                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
+                int type = stream.readShort();
+                if ( type == PersisterEnums.LEFT_TUPLE_NOT_BLOCKED ) {
+                    addToLeftMemory(parentLeftTuple, memory);
+
+                    while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
+                        LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                        LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                                  childSink,
+                                                                  true );
+                        readLeftTuple( childLeftTuple,
+                                       context );
+                    }
+
+                } else {
+                    int factHandleId = stream.readInt();
+                    RightTupleKey key = new RightTupleKey( factHandleId,
+                                                           sink );
+                    RightTuple rightTuple = context.rightTuples.get( key );
+
+                    parentLeftTuple.setBlocker( rightTuple );
+                    rightTuple.addBlocked( parentLeftTuple );
+                }
+                break;
+            }
+            case NodeTypeEnums.ExistsNode : {
+                BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
+                int type = stream.readShort();
+                if ( type == PersisterEnums.LEFT_TUPLE_NOT_BLOCKED ) {
+                    addToLeftMemory(parentLeftTuple, memory);
+                } else {
+                    int factHandleId = stream.readInt();
+                    RightTupleKey key = new RightTupleKey( factHandleId,
+                                                           sink );
+                    RightTuple rightTuple = context.rightTuples.get( key );
+
+                    parentLeftTuple.setBlocker( rightTuple );
+                    rightTuple.addBlocked( parentLeftTuple );
+
+                    while ( stream.readShort() == PersisterEnums.LEFT_TUPLE ) {
+                        LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                        LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                                  childSink,
+                                                                  true );
+                        readLeftTuple( childLeftTuple,
+                                       context );
+                    }
+                }
+                break;
+            }
+            case NodeTypeEnums.AccumulateNode : {
+                // accumulate nodes generate new facts on-demand and need special procedures when de-serializing from persistent storage
+                AccumulateMemory memory = (AccumulateMemory) context.wm.getNodeMemory( (BetaNode) sink );
+                memory.betaMemory.getLeftTupleMemory().add( parentLeftTuple );
+
+                AccumulateContext accctx = new AccumulateContext();
+                memory.betaMemory.getCreatedHandles().put( parentLeftTuple,
+                                                           accctx,
+                                                           false );
+                // first we de-serialize the generated fact handle
+                InternalFactHandle handle = readFactHandle( context );
+                accctx.result = new RightTuple( handle,
+                                                (RightTupleSink) sink );
+
+                // then we de-serialize the associated accumulation context
+                accctx.context = (Serializable) stream.readObject();
+                // then we de-serialize the boolean propagated flag
+                accctx.propagated = stream.readBoolean();
+
+                // then we de-serialize all the propagated tuples
+                short head = -1;
+                while ( (head = stream.readShort()) != PersisterEnums.END ) {
+                    switch ( head ) {
+                        case PersisterEnums.RIGHT_TUPLE : {
+                            int factHandleId = stream.readInt();
+                            RightTupleKey key = new RightTupleKey( factHandleId,
+                                                                   sink );
+                            RightTuple rightTuple = context.rightTuples.get( key );
+                            // just wiring up the match record
+                            new LeftTuple( parentLeftTuple,
+                                           rightTuple,
+                                           sink,
+                                           true );
+
+                            break;
+                        }
+                        case PersisterEnums.LEFT_TUPLE : {
+                            LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                            LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                                      accctx.result,
+                                                                      childSink,
+                                                                      true );
+                            readLeftTuple( childLeftTuple,
+                                           context );
+                            break;
+                        }
+                        default : {
+                            throw new RuntimeDroolsException( "Marshalling error. This is a bug. Please contact the development team." );
+                        }
+                    }
+                }
+                break;
+            }
+            case NodeTypeEnums.RightInputAdaterNode : {
+                // RIANs generate new fact handles on-demand to wrap tuples and need special procedures when de-serializing from persistent storage
+                ObjectHashMap memory = (ObjectHashMap) context.wm.getNodeMemory( (NodeMemory) sink );
+                // create fact handle
+                int id = stream.readInt();
+                long recency = stream.readLong();
+                InternalFactHandle handle = new DefaultFactHandle( id,
+                                                                   parentLeftTuple,
+                                                                   recency,
+                                                                   context.wm.getEntryPoints().get( EntryPoint.DEFAULT.getEntryPointId() ) );
+                memory.put( parentLeftTuple, handle );
+                
+                readRightTuples( handle, context );
+                
+                stream.readShort(); // Persistence.END
+                break;
+            }
+            case NodeTypeEnums.FromNode: {
+//              context.out.println( "FromNode" );
+                // FNs generate new fact handles on-demand to wrap objects and need special procedures when serializing to persistent storage
+                FromMemory memory = (FromMemory) context.wm.getNodeMemory( (NodeMemory) sink );
+                
+                memory.betaMemory.getLeftTupleMemory().add( parentLeftTuple );                
+                Map<Object, RightTuple> matches =  new LinkedHashMap<Object, RightTuple>();
+                memory.betaMemory.getCreatedHandles().put( parentLeftTuple, matches );
+                
+                while( stream.readShort() == PersisterEnums.FACT_HANDLE ) {
+                    // we de-serialize the generated fact handle ID
+                    InternalFactHandle handle = readFactHandle( context );
+                    context.handles.put( handle.getId(),
+                                         handle );
+                    readRightTuples( handle, 
+                                     context );
+                    matches.put( handle.getObject(), handle.getFirstRightTuple() );
+                }
+                while( stream.readShort() == PersisterEnums.RIGHT_TUPLE ) {
+                    LeftTupleSink childSink = (LeftTupleSink) sinks.get( stream.readInt() );
+                    int factHandleId = stream.readInt();
+                    RightTupleKey key = new RightTupleKey( factHandleId,
+                                                           null ); // created tuples in from node always use null sink
+                    RightTuple rightTuple = context.rightTuples.get( key );
+                    LeftTuple childLeftTuple = new LeftTuple( parentLeftTuple,
+                                                              rightTuple,
+                                                              childSink,
+                                                              true );
+                    readLeftTuple( childLeftTuple,
+                                   context );
+                }
+//                context.out.println( "FromNode   ---   END" );
+                break;
+            }
+            case NodeTypeEnums.RuleTerminalNode : {
+                int pos = context.terminalTupleMap.size();
+                context.terminalTupleMap.put( pos,
+                                              parentLeftTuple );
+                break;
+            }
+        }
+    }
+
+    private static void addToLeftMemory(LeftTuple parentLeftTuple,
+            BetaMemory memory) {
+        memory.getLeftTupleMemory().add( parentLeftTuple );
+        memory.linkRight();
+    }
+
+    public static void readActivations(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+
+        while ( stream.readShort() == PersisterEnums.ACTIVATION ) {
+            readActivation( context );
+        }
+    }
+
+    public static Activation readActivation(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+        InternalRuleBase ruleBase = context.ruleBase;
+        InternalWorkingMemory wm = context.wm;
+
+        long activationNumber = stream.readLong();
+
+        int pos = stream.readInt();
+        LeftTuple leftTuple = context.terminalTupleMap.get( pos );
+
+        int salience = stream.readInt();
+
+        //PropagationContext context,
+        String pkgName = stream.readUTF();
+        String ruleName = stream.readUTF();
+        Package pkg = ruleBase.getPackage( pkgName );
+        Rule rule = pkg.getRule( ruleName );
+
+        RuleTerminalNode ruleTerminalNode = (RuleTerminalNode) leftTuple.getLeftTupleSink();
+        GroupElement subRule = ruleTerminalNode.getSubRule();
+
+        PropagationContext pc = context.propagationContexts.get( stream.readLong() );
+
+        AgendaItem activation = new AgendaItem( activationNumber,
+                                                leftTuple,
+                                                salience,
+                                                pc,
+                                                rule,
+                                                subRule );
+
+        leftTuple.setObject( activation );
+
+        if ( stream.readBoolean() ) {
+            String activationGroupName = stream.readUTF();
+            ((DefaultAgenda) wm.getAgenda()).getActivationGroup( activationGroupName ).addActivation( activation );
+        }
+
+        boolean activated = stream.readBoolean();
+        activation.setActivated( activated );
+
+        InternalAgendaGroup agendaGroup;
+        if ( rule.getAgendaGroup() == null || rule.getAgendaGroup().equals( "" ) || rule.getAgendaGroup().equals( AgendaGroup.MAIN ) ) {
+            // Is the Rule AgendaGroup undefined? If it is use MAIN,
+            // which is added to the Agenda by default
+            agendaGroup = (InternalAgendaGroup) ((DefaultAgenda) wm.getAgenda()).getAgendaGroup( AgendaGroup.MAIN );
+        } else {
+            // AgendaGroup is defined, so try and get the AgendaGroup
+            // from the Agenda
+            agendaGroup = (InternalAgendaGroup) ((DefaultAgenda) wm.getAgenda()).getAgendaGroup( rule.getAgendaGroup() );
+        }
+
+        activation.setAgendaGroup( agendaGroup );
+
+        if ( activated ) {
+            if ( rule.getRuleFlowGroup() == null ) {
+                agendaGroup.add( activation );
+            } else {
+                InternalRuleFlowGroup rfg = (InternalRuleFlowGroup) ((DefaultAgenda) wm.getAgenda()).getRuleFlowGroup( rule.getRuleFlowGroup() );
+                rfg.addActivation( activation );
+            }
+        }
+
+        TruthMaintenanceSystem tms = context.wm.getTruthMaintenanceSystem();
+        while ( stream.readShort() == PersisterEnums.LOGICAL_DEPENDENCY ) {
+            int factHandleId = stream.readInt();
+            InternalFactHandle handle = (InternalFactHandle) context.handles.get( factHandleId );
+            tms.addLogicalDependency( handle,
+                                      activation,
+                                      pc,
+                                      rule );
+        }
+
+        return activation;
+    }
+
+    public static void readPropagationContexts(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+
+        while ( stream.readShort() == PersisterEnums.PROPAGATION_CONTEXT ) {
+            readPropagationContext( context );
+        }
+
+    }
+
+    public static void readPropagationContext(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+        InternalRuleBase ruleBase = context.ruleBase;
+
+        int type = stream.readInt();
+
+        Rule rule = null;
+        if ( stream.readBoolean() ) {
+            String pkgName = stream.readUTF();
+            String ruleName = stream.readUTF();
+            Package pkg = ruleBase.getPackage( pkgName );
+            rule = pkg.getRule( ruleName );
+        }
+
+        LeftTuple leftTuple = null;
+        if ( stream.readBoolean() ) {
+            int tuplePos = stream.readInt();
+            leftTuple = (LeftTuple) context.terminalTupleMap.get( tuplePos );
+        }
+
+        long propagationNumber = stream.readLong();
+
+        int factHandleId = stream.readInt();
+        InternalFactHandle factHandle = context.handles.get( factHandleId );
+
+        int activeActivations = stream.readInt();
+        int dormantActivations = stream.readInt();
+        String entryPointId = stream.readUTF();
+
+        EntryPoint entryPoint = context.entryPoints.get( entryPointId );
+        if ( entryPoint == null ) {
+            entryPoint = new EntryPoint( entryPointId );
+            context.entryPoints.put( entryPointId,
+                                     entryPoint );
+        }
+
+        PropagationContext pc = new PropagationContextImpl( propagationNumber,
+                                                            type,
+                                                            rule,
+                                                            leftTuple,
+                                                            factHandle,
+                                                            activeActivations,
+                                                            dormantActivations,
+                                                            entryPoint );
+        context.propagationContexts.put( propagationNumber,
+                                         pc );
+    }
+
+    public static WorkItem readWorkItem(MarshallerReaderContext context) throws IOException {
+        ObjectInputStream stream = context.stream;
+
+        WorkItemImpl workItem = new WorkItemImpl();
+        workItem.setId( stream.readLong() );
+        workItem.setProcessInstanceId( stream.readLong() );
+        workItem.setName( stream.readUTF() );
+        workItem.setState( stream.readInt() );
+
+        //WorkItem Paramaters
+        int nbVariables = stream.readInt();
+            if (nbVariables > 0) {
+                
+                for (int i = 0; i < nbVariables; i++) {
+                    String name = stream.readUTF();
+                    try {
+                        int index = stream.readInt();
+                        ObjectMarshallingStrategy strategy = context.resolverStrategyFactory.getStrategy(index);
+                        
+                        Object value = strategy.read(stream);
+                        workItem.setParameter(name, value);
+                    } catch (ClassNotFoundException e) {
+                        throw new IllegalArgumentException(
+                                "Could not reload variable " + name);
+                    }
+                }
+            }
+        
+      
+        
+
+        return workItem;
+    }
+
+   
+
+}

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/AbstractObjectSinkAdapter.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/AbstractObjectSinkAdapter.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/AbstractObjectSinkAdapter.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -21,6 +21,8 @@
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 
+import org.drools.common.InternalWorkingMemory;
+import org.drools.common.NodeMemory;
 import org.drools.common.RuleBasePartitionId;
 
 /**
@@ -60,4 +62,5 @@
     public void setPartitionId( RuleBasePartitionId partitionId ) {
         this.partitionId = partitionId;
     }
+   
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaMemory.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaMemory.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaMemory.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -24,36 +24,39 @@
 import org.drools.core.util.ObjectHashMap;
 import org.drools.rule.ContextEntry;
 
-public class BetaMemory
-    implements
-    Externalizable {
+public class BetaMemory implements Externalizable, Unlinkable {
 
     private static final long serialVersionUID = 510l;
 
-    private LeftTupleMemory   leftTupleMemory;
-    private RightTupleMemory  rightTupleMemory;
-    private ObjectHashMap     createdHandles;
-    private ContextEntry[]    context;
-    private Object            behaviorContext;
+    private LeftTupleMemory leftTupleMemory;
+    private RightTupleMemory rightTupleMemory;
+    private ObjectHashMap createdHandles;
+    private ContextEntry[] context;
+    private Object behaviorContext;
+    
+    /* Let's start with only left unlinked. */
+    private boolean isLeftUnlinked = false;
+    private boolean isRightUnlinked = true;
 
     public BetaMemory() {
     }
 
     public BetaMemory(final LeftTupleMemory tupleMemory,
-                      final RightTupleMemory objectMemory,
-                      final ContextEntry[] context) {
+            final RightTupleMemory objectMemory, final ContextEntry[] context) {
         this.leftTupleMemory = tupleMemory;
         this.rightTupleMemory = objectMemory;
         this.context = context;
     }
 
     public void readExternal(ObjectInput in) throws IOException,
-                                            ClassNotFoundException {
+            ClassNotFoundException {
         leftTupleMemory = (LeftTupleMemory) in.readObject();
         rightTupleMemory = (RightTupleMemory) in.readObject();
         createdHandles = (ObjectHashMap) in.readObject();
         context = (ContextEntry[]) in.readObject();
         behaviorContext = (Object) in.readObject();
+        isLeftUnlinked = in.readBoolean();
+        isRightUnlinked = in.readBoolean();
     }
 
     public void writeExternal(ObjectOutput out) throws IOException {
@@ -62,6 +65,8 @@
         out.writeObject( createdHandles );
         out.writeObject( context );
         out.writeObject( behaviorContext );
+        out.writeBoolean( isLeftUnlinked );
+        out.writeBoolean( isRightUnlinked );
     }
 
     public RightTupleMemory getRightTupleMemory() {
@@ -73,7 +78,7 @@
     }
 
     public ObjectHashMap getCreatedHandles() {
-        if ( this.createdHandles == null ) {
+        if (this.createdHandles == null) {
             this.createdHandles = new ObjectHashMap();
         }
         return this.createdHandles;
@@ -93,4 +98,29 @@
     public void setBehaviorContext(Object behaviorContext) {
         this.behaviorContext = behaviorContext;
     }
+
+    public boolean isLeftUnlinked() {
+        return this.isLeftUnlinked;
+    }
+
+    public boolean isRightUnlinked() {
+        return this.isRightUnlinked;
+    }
+
+    public void linkLeft() {
+        this.isLeftUnlinked = false;
+    }
+
+    public void linkRight() {
+        this.isRightUnlinked = false;
+    }
+
+    public void unlinkLeft() {
+        this.isLeftUnlinked = true;
+    }
+
+    public void unlinkRight() {
+        this.isRightUnlinked = true;
+    }
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaNode.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaNode.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/BetaNode.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -23,6 +23,7 @@
 import java.util.List;
 
 import org.drools.RuleBaseConfiguration;
+import org.drools.builder.conf.LRUnlinkingOption;
 import org.drools.common.BaseNode;
 import org.drools.common.BetaConstraints;
 import org.drools.common.InternalFactHandle;
@@ -81,6 +82,9 @@
     protected boolean         objectMemory               = true; // hard coded to true
     protected boolean         tupleMemoryEnabled;
     protected boolean         concurrentRightTupleMemory = false;
+    
+    /** @see LRUnlinkingOption */
+    protected boolean         lrUnlinkingEnabled         = false;
 
     // ------------------------------------------------------------
     // Constructors
@@ -113,7 +117,7 @@
         this.behavior = new BehaviorManager( behaviors );
 
         if ( this.constraints == null ) {
-            throw new RuntimeException( "cannot have null constraints, must at least be an instance of EmptyBetaConstraints" );
+            throw new IllegalStateException( "cannot have null constraints, must at least be an instance of EmptyBetaConstraints" );
         }
     }
 
@@ -130,6 +134,7 @@
         objectMemory = in.readBoolean();
         tupleMemoryEnabled = in.readBoolean();
         concurrentRightTupleMemory = in.readBoolean();
+        lrUnlinkingEnabled = in.readBoolean();
 
         super.readExternal( in );
     }
@@ -146,6 +151,7 @@
         out.writeBoolean( objectMemory );
         out.writeBoolean( tupleMemoryEnabled );
         out.writeBoolean( concurrentRightTupleMemory );
+        out.writeBoolean( lrUnlinkingEnabled );
 
         super.writeExternal( out );
     }
@@ -211,9 +217,19 @@
                                                                                       null,
                                                                                       null,
                                                                                       null );
-            this.rightInput.updateSink( this,
-                                        propagationContext,
-                                        workingMemory );
+
+            /* When L&R Unlinking is enabled, we only need to update the side
+             * that is initially linked. If there are tuples to be propagated,
+             * they will trigger the update (thus, population) of the other side.
+             * */
+            if (!lrUnlinkingEnabled || 
+                    (lrUnlinkingEnabled && !(this instanceof JoinNode) )) {
+
+                this.rightInput.updateSink( this,
+                                            propagationContext,
+                                            workingMemory );
+            }
+            
             this.leftInput.updateSink( this,
                                        propagationContext,
                                        workingMemory );
@@ -300,6 +316,7 @@
                              ModifyPreviousTuples modifyPreviousTuples,
                              PropagationContext context,
                              InternalWorkingMemory workingMemory) {
+        
         RightTuple rightTuple = modifyPreviousTuples.removeRightTuple( this );
         if ( rightTuple != null ) {
             rightTuple.reAdd();
@@ -319,6 +336,7 @@
                                 ModifyPreviousTuples modifyPreviousTuples,
                                 PropagationContext context,
                                 InternalWorkingMemory workingMemory) {
+        
         LeftTuple leftTuple = modifyPreviousTuples.removeLeftTuple( this );
         if ( leftTuple != null ) {
             leftTuple.reAdd(); //
@@ -485,4 +503,39 @@
         }
     }
 
+    protected boolean leftUnlinked(final PropagationContext context, 
+            final InternalWorkingMemory workingMemory, final BetaMemory memory) {
+        
+        // If left input is unlinked, don't do anything.
+        if(memory.isLeftUnlinked()) {
+            return true;
+        }
+        
+        if (memory.isRightUnlinked()) {
+            memory.linkRight();
+            // updates the right input memory before going on.
+            this.rightInput.updateSink(this, context, workingMemory);
+        }
+        
+        return false;
+    }
+    
+    protected boolean rightUnlinked(final PropagationContext context,
+            final InternalWorkingMemory workingMemory, final BetaMemory memory) {
+        
+        if (memory.isRightUnlinked()) {
+            return true;
+        }
+        
+        if (memory.isLeftUnlinked()) {
+            
+            memory.linkLeft();
+            // updates the left input memory before going on.
+            this.leftInput.updateSink(this, context, workingMemory);
+        }
+        
+        return false;
+    }
+    
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/CompositeObjectSinkAdapter.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -338,6 +338,7 @@
     public void propagateAssertObject(final InternalFactHandle factHandle,
                                       final PropagationContext context,
                                       final InternalWorkingMemory workingMemory) {
+        
         final Object object = factHandle.getObject();
 
         // Iterates the FieldIndex collection, which tells you if particularly field is hashed or not
@@ -447,6 +448,7 @@
                                            PropagationContext context,
                                            InternalWorkingMemory workingMemory,
                                            ObjectSink sink) {
+        
         sink.assertObject( factHandle,
                            context,
                            workingMemory );
@@ -520,7 +522,10 @@
     public int size() {
         return (this.otherSinks != null ? this.otherSinks.size() : 0) + (this.hashableSinks != null ? this.hashableSinks.size() : 0) + (this.hashedSinkMap != null ? this.hashedSinkMap.size() : 0);
     }
+    
+    
 
+    
     public static class HashKey
         implements
         Externalizable {

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/EmptyObjectSinkAdapter.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/EmptyObjectSinkAdapter.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/EmptyObjectSinkAdapter.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -95,5 +95,12 @@
     }
 
 
+    public boolean shouldPropagate(InternalWorkingMemory workingMemory) {
+        return true;
+    }
+    
+    
 
+
+
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/JoinNode.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/JoinNode.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/JoinNode.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -53,14 +53,26 @@
                binder,
                behaviors );
         this.tupleMemoryEnabled = context.isTupleMemoryEnabled();
+        this.lrUnlinkingEnabled = context.getRuleBase().getConfiguration().isLRUnlinkingEnabled();
     }
 
     public void assertLeftTuple(final LeftTuple leftTuple,
                                 final PropagationContext context,
                                 final InternalWorkingMemory workingMemory) {
+        
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
 
+
+        if(lrUnlinkingEnabled &&
+           leftUnlinked(context,
+                        workingMemory,
+                        memory)) {
+            return;
+        }
+
+        
         boolean useLeftMemory = true;
+        
         if ( this.tupleMemoryEnabled ) {
             memory.getLeftTupleMemory().add( leftTuple );
         } else {
@@ -98,8 +110,18 @@
     public void assertObject(final InternalFactHandle factHandle,
                              final PropagationContext context,
                              final InternalWorkingMemory workingMemory) {
+        
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
 
+        if (lrUnlinkingEnabled &&
+            rightUnlinked(context,
+                          workingMemory,
+                          memory)) {
+            context.setLatestPropagationAttempt();
+            return;
+        }
+        
+
         RightTuple rightTuple = createRightTuple( factHandle,
                                                   this );
 
@@ -121,6 +143,8 @@
                                                workingMemory,
                                                factHandle );
         int i = 0;
+        
+        // TODO lgomes: Do I really need to propagate when I know that the sink is an EmptyLeftTupleSinkAdapter
         for ( LeftTuple leftTuple = memory.getLeftTupleMemory().getFirst( rightTuple ); leftTuple != null; leftTuple = (LeftTuple) leftTuple.getNext() ) {
             if ( this.constraints.isAllowedCachedRight( memory.getContext(),
                                                         leftTuple ) ) {
@@ -138,14 +162,25 @@
         this.constraints.resetFactHandle( memory.getContext() );
     }
 
+
     public void retractRightTuple(final RightTuple rightTuple,
                                   final PropagationContext context,
                                   final InternalWorkingMemory workingMemory) {
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
+        
+        if (lrUnlinkingEnabled &&
+            memory.isRightUnlinked()) {
+            return;
+        }
+        
         behavior.retractRightTuple( memory.getBehaviorContext(),
-                                    rightTuple,
-                                    workingMemory );
+                rightTuple,
+                workingMemory );
+        
         memory.getRightTupleMemory().remove( rightTuple );
+        
+        // Check if memory should be unlinked
+        //checkLeftUnlinking(memory);
 
         if ( rightTuple.firstChild != null ) {
             this.sink.propagateRetractRightTuple( rightTuple,
@@ -154,11 +189,24 @@
         }
     }
 
+
     public void retractLeftTuple(final LeftTuple leftTuple,
                                  final PropagationContext context,
                                  final InternalWorkingMemory workingMemory) {
+        
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
+        
+        if (lrUnlinkingEnabled &&
+            memory.isLeftUnlinked()) {
+            return;
+        }
+        
         memory.getLeftTupleMemory().remove( leftTuple );
+        
+        // Check if right side should be unlinked.
+        //checkRightUnlinking(memory);
+        
+        
         if ( leftTuple.firstChild != null ) {
             this.sink.propagateRetractLeftTuple( leftTuple,
                                                  context,
@@ -170,7 +218,7 @@
                                  final PropagationContext context,
                                  final InternalWorkingMemory workingMemory) {
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
-
+        
         // WTD here
         //                if ( !behavior.assertRightTuple( memory.getBehaviorContext(),
         //                                                 rightTuple,
@@ -268,8 +316,9 @@
     public void modifyLeftTuple(final LeftTuple leftTuple,
                                 final PropagationContext context,
                                 final InternalWorkingMemory workingMemory) {
+        
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
-
+        
         // Add and remove to make sure we are in the right bucket and at the end
         // this is needed to fix for indexing and deterministic iteration
         memory.getLeftTupleMemory().remove( leftTuple );
@@ -359,7 +408,7 @@
     public void updateSink(final LeftTupleSink sink,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
-
+        
         final BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory( this );
 
         final Iterator tupleIter = memory.getLeftTupleMemory().iterator();
@@ -369,8 +418,10 @@
                                               leftTuple );
             for ( RightTuple rightTuple = memory.getRightTupleMemory().getFirst( leftTuple,
                                                                                  (InternalFactHandle) context.getFactHandle() ); rightTuple != null; rightTuple = (RightTuple) rightTuple.getNext() ) {
-                if ( this.constraints.isAllowedCachedLeft( memory.getContext(),
+            	
+                if (this.constraints.isAllowedCachedLeft( memory.getContext(),
                                                            rightTuple.getFactHandle() ) ) {
+                	
                     sink.assertLeftTuple( new LeftTuple( leftTuple,
                                                          rightTuple,
                                                          null,
@@ -385,7 +436,7 @@
             this.constraints.resetTuple( memory.getContext() );
         }
     }
-
+    
     public short getType() {
         return NodeTypeEnums.JoinNode;
     }
@@ -398,4 +449,35 @@
 
         return "[JoinNode(" + this.getId() + ") - " + ((ObjectTypeNode) source).getObjectType() + "]";
     }
+
+    @Override
+    public void modifyLeftTuple(InternalFactHandle factHandle,
+            ModifyPreviousTuples modifyPreviousTuples,
+            PropagationContext context, InternalWorkingMemory workingMemory) {
+
+        BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory(this);
+        
+        if (lrUnlinkingEnabled &&
+            memory.isLeftUnlinked())
+            return;
+        
+        super.modifyLeftTuple(factHandle, modifyPreviousTuples, context, workingMemory);
+    }
+
+    @Override
+    public void modifyObject(InternalFactHandle factHandle,
+            ModifyPreviousTuples modifyPreviousTuples,
+            PropagationContext context, InternalWorkingMemory workingMemory) {
+        
+
+        BetaMemory memory = (BetaMemory) workingMemory.getNodeMemory(this);
+        
+        if (lrUnlinkingEnabled &&
+            memory.isRightUnlinked())
+            return;
+        
+        super.modifyObject(factHandle, modifyPreviousTuples, context, workingMemory);
+    }
+    
+    
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTuple.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTuple.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTuple.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -18,12 +18,10 @@
 
 import java.util.Arrays;
 
-import org.drools.common.AgendaItem;
 import org.drools.common.InternalFactHandle;
 import org.drools.core.util.Entry;
 import org.drools.core.util.LeftTupleList;
 import org.drools.rule.Declaration;
-import org.drools.spi.Activation;
 import org.drools.spi.Tuple;
 
 public class LeftTuple

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTupleMemory.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTupleMemory.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/LeftTupleMemory.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -39,5 +39,7 @@
     //    public Entry[] getTable();
 
     public Entry[] toArray();
+    
+    public void clear();
 
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectSinkPropagator.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectSinkPropagator.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectSinkPropagator.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -44,5 +44,5 @@
                                       ModifyPreviousTuples modifyPreviousTuples,
                                       PropagationContext context,
                                       InternalWorkingMemory workingMemory);
-
+    
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -24,6 +24,7 @@
 import org.drools.RuleBaseConfiguration;
 import org.drools.base.ClassObjectType;
 import org.drools.base.ValueType;
+import org.drools.builder.conf.LRUnlinkingOption;
 import org.drools.common.AbstractRuleBase;
 import org.drools.common.BaseNode;
 import org.drools.common.DroolsObjectInputStream;
@@ -97,7 +98,15 @@
     private transient ExpireJob job              = new ExpireJob();
 
     private CompiledNetwork     compiledNetwork;
-
+    
+    /** When L&R Unlinking is active, updateSink() may be recursively 
+     *  called during propagation and we need to keep track of the 
+     *  fact that we are propagating. */
+    private boolean isPropagating = false;
+    
+    /** @see LRUnlinkingOption */
+    private boolean lrUnlinkingEnabled = false;
+    
     public ObjectTypeNode() {
 
     }
@@ -119,6 +128,7 @@
                source,
                context.getRuleBase().getConfiguration().getAlphaNodeHashingThreshold() );
         this.objectType = objectType;
+        this.lrUnlinkingEnabled = context.getRuleBase().getConfiguration().isLRUnlinkingEnabled();
         setObjectMemoryEnabled( context.isObjectTypeNodeMemoryEnabled() );
     }
 
@@ -136,6 +146,7 @@
         skipOnModify = in.readBoolean();
         objectMemoryEnabled = in.readBoolean();
         expirationOffset = in.readLong();
+        lrUnlinkingEnabled = in.readBoolean();
     }
 
     public void writeExternal(ObjectOutput out) throws IOException {
@@ -144,6 +155,7 @@
         out.writeBoolean( skipOnModify );
         out.writeBoolean( objectMemoryEnabled );
         out.writeLong( expirationOffset );
+        out.writeBoolean( lrUnlinkingEnabled );
     }
 
     /**
@@ -177,6 +189,7 @@
     public void assertObject(final InternalFactHandle factHandle,
                              final PropagationContext context,
                              final InternalWorkingMemory workingMemory) {
+        
         if ( this.objectMemoryEnabled ) {
             final ObjectHashSet memory = (ObjectHashSet) workingMemory.getNodeMemory( this );
             memory.add( factHandle,
@@ -187,9 +200,10 @@
                                           context,
                                           workingMemory );
         } else {
-            this.sink.propagateAssertObject( factHandle,
-                                             context,
-                                             workingMemory );
+            
+            doPropagateAssertObject( factHandle, 
+                                     context, 
+                                     workingMemory);
         }
 
         if ( this.expirationOffset >= 0 && this.expirationOffset != Long.MAX_VALUE ) {
@@ -212,6 +226,18 @@
 
     }
 
+    private void doPropagateAssertObject(final InternalFactHandle factHandle,
+            final PropagationContext context,
+            final InternalWorkingMemory workingMemory) {
+        
+        isPropagating = true;
+        
+        this.sink.propagateAssertObject( factHandle,
+                                         context,
+                                         workingMemory );
+        isPropagating = false;
+    }
+
     /**
      * Retract the <code>FactHandleimpl</code> from the <code>Rete</code> network. Also remove the
      * <code>FactHandleImpl</code> from the node memory.
@@ -270,15 +296,68 @@
     public void updateSink(final ObjectSink sink,
                            final PropagationContext context,
                            final InternalWorkingMemory workingMemory) {
+
+        if (lrUnlinkingEnabled) {
+            // Update sink taking into account L&R unlinking peculiarities
+            updateLRUnlinking(sink, context, workingMemory);
+            
+        } else {
+            // Regular updateSink
+            final ObjectHashSet memory = (ObjectHashSet) workingMemory.getNodeMemory( this );
+            Iterator it = memory.iterator();
+    
+            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                sink.assertObject( (InternalFactHandle) entry.getValue(),
+                        context,
+                        workingMemory );
+            }
+        }
+
+    }
+
+    /**
+     *  When L&R Unlinking is enabled, updateSink() is used to populate 
+     *  a node's memory, but it has to take into account if it's propagating.
+     */
+    private void updateLRUnlinking(final ObjectSink sink,
+            final PropagationContext context,
+            final InternalWorkingMemory workingMemory) {
+        
         final ObjectHashSet memory = (ObjectHashSet) workingMemory.getNodeMemory( this );
+        
         Iterator it = memory.iterator();
-        for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
-            sink.assertObject( (InternalFactHandle) entry.getValue(),
-                               context,
-                               workingMemory );
+            
+        
+        InternalFactHandle ctxHandle = (InternalFactHandle)context.getFactHandle(); 
+        
+        if (!isPropagating || 
+                (isPropagating && context.getLatestPropagationAttempt() == ctxHandle.getId())){
+            
+            context.resetLatestPropagationAttempt();
+            
+            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                // Assert everything
+                sink.assertObject( (InternalFactHandle) entry.getValue(),
+                        context,
+                        workingMemory );
+            }
+            
+        } else {
+            
+            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                InternalFactHandle handle = (InternalFactHandle) entry.getValue();
+                // Exclude the current fact propagation
+                if (handle.getId() != ctxHandle.getId()) {
+                    sink.assertObject( handle,
+                            context,
+                            workingMemory );
+                }
+            }
         }
     }
+    
 
+
     /**
      * Rete needs to know that this ObjectTypeNode has been added
      */

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/RightTupleMemory.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/RightTupleMemory.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/RightTupleMemory.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -38,4 +38,6 @@
     public Entry[] toArray();
 
     public int size();
+    
+    public void clear();
 }

Added: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/Unlinkable.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/Unlinkable.java	                        (rev 0)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/reteoo/Unlinkable.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -0,0 +1,39 @@
+/**
+ * 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.reteoo;
+
+/**
+ * A marker interface to specify unlinkable node memories.
+ * 
+ * @author lgomes
+ */
+public interface Unlinkable {
+
+	/** Whether or not the left side is unlinked. */
+	boolean isLeftUnlinked();
+
+	/** Whether or not the right side is unlinked. */
+	boolean isRightUnlinked();
+	
+	void linkLeft();
+	
+	void linkRight();
+	
+	void unlinkLeft();
+	
+	void unlinkRight();
+
+}

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/spi/PropagationContext.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/spi/PropagationContext.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/main/java/org/drools/spi/PropagationContext.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -59,5 +59,8 @@
 
     public EntryPoint getEntryPoint();
     
+    public int getLatestPropagationAttempt();
+    public void setLatestPropagationAttempt();
+    public void resetLatestPropagationAttempt();
 
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/RuleBaseConfigurationTest.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/RuleBaseConfigurationTest.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/RuleBaseConfigurationTest.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -113,4 +113,20 @@
         assertTrue( cfg.getAgendaGroupFactory() instanceof PriorityQueueAgendaGroupFactory );
     }
     
+    public void testLRUnlinking() {
+        RuleBaseConfiguration cfg = new RuleBaseConfiguration();
+        assertEquals( false,
+                      cfg.isLRUnlinkingEnabled() );
+
+        Properties properties = new Properties();
+        properties.setProperty( "drools.lrUnlinkingEnabled",
+                                "true" );
+        cfg = new RuleBaseConfiguration( properties );
+
+        assertEquals( true,
+                      cfg.isLRUnlinkingEnabled() );
+    }
+    
+
+    
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/MockObjectSource.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/MockObjectSource.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/MockObjectSource.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -78,6 +78,10 @@
     public void addFact(final InternalFactHandle handle) {
         this.facts.add( handle );
     }
+    
+    public void removeFact(final InternalFactHandle handle) {
+        this.facts.remove( handle );
+    }
 
     public void updateSink(final ObjectSink sink,
                            final PropagationContext context,

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -343,7 +343,7 @@
                       sink2.getAsserted().size() );
 
         objectTypeNode.updateSink( sink2,
-                                   null,
+                                   new PropagationContextImpl(),
                                    workingMemory );
 
         assertEquals( 2,

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/JUnitNodeTestRunner.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/JUnitNodeTestRunner.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/JUnitNodeTestRunner.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -20,8 +20,8 @@
 import java.util.List;
 
 import org.drools.RuntimeDroolsException;
+import org.drools.reteoo.test.dsl.NodeTestCase;
 import org.drools.reteoo.test.dsl.NodeTestDef;
-import org.drools.reteoo.test.dsl.NodeTestCase;
 import org.junit.runner.Description;
 import org.junit.runner.Runner;
 import org.junit.runner.notification.RunNotifier;
@@ -34,33 +34,40 @@
 public class JUnitNodeTestRunner extends Runner {
 
     // The description of the test suite
-    private Description        descr;
+    private Description descr;
     private List<NodeTestCase> testCases = new ArrayList<NodeTestCase>();
 
-    public JUnitNodeTestRunner(Class< ? > clazz) {
+    public JUnitNodeTestRunner(Class<?> clazz) {
         try {
-            NodeTestCasesSource ntsuite = (NodeTestCasesSource) clazz.newInstance();
+            NodeTestCasesSource ntsuite = (NodeTestCasesSource) clazz
+                    .newInstance();
             testCases = ntsuite.getTestCases();
-        } catch ( Exception e ) {
+        } catch (Exception e) {
             e.printStackTrace();
-            throw new RuntimeDroolsException( "Error instantiating node test runner: "+e, e );
+            throw new RuntimeDroolsException(
+                    "Error instantiating node test runner: " + e, e);
         }
-        this.descr = Description.createSuiteDescription( "Node test case suite" );
+        this.descr = Description.createSuiteDescription("Node test case suite");
 
-        for ( NodeTestCase tcase : testCases ) {
-            Description tcaseDescr = Description.createSuiteDescription( tcase.getName() );
-            tcase.setDescription( tcaseDescr );
-            this.descr.addChild( tcaseDescr );
-            for ( NodeTestDef ntest : tcase.getTests() ) {
-                Description ntestDescr = Description.createTestDescription( clazz,
-                                                                            ntest.getName() );
-                tcaseDescr.addChild( ntestDescr );
-                ntest.setDescription( ntestDescr );
+        for (NodeTestCase tcase : testCases) {
+            
+        	Description tcaseDescr = Description.createSuiteDescription(tcase
+                    .getFileName() != null ? tcase.getFileName() : tcase.getName());
+            
+            tcase.setDescription(tcaseDescr);
+            this.descr.addChild(tcaseDescr);
+            for (NodeTestDef ntest : tcase.getTests()) {
+                Description ntestDescr = Description.createTestDescription(
+                        clazz, ntest.getName());
+                tcaseDescr.addChild(ntestDescr);
+                ntest.setDescription(ntestDescr);
             }
         }
     }
 
-    /* (non-Javadoc)
+    /*
+     * (non-Javadoc)
+     * 
      * @see org.junit.runner.Runner#getDescription()
      */
     @Override
@@ -68,16 +75,18 @@
         return descr;
     }
 
-    /* (non-Javadoc)
-     * @see org.junit.runner.Runner#run(org.junit.runner.notification.RunNotifier)
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.junit.runner.Runner#run(org.junit.runner.notification.RunNotifier)
      */
     @Override
     public void run(RunNotifier notifier) {
+
         ReteDslTestEngine tester = new ReteDslTestEngine();
-        for ( NodeTestCase tcase : testCases ) {
-            tester.run( tcase,
-                        notifier );
+        for (NodeTestCase tcase : testCases) {
+            tester.run(tcase, notifier);
         }
     }
-
 }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/NodeSuiteTest.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/NodeSuiteTest.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/NodeSuiteTest.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -45,11 +45,12 @@
         for ( File file : base.listFiles( new FilenameFilter() {
             public boolean accept(File arg0,
                                   String arg1) {
-                return arg1.endsWith( ".nodeTestCase" );
+                return arg1.endsWith( NodeTestCase.SUFFIX );
             }
         } ) ) {
             InputStream is = new FileInputStream( file );
             NodeTestCase tcase = ReteDslTestEngine.compile( is );
+            tcase.setFileName(file.getName());
             if ( tcase.hasErrors() ) {
                 throw new IllegalArgumentException( "Error parsing and loading testcase: " + file.getAbsolutePath() + "\n" + tcase.getErrors().toString() );
             }

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngine.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngine.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngine.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -22,11 +22,14 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import junit.framework.AssertionFailedError;
+
 import org.antlr.runtime.ANTLRInputStream;
 import org.antlr.runtime.ANTLRReaderStream;
 import org.antlr.runtime.ANTLRStringStream;
@@ -63,6 +66,7 @@
 import org.drools.reteoo.test.dsl.AccumulateNodeStep;
 import org.drools.reteoo.test.dsl.BetaNodeStep;
 import org.drools.reteoo.test.dsl.BindingStep;
+import org.drools.reteoo.test.dsl.ConfigStep;
 import org.drools.reteoo.test.dsl.DSLMock;
 import org.drools.reteoo.test.dsl.DslStep;
 import org.drools.reteoo.test.dsl.EvalNodeStep;
@@ -97,22 +101,26 @@
 
 public class ReteDslTestEngine {
 
-    private static final String OBJECT_TYPE_NODE         = "ObjectTypeNode";
-    private static final String LEFT_INPUT_ADAPTER_NODE  = "LeftInputAdapterNode";
-    private static final String BINDING                  = "Binding";
-    private static final String JOIN_NODE                = "JoinNode";
-    private static final String NOT_NODE                 = "NotNode";
-    private static final String EXISTS_NODE              = "ExistsNode";
-    private static final String ACCUMULATE_NODE          = "AccumulateNode";
-    private static final String RULE_TERMINAL_NODE       = "RuleTerminalNode";
-    private static final String EVAL_NODE                = "EvalNode";
-    private static final String WITH                     = "With";
-    private static final String FACTS                    = "Facts";
+    public static final String WORKING_MEMORY = "WorkingMemory";
+    public static final String BUILD_CONTEXT = "BuildContext";
+    
+    private static final String CONFIG = "Config";
+    private static final String OBJECT_TYPE_NODE = "ObjectTypeNode";
+    private static final String LEFT_INPUT_ADAPTER_NODE = "LeftInputAdapterNode";
+    private static final String BINDING = "Binding";
+    private static final String JOIN_NODE = "JoinNode";
+    private static final String NOT_NODE = "NotNode";
+    private static final String EXISTS_NODE = "ExistsNode";
+    private static final String ACCUMULATE_NODE = "AccumulateNode";
+    private static final String RULE_TERMINAL_NODE = "RuleTerminalNode";
+    private static final String EVAL_NODE = "EvalNode";
+    private static final String WITH = "With";
+    private static final String FACTS = "Facts";
     private static final String RIGHT_INPUT_ADAPTER_NODE = "RightInputAdapterNode";
-    private static final String LEFT_TUPLE_SINK_STEP     = "LeftTupleSink";
-    private static final String BETA_NODE_STEP           = "BetaNodeStep";
+    private static final String LEFT_TUPLE_SINK_STEP = "LeftTupleSink";
+    private static final String BETA_NODE_STEP = "BetaNodeStep";
 
-    private ReteTesterHelper    reteTesterHelper;
+    private ReteTesterHelper reteTesterHelper;
     private Map<String, Object> steps;
 
     public ReteDslTestEngine() {
@@ -120,173 +128,160 @@
         this.reteTesterHelper = new ReteTesterHelper();
 
         this.steps = new HashMap<String, Object>();
-
-        this.steps.put( OBJECT_TYPE_NODE,
-                        new ObjectTypeNodeStep( this.reteTesterHelper ) );
-        this.steps.put( LEFT_INPUT_ADAPTER_NODE,
-                        new LeftInputAdapterNodeStep( this.reteTesterHelper ) );
-        this.steps.put( BINDING,
-                        new BindingStep( this.reteTesterHelper ) );
-        this.steps.put( JOIN_NODE,
-                        new JoinNodeStep( this.reteTesterHelper ) );
-        this.steps.put( NOT_NODE,
-                        new NotNodeStep( this.reteTesterHelper ) );
-        this.steps.put( EXISTS_NODE,
-                        new ExistsNodeStep( this.reteTesterHelper ) );
-        this.steps.put( ACCUMULATE_NODE,
-                        new AccumulateNodeStep( this.reteTesterHelper ) );
-        this.steps.put( RULE_TERMINAL_NODE,
-                        new RuleTerminalNodeStep( this.reteTesterHelper ) );
-        this.steps.put( EVAL_NODE,
-                        new EvalNodeStep( this.reteTesterHelper ) );
-        this.steps.put( RIGHT_INPUT_ADAPTER_NODE,
-                        new RIANodeStep( this.reteTesterHelper ) );
-        this.steps.put( FACTS,
-                        new FactsStep( this.reteTesterHelper ) );
-        this.steps.put( WITH,
-                        new WithStep( this.reteTesterHelper ) );
-        this.steps.put( LEFT_TUPLE_SINK_STEP,
-                        new LeftTupleSinkStep( this.reteTesterHelper ) );
-        this.steps.put( BETA_NODE_STEP,
-                        new BetaNodeStep( this.reteTesterHelper ) );
+        
+        this.steps.put(CONFIG, new ConfigStep());
+        this.steps.put(OBJECT_TYPE_NODE, new ObjectTypeNodeStep(
+                this.reteTesterHelper));
+        this.steps.put(LEFT_INPUT_ADAPTER_NODE, new LeftInputAdapterNodeStep(
+                this.reteTesterHelper));
+        this.steps.put(BINDING, new BindingStep(this.reteTesterHelper));
+        this.steps.put(JOIN_NODE, new JoinNodeStep(this.reteTesterHelper));
+        this.steps.put(NOT_NODE, new NotNodeStep(this.reteTesterHelper));
+        this.steps.put(EXISTS_NODE, new ExistsNodeStep(this.reteTesterHelper));
+        this.steps.put(ACCUMULATE_NODE, new AccumulateNodeStep(
+                this.reteTesterHelper));
+        this.steps.put(RULE_TERMINAL_NODE, new RuleTerminalNodeStep(
+                this.reteTesterHelper));
+        this.steps.put(EVAL_NODE, new EvalNodeStep(this.reteTesterHelper));
+        this.steps.put(RIGHT_INPUT_ADAPTER_NODE, new RIANodeStep(
+                this.reteTesterHelper));
+        this.steps.put(FACTS, new FactsStep(this.reteTesterHelper));
+        this.steps.put(WITH, new WithStep(this.reteTesterHelper));
+        this.steps.put(LEFT_TUPLE_SINK_STEP, new LeftTupleSinkStep(
+                this.reteTesterHelper));
+        this.steps.put(BETA_NODE_STEP, new BetaNodeStep(this.reteTesterHelper));
     }
 
-    public NodeTestCaseResult run(NodeTestCase testCase,
-                                  RunNotifier notifier) {
-        if ( testCase == null || testCase.hasErrors() ) {
-            throw new IllegalArgumentException( "Impossible to execute test case due to existing errors: " + testCase.getErrors() );
+    public NodeTestCaseResult run(NodeTestCase testCase, RunNotifier notifier) {
+        if (testCase == null || testCase.hasErrors()) {
+            throw new IllegalArgumentException(
+                    "Impossible to execute test case due to existing errors: "
+                            + testCase.getErrors());
         }
-        if ( notifier == null ) {
+        if (notifier == null) {
             notifier = EmptyNotifier.INSTANCE;
         }
-        this.reteTesterHelper.addImports( testCase.getImports() );
-        NodeTestCaseResult result = new NodeTestCaseResult( testCase );
-        for ( NodeTestDef test : testCase.getTests() ) {
-            notifier.fireTestStarted( test.getDescription() );
-            NodeTestResult testResult = run( testCase,
-                                             test );
-            switch ( testResult.result ) {
-                case SUCCESS :
-                    notifier.fireTestFinished( test.getDescription() );
+        this.reteTesterHelper.addImports(testCase.getImports());
+        NodeTestCaseResult result = new NodeTestCaseResult(testCase);
+        
+        for (NodeTestDef test : testCase.getTests()) {
+            
+            notifier.fireTestStarted(test.getDescription());
+            NodeTestResult testResult = createTestResult(test, null);
+            
+            try {
+                
+                testResult = run(testCase, test);
+                
+                switch (testResult.result) {
+                case SUCCESS:
+                    notifier.fireTestFinished(test.getDescription());
                     break;
-                case ERROR :
-                case FAILURE :
-                    notifier.fireTestFailure( new Failure( test.getDescription(),
-                                                           new AssertionError( testResult.errorMsgs ) ) );
+                case ERROR:
+                case FAILURE:
+                    notifier.fireTestFailure(new Failure(test.getDescription(),
+                            new AssertionError(testResult.errorMsgs)));
                     break;
+                }
+
+                
+            } catch (Throwable e) {
+                notifier.fireTestFailure(new Failure(test.getDescription(), e));
             }
-            result.add( testResult );
+            
+            result.add(testResult);
         }
+        
         return result;
     }
 
-    private NodeTestResult run(NodeTestCase testCase,
-                               NodeTestDef test) {
-        Map<String, Object> context = createContext( testCase );
-        NodeTestResult result = new NodeTestResult( test,
-                                                    Result.NOT_EXECUTED,
-                                                    context,
-                                                    new LinkedList<String>() );
+    private NodeTestResult run(NodeTestCase testCase, NodeTestDef test) {
+        Map<String, Object> context = createContext(testCase);
+        NodeTestResult result = createTestResult(test, context);
+
         try {
             // run setup
-            run( context,
-                 testCase.getSetup(),
-                 result );
+            run(context, testCase.getSetup(), result);
             // run test
-            run( context,
-                 test.getSteps(),
-                 result );
+            run(context, test.getSteps(), result);
             // run tearDown
-            run( context,
-                 testCase.getTearDown(),
-                 result );
+            run(context, testCase.getTearDown(), result);
             result.result = Result.SUCCESS;
-        } catch ( Exception e ) {
+        } catch (Throwable e) {
             result.result = Result.ERROR;
-            result.errorMsgs.add( e.getMessage() );
+            result.errorMsgs.add(e.toString());
         }
         return result;
     }
 
+    private NodeTestResult createTestResult(NodeTestDef test,
+            Map<String, Object> context) {
+        NodeTestResult result = new NodeTestResult(test, Result.NOT_EXECUTED,
+                context, new LinkedList<String>());
+        return result;
+    }
+
     private Map<String, Object> createContext(NodeTestCase testCase) {
         Map<String, Object> context = new HashMap<String, Object>();
-        context.put( "TestCase",
-                     testCase );
+        context.put("TestCase", testCase);
 
         RuleBaseConfiguration conf = new RuleBaseConfiguration();
 
-        ReteooRuleBase rbase = new ReteooRuleBase( "ID",
-                                                   conf );
-        BuildContext buildContext = new BuildContext( rbase,
-                                                      rbase.getReteooBuilder().getIdGenerator() );
-        context.put( "BuildContext",
-                     buildContext );
-        context.put( "ClassFieldAccessorStore",
-                     this.reteTesterHelper.getStore() );
+        ReteooRuleBase rbase = new ReteooRuleBase("ID", conf);
+        BuildContext buildContext = new BuildContext(rbase, rbase
+                .getReteooBuilder().getIdGenerator());
+        context.put(BUILD_CONTEXT, buildContext);
+        context
+                .put("ClassFieldAccessorStore", this.reteTesterHelper
+                        .getStore());
 
-        InternalWorkingMemory wm = (InternalWorkingMemory) rbase.newStatefulSession( true );
-        context.put( "WorkingMemory",
-                     wm );
+        InternalWorkingMemory wm = (InternalWorkingMemory) rbase
+                .newStatefulSession(true);
+        context.put(WORKING_MEMORY, wm);
         return context;
     }
 
     public Map<String, Object> run(Map<String, Object> context,
-                                   List<DslStep> steps,
-                                   NodeTestResult result) {
-        InternalWorkingMemory wm = (InternalWorkingMemory) context.get( "WorkingMemory" );
-        for ( DslStep step : steps ) {
+            List<DslStep> steps, NodeTestResult result) {
+        InternalWorkingMemory wm = (InternalWorkingMemory) context
+                .get(WORKING_MEMORY);
+        for (DslStep step : steps) {
             String name = step.getName();
-            Object object = this.steps.get( name );
-            if ( object != null && object instanceof Step ) {
+            Object object = this.steps.get(name);
+            if (object != null && object instanceof Step) {
                 Step stepImpl = (Step) object;
                 try {
-                    stepImpl.execute( context,
-                                      step.getCommands() );
-                } catch ( Exception e ) {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                        e );
+                    stepImpl.execute(context, step.getCommands());
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": unable to execute step " + step, e);
                 }
-            } else if ( "assert".equals( name.trim() ) ) {
-                assertObject( step,
-                              context,
-                              wm );
-            } else if ( "retract".equals( name.trim() ) ) {
-                retractObject( step,
-                               context,
-                               wm );
-            } else if ( "modify".equals( name.trim() ) ) {
-                modifyObject( step,
-                              context,
-                              wm );
+            } else if ("assert".equals(name.trim())) {
+                assertObject(step, context, wm);
+            } else if ("retract".equals(name.trim())) {
+                retractObject(step, context, wm);
+            } else if ("modify".equals(name.trim())) {
+                modifyObject(step, context, wm);
             } else {
-                Object node = context.get( name.trim() );
-                if ( node == null ) {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": step " + name + " does not exist" );
+                Object node = context.get(name.trim());
+                if (node == null) {
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": step " + name + " does not exist");
                 }
 
-                if ( node instanceof DSLMock ) {
+                if (node instanceof DSLMock) {
                     // it is a mock
-                    MockitoHelper.process( step,
-                                           (LeftTupleSink) node,
-                                           context,
-                                           wm );
-                } else if ( node instanceof BetaNode ) {
-                    betaNode( step,
-                              (BetaNode) node,
-                              context,
-                              wm );
-                } else if ( node instanceof RightInputAdapterNode ) {
-                    riaNode( step,
-                             (RightInputAdapterNode) node,
-                             context,
-                             wm );
-                } else if ( node instanceof RuleTerminalNode ) {
-                    ruleTerminalNode( step,
-                                      (RuleTerminalNode) node,
-                                      context,
-                                      wm );
+                    MockitoHelper.process(step, (LeftTupleSink) node, context,
+                            wm);
+                } else if (node instanceof BetaNode) {
+                    betaNode(step, (BetaNode) node, context, wm);
+                } else if (node instanceof RightInputAdapterNode) {
+                    riaNode(step, (RightInputAdapterNode) node, context, wm);
+                } else if (node instanceof RuleTerminalNode) {
+                    ruleTerminalNode(step, (RuleTerminalNode) node, context, wm);
                 } else {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": unknown node " + node );
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": unknown node " + node);
                 }
             }
         }
@@ -294,523 +289,663 @@
         return context;
     }
 
-    private void betaNode(DslStep step,
-                          BetaNode node,
-                          Map<String, Object> context,
-                          InternalWorkingMemory wm) {
+    private void betaNode(DslStep step, BetaNode node,
+            Map<String, Object> context, InternalWorkingMemory wm) {
+        
+        final boolean lrUnlinkingEnabled = ((BuildContext) context.get(BUILD_CONTEXT))
+                                                                  .getRuleBase()
+                                                                  .getConfiguration().isLRUnlinkingEnabled();
+        
         try {
             List<String[]> cmds = step.getCommands();
-            List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
+            List<InternalFactHandle> handles = (List<InternalFactHandle>) context
+                    .get("Handles");
 
             BetaMemory memory = null;
-            if ( node instanceof AccumulateNode ) {
-                AccumulateMemory accmem = (AccumulateMemory) wm.getNodeMemory( node );
+            if (node instanceof AccumulateNode) {
+                AccumulateMemory accmem = (AccumulateMemory) wm
+                        .getNodeMemory(node);
                 memory = accmem.betaMemory;
             } else {
-                memory = (BetaMemory) wm.getNodeMemory( node );
+                memory = (BetaMemory) wm.getNodeMemory(node);
             }
-            for ( String[] cmd : cmds ) {
-                if ( cmd[0].equals( "leftMemory" ) ) {
+            for (String[] cmd : cmds) {
+                if (cmd[0].equals("leftMemory")) {
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
-                    List< ? > expectedLeftTuples = (List< ? >) MVEL.eval( listString,
-                                                                          vars );
+                    vars.put("h", handles);
+                    List<?> expectedLeftTuples = (List<?>) MVEL.eval(
+                            listString, vars);
 
                     LeftTupleMemory leftMemory = memory.getLeftTupleMemory();
 
-                    if ( expectedLeftTuples.isEmpty() && leftMemory.size() != 0 ) {
-                        throw new AssertionError( "line " + step.getLine() + ": left Memory expected [] actually " + leftMemory );
-                    } else if ( expectedLeftTuples.isEmpty() && leftMemory.size() == 0 ) {
+                    if (expectedLeftTuples.isEmpty() && leftMemory.size() != 0) {
+                        throw new AssertionFailedError("line " + step.getLine()
+                                + ": left Memory expected [] actually "
+                                + print(leftMemory, lrUnlinkingEnabled));
+                    } else if (expectedLeftTuples.isEmpty()
+                            && leftMemory.size() == 0) {
                         return;
                     }
 
-                    // we always lookup from the first element, in case it's indexed
-                    List<InternalFactHandle> first = (List<InternalFactHandle>) expectedLeftTuples.get( 0 );
-                    LeftTuple firstTuple = new LeftTuple( first.get( 0 ),
-                                                          null,
-                                                          false );
-                    for ( int i = 1; i < first.size(); i++ ) {
-                        firstTuple = new LeftTuple( firstTuple,
-                                                    null,
-                                                    false );
+                    // we always lookup from the first element, in case it's
+                    // indexed
+                    List<InternalFactHandle> first = (List<InternalFactHandle>) expectedLeftTuples
+                            .get(0);
+                    LeftTuple firstTuple = new LeftTuple(first.get(0), null,
+                            false);
+                    for (int i = 1; i < first.size(); i++) {
+                        firstTuple = new LeftTuple(firstTuple, null, false);
                     }
 
                     List<LeftTuple> leftTuples = new ArrayList<LeftTuple>();
 
-                    for ( LeftTuple leftTuple = memory.getLeftTupleMemory().getFirst( firstTuple ); leftTuple != null; leftTuple = (LeftTuple) leftTuple.getNext() ) {
-                        leftTuples.add( leftTuple );
+                    for (LeftTuple leftTuple = memory.getLeftTupleMemory()
+                            .getFirst(firstTuple); leftTuple != null; leftTuple = (LeftTuple) leftTuple
+                            .getNext()) {
+                        leftTuples.add(leftTuple);
                     }
-                    List<List<InternalFactHandle>> actualLeftTuples = new ArrayList<List<InternalFactHandle>>( leftTuples.size() );
-                    for ( LeftTuple leftTuple : leftTuples ) {
-                        List<InternalFactHandle> tupleHandles = Arrays.asList( leftTuple.toFactHandles() );
-                        actualLeftTuples.add( tupleHandles );
+                    
+
+                    if (lrUnlinkingEnabled) {
+                        // When L&R Unlinking is active, we need to sort the tuples here, 
+                        // because we might have asserted things in the wrong order, 
+                        // because linking a node's side means populating its memory
+                        // from the OTN which stores things in a hash-set, so insertion order is not kept. 
+                        Collections.sort(leftTuples, new LeftTupleComparator());
                     }
+                    
+                    List<List<InternalFactHandle>> actualLeftTuples = getHandlesList(leftTuples);
+                    
 
-                    if ( !expectedLeftTuples.equals( actualLeftTuples ) ) {
-                        throw new AssertionError( "line " + step.getLine() + ": left Memory expected " + expectedLeftTuples + " actually " + actualLeftTuples );
+                    if (!expectedLeftTuples.equals(actualLeftTuples)) {
+                        throw new AssertionFailedError("line " + step.getLine()
+                                + ": left Memory expected "
+                                + print(expectedLeftTuples) + " actually "
+                                + print(actualLeftTuples));
                     }
 
-                } else if ( cmd[0].equals( "rightMemory" ) ) {
+                } else if (cmd[0].equals("rightMemory")) {
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
-                    List< ? > expectedFactHandles = (List< ? >) MVEL.eval( listString,
-                                                                           vars );
+                    vars.put("h", handles);
+                    List<?> expectedFactHandles = (List<?>) MVEL.eval(
+                            listString, vars);
 
                     RightTupleMemory rightMemory = memory.getRightTupleMemory();
 
-                    if ( expectedFactHandles.isEmpty() && rightMemory.size() != 0 ) {
-                        throw new AssertionError( "line " + step.getLine() + ": right Memory expected [] actually " + rightMemory );
-                    } else if ( expectedFactHandles.isEmpty() && rightMemory.size() == 0 ) {
+                    if (expectedFactHandles.isEmpty()
+                            && rightMemory.size() != 0) {
+                        throw new AssertionFailedError("line " + step.getLine()
+                                + ": right Memory expected [] actually "
+                                + print(rightMemory));
+                    } else if (expectedFactHandles.isEmpty()
+                            && rightMemory.size() == 0) {
                         return;
                     }
 
-                    RightTuple first = new RightTuple( (InternalFactHandle) expectedFactHandles.get( 0 ) );
+                    RightTuple first = new RightTuple(
+                            (InternalFactHandle) expectedFactHandles.get(0));
                     List<RightTuple> actualRightTuples = new ArrayList<RightTuple>();
-                    for ( RightTuple rightTuple = memory.getRightTupleMemory().getFirst( first ); rightTuple != null; rightTuple = (RightTuple) rightTuple.getNext() ) {
-                        actualRightTuples.add( rightTuple );
+                    for (RightTuple rightTuple = memory.getRightTupleMemory()
+                            .getFirst(first); rightTuple != null; rightTuple = (RightTuple) rightTuple
+                            .getNext()) {
+                        actualRightTuples.add(rightTuple);
                     }
 
-                    if ( expectedFactHandles.size() != actualRightTuples.size() ) {
-                        throw new AssertionError( "line " + step.getLine() + ": right Memory expected " + expectedFactHandles + " actually " + actualRightTuples );
+                    if (expectedFactHandles.size() != actualRightTuples.size()) {
+                        throw new AssertionFailedError("line " + step.getLine()
+                                + ": right Memory expected "
+                                + print(expectedFactHandles) + " actually "
+                                + print(actualRightTuples));
                     }
 
-                    for ( int i = 0, length = actualRightTuples.size(); i < length; i++ ) {
-                        if ( expectedFactHandles.get( i ) != actualRightTuples.get( i ).getFactHandle() ) {
-                            throw new AssertionError( "line " + step.getLine() + ": right Memory expected " + expectedFactHandles + " actually " + actualRightTuples );
+                    for (int i = 0, length = actualRightTuples.size(); i < length; i++) {
+                        if (expectedFactHandles.get(i) != actualRightTuples
+                                .get(i).getFactHandle()) {
+                            throw new AssertionFailedError("line "
+                                    + step.getLine()
+                                    + ": right Memory expected ["
+                                    + print(expectedFactHandles) + "] actually ["
+                                    + print(actualRightTuples)
+                                    + "]");
                         }
                     }
 
                 } else {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": command does not exist " + Arrays.toString( cmd ) );
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": command does not exist "
+                            + Arrays.toString(cmd));
                 }
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
-    private void riaNode(DslStep step,
-                         RightInputAdapterNode node,
-                         Map<String, Object> context,
-                         InternalWorkingMemory wm) {
+    private List<List<InternalFactHandle>> getHandlesList(
+            List<LeftTuple> leftTuples) {
+        List<List<InternalFactHandle>> actualLeftTuples = new ArrayList<List<InternalFactHandle>>(
+                leftTuples.size());
+        for (LeftTuple leftTuple : leftTuples) {
+            List<InternalFactHandle> tupleHandles = Arrays
+                    .asList(leftTuple.toFactHandles());
+            actualLeftTuples.add(tupleHandles);
+        }
+        return actualLeftTuples;
+    }
+
+    private String print(LeftTupleMemory leftMemory, boolean lrUnlinkingEnabled) {
+        
+        List<LeftTuple> tuples = new ArrayList<LeftTuple>();
+        Iterator it = leftMemory.iterator();
+        for ( LeftTuple tuple = (LeftTuple) it.next(); tuple != null; tuple = (LeftTuple) it.next() ) {
+            tuples.add(tuple);
+        }
+        
+        if (lrUnlinkingEnabled) {
+            // Necessary only when L&R unlinking are active.
+            Collections.sort(tuples, new LeftTupleComparator());
+        }
+
+        return print(getHandlesList(tuples));
+    }
+    
+    private String print(RightTupleMemory memory) {
+
+        List<RightTuple> tuples = new ArrayList<RightTuple>();
+        Iterator it = memory.iterator();
+
+        for ( RightTuple tuple = (RightTuple) it.next(); tuple != null; tuple = (RightTuple) it.next() ) {
+            tuples.add(tuple);
+        }
+        
+        return "[" + print(tuples) + "]";
+    }
+
+    /** Provides better error messages. */
+    protected String print(List<?> tuples) {
+        
+        StringBuilder b = new StringBuilder();
+        
+        for (java.util.Iterator iterator = tuples.iterator(); iterator.hasNext();) {
+            
+                Object tuple = (Object) iterator.next();
+    
+                if (tuple instanceof List<?>) {
+                    b.append("[");
+                    b.append(print((List<?>) tuple));
+                    b.append("]");
+                } else if (tuple instanceof InternalFactHandle){
+                    InternalFactHandle h = (InternalFactHandle) tuple;
+                    b.append("h").append(h.getId()-1);
+                } else if (tuple instanceof RightTuple){
+                    InternalFactHandle h = (InternalFactHandle) ((RightTuple) tuple).getFactHandle();
+                    b.append("h").append(h.getId()-1);
+                }
+                
+                if(iterator.hasNext()) 
+                    b.append(", ");
+            }
+
+        if (b.length() == 0)
+            return "[]";
+        
+        return b.toString();
+    }
+    
+
+    private void riaNode(DslStep step, RightInputAdapterNode node,
+            Map<String, Object> context, InternalWorkingMemory wm) {
         try {
             List<String[]> cmds = step.getCommands();
-            List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
+            List<InternalFactHandle> handles = (List<InternalFactHandle>) context
+                    .get("Handles");
 
-            final ObjectHashMap memory = (ObjectHashMap) wm.getNodeMemory( node );
-            for ( String[] cmd : cmds ) {
-                if ( cmd[0].equals( "leftMemory" ) ) {
+            final ObjectHashMap memory = (ObjectHashMap) wm.getNodeMemory(node);
+            for (String[] cmd : cmds) {
+                if (cmd[0].equals("leftMemory")) {
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
-                    List< ? > expectedLeftTuples = (List< ? >) MVEL.eval( listString,
-                                                                          vars );
+                    vars.put("h", handles);
+                    List<?> expectedLeftTuples = (List<?>) MVEL.eval(
+                            listString, vars);
 
-                    if ( expectedLeftTuples.isEmpty() && memory.size() != 0 ) {
-                        throw new AssertionError( "line " + step.getLine() + ": left Memory expected [] actually " + memory );
-                    } else if ( expectedLeftTuples.isEmpty() && memory.size() == 0 ) {
+                    if (expectedLeftTuples.isEmpty() && memory.size() != 0) {
+                        throw new AssertionFailedError("line " + step.getLine()
+                                + ": left Memory expected [] actually "
+                                + memory);
+                    } else if (expectedLeftTuples.isEmpty()
+                            && memory.size() == 0) {
                         return;
                     }
 
                     // create expected tuples
                     List<LeftTuple> leftTuples = new ArrayList<LeftTuple>();
-                    for ( List<InternalFactHandle> tlist : (List<List<InternalFactHandle>>) expectedLeftTuples ) {
-                        LeftTuple tuple = new LeftTuple( tlist.get( 0 ),
-                                                         null,
-                                                         false );
-                        for ( int i = 1; i < tlist.size(); i++ ) {
-                            tuple = new LeftTuple( tuple,
-                                                   new RightTuple( tlist.get( i ) ),
-                                                   null,
-                                                   false );
+                    for (List<InternalFactHandle> tlist : (List<List<InternalFactHandle>>) expectedLeftTuples) {
+                        LeftTuple tuple = new LeftTuple(tlist.get(0), null,
+                                false);
+                        for (int i = 1; i < tlist.size(); i++) {
+                            tuple = new LeftTuple(tuple, new RightTuple(tlist
+                                    .get(i)), null, false);
                         }
-                        leftTuples.add( tuple );
+                        leftTuples.add(tuple);
 
                     }
 
                     // get actual tuples
                     final List<LeftTuple> actualTuples = new ArrayList<LeftTuple>();
                     final Iterator it = memory.iterator();
-                    for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
-                        actualTuples.add( (LeftTuple) entry.getKey() );
+                    for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
+                            .next()) {
+                        actualTuples.add((LeftTuple) entry.getKey());
                     }
 
-                    // iterate over expected tuples and compare with actual tuples 
-                    for ( LeftTuple tuple : leftTuples ) {
-                        if ( !actualTuples.remove( tuple ) ) {
-                            throw new AssertionError( "line " + step.getLine() + ": left Memory expected " + tuple + " not found in memory." );
+                    // iterate over expected tuples and compare with actual
+                    // tuples
+                    for (LeftTuple tuple : leftTuples) {
+                        if (!actualTuples.remove(tuple)) {
+                            throw new AssertionFailedError("line "
+                                    + step.getLine()
+                                    + ": left Memory expected " + tuple
+                                    + " not found in memory.");
                         }
                     }
-                    if ( !actualTuples.isEmpty() ) {
-                        throw new AssertionError( "line " + step.getLine() + ": left Memory unexpected tuples in the node memory " + actualTuples );
+                    if (!actualTuples.isEmpty()) {
+                        throw new AssertionFailedError(
+                                "line "
+                                        + step.getLine()
+                                        + ": left Memory unexpected tuples in the node memory "
+                                        + actualTuples);
                     }
                 } else {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": command does not exist " + Arrays.toString( cmd ) );
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": command does not exist "
+                            + Arrays.toString(cmd));
                 }
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
-    private void ruleTerminalNode(DslStep step,
-                                  RuleTerminalNode node,
-                                  Map<String, Object> context,
-                                  InternalWorkingMemory wm) {
+    private void ruleTerminalNode(DslStep step, RuleTerminalNode node,
+            Map<String, Object> context, InternalWorkingMemory wm) {
         try {
             List<String[]> cmds = step.getCommands();
-            //List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
+            // List<InternalFactHandle> handles = (List<InternalFactHandle>)
+            // context.get( "Handles" );
 
-            for ( String[] cmd : cmds ) {
-                throw new IllegalArgumentException( "line " + step.getLine() + ": command does not exist " + Arrays.toString( cmd ) );
+            for (String[] cmd : cmds) {
+                throw new IllegalArgumentException("line " + step.getLine()
+                        + ": command does not exist " + Arrays.toString(cmd));
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
     @SuppressWarnings("unchecked")
-    private void assertObject(DslStep step,
-                              Map<String, Object> context,
-                              InternalWorkingMemory wm) {
+    private void assertObject(DslStep step, Map<String, Object> context,
+            InternalWorkingMemory wm) {
         try {
             List<String[]> cmds = step.getCommands();
-            List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
-            for ( String[] cmd : cmds ) {
+            List<InternalFactHandle> handles = (List<InternalFactHandle>) context
+                    .get("Handles");
+            for (String[] cmd : cmds) {
                 try {
                     String nodeName = cmd[0];
-                    Sink sink = (Sink) context.get( nodeName );
-                    if ( sink == null ) {
-                        throw new IllegalArgumentException( "line " + step.getLine() + ": node " + nodeName + " does not exist" );
+                    Sink sink = (Sink) context.get(nodeName);
+                    if (sink == null) {
+                        throw new IllegalArgumentException("line "
+                                + step.getLine() + ": node " + nodeName
+                                + " does not exist");
                     }
 
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
+                    vars.put("h", handles);
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
-                    List< ? > list = (List< ? >) MVEL.eval( listString,
-                                                            vars );
-                    if ( list == null ) {
-                        throw new IllegalArgumentException( cmd + " does not specify an existing fact handle" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
+                    List<?> list = (List<?>) MVEL.eval(listString, vars);
+                    if (list == null) {
+                        throw new IllegalArgumentException(cmd
+                                + " does not specify an existing fact handle");
                     }
 
-                    for ( Object element : list ) {
-                        if ( element == null ) {
-                            throw new IllegalArgumentException( cmd + " does not specify an existing fact handle" );
+                    for (Object element : list) {
+                        if (element == null) {
+                            throw new IllegalArgumentException(
+                                    cmd
+                                            + " does not specify an existing fact handle");
                         }
 
-                        if ( element instanceof InternalFactHandle ) {
+                        if (element instanceof InternalFactHandle) {
                             InternalFactHandle handle = (InternalFactHandle) element;
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.ASSERTION,
-                                                                                      null,
-                                                                                      null,
-                                                                                      handle );
-                            ((ObjectSink) sink).assertObject( handle,
-                                                              pContext,
-                                                              wm );
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.ASSERTION, null, null,
+                                    handle);
+                            ((ObjectSink) sink).assertObject(handle, pContext,
+                                    wm);
                         } else {
                             List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
-                            LeftTuple tuple = createTuple( context,
-                                                           tlist );
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.ASSERTION,
-                                                                                      null,
-                                                                                      tuple,
-                                                                                      null );
-                            ((LeftTupleSink) sink).assertLeftTuple( tuple,
-                                                                    pContext,
-                                                                    wm );
+                            LeftTuple tuple = createTuple(context, tlist);
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.ASSERTION, null, tuple,
+                                    null);
+                            ((LeftTupleSink) sink).assertLeftTuple(tuple,
+                                    pContext, wm);
                         }
 
                     }
-                } catch ( Exception e ) {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute command " + cmd,
-                                                        e );
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": unable to execute command " + cmd, e);
 
                 }
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
     private LeftTuple createTuple(Map<String, Object> context,
-                                  List<InternalFactHandle> tlist) {
+            List<InternalFactHandle> tlist) {
         LeftTuple tuple = null;
-        String id = getTupleId( tlist );
-        for ( InternalFactHandle handle : tlist ) {
-            if ( tuple == null ) {
-                tuple = new LeftTuple( handle,
-                                       null,
-                                       false ); // do not keep generated tuples on the handle list
+        String id = getTupleId(tlist);
+        for (InternalFactHandle handle : tlist) {
+            if (tuple == null) {
+                tuple = new LeftTuple(handle, null, false); // do not keep
+                                                            // generated tuples
+                                                            // on the handle
+                                                            // list
             } else {
-                tuple = new LeftTuple( tuple,
-                                       new RightTuple( handle ),
-                                       null,
-                                       true );
+                tuple = new LeftTuple(tuple, new RightTuple(handle), null, true);
             }
         }
-        context.put( id,
-                     tuple );
+        context.put(id, tuple);
         return tuple;
     }
 
     private String getTupleId(List<InternalFactHandle> tlist) {
         StringBuilder id = new StringBuilder();
-        id.append( "T." );
-        for ( InternalFactHandle handle : tlist ) {
-            id.append( handle.getId() );
-            id.append( "." );
+        id.append("T.");
+        for (InternalFactHandle handle : tlist) {
+            id.append(handle.getId());
+            id.append(".");
         }
         return id.toString();
     }
 
-    private void retractObject(DslStep step,
-                               Map<String, Object> context,
-                               InternalWorkingMemory wm) {
+    private void retractObject(DslStep step, Map<String, Object> context,
+            InternalWorkingMemory wm) {
         try {
             List<String[]> cmds = step.getCommands();
-            List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
-            for ( String[] cmd : cmds ) {
+            List<InternalFactHandle> handles = (List<InternalFactHandle>) context
+                    .get("Handles");
+            for (String[] cmd : cmds) {
                 try {
                     String nodeName = cmd[0];
-                    Sink sink = (Sink) context.get( nodeName );
-                    if ( sink == null ) {
-                        throw new IllegalArgumentException( "line " + step.getLine() + ": node " + nodeName + " does not exist" );
+                    Sink sink = (Sink) context.get(nodeName);
+                    if (sink == null) {
+                        throw new IllegalArgumentException("line "
+                                + step.getLine() + ": node " + nodeName
+                                + " does not exist");
                     }
 
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
-                    List< ? > list = (List< ? >) MVEL.eval( listString,
-                                                            vars );
-                    if ( list == null ) {
-                        throw new IllegalArgumentException( Arrays.toString( cmd ) + " does not specify an existing fact handle" );
+                    vars.put("h", handles);
+                    List<?> list = (List<?>) MVEL.eval(listString, vars);
+                    if (list == null) {
+                        throw new IllegalArgumentException(Arrays.toString(cmd)
+                                + " does not specify an existing fact handle");
                     }
 
-                    for ( Object element : list ) {
-                        if ( element == null ) {
-                            throw new IllegalArgumentException( Arrays.toString( cmd ) + " does not specify an existing fact handle" );
+                    for (Object element : list) {
+                        if (element == null) {
+                            throw new IllegalArgumentException(
+                                    Arrays.toString(cmd)
+                                            + " does not specify an existing fact handle");
                         }
 
-                        if ( element instanceof InternalFactHandle ) {
+                        if (element instanceof InternalFactHandle) {
                             InternalFactHandle handle = (InternalFactHandle) element;
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.RETRACTION,
-                                                                                      null,
-                                                                                      null,
-                                                                                      handle );
-                            if ( sink instanceof ObjectTypeNode ) {
-                                ((ObjectTypeNode) sink).retractObject( handle,
-                                                                       pContext,
-                                                                       wm );
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.RETRACTION, null, null,
+                                    handle);
+                            if (sink instanceof ObjectTypeNode) {
+                                ((ObjectTypeNode) sink).retractObject(handle,
+                                        pContext, wm);
                             } else {
-                                for ( RightTuple rightTuple = handle.getFirstRightTuple(); rightTuple != null; rightTuple = (RightTuple) rightTuple.getHandleNext() ) {
-                                    rightTuple.getRightTupleSink().retractRightTuple( rightTuple,
-                                                                                      pContext,
-                                                                                      wm );
+                                for (RightTuple rightTuple = handle
+                                        .getFirstRightTuple(); rightTuple != null; rightTuple = (RightTuple) rightTuple
+                                        .getHandleNext()) {
+                                    rightTuple.getRightTupleSink()
+                                            .retractRightTuple(rightTuple,
+                                                    pContext, wm);
                                 }
-                                handle.setFirstRightTuple( null );
-                                handle.setLastRightTuple( null );
-                                for ( LeftTuple leftTuple = handle.getFirstLeftTuple(); leftTuple != null; leftTuple = (LeftTuple) leftTuple.getLeftParentNext() ) {
-                                    leftTuple.getLeftTupleSink().retractLeftTuple( leftTuple,
-                                                                                   pContext,
-                                                                                   wm );
+                                handle.setFirstRightTuple(null);
+                                handle.setLastRightTuple(null);
+                                for (LeftTuple leftTuple = handle
+                                        .getFirstLeftTuple(); leftTuple != null; leftTuple = (LeftTuple) leftTuple
+                                        .getLeftParentNext()) {
+                                    leftTuple.getLeftTupleSink()
+                                            .retractLeftTuple(leftTuple,
+                                                    pContext, wm);
                                 }
-                                handle.setFirstLeftTuple( null );
-                                handle.setLastLeftTuple( null );
+                                handle.setFirstLeftTuple(null);
+                                handle.setLastLeftTuple(null);
                             }
                         } else {
                             List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
-                            String id = getTupleId( tlist );
-                            LeftTuple tuple = (LeftTuple) context.remove( id );
-                            if ( tuple == null ) {
-                                throw new IllegalArgumentException( "Tuple not found: " + id + " : " + tlist.toString() );
+                            String id = getTupleId(tlist);
+                            LeftTuple tuple = (LeftTuple) context.remove(id);
+                            if (tuple == null) {
+                                throw new IllegalArgumentException(
+                                        "Tuple not found: " + id + " : "
+                                                + tlist.toString());
                             }
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.RETRACTION,
-                                                                                      null,
-                                                                                      tuple,
-                                                                                      null );
-                            ((LeftTupleSink) sink).retractLeftTuple( tuple,
-                                                                     pContext,
-                                                                     wm );
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.RETRACTION, null, tuple,
+                                    null);
+                            ((LeftTupleSink) sink).retractLeftTuple(tuple,
+                                    pContext, wm);
                         }
 
                     }
-                } catch ( Exception e ) {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute command " + Arrays.toString( cmd ),
-                                                        e );
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": unable to execute command "
+                            + Arrays.toString(cmd), e);
 
                 }
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
-    private void modifyObject(DslStep step,
-                              Map<String, Object> context,
-                              InternalWorkingMemory wm) {
+    private void modifyObject(DslStep step, Map<String, Object> context,
+            InternalWorkingMemory wm) {
         try {
             List<String[]> cmds = step.getCommands();
-            List<InternalFactHandle> handles = (List<InternalFactHandle>) context.get( "Handles" );
-            for ( String[] cmd : cmds ) {
+            List<InternalFactHandle> handles = (List<InternalFactHandle>) context
+                    .get("Handles");
+            for (String[] cmd : cmds) {
                 try {
                     String nodeName = cmd[0];
-                    Sink sink = (Sink) context.get( nodeName );
-                    if ( sink == null ) {
-                        throw new IllegalArgumentException( "line " + step.getLine() + ": node " + nodeName + " does not exist" );
+                    Sink sink = (Sink) context.get(nodeName);
+                    if (sink == null) {
+                        throw new IllegalArgumentException("line "
+                                + step.getLine() + ": node " + nodeName
+                                + " does not exist");
                     }
 
                     String args = cmd[1];
-                    String listString = args.replaceAll( "h(\\d+)",
-                                                         "h[$1]" );
+                    String listString = args.replaceAll("h(\\d+)", "h[$1]");
                     Map<String, Object> vars = new HashMap<String, Object>();
-                    vars.put( "h",
-                              handles );
-                    List< ? > list = (List< ? >) MVEL.eval( listString,
-                                                            vars );
-                    if ( list == null ) {
-                        throw new IllegalArgumentException( Arrays.toString( cmd ) + " does not specify an existing fact handle" );
+                    vars.put("h", handles);
+                    List<?> list = (List<?>) MVEL.eval(listString, vars);
+                    if (list == null) {
+                        throw new IllegalArgumentException(Arrays.toString(cmd)
+                                + " does not specify an existing fact handle");
                     }
 
-                    for ( Object element : list ) {
-                        if ( element == null ) {
-                            throw new IllegalArgumentException( Arrays.toString( cmd ) + " does not specify an existing fact handle" );
+                    for (Object element : list) {
+                        if (element == null) {
+                            throw new IllegalArgumentException(
+                                    Arrays.toString(cmd)
+                                            + " does not specify an existing fact handle");
                         }
 
-                        if ( element instanceof InternalFactHandle ) {
+                        if (element instanceof InternalFactHandle) {
                             InternalFactHandle handle = (InternalFactHandle) element;
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.MODIFICATION,
-                                                                                      null,
-                                                                                      null,
-                                                                                      handle );
-                            ModifyPreviousTuples modifyPreviousTuples = new ModifyPreviousTuples( handle.getFirstLeftTuple(),
-                                                                                                  handle.getFirstRightTuple() );
-                            handle.setFirstLeftTuple( null );
-                            handle.setFirstRightTuple( null );
-                            handle.setLastLeftTuple( null );
-                            handle.setLastRightTuple( null );
-                            ((ObjectSink) sink).modifyObject( handle,
-                                                              modifyPreviousTuples,
-                                                              pContext,
-                                                              wm );
-                            modifyPreviousTuples.retractTuples( pContext,
-                                                                wm );
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.MODIFICATION, null,
+                                    null, handle);
+                            ModifyPreviousTuples modifyPreviousTuples = new ModifyPreviousTuples(
+                                    handle.getFirstLeftTuple(), handle
+                                            .getFirstRightTuple());
+                            handle.setFirstLeftTuple(null);
+                            handle.setFirstRightTuple(null);
+                            handle.setLastLeftTuple(null);
+                            handle.setLastRightTuple(null);
+                            ((ObjectSink) sink).modifyObject(handle,
+                                    modifyPreviousTuples, pContext, wm);
+                            modifyPreviousTuples.retractTuples(pContext, wm);
                         } else {
                             List<InternalFactHandle> tlist = (List<InternalFactHandle>) element;
-                            String id = getTupleId( tlist );
-                            LeftTuple tuple = (LeftTuple) context.get( id );
-                            if ( tuple == null ) {
-                                throw new IllegalArgumentException( "Tuple not found: " + id + " : " + tlist.toString() );
+                            String id = getTupleId(tlist);
+                            LeftTuple tuple = (LeftTuple) context.get(id);
+                            if (tuple == null) {
+                                throw new IllegalArgumentException(
+                                        "Tuple not found: " + id + " : "
+                                                + tlist.toString());
                             }
-                            PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(),
-                                                                                      PropagationContext.MODIFICATION,
-                                                                                      null,
-                                                                                      tuple,
-                                                                                      null );
-                            ((LeftTupleSink) sink).modifyLeftTuple( tuple,
-                                                                    pContext,
-                                                                    wm );
+                            PropagationContext pContext = new PropagationContextImpl(
+                                    wm.getNextPropagationIdCounter(),
+                                    PropagationContext.MODIFICATION, null,
+                                    tuple, null);
+                            ((LeftTupleSink) sink).modifyLeftTuple(tuple,
+                                    pContext, wm);
                         }
                     }
-                } catch ( Exception e ) {
-                    throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute command " + cmd,
-                                                        e );
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("line " + step.getLine()
+                            + ": unable to execute command " + cmd, e);
                 }
             }
-        } catch ( Exception e ) {
-            throw new IllegalArgumentException( "line " + step.getLine() + ": unable to execute step " + step,
-                                                e );
+        } catch (Exception e) {
+            throw new IllegalArgumentException("line " + step.getLine()
+                    + ": unable to execute step " + step, e);
         }
     }
 
     public static NodeTestCase compile(final Reader reader) throws IOException {
-        NodeTestDSLParser parser = getParser( reader );
-        return compile( parser );
+        NodeTestDSLParser parser = getParser(reader);
+        return compile(parser);
     }
 
     public static NodeTestCase compile(final InputStream is) throws IOException {
-        NodeTestDSLParser parser = getParser( is );
-        return compile( parser );
+        NodeTestDSLParser parser = getParser(is);
+        return compile(parser);
     }
 
     public static NodeTestCase compile(final String source) throws IOException {
-        NodeTestDSLParser parser = getParser( source );
-        return compile( parser );
+        NodeTestDSLParser parser = getParser(source);
+        return compile(parser);
     }
 
     private static NodeTestCase compile(final NodeTestDSLParser parser) {
         try {
             compilation_unit_return cur = parser.compilation_unit();
-            if ( parser.hasErrors() ) {
+            if (parser.hasErrors()) {
                 NodeTestCase result = new NodeTestCase();
-                result.setErrors( parser.getErrorMessages() );
+                result.setErrors(parser.getErrorMessages());
                 return result;
             }
-            NodeTestCase testCase = walk( parser.getTokenStream(),
-                                          (CommonTree) cur.getTree() );
+            NodeTestCase testCase = walk(parser.getTokenStream(),
+                    (CommonTree) cur.getTree());
             return testCase;
-        } catch ( RecognitionException e ) {
+        } catch (RecognitionException e) {
             NodeTestCase result = new NodeTestCase();
-            result.setErrors( Collections.singletonList( e.getMessage() ) );
+            result.setErrors(Collections.singletonList(e.getMessage()));
             return result;
         }
     }
 
-    private static NodeTestCase walk(TokenStream tokenStream,
-                                     Tree resultTree) throws RecognitionException {
-        CommonTreeNodeStream nodes = new CommonTreeNodeStream( resultTree );
+    private static NodeTestCase walk(TokenStream tokenStream, Tree resultTree)
+            throws RecognitionException {
+        CommonTreeNodeStream nodes = new CommonTreeNodeStream(resultTree);
         // AST nodes have payload that point into token stream
-        nodes.setTokenStream( tokenStream );
+        nodes.setTokenStream(tokenStream);
         // Create a tree walker attached to the nodes stream
-        NodeTestDSLTree walker = new NodeTestDSLTree( nodes );
+        NodeTestDSLTree walker = new NodeTestDSLTree(nodes);
         walker.compilation_unit();
         return walker.getTestCase();
     }
 
-    private static NodeTestDSLParser getParser(final Reader reader) throws IOException {
-        NodeTestDSLLexer lexer = new NodeTestDSLLexer( new ANTLRReaderStream( reader ) );
-        NodeTestDSLParser parser = new NodeTestDSLParser( new CommonTokenStream( lexer ) );
+    private static NodeTestDSLParser getParser(final Reader reader)
+            throws IOException {
+        NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRReaderStream(
+                reader));
+        NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
+                lexer));
         return parser;
     }
 
-    private static NodeTestDSLParser getParser(final InputStream is) throws IOException {
-        NodeTestDSLLexer lexer = new NodeTestDSLLexer( new ANTLRInputStream( is ) );
-        NodeTestDSLParser parser = new NodeTestDSLParser( new CommonTokenStream( lexer ) );
+    private static NodeTestDSLParser getParser(final InputStream is)
+            throws IOException {
+        NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRInputStream(is));
+        NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
+                lexer));
         return parser;
     }
 
-    private static NodeTestDSLParser getParser(final String source) throws IOException {
-        NodeTestDSLLexer lexer = new NodeTestDSLLexer( new ANTLRStringStream( source ) );
-        NodeTestDSLParser parser = new NodeTestDSLParser( new CommonTokenStream( lexer ) );
+    private static NodeTestDSLParser getParser(final String source)
+            throws IOException {
+        NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRStringStream(
+                source));
+        NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream(
+                lexer));
         return parser;
     }
 
+    private final class LeftTupleComparator implements Comparator<LeftTuple> {
+        public int compare(LeftTuple o1, LeftTuple o2) {
+            
+            InternalFactHandle[] h1 = o1.getFactHandles();
+            InternalFactHandle[] h2 = o2.getFactHandles();
+            
+            // Handles have to be compared in the inverse order.
+            for (int i = (h1.length-1); i >= 0; i--) {
+                
+                int diff = h1[i].getId() - h2[i].getId();
+            
+                // Will continue comparing handles until
+                // a difference is found.
+                if (diff != 0)
+                    return diff;
+            }
+            
+            return 0;
+        }
+    }
+
     public static class EmptyNotifier extends RunNotifier {
         public static final EmptyNotifier INSTANCE = new EmptyNotifier();
 
@@ -839,7 +974,8 @@
         }
 
         @Override
-        public void fireTestStarted(Description description) throws StoppedByUserException {
+        public void fireTestStarted(Description description)
+                throws StoppedByUserException {
         }
     }
 }
\ No newline at end of file

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngineTest.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngineTest.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/ReteDslTestEngineTest.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -21,20 +21,17 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import junit.framework.TestCase;
 
 import org.drools.FactHandle;
 import org.drools.Person;
-import org.drools.RuleBaseConfiguration;
 import org.drools.RuleBaseFactory;
 import org.drools.WorkingMemory;
 import org.drools.base.ClassObjectType;
 import org.drools.common.DefaultFactHandle;
 import org.drools.common.InternalFactHandle;
 import org.drools.common.InternalWorkingMemory;
-import org.drools.common.NodeMemory;
 import org.drools.common.PropagationContextImpl;
 import org.drools.reteoo.BetaMemory;
 import org.drools.reteoo.JoinNode;
@@ -43,11 +40,11 @@
 import org.drools.reteoo.ObjectTypeNode;
 import org.drools.reteoo.ReteooRuleBase;
 import org.drools.reteoo.ReteooWorkingMemory;
-import org.drools.reteoo.RuleTerminalNode;
+import org.drools.reteoo.builder.BuildContext;
 import org.drools.reteoo.test.dsl.DslStep;
-import org.drools.reteoo.test.dsl.NodeTestDef;
 import org.drools.reteoo.test.dsl.NodeTestCase;
 import org.drools.reteoo.test.dsl.NodeTestCaseResult;
+import org.drools.reteoo.test.dsl.NodeTestDef;
 import org.drools.reteoo.test.dsl.NodeTestCaseResult.NodeTestResult;
 import org.drools.reteoo.test.dsl.NodeTestCaseResult.Result;
 import org.drools.rule.Declaration;
@@ -741,6 +738,21 @@
                       memory.getLeftTupleMemory().size() );         
     }
     
+    public void testConfigOptions() throws IOException {
+        String str = "TestCase 'testOTN'\nTest 'dummy'\n";
+        str += "Config:\n";
+        str += "    lrUnlinkingEnabled, true;\n";
+        str += "ObjectTypeNode:\n";
+        str += "    otn1, java.lang.Integer;\n";
+        
+        NodeTestResult result = executeTest( str );
+        Map<String, Object> map = result.context;
+
+        BuildContext buildCtx = (BuildContext) map.get( ReteDslTestEngine.BUILD_CONTEXT );
+        assertTrue(buildCtx.getRuleBase().getConfiguration().isLRUnlinkingEnabled());
+    }
+
+    
     public void testNotNodeStep() throws IOException {
         String str = "TestCase 'testOTN'\nTest 'dummy'\n";
         str += "ObjectTypeNode:\n";

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/SingleTestCase.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/SingleTestCase.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/SingleTestCase.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -62,6 +62,7 @@
                 }
                 for( NodeTestDef test : tcase.getTests() ) {
                     if( test.getName().equals( testName ) ) {
+                        result.setFileName(file.getName());
                         result.setName( tcase.getName() );
                         result.setImports( tcase.getImports() );
                         result.setSetup( tcase.getSetup() );

Added: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/ConfigStep.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/ConfigStep.java	                        (rev 0)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/ConfigStep.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -0,0 +1,44 @@
+package org.drools.reteoo.test.dsl;
+
+import static org.drools.reteoo.test.ReteDslTestEngine.BUILD_CONTEXT;
+import static org.drools.reteoo.test.ReteDslTestEngine.WORKING_MEMORY;
+
+import java.util.List;
+import java.util.Map;
+
+import org.drools.RuleBaseConfiguration;
+import org.drools.common.InternalWorkingMemory;
+import org.drools.reteoo.ReteooRuleBase;
+import org.drools.reteoo.builder.BuildContext;
+
+/**
+ * A step in the setup of a nodeTestCase, it allows configuration parameters to
+ * be passed.
+ * 
+ * @author lgomes
+ * 
+ */
+public class ConfigStep implements Step {
+
+    public void execute(Map<String, Object> context, List<String[]> args) {
+
+        RuleBaseConfiguration conf = new RuleBaseConfiguration();
+
+        for (String[] configOption : args) {
+            conf.setProperty("drools." + configOption[0], configOption[1]);
+        }
+
+        ReteooRuleBase rbase = new ReteooRuleBase("ID", conf);
+        BuildContext buildContext = new BuildContext(rbase, rbase
+                .getReteooBuilder().getIdGenerator());
+
+        InternalWorkingMemory wm = (InternalWorkingMemory) rbase
+                .newStatefulSession(true);
+
+        // Overwrite values now taking into account the configuration options.
+        context.put(BUILD_CONTEXT, buildContext);
+        context.put(WORKING_MEMORY, wm);
+
+    }
+
+}

Modified: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/NodeTestCase.java
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/NodeTestCase.java	2010-11-19 16:38:06 UTC (rev 36013)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/java/org/drools/reteoo/test/dsl/NodeTestCase.java	2010-11-21 23:40:59 UTC (rev 36014)
@@ -21,14 +21,15 @@
 
 import org.junit.runner.Description;
 
-
 /**
  * A class to describe a test case for reteoo nodes
  * 
  * @author etirelli
  */
 public class NodeTestCase {
-    
+
+    public static final String SUFFIX = ".nodeTestCase";
+
     private String name;
     private List<String> imports;
     private List<DslStep> setup;
@@ -36,48 +37,58 @@
     private List<NodeTestDef> tests;
     private List<String> errors;
     private Description description;
-    
+    private String fileName;
+
     public NodeTestCase() {
         this("");
     }
-    
-    public NodeTestCase( String name ) {
+
+    public NodeTestCase(String name) {
         this.name = name;
         this.imports = new ArrayList<String>();
         this.setup = new ArrayList<DslStep>();
         this.tearDown = new ArrayList<DslStep>();
         this.tests = new ArrayList<NodeTestDef>();
     }
-    
+
     public String getName() {
         return name;
     }
+
     public void setName(String name) {
         this.name = name;
     }
-    public void addImport( String clazz ) {
-        this.imports.add( clazz );
+
+    public void addImport(String clazz) {
+        this.imports.add(clazz);
     }
+
     public List<String> getImports() {
         return this.imports;
     }
+
     public List<DslStep> getSetup() {
         return setup;
     }
+
     public void addSetupStep(DslStep step) {
-        this.setup.add( step );
+        this.setup.add(step);
     }
+
     public List<DslStep> getTearDown() {
         return tearDown;
     }
+
     public void addTearDownStep(DslStep step) {
-        this.tearDown.add( step );
+        this.tearDown.add(step);
     }
+
     public List<NodeTestDef> getTests() {
         return tests;
     }
+
     public void addTest(NodeTestDef test) {
-        this.tests.add( test );
+        this.tests.add(test);
     }
 
     public List<String> getErrors() {
@@ -89,7 +100,7 @@
     }
 
     public boolean hasErrors() {
-        return this.errors != null && ! this.errors.isEmpty();
+        return this.errors != null && !this.errors.isEmpty();
     }
 
     public Description getDescription() {
@@ -111,4 +122,24 @@
     public void setTearDown(List<DslStep> tearDown) {
         this.tearDown = tearDown;
     }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+
+        this.fileName = removeSuffix(fileName);
+    }
+
+    private String removeSuffix(String name) {
+        // removes the suffix, if present.
+        if (name.endsWith(SUFFIX)) {
+            return name.substring(0, name.indexOf(SUFFIX));
+        }
+        
+        return name;
+
+    }
+
 }

Added: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingComplex.nodeTestCase
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingComplex.nodeTestCase	                        (rev 0)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingComplex.nodeTestCase	2010-11-21 23:40:59 UTC (rev 36014)
@@ -0,0 +1,57 @@
+TestCase "RightUnlinkingComplex"
+
+Setup
+	Config:
+		lrUnlinkingEnabled, true;
+	ObjectTypeNode:
+	    otnLeft1, org.drools.Person;
+	LeftInputAdapterNode:
+	    lian0, otnLeft1;
+	ObjectTypeNode:
+	    otnRight1, org.drools.Person;
+	ObjectTypeNode:
+	    otnRight2, org.drools.Person;
+	Binding:
+	     p1, 0, org.drools.Person, age;
+	Binding:
+	     p1, 0, org.drools.Person, likes;
+	JoinNode:
+	    join1, lian0, otnRight1;
+	    age, !=, p1;
+	JoinNode:
+	    join2, join1, otnRight1;
+	    likes, ==, p1;
+	JoinNode:
+	    join3, join2, otnRight2;
+	    age, !=, p1;
+	Facts:
+	    new org.drools.Person('darth', 35, 'stilton'), new org.drools.Person('bobba', 36, 'brie'),
+	    new org.drools.Person('yoda', 37, 'cammembert'), new org.drools.Person('luke', 38, 'minas'),
+	    new org.drools.Person('dave', 33, 'prato'), new org.drools.Person('bob', 32, 'parmigiano'),
+	    new org.drools.Person('dave', 31, 'stilton'), new org.drools.Person('bob', 30, 'minas');
+	    
+
+Test "RightUnlinkingComplex"	    
+
+	assert:
+		otnRight1, [h1];
+	join1:
+		leftMemory, [];	
+		rightMemory, []; 	// right is side is unlinked - PROBLEM here!
+	assert:
+		otnLeft1, [h0];			 	
+	join1:
+		leftMemory, [[h0]];	
+		rightMemory, [h1];
+	join2:
+		leftMemory, [[h0, h1]];
+		rightMemory, [h1];
+	join3:
+		leftMemory, [];
+		rightMemory, [];
+	
+	assert:
+		otnRight2, [h3];
+	join3:
+		leftMemory, [];
+		rightMemory, [];

Added: labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingModify.nodeTestCase
===================================================================
--- labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingModify.nodeTestCase	                        (rev 0)
+++ labs/jbossrules/branches/lr_unlinking_20101116/drools-core/src/test/resources/org/drools/reteoo/test/RightUnlinkingModify.nodeTestCase	2010-11-21 23:40:59 UTC (rev 36014)
@@ -0,0 +1,70 @@
+TestCase "RightUnlinkingModify"
+
+Setup
+	Config:
+		lrUnlinkingEnabled, true;
+	ObjectTypeNode:
+	    otnLeft1, org.drools.Person;
+	LeftInputAdapterNode:
+	    lian0, otnLeft1;
+	ObjectTypeNode:
+	    otnRight1, org.drools.Person;
+	ObjectTypeNode:
+	    otnRight2, org.drools.Person;
+	ObjectTypeNode:
+	    otnRight3, org.drools.Person;
+	Binding:
+	     p1, 0, org.drools.Person, age;
+	JoinNode:
+	    join1, lian0, otnRight1;
+	    age, !=, p1;
+	JoinNode:
+	    join2, join1, otnRight2;
+	    age, !=, p1;
+	JoinNode:
+	    join3, join1, otnRight3;
+	    age, !=, p1;
+	Facts:
+	    new org.drools.Person('darth', 35), new org.drools.Person('bobba', 36),
+	    new org.drools.Person('yoda', 37), new org.drools.Person('luke', 38),
+	    new org.drools.Person('dave', 33), new org.drools.Person('bob', 32),
+	    new org.drools.Person('dave', 31), new org.drools.Person('bob', 30);
+	    
+
+Test "RightUnlinkingModify"	    
+
+	assert:
+		otnRight1, [h0];	
+		otnRight2, [h1]; 	
+	join1:
+		rightMemory, [];
+	join2:
+		rightMemory, [];
+	
+	assert:
+		otnLeft1, [h2];
+	join1:
+		leftMemory, [[h2]];
+		rightMemory, [h0];
+	join2:
+		leftMemory, [[h2,h0]];
+		rightMemory, [h1]; 					
+
+	With:
+	    h2, age = 35;
+	modify:
+	    otnLeft1, [h2];
+
+	// With the modify, [h2,h0] don't match anymore 		
+	join1:
+		leftMemory, [[h2]];
+		rightMemory, [h0];
+	join2:
+		leftMemory, [];		// left tuple is retracted
+		rightMemory, [h1]; 	// right side is not unlinked
+		
+	assert:
+		otnRight2, [h3];	
+	join2:
+		rightMemory, [h1,h3]; 
+



More information about the jboss-svn-commits mailing list