I am seeing a problem in the latest stable build 12/27 andn
1/26 where a flow variable does not get updates from a facts updated in a rule
node in the flow. Both the flow variable and the fact are initially the
same object instance, but It looks like the flow variable gets a cloned
instance after the rule node.
I am inserting 1 fact and running a flow with a debug node,
a rule node, a debug node, and a human task. As you can see below the
hashcode changes for the flow variable after the rule node, but retains the
user value set earlier in the flow. After the rule node the user should
be myuser which is set in the rule, but instead has the value bogus, which is
set in the first debug node.
Is anyone else seeing similar issues in drools flow?
This is happening when we run in JBoss 5.1. This is a blocking
issue that makes drools flow unusable for us, so any help you can provide is
appreciated.
Debug:
15:00:31,985 INFO [STDOUT]
******** flowApproval hashcode at fact insert:18886777
15:00:31,985 INFO [STDOUT]
Hibernate: insert into ProcessInstanceInfo (externalVariables,
lastModificationDate, lastReadDate, processId, processInstanceByteArray,
startDate, state, OPTLOCK) values (?, ?, ?, ?, ?, ?, ?, ?)
15:00:31,985 INFO [STDOUT]
******** first flow node, hashcode: 18886777 user: null
15:00:31,985 INFO [STDOUT]
******** first flow node after approver set to bogus, hashcode: 18886777
user: bogus
15:00:32,000 INFO [STDOUT]
Hibernate: update ProcessInstanceInfo set externalVariables=?,
lastModificationDate=?, lastReadDate=?, processId=?,
processInstanceByteArray=?, startDate=?, state=?, OPTLOCK=? where
processInstanceId=? and OPTLOCK=?
15:00:32,000 INFO [STDOUT]
Hibernate: insert into ProcessInstanceInfo_eventTypes
(ProcessInstanceInfo_processInstanceId, element) values (?, ?)
15:00:32,000 INFO [STDOUT]
Hibernate: update SessionInfo set dirty=?, lastModificationDate=?,
rulesByteArray=?, startDate=? where id=?
15:00:32,000 INFO [STDOUT]
******** in rule node setting approver to enduser, hashcode: 18886777 user:
myuser
15:00:32,000 INFO [STDOUT]
Hibernate: select processins0_.processInstanceId as col_0_0_ from
ProcessInstanceInfo processins0_ where ? in (select eventtypes1_.element from
ProcessInstanceInfo_eventTypes eventtypes1_ where
processins0_.processInstanceId=eventtypes1_.ProcessInstanceInfo_processInstanceId)
15:00:32,000 INFO [STDOUT]
Hibernate: select processins0_.processInstanceId as processI1_149_0_,
processins0_.externalVariables as external2_149_0_,
processins0_.lastModificationDate as lastModi3_149_0_, processins0_.lastReadDate
as lastRead4_149_0_, processins0_.processId as processId149_0_,
processins0_.processInstanceByteArray as processI6_149_0_,
processins0_.startDate as startDate149_0_, processins0_.state as state149_0_,
processins0_.OPTLOCK as OPTLOCK149_0_ from ProcessInstanceInfo processins0_
where processins0_.processInstanceId=?
15:00:32,016 INFO [STDOUT] ******** after rule node,
hashcode:15392910 user: bogus
Rule:
dialect "mvel"
rule "Assign Initial
Approver"
ruleflow-group "approver"
no-loop
when
$flow
: com.datacert.FlowApproval()
then
modify( $flow ) {
setLastapprover( 'myuser' ) };
System.out.println("********
in rule node setting approver to enduser, hashcode: " +
$flow.hashCode() + " user: " + $flow.getLastapprover());
end
Flow:
<?xml version="1.0"
encoding="UTF-8"?>
<definitions id="Definition"
targetNamespace="http://www.jboss.org/drools"
typeLanguage="http://www.java.com/javaTypes"
expressionLanguage="http://www.mvel.org/2.0"
xmlns="http://schema.omg.org/spec/BPMN/2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://schema.omg.org/spec/BPMN/2.0 BPMN20.xsd"
xmlns:g="http://www.jboss.org/drools/flow/gpd"
xmlns:tns="http://www.jboss.org/drools">
<itemDefinition
id="entityIdItem" structureRef="java.lang.Object" />
<itemDefinition id="entityItem"
structureRef="java.lang.Object" />
<itemDefinition
id="flowApprovalItem" structureRef="java.lang.Object" />
<resource id="Actor"
name="Human Actor" />
<process
id="com.datacert.workflow.Base" name="approval" >
<!-- process variables -->
<property
id="entityId" itemSubjectRef="tns:entityIdItem"/>
<property
id="entity" itemSubjectRef="tns:entityItem"/>
<property
id="flowApproval"
itemSubjectRef="tns:flowApprovalItem"/>
<!-- nodes -->
<endEvent id="_2"
name="End" g:x="564" g:y="16"
g:width="48" g:height="48" />
<userTask id="_4"
name="Human Task" g:x="432" g:y="16"
g:width="100" g:height="48" >
<ioSpecification>
<dataInput id="_4_CommentInput" name="Comment" />
<dataInput id="_4_TaskNameInput" name="TaskName" />
<dataInput id="_4_ContentInput" name="Content" />
<inputSet>
<dataInputRefs>_4_CommentInput</dataInputRefs>
<dataInputRefs>_4_TaskNameInput</dataInputRefs>
<dataInputRefs>_4_ContentInput</dataInputRefs>
</inputSet>
<outputSet>
</outputSet>
</ioSpecification>
<property
id="_4_Comment" />
<property
id="_4_TaskName" />
<property
id="_4_Content" />
<dataInputAssociation>
<assignment>
<from xs:type="tFormalExpression">workitem</from>
<to xs:type="tFormalExpression">_4_CommentInput</to>
</assignment>
<sourceRef>_4_Comment</sourceRef>
<targetRef>_4_CommentInput</targetRef>
</dataInputAssociation>
<dataInputAssociation>
<assignment>
<from xs:type="tFormalExpression">Human Task</from>
<to xs:type="tFormalExpression">_4_TaskNameInput</to>
</assignment>
<sourceRef>_4_TaskName</sourceRef>
<targetRef>_4_TaskNameInput</targetRef>
</dataInputAssociation>
<dataInputAssociation>
<assignment>
<from xs:type="tFormalExpression">#{entityId}</from>
<to xs:type="tFormalExpression">_4_ContentInput</to>
</assignment>
<sourceRef>_4_Content</sourceRef>
<targetRef>_4_ContentInput</targetRef>
</dataInputAssociation>
<potentialOwner
resourceRef="tns:Actor" >
<resourceAssignmentExpression>
<formalExpression>#{flowApproval.getLastapprover()}</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<scriptTask id="_6"
name="After Rule" g:x="320" g:y="16"
g:width="80" g:height="48" >
<script>System.out.println("******** after rule node, hashcode:" + flowApproval.hashCode() +
" user: " + flowApproval.getLastapprover());</script>
</scriptTask>
<startEvent id="_1"
name="StartProcess" g:x="16" g:y="16"
g:width="48" g:height="48" />
<businessRuleTask
id="_3" name="Rule" g:x="208" g:y="16"
g:width="80" g:height="48"
g:ruleFlowGroup="approver" />
<scriptTask id="_7"
name="Before Rule" g:x="96" g:y="16"
g:width="80" g:height="48" >
<script>System.out.println("******** first flow node, hashcode: " + flowApproval.hashCode() +
" user: " + flowApproval.getLastapprover());
flowApproval.setLastapprover("bogus");
System.out.println("******** first flow node
after approver set to bogus, hashcode:
" + flowApproval.hashCode() + " user: " +
flowApproval.getLastapprover());</script>
</scriptTask>
<!-- connections -->
<sequenceFlow
sourceRef="_4" targetRef="_2" />
<sequenceFlow
sourceRef="_6" targetRef="_4" />
<sequenceFlow
sourceRef="_3" targetRef="_6" />
<sequenceFlow
sourceRef="_7" targetRef="_3" />
<sequenceFlow
sourceRef="_1" targetRef="_7" />
</process>
</definitions>
Most
Code:
package
com.datacert;
import
groovy.lang.GroovyClassLoader;
import
java.util.HashMap;
import
java.util.Map;
import
java.util.Properties;
import
javax.annotation.Resource;
import
javax.naming.InitialContext;
import
javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
import
javax.persistence.Persistence;
import
org.apache.log4j.Level;
import
org.drools.KnowledgeBase;
import
org.drools.KnowledgeBaseConfiguration;
import
org.drools.KnowledgeBaseFactory;
import org.drools.SessionConfiguration;
import
org.drools.SystemEventListenerFactory;
import
org.drools.base.MapGlobalResolver;
import
org.drools.bpmn2.xml.BPMNSemanticModule;
import
org.drools.builder.KnowledgeBuilder;
import
org.drools.builder.KnowledgeBuilderConfiguration;
import
org.drools.builder.KnowledgeBuilderError;
import
org.drools.builder.KnowledgeBuilderErrors;
import
org.drools.builder.KnowledgeBuilderFactory;
import
org.drools.builder.ResourceType;
import
org.drools.compiler.PackageBuilderConfiguration;
import
org.drools.io.ResourceFactory;
import
org.drools.logger.KnowledgeRuntimeLogger;
import
org.drools.logger.KnowledgeRuntimeLoggerFactory;
import
org.drools.persistence.jpa.JPAKnowledgeService;
import
org.drools.process.workitem.wsht.CommandBasedWSHumanTaskHandler;
import
org.drools.runtime.Environment;
import
org.drools.runtime.EnvironmentName;
import
org.drools.runtime.KnowledgeSessionConfiguration;
import
org.drools.runtime.StatefulKnowledgeSession;
import
org.drools.runtime.process.ProcessInstance;
import
org.drools.runtime.process.WorkItemManager;
import
org.drools.task.service.MinaTaskServer;
import
org.drools.task.service.TaskService;
import
com.arjuna.ats.jta.TransactionManager;
public class
FlowService {
private static final String
FLOW_SERVICE_URL = "flow.service.url";
private static final String FLOW_SERVICE_LOCATION =
"flow.service.location";
private TaskService
taskService;
private MinaTaskServer server;
private EntityManagerFactory taskServerEntityManagerFactory;
public void startWorkflow() {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("entityId", 22);
parameters.put("flowApproval", new FlowApproval());
KnowledgeBase kbase = getKnowledgeBase("approval.bpmn",
"approval.drl");
StatefulKnowledgeSession ksession = getSession(kbase);
KnowledgeRuntimeLogger logger =
KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
registerHumanTaskHandler(ksession);
ksession.insert(parameters.get("flowApproval"));
System.out.println("******** flowApproval hashcode at fact insert:" +
parameters.get("flowApproval").hashCode());
ProcessInstance pi =
ksession.startProcess("com.datacert.workflow.Base", parameters);
ksession.fireAllRules();
}
private ClassLoader getClassLoader() {
GroovyClassLoader gcl = new GroovyClassLoader();
gcl.addClasspath("c:\\temp\\");
return gcl;
}
private Environment getEnvironment() {
Environment env = KnowledgeBaseFactory.newEnvironment();
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("org.drools.persistence.jpa");
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
env.set(EnvironmentName.GLOBALS, new MapGlobalResolver());
InitialContext ctx = null;
try {
ctx
= new InitialContext();
} catch (NamingException e) {
System.err.println(e);
}
env.set(EnvironmentName.TRANSACTION_MANAGER,
TransactionManager.transactionManager(ctx));
return env;
}
private KnowledgeBase getKnowledgeBase(String xmlFileName, String ruleFileName)
{
KnowledgeBaseConfiguration config =
KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, getClassLoader());
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(config);
kbase.addKnowledgePackages(getKnowledgeBuilder(xmlFileName,
ruleFileName).getKnowledgePackages());
return kbase;
}
private KnowledgeBuilder getKnowledgeBuilder(String xmlFileName, String
ruleFileName) {
Thread.currentThread().setContextClassLoader(getClassLoader());
System.setProperty("drools.dialect.mvel.strict", "false");
KnowledgeBuilderConfiguration conf =
KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null,
getClassLoader());
((PackageBuilderConfiguration) conf).initSemanticModules();
((PackageBuilderConfiguration) conf).addSemanticModule(new
BPMNSemanticModule());
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(conf);
kbuilder.add(ResourceFactory.newClassPathResource("approval.bpmn"),
ResourceType.DRF);
kbuilder.add(ResourceFactory.newClassPathResource("approval.drl"),
ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error : errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
return kbuilder;
}
private StatefulKnowledgeSession getSession(int sessionId, KnowledgeBase kbase)
{
return JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase,
getSessionConfig(), getEnvironment());
}
private StatefulKnowledgeSession getSession(KnowledgeBase kbase) {
return JPAKnowledgeService.newStatefulKnowledgeSession(kbase,
getSessionConfig(), getEnvironment());
}
private KnowledgeSessionConfiguration getSessionConfig() {
SessionConfiguration c = new SessionConfiguration();
c.setClassLoader(getClassLoader());
return c;
}
private void registerHumanTaskHandler(StatefulKnowledgeSession ksession) {
CommandBasedWSHumanTaskHandler approveTask = new
CommandBasedWSHumanTaskHandler(ksession);
WorkItemManager wm = ksession.getWorkItemManager();
wm.registerWorkItemHandler("Human Task", approveTask);
}
}
Thanks,
Todd