-
1. Re: Where to route obsolete parent token in token multiplica
estaub Aug 8, 2007 7:37 AM (in response to karmen38)All child tokens, when they're done, should/could transition to a join. The join will count down the outstanding tokens for the parent token, and allow it to resume when the last child completes.
To avoid other potential problems, I suggest that you make your token-generating node a fork.
I believe that there's a race condition leading to Hibernate StaleObject exceptions in join. Before you go into production, be sure to do some concurrency testing on join.
-Ed Staub -
2. Re: Where to route obsolete parent token in token multiplica
karmen38 Aug 9, 2007 1:26 AM (in response to karmen38)Hi Ed,
Thank you for the reply. I made this token-generating node a fork. One of the transition is the transition with the specific name "root". In the node action I route the root token (the one that came into the node with the list variable) to this "root" transition. The other, generated, tokens leave to all other transitions except the "root" one.
It all works perfectly. I can now even route the root token to any node in the workflow right in the flow definition. It seems like exactly what I needed.
Thank you,
Mark -
3. Re: Where to route obsolete parent token in token multiplica
jcruise Aug 9, 2007 2:49 AM (in response to karmen38)Hi Mark,
I am trying to solve a similar problem, but am a bit of a custom node newbie ... do you have any code that you can share for your custom fork node?
Optimistically,
J -
4. Re: Where to route obsolete parent token in token multiplica
karmen38 Aug 9, 2007 11:32 PM (in response to karmen38)Hi J!
Here is the code for the custom fork. The code is a little bit too tailored to our specific needs and we are still thinking how to do it better. I hope it helps. You comments are appreciated.
Thank you,
Markimport java.util.LinkedList; import java.util.List; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.Node; import org.jbpm.graph.def.Transition; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.Token; public class MultiplexorAction implements ActionHandler { private static final long serialVersionUID = -5530634487359948748L; // // Name of the process variable that contains the list of items // private String listVariable; // // Name of the process variable that will contain the item - // output for each new token // protected String as; private List list; protected static final String FOREACH_PREFIX = "ea"; /** * Create a new child token for each item in list. * * @param executionContext * @throws Exception */ public void execute(final ExecutionContext executionContext) throws Exception { final Token rootToken = executionContext.getToken(); final Node node = executionContext.getNode(); initializeList(executionContext, rootToken); final List argSets = new LinkedList(); Transition rootTransition = null; Transition defaultTransition = null; // // First, create a new token and execution context for each item in // list. // for (int i = 0; i < node.getLeavingTransitions().size(); i++) { final Transition transition = (Transition) node .getLeavingTransitions().get(i); // // A transition that starts from the words "root exit" // will be reserved for the original (root) token // that came to the multiplexor node // if (transition.getName() != null && transition.getName().toLowerCase().startsWith( "root exit")) { rootTransition = transition; continue; } // // A transition that starts from the words "default exit" // will be reserved for the new token // that will be the exact copy of the original (root token) // if (transition.getName() != null && transition.getName().toLowerCase().startsWith( "default exit")) { defaultTransition = transition; continue; } for (int j = 0; list != null && j < list.size(); j++) { final Object item = list.get(j); // generate a name for the new child token String tokenName = FOREACH_PREFIX + "." + node.getId() + "." + j; // generate and save new token final Token newToken = new Token(rootToken, tokenName); newToken.setTerminationImplicit(true); executionContext.getJbpmContext().getSession().save(newToken); // create token variable from the list item final ExecutionContext newExecutionContext = new ExecutionContext( newToken); newExecutionContext.getContextInstance().createVariable(as, item, newToken); // remember this new token with its context so we can // launch it later argSets.add(new Object[] { newExecutionContext, transition }); } } // Delete the original list variable. // This step could be done in the process itself // since someone may still want reuse this list executionContext.getContextInstance().deleteVariable(listVariable); // // Now, let each new tokens leave the node. // for (int i = 0; i < argSets.size(); i++) { final Object[] args = (Object[]) argSets.get(i); node.leave((ExecutionContext) args[0], (Transition) args[1]); } // // Generate the token for the default node and launch it // if (defaultTransition != null) { String tokenName = "df." + node.getId(); final Token newToken = new Token(rootToken, tokenName); newToken.setTerminationImplicit(true); executionContext.getJbpmContext().getSession().save(newToken); final ExecutionContext newExecutionContext = new ExecutionContext( newToken); node.leave(newExecutionContext, defaultTransition); } // // Send the original root token that came to this node with the list // to the root exit transition. The assumed case is that this // original token would come to the join node to wait for all its // children generated above. // // If the original root token is routed to some business nodes // then after all its children would come // to the join node, it will effectively terminate this original // token. This behavior would leave the only option for this // root token: to be routed to the join node to wait for all // its children to complete. // if (rootTransition != null) { node.leave(executionContext, rootTransition); } } public List getList() { return list; } public void setList(final List list) { this.list = list; } public String getAs() { return as; } public void setAs(final String as) { this.as = as; } public String getListVariable() { return listVariable; } public void setListVariable(final String listVariable) { this.listVariable = listVariable; } protected void initializeList(final ExecutionContext executionContext, Token token) { if (listVariable != null) { list = (List) executionContext.getContextInstance().getVariable( listVariable, token); if (list == null) list = (List) executionContext.getContextInstance() .getVariable(listVariable); } } }