Uploaded image for project: 'jBPM'
  1. jBPM
  2. JBPM-4030

Incorrect execution of fork/join after async node

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • jBPM 3.2.15
    • jBPM 3.2.13
    • Runtime Engine
    • None

      If you have any node before the fork that is asynchonous, the fork will not work as expected. It will create child tokens but they will not be "saved" into the "children" map of the parent (root) token. You can do the following test process:

      1) Create a Start State going to 2)
      2) Create a node flagged ASYNCHRONOUS with a "PrintTokens" action doing the following:

      package test.actions;
      
      import org.jbpm.graph.def.ActionHandler;
      import org.jbpm.graph.exe.ExecutionContext;
      
      
      public class PrintTokens extends AbstractBpmHandler implements ActionHandler {
      
          @Override
          public void execute(ExecutionContext executionContext) throws Exception {
              System.out.println("PRINT TOKENS START");
              System.out.println("TOKEN: " + executionContext.getToken().getId());
              System.out.println("PARENT: " + (executionContext.getToken().getParent() != null ? executionContext.getToken().getParent().getId() : "null") );
              System.out.println("PARENT CHILDREN: " + (executionContext.getToken().getParent() != null ? executionContext.getToken().getParent().getChildren() : "null"));
              System.out.println("PRINT TOKENS END");
              executionContext.leaveNode();
          }
      
      }
      

      This node will go to 3)

      3) Create a fork
      4) create two SYNCHRONOUS nodes (node1 and node2) associated to the same action "PrintTokens" as 2) and create a transition from the fork to node1 and another from the fork to node 2
      5) create a join and create a transition from node1 to the join and another transition from node2 to the join.
      6) Create an END state and the transition from the join to the End state.

      You will then see the following:

      17:57:50,219 INFO  [STDOUT] PRINT TOKENS START
      17:57:50,220 INFO  [STDOUT] TOKEN: 10931
      17:57:50,221 INFO  [STDOUT] PARENT: null
      17:57:50,225 INFO  [STDOUT] PARENT CHILDREN: null
      17:57:50,228 INFO  [STDOUT] PRINT TOKENS END
      17:57:50,251 INFO  [STDOUT] PRINT TOKENS START
      17:57:50,251 INFO  [STDOUT] TOKEN: 10941
      17:57:50,252 INFO  [STDOUT] PARENT: 10931
      17:57:50,255 INFO  [STDOUT] PARENT CHILDREN: {}
      17:57:50,259 INFO  [STDOUT] PRINT TOKENS END
      17:57:50,273 INFO  [STDOUT] PRINT TOKENS START
      17:57:50,274 INFO  [STDOUT] TOKEN: 10942
      17:57:50,275 INFO  [STDOUT] PARENT: 10931
      17:57:50,278 INFO  [STDOUT] PARENT CHILDREN: {}
      17:57:50,279 INFO  [STDOUT] PRINT TOKENS END
      17:57:50,279 WARN  [Token] Token(10931) was already unlocked
      

      The children sets are EMPTY even if they should contain the two children of the fork !!!!

      This means that when arriving to the join it will arrives into that part of the code:

      ...
          // if no configuration is specified
          else {
            // check all child tokens and reactivate the parent
            // when the last token arrives in the join
            Collection tokenNames = parentToken.getChildren().keySet();
            reactivateParent = !parentToken.hasActiveChildren()
              && mustParentBeReactivated(parentToken, tokenNames);
          }
      ...
      

      which will execute the getChildren method and so will return an empty set. This will allow the first token arrived into the join to reactivate the parent to continue the process. Then when the second child token arrives, it will do the same and then the nightmare begins. IN the test case I provide, there is just a warning saying that the token was already unlocked, but if you add another node after the join and, worse, if this node is asynchronous, you will get a bunch of exceptions.... meaning that in this test case, the join is not waiting on all the token before reactivating the root token !

      By the way, you can do the same test case with setting the first not not asynchronous and you will get a working situation:

      18:02:54,307 INFO  [STDOUT] PRINT TOKENS START
      18:02:54,307 INFO  [STDOUT] TOKEN: 11041
      18:02:54,308 INFO  [STDOUT] PARENT: null
      18:02:54,311 INFO  [STDOUT] PARENT CHILDREN: null
      18:02:54,316 INFO  [STDOUT] PRINT TOKENS END
      18:02:54,329 INFO  [STDOUT] PRINT TOKENS START
      18:02:54,330 INFO  [STDOUT] TOKEN: 11052
      18:02:54,332 INFO  [STDOUT] PARENT: 11041
      18:02:54,335 INFO  [STDOUT] PARENT CHILDREN: {to Branch 1=Token(11052), to Branch 2=Token(11053)}
      18:02:54,339 INFO  [STDOUT] PRINT TOKENS END
      18:02:54,351 INFO  [STDOUT] PRINT TOKENS START
      18:02:54,351 INFO  [STDOUT] TOKEN: 11053
      18:02:54,352 INFO  [STDOUT] PARENT: 11041
      18:02:54,356 INFO  [STDOUT] PARENT CHILDREN: {to Branch 1=Token(11052), to Branch 2=Token(11053)}
      18:02:54,359 INFO  [STDOUT] PRINT TOKENS END
      

      Just in case here is the content of the processdefinition.xml:

      <?xml version="1.0" encoding="UTF-8"?>
      <process-definition  xmlns=""  name="ForkTest">
      	<start-state name="Start">
      		<transition to="Print Tokens"></transition>
      	</start-state>
      	<fork name="fork1">
      		<transition to="Print Token (1)" name="to Branch 1"></transition>
      		<transition to="Print Token (2)" name="to Branch 2"></transition>
      	</fork>
      	<join name="join1">
      		<transition to="End"></transition>
      	</join>
      	<node name="Print Token (1)">
      		<action class="test.actions.PrintTokens"></action>
      		<transition to="join1"></transition>
      	</node>
      	<node name="Print Token (2)">
      		<action class="test.actions.PrintTokens"></action>
      		<transition to="join1"></transition>
      	</node>
      	<node name="Print Tokens" async="true">
      		<action class="test.actions.PrintTokens"></action>
      		<transition to="fork1"></transition>
      	</node>
      	<end-state name="End"></end-state>
      </process-definition>
      

              marco.rietveld Marco Rietveld (Inactive)
              rhn-support-mputz Martin Weiler (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: