How to implement a generic Spring Boot EventListener when the process starts?

We have already implemented a couple of workflows in our Spring Boot application. Now I want to implement a generic execution listener that is called exactly once when a workflow starts (or was started). I know that I could edit each process definition in Camunda Modeler and add a process start listener bean. However, I would prefer to do this programmatically at the Spring Boot level.

My question is: How can I define the condition for the Spring Boot EventListener so that it is called exactly once at each process start?

Here is what I have so far:

@Component
public class ProcessEventListener {

  @EventListener(
      condition =
          "#execution.eventName=='start' "
              + "&& #execution instanceof T(org.camunda.bpm.engine.runtime.ProcessInstance) "
              + "&& #execution.activityInstanceId == null")
  public void onProcessStart(DelegateExecution execution) {
		// do something
  }
}

In fact, this code seems to work as expected. But I doubt that there is not a better way to do this. If I omit the conditions “#execution instanceof T(org.camunda.bpm.engine.runtime.ProcessInstance) && #execution.activityInstanceId == null”, I get too many calls to my method because it is also called on each task execution, but I am only interested in the process start.

Is there a better way to define the condition for this listener so that it is called only once at process start?

Hello my dear! In your “start event” could you not put the “execution listner” as START, calling your Java class every time an instance starts in your flow?

I believe that the ideal would be for you to implement “JavaDelegate” in your class as well.

i hope this helps!

Regards.
William Robert Alves

Hi @phloxy,

additional to Williams hint, you can register a process engine plugin and add an execution listener to each process instance start event (and skip the spring-boot extension).

Then you don’t need the condition anymore.

You can find a example of a process instance start listener here: Delegation Code | docs.camunda.org

The packaging in a process engine plugin is shown here: camunda-bpm-examples/process-engine-plugin/bpmn-parse-listener at master · camunda/camunda-bpm-examples · GitHub.

Let us know which implementation is easier. Technically, you will get the same result.

Hope this helps, Ingo

@Ingo_Richtsmeier Many thanks for the hint with the engine plugin. I like the approach with the ParseListener / EnginePlugin. It’s a bit more code, but with the advantage of having full control over the events to be notified and not having to rely on somehow guessed conditions (as in my original approach with the EventListener).

Now my solution looks like this (using Spring Boot / Lombok / Apache Commons)

@Component
@RequiredArgsConstructor
public class ProcessEventPlugin extends SpringBootProcessEnginePlugin {
  private final ProcessEventParseListener processEventParseListener;

  @Override
  public void preInit(SpringProcessEngineConfiguration processEngineConfiguration) {
    processEngineConfiguration.getCustomPostBPMNParseListeners().add(processEventParseListener);
  }
}

@Component
@RequiredArgsConstructor
public class ProcessEventParseListener extends AbstractBpmnParseListener {
  private static final List<String> EXECUTION_EVENTS = List.of(EVENTNAME_START, EVENTNAME_END);

  private final List<AbstractProcessEventListener> eventListeners;

  @Override
  public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
    if (CollectionUtils.isNotEmpty(eventListeners)) {
      eventListeners.forEach(
          listener ->
              EXECUTION_EVENTS.forEach(event -> processDefinition.addListener(event, listener)));
    }
  }
}

public abstract class AbstractProcessEventListener implements ExecutionListener {

  public abstract void onProcessStart(DelegateExecution execution);

  public abstract void onProcessEnd(DelegateExecution execution);

  @Override
  public final void notify(DelegateExecution execution) {
    if (StringUtils.equals(execution.getEventName(), EVENTNAME_START)) {
      onProcessStart(execution);
    } else if (StringUtils.equals(execution.getEventName(), EVENTNAME_END)) {
      onProcessEnd(execution);
    }
  }
}

So far the framework part. Now we can create one or many implementations of AbstractProcessEventListener:

@Component
@Slf4J
public class ProcessEventListener extends AbstractProcessEventListener {

  @Override
  public void onProcessStart(DelegateExecution execution) {
    log.debug("Process {} started", ((ExecutionEntity) execution).getProcessDefinition().getName());
    // do something ...
  }

  @Override
  public void onProcessEnd(DelegateExecution execution) {
    log.debug("Process {} ended", ((ExecutionEntity) execution).getProcessDefinition().getName());
  }
}