Create Execution Listener in Spring[Modified]

Hi! I’m trying to create an execution listener to define into my process engine spring configuration.

Following a simple execution listener:

 @ProcessApplication
 public class CustomExecutionListener implements ExecutionListener{

@SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(CustomExecutionListener .class);

public class BPMExecutionListener implements ExecutionListener {

  @SuppressWarnings("unused")
  private static final Logger LOG = LoggerFactory.getLogger(BPMExecutionListener.class);

  public class BPMExecutionListener implements ExecutionListener {

      @SuppressWarnings("unused")
      private static final Logger LOG = LoggerFactory.getLogger(BPMExecutionListener.class);

      @Override
      public void notify(DelegateExecution execution) throws Exception {
      LOG.info(execution.getId());
      }
   }

so I defined it into Application context:

    <property name="processEnginePlugins">
        <list>
            <ref bean="processApplicationEventListenerPlugin" />
        </list>
    </property>
</bean>

 <bean id="processApplicationEventListenerPlugin"
    class="org.camunda.bpm.application.impl.event.ProcessApplicationEventListenerPlugin" />

How can I run globally my custom ExecutionListener using spring?

Thanks a lot!

Hi @devj87,

The code you posted looks a bit messed up and does not compile. Perhaps something got mixed up?

The global execution listener is an instance of ExecutionListener that is returned by ProcessApplicationInterface#getExecutionListener. In a Spring context you are probably using either SpringProcessApplication or SpringServletProcessApplication (confer https://docs.camunda.org/manual/7.5/user-guide/process-applications/the-process-application-class/#the-springprocessapplication). You could subclass the process application class, add an ExecutionListener field, override #getExecutionListener appropriately and then populate that field in your Spring context.

Cheers,
Thorben

Thanks for reply @thorben

So I try to create a Listener following you post!

Have you yet succeeded in installing global task and execution listeners with Spring?

I did register the plugin in the SpringProcessEngineConfiguration:
configuration.getProcessEnginePlugins().add(new ProcessApplicationEventListenerPlugin());

However I have not yet found a way to override getTaskListener() or getExecutionListener() of the SpringServletProcessApplication, because our code just calls processEngineConfiguration.buildProcessEngine() to initialize the process engine (like ProcessEngineFactoryBean.getObject does) without explicit configuration of a ProcessApplication bean. Can I add this in the code? (We have an application that must not start the process application before a certain installation level is reached.)

When I (like shown in the examples and devj87 tried) create my own
@ProcessApplication
public class MyProcessApplication extends SpringServletProcessApplication {
its methods getTaskListener() and getExecutionListener() are never called.

A (former) co-worker (thank you, M.O.!) found a solution, perhaps others find it useful: add a ProcessEnginePlugin, a BpmnParseListener, and your own global TaskListener like this (not sure if all is necessary, hopefully I did not miss something):

ProcessEngineConfig:

@Configuration
public class ProcessEngineConfig {
...
  @Bean
  public ProcessEngineConfiguration processEngineConfiguration(PlatformTransactionManager transactionManager) {
    SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
    ...
    configuration.getProcessEnginePlugins().add(myProcessEnginePlugin());
    return configuration;
  }

  @Bean
  public MyProcessEnginePlugin myProcessEnginePlugin() {
    return new MyProcessEnginePlugin();
  }

  @Bean
  public MyGlobalBpmnParseListener myGlobalBpmnParseListener() {
    return new MyGlobalBpmnParseListener();
  }

  @Bean
  public GlobalCreateTaskListener createTaskListener() {
    return new GlobalCreateTaskListener();
  }
}

MyProcessEnginePlugin:

public class MyProcessEnginePlugin extends AbstractProcessEnginePlugin {

  @Autowired
  private MyGlobalBpmnParseListener myGlobalBpmnParseListener;

  public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
    List<BpmnParseListener> preParseListeners = processEngineConfiguration.getCustomPreBPMNParseListeners();
    if (preParseListeners == null) {
      preParseListeners = new ArrayList<>();
      processEngineConfiguration.setCustomPreBPMNParseListeners(preParseListeners);
    }
    preParseListeners.add(myGlobalBpmnParseListener);
  }
}

GlobalTaskListener:

public interface GlobalTaskListener extends TaskListener {
}

MyGlobalBpmnParseListener:

public class MyGlobalBpmnParseListener extends AbstractBpmnParseListener {

  @Autowired(required = false)
  private List<GlobalTaskListener> globalTaskListeners;

  @Override
  public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) {
    if (globalTaskListeners != null) {
      UserTaskActivityBehavior activityBehavior = (UserTaskActivityBehavior) activity.getActivityBehavior();
      TaskDefinition taskDefinition = activityBehavior.getTaskDefinition();
      for (GlobalTaskListener globalTaskListener : globalTaskListeners) {
        taskDefinition.addTaskListener(TaskListener.EVENTNAME_ASSIGNMENT, globalTaskListener);
        taskDefinition.addTaskListener(TaskListener.EVENTNAME_CREATE, globalTaskListener);
        taskDefinition.addTaskListener(TaskListener.EVENTNAME_COMPLETE, globalTaskListener);
        taskDefinition.addTaskListener(TaskListener.EVENTNAME_DELETE, globalTaskListener);
      }
    }
  }
}

GlobalCreateTaskListener:

public class GlobalCreateTaskListener implements GlobalTaskListener {

  @Override
  public void notify(DelegateTask delegateTask) {
    // delegateTask.getEventName() should be one of TaskListener.EVENTNAME_*
  }
}
2 Likes

Great, thanks for sharing your solution!