Friday, October 26, 2007

jboss seam and jbpm async nodes

In a jboss seam project i wanted to use async nodes in a jbpm workflow. You can add "async=true" to any node in you jbpm process definition. This makes the workflow continue in the background so your user interface does not have to wait for the complete workflow to complete.

I accomplished this by starting the JbpmThreadServlet by adding in the web.xml:


<!-- Jpbpm Thread listener which will do async continuations & Timer -->

<servlet>
<servlet-name>JbpmThreadsServlet</servlet-name>
<servlet-class>org.jbpm.web.JbpmThreadsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JbpmThreadsServlet</servlet-name>
<url-pattern>/jbpmthreads</url-pattern>
</servlet-mapping>



When your web application starts, jbpm will spawn off a thread that will continue your workflow. This thread is also being used for sending out email notifications of task nodes.

A problem that rises here is that your action handlers don't have access to the seam contexts. To solve this problem I made a abstract action handler which will startup the seam lifecycle from a such an actionHandler if it does not exist. This code comes from the seam 2.0.0 framework which actually support this. I haven't tried it out still.

The code for the actionHandler is:



import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.core.BusinessProcess;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.exe.TaskInstance;

public abstract class SeamContextualActionHandler implements ActionHandler {

abstract public void executeInSeam(ExecutionContext context)
throws Exception;

public void execute(final ExecutionContext context) throws Exception {

try {
if (Contexts.isEventContextActive()
|| Contexts.isApplicationContextActive()) //not sure about the second bit (only needed at init time!)
{

} else {
Lifecycle.beginCall();
try {
initProcessAndTask(context);
executeInSeam(context);
} finally {
Lifecycle.endCall();
}
}
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static void initProcessAndTask(ExecutionContext context) {
BusinessProcess businessProcess = BusinessProcess.instance();
businessProcess.setProcessId(context.getProcessInstance().getId());
TaskInstance taskInstance = context.getTaskInstance();
if (taskInstance != null) {
businessProcess.setTaskId(taskInstance.getId());
}
}

}





Your actionHandlers should extend this class and implement the executeInSeam() method instead of the execute method from the ActionHandler interface.

1 comments:

jeremyrdavis said...

Jo,

This is really interesting. Did you get it working, btw?