Keep informed?
Subscribe for our newsletter now!

Easily generate BAM-Events wih jBPM

One interessting emerging technology today is Business Activity Monitoring (BAM). The basic idea is easy: Give the management (or operations) much more information about the status of your running business processes in almost realtime. In most implementations the trick is to generate Events about status change of business process instances and to send them to the BAM system. Today I want to describe how we generated these events easily and generic with jBPM in a current project.

Looking at existing BAM tools in the area of JBoss jBPM you will normaly take a look at SeeWhy. The integration with SeeWhy works by adding ActionHandlers to your jBPM processes in order to send events. Basically this isn’t a bad idea.

But in our project we have additional requirements. We face a company wide SOA with more than one jBPM instance running. The the central BAM system should not only process BAM events but also provide one central view on the status of all running processes everywhere.

Hence, one additional requirement is, to send events on process instance status change to the central system. To make it more concrete, our events are of the following type:

  • PROCESS_STARTED
  • WAIT_STATE_REACHED
  • TOKEN_ENDED
  • EXCEPTION_OCCURED

Obviously we don’t want to blur the business processes with details of this event sending, so the requirement is, to create the events generic within the infrastructure.

Environment

What means infrastructure? In the project we use JBoss AS 4.2.3, JBoss ESB 4.4, JBoss jBPM 3.3. So for including the event fire mechanism into this tooling we had a couple of options. First I want to describe briefly which we didn’t choose:

  • Change the BpmProcessor of the ESB. Beside the fact, that the BpmProcessor is implemented quite dirty and isn’t easy to extend, the main downside of this approach is, that you have to access jBPM via the BpmProcessor (means via the ESB) in order to fire events. This isn’t always the case, an easy eample are the jBPM timers.
  • Change the jBPM Commands. This isn’t easy to implement. First of all you have to make sure, that only the changed commands are used. And even then, you have to know what the Command does exactly in order to decide which events should fire.
  • Hook in a jBPM EventListener like I proposed in the Forum. Basically this isn’t yet implemented, so not my number one option 😉

So in the end I had the idea to hook in the jBPM Logging mechanism. jBPM writes audit log entries for everything it does, so these Logs provide all information required for the events.

Implementation of LoggingService in jBPM

So the main component of this “generic BAM event generator” is an own LoggingService which intercepts all written logs and fires necessary events. The LoggingService can be easily hooked in jBPM via configuration (jbpm.cfg.xml):

<jbpm-configuration>
  <jbpm-context>
    <service name="logging" factory="...event.BamLoggingServiceFactory" />
    <service name="persistence">
    ...

One small implementation detail: It is important to place the logger before the persistence service, since it may need the Hibernate session to load some information.

The LoggingService itself is easy, you can hook in the close() Method which is called after a jbpm signal is “calculated” completely and fire all necessary events. To give you an impression, here is a section of the LoggingService:

public void close() {
  try {
    fireOnExecutionEvents();
  }
  finally {
    clear();
    super.close();
  }
}

// getLogs lazy initializes an array to remember all logs to handle them at closing time
// clear() removes the reference to this array

public void log(ProcessLog processLog) {
  getLogs().add(processLog);
  if (processLog instanceof ProcessInstanceCreateLog) {
    fireOnProcessStartEvent(processLog);
  }
  super.log(processLog);
}

public void fireOnExecutionEvents() {
  if (getOccuredException()!=null) {
    ...
  } else {
    Set<Token> touchedTokens = new HashSet<Token>();

    ActionLog exceptionCandidate = null;
    String transitionName = null;

    for (ProcessLog log : getLogs()) {
      // set back to null, exceptions are only treated as unhandled
      // if it is the last log (see below)
      exceptionCandidate = null;

      // remember signal name
      if (log instanceof SignalLog) {
        SignalLog signalLog = (SignalLog)log;
        transitionName = signalLog.getTransition().getName();
      }                

      // handle current wait-state for all touched tokens
      if (!touchedTokens.contains(log.getToken())) {
        touchedTokens.add(log.getToken());
        fireOnCloseEvents(log.getToken());
      }
      // do NOT fire exception here, maybe it was handled by an ExceptionHandler!
      // handle errors on ActionHandler
      if (log instanceof ActionLog) {
        ActionLog actionLog = (ActionLog)log;
        if (actionLog.getException()!=null) {
          exceptionCandidate = actionLog;
        }
      }
    }

    // if an exception occurred but we don't have a FOLLOWING log
    // this means that the exception was NOT handled, so it was
    // thrown to the client.
    if (exceptionCandidate!=null) {
      fireExceptionEvents(exceptionCandidate.getToken(), transitionName, exceptionCandidate.getException());
    }
  }
}

