Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[DROOLS-293] Out of order RightTuples when adding rules dynamically
  • Loading branch information
mariofusco authored and sotty committed Oct 12, 2013
1 parent 4a72926 commit 4b67279
Show file tree
Hide file tree
Showing 39 changed files with 480 additions and 258 deletions.
Expand Up @@ -43,11 +43,11 @@ public void setUp() throws Exception {
addRule("raiseAlarm");
}

protected static StatefulKnowledgeSession createKnowledgeSession(KnowledgeBase kbase) {
protected static StatefulKnowledgeSession createKnowledgeSession(KnowledgeBase kbase) {
return kbase.newStatefulKnowledgeSession();
}


@Test(timeout=10000)
public void testConcurrentRuleAdditions() throws Exception {
parallelExecute(RulesExecutor.getSolvers());
Expand Down
Expand Up @@ -28,15 +28,18 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import org.drools.Cheese;
Expand Down Expand Up @@ -69,9 +72,15 @@
import org.drools.definition.KnowledgePackage;
import org.drools.definition.type.FactType;
import org.drools.definitions.impl.KnowledgePackageImp;
import org.drools.event.rule.ActivationCancelledEvent;
import org.drools.event.rule.ActivationCreatedEvent;
import org.drools.event.rule.AfterActivationFiredEvent;
import org.drools.event.rule.AgendaEventListener;
import org.drools.event.rule.AgendaGroupPoppedEvent;
import org.drools.event.rule.AgendaGroupPushedEvent;
import org.drools.event.rule.BeforeActivationFiredEvent;
import org.drools.event.rule.RuleFlowGroupActivatedEvent;
import org.drools.event.rule.RuleFlowGroupDeactivatedEvent;
import org.drools.impl.EnvironmentFactory;
import org.drools.io.ResourceFactory;
import org.drools.marshalling.ObjectMarshallingStrategy;
Expand All @@ -83,6 +92,7 @@
import org.drools.runtime.EnvironmentName;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;
import org.drools.runtime.rule.Variable;
import org.drools.runtime.rule.WorkingMemoryEntryPoint;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
Expand Down Expand Up @@ -1065,6 +1075,73 @@ public void testRuleBaseAddRemoveSubNetworks() throws Exception {
}
}


@Test
public void testRuleBaseAddRemoveQuery() throws Exception {
try {
String drl = "package org.drools.test; \n" +
"global java.util.List list; \n" +
"query foo( String $s ) $s := String() end \n" +
"" +
"rule R when String() ?foo( $s ; ) then list.add( $s ); end \n";

RuleBase ruleBase = RuleBaseFactory.newRuleBase();
StatefulSession sf = ruleBase.newStatefulSession();
ArrayList list = new ArrayList();

//add and remove
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( new ByteArrayInputStream( drl.getBytes() ) ) );
Package pkg = builder.getPackage();
ruleBase.addPackage( pkg );

sf.setGlobal( "list", list );
sf.fireAllRules();
sf.insert( "bar" );
sf.fireAllRules();

org.drools.QueryResults rs = sf.getQueryResults( "foo", Variable.v );
assertEquals( 1, rs.size() );
assertEquals( Arrays.asList( "bar" ), list );

ruleBase.removePackage( pkg.getName() );

} catch ( Exception e ) {
e.printStackTrace();
fail( "Should not raise any exception" );
}
}

@Test
public void testRuleBaseAddRemoveEval() throws Exception {
try {
String drl = "package org.drools.test; \n" +
"global java.util.List list; \n" +
"rule R when $s : String() from \"bar\" eval( $s.length() > 0 ) then list.add( $s ); end \n";

RuleBase ruleBase = RuleBaseFactory.newRuleBase();
StatefulSession sf = ruleBase.newStatefulSession();
ArrayList list = new ArrayList();

//add and remove
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( new ByteArrayInputStream( drl.getBytes() ) ) );
Package pkg = builder.getPackage();
ruleBase.addPackage( pkg );

sf.setGlobal( "list", list );
sf.fireAllRules();
assertEquals( Arrays.asList( "bar" ), list );

ruleBase.removePackage( pkg.getName() );

} catch ( Exception e ) {
e.printStackTrace();
fail( "Should not raise any exception" );
}
}


