1 2 Previous Next 20 Replies Latest reply on Feb 25, 2008 12:20 PM by pojomonkey

    Token.signal() ---> 2 tokens?

    Alexander Eitner Newbie

      Hello, could you please give me a short answer to this understanding problem?

      The example situation:

      TaskNode A ------> Fork

      Fork ------> TaskNode B
      Fork ------> TaskNode C

      Lets assume, i have a token in TaskNode A. Now i call token.signal().
      The token moves to Fork. Now again token.signal().

      Do i result in 2 tokens, one for TaskNode B, one for TaskNode C?
      Or do i only have one token for both TaskNodes?

      I have this situation in my code, and dont know how to get all active TaskInstances after leaving a fork....

      thx in advance

        • 1. Re: Token.signal() ---> 2 tokens?
          Alexander Eitner Newbie

          I mean, i move my token to the fork.

          Then again token.signal()

          Then i check with

          Collection 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?
            Ronald van Kuijk Master

            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?
              Ronald van Kuijk Master

              I should not have kept you in the dark.... there are 2 tokens...

              • 5. Re: Token.signal() ---> 2 tokens?
                pojomonkey Newbie

                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?
                  Ronald van Kuijk Master

                  please make a unit test so we can see the problem and what you specifically do.

                  • 7. Re: Token.signal() ---> 2 tokens?
                    pojomonkey Newbie

                    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 Newbie

                      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?
                        Ronald van Kuijk Master

                        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 Newbie

                           

                          "kukeltje" wrote:
                          I do not see strange things in here.
                          There may be nothing 'strange', but I wasn't expecting multiple tokens to pass through any node.

                          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?
                            Martin Weiler Apprentice

                            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 Newbie

                               

                              "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 Newbie

                                 

                                "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!)

                                1 2 Previous Next