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.

3 comments:

jeremyrdavis said...

Jo,

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

Diego Santiviago said...
This comment has been removed by the author.
Diego Santiviago said...

Contexts.isEventContextActive() is returning true, and execute() is not executed. what i need to do ?