@Test
public void testDynamicRuleAdditionsWithEntryPoints() throws Exception {
Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_DynamicWithEntryPoint.drl" ) );
Expand Down Expand Up @@ -1362,9 +1439,23 @@ public void testJBRULES_2206() {
((RuleBaseConfiguration) config).setRuleBaseUpdateHandler( null );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase( config );
StatefulKnowledgeSession session = createKnowledgeSession(kbase);

AgendaEventListener ael = mock( AgendaEventListener.class );
session.addEventListener( ael );
session.addEventListener( new AgendaEventListener() {
public void activationCreated( ActivationCreatedEvent activationCreatedEvent ) {
System.out.println( activationCreatedEvent );
}
public void activationCancelled( ActivationCancelledEvent activationCancelledEvent ) { }
public void beforeActivationFired( BeforeActivationFiredEvent beforeActivationFiredEvent ) {}
public void afterActivationFired( AfterActivationFiredEvent afterActivationFiredEvent ) {}
public void agendaGroupPopped( AgendaGroupPoppedEvent agendaGroupPoppedEvent ) {}
public void agendaGroupPushed( AgendaGroupPushedEvent agendaGroupPushedEvent ) {}
public void beforeRuleFlowGroupActivated( RuleFlowGroupActivatedEvent ruleFlowGroupActivatedEvent ) {}
public void afterRuleFlowGroupActivated( RuleFlowGroupActivatedEvent ruleFlowGroupActivatedEvent ) {}
public void beforeRuleFlowGroupDeactivated( RuleFlowGroupDeactivatedEvent ruleFlowGroupDeactivatedEvent ) {}
public void afterRuleFlowGroupDeactivated( RuleFlowGroupDeactivatedEvent ruleFlowGroupDeactivatedEvent ) {}
} );

for ( int i = 0; i < 5; i++ ) {
session.insert( new Cheese() );
Expand Down Expand Up @@ -1427,4 +1518,131 @@ protected synchronized Class loadClass(String name,
return c;
}
}



@Test
public void testDynamicRulesWithInheritance() {
String type = "package com.sample\n" +
"global java.util.List list; \n" +
"declare Foo\n" +
" id : int\n" +
"end\n" +
"" +
"declare Bar extends Foo end\n" +
"";

String r1 = "package com.sample\n" +
"global java.util.List list; \n" +
"rule R1 when\n" +
" Bar()\n" +
"then\n" +
" list.add( 1 ); \n" +
"end \n" +
"" +
"rule Init when \n" +
"then \n" +
" insert( new Bar() );\n" +
"end\n";

String r2 = "package com.sample\n" +
"global java.util.List list; \n" +
"rule R2 when\n" +
" $f : Foo()\n" +
"then\n" +
" list.add( 2 );\n" +
"end\n";

KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newByteArrayResource( type.getBytes() ), ResourceType.DRL );
assertFalse( kbuilder.getErrors().toString(), kbuilder.hasErrors() );

KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ArrayList list = new ArrayList();
ksession.setGlobal( "list", list );

kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder( kbase );
kbuilder.add( ResourceFactory.newByteArrayResource( r1.getBytes() ), ResourceType.DRL );
assertFalse( kbuilder.getErrors().toString(), kbuilder.hasErrors() );

ksession.fireAllRules();
assertEquals( Arrays.asList( 1 ), list );

kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder( kbase );
kbuilder.add( ResourceFactory.newByteArrayResource( r2.getBytes() ), ResourceType.DRL );
assertFalse( kbuilder.getErrors().toString(), kbuilder.hasErrors() );

ksession.fireAllRules();
assertEquals( Arrays.asList( 1, 2 ), list );

ksession.dispose();

}



@Test
public void testDynamicRulesWithNamedConsequencesAndConditionalBranches() {
String type = "package com.sample\n" +
"declare type Foo\n" +
" id : int\n" +
"end\n" +
"" +
"rule Init when then \n" +
" insert( new Foo( 1 ) ); \n" +
" insert( new Foo( 2 ) ); \n" +
"end \n";

String r1 = "package com.sample\n" +
"global java.util.Set set; \n" +
"rule R1 when\n" +
" $s: String() do[c1] \n" +
" Foo( $i : id ) do[c2] \n" +
" if ( id == 1 ) do[c3] \n" +
" else do[c4]" +
"then\n" +
" set.add( $i ); \n" +
"then[c1] \n" +
" set.add( $s ); \n" +
"then[c2] \n" +
" set.add( 100 + $i ); \n" +
"then[c3] \n" +
" set.add( 200 + $i ); \n" +
"then[c4] \n" +
" set.add( 300 + $i ); \n" +
"end\n";

KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newByteArrayResource( type.getBytes() ), ResourceType.DRL );
assertFalse( kbuilder.getErrors().toString(), kbuilder.hasErrors() );

KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

ksession.fireAllRules();

kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder( kbase );
kbuilder.add( ResourceFactory.newByteArrayResource( r1.getBytes() ), ResourceType.DRL );
assertFalse( kbuilder.getErrors().toString(), kbuilder.hasErrors() );
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

HashSet set = new HashSet();
ksession.setGlobal( "set", set );
ksession.insert( "go" );

ksession.fireAllRules();

System.out.print( set );
assertTrue( set.containsAll( Arrays.asList( 1, 2, 101, 201, 102, 302, "go" ) ) );

ksession.dispose();

}


}
Expand Up @@ -103,6 +103,9 @@ public void attach(BuildContext buildContext) {

}

public void updateSinkOnAttach( BuildContext context, PropagationContext propagationContext, InternalWorkingMemory workingMemory ) {
}

