I found a much easier way to recreate the problem described in my previous
post. I believe I have also found the root cause of the problem. Below are
the steps to recreate the issue:
- Create a simple ruleflow and rules and compile them into a rule package.
- In code create a stateless session
- In a loop
o Create a fact
o Create a new execution batch with the following commands
- Insert the fact from above
- Start Process
- Fire All Rules
- Get Objects
o Execute the batch
o Check the results (number of rules that ran)
I posted before that the problem didn’t happen while executing locally (not
in drools server). I didn’t realize this before, but adding a loop that
executes the rules multiple times with the same session recreates the
problem.
Below is code that will recreate the issue. Notice that the code below
creates one instance of ksession and reuses it in the loop.
//Setup
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
//The package includes the ruleflow
kbuilder.add( ResourceFactory.newFileResource(
("YourRulePackage_WithRuleflow.pkg") ), ResourceType.PKG );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
* //Create the stateless knowledge session outside the for loop
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();*
//Loop through executing the rules with the same data 3 times
for (int x = 0; x < 3; x++)
{
//Create a new instance of the input fact
FactType inputDataType = kbase.getFactType("SimpleRuleflowTest",
"Input");
Object inputData = inputDataType.newInstance();
inputDataType.set(inputData, "Name", "Test data");
//Create a new instance of the command list
List cmds = new ArrayList();
cmds.add( CommandFactory.newInsert( inputData ));
cmds.add( CommandFactory.newStartProcess( "TestRuleflow"));
cmds.add( CommandFactory.newFireAllRules("rules"));
cmds.add( CommandFactory.newGetObjects("output"));
//Execute the rules
ExecutionResults results = ksession.execute(
CommandFactory.newBatchExecution( cmds ) );
//Get the number of rules that ran
Object rules = results.getValue("rules");
System.out.println("Rules that ran: " + rules.toString());
}
Each iteration through the loop should fire 3 rules. Running the code above
you should get the following output:
Rules that ran: 3
Rules that ran: 1
Rules that ran: 1
I spent several hours researching the drools source code and I believe I
have found the problem. What I have found is related to Issue 2718
<
https://issues.jboss.org/browse/JBRULES-2718> .
Check out the change made for this issue: 2718 fix
<
https://source.jboss.org/viewrep/Drools/drools-core/src/main/java/org/dro...
.
Below is a snippet from the code that was modified for issue 2718. The “if
(!initialized)” block was added. The code loops through event listeners on
the wm object and adds them to the stateless session’s listeners. This
works fine for the first rule execution. But the next rule execution
creates a new instance for wm. Therefore it also creates new instances of
listeners for wm. So we have a new instance of wm and the stateless session
is pointing to the old listeners from the first instance of wm. I believe
that is why it only runs correctly one time. It seems that that the “if
(!initialized)” block of code needs to execute every time newWorkingMemory()
is called.
public StatefulKnowledgeSession newWorkingMemory() {
:
:
ReteooWorkingMemory wm = new ReteooWorkingMemory(
this.ruleBase.nextWorkingMemoryCounter(),
this.ruleBase,
(SessionConfiguration) this.conf,
this.environment );
:
:
*if (!initialized) {*
// copy over the default generated listeners that are used for internal
stuff once
for (org.drools.event.AgendaEventListener listener:
wm.getAgendaEventSupport().getEventListeners()) {
this.agendaEventSupport.addEventListener(listener);
}
for (org.drools.event.WorkingMemoryEventListener listener:
wm.getWorkingMemoryEventSupport().getEventListeners()) {
this.workingMemoryEventSupport.addEventListener(listener);
}
InternalProcessRuntime processRuntime = wm.getProcessRuntime();
if (processRuntime != null) {
for (ProcessEventListener listener:
processRuntime.getProcessEventListeners()) {
this.processEventSupport.addEventListener(listener);
}
}
initialized = true;
}
:
:
}
So, I tested this theory in my test function. I used reflection to flip the
“initialized” flag at the bottom of my loop. I did this to force the
listeners of the stateless session to be refreshed for every execute call.
It worked! The other nodes in the ruleflow now get executed after the first
rule execution. Below is a snippet of my modified test code:
//Same As Above
:
:
//Create the stateless knowledge session outside the for loop
StatelessKnowledgeSession ksession =
kbase.newStatelessKnowledgeSession();
//Loop through executing the rules with the same data 3 times
for (int x = 0; x < 3; x++)
{
:
:
//Flip the initialized flag to false through reflection*
Field field =
StatelessKnowledgeSessionImpl.class.getDeclaredField("initialized");
field.setAccessible(true);
field.setBoolean(ksession, false); *
}
Running the code above you should get the following output:
Rules that ran: 3
Rules that ran: 3
Rules that ran: 3
My original problem deals with drools-server. It isn’t possible for me to
flip the initialized flag to false in that scenario. So, is there any sort
of workaround that would get me past this issue when using drools-server?
--
View this message in context:
http://drools.46999.n3.nabble.com/Ruleflow-not-working-with-stateless-ses...
Sent from the Drools: User forum mailing list archive at
Nabble.com.