"The nodetype node expects one subelement action. The action is executed when the execution arrives in the node. The code you write in the actionhandler can do anything you want but it is also responsible for propagating the execution."
And you may have a different opinion, but I think it's quite tedious to have to repeat the same kind of boiler plate code in each and every ActionHandler implementation used in Nodes, so I wanted to come up with a way to do it more generically.
This standard node type actually gives you a choice as it comes to its execution:
- as stated above you add an Action (directly to the Node) and have it execute that, or
- you don't add an Action and have it leave through the default Transition.
The simple approach I chose to illustrate this was to provide an abstract base class, which implements the ActionHandler interface, which has to be extended by all action handlers in your code base. Well, nearly all, but I'll get back to that later. Surely there would be other approaches that will come up with the same result (like annotations or aspects); just knock yourself out.
Such a base class would look something like this:
public abstract class AbstractActionHandler implements ActionHandler { protected String transitionName; public final void execute(ExecutionContext ctx) throws Exception { performAction(ctx); if (ctx.getEvent() == null) { // When leaving the node we can either have a transition set to be taken or else take the default transition. if (StringUtils.isBlank(transitionName)) { ctx.getNode().leave(ctx); } else { ctx.getNode().leave(ctx, transitionName); } } } // To be implemented by concrete subclasses; execute the intended Java code and optionally set the transition to be taken. public abstract void performAction(ExecutionContext ctx) throws Exception; }
Now the main 'trick' here is to know when to continue the execution and when not to; as you can tell from the code above this can be derived from the fact whether an event is available in the execution context. At the basis of this fact is the knowledge at which places in a process definition Actions can be added (and when/how they're executed in those instances), and cross-reference that with the required point of continuation (an Action directly in a Node).
You can add an Action at six different places:
- Directly to a Node: which is what we're talking about here for having automatic continuation.
- In an event (e.g. 'node-enter'): most of the time that's an explicit event in the process definition.
- In a Transition: actually then it's executed from within a 'transition' event.
- In a timer: here it's executed after the 'timer' event is fired, so not within it.
- In an exception handler: executed from within GraphElement's raiseException(...) method, which also has no event associated with it, but does put the current Exception in the execution context.
- Directly to the process definition (highest level): these are just for reference from within other elements, so not an 'extra' type in any sense - so we'll just forget about this one for now.
It is however possible to extend the 'trick' for these other two instances, by checking the execution context for the availability of a timer (ctx.getTimer() == null) and/or the availability of an exception (ctx.getException() == null) respectively - it depends for which of the cases you want to provide a base class (or mechanism of your choice) in order to have these automatic continuations I was after.
Credit where credit is due: thanks to Arnoud W. for the hint!