/** * Copyright 2005 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.reteoo; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.List; import org.drools.DroolsTestCase; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.WorkingMemory; import org.drools.base.SalienceInteger; import org.drools.common.DefaultAgenda; import org.drools.common.DefaultFactHandle; import org.drools.common.InternalAgenda; import org.drools.common.InternalKnowledgeRuntime; import org.drools.common.InternalWorkingMemory; import org.drools.common.PropagationContextImpl; import org.drools.common.RuleFlowGroupImpl; import org.drools.impl.InternalKnowledgeBase; import org.drools.impl.StatefulKnowledgeSessionImpl; import org.drools.process.instance.ProcessInstance; import org.drools.process.instance.impl.ConstraintEvaluator; import org.drools.reteoo.builder.BuildContext; import org.drools.rule.Rule; import org.drools.ruleflow.core.RuleFlowProcess; import org.drools.ruleflow.instance.RuleFlowProcessInstance; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.rule.impl.AgendaImpl; import org.drools.spi.Consequence; import org.drools.spi.KnowledgeHelper; import org.drools.spi.PropagationContext; import org.drools.workflow.core.Connection; import org.drools.workflow.core.Node; import org.drools.workflow.core.impl.ConnectionImpl; import org.drools.workflow.core.node.EndNode; import org.drools.workflow.core.node.Join; import org.drools.workflow.core.node.RuleSetNode; import org.drools.workflow.core.node.Split; import org.drools.workflow.core.node.StartNode; /** * @author mproctor */ public class RuleFlowGroupTest extends DroolsTestCase { private KnowledgeBase kbase; private BuildContext buildContext; protected void setUp() throws Exception { kbase = KnowledgeBaseFactory.newKnowledgeBase(); ReteooRuleBase ruleBase = (ReteooRuleBase) ((InternalKnowledgeBase) kbase).getRuleBase(); buildContext = new BuildContext( ruleBase, ruleBase.getReteooBuilder().getIdGenerator() ); } public void testRuleFlowGroup() { final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); InternalWorkingMemory workingMemory = ((StatefulKnowledgeSessionImpl) ksession).session; final InternalAgenda agenda = ((AgendaImpl) ksession.getAgenda()).getAgenda(); final List list = new ArrayList(); // create the consequence final Consequence consequence = new Consequence() { /** * */ private static final long serialVersionUID = 510l; public void evaluate(KnowledgeHelper knowledgeHelper, WorkingMemory workingMemory) { System.out.println( knowledgeHelper.getRule() ); list.add( knowledgeHelper.getRule() ); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } public void writeExternal(ObjectOutput out) throws IOException { } public String getName() { return "default"; } }; // create a rule for each rule flow groups final Rule rule0 = new Rule( "test-rule0" ); rule0.setRuleFlowGroup( "rule-flow-group-0" ); rule0.setConsequence( consequence ); final RuleTerminalNode node0 = new RuleTerminalNode( 3, new MockTupleSource( 2 ), rule0, rule0.getLhs(), buildContext ); final Rule rule1 = new Rule( "test-rule1" ); rule1.setRuleFlowGroup( "rule-flow-group-1" ); rule1.setConsequence( consequence ); final RuleTerminalNode node1 = new RuleTerminalNode( 4, new MockTupleSource( 2 ), rule1, rule1.getLhs(), buildContext ); final Rule rule2 = new Rule( "test-rule2" ); rule2.setRuleFlowGroup( "rule-flow-group-2" ); rule2.setConsequence( consequence ); rule2.setSalience( new SalienceInteger( 10 ) ); final RuleTerminalNode node2 = new RuleTerminalNode( 5, new MockTupleSource( 2 ), rule2, rule2.getLhs(), buildContext ); final Rule rule3 = new Rule( "test-rule3" ); rule3.setRuleFlowGroup( "rule-flow-group-3" ); rule3.setConsequence( consequence ); final RuleTerminalNode node3 = new RuleTerminalNode( 6, new MockTupleSource( 2 ), rule3, rule3.getLhs(), buildContext ); final PropagationContext context0 = new PropagationContextImpl( 0, PropagationContext.ASSERTION, rule0, null, null ); // nodes final StartNode start = new StartNode(); start.setId(1); final RuleSetNode ruleSet0 = new RuleSetNode(); ruleSet0.setRuleFlowGroup( "rule-flow-group-0" ); ruleSet0.setId(2); final RuleSetNode ruleSet1 = new RuleSetNode(); ruleSet1.setRuleFlowGroup( "rule-flow-group-1" ); ruleSet1.setId(3); final RuleSetNode ruleSet2 = new RuleSetNode(); ruleSet2.setRuleFlowGroup( "rule-flow-group-2" ); ruleSet2.setId(4); final RuleSetNode ruleSet3 = new RuleSetNode(); ruleSet3.setRuleFlowGroup( "rule-flow-group-3" ); ruleSet3.setId(5); final Split split = new Split(); split.setType( Split.TYPE_AND ); split.setId(6); final Join join = new Join(); join.setType( Join.TYPE_AND ); join.setId(7); final EndNode end = new EndNode(); end.setId(8); // connections new ConnectionImpl( start, Node.CONNECTION_DEFAULT_TYPE, ruleSet0, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet0, Node.CONNECTION_DEFAULT_TYPE, split, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( split, Node.CONNECTION_DEFAULT_TYPE, ruleSet1, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( split, Node.CONNECTION_DEFAULT_TYPE, ruleSet2, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet1, Node.CONNECTION_DEFAULT_TYPE, join, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet2, Node.CONNECTION_DEFAULT_TYPE, join, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( join, Node.CONNECTION_DEFAULT_TYPE, ruleSet3, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet3, Node.CONNECTION_DEFAULT_TYPE, end, Node.CONNECTION_DEFAULT_TYPE ); // process final RuleFlowProcess process = new RuleFlowProcess(); process.addNode( start ); process.addNode( ruleSet0 ); process.addNode( ruleSet1 ); process.addNode( ruleSet2 ); process.addNode( ruleSet3 ); process.addNode( split ); process.addNode( join ); process.addNode( end ); // proces instance final RuleFlowProcessInstance processInstance = new RuleFlowProcessInstance(); processInstance.setKnowledgeRuntime( (InternalKnowledgeRuntime) ksession ); processInstance.setProcess( process ); assertEquals( ProcessInstance.STATE_PENDING, processInstance.getState() ); final RuleFlowGroupImpl ruleFlowGroup0 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-0" ); final RuleFlowGroupImpl ruleFlowGroup1 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-1" ); final RuleFlowGroupImpl ruleFlowGroup2 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-2" ); final RuleFlowGroupImpl ruleFlowGroup3 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-3" ); final LeftTuple tuple0 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node0.assertLeftTuple( tuple0, context0, workingMemory ); final LeftTuple tuple1 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node0.assertLeftTuple( tuple1, context0, workingMemory ); final LeftTuple tuple2 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node1.assertLeftTuple( tuple2, context0, workingMemory ); final LeftTuple tuple3 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node2.assertLeftTuple( tuple3, context0, workingMemory ); final LeftTuple tuple4 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node3.assertLeftTuple( tuple4, context0, workingMemory ); // RuleFlowGroups should be populated, but the agenda shouldn't assertEquals( 2, ruleFlowGroup0.size() ); assertEquals( 1, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 1, ruleFlowGroup3.size() ); assertEquals( 0, agenda.agendaSize() ); // Activate process instance, the activations stay in the group, // but should now also be in the Agenda processInstance.start(); assertEquals( ProcessInstance.STATE_ACTIVE, processInstance.getState() ); assertEquals( 2, ruleFlowGroup0.size() ); assertEquals( 2, agenda.agendaSize() ); // As we fire each rule they are removed from both the Agenda and the RuleFlowGroup agenda.fireNextItem( null ); assertEquals( 1, ruleFlowGroup0.size() ); assertEquals( 1, agenda.agendaSize() ); // on firing the last activation the child rule flow groups should // activate and thus repopulate the agenda agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup0.size() ); assertEquals( 1, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 2, agenda.agendaSize() ); // we set the salience higher on rule2, so it sould fire first and empty ruleFlowGroup2 agenda.fireNextItem( null ); assertEquals( 1, ruleFlowGroup1.size() ); assertEquals( 0, ruleFlowGroup2.size() ); assertEquals( 1, agenda.agendaSize() ); // executing rule1, which should activate AND-join and thus group 3 agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup0.size() ); assertEquals( 0, ruleFlowGroup1.size() ); assertEquals( 0, ruleFlowGroup2.size() ); assertEquals( 1, ruleFlowGroup3.size() ); assertEquals( 1, agenda.agendaSize() ); // executing rule3, and finishing execution agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup0.size() ); assertEquals( 0, ruleFlowGroup1.size() ); assertEquals( 0, ruleFlowGroup2.size() ); assertEquals( 0, ruleFlowGroup3.size() ); assertEquals( 0, agenda.agendaSize() ); assertEquals( ProcessInstance.STATE_COMPLETED, processInstance.getState() ); } /** XOR split and join */ public void testRuleFlowGroup2() { final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); InternalWorkingMemory workingMemory = ((StatefulKnowledgeSessionImpl) ksession).session; final DefaultAgenda agenda = (DefaultAgenda) workingMemory.getAgenda(); final List list = new ArrayList(); // create the consequence final Consequence consequence = new Consequence() { /** * */ private static final long serialVersionUID = 510l; public void evaluate(KnowledgeHelper knowledgeHelper, WorkingMemory workingMemory) { list.add( knowledgeHelper.getRule() ); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } public void writeExternal(ObjectOutput out) throws IOException { } public String getName() { return "default"; } }; // create a rule for each rule flow groups final Rule rule0 = new Rule( "test-rule0" ); rule0.setRuleFlowGroup( "rule-flow-group-0" ); rule0.setConsequence( consequence ); final RuleTerminalNode node0 = new RuleTerminalNode( 3, new MockTupleSource( 2 ), rule0, rule0.getLhs(), buildContext ); final Rule rule1 = new Rule( "test-rule1" ); rule1.setRuleFlowGroup( "rule-flow-group-1" ); rule1.setConsequence( consequence ); final RuleTerminalNode node1 = new RuleTerminalNode( 4, new MockTupleSource( 2 ), rule1, rule1.getLhs(), buildContext ); final Rule rule2 = new Rule( "test-rule2" ); rule2.setRuleFlowGroup( "rule-flow-group-2" ); rule2.setConsequence( consequence ); rule2.setSalience( new SalienceInteger( 10 ) ); final RuleTerminalNode node2 = new RuleTerminalNode( 5, new MockTupleSource( 2 ), rule2, rule2.getLhs(), buildContext ); final Rule rule3 = new Rule( "test-rule3" ); rule3.setRuleFlowGroup( "rule-flow-group-3" ); rule3.setConsequence( consequence ); final RuleTerminalNode node3 = new RuleTerminalNode( 6, new MockTupleSource( 2 ), rule3, rule3.getLhs(), buildContext ); final PropagationContext context0 = new PropagationContextImpl( 0, PropagationContext.ASSERTION, rule0, null, null ); // nodes final StartNode start = new StartNode(); start.setId(1); final RuleSetNode ruleSet0 = new RuleSetNode(); ruleSet0.setRuleFlowGroup( "rule-flow-group-0" ); ruleSet0.setId(2); final RuleSetNode ruleSet1 = new RuleSetNode(); ruleSet1.setRuleFlowGroup( "rule-flow-group-1" ); ruleSet1.setId(3); final RuleSetNode ruleSet2 = new RuleSetNode(); ruleSet2.setRuleFlowGroup( "rule-flow-group-2" ); ruleSet2.setId(4); final RuleSetNode ruleSet3 = new RuleSetNode(); ruleSet3.setRuleFlowGroup( "rule-flow-group-3" ); ruleSet3.setId(5); final Split split = new Split(); split.setType( Split.TYPE_XOR ); split.setId(6); final Join join = new Join(); join.setType( Join.TYPE_XOR ); join.setId(7); final EndNode end = new EndNode(); end.setId(8); // connections new ConnectionImpl( start, Node.CONNECTION_DEFAULT_TYPE, ruleSet0, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet0, Node.CONNECTION_DEFAULT_TYPE, split, Node.CONNECTION_DEFAULT_TYPE ); Connection out1 = new ConnectionImpl( split, Node.CONNECTION_DEFAULT_TYPE, ruleSet1, Node.CONNECTION_DEFAULT_TYPE ); Connection out2 = new ConnectionImpl( split, Node.CONNECTION_DEFAULT_TYPE, ruleSet2, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet1, Node.CONNECTION_DEFAULT_TYPE, join, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet2, Node.CONNECTION_DEFAULT_TYPE, join, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( join, Node.CONNECTION_DEFAULT_TYPE, ruleSet3, Node.CONNECTION_DEFAULT_TYPE ); new ConnectionImpl( ruleSet3, Node.CONNECTION_DEFAULT_TYPE, end, Node.CONNECTION_DEFAULT_TYPE ); ConstraintEvaluator constraint1 = new org.drools.process.instance.impl.RuleConstraintEvaluator(); constraint1.setPriority( 1 ); split.setConstraint( out1, constraint1 ); ConstraintEvaluator constraint2 = new org.drools.process.instance.impl.RuleConstraintEvaluator(); constraint2.setPriority( 2 ); split.setConstraint( out2, constraint2 ); // process final RuleFlowProcess process = new RuleFlowProcess(); process.setId( "1" ); process.addNode( start ); process.addNode( ruleSet0 ); process.addNode( ruleSet1 ); process.addNode( ruleSet2 ); process.addNode( ruleSet3 ); process.addNode( split ); process.addNode( join ); process.addNode( end ); // rules for split final Rule splitRule1 = new Rule( "RuleFlow-Split-1-" + split.getUniqueId() + "-" + ruleSet1.getUniqueId() + "-" + Node.CONNECTION_DEFAULT_TYPE ); splitRule1.setRuleFlowGroup( "DROOLS_SYSTEM" ); splitRule1.setConsequence( consequence ); final RuleTerminalNode splitNode1 = new RuleTerminalNode( 7, new MockTupleSource( 2 ), splitRule1, splitRule1.getLhs(), buildContext ); final Rule splitRule2 = new Rule( "RuleFlow-Split-1-" + split.getUniqueId() + "-" + ruleSet2.getUniqueId() + "-" + Node.CONNECTION_DEFAULT_TYPE ); splitRule2.setRuleFlowGroup( "DROOLS_SYSTEM" ); splitRule2.setConsequence( consequence ); final RuleTerminalNode splitNode2 = new RuleTerminalNode( 8, new MockTupleSource( 2 ), splitRule2, splitRule2.getLhs(), buildContext ); // proces instance final RuleFlowProcessInstance processInstance = new RuleFlowProcessInstance(); processInstance.setKnowledgeRuntime( workingMemory.getKnowledgeRuntime() ); processInstance.setProcess( process ); assertEquals( ProcessInstance.STATE_PENDING, processInstance.getState() ); final RuleFlowGroupImpl ruleFlowGroup0 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-0" ); final RuleFlowGroupImpl ruleFlowGroup1 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-1" ); final RuleFlowGroupImpl ruleFlowGroup2 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-2" ); final RuleFlowGroupImpl ruleFlowGroup3 = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "rule-flow-group-3" ); final LeftTuple tuple0 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node0.assertLeftTuple( tuple0, context0, workingMemory ); final LeftTuple tuple1 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node0.assertLeftTuple( tuple1, context0, workingMemory ); final LeftTuple tuple2 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node1.assertLeftTuple( tuple2, context0, workingMemory ); final LeftTuple tuple3 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node2.assertLeftTuple( tuple3, context0, workingMemory ); final LeftTuple tuple4 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); node3.assertLeftTuple( tuple4, context0, workingMemory ); final LeftTuple splitTuple1 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); splitNode1.assertLeftTuple( splitTuple1, context0, workingMemory ); final LeftTuple splitTuple2 = new LeftTuple( new DefaultFactHandle( 1, "cheese" ), null, true ); splitNode2.assertLeftTuple( splitTuple2, context0, workingMemory ); final RuleFlowGroupImpl systemRuleFlowGroup = (RuleFlowGroupImpl) agenda.getRuleFlowGroup( "DROOLS_SYSTEM" ); // RuleFlowGroups should be populated, but the agenda shouldn't assertEquals( 2, ruleFlowGroup0.size() ); assertEquals( 1, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 1, ruleFlowGroup3.size() ); assertEquals( 2, systemRuleFlowGroup.size() ); assertEquals( 0, agenda.agendaSize() ); // Activate process instance, the activations stay in the group, // but should now also be in the Agenda processInstance.start(); assertEquals( ProcessInstance.STATE_ACTIVE, processInstance.getState() ); assertEquals( 2, ruleFlowGroup0.size() ); assertEquals( 2, agenda.agendaSize() ); // As we fire each rule they are removed from both the Agenda and the RuleFlowGroup agenda.fireNextItem( null ); assertEquals( 1, ruleFlowGroup0.size() ); assertEquals( 1, agenda.agendaSize() ); // XOR split should activate group1 agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup0.size() ); assertEquals( 1, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 1, agenda.agendaSize() ); // executing group1, XOR join should activate group3 agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 1, ruleFlowGroup3.size() ); assertEquals( 1, agenda.agendaSize() ); // executing rule3, and finishing execution agenda.fireNextItem( null ); workingMemory.executeQueuedActions(); assertEquals( 0, ruleFlowGroup0.size() ); assertEquals( 0, ruleFlowGroup1.size() ); assertEquals( 1, ruleFlowGroup2.size() ); assertEquals( 0, ruleFlowGroup3.size() ); assertEquals( 0, agenda.agendaSize() ); assertEquals( ProcessInstance.STATE_COMPLETED, processInstance.getState() ); } }