diff --git a/drools-core/src/main/java/org/drools/core/common/DefaultFactHandle.java b/drools-core/src/main/java/org/drools/core/common/DefaultFactHandle.java index 002feeb0edeb..51365d30d7cd 100644 --- a/drools-core/src/main/java/org/drools/core/common/DefaultFactHandle.java +++ b/drools-core/src/main/java/org/drools/core/common/DefaultFactHandle.java @@ -28,6 +28,7 @@ import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlRootElement; +import org.drools.base.common.RuleBasePartitionId; import org.drools.base.factmodel.traits.TraitTypeEnum; import org.drools.base.rule.EntryPointId; import org.drools.core.WorkingMemoryEntryPoint; @@ -369,10 +370,6 @@ public void addLastLeftTuple( TupleImpl leftTuple ) { linkedTuples.addLastLeftTuple( leftTuple ); } - public void addTupleInPosition( TupleImpl tuple ) { - linkedTuples.addTupleInPosition( tuple ); - } - public void removeLeftTuple( TupleImpl leftTuple ) { linkedTuples.removeLeftTuple( leftTuple ); } @@ -510,48 +507,6 @@ public void addLastLeftTuple( TupleImpl leftTuple) { lastLeftTuple = leftTuple; } - @Override - public void addTupleInPosition( TupleImpl tuple ) { - boolean left = tuple.isLeftTuple(); - ObjectTypeNodeId otnId = tuple.getInputOtnId(); - if (otnId == null) { // can happen only in tests // @FIXME fix this - addLastTuple( tuple, left ); - return; - } - - TupleImpl previous = left ? lastLeftTuple : lastRightTuple; - if ( previous == null ) { - // no other LeftTuples, just add. - tuple.setHandlePrevious( null ); - tuple.setHandleNext( null ); - setFirstTuple( tuple, left ); - setLastTuple( tuple, left ); - return; - } else if (previous.getSink() == null || !otnId.before(previous.getInputOtnId()) ) { - // the last LeftTuple comes before the new one so just add it at the end - tuple.setHandlePrevious( previous ); - tuple.setHandleNext( null ); - previous.setHandleNext( tuple ); - setLastTuple( tuple, left ); - return; - } - - TupleImpl next = previous; - previous = previous.getHandlePrevious(); - while (previous != null && otnId.before( previous.getInputOtnId()) ) { - next = previous; - previous = previous.getHandlePrevious(); - } - tuple.setHandleNext( next ); - next.setHandlePrevious( tuple ); - tuple.setHandlePrevious( previous ); - if ( previous != null ) { - previous.setHandleNext( tuple ); - } else { - setFirstTuple( tuple, left ); - } - } - private void addLastTuple(TupleImpl tuple, boolean left) { if (left) { addLastLeftTuple(tuple); @@ -715,6 +670,82 @@ public TupleImpl getFirstRightTuple(int partition) { TupleImpl getFirstRightTuple() { return firstRightTuple; } + + public TupleImpl detachLeftTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + TupleImpl tuple = lastLeftTuple; + TupleImpl detached = null; + // Find the first Tuple that comes after the current ID, so it can be detached. + while (tuple != null && otnId.before(tuple.getInputOtnId())) { + detached = tuple; + tuple = tuple.getHandlePrevious(); + } + + if (detached != null) { + if (firstLeftTuple == detached) { + firstLeftTuple = null; + } + + if (lastLeftTuple == detached) { + lastLeftTuple = null; + } + + if (detached.getHandlePrevious() != null) { + lastLeftTuple = detached.getHandlePrevious(); + detached.setHandlePrevious(null); + lastLeftTuple.setHandleNext(null); + } + } + + return detached; + } + + public TupleImpl detachRightTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + TupleImpl tuple = lastRightTuple; + TupleImpl detached = null; + // Find the first Tuple that comes after the current ID, so it can be detached. + while (tuple != null && otnId.before(tuple.getInputOtnId())) { + detached = tuple; + tuple = tuple.getHandlePrevious(); + } + + if (detached != null) { + if (firstRightTuple == detached) { + firstRightTuple = null; + } + + if (lastRightTuple == detached) { + lastRightTuple = null; + } + + if (detached.getHandlePrevious() != null) { + lastRightTuple = detached.getHandlePrevious(); + detached.setHandlePrevious(null); + lastRightTuple.setHandleNext(null); + } + } + + return detached; + } + + public void reattachToLeft(TupleImpl tuple) { + if (lastLeftTuple == null) { + lastLeftTuple = tuple; + } else { + lastLeftTuple.setHandleNext(tuple); + tuple.setHandlePrevious(lastLeftTuple); + lastLeftTuple = tuple; + } + } + + public void reattachToRight(TupleImpl tuple) { + if (lastRightTuple == null) { + lastRightTuple = tuple; + } else { + lastRightTuple.setHandleNext(tuple); + tuple.setHandlePrevious(lastRightTuple); + lastRightTuple = tuple; + } + } } public static class CompositeLinkedTuples implements LinkedTuples { @@ -780,11 +811,6 @@ public void addLastLeftTuple( TupleImpl leftTuple ) { getOrCreatePartitionedTuple(leftTuple).addLastLeftTuple( leftTuple ); } - @Override - public void addTupleInPosition( TupleImpl tuple ) { - getOrCreatePartitionedTuple(tuple).addTupleInPosition( tuple ); - } - @Override public void removeLeftTuple( TupleImpl leftTuple ) { getPartitionedTuple(leftTuple).removeLeftTuple( leftTuple ); @@ -807,6 +833,26 @@ public void removeRightTuple( TupleImpl rightTuple ) { } } + @Override + public TupleImpl detachLeftTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + return getOrCreatePartitionedTuple(partitionId.getId()).detachLeftTupleAfter(partitionId, otnId); + } + + @Override + public TupleImpl detachRightTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + return getOrCreatePartitionedTuple(partitionId.getId()).detachRightTupleAfter(partitionId, otnId); + } + + @Override + public void reattachToLeft(TupleImpl tuple) { + getOrCreatePartitionedTuple(tuple).reattachToLeft(tuple); + } + + @Override + public void reattachToRight(TupleImpl tuple) { + getOrCreatePartitionedTuple(tuple).reattachToRight(tuple); + } + @Override public void clearLeftTuples() { for (int i = 0; i < partitionedTuples.length; i++) { @@ -900,11 +946,6 @@ public void addLastLeftTuple(TupleImpl leftTuple) { throw new UnsupportedOperationException(); } - @Override - public void addTupleInPosition(TupleImpl tuple) { - throw new UnsupportedOperationException(); - } - @Override public void removeLeftTuple(TupleImpl leftTuple) { throw new UnsupportedOperationException(); diff --git a/drools-core/src/main/java/org/drools/core/common/DisconnectedFactHandle.java b/drools-core/src/main/java/org/drools/core/common/DisconnectedFactHandle.java index 46546638a0d5..e9a44b27354e 100644 --- a/drools-core/src/main/java/org/drools/core/common/DisconnectedFactHandle.java +++ b/drools-core/src/main/java/org/drools/core/common/DisconnectedFactHandle.java @@ -363,10 +363,6 @@ public void addLastRightTuple(TupleImpl rightTuple) { throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE); } - public void addTupleInPosition(TupleImpl rightTuple) { - throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE); - } - public void removeRightTuple(TupleImpl rightTuple) { throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE); } diff --git a/drools-core/src/main/java/org/drools/core/common/InternalFactHandle.java b/drools-core/src/main/java/org/drools/core/common/InternalFactHandle.java index d83f0940c8e6..d8a4af38dff7 100644 --- a/drools-core/src/main/java/org/drools/core/common/InternalFactHandle.java +++ b/drools-core/src/main/java/org/drools/core/common/InternalFactHandle.java @@ -26,6 +26,7 @@ import org.drools.base.factmodel.traits.TraitTypeEnum; import org.drools.base.rule.EntryPointId; import org.drools.core.WorkingMemoryEntryPoint; +import org.drools.core.reteoo.ObjectTypeNodeId; import org.drools.core.reteoo.TupleImpl; import org.kie.api.runtime.rule.FactHandle; @@ -112,8 +113,6 @@ default String getEntryPointName() { void removeRightTuple( TupleImpl rightTuple); - void addTupleInPosition( TupleImpl tuple); - boolean isNegated(); void setNegated(boolean negated); @@ -142,9 +141,7 @@ interface LinkedTuples extends Serializable { void addFirstLeftTuple( TupleImpl leftTuple); void addLastLeftTuple( TupleImpl leftTuple); - - void addTupleInPosition( TupleImpl tuple); - + void removeLeftTuple( TupleImpl leftTuple); void addFirstRightTuple( TupleImpl rightTuple); @@ -171,6 +168,22 @@ default TupleImpl getFirstLeftTuple(RuleBasePartitionId partitionId) { default TupleImpl getFirstRightTuple(RuleBasePartitionId partitionId) { return getFirstRightTuple( partitionId.getParallelEvaluationSlot() ); } + + default TupleImpl detachLeftTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + throw new UnsupportedOperationException(); + } + + default TupleImpl detachRightTupleAfter(RuleBasePartitionId partitionId, ObjectTypeNodeId otnId) { + throw new UnsupportedOperationException(); + } + + default void reattachToLeft(TupleImpl tuple) { + throw new UnsupportedOperationException(); + } + + default void reattachToRight(TupleImpl tuple) { + throw new UnsupportedOperationException(); + } } static InternalFactHandle dummyFactHandleOf(Object object) { @@ -345,11 +358,6 @@ public void removeRightTuple( TupleImpl rightTuple) { throw new UnsupportedOperationException(); } - @Override - public void addTupleInPosition( TupleImpl tuple) { - throw new UnsupportedOperationException(); - } - @Override public boolean isNegated() { throw new UnsupportedOperationException(); diff --git a/drools-core/src/main/java/org/drools/core/common/QueryElementFactHandle.java b/drools-core/src/main/java/org/drools/core/common/QueryElementFactHandle.java index 26227b0d4609..cb9851912624 100644 --- a/drools-core/src/main/java/org/drools/core/common/QueryElementFactHandle.java +++ b/drools-core/src/main/java/org/drools/core/common/QueryElementFactHandle.java @@ -250,10 +250,6 @@ public void addLastRightTuple( TupleImpl rightTuple ) { throw new UnsupportedOperationException( "QueryElementFactHandle does not support this method" ); } - public void addTupleInPosition( TupleImpl rightTuple ) { - throw new UnsupportedOperationException( "QueryElementFactHandle does not support this method" ); - } - public void removeRightTuple( TupleImpl rightTuple ) { throw new UnsupportedOperationException( "QueryElementFactHandle does not support this method" ); } diff --git a/drools-core/src/main/java/org/drools/core/phreak/DetachedTuple.java b/drools-core/src/main/java/org/drools/core/phreak/DetachedTuple.java new file mode 100644 index 000000000000..f73cbdc7bed2 --- /dev/null +++ b/drools-core/src/main/java/org/drools/core/phreak/DetachedTuple.java @@ -0,0 +1,22 @@ +package org.drools.core.phreak; + +import org.drools.core.common.DefaultFactHandle; +import org.drools.core.reteoo.TupleImpl; + +public class DetachedTuple { + private DefaultFactHandle fh; + private TupleImpl tuple; + + public DetachedTuple(DefaultFactHandle fh, TupleImpl tuple) { + this.fh = fh; + this.tuple = tuple; + } + + public void reattachToLeft() { + fh.getLinkedTuples().reattachToLeft(tuple); + } + + public void reattachToRight() { + fh.getLinkedTuples().reattachToRight(tuple); + } +} diff --git a/drools-core/src/main/java/org/drools/core/phreak/EagerPhreakBuilder.java b/drools-core/src/main/java/org/drools/core/phreak/EagerPhreakBuilder.java index e53afd9935b4..a4d37f49d32f 100644 --- a/drools-core/src/main/java/org/drools/core/phreak/EagerPhreakBuilder.java +++ b/drools-core/src/main/java/org/drools/core/phreak/EagerPhreakBuilder.java @@ -46,9 +46,10 @@ import org.drools.core.reteoo.AlphaTerminalNode; import org.drools.core.reteoo.BetaMemory; import org.drools.core.reteoo.BetaNode; +import org.drools.core.reteoo.BetaNode.RightTupleSinkAdapter; import org.drools.core.reteoo.FromNode.FromMemory; import org.drools.core.reteoo.LeftInputAdapterNode; -import org.drools.core.reteoo.LeftInputAdapterNode.RightTupleSinkAdapter; +import org.drools.core.reteoo.LeftInputAdapterNode.LeftTupleSinkAdapter; import org.drools.core.reteoo.LeftTuple; import org.drools.core.reteoo.LeftTupleNode; import org.drools.core.reteoo.LeftTupleSinkNode; @@ -268,11 +269,26 @@ public static void insertLiaFacts(LeftTupleNode startNode, InternalWorkingMemory final PropagationContext pctx = pctxFactory.createPropagationContext(wm.getNextPropagationIdCounter(), PropagationContext.Type.RULE_ADDITION, null, null, null); LeftInputAdapterNode lian = (LeftInputAdapterNode) startNode; if (allBranches && visited.add(lian.getId()) || lian.getAssociatedTerminalsSize() == 1 ) { - RightTupleSinkAdapter liaAdapter = new RightTupleSinkAdapter(lian); - lian.getObjectSource().updateSink(liaAdapter, pctx, wm); + attachAdapterAndPropagate(wm, lian, pctx); } } + public static void attachAdapterAndPropagate(InternalWorkingMemory wm, LeftInputAdapterNode lian, PropagationContext pctx) { + List detachedTuples = new ArrayList<>(); + LeftTupleSinkAdapter liaAdapter = new LeftTupleSinkAdapter(lian, detachedTuples); + lian.getObjectSource().updateSink(liaAdapter, pctx, wm); + detachedTuples.forEach(d -> d.reattachToLeft()); + } + + public static void attachAdapterAndPropagate(InternalWorkingMemory wm, BetaNode bn) { + PropagationContextFactory pctxFactory = RuntimeComponentFactory.get().getPropagationContextFactory(); + final PropagationContext pctx = pctxFactory.createPropagationContext(wm.getNextPropagationIdCounter(), PropagationContext.Type.RULE_ADDITION, null, null, null); + List detachedTuples = new ArrayList<>(); + RightTupleSinkAdapter bnAdapter = new RightTupleSinkAdapter(bn, detachedTuples); + bn.getRightInput().updateSink(bnAdapter, pctx, wm); + detachedTuples.forEach(d -> d.reattachToRight()); + } + public static SegmentPrototype processSplit(LeftTupleNode splitNode, InternalRuleBase kbase, Collection wms, Set smemsToNotify) { LeftTupleNode segmentRoot = BuildtimeSegmentUtilities.findSegmentRoot(splitNode); SegmentPrototype proto1 = kbase.getSegmentPrototype(segmentRoot); @@ -326,9 +342,7 @@ public static void insertFacts(TerminalNode tn, InternalWorkingMemory wm, Set wms) { @@ -730,9 +729,7 @@ private static void insertFacts(PathEndNodes endNodes, Collection detachedTuples; + + public RightTupleSinkAdapter(BetaNode bnNode, List detachedTuples) { + this.bnNode = bnNode; + this.detachedTuples = detachedTuples; + } + + /** + * Do not use this constructor. It should be used just by deserialization. + */ + public RightTupleSinkAdapter() { + } + + public void assertObject(final InternalFactHandle factHandle, + final PropagationContext context, + final ReteEvaluator reteEvaluator) { + ObjectTypeNodeId otnId = bnNode.getRightInputOtnId(); + TupleImpl detached = factHandle.getLinkedTuples().detachRightTupleAfter(getPartitionId(), otnId); + if (detached != null) { + detachedTuples.add(new DetachedTuple((DefaultFactHandle) factHandle, detached)); + } + + bnNode.assertObject(factHandle, context, reteEvaluator); + } + + public void modifyObject(InternalFactHandle factHandle, + ModifyPreviousTuples modifyPreviousTuples, + PropagationContext context, + ReteEvaluator reteEvaluator) { + throw new UnsupportedOperationException( "ObjectSinkAdapter onlys supports assertObject method calls" ); + } + + public int getId() { + return 0; + } + + public RuleBasePartitionId getPartitionId() { + return bnNode.getPartitionId(); + } + + public void byPassModifyToBetaNode(InternalFactHandle factHandle, + ModifyPreviousTuples modifyPreviousTuples, + PropagationContext context, + ReteEvaluator reteEvaluator) { + throw new UnsupportedOperationException(); + } + + public int getType() { + return NodeTypeEnums.LeftInputAdapterNode; + } + + @Override public Rule[] getAssociatedRules() { + return bnNode.getAssociatedRules(); + } + + public boolean isAssociatedWith(Rule rule) { + return bnNode.isAssociatedWith( rule ); + } + + @Override + public NetworkNode[] getSinks() { + return new NetworkNode[0]; + } + + @Override + public void addAssociatedTerminal(BaseTerminalNode terminalNode) { + bnNode.addAssociatedTerminal(terminalNode); + } + + @Override + public void removeAssociatedTerminal(BaseTerminalNode terminalNode) { + bnNode.removeAssociatedTerminal(terminalNode); + } + + @Override + public int getAssociatedTerminalsSize() { + return bnNode.getAssociatedTerminalsSize(); + } + + @Override + public boolean hasAssociatedTerminal(BaseTerminalNode terminalNode) { + return bnNode.hasAssociatedTerminal(terminalNode); + } + } } diff --git a/drools-core/src/main/java/org/drools/core/reteoo/LeftInputAdapterNode.java b/drools-core/src/main/java/org/drools/core/reteoo/LeftInputAdapterNode.java index e2f73f70cf95..793d0ff3303a 100644 --- a/drools-core/src/main/java/org/drools/core/reteoo/LeftInputAdapterNode.java +++ b/drools-core/src/main/java/org/drools/core/reteoo/LeftInputAdapterNode.java @@ -31,7 +31,7 @@ import org.drools.base.reteoo.NodeTypeEnums; import org.drools.base.rule.Pattern; import org.drools.core.RuleBaseConfiguration; -import org.drools.core.common.BaseNode; +import org.drools.core.common.DefaultFactHandle; import org.drools.core.common.InternalFactHandle; import org.drools.core.common.Memory; import org.drools.core.common.MemoryFactory; @@ -40,6 +40,7 @@ import org.drools.core.common.SuperCacheFixer; import org.drools.core.common.TupleSets; import org.drools.core.common.UpdateContext; +import org.drools.core.phreak.DetachedTuple; import org.drools.core.phreak.RuntimeSegmentUtilities; import org.drools.core.reteoo.builder.BuildContext; import org.drools.core.rule.consequence.InternalMatch; @@ -595,25 +596,33 @@ public void reset() { * Used with the updateSink method, so that the parent ObjectSource * can update the TupleSink */ - public static class RightTupleSinkAdapter + public static class LeftTupleSinkAdapter implements ObjectSink { - private LeftTupleSink sink; private LeftInputAdapterNode liaNode; - public RightTupleSinkAdapter(LeftInputAdapterNode liaNode) { + private List detachedTuples; + + public LeftTupleSinkAdapter(LeftInputAdapterNode liaNode, List detachedTuples) { this.liaNode = liaNode; + this.detachedTuples = detachedTuples; } /** * Do not use this constructor. It should be used just by deserialization. */ - public RightTupleSinkAdapter() { + public LeftTupleSinkAdapter() { } public void assertObject(final InternalFactHandle factHandle, final PropagationContext context, final ReteEvaluator reteEvaluator) { + ObjectTypeNodeId otnId = liaNode.getSinkPropagator().getFirstLeftTupleSink().getLeftInputOtnId(); + TupleImpl detached = factHandle.getLinkedTuples().detachLeftTupleAfter(getPartitionId(), otnId); + if (detached != null) { + detachedTuples.add(new DetachedTuple((DefaultFactHandle) factHandle, detached)); + } + liaNode.assertObject(factHandle, context, reteEvaluator); } @@ -629,7 +638,7 @@ public int getId() { } public RuleBasePartitionId getPartitionId() { - return sink.getPartitionId(); + return liaNode.getPartitionId(); } public void byPassModifyToBetaNode(InternalFactHandle factHandle, @@ -644,11 +653,11 @@ public int getType() { } @Override public Rule[] getAssociatedRules() { - return sink.getAssociatedRules(); + return liaNode.getAssociatedRules(); } public boolean isAssociatedWith(Rule rule) { - return sink.isAssociatedWith( rule ); + return liaNode.isAssociatedWith( rule ); } @Override @@ -658,22 +667,22 @@ public NetworkNode[] getSinks() { @Override public void addAssociatedTerminal(BaseTerminalNode terminalNode) { - sink.addAssociatedTerminal(terminalNode); + liaNode.addAssociatedTerminal(terminalNode); } @Override public void removeAssociatedTerminal(BaseTerminalNode terminalNode) { - sink.removeAssociatedTerminal(terminalNode); + liaNode.removeAssociatedTerminal(terminalNode); } @Override public int getAssociatedTerminalsSize() { - return sink.getAssociatedTerminalsSize(); + return liaNode.getAssociatedTerminalsSize(); } @Override public boolean hasAssociatedTerminal(BaseTerminalNode terminalNode) { - return sink.hasAssociatedTerminal(terminalNode); + return liaNode.hasAssociatedTerminal(terminalNode); } } diff --git a/drools-core/src/main/java/org/drools/core/reteoo/RuleTerminalNode.java b/drools-core/src/main/java/org/drools/core/reteoo/RuleTerminalNode.java index 1e53bcd60070..9bb2559d6703 100644 --- a/drools-core/src/main/java/org/drools/core/reteoo/RuleTerminalNode.java +++ b/drools-core/src/main/java/org/drools/core/reteoo/RuleTerminalNode.java @@ -48,7 +48,7 @@ public class RuleTerminalNode extends AbstractTerminalNode { protected boolean fireDirect; - protected transient ObjectTypeNodeId leftInputOtnId; + protected transient ObjectTypeNodeId leftInputOtnId = ObjectTypeNodeId.DEFAULT_ID; protected String consequenceName; diff --git a/drools-core/src/main/java/org/drools/core/reteoo/TupleImpl.java b/drools-core/src/main/java/org/drools/core/reteoo/TupleImpl.java index 885fe9a39418..fea2b07301df 100644 --- a/drools-core/src/main/java/org/drools/core/reteoo/TupleImpl.java +++ b/drools-core/src/main/java/org/drools/core/reteoo/TupleImpl.java @@ -79,7 +79,7 @@ public TupleImpl(InternalFactHandle factHandle, setSink(sink); this.handle = factHandle; if ( leftTupleMemoryEnabled ) { - factHandle.addTupleInPosition( this ); + factHandle.addLastLeftTuple( this ); } }