1 2 Previous Next 20 Replies Latest reply on Nov 14, 2008 9:08 AM by Mauricio Salatino

    fork and join childeren

    Tom Eugelink Newbie

      The 3.2.3 documentation says: "When the root execution path is forked into multiple concurrent execution paths, the root is the parent and the newly created execution paths are all children of the root."

      So if I have this flow:

       S
       |
       F
       / \
       s1 s2
       \ /
       J
       |
       E
      


      The when the signal comes on the start node, it moves to the fork (F). It should then fork two childeren and then move forward to the join (J) and wait for the two childeren to reach the J as well.

      However, if I write out the active node from the parent's token I see this:
      1. StartState(start)
      2. Fork(fork1)
      3. State(state1)
      4. Join(join1)
      5. EndState

      As soon as the token enters 'fork1', it does get 1 child. The getChilderen returns another token which is in state1 immediately. Then the parent token can be moved forward through state1 and the join, all the way to the end state.

      The documentation states: "This way, implementation of a join can become straightforward: the implementation of the join just has to verify if all sibling-execution-paths are already positioned in the join node. If that is the case, the parent execution path can resume execution leaving the join node."

      However I can move the parent token all the way to the end node, without ever signalling its childeren.

      1. I do not understand where the two childeren are when the fork is done.
      2. I do not understand why the parent remains in the fork instead of jumping to the join.
      3. I do not understand how I can move the parent token past the join.

      Something it not matching up.

        • 1. Re: fork and join childeren
          Mauricio Salatino Master

          First of all, great ASCII graphic!!

          Hmm... you have an wierd situation...
          The behaviour is like the documentation said...
          So.. show us your process definition and your code that signal this process..
          because there is something missing sure....

          Greetings!

          • 2. Re: fork and join childeren
            Tom Eugelink Newbie

             

            "salaboy21" wrote:
            First of all, great ASCII graphic!!


            Ah well ;-)

            I'm building up unit test to slow work jbpm into my application. This is the one where I'm trying out parallel flows. I have written the output as comment next to the println's.

             /**
             */
             @Test
             public void test()
             {
             // Declare a process with a fork
             // S
             // |
             // F
             // / \
             // s1 s2
             // \ /
             // J
             // |
             // E
             ProcessDefinition lProcessDefinition = ProcessDefinition.parseXmlString(
             "<process-definition>" +
             " <start-state name=\"start\">" +
             " <transition to=\"fork1\"></transition>" +
             " </start-state>" +
             " <fork name=\"fork1\">" +
             " <transition to=\"state1\"></transition>" +
             " <transition to=\"state2\"></transition>" +
             " </fork>" +
             " <state name=\"state1\">" +
             " <transition to=\"join1\"></transition>" +
             " </state>" +
             " <state name=\"state2\">" +
             " <transition to=\"join1\"></transition>" +
             " </state>" +
             " <join name=\"join1\">" +
             " <transition to=\"end\"></transition>" +
             " </join>" +
             " <end-state name=\"end\"></end-state>" +
             "</process-definition>"
             );
            
             // Start a process
             ProcessInstance lProcessInstance = new ProcessInstance(lProcessDefinition);
            
             // Get the token
             Token lToken = lProcessInstance.getRootToken();
             System.out.println(lToken.getNode() + "/" + lToken.getChildren()); // start
             if (lToken.getChildren() != null) System.out.println( " -" + ((Token)lToken.getChildren().get(lToken.getChildren().keySet().iterator().next())).getNode() );
            
             // Goto the next
             lToken.signal();
             System.out.println(lToken.getNode() + "/" + lToken.getChildren()); // fork1
             if (lToken.getChildren() != null) System.out.println( " -" + ((Token)lToken.getChildren().get(lToken.getChildren().keySet().iterator().next())).getNode() );
            
             // Goto the next
             lToken.signal();
             System.out.println(lToken.getNode() + "/" + lToken.getChildren()); // state1
             if (lToken.getChildren() != null) System.out.println( " -" + ((Token)lToken.getChildren().get(lToken.getChildren().keySet().iterator().next())).getNode() );
            
             //Goto the next
             lToken.signal();
             System.out.println(lToken.getNode() + "/" + lToken.getChildren()); // join1
             if (lToken.getChildren() != null) System.out.println( " -" + ((Token)lToken.getChildren().get(lToken.getChildren().keySet().iterator().next())).getNode() );
            
             //Goto the next
             lToken.signal();
             System.out.println(lToken.getNode() + "/" + lToken.getChildren()); // end
             if (lToken.getChildren() != null) System.out.println( " -" + ((Token)lToken.getChildren().get(lToken.getChildren().keySet().iterator().next())).getNode() );
             }
            


            • 3. Re: fork and join childeren
              Mauricio Salatino Master

              At first sight..
              I think you have a concept problem..
              Remember that signal method will execute your process until the processInstance reach a wait state...

              And the process is behaving well!!

              If you look you very nice graph and follow the signals you will see...
              1) the first signal will go out from the start node
              2) in this step you have two child tokens waiting in the S1 and S2
              3) the next signal (the second one) signal the S1 child token and it moves to join...(the parent token is still in the fork node)
              4) the next signal (the third one) will signal S2 and then beacuse S1 and S2 are both in the join node your output will be end

              Its that clear?

              • 4. Re: fork and join childeren
                Tom Eugelink Newbie

                 

                "salaboy21" wrote:
                I think you have a concept problem..

                Its that clear?


                It is good that I'm the one with the problem, that is probably the easiest to fix.

                So, my main problem is with the magical token swapping. I get the token once. The root. The parent. How come it suddenly becomes child token 1 and 2? How would one write this code correctly?

                • 5. Re: fork and join childeren
                  Mauricio Salatino Master

                  The code is in some way correct.. you need to understand the behaviour only....
                  The two child tokens are created and propagated in the fork node, and the parent token stay in the fork node..

                  In your code you can look for child tokens in the root token and then signal them and not always signal the parent. Just to clear the concepts...
                  Because when you signal the root token.. it propagate the signal to the child tokens..

                  • 6. Re: fork and join childeren
                    Tom Eugelink Newbie

                     

                    "salaboy21" wrote:
                    The code is in some way correct.. you need to understand the behaviour only....
                    The two child tokens are created and propagated in the fork node, and the parent token stay in the fork node..

                    In your code you can look for child tokens in the root token and then signal them and not always signal the parent. Just to clear the concepts...
                    Because when you signal the root token.. it propagate the signal to the child tokens..


                    Ok. So. Ah... I get the initial root token. It moves to the fork and at that time two child tokens are spawned. These become its childeren.

                    
                     R
                     / \
                     C1 C2
                    
                    


                    So if I do a R.getNode() I actually get the node of C1, because R is in de join and holding for its childeren. Then C1 moves to the join. With another R.getNode() I then should get the node of C2. But that does not match, because I never have a token that has "state2".

                    So I'll see what happens if I refetch the root token every time.

                    • 7. Re: fork and join childeren
                      Tom Eugelink Newbie

                       

                      "tbeernot" wrote:

                      So I'll see what happens if I refetch the root token every time.


                      No, the behavior is identical.

                      So.

                      Back to the documentation: "The default fork behaviour is to create a child token for each transition that leaves the fork, creating a parent-child relation between the token that arrives in the fork."

                      1. The token is in start.
                      2. I do a signal and then the token arrives at the fork. At that time it does not know what to do, they may be transitions with conditions.
                      3. When another signal is done on the token in the fork, THEN it will determine what transitions to follow and thus childeren can be created.

                      It still is THE token that arrived in the fork, so after the second signal there should be 2 childeren. Let's verify...

                      No. If I print the getNode and the getChilderen.size of the same token, it does not match. There is only one child. And the childeren appear at the wrong time.

                      StartState(start)/null
                      Fork(fork1)/1
                      State(state1)/1

                      Is there a good documentation? This is very confusing.

                      • 8. Re: fork and join childeren
                        Mauricio Salatino Master

                        No NO NO!.. LOL



                        1. The token is in start.
                        2. I do a signal and then the token arrives at the fork. At that time it does not know what to do, they may be transitions with conditions.
                        3. When another signal is done on the token in the fork, THEN it will determine what transitions to follow and thus childeren can be created.


                        the second (2) point is wrong.. when the process enter to de fork node It know how many transition have.. so it create all the child token.. (If you don't belive me.. see the ForkNode.java class)

                        The fork node is not a wait state.. so if you signal the start-state, the process will continue passing thru the fork node until reach the S1 and S2 nodes...

                        So do this test:

                        1) signal de root token
                        2) look for childrens.. at this time with only one signal.. you must have 2 children.
                        3) then you can signal one of the child tokens and you see that the root token still in the fork node and the child token go to join node
                        4) then signal the other token and you will see that the root token moves to the end and the child token like the other move to join node...

                        Can you do that test and post your code?


                        • 9. Re: fork and join childeren
                          Tom Eugelink Newbie

                           

                          "salaboy21" wrote:
                          The fork node is not a wait state.. so if you signal the start-state, the process will continue passing thru the fork node until reach the S1 and S2 nodes...

                          So do this test:

                          1) signal de root token
                          2) look for childrens.. at this time with only one signal.. you must have 2 children.
                          3) then you can signal one of the child tokens and you see that the root token still in the fork node and the child token go to join node
                          4) then signal the other token and you will see that the root token moves to the end and the child token like the other move to join node...

                          Can you do that test and post your code?



                          Sure!


                           public void test()
                           {
                           // Declare a process with a fork
                           // S
                           // |
                           // F
                           // / \
                           // s1 s2
                           // \ /
                           // J
                           // |
                           // E
                           ProcessDefinition lProcessDefinition = ProcessDefinition.parseXmlString(
                           "<process-definition>" +
                           " <start-state name=\"start\">" +
                           " <transition to=\"fork1\"></transition>" +
                           " </start-state>" +
                           " <fork name=\"fork1\">" +
                           " <transition to=\"state1\"></transition>" +
                           " <transition to=\"state2\"></transition>" +
                           " </fork>" +
                           " <state name=\"state1\">" +
                           " <transition to=\"join1\"></transition>" +
                           " </state>" +
                           " <state name=\"state2\">" +
                           " <transition to=\"join1\"></transition>" +
                           " </state>" +
                           " <join name=\"join1\">" +
                           " <transition to=\"end\"></transition>" +
                           " </join>" +
                           " <end-state name=\"end\"></end-state>" +
                           "</process-definition>"
                           );
                          
                           // Start a process
                           ProcessInstance lProcessInstance = new ProcessInstance(lProcessDefinition);
                          
                           // Get the token: we're in the start node
                           Token lToken = lProcessInstance.getRootToken();
                           System.out.println("1: " + lToken.getNode());
                          
                           // First signal: goto the fork
                           lToken.signal();
                           // this should mean that this token gets two childeren
                           System.out.println("2a: " + lToken.getChildren().size());
                           // this token should still be in the fork
                           System.out.println("2b: " + lToken.getNode());
                           // this token should still be in the fork
                           System.out.println("2c: " + lToken.getChildren());
                           Token lChild1 = (Token)lToken.getChildren().values().iterator().next();
                           //Token lChild2 = (Token)lToken.getChildren().values().iterator().next();
                           // the childeren should be in state1 and state2
                           System.out.println("2d: " + lChild1.getNode() );
                           //System.out.println("2e: " + lChild2.getNode() );
                           }
                          


                          I commented out lines because I do not have two childeren:

                          1: StartState(start)
                          2a: 1
                          2b: Fork(fork1)
                          2c: {1=Token(/1)}
                          2d: State(state1)

                          • 10. Re: fork and join childeren
                            Mauricio Salatino Master

                            Ok, you are right.. there is something with your process definition and at first sight i don't find this error...
                            So when I test it in my IDE i find the problem very quickly:

                            This is your problem:

                            "<process-definition>" +
                             " <start-state name=\"start\">" +
                             " <transition to=\"fork1\"></transition>" +
                             " </start-state>" +
                             " <fork name=\"fork1\">" +
                             " <transition to=\"state1\" name=\"2\"></transition>" +
                             " <transition to=\"state2\" name=\"1\"></transition>" +
                             " </fork>" +
                             " <state name=\"state1\">" +
                             " <transition to=\"join1\"></transition>" +
                             " </state>" +
                             " <state name=\"state2\">" +
                             " <transition to=\"join1\"></transition>" +
                             " </state>" +
                             " <join name=\"join1\">" +
                             " <transition to=\"end\"></transition>" +
                             " </join>" +
                             " <end-state name=\"end\"></end-state>" +
                             "</process-definition>"
                            

                            If you look it carefully, you will find that I add the name attribute in the transitions..
                            This confuse jBPM because have to with null or empty names...
                            If you correct that you see the right behaviour..
                            Hope it helps...


                            • 11. Re: fork and join childeren
                              Tom Eugelink Newbie

                               

                              "salaboy21" wrote:

                              If you look it carefully, you will find that I add the name attribute in the transitions..


                              Ah. Now indeed the behavior is more logical. Thanks! I'll experiment some more now and see if I get the concept now.

                              Thanks again!

                              • 12. Re: fork and join childeren
                                Ronald van Kuijk Master

                                What is best in these cases is to make a real unittest and do asserts on what node you think the token is in, how many tokens are there etc..... Not using system.out or whatever. Using asserts, you can make clear to everyone in a declared way what you expect, without using natural language.

                                • 13. Re: fork and join childeren
                                  Tom Eugelink Newbie

                                   

                                  "kukeltje" wrote:
                                  What is best in these cases is to make a real unittest and do asserts on what node you think the token is in, how many tokens are there etc..... Not using system.out or whatever. Using asserts, you can make clear to everyone in a declared way what you expect, without using natural language.


                                  He he, naturally. In fact I was creating a unit test. But before I can assert something, I need to see it first... Are there really two tokens? Yes... Ok, assert.

                                  And the two tokens weren't there.

                                  • 14. Re: fork and join childeren
                                    Ronald van Kuijk Master

                                     

                                    But before I can assert something, I need to see it first... Are there really two tokens? Yes... Ok, assert.


                                    I disagree. You can assert ANYTHING, e.g. assert that you expect one token. The test fails because there are more tokens. Assert that the token is in a certain state: it succeeds or fails and the real value is always printed.... I initially did it as you mention, but starting with assertions is way easier and more understandable for others if something goes wrong.

                                    1 2 Previous Next