IBM Business Automation Manager Open Editions (BAMOE) has evolved its event streaming capabilities between versions 8 and 9.

BAMOE v8 allows users to configure the KIE Server to emit Kafka messages about every event when a process, case, or task is completed.

BAMOE v9 introduces a more modular and flexible approach using add-ons to enable messaging and event-driven capabilities in Quarkus or Spring Boot projects. Events are published in the CloudEvents format, making them easily consumable by external systems.

Events are categorized into:

  • Messaging Events: Related to BPMN message elements.

  • Process/Runtime Events: Related to process instances, tasks, and variables

Messaging event add-on

This add-on enables integration between BPMN message elements and external event sources or listeners, depending on the message type.

For more information see Using Events in Workflows

Process event add-on

This add-on emits events when operations alter the state of a process. Such operations include the creation of a process instance, task transitions, and variable modifications. Events are published in the CloudEvents format, with the data field containing a JSON representation of one of the following types

For more information see Using Events in Workflows

Migrating Event Listeners from BAMOE v8

Every time a process or task changes to a different point in its lifecycle, the Process Engine generates an event. You can develop a class that receives and processes such events called an event listener.

The BAMOE 9.5 Process Engine provides a default implementation of the event listener interface. You can use this implementation as a starting point for your own implementation via the following two interfaces: ProcessEventListener and UserTaskEventListener.

The main elements to consider when migrating your Event Listeners from BAMOE 9.5 to 8.0.x are:

Event timing

In 8.0.x, you had separate before/after callbacks to distinguish when your code runs relative to the state change. In (VERSION), the single callback is invoked after the state change, and you have access to both the old and new states via the event object.

Interface changes

TaskLifeCycleEventListener is replaced by UserTaskEventListener.

Event object changes

Replace TaskEvent with the appropriate event type (UserTaskStateEvent, UserTaskAssignmentEvent, UserTaskVariableEvent, etc.).

CDI registration

In 9.5 CDI is used for listener registration. There is no need for manual registration

If you have implemented your own event listener in 8.0.x the following sections provide more information to help you upgrade your code to work with 9.5.

For more information about migration, see Tutorial 05: Event Listeners Migration from BAMOE v8 to v9.

The Process Event Listener Interface

The ProcessEventListener interface is mostly the same in 8.0.x and 9.5. The main difference is that are few additional event methods in 9.5, namely:

  • onSignal

  • onMessage

  • onError

  • onMigration

  • onProcessStateChanged

  • onNodeStateChange

The User Task Event Listener Interface

The TaskLifeCycleEventListener interface in 8.0.x has been replaced by UserTaskEventListener in 9.5. The main difference is that the TaskLifeCycleEventListener interface had separate beforeTaskXXX() and afterTaskXXX() methods. In 9.5 a different pattern where a single callback method receives an event containing both the old and new states, as shown in the code below:

// Single callback with old/new state in event
onUserTaskState(UserTaskStateEvent event) {
    UserTaskState oldStatus = event.getOldStatus();  // Previous state
    UserTaskState newStatus = event.getNewStatus();  // Current state
}

This architectural change means that a single event object contains all required information rather than before/after callbacks. This makes it easier to implement a listener that reacts to a single event.

The example below shows BAMOE 8.0.x code:

public class MyTaskListener implements TaskLifeCycleEventListener {

    @Override
    public void beforeTaskClaimed(TaskEvent event) {
        // Validation logic before claim
        String taskName = event.getTask().getName();
        System.out.println("About to claim task: " + taskName);
    }

    @Override
    public void afterTaskClaimed(TaskEvent event) {
        // Notification logic after claim
        String owner = event.getTask().getActualOwner().getId();
        System.out.println("Task claimed by: " + owner);
    }
}

And the equivalent code in BAMOE 9.5 is:

@ApplicationScoped
public class MyTaskListener implements UserTaskEventListener {

    @Override
    public void onUserTaskState(UserTaskStateEvent event) {
        UserTaskState oldStatus = event.getOldStatus();
        UserTaskState newStatus = event.getNewStatus();

        // Check if this is a claim transition (Ready → Reserved)
        if (oldStatus != null && "Ready".equals(oldStatus.getName())
            && "Reserved".equals(newStatus.getName())) {

            // Same logic as BAMOE 8 beforeTaskClaimed
            String taskName = event.getUserTask().getTaskName();
            System.out.println("About to claim task: " + taskName);

            // Note: actualOwner info available via getUserTaskInstance() if needed
            System.out.println("Task claimed");
        }
    }
}

For more information on the Event Listener Lifecycle in 9.5 see Working with Events → Implementing an Event Listener Lifecycle in the Process Engine

Migrating External Event Emitters from BAMOE v8

In BAMOE 8.0.x, users who needed to replicate engine state to an external read model (for example, an Elasticsearch index that powers a custom dashboard) implemented a custom EventEmitter on top of TransactionalPersistenceEventManager. The engine delivered InstanceView-style snapshots through the deliver / apply / drop lifecycle around the JTA transaction.

In BAMOE 9, the v8 type names (EventEmitter, InstanceView, TransactionalPersistenceEventManager) no longer exist. The equivalent extension point is the org.kie.kogito.event.EventPublisher interface in kogito-api. The engine batches the events produced during each command and delivers the batch to every registered publisher when the unit of work commits. If the unit of work is aborted, the batch is discarded and the publisher is not called.

The customer surface is preserved: one (or two) implementation classes, registered through standard CDI / Spring annotations instead of a META-INF/services file.

Type-by-type mapping

The following table shows the 8.0.x types and their 9.5 equivalents:

BAMOE 8.0.x BAMOE 9.5

org.jbpm.persistence.api.integration.EventEmitter

org.kie.kogito.event.EventPublisher

TransactionalPersistenceEventManager (deliver / apply / drop dispatcher)

UnitOfWork (start / end / abort) plus EventManager

InstanceView

DataEvent<?> - see org.kie.kogito.event.process. and org.kie.kogito.event.usertask.

TaskInstanceView

UserTaskInstanceStateDataEvent

ProcessInstanceView

ProcessInstanceStateDataEvent

META-INF/services registration

@ApplicationScoped (Quarkus) or @Component (Spring Boot)

For the full EventPublisher SPI reference, the two implementation styles, and a working code example, see Working with Events → Publishing engine events to an external read model.