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.