[jbpm-users] Foreach / Fork / Pattern 15 (Multiple Instances without a priori Run-Time Knowledge)

Josh Long starbuxman at gmail.com
Thu Sep 3 04:56:37 EDT 2009


Hello,

I'm trying to port the jBPM3 foreach handler written (if my copy of
the source is faithful) by chadwickboggs at yahoo.com. I know the code's
on the WIKI somewhere as well.

So far it's been easy going. The APIs speak for themselves.

I've got the following code :


import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.jbpm.api.Execution;
import org.jbpm.api.activity.ActivityBehaviour;
import org.jbpm.api.activity.ActivityExecution;
import org.jbpm.pvm.internal.model.Activity;
import org.jbpm.pvm.internal.model.ExecutionImpl;
import org.jbpm.pvm.internal.model.Transition;
import org.jbpm.pvm.internal.model.TransitionImpl;
import org.jbpm.pvm.internal.model.ExecutionImpl.Propagation;

public class ForeachAction implements ActivityBehaviour {
	
	private static final long serialVersionUID = 1L;
	
	public void execute(ActivityExecution ae) throws Exception {
		ExecutionImpl execution = (ExecutionImpl) ae;
		Activity activity = execution.getActivity();

		List<Transition> forkingTransitions = new ArrayList<Transition>();
		List<Transition> outgoingTransitions = activity.getOutgoingTransitions();

		for (Transition t : outgoingTransitions)
			if (!StringUtils.defaultString(t.getName()).equals(StringUtils.defaultString(emptyListTransition)))
				forkingTransitions.add(t);

		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 (Execution.STATE_ACTIVE_CONCURRENT.equals(ae.getState())) {
			concurrentRoot = execution.getParent();
		}

		List<?> toIterate =  getList();
		int ctr = 0;
		List<Object[]> todo = new ArrayList<Object[]>();
		for (Transition tt : forkingTransitions) {
			for (Object o : toIterate) {
				ctr += 1;
				ExecutionImpl ei = concurrentRoot.createExecution(tt.getName() +
":" + "foreach" + ctr);
				ei.setParent(execution);
				ei.setState(Execution.STATE_ACTIVE_CONCURRENT);
				ei.setActivity(activity);
				ei.setPropagation(Propagation.EXPLICIT);
				ei.createVariable(getAs(), o);
				todo.add(new Object[] { ei, tt });
			}
		}
		for (Object[] set : todo) {
			ExecutionImpl ei1 = (ExecutionImpl) set[0];
			ei1.setState(Execution.STATE_ACTIVE_CONCURRENT);
			TransitionImpl transitionImpl = (TransitionImpl) set[1];
			ei1.take(transitionImpl);
		}
	}

	
	/*
	 * hard coding the list right now - later it will take its value from a
	 * process variable
	 */
	List list;
	String emptyListTransition;
	String as;

	public String getAs() {
		return as;
	}
	public void setAs(String as) {
		this.as = as;
	}
	public List getList() {
		return list;
	}
	public void setList(List list) {
		this.list = list;
	}
}

and my test process looks like:


<?xml version="1.0" encoding="UTF-8"?>
<process name="Fork" xmlns="http://jbpm.org/4.0/jpdl">

	<start>
		<transition to="action1" />
	</start>

	<custom name="action1"
		class="foo.bar.fork.ForeachAction">
		<field name="as">
			<string value="number" />
		</field>
		<field name="list">
			<list>
				<int value="1" />
				<int value="2" />
				<int value="3" />
				<int value="4" />
			</list>
		</field>
		<transition name="t1" to="join1"></transition>
	</custom>

	<join name="join1" > <!-- multiplicity="4" -->
		<transition to="end" />
	</join>

	<end name="end" />

</process>


It seems to be working rather well in that I can verify the creation
of the executions, each with a variable (in this case there are 4
executions and each gets one of (1,2,3,4)).

Each execution successfully transitions (such that I can see it pass
through the transition on all 4 executives) but then it errors on the
join node, giving me the following error:

