maandag 15 april 2013

A basic outline of a workflow system using J2EE and CDI


For one of my customers, we needed a very simple workflow-framework which coordinates the flow between methods inside a J2EE6 application on Glassfish. The business required to implement the following simple logic:
  1. Create a job.
  2. When step 1 was successful, then start a job (which affects some thousand objects) and send an email
  3. End a job when step 2 was successfull.
  4. Mark the job as failed when one of the steps couldn't be completed due to the implemented business-logic.
  5. Provide a handler to handle exceptions.
Taking a fully fledged workflow-system was way over the top to solve this problem. The workflow will also remain unchanged; and when it is changed, the code needed to be altered to. We needed to come up with a simple workflow-management which is bound to transitions and methods.
So, first we decided to create an observer the change the transitions of the workflow-steps. This is basically a very simple method which alters the status and stores it in the database. The next step was to create some annotations to add to the methods which needed to be executed during a certain step. We annotate our workflow methods as follows:

    @Asynchronous
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    @Workflow
    @WorkflowStep(from = Status.IDLE, to = Status.PROGRESS)
    public void startJob(
            @WorkflowActionHandler
            @Observes(notifyObserver = Reception.ALWAYS,
            during = TransactionPhase.AFTER_SUCCESS)
            final JobWorkflowEvent event) {}

This straight-forward approach is very clean and simple. Basically it says that this method will be executed after the transition from a Status.IDLE to Status.PROGRESS. Although we use an enum here, you could take any arbitrary integer. Using the CDI annotations we get some additional power. This method will only be executed after the success of the previous step. Here you can combine the full power of CDI with some basic workflow concepts to create a nice system.
Now remains the problem of the transition handling. The transitions are handled in an interceptor which is marked by the @Workflow annotation.

    @Interceptor @Workflow
    public class WorkflowInterceptor {
    }

This interceptor does not do much more than changing the transition state when the method pinged by CDI has the correct workflow-steps. It uses some reflection to figure that out and allows access to the method. 
To handle exceptions, we introduce a new annotation:

    @Asynchronous  
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    @Workflow @WorkflowException
    public void excJob(
            @WorkflowActionHandler 
            @Observes(notifyObserver = Reception.ALWAYS, 
            during = TransactionPhase.AFTER_FAILURE) 
            final JobWorkflowEvent event) {}

Whenever one step fails unexpected, control is transferred to this method where certains actions can be executed. We need this annotation to prevent other methods to pick this event up. 
Using CDI together with some custom annotations really did the job and works fine.

Geen opmerkingen:

Een reactie posten