camunda BPM OSGi - Event Bridge

I have implemented the eventing feature already some months ago but I haven't managed to advertise it a little bit more until now. So, let's praise my work ;)

I'll start with some background information, which you can skip if you're familiar with camunda BPM and the OSGi EventAdmin. Then, some information about the what and how follows.

Let's start with OSGi eventing.

OSGi Event Admin

The Event Admin is a part of the OSGi Compendium Specification. It is a way to communicate between bundles in a decoupled way by sending events. The communication follows a publish/subcribe scheme.

One bundle obtains the EventAdmin service, creates an Event object and sends it. Every event is created with a certain topic and can contain arbitrary String properties in a key-value way. Topics are hierarchical separated by a "/" and wildcards are allowed. E.g. org/osgi/framework/BundleEvent/STARTED is a topic used by the OSGi framework.

Events can be sent in a synchronous or asynchronous way and additional LDAP filters can be used based on the properties.

You can find a good example on the Apache Felix website.

Now that we know a little bit about the EventAdmin let's take a look at camunda BPM.

camunda BPM events

During the execution of a process certain events occur, e.g. a task is being assigned or a process end. To be able to "see" those events the user has to register either an ExecutionListener or a TaskListener (for more details see here and here).

The common way to register the listeners is to directly add them to the process definition, i.e. the .bpmn file. But there are certainly cases where we do not own the process file but would like to receive events (e.g. for monitoring).

Let's see how to achieve this in an OSGi environment.

camunda BPM OSGi - Event Bridge

I gotta admit the idea of an event bridge is not my own, because the CDI extension for camunda BPM already has an CDI event bridge. Anyways, for OSGi this feature was missing. I'll explain to you what happens internally and how you can use it.

What happens?

The OSGi event bridge implementation exports a service that is a BpmnParseListener. Whenever the engine parses a process definition this listener will become active and attach TaskListener and ExecutionListener wherever possible. But these listeners aren't full implementations. They are dynamic proxies with a special InvocationHandler.

When the InvocationHandler is being invoked it checks if the OSGi event bridge is still active and if the EventAdmin is present. If yes, it instantiates a new OSGiEventDistributor, which creates a new event and fills the properties.

I've tried to use all properties the camunda events provide and put them into the event properties. You can see a full list in this class.

This is basically what is happening. So, what can you do with the event bridge?

How to use it?

Before you can make use of the OSGi event bridge you have to add the OSGiEventBridgeActivator as a BpmnParseListener to your ProcessEngineConfiguration. You do this with the method setCustomPreBPMNParseListeners(). Unfortunately, there is no way to add the listener to an already created engine. After adding the listener events are being published. The event topics are:

  • org/camunda/bpm/extension/osgi/eventing/TaskEvent
  • org/camunda/bpm/extension/osgi/eventing/Execution

Of course you can use an asterisk after ../eventing/ to match both.

Wherever you want to listen to events, you can create your own EventHandler and subscribe to the topic you need/want. A simple example would be:

EventHandler eventHandler = new EventHandler() {
  @Override
  public void handleEvent(Event event) {
    Logger.getLogger("Event occured: " + event.getTopic());
  }
};
Dictionary props = new Hashtable();
props.put(org.osgi.service.event.EventConstants.EVENT_TOPIC, org.camunda.bpm.extension.osgi.eventing.api.Topics.ALL_EVENTING_EVENTS_TOPIC);
bundleContext.registerService(EventHandler.class.getName(), eventHandler, props);

Since many information is inside the event properties you can also use a more sophisticated LDAP filter expression based on that information. E.g. if you only want to receive events for a certain process you can do this:

EventHandler eventHandler = new EventHandler() {
...
};
Dictionary<String, String> props = new Hashtable<String, String>();props.put(EventConstants.EVENT_TOPIC, Topics.ALL_EVENTING_EVENTS_TOPIC);
props.put(EventConstants.EVENT_FILTER, "(processDefinitionId=invoice");
bundleContext.registerService(EventHandler.class.getName(), eventHandler, props);

And that's it. At the moment there is no way to limit the applications that are allowed to receive events, so everybody can see all the events if he subscribes to them. If you have an idea how to do this in a nice way, please let me know.

I hope you can make good use of the OSGi event bridge. My plan is to release camunda BPM OSGi 2.0.0 (which includes the event bridge) shortly after camunda BPM 7.5.0 is being released.

Did you find this article valuable?

Support Ronny Bräunlich by becoming a sponsor. Any amount is appreciated!