Exception in thread "main" java.lang.NullPointerException: attempted
to lock null
	at org.hibernate.event.def.DefaultLockEventListener.onLock(DefaultLockEventListener.java:58)
	at org.hibernate.impl.SessionImpl.fireLock(SessionImpl.java:611)
	at org.hibernate.impl.SessionImpl.lock(SessionImpl.java:603)
	at org.jbpm.jpdl.internal.activity.JoinActivity.execute(JoinActivity.java:68)
	at org.jbpm.jpdl.internal.activity.JoinActivity.execute(JoinActivity.java:49)
	at org.jbpm.pvm.internal.model.op.ExecuteActivity.perform(ExecuteActivity.java:60)
	at org.jbpm.pvm.internal.model.ExecutionImpl.performAtomicOperationSync(ExecutionImpl.java:637)
	at org.jbpm.pvm.internal.model.ExecutionImpl.fire(ExecutionImpl.java:547)
	at org.jbpm.pvm.internal.model.ExecutionImpl.take(ExecutionImpl.java:453)
	at foo.bar.fork.ForeachAction.execute(ForkAction.java:135)
	at org.jbpm.jpdl.internal.activity.CustomActivity.execute(CustomActivity.java:46)
	at org.jbpm.pvm.internal.model.op.ExecuteActivity.perform(ExecuteActivity.java:60)
	at org.jbpm.pvm.internal.model.ExecutionImpl.performAtomicOperationSync(ExecutionImpl.java:637)
	at org.jbpm.pvm.internal.model.ExecutionImpl.performAtomicOperation(ExecutionImpl.java:597)
	at org.jbpm.pvm.internal.model.ExecutionImpl.start(ExecutionImpl.java:201)
	at org.jbpm.pvm.internal.cmd.StartProcessInstanceInLatestCmd.execute(StartProcessInstanceInLatestCmd.java:64)
	at org.jbpm.pvm.internal.cmd.StartProcessInstanceInLatestCmd.execute(StartProcessInstanceInLatestCmd.java:37)
	at org.jbpm.pvm.internal.svc.DefaultCommandService.execute(DefaultCommandService.java:42)
	at org.jbpm.pvm.internal.spring.CommandTransactionCallback.doInTransaction(CommandTransactionCallback.java:50)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:128)
	at org.jbpm.pvm.internal.tx.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:79)
	at org.jbpm.pvm.internal.svc.EnvironmentInterceptor.execute(EnvironmentInterceptor.java:54)
	at org.jbpm.pvm.internal.svc.RetryInterceptor.execute(RetryInterceptor.java:55)
	at org.jbpm.pvm.internal.svc.ExecutionServiceImpl.startProcessInstanceByKey(ExecutionServiceImpl.java:74)
    ...

I'm exhausted and am going to get some sleep, but I wanted to put the
following out there and ask if anybody's got any particular ideas:

it fails in the join, and when I put it under the debugger it shows
that it's testing for whether the join is 'complete' or not (it does
this by testing whether they size of the incoming transitions
collection is equal to the accumulated transition collection, or
whether the multiplicity attribute on the join element is equal to the
size of the accumulated transition collection. In this case,
regardless of whether I set the multiplicity attribute, it's always
equal to -1, so it's deferring to the incoming transitions collection,
which, curiously, has only one element. It sees that element, thinks
it's 'complete' and done waiting on incoming nodes and then fails down
the line trying to tell the database as much (while there are
outstanding transitions in process.)

What could be causing this? Any special handling you guys know about?
I'm doing - as far as I can tell - the most sensible thing wrt to the
code, mimicking as best as possible the ForkActivity... anyway, I'm
sure it's close, but if somebody wouldn't mind applying that second
pair of eyes, you might spare me another evening scratching my head.

PS: Also, what does anybody think about setting up a second repository
to collect these third party efforts of reusable ActivityBehaviors.
jBPM -while powerful and extensible - doesn't offer a lot of
primitives for modeling. If nothing else, such a repository would be a
great place to store things that are tested and help achieve all the
patterns on http://www.workflowpatterns.com. Perhaps the unit of
distribution would be code and/ or recipes for their use... who knows,
such a site might well blossom into forums, articles, and so on. A
jBPMForge a la MuleForge? :-)

Thanks!
Joshua Long
http://www.joshlong.com/


More information about the jbpm-users mailing list