As there is only a single CommandExecutorThread on which all async ActionHandler execute methods are called, it is important to write execute methods that run asynchronously when these may be long running; otherwise the CommandExecutorThread is held up from processing other actions.
This presents a thread safety problem. The problem lies in beginning a new transaction from within the ActionHandler execute method; e.g.
(1) Prior to executing an action, the JBPM framework opens a new JbpmContext and locks the token.
(2) The framework executes the action by calling ActionHandler.execute(ExecutionContext executionContext); the ExecutionContext provides access to the JbpmContext opened by the framework.
(3) I start a new thread from within the execute method to execute asynchronously.
(4) The execute method exits and the framework saves and closes the original JbpmContext and token
(5) The new thread opens a second JbpmContext and finds and progresses the token (this being possible even if the framework has not unlocked its token yet because the new thread gets a new copy of the token that is not locked; tokens are locked in memory; not at a database level).
(6) The new thread saves and closes the second JbpmContext.
The problem arises when the framework attempts to save a token that has already been progressed by the new thread; or the new thread tries to progress a token that has since been updated by the framework ? we have a race condition.
I think that there are a couple of obvious solutions:
(1) As a work-around, the execute method starts the new thread and then sets a context variable in the process instance ? this will not be saved to the database until the framework closes its JbpmContext. The asynchronous thread sleeps until the process instance has the context variable set before progressing the token.
(2) The JBPM framework examines the class of the ActionHandler before calling its execute method; if it is an instance of an AsyncActionHandler, the framework gets the process instance ID and task instance ID from the token and then saves and closes its JbpmContext. It then calls the execute method of the AsyncActionHandler, passing in the process instance ID and task instance ID (the latter may be null) instead of the ExecutionContext.
I think that the latter solution or something like it would be a very useful addition to the JBPM framework.