public List getPropagatedTuples(final ReteooWorkingMemory workingMemory,
final LeftTupleSink sink) {
// TODO Auto-generated method stub
Expand Down
13 changes: 13 additions & 0 deletions drools-core/src/main/java/org/drools/common/BaseNode.java
Expand Up @@ -22,6 +22,7 @@
import org.drools.reteoo.ReteooBuilder;
import org.drools.reteoo.RuleRemovalContext;
import org.drools.reteoo.builder.BuildContext;
import org.drools.spi.PropagationContext;
import org.drools.spi.RuleComponent;

import java.io.IOException;
Expand Down Expand Up @@ -94,6 +95,18 @@ public void attach() {
*/
public abstract void attach(BuildContext context);

public abstract void updateSinkOnAttach( BuildContext context, PropagationContext propagationContext, InternalWorkingMemory workingMemory );

public void updateSinkOnAttach( BuildContext context, InternalWorkingMemory workingMemory ) {
final PropagationContext propagationContext = new PropagationContextImpl(workingMemory.getNextPropagationIdCounter(),
PropagationContext.RULE_ADDITION,
null,
null,
null);
this.updateSinkOnAttach( context, propagationContext, workingMemory );
propagationContext.evaluateActionQueue( workingMemory );
}

/**
* A method that is called for all nodes whose network below them
* changed, after the change is complete, providing them with an oportunity
Expand Down
2 changes: 2 additions & 0 deletions drools-core/src/main/java/org/drools/common/NetworkNode.java
Expand Up @@ -42,4 +42,6 @@ public interface NetworkNode
*/
public RuleBasePartitionId getPartitionId();

public short getType();

}
Expand Up @@ -77,7 +77,6 @@
import org.drools.reteoo.ObjectTypeNode;
import org.drools.reteoo.QueryElementNode;
import org.drools.reteoo.QueryElementNode.UnificationNodeViewChangedEventListener;
import org.drools.reteoo.ReteooComponentFactory;
import org.drools.reteoo.ReteooStatefulSession;
import org.drools.reteoo.ReteooWorkingMemory;
import org.drools.reteoo.RightTuple;
Expand Down Expand Up @@ -268,6 +267,7 @@ public static ReteooStatefulSession readSession( ReteooStatefulSession session,
try {
// Yeah, I know, because one session is being deserialized, we go and lock all of them...
initialFactNode.attach( buildContext );
initialFactNode.updateSinkOnAttach( buildContext, context.wm );
} finally {
context.ruleBase.unlock();
}
Expand Down Expand Up @@ -659,8 +659,7 @@ public static void readLeftTuple( LeftTuple parentLeftTuple,
}
break;
}
case NodeTypeEnums.NotNode:
case NodeTypeEnums.ForallNotNode: {
case NodeTypeEnums.NotNode: {
BetaMemory memory = (BetaMemory) context.wm.getNodeMemory( (BetaNode) sink );
int type = stream.readShort();
if (type == PersisterEnums.LEFT_TUPLE_NOT_BLOCKED) {
Expand Down Expand Up @@ -768,7 +767,7 @@ public static void readLeftTuple( LeftTuple parentLeftTuple,
}
break;
}
case NodeTypeEnums.RightInputAdaterNode: {
case NodeTypeEnums.RightInputAdapterNode: {
// 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
Expand Down
Expand Up @@ -605,8 +605,7 @@ public static void writeLeftTuple(LeftTuple leftTuple,
//context.out.println( "---- EvalConditionNode --- END" );
break;
}
case NodeTypeEnums.NotNode :
case NodeTypeEnums.ForallNotNode : {
case NodeTypeEnums.NotNode : {
if ( leftTuple.getBlocker() == null ) {
// is not blocked so has children
stream.writeShort( PersisterEnums.LEFT_TUPLE_NOT_BLOCKED );
Expand Down Expand Up @@ -682,7 +681,7 @@ public static void writeLeftTuple(LeftTuple leftTuple,
//context.out.println( "---- AccumulateNode --- END" );
break;
}
case NodeTypeEnums.RightInputAdaterNode : {
case NodeTypeEnums.RightInputAdapterNode : {
//context.out.println( ".... RightInputAdapterNode" );
// RIANs generate new fact handles on-demand to wrap tuples and need special procedures when serializing to persistent storage
ObjectHashMap memory = (ObjectHashMap) context.wm.getNodeMemory( (NodeMemory) sink );
Expand Down
Expand Up @@ -297,7 +297,7 @@ private static void writeNodeMemories(MarshallerWriteContext context,
_node = writeAccumulateNodeMemory(i, memory);
break;
}
case NodeTypeEnums.RightInputAdaterNode: {
case NodeTypeEnums.RightInputAdapterNode: {
_node = writeRIANodeMemory(i, memory);
break;
}
Expand Down

0 comments on commit 4b67279

Please sign in to comment.