4 Replies Latest reply on Sep 9, 2005 2:43 PM by Shane Robinson

    How can I represent this workflow in process definition?

    Shane Robinson Newbie

      I am trying to represent the following graph in a valid jBPM process definition. As far as I can see, you must have a fork after node 2 and node 3 and a branch from each of these forks must enter a join placed before node 5. But when I try to do this, the flow never leaves the join and node 5 is never called. Plus, in testing, the process completes even though not all of the nodes were entered.

       +---+
       | S |
       +---+
       |
       |
       |
       +---+
       | 1 |
       +---+
       / \
       / \
       / \
       +---+ +---+
       | 2 | | 3 |
       +---+ +---+
       / \ / \
       / \ / \
       / \ / \
       +---+ +---+ +---+
       | 4 | | 5 | | 6 |
       +---+ +---+ +---+
       \ | /
       \ | /
       \ | /
       \ | /
       \ | /
       \ | /
       \ | /
       \ | /
       +---+
       | E |
       +---+
      


      Any help would be greatly appreciated.
      Thanks in advance,
      Shane

        • 1. Re: How can I represent this workflow in process definition?
          Alejandro Guizar Master

          The default join is designed for child tokens with a common parent. The tokens that arrive at join [5] have different parents, [2] and [3]. I can help you further if you provide your process definition.

          • 2. Re: How can I represent this workflow in process definition?
            Shane Robinson Newbie

            Here is the process definition I was trying to use.

            <?xml version="1.0" encoding="UTF-8"?>
            <process-definition name="Process">
             <start-state name="start">
             <transition name="start->node1" to="node1"></transition>
             </start-state>
             <node name="node1">
             <transition name="node1->fork1" to="fork1"></transition>
             </node>
             <fork name="fork1">
             <transition name="fork1->node2" to="node2"></transition>
             <transition name="fork1->node3" to="node3"></transition>
             </fork>
             <node name="node2">
             <transition name="node2->fork2" to="fork2"></transition>
             </node>
             <node name="node3">
             <transition name="node3->fork3" to="fork3"></transition>
             </node>
             <fork name="fork2">
             <transition name="fork2->node4" to="node4"></transition>
             <transition name="fork2->join1" to="join1"></transition>
             </fork>
             <fork name="fork3">
             <transition name="fork3->join1" to="join1"></transition>
             <transition name="fork3->node5" to="node5"></transition>
             </fork>
             <join name="join1">
             <transition name="join1->node6" to="node6"></transition>
             </join>
             <node name="node4">
             <transition name="node4->join2" to="join2"></transition>
             </node>
             <node name="node5">
             <transition name="node5->join2" to="join2"></transition>
             </node>
             <node name="node6">
             <transition name="node6->join2" to="join2"></transition>
             </node>
             <join name="join2">
             <transition name="join2->end1" to="end1"></transition>
             </join>
             <end-state name="end1"></end-state>
            </process-definition>


            • 3. Re: How can I represent this workflow in process definition?
              Alejandro Guizar Master

              Shane,

              As stated earlier, the built-in join node can only join tokens that are siblings. In practice this means structured fork-join pairs. In your process, none of your joins is structured. The simplest technique I could think of is to introduce a custom joiner action that checks that all transitions arriving at a node have been taken.

              In the first place, add an action to transition arriving at joins that sets a variable with the name of the transition.

              public class TransitionMarker implements ActionHandler {
              
               public void execute(ExecutionContext executionContext) throws Exception {
               Transition transition = executionContext.getTransition();
               executionContext.setVariable(transition.getName(), Boolean.TRUE);
               }
              }

              Later on, replace each join with a normal node, and configure an action handler that checks for the marks of transitions arriving at that node. If not all marks are found, end the token. The implicit termination mechanism of jBPM will take care of ending parent tokens whose children are all ended, and will eventually end the process instance.
              public class TransitionJoiner implements ActionHandler {
              
               public void execute(ExecutionContext executionCtx) throws Exception {
               if (haveAllTransitionsBeenTaken(executionCtx)) {
               executionCtx.leaveNode();
               }
               else {
               // end the token, rely on the implicit termination mechanism
               executionCtx.getToken().end();
               log.debug("join is not leaving node, not all transitions have been taken: " +
               executionCtx.getNode().getName());
               }
               }
              
               private static boolean haveAllTransitionsBeenTaken(ExecutionContext executionCtx) {
               ContextInstance ctxInstance = executionCtx.getContextInstance();
              
               Iterator transitionIt = executionCtx.getNode().getArrivingTransitions().iterator();
               while (transitionIt.hasNext()) {
               Transition transition = (Transition) transitionIt.next();
               if (!ctxInstance.hasVariable(transition.getName()))
               return false;
               }
               return true;
               }
              }

              There is one caveat to this technique, tough. The process instance will behave as expected, but the root token will end in the first fork, not in the end node.