Posted by: kukeltje | 1 October, 2008

Implementing ‘milestones’ in jBPM

Very intelligent people have made it their daytime job to study Workflow Patterns. One of the patterns Workflow Control Pattern 18 (WCP-18) is about so called milestones. Since I do not want to completely redefine, what a milestone is, I’ll take their definition of a milestone as a startingpoint.

A task is only enabled when the process instance (of which it is part) is in a specific state (typically a parallel branch). The state is assumed to be a specific execution point (also known as a milestone) in the process model. When this execution point is reached the nominated task can be enabled. If the process instance has progressed beyond this state, then the task cannot be enabled now or at any future time (i.e. the deadline has expired). Note that the execution does not influence the state itself, i.e. unlike normal control-flow dependencies it is a test rather than a trigger.

First of all, the definition of a task is important. Their definition which in the Data Control Patterns btw, so not very logical since it is also part of control patterns (but hey, they are scientists, who’d expect logic from them)

A task corresponds to a single unit of work. Four distinct types of task are denoted: atomic, block, multi-instance and multi-instance block. We use the generic term components of a workflow to refer to all of the tasks that comprise a given workflow model. An atomic task is one which has a simple, self-contained definition (i.e. one that is not described in terms of other workflow tasks) and only one instance of the task executes when it is initiated. A block task is a complex action which has its implementation described in terms of a sub-workflow. When a block task is started, it passes control to the first task(s) in its corresponding sub-workflow. This sub-workflow executes to completion and at its conclusion, it passes control back to the block task . E.g. block task C is defined in terms of the sub-workflow comprising tasks, X, Y and Z.

What I get from this is that it comprises jBPM tasks, states, nodes, even subprocesses, which in fact is no problem for jBPM at all.

Secondly and that surprises me a little is that they state “is in a specific state”, so not in or after a specific state. Again, no problem at all for jBPM, since it can handle both, but rather limiting from a real world point of view since I had to implement this twice. Maybe it’s the terminology or maybe it is just me or maybe they just missed this? 😉

What is nice about this site, is that for each of these WCP, they have a flash example, but keep in mind that this is just one example. There does not need to be a loop back e.g (imo). You can start it by clicking on the ‘start’ button in the lower left-hand corner if it does not start automatically. After that, you can click on each of the individual tokens to see the behaviour and play with different scenarios.

You can enable ‘task E’ by putting the upper token in the dotted circle. If you play with it some more you’ll see that ‘E’ is no (wait-)state as opposed to the other ‘tasks’ (so is it a task at all??? hmmm, scientists)

A deffered choice is nothing more than providing an option to choose one path of execution from a variable number. jBPM has two options for this. A default decisionnode jBPM or the user selecting one ‘transition’ when executing a task. So to me it is fairly easy to see that this can be modelled in jBPM with all basic constructs available. Having the possibility to go to E or F is nothing more than implementing either a ‘guards’ in the form of conditions on transitions on a decision node that checks what state a specific (other) token is in, or even guards directly on transitions on a task node.

<task-node name='D'>
    <task name='Task D'/>
    <transition name='to E' condition='[check if a token is between B and C]' />
    <transition name='to F' />
</task-node>

Now I have to admit that this is something that has only been possible since jBPM 3.2. So that the scientists say this is not possible in jBPM 3.1, I have to agree. Besides that, it is kind of a combination of a task with one transition and exclusive choice pattern. So it is not possible in this way in jBPM 3.1, but what if we try to move a little closer to the real pattern

If you take another look at their example, you’ll see that between ‘task D’ and ‘task E’/’task F’ there is a deferred choice. To me, only in this deferred choice knowledge exists of whether task E can be chosen or not. So it seems this is not available in ‘task D’. So the crux is how to present this to the user (assuming there is a user). I’ve not been able to find (nor did I search very thoroughly, e.g. in YAWL) how they solve this.

Ok, lets go  a step further. In the milestone pattern figure it states explicitly that A can only be started if there is a token between B and C. Where C is the state that Task C is started. In my opinion is it therefor allowed to be in a task-node C (which has a task c). There is one issue. Task A may only be created if the token has left task-node B and task C is not started. Now look at this basic model

	<fork name='fork'>
		<transition to='task-node a' name='to task-node a' />
		<transition to='task-node b' name='to task-node b' />
	</fork>
	<task-node name='task-node a'>
		<task name='task a' swimlane='users' />
		<transition name='to join' to='join' />
	</task-node>
	<task-node name='task-node b'>
		<task name='task b' swimlane='users' />
		<transition name='to task-node c' to='task-node c' />
	</task-node>
	<task-node name='task-node c'>
		<task name='task c' swimlane='users' />
		<transition name='to join' to='join' />
	</task-node>
	<join name='join'>
		<transition name='to end' to='end' />
	</join>

What is missing here is that task A is directly created, which it should not be. This can be very easily changed in jBPM by adding a ‘create-tasks=”false”‘ attribute on task-node A, so this becomes

	<task-node name='task-node a' create-tasks='false'>
		<task name='task a' swimlane='users' />
		<transition name='to join' to='join' />
	</task-node>

