You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 25 Next »

BeanFlow is a lightweight Java library for building workflows using beans to orchestrate events. You can think of BeanFlow as a simple alternative to BPEL where the workflows are all specified and implemented using Java code rather than declarative XML.

Motivation

When building highly concurrent or distributed applications it is very common for there to be many events happening; often asynchronously and in different threads and its very common to need to perform kinds of workflow or orchestration across these events.

Now you certainly can use things like BPEL to solve these kinds of problems. However often this is a bit heavy weight & complex and you just want to have a bean based workflow using regular Java code to represent the activities involved in the workflow.

Transforming the traditional workflow approach

One of the main goals of BeanFlow is to reuse what the Java platform is good for in the workflow space; then supplement it with missing abstractions rather than inventing the wheel in a sub-optimal way.

Traditional approach

BeanFlow approach

use a declarative workflow language to specify a workflow

write a Java class

use XML markup or pictures to define loops and flows

use regular Java code (if/for/while)

implement a generic state and persistence framework for workflow instances

use regular Java fields in your workflow class, then use JDBC/DAO/JPA to deal with persistence

Use cases

When working with concurrent or distributed applications there are many use cases for needing to orchestrate among multiple concurrent events. Here are a few examples from our use cases implementing ServiceMix and ActiveMQ

  • starting dependent services; where a parent component is dependent on the child components starting; where the start process may be asynchronous in different threads. e.g. there may be a recovery process on startup which you need to wait for.
  • implementing master-slave type protocols where you need to monitor the state of the master and slave to make decisions on what to do; together with dealing with transitions from Started to Recovering to Running then maybe to FailingOver etc.
  • implementing message orchestration. You may want to implement some simple orchestrations, waiting for either a response to arrive or a timeout to fire etc

Activity overview

An activity is just a POJO which implements the Activity interface; it has a simple lifecycle, you can start them and stop them and ask their status.

Once a flow has been started via the start() method it can take an arbitrarily long time to complete; you can see if the flow has completed via the isStopped() method. You can explicitly complete a flow using the stop() method whenever you like; typically when you are responding to state changes.

An activity could fail for some reason (such as it timed out or some error occurred) so there is a method isFailed() which can be used to easily inspect the activity. Rather like the stop() method, you can call fail(reason) if you wish to terminate an activity with the activity marked as the Failed rather than Stopped state.

Using timeouts

Its very common to add timeouts onto activities so that if the activity is not complete by a certain time then the activity is stopped & failed; often another parent activity may then do something differently if one of its child activities fails.

There is a useful base activity called TimeoutFlow which you can derive from to make your own activity which can be started/stopped/failed and which can be timed out.

You can explicitly register the activity with a timer and call the onTimedOut() method yourself or just call the startWithTimeout() method to start the activity registering the timeout.

Join conditions

In workflows you often want to fork parallel steps and then join on certain events. You often have complex logic to decide on what the join condition is. e.g. in pseudo code

fork steps A, B, C
join on A and (B or C)

In BeanFlow we implement join conditions using regular Java code. The basic idea is that a Flow will listen to changes in a number of different State objects such as fields a, b and c. You can then write an activity bean to perform the join condition you need. When the condition is met you can then perform whatever logic you wish such as

  • stopping the activity
  • forking off a new child activity
  • changing your state
  • changing the state of some other bean
  • calling arbitrary Java code

Here is an example activity implemented in Java code for the above code.

Error formatting macro: snippet: java.lang.NullPointerException

Composing activities

One of the main reasons for using an object orientated language is to make composition and reuse possible; similarly BeanFlow allows you to compose activities together to make modular and reusable workflow constructs easily. So BeanFlow attempts to create a collection of reusable activities which you can then use to derive from or aggregate to make whatever activities you need.

Composition Flows

Description

JoinAll

Performs a join on all the given child activities. So the activity waits until all the child activities have completed, then it completes itself. You can add additional constraints to the activity using derivation. The default is to wait for all the child activities to complete; though you can enable fast-fail mode so that the activity fails as soon as a child activity fails

JoinQuorum

This activity is useful for implementing clustering style activities where you want a quorum of activities to complete. e.g. if you have 5 child activities you want to wait for at least 3 activities to complete succesfully before continuing

Here is an example of performing joins using collections of child workflows

Error formatting macro: snippet: java.lang.NullPointerException

Building workflows

So we've been over the basics of activities. Remember they are just Java bean so you can implement them however you wish.

However often folks think of workflows as a number of discrete steps; using a kind of state machine to move from step to step. One real simple approach to
writing workflows in this model is to use a method to map to a workflow step.

The following example is a fairly large one, but it shows a variety of different concepts

  • suspending workflows (such as when they wait for external events such as user input or an asynchronous message to arrive)
  • looping & using predicates
  • forking and joining child activities
  • declaratively moving from one step to another
Error formatting macro: snippet: java.lang.NullPointerException
  • No labels