Actions and async=
jeffj55374 Aug 9, 2007 2:25 PMorg.jbpm.JbpmException: token '18547' can't be locked by 'job[18548]' cause it's already locked by 'token[18547]'
Full stack trace, process definition, action handler are all below.
Environment
jBPM 3.2.1
Standalone Java 1.6 (no appserver)
Actions and async="true" don't mix.
JobExecutor is configured for one thread.
Hibernate 3.2.4.sp1
Oracle 10g
Standalone Java 1.6 (no appserver)
Actions and async="true" don't mix.
JobExecutor is configured for one thread.
Hibernate 3.2.4.sp1
Oracle 10g
Summary:
1. Token is signaled to start the process.
2. Token transitions to node1 from the start node.
3. Node one has an action handler defined so Node.execute(...) calls GraphElement.executeAction() which locks the token.
4. The action handler calls ctx.leaveNode after it does its work. (Section 9.5 of the user guide states: "Note the difference
between an action that is placed in an event versus an action that is placed in a node. Actions that are put in an event
are executed when the event fires. Actions on events have no way to influence the flow of control of the process.
It is similar to the observer pattern. On the other hand, an action that is put on a node has the responsibility of propagating the execution.
5. ctx.leaveNode on Node1 ultimately results in Node.enter() being called on Node2
6. Since Node2 is an async node, rather than executing the node, the code prepares for an async continuation.
7. After sending the message the code attempts to lock the token, but it already locked as a result of #3
// execute the node
if (isAsync) {
ExecuteNodeJob job = createAsyncContinuationJob(token);
MessageService messageService = (MessageService) Services.getCurrentService(Services.SERVICENAME_MESSAGE);
messageService.send(job);
token.lock(job.toString()); // ** THIS IS THE PROBLEM LINE!!!! ***
} else {
execute(executionContext);
}
The conflict:
Javadoc for Token.lock():
/**
* locks a process instance for further execution. A locked token
* cannot continue execution. This is a non-persistent
* operation. This is used to prevent tokens being propagated during
* the execution of actions.
* @see #unlock(String)
*/
public void lock(String lockOwnerId) {
But Section 9.5 of the user's guide says that an action is responsible for propagating the execution, but the token is locked in an attempt to propagate it.
I'm currently trying to understand the design and come up with a solution to allow my process to run as designed.
What I don't understand yet is why the token is used to prevent propagation or why the code in Node.execute is trying to lock the token with the job id.
Should the code unlock the token and then lock it with the job? Is the token lock really necessary?
If I remove the action fron Node 1, things work fine.
Possibly related to http://jira.jboss.com/jira/browse/JBPM-983. Similar, but a little different twist
Any ideas?
Once I research this a little more, I might submit a bug, but I'd like a little feedback to verify that I'm on the right track.
Thanks for your help.
Full stack trace:
org.jbpm.JbpmException: token '18547' can't be locked by 'job[18548]' cause it's already locked by 'token[18547]'
at org.jbpm.graph.exe.Token.lock(Token.java:646)
at org.jbpm.graph.def.Node.enter(Node.java:316)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$dc89a302.enter()
at org.jbpm.graph.def.Transition.take(Transition.java:151)
at org.jbpm.graph.def.Node.leave(Node.java:393)
at org.jbpm.graph.def.Node.leave(Node.java:357)
at org.jbpm.graph.exe.ExecutionContext.leaveNode(ExecutionContext.java:120)
at com.digitalriver.mds.jbpm.SampleActionHandler.execute(SampleActionHandler.java:46)
at org.jbpm.graph.def.Action.execute(Action.java:122)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Action$$EnhancerByCGLIB$$5863de36.execute()
at org.jbpm.graph.def.GraphElement.executeAction(GraphElement.java:255)
at org.jbpm.graph.def.Node.execute(Node.java:338)
at org.jbpm.graph.def.Node.enter(Node.java:318)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$dc89a302.enter()
at org.jbpm.graph.def.Transition.take(Transition.java:151)
at org.jbpm.graph.def.Node.leave(Node.java:393)
at org.jbpm.graph.node.StartState.leave(StartState.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$dc89a302.leave()
at org.jbpm.graph.exe.Token.signal(Token.java:194)
at org.jbpm.graph.exe.Token.signal(Token.java:139)
at com.digitalriver.proto.StandaloneSample.runStandaloneSample(StandaloneSample.java:89)
at com.digitalriver.proto.StandaloneSample.run(StandaloneSample.java:130)
at com.digitalriver.proto.StandaloneSample.main(StandaloneSample.java:40)
The Job Executor bean config
<bean id="jbpm.job.executor" class="org.jbpm.job.executor.JbpmJobExecutor"> <property name="jbpmConfiguration" ref="jbpmConfiguration" /> <property name="name" value="JbpmJobExector"/> <property name="nbrOfThreads" value="1"/> <property name="idleInterval" value="5000"/> <property name="maxIdleInterval" value="300000"/> <property name="historyMaxSize" value="20"/> <property name="maxLockTime" value="600000"/> <!-- 10 minutes --> <property name="lockMonitorInterval" value="60000"/> <!-- 1 minute --> <property name="lockBufferTime" value="5000"/> <!-- 5 seconds --> </bean>
The Process Definition
<?xml version="1.0" encoding="UTF-8"?> <process-definition xmlns="" name="StandaloneSample"> <start-state name="start"> <transition name="" to="node1"></transition> </start-state> <end-state name="end1"></end-state> <node name="node1"> <action class="com.digitalriver.mds.jbpm.SampleActionHandler"></action> <transition name="" to="node2"></transition> </node> <node async="true" name="node2"> <action class="com.digitalriver.mds.jbpm.SampleActionHandler"></action> <transition name="" to="end1"></transition> </node> </process-definition>
The action handler
package com.digitalriver.mds.jbpm; /** Copyright (c) Digital River, Inc. All rights reserved. 2007 * Filename: SampleActionHandler.java * Author: @author */ import java.io.FileWriter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jbpm.context.exe.ContextInstance; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.exe.ExecutionContext; /** * @author jpjohnson * */ public class SampleActionHandler implements ActionHandler { /** * */ private static final long serialVersionUID = 1L; @SuppressWarnings("unused") private static final Log log = LogFactory.getLog(SampleActionHandler.class); /* (non-Javadoc) * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) */ public void execute(final ExecutionContext ctx) throws Exception { log.debug("JPJ execute called for task " + ctx.getNode().getName() + " action " + ctx.getAction().getName()); ContextInstance contextInstance = ctx.getProcessInstance().getContextInstance(); Integer id = (Integer) contextInstance.getVariable("MyID"); FileWriter file = new FileWriter("Node-" + ctx.getNode().getName() + " PI-" + ctx.getProcessInstance().getId() + ".txt"); file.write("Test output: Process ID " + id); file.close(); ctx.leaveNode(); // ctx.getToken().signal(); } }