Now we should create a ‘task a’ the moment task-node b is ‘left’. This is very simple in jBPM and can either be done on a transition, or on an event, with java and javascript. In this example I use a java class.

	<task-node name='task-node b'>
		<task name='task b' swimlane='users' />
		<event type='node-leave'>
			<action class='net.vankuijk.jbpm.utils.CreateTask'>
				<taskNodeName>task-node a</taskNodeName>
			</action>
		</event>
		<transition name='to task-node c' to='task-node c' />
	</task-node>

	<task-node name='task-node b'>
		<task name='task b' swimlane='users' />

		<transition name='to task-node c' to='task-node c'>
            	<action class='net.vankuijk.jbpm.utils.CreateTask'>
				<taskNodeName>task-node a</taskNodeName>
			</action>
            </transition>
	</task-node>

Both are valid and I made the class a little generic (since I use it more and more) so I can pass it parameters. Now how does this class look like

public class CreateTask implements ActionHandler {

	String taskNodeName;

	public void execute(ExecutionContext executionContext) throws Exception {

		TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
		Node node = executionContext.getProcessDefinition().getNode(
				taskNodeName);

		TaskNode taskNode = (TaskNode) node;

		Token parentToken = (Token) executionContext.getToken().getParent();

		Task task = (Task) taskNode.getTasksMap().values()
				.iterator().next();
		Token childToken = new Token(parentToken, taskNode.getName());
		childToken.setNode(taskNode);
		tmi.createTaskInstance(task, childToken);

	}
}

As you can see, a very simple class.

Now how do we end this task when task C starts? Remember that jBPM creates tasks but not automatically starts them. Starting them is calling a method on the api that actually does this and due the the flexible nature of jBPM, there is an event fired in this case, called ‘task-start’.

	<task-node name='task-node c'>
		<task name='task c' swimlane='users' />
		<transition name='to join' to='join' />
		<event type='task-start'>
			<action class='net.vankuijk.jbpm.utils.CancelTask'>
				<taskNodeName>task-node a</taskNodeName>
			</action>
		</event>
	</task-node>

We should just not forget to test whether task a was not started yet since it has to be left alone once it is.

public class CancelTask implements ActionHandler{

	String taskNodeName;

	public void execute(ExecutionContext executionContext) throws Exception {

		// Assuming max one active childToken and max one unfinished task in
		// this childtoken

		Node node = executionContext.getProcessDefinition().getNode(
				taskNodeName);

		TaskNode taskNode = (TaskNode) node;

		Token token = (Token) executionContext.getToken().getParent()
				.getChildrenAtNode(taskNode).get(0);

		Iterator unfinishedTasks = executionContext.getTaskMgmtInstance()
				.getUnfinishedTasks(token).iterator();
		if (unfinishedTasks.hasNext()) {
			TaskInstance ti = (TaskInstance) unfinishedTasks.next();
			if (ti.isOpen() && ti.getStart() == null) {
				ti.cancel();
			}
		}
	}
}

Of course some more nullpointer checking, logging etc should be in there. This example will become part of the jBPM distribution and it will all be in there. Maybe this is a good starting point to initiate a community effort to create these kinds of utility classes, maybe also for TDD

I hope that I demonstrated with this that jBPM, with some small extensions, supports one of the many milestones patterns, albeit a simple one. And that even jBPM 3.1 should have gotten a + in the product evaluation, you just have to know the capabilities of a product/framework/… and not expect it to contain everything but the kitchensink out of the box.

Ronald

p.s. I’ve split this post into 2 parts since it became to long. The second part of this post, a more complex ‘milestone/trigger/…’ example with a slightly different approach from B2 International will be posted next week

Advertisements

Responses

  1. Ronald, excellent post!

    I agree that some parts should be incorporated into the distribution (CreateTask/CancelTask classes … I’ve written them myself too in the past… )

    I’m hoping that the WF pattern lovers once will see that (with a little creativity) many (if not all) patterns can be supported by jBPM.

    My only remark is for the CancelTask: if there are other parallell paths which contain unfinished tasks, they are cancelled too…

    For performance reasons, I would do a Hibernate call for retrieving the specific TaskInstances with the given name which are open.

    Looking forward to the next post!

  2. Joram,

    Regarding your ‘remark’… look at my comment in the code 😉 but….

    What happens is that the token for the specifically configured task-node is retrieved (task-node a) with

    TaskNode taskNode = (TaskNode) node;

    Token token = (Token) executionContext.getToken().getParent().getChildrenAtNode(taskNode).get(0);

    (assuming there is only one token created in task-node a)

    and within that tasknode (token) the unfinished tasks on that token are retrieved with

    Iterator unfinishedTasks = executionContext.getTaskMgmtInstance().getUnfinishedTasks(token).iterator();

    So I do not think there is a problem if there are parallel tasks, but I’ll extend my testcase that I HAVE to write to get it included in jBPM with an additional branche in the fork and see what happens.

    Regarding performance (regarding the code above that is?), I did not look into that (yet) but since it happens not that often, I’m not inclined to look into it yet. Maybe an index on something will help out.

  3. Good post Ronald!

  4. Ronald,

    I did not see the comment the first time, so my bad 🙂

    The performance shouldn’t be an issue if there aren’t many open tasks for a given process instance. But it happen sometimes and than a simple Hibernate query does the trick much faster than iterating over the collection.

  5. Ronald,

    Just wondering what happened to the 2nd part of the post.

    • It got ‘lost’ in doing lots of other things. Not lost in the sence that my text got lost, no, the ‘getting to it’ got lost.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: