Task listener specified by class - CDI

Hello,

I am trying to create a task listener which would notify all candidate users about creation of the task. However, dependency injection is not working for me.

The task listener is define as following:
<camunda:taskListener class="poc.ReferentListener" event="create" />

The ReferentListener class implements TaskListener interface:

@Named(value = "ReferentListener")
public class ReferentListener implements TaskListener {

    @Inject
    private IdentityService identityService;

    @Override
    public void notify(DelegateTask delegateTask) {
        ...
    }

    public void test() {
        System.out.println(identityService);
    }
}

notify(DelegateTask) method is called correctly, when the create-event occurs. But NullPointerException is thrown, when using IdentityService.

I tried to substitute the tasklistener definition with this:
<camunda:taskListener expression="${ReferentListener.test()}" event="create" />
then identityService was correctly instantiated.

Slightly offtopic: Is this the best practice? Or is there more suitable solution?

Probably because when using class the instance is created as a POJO with the new keyword, not by the IoC/bean container.

The docs state expressions must be used for injection. https://docs.camunda.org/manual/7.5/user-guide/process-engine/delegation-code/

To inject values that are dynamically resolved at runtime, expressions can be used. Those expressions can use process variables, CDI or Spring beans.

Hi,

The answer from @nvanbelle is correct.

Cheers,
Christian

Thank you guys for your advice! That did the work.

An addition: You can use the process engine configuration class org.camunda.bpm.engine.cdi.CdiStandaloneProcessEngineConfiguration. This configuration uses an artifact factory (that is called when the engine instantiates classes), that looks into the application’s CDI context.

Cheers,
Thorben

1 Like

I just had another look at the code. Should also work for shared engines. I thought we would cache the resolved class but that is not the case. I’ll update before answer.

Hi thorben. There is some example on how to configure the default shared engine for this kind of purpose ?

Using a delegateExpression with a bean could be a solution but the field injection is permanent (as it’s a singleton bean) and I can use custom spring beans already wired.
Using a Class is a better solution because my injected fields are changing every time, but inside that class actually I can’t inject useful bean like db connections, custom services etc, and I don’t want to specify those beans for every call.

For example for a TaskListener : class : pack.CompleteTask
injected field : var1 (manually set on modeler)
injected field : var 2 (manually set on modeler)
Inside the notify method of CompleteTask class:
bean1.foo(var1, var2, task.getExecution().getVariable(“form_var_foo”))
bean2.bar(var1, var2)

I want var1,var2 to be specified every time, while bean1 and bean2 should be autowired at every class instantiation. Is this possible ? Or there is a way to detect spring environment from this class and get the desired beans ?