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?
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).
@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());
}
}