6 Replies Latest reply on Jan 21, 2010 7:47 AM by andre.muniz

    Strange Fork Behavior

      Hi!

       

      I'm getting an unexpected behavior on the following test process:

       

      forkTest.jpg


      When task2 is completed and the execution reaches fork2, JBPM 4.3 is duplicating task3, and I'm getting 3 pending tasks instead of two.

       

      Here is the unit test for this process.

       

      package test2;
      
      import java.util.List;
      
      import org.jbpm.api.task.Task;
      import org.jbpm.test.JbpmTestCase;
      
      public class ForkTest extends JbpmTestCase {
      
          /** Deployment id. */
          String deploymentId;
      
          /**
           * Set up.
           * @throws Exception exception
           */
          protected void setUp() throws Exception {
              super.setUp();
      
              // XML definition
              StringBuilder jpdl = new StringBuilder();
              jpdl.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
              jpdl.append("<process key=\"forkTest\" name=\"Fork Test\" xmlns=\"http://jbpm.org/4.3/jpdl\">");
              jpdl.append("   <start g=\"7,137,48,48\" name=\"Lease Termination\">");
              jpdl.append("      <transition g=\"-43,-18\" name=\"to fork1\" to=\"fork1\"/>");
              jpdl.append("   </start>");
              jpdl.append("   <end g=\"841,83,48,48\" name=\"end1\"/>");
              jpdl.append("   <fork g=\"118,140,48,48\" name=\"fork1\">");
              jpdl.append("      <transition g=\"-44,-18\" name=\"to task1\" to=\"task1\"/>");
              jpdl.append("      <transition g=\"-44,-18\" name=\"to task2\" to=\"task2\"/>");
              jpdl.append("   </fork>");
              jpdl.append("   <task candidate-groups=\"leasing\" g=\"231,71,92,52\" name=\"task1\">");
              jpdl.append("      <transition name=\"to join2\" to=\"join2\" g=\"-41,-18\"/>");
              jpdl.append("   </task>");
              jpdl.append("   <task candidate-groups=\"leasing\" g=\"230,225,92,52\" name=\"task2\">");
              jpdl.append("      <transition name=\"to fork2\" to=\"fork2\" g=\"-43,-18\"/>");
              jpdl.append("   </task>");
              jpdl.append("   <task candidate-groups=\"leasing\" g=\"507,188,92,52\" name=\"task3\">");
              jpdl.append("      <transition g=\"-41,-18\" name=\"to join1\" to=\"join1\"/>");
              jpdl.append("   </task>");
              jpdl.append("   <task candidate-groups=\"leasing\" g=\"511,306,92,52\" name=\"task4\">");
              jpdl.append("      <transition g=\"-41,-18\" name=\"to join1\" to=\"join1\"/>");
              jpdl.append("   </task>");
              jpdl.append("   <join g=\"653,244,48,48\" name=\"join1\">");
              jpdl.append("      <transition g=\"-44,-18\" name=\"to task5\" to=\"task5\"/>");
              jpdl.append("   </join>");
              jpdl.append("   <task candidate-groups=\"leasing\" g=\"781,237,92,52\" name=\"task5\">");
              jpdl.append("      <transition g=\"-41,-18\" name=\"to join2\" to=\"join2\"/>");
              jpdl.append("   </task>");
              jpdl.append("   <join g=\"674,80,48,48\" name=\"join2\">");
              jpdl.append("      <transition g=\"-42,-18\" name=\"to end1\" to=\"end1\"/>");
              jpdl.append("   </join>");
              jpdl.append("   <fork g=\"402,232,48,48\" name=\"fork2\">");
              jpdl.append("      <transition g=\"-44,-18\" name=\"to task3\" to=\"task3\"/>");
              jpdl.append("      <transition g=\"-44,-18\" name=\"to task4\" to=\"task4\"/>");
              jpdl.append("   </fork>");
              jpdl.append("</process>");
      
              // Deploys the process
              deploymentId =
                      repositoryService.createDeployment().addResourceFromString("forkTest.jpdl.xml", jpdl.toString())
                              .deploy();
          }
      
          /**
           * Tear down.
           * @throws Exception exception
           */
          protected void tearDown() throws Exception {
              repositoryService.deleteDeploymentCascade(deploymentId);
              super.tearDown();
          }
      
          /**
           * Tests the process.
           */
          public void testProcess() {
      
              // Starts a new process instance
              executionService.startProcessInstanceByKey("forkTest");
      
              // Collection to store the pending tasks
              List < Task > taskList = null;
              // Sets if it's to check the number of tasks after the fork2 (2 tasks - task3 and task4)
              boolean checkNumberOfTasks = false;
      
              // Loop
              do {
      
                  // Gets the pending tasks
                  taskList = taskService.createTaskQuery().list();
      
                  // If it's to check the number of tasks, do it
                  if (checkNumberOfTasks) {
                      assertEquals(2, taskList.size());
                      break;
                  }
      
                  // Iterates the tasks list
                  for (Task t : taskList) {
      
                      // If the execution reaches the task2, changes the boolean, so the next iteration checks the number of tasks
                      if ("task2".equals(t.getName()))
                          checkNumberOfTasks = true;
      
                      // Completes the task
                      taskService.completeTask(t.getId());
                  }
      
              } while (taskList != null && taskList.size() > 0);
          }
      }
      

       

       

      Does anyone have any idea what is going on? Thanks!

        • 1. Re: Strange Fork Behavior
          kukeltje

          the JbpmTestcase has more assertions specific for task(lists) activties etc... can you make it explicit? And how do you know 3 is duplicated? I do not see that in your test, could you make that explicit as well?

           

          Ronald

          • 2. Re: Strange Fork Behavior

            Sorry, the test really wasn't clear enough. I know task3 is duplicated because I debugged it!

             

            Here goes the new unit test:

             

            package test2;
            
            import java.util.List;
            
            import org.jbpm.api.task.Task;
            import org.jbpm.test.JbpmTestCase;
            
            public class ForkTest extends JbpmTestCase {
            
                /** Deployment id. */
                String deploymentId;
            
                /**
                 * Set up.
                 * @throws Exception exception
                 */
                protected void setUp() throws Exception {
                    super.setUp();
            
                    // XML definition
                    StringBuilder jpdl = new StringBuilder();
                    jpdl.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                    jpdl.append("<process key=\"forkTest\" name=\"Fork Test\" xmlns=\"http://jbpm.org/4.3/jpdl\">");
                    jpdl.append("   <start g=\"7,137,48,48\" name=\"Lease Termination\">");
                    jpdl.append("      <transition g=\"-43,-18\" name=\"to fork1\" to=\"fork1\"/>");
                    jpdl.append("   </start>");
                    jpdl.append("   <end g=\"841,83,48,48\" name=\"end1\"/>");
                    jpdl.append("   <fork g=\"118,140,48,48\" name=\"fork1\">");
                    jpdl.append("      <transition g=\"-44,-18\" name=\"to task1\" to=\"task1\"/>");
                    jpdl.append("      <transition g=\"-44,-18\" name=\"to task2\" to=\"task2\"/>");
                    jpdl.append("   </fork>");
                    jpdl.append("   <task candidate-groups=\"leasing\" g=\"231,71,92,52\" name=\"task1\">");
                    jpdl.append("      <transition name=\"to join2\" to=\"join2\" g=\"-41,-18\"/>");
                    jpdl.append("   </task>");
                    jpdl.append("   <task candidate-groups=\"leasing\" g=\"230,225,92,52\" name=\"task2\">");
                    jpdl.append("      <transition name=\"to fork2\" to=\"fork2\" g=\"-43,-18\"/>");
                    jpdl.append("   </task>");
                    jpdl.append("   <task candidate-groups=\"leasing\" g=\"507,188,92,52\" name=\"task3\">");
                    jpdl.append("      <transition g=\"-41,-18\" name=\"to join1\" to=\"join1\"/>");
                    jpdl.append("   </task>");
                    jpdl.append("   <task candidate-groups=\"leasing\" g=\"511,306,92,52\" name=\"task4\">");
                    jpdl.append("      <transition g=\"-41,-18\" name=\"to join1\" to=\"join1\"/>");
                    jpdl.append("   </task>");
                    jpdl.append("   <join g=\"653,244,48,48\" name=\"join1\">");
                    jpdl.append("      <transition g=\"-44,-18\" name=\"to task5\" to=\"task5\"/>");
                    jpdl.append("   </join>");
                    jpdl.append("   <task candidate-groups=\"leasing\" g=\"781,237,92,52\" name=\"task5\">");
                    jpdl.append("      <transition g=\"-41,-18\" name=\"to join2\" to=\"join2\"/>");
                    jpdl.append("   </task>");
                    jpdl.append("   <join g=\"674,80,48,48\" name=\"join2\">");
                    jpdl.append("      <transition g=\"-42,-18\" name=\"to end1\" to=\"end1\"/>");
                    jpdl.append("   </join>");
                    jpdl.append("   <fork g=\"402,232,48,48\" name=\"fork2\">");
                    jpdl.append("      <transition g=\"-44,-18\" name=\"to task3\" to=\"task3\"/>");
                    jpdl.append("      <transition g=\"-44,-18\" name=\"to task4\" to=\"task4\"/>");
                    jpdl.append("   </fork>");
                    jpdl.append("</process>");
            
                    // Deploys the process
                    deploymentId =
                            repositoryService.createDeployment().addResourceFromString("forkTest.jpdl.xml", jpdl.toString())
                                    .deploy();
                }
            
                /**
                 * Tear down.
                 * @throws Exception exception
                 */
                protected void tearDown() throws Exception {
                    repositoryService.deleteDeploymentCascade(deploymentId);
                    super.tearDown();
                }
            
                /**
                 * Tests the process.
                 */
                public void testProcess() {
            
                    // Starts a new process instance
                    executionService.startProcessInstanceByKey("forkTest");
            
                    // Gets the pending tasks (task1 and task2)
                    List < Task > taskList = taskService.createTaskQuery().list();
                    assertEquals(2, taskList.size());
                    assertContainsTask(taskList, "task1");
                    assertContainsTask(taskList, "task2");
                    // Completes task1 and task2
                    taskService.completeTask(taskList.get(0).getId());
                    taskService.completeTask(taskList.get(1).getId());
            
                    // Gets the pending tasks (task3 and task4)
                    taskList = taskService.createTaskQuery().list();
            
                    // At this point we should have 2 pending tasks ([0] = task3, [1] = task4)
                    // But the following tests shows that the taskList collection contains 3 pending tasks ([0] = task3, [1] = task4, [2] = task3)
                    assertEquals(3, taskList.size());
                    assertEquals("task3", taskList.get(0).getName());
                    assertEquals("task4", taskList.get(1).getName());
                    assertEquals("task3", taskList.get(2).getName());
            
                    // This should be the right result, but it's where the test is breaking now
                    // Failure Trace: "expected:<2> but was:<3>"
                    assertEquals(2, taskList.size());
                    assertEquals("task3", taskList.get(0).getName());
                    assertEquals("task4", taskList.get(1).getName());
                }
            }
            
            • 3. Re: Strange Fork Behavior
              saraswati.santanu

              This is a bug in JBPM. The class that causes the problem is ForkActivity. Here is a discussion which explains the problem

              http://community.jboss.org/message/400655#400655

               

              One of the solution there, which keeps a dummy node, is a hack for sure. But it works. If you do not want to modify JBPM code and want to lie with the bug till next release (hopefully), then you can do that.

               

              The other one which modifies the ForkActivity source sounds more logical.

               

              There can be one more way to fix this. Lets look at the problematic segment once more (the one in red):

               

              if (Execution.STATE_ACTIVE_ROOT.equals(execution.getState())) {
                   concurrentRoot = execution;
                   execution.setState(Execution.STATE_INACTIVE_CONCURRENT_ROOT);
                   execution.setActivity(null);
              } else if (Exec ution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
                   concurrentRoot = execution.getParent();
              }

               

              for (Transition transition: forkingTransitions) {
                    // launch a concurrent path of execution
                    String childExecutionName = transition.getName();
                    ExecutionImpl concurrentExecution = concurrentRoot.createExecution(childExecutionName);
                    concurrentExecution.setActivity(activity);
                    concurrentExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
                    concurrentExecution.take(transition);
               
                    if (concurrentRoot.isEnded()) {
                        break;
                    }
              }

               

              Here the parent of the first level fork becomes the parent of the second level fork as well. And that is the root of the problem. So there can be two solutions possible. We talked about one of them in that older post. Lets look at both of them one by one:

               

              1. This sound most logical to me. The parent of the second level fork should be the first level for. So set the first level fork as the parent of the econd level fork. For this the first line in red needs to be changed to :

               

              else if (Exec ution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {

                  //notice that we removed getParent() here
                    concurrentRoot = execution;
                }

               

              2. One more way to solve the prblem might be to end the first level fork execution, because that has anyway nothing on earth to do. To do this we may do something like this:

               

              else if (Exec ution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
                    concurrentRoot = execution.getParent();

                  //notice this end call here

                  execution.end();
                }

               

              Again, my vote is for the first one. But both af them seem to work.

               

              Regards,

              Santanu

              • 4. Re: Strange Fork Behavior
                kukeltje

                I wanted to dig a little into this, but suddenly remembered some checkins not so long ago. This issue is already fixed in the trunk.

                 

                Ronald

                1 of 1 people found this helpful
                • 5. Re: Strange Fork Behavior
                  kukeltje
                  And number 2 seems to be the solution that was choosen (seeing what you proposed in the source triggerd me)
                  1 of 1 people found this helpful
                  • 6. Re: Strange Fork Behavior

                    Both solutions worked!

                     

                    Thank you very much for your help!