Thursday, April 16, 2009

jBPM from the trenches - a state-proxy solution final report

As previously posted, we have been working on something we call a state-proxy.Now that it has been realized I can provide some more details.

The problem was that we had no ESB solution in our SOA infrastructure and all processes in jBPM are modeled using nodes, decisions, process-states, and tasks. We wanted to make use of state nodes to provide at least the ability to enter real wait states and persist the process data. This was to give us more reliability and constancy in our process implementations.

In each state node a single service call is made through the infrastructure provided by a state-proxy. This state-proxy infrastructure is used to create a new thread of execution and release the process to enter a wait state. The state-proxy will then handle the service call, serialize the results or exceptions, place them in the context, and signal back to the waiting process.

The process flow shown here (top left) depicts the integration test that was run using our state-proxy infrastructure and state nodes. The process definition for these state nodes looks something like this:

<state name="someServiceCall">
<event type="node-enter">
<action class="some.domain.handler.SomeServiceCallHandler" />
</event>
<event type="node-leave">
<action class="some.domain.handler.SomeServiceCallHandler" />
</event>
<transition to="anotherServiceCall?" />
</state>

Each state node makes use of a handler that contains three methods:

/**
 * Where the service proxy is created, the input object for the service call
 * is filled, and the service call is made.
 *
 * This method is run on the event 'on node-enter'. 
 */
public void onPerformCall(ExecutionContext exc) {}

/**
* When the state-proxy call has completed, it will signal back to the process
* which reacts to the event 'on node-leave'. The state-proxy has set a context
* variable RESULT_DATA which is used to transport the JBoss serialized results
* and is placed into the input 'result' object. This method allows processing
* of the service results before taking the default transition to exit the state
* node.
*
* This method is run on the event 'on node-leave'.
*/
public void onHandleResults(ExecutionContext exc, Object result) {)

/**
* When the state-proxy call has completed, it will signal back to the process
* which reacts to the event 'on node-leave'. The state-proxy has set a context
* variable RESULT_ERROR which is used to transport the JBoss serialized exception
* and is placed into the exception 'e' object. This method allows processing
* of the exception before taking the default transition to exit the state
* node.
*
* This method is run on the event 'on node-leave'.
*/
public void onHandleException(ExecutionContext exc, Exception e) {}

I am unable to provide exact details from the code of the state-proxy, but I can say that this enables us to detach our service calls from the calling process node, persist our state, and await the results. These results can then be handled or the resulting error can be processed before continuing with the flow.

Some issues remain.

It is not possible to exit a state node via a transition that you may choose from the 'on node-leave' event (either from our onHandleResults or onHandleException). The signal being sent back from the state-proxy has already retrieved the process context and instance and filled the node with the default transition. We are looking into this and you can see the current way of working is to place a decision node directly after the state to branch as needed.

Another rather simple problem, is that a failing signal from the state-proxy back to the process is a single point of failure that could lead to a process 'waiting' forever. We need to build in a mechanism, such as email to a process administrator, to ensure that processes do not fall asleep at the end of an 'on node-enter' event.

I hope this brings some of the promised details to light on what we are doing with a state-proxy to ensure some sort of process reliability and data consistency without the use of an ESB.