-
1. Re: Token.signal() ---> 2 tokens?
qpool.char Jan 31, 2008 3:29 PM (in response to qpool.char)I mean, i move my token to the fork.
Then again token.signal()
Then i check withCollection c = jbpmContext.getTaskMgmtSession().findTaskInstancesByToken(token.getId()); log.info("************ Number of TaskInstances for Token: " + c.size());
the number of available TaskInstances for this token, and the number is 1.
But i m expecting 2.....
thx in advance -
2. Re: Token.signal() ---> 2 tokens?
kukeltje Jan 31, 2008 5:38 PM (in response to qpool.char)Don't 'expect', be sure. So probably there is more than one token. Look at the unit tests in cvs to see how the internals of jbpm work for forks. Unit tests are so great...
-
3. Re: Token.signal() ---> 2 tokens?
kukeltje Jan 31, 2008 6:15 PM (in response to qpool.char)I should not have kept you in the dark.... there are 2 tokens...
-
4. Re: Token.signal() ---> 2 tokens?
qpool.char Feb 1, 2008 8:20 AM (in response to qpool.char)thx :)
-
5. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 20, 2008 12:54 PM (in response to qpool.char)In a similar scenario, I find that following the fork, two new tokens proceed along each of the (2) transitions leaving the fork to their respective nodes - however, the 'root' token then 'follows' one of the other tokens and also enters the same node, and generates a node-enter event.
Generally, I could track the progress of the root token to see the progress along the execution path - but following the fork, it then looks like there are 2 executions of the path.
I can't figure how I can determine which token is the 'active' one on an execution path, and which one is isn't. (Because I would not expect both tokens on that same execution path to be 'active'.) -
6. Re: Token.signal() ---> 2 tokens?
kukeltje Feb 21, 2008 5:26 AM (in response to qpool.char)please make a unit test so we can see the problem and what you specifically do.
-
7. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 21, 2008 6:29 AM (in response to qpool.char)Process definition:
<?xml version="1.0" encoding="UTF-8"?> <process-definition xmlns="" name="ForkTest"> <start-state name="startstate"> <task name="NewProcess"> <controller> <variable access="read,write,required" name="choice"></variable> </controller> </task> <transition to="decisonnode" name="ok"></transition> </start-state> <decision name="decisonnode"> <transition to="optiona" name="choose a"> <condition>#{choice == "OptionA"}</condition> </transition> <transition to="optionb" name="choose b"> <condition>#{choice == "OptionB"}</condition> </transition> </decision> <fork name="fork1"> <transition to="track" name="t10"></transition> <transition to="proceed" name="t20"></transition> </fork> <fork name="fork2" async="true"> <transition to="monitor" name="t1"></transition> <transition to="step1" name="t2"></transition> </fork> <join name="join1"> <transition to="join2"></transition> </join> <join name="join2"> <transition to="complete"></transition> </join> <state name="optiona"> <transition to="nextstate" name="ok"></transition> </state> <state name="nextstate"> <transition to="fork1" name="ok"></transition> </state> <state name="optionb"> <transition to="nextstate" name="ok"></transition> </state> <state name="proceed" async="true"> <transition to="fork2" name="ok"></transition> </state> <state name="track" async="true"> <transition to="join2" name="ok"></transition> </state> <state name="monitor" async="true"> <transition to="join1" name="ok"></transition> </state> <state name="step1" async="true"> <transition to="step2" name="ok"></transition> </state> <state name="step2" async="true"> <transition to="step3" name="ok"></transition> </state> <state name="complete"> <transition to="end" name="ok"></transition> </state> <state async="true" name="step3"> <transition to="join1" name="ok"></transition> </state> <end-state name="end"></end-state> <action name="EnterNode" class="taskmanager.EnterStateHandler"></action> <action name="LeaveNode" class="taskmanager.ExitStateHandler"></action> <event type="node-enter"> <action class="taskmanager.EnterStateHandler" ref-name="EnterNode"></action> </event> <event type="node-leave"> <action ref-name="LeaveNode"></action> </event> </process-definition>
Unit test:package forktest; import java.util.Map; import junit.framework.TestCase; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.context.exe.ContextInstance; import org.jbpm.graph.def.Node; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.jbpm.persistence.db.DbPersistenceServiceFactory; import org.jbpm.svc.Services; import org.jbpm.taskmgmt.exe.TaskInstance; public class ForkUnitTest extends TestCase { /** * Some helpers */ JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance(); DbPersistenceServiceFactory dbPersistenceServiceFactory = (DbPersistenceServiceFactory) jbpmConfiguration.getServiceFactory(Services.SERVICENAME_PERSISTENCE); JbpmContext jbpmContext; private ProcessInstance processInstance; private long processInstanceId; private ContextInstance contextInstance; // private TaskMgmtInstance taskMgmtInstance; public ForkUnitTest() { } /** * Set up the basic in-memory DB, deploy the process description and * set up a jBPM context. * @see jBPM docs wrt jbpm configuration file */ protected void setUp() throws Exception { super.setUp(); dbPersistenceServiceFactory.createSchema(); deployProcess(); jbpmConfiguration.startJobExecutor(); jbpmContext = jbpmConfiguration.createJbpmContext(); } /** * Tidy up. */ protected void tearDown() throws Exception { super.tearDown(); jbpmContext.close(); dbPersistenceServiceFactory.dropSchema(); jbpmContext = null; } /** * */ public void newTransaction() { jbpmContext.close(); jbpmContext = jbpmConfiguration.createJbpmContext(); processInstance = jbpmContext.loadProcessInstanceForUpdate(processInstanceId); // processInstanceId = processInstance.getId(); } /** * Get the process description file, parse it and deploy the * process definition. */ private void deployProcess() { JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("ForkProcess/processdefinition.xml"); jbpmContext.deployProcessDefinition(processDefinition); } finally { jbpmContext.close(); } } /** * Each subsequent test will need a new process instance * @return a TaskInstance, or null if there is no start task defined. */ private TaskInstance createNewProcessInstance() { processInstance = jbpmContext.newProcessInstanceForUpdate("ForkTest"); processInstanceId = processInstance.getId(); contextInstance = processInstance.getContextInstance(); // taskMgmtInstance = processInstance.getTaskMgmtInstance(); return processInstance.getTaskMgmtInstance().createStartTaskInstance(); } public void testFork() { TaskInstance taskInstance = createNewProcessInstance(); contextInstance.setVariable("choice", "OptionB"); assertEquals("startstate", processInstance.getRootToken().getNode().getName()); processInstance.signal(); jbpmContext.save(processInstance); newTransaction(); assertEquals("optionb", processInstance.getRootToken().getNode().getName()); processInstance.signal("ok"); jbpmContext.save(processInstance); newTransaction(); assertEquals("nextstate", processInstance.getRootToken().getNode().getName()); processInstance.signal("ok"); jbpmContext.save(processInstance); newTransaction(); Token root = processInstance.getRootToken(); root.signal(); Map map = root.getChildren(); assertEquals(2, map.size()); Token tk_t10 = (Token) map.get("t10"); assertEquals("t10", tk_t10.getName()); ProcessInstance pi = tk_t10.getProcessInstance(); assertEquals(processInstanceId, pi.getId()); Node node = tk_t10.getNode(); assertEquals("track", node.getName()); tk_t10.signal("ok"); jbpmContext.save(processInstance); newTransaction(); root = processInstance.getRootToken(); map = root.getChildren(); assertEquals(2, map.size()); tk_t10 = (Token) map.get("t10"); assertEquals("t10", tk_t10.getName()); Token tk_t20 = (Token) map.get("t20"); assertEquals("t20", tk_t20.getName()); pi = tk_t20.getProcessInstance(); assertEquals(processInstanceId, pi.getId()); node = tk_t20.getNode(); assertEquals("proceed", node.getName()); tk_t20.signal(); assertEquals("fork2", processInstance.getRootToken().getNode().getName()); } }
And the two handlers:/** * */ package forktest; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.Node; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.Token; import org.jbpm.graph.node.State; /** * @author pauls * */ public class EnterStateHandler implements ActionHandler { /** * */ private static final long serialVersionUID = 7209848070013962714L; /* (non-Javadoc) * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) */ public void execute(ExecutionContext executionContext) throws Exception { Node node = executionContext.getNode(); Token token = executionContext.getToken(); String tok_name = (token.getName() == null ? "root" : token.getName()); if (token.isLocked()) { System.out.println("Token: " + tok_name + " is locked"); } if (token.isSuspended()) { System.out.println("Token: " + tok_name + " is suspended"); } if (node instanceof State) { System.out.println("Token: " + tok_name + " entering node: " + node.getName()); } else { System.out.println("Node is: " + node.getName()); } } } /** * */ package forktest; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.Node; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.Token; import org.jbpm.graph.node.State; /** * @author pauls * */ public class ExitStateHandler implements ActionHandler { /** * */ private static final long serialVersionUID = 7209848070013962714L; /* (non-Javadoc) * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) */ public void execute(ExecutionContext executionContext) throws Exception { Node node = executionContext.getNode(); Token token = executionContext.getToken(); String tok_name = (token.getName() == null ? "root" : token.getName()); if (node instanceof State) { System.out.println("Token: " + tok_name + " leaving node: " + node.getName()); } else { System.out.println("Node was: " + node.getName()); } } } [/CODE] And on the console the output from the handlers: Node was: startstate Token: root is locked Node is: decisonnode Node was: decisonnode Token: root is locked Token: root entering node: optionb Token: root leaving node: optionb Token: root is locked Token: root entering node: nextstate Token: root leaving node: nextstate Token: root is locked Node is: fork1 Node was: fork1 Token: t10 is locked Token: t10 entering node: track Node was: fork1 Token: t20 is locked Token: t20 entering node: proceed Node was: fork1 Token: root is locked Token: root entering node: track
And it's that last line that's bugging me. -
9. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 21, 2008 7:29 AM (in response to qpool.char)Correction. In the process defintion the actions should be defined as follows:
<action name="EnterNode" class="forktest.EnterStateHandler"></action> <action name="LeaveNode" class="forktest.ExitStateHandler"></action>
-
10. Re: Token.signal() ---> 2 tokens?
kukeltje Feb 21, 2008 8:38 AM (in response to qpool.char)I do not see strange things in here. The only thing that surprises me is that token.getName() is null for the root token....
What version of jBPM are you using? I'll try to reproduce tonight... btw -
11. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 21, 2008 9:26 AM (in response to qpool.char)"kukeltje" wrote:
There may be nothing 'strange', but I wasn't expecting multiple tokens to pass through any node.
I do not see strange things in here.
I'm looking to use 'client base assignment' (see http://www.jboss.com/products/jbpm/docs/jpdl) and use jBPM as the execution engine. Since I then want to hang actions off the node-enter/leave events, I need to know which token is the 'active' execution path.
In the example supplied, the root token that 'follows' the 't10' token is - in effect - creating a second execution path through the same node, unless there is some way to distinguish when the root token is the active token on the execution path and when it is not. (I am assuming that following the 'fork' that the root token - being the parent of the other 2 - is no longer the 'active' token'?)"kukeltje" wrote:
The only thing that surprises me is that token.getName() is null for the root token....
Yeah, that threw me too."kukeltje" wrote:
What version of jBPM are you using? I'll try to reproduce tonight... btw
I downloaded jbpm-jpdl-suite-3.2.2.zip -
12. Re: Token.signal() ---> 2 tokens?
mputz Feb 21, 2008 9:46 AM (in response to qpool.char)IMO, you shouldn't signal the root token once it has arrived in the fork:
Token root = processInstance.getRootToken(); // root token is already on the fork, signal children instead // root.signal();
I've found a (rejected) feature request to prevent the root token from being signaled on a fork node: http://jira.jboss.com/jira/browse/JBPM-642 -
13. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 21, 2008 11:15 AM (in response to qpool.char)"mputz" wrote:
IMO, you shouldn't signal the root token once it has arrived in the fork:Token root = processInstance.getRootToken(); // root token is already on the fork, signal children instead // root.signal();
OK, but how should the root token progress to the matching join? And what about the example I've given where on of the execuation paths forks again?
What is the logic to determine when I can signal which tokens? In my test - see below - from fork1, I would expect to be signalling token t20 - but when that arrives at fork2, should I cease signalling t20? Then how is it to join finally?"mputz" wrote:
I've found a (rejected) feature request to prevent the root token from being signaled on a fork node: http://jira.jboss.com/jira/browse/JBPM-642
I'll be reading that shortly...
It may explain why I can get the root token to the last state in the process when it's children haven't all joined yet. Seems to me that the join is operating like a discriminator..package forktest; import java.util.Map; import junit.framework.TestCase; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.context.exe.ContextInstance; import org.jbpm.graph.def.Node; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.jbpm.persistence.db.DbPersistenceServiceFactory; import org.jbpm.svc.Services; import org.jbpm.taskmgmt.exe.TaskInstance; public class ForkUnitTest extends TestCase { /** * Some helpers */ JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance(); DbPersistenceServiceFactory dbPersistenceServiceFactory = (DbPersistenceServiceFactory) jbpmConfiguration.getServiceFactory(Services.SERVICENAME_PERSISTENCE); JbpmContext jbpmContext; private ProcessInstance processInstance; private long processInstanceId; private ContextInstance contextInstance; // private TaskMgmtInstance taskMgmtInstance; public ForkUnitTest() { } /** * Set up the basic in-memory DB, deploy the process description and * set up a jBPM context. * @see jBPM docs wrt jbpm configuration file */ protected void setUp() throws Exception { super.setUp(); dbPersistenceServiceFactory.createSchema(); deployProcess(); jbpmConfiguration.startJobExecutor(); jbpmContext = jbpmConfiguration.createJbpmContext(); } /** * Tidy up. */ protected void tearDown() throws Exception { super.tearDown(); jbpmContext.close(); dbPersistenceServiceFactory.dropSchema(); jbpmContext = null; } /** * */ public void newTransaction() { jbpmContext.close(); jbpmContext = jbpmConfiguration.createJbpmContext(); processInstance = jbpmContext.loadProcessInstanceForUpdate(processInstanceId); // processInstanceId = processInstance.getId(); } /** * Get the process description file, parse it and deploy the * process definition. */ private void deployProcess() { JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("ForkProcess/processdefinition.xml"); jbpmContext.deployProcessDefinition(processDefinition); } finally { jbpmContext.close(); } } /** * Each subsequent test will need a new process instance * @return a TaskInstance, or null if there is no start task defined. */ private TaskInstance createNewProcessInstance() { processInstance = jbpmContext.newProcessInstanceForUpdate("ForkTest"); processInstanceId = processInstance.getId(); contextInstance = processInstance.getContextInstance(); // taskMgmtInstance = processInstance.getTaskMgmtInstance(); return processInstance.getTaskMgmtInstance().createStartTaskInstance(); } public void testFork() { TaskInstance taskInstance = createNewProcessInstance(); contextInstance.setVariable("choice", "OptionB"); assertEquals("startstate", processInstance.getRootToken().getNode().getName()); processInstance.signal(); jbpmContext.save(processInstance); newTransaction(); assertEquals("optionb", processInstance.getRootToken().getNode().getName()); processInstance.signal("ok"); jbpmContext.save(processInstance); newTransaction(); assertEquals("nextstate", processInstance.getRootToken().getNode().getName()); processInstance.signal("ok"); jbpmContext.save(processInstance); newTransaction(); Token root = processInstance.getRootToken(); root.signal(); jbpmContext.save(processInstance); newTransaction(); root = processInstance.getRootToken(); Map map = root.getChildren(); assertEquals(2, map.size()); Token tk_t10 = (Token) map.get("t10"); assertEquals("t10", tk_t10.getName()); ProcessInstance pi = tk_t10.getProcessInstance(); assertEquals(processInstanceId, pi.getId()); Node node = tk_t10.getNode(); assertEquals("track", node.getName()); // tk_t10.signal("ok"); jbpmContext.save(processInstance); newTransaction(); root = processInstance.getRootToken(); map = root.getChildren(); assertEquals(2, map.size()); tk_t10 = (Token) map.get("t10"); assertEquals("t10", tk_t10.getName()); Token tk_t20 = (Token) map.get("t20"); assertEquals("t20", tk_t20.getName()); pi = tk_t20.getProcessInstance(); assertEquals(processInstanceId, pi.getId()); node = tk_t20.getNode(); assertEquals("proceed", node.getName()); tk_t20.signal(); node = tk_t20.getNode(); assertEquals("fork2", node.getName()); map = tk_t20.getChildren(); assertEquals(2, map.size()); assertEquals("track", processInstance.getRootToken().getNode().getName()); node = tk_t10.getNode(); assertEquals("track", node.getName()); tk_t10.signal("ok"); node = tk_t10.getNode(); assertEquals("join2", node.getName()); jbpmContext.save(processInstance); newTransaction(); root = processInstance.getRootToken(); root.signal(); assertEquals("join2", processInstance.getRootToken().getNode().getName()); root.signal(); // FAILS on the next assert as root token is now on state 'complete' // but shouldn't be as token t20 is still at fork2!!! assertEquals("join2", processInstance.getRootToken().getNode().getName()); } }
-
14. Re: Token.signal() ---> 2 tokens?
pojomonkey Feb 21, 2008 11:36 AM (in response to qpool.char)"mputz" wrote:
I've found a (rejected) feature request to prevent the root token from being signaled on a fork node: http://jira.jboss.com/jira/browse/JBPM-642
That seems to be exactly what I've found.
So if this behaviour is 'normal', how is it possibly to use jBPM as the execution engine in the 'client based assignment' mode as described in the documentation?
If the code that's signalling the tokens has to actually be 'aware' of the process definition, then that goes out of the window completely!
Having looked at some of the unit tests in the jBPM source repository, I think that some of those 'ignore' this behaviour (or avoid dealing with it!)