In the SOA we build, firing events mean call an asynchronous JBoss ESB Service by the way. This service takes the event as XML and could be implemented somewhere somehow. In the project I talk about all the events are collected centrally and status of all running business process instances of all jbpm instances in the SOA is maintained in one central database (via EJB 3). This is illustrated here:

Exceptions & Transactions

One wish I faced in a lot of jbpm projects already is to save some log information in the case of an unhandled exception during processing. Unhandled means, this exception is thrown to the client and rolls back the transaction. During rollback, the log entries are rolled back as well. I discussed about that a couple of times, but in my eyes, it is important that the logs roll back, it is what ACID promises: All things in the transaction, written in the log, did never happen.

But I understand, that in the case of an exception it would be very handy to have the thrown away logs at hand to get a better idea what caused the exception.

What we did was to throw an EXCEPTION_OCCURED event, but in contrast to all other events, this event is fired in a own transaction. On this event we attached not only the exception details but also the jbpm logs. This event will reach our BAM tool, so in this central tool we can see occurred exceptions, with Stacktrace, jBPM log, sent signal and some more. This is a really cool improvement for operations!

The WrapperCommand

In the picture above you may have noticed the WrapperCommand. But I talked about not changing the Commands? This is just an additional trick for convenience: I use the WrapperCommand to wrap the original commands. What I can do there, is to intercept the exception thrown to the client. By this approach I have the exception object and know, that an exception is thrown to the client:

public class BamWrapperCommand implements Command {
  public Object execute(JbpmContext jbpmContext) throws Exception {
    try {
      Object result = wrappedCommand.execute(jbpmContext);
      return result;
    } catch (RuntimeException ex) {
      BamDbLoggingService.notifyLoggingServiceOfException(ex);
      throw ex;
    }
    finally {
    }
  }
}

Not in all cases the WrapperCommand can be plugged in (e.g. when using the web-console). In this case, an occurred exception is taken from the jbpm logs, where I have less information and can never be 100% sure, if the exception logged wasn’t maybe handled by a jbpm exception handler. To be a bit more sure about not handling the wrong exceptions, I only treat them as exception if no further log is available (means, the processing stopped with this exception).

Business Events

By the way: I talked about not changing jbpm process in order to send all these events. This is true, but not for business motivated events. For these, we used the same mechanism as SeeWhy does: Adding an ActionHandler which fires the event. The important event we know is BUSINESS_MILESTONE which is fired, when we cross some milestone with business meaning. So the central process status database also contains some information on business phases.

In the process-definition.xml it will look like this:

<event type="node-enter">
  <action name="milestone1" class="...BusinessMilestoneAction">
    <name>milestone1</name>
  </action>
</event>

The Result

Here an impression of the resulting central database (only part of it):

+---------+-------------------+---------------+--------+-----------+---------------------+------------+---------------------+---------------+---------------------+
| tokenId | processInstanceId | parentTokenId | status | state     | stateTimestamp      | phase      | phaseTimestamp      | lastEventType | lastEventTimestamp  |
+---------+-------------------+---------------+--------+-----------+---------------------+------------+---------------------+---------------+---------------------+
|      83 |                38 |             0 | ENDED  | end-state | 2008-12-04 14:31:26 | milestone2 | 2008-12-04 14:31:26 | TOKEN_ENDED   | 2008-12-04 14:31:26 |
+---------+-------------------+---------------+--------+-----------+---------------------+------------+---------------------+---------------+---------------------+

Conclusion

Again jbpm proved to be quite flexible. Adding the LoggingService was an easy thing to do and is quite powerful tosupport BAM or central Process Administration requirements. But even if the solution presented here sounds quite easy and nice, it took me a couple of time to figure out all nuts and bolts and to understand all consequences of the different solutions approaches 😉

Already read?

Scientific performance benchmark of open source BPMN engines

Why BPMN is not enough

Decision Model and Notation (DMN) – the new Business Rules Standard. An introduction by example.

New Whitepaper: The Zero-Code BPM Myth

One Response

Leave a reply