Hi Sebastian,
   You found a bug there. It took sometime to understand why is it behaving 
that way. I think the problem is there in ForkActivity class. Actually the 
following things happen in sequence transition from one Fork node to any other 
node.

   1. Create child execution for each transition
   2. Mark state of each child execution as STATE_ACTIVE_CONCURRENT
   3. take the transition
next few steps are regular transition steps:
   4. Perform transition end activity (for the source node)
   5. Perform transition take activity
   6. Execute transition start event listener on destination
   7. Perform transition start activity (for destination)
   8. Execute the activity behaviour of the destination

   Now for your case, at the last step it will execute ForkActivity again. Here 
is the fork activity execute method. Please notice the line in bold and the if 
block surrounding:


  |   public void execute(ExecutionImpl execution) {
  |     Activity activity = execution.getActivity();
  | 
  |     // evaluate the conditions and find the transitions that should be 
forked
  |     List<Transition> forkingTransitions = new ArrayList<Transition>();
  |     List<Transition> outgoingTransitions = 
activity.getOutgoingTransitions();
  |     for (Transition transition: outgoingTransitions) {
  |       Condition condition = transition.getCondition();
  |       if  ( (condition==null)
  |             || (condition.evaluate(execution))
  |           ) {
  |         forkingTransitions.add(transition);
  |       }
  |     }
  | 
  |     // if no outgoing transitions should be forked, 
  |     if (forkingTransitions.size()==0) {
  |       // end this execution
  |       execution.end();
  |       
  |     // if there is exactly 1 transition to be taken, just use the incoming 
execution
  |     } else if (forkingTransitions.size()==1) {
  |       execution.take(forkingTransitions.get(0));
  |       
  |     // if there are more transitions
  |     } else {
  |       ExecutionImpl concurrentRoot = null;
  |      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;
  |         }
  |       }
  |     }
  |   }
  | 

For a fork, the state of any child execution is 
Execution.STATE_ACTIVE_CONCURRENT. So for our nested fork state will be 
STATE_ACTIVE_CONCURRENT and it will satisfy the else if condition:


  |      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();
  |       }
  | 

So the root of the children of the nested fork becomes the first level fork! 
Now the codes goes into the next loop:


  |       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 ExecutionImpl concurrentExecution = 
concurrentRoot.createExecution(childExecutionName); line creates a child 
execution of actually the grand parent execution. In the same createExecution 
method of ExecutionImpl it also sets the propagation to explicit to avoid 
automatic transition.


  | public ExecutionImpl createExecution(String name) {
  |     // when an activity calls createExecution, propagation is explicit.
  |     // this means that the default propagation (proceed()) will not be 
called 
  |     propagation = Propagation.EXPLICIT;
  |     ...
  |     ...
  | }
  | 

Now the stuation where we land up is - the grand parent execution of the first 
fork (fork1) is set to propagation EXPLICIT, but it was already EXPLICIT 
because the grand parent has its own children and while taking transition to 
the children it became EXPLICIT. The execution of second lavel fork (fork2) has 
never been used to create a child execution. So its propagation status remains 
UNSPECIFIED.

All executions with unspecified propagation status proceeds automatically. So 
does our fork2. So fork2 takes its default transition again. Which for your 
case is to Task1. So it goes ahead and executes TaskActivity for Task1 again. 
Thus we get two Task1 created at the end.

Now if we change the line in bold there to this:

  |      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();
  |         concurrentRoot = execution;
  |       }
  | 

Basically we make the current root always the current execution and create 
children of that. This will fix your problem.

Now I have no idea why for nested forks the parent of the inner fork is used to 
create children of the inner fork is not very clear to me. I would expect there 
must be some usecase which require this.

So the only thing we can do now is to report a bug. You also have the nice flow 
and testcase ready.

Well, the post became quite big and complex. Hope this is not very confusing!

View the original post : 
http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4266301#4266301

Reply to the post : 
http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4266301
_______________________________________________
jboss-user mailing list
jboss-user@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to