Depending on how the JavaDelegate class is included in the bpmn file a Spring Bean is instantiated or not

I seted up my project according to the following documentation
https://docs.camunda.org/get-started/spring/ with Camunda.

My application runs as a Shared Process Engine with Docker.

Now I have noticed, that depending on how I embed the class which implements JavaDelegate into my bpmn file, the beans in the classes are instantiated or not.

@Component("logger")
public class LoggerDelegate implements JavaDelegate {
 
  private final Logger LOGGER = Logger.getLogger(LoggerDelegate.class.getName());

    @Autowired
    public JavaMailSender emailSender;

@Autowired works

<bpmn2:serviceTask id="Task_0az71c0" name="Log Info Messages" camunda:delegateExpression="${loggerDelegate}">

@Autowired does not work - emailSender is null

<bpmn:serviceTask id="Task_14akvxf" name="Log Info Messages" camunda:class="x.y.z.LoggerDelegate">

Can someone please explain me why we have this behavior?

If I have my own classes in my project, the beans are not instantiated either.
Is there a way to instantiate the customer beans as well?

I read this documentation: https://docs.camunda.org/manual/7.9/user-guide/spring-framework-integration/configuration/ with this example

@Configuration
@ComponentScan("com.example")
public class ExampleProcessEngineConfiguration {
}

But the beans were still null.

My applicationContext.xml

...
  <context:annotation-config />
  <bean class="x.y.z.AppConfig"/>
  <bean id="loggerDelegate" class="x.y.z.LoggerDelegate" />
  <bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl" />
...

Thanks
Stefan

@camouflagethis looks to be your exact problem: Impacts of not using Delegate Expression for SpringBoot Java Delegate Resolution needs to be highlighted more clearly in docs!? Explanations are in the links in that thread

2 Likes

@camouflage

Hi

Just a shot in the dark. Do you have an code example of how you solved this? I have a similar problem, but doesnt seem to be able to make the solution with Delegate expression work.

Will be grateful for any help.

Br

Frank

Can you provide a snippet of your java delegate and a single task Bpmn file that calls your delegate through a service task. Will see how implement it.

Hi @StephenOTT

I have added the BPMN file, casework2.bpmn and snippet of the java delegate class, GetCaseworkDelegate. The problem is that the autowired CaseworkClient is null. It works from another class I made for testing. That class doesnt implement JavaDelegate.

casework2.bpmn (2.9 KB)

public class GetCaseworkDelegate implements JavaDelegate {

private static Logger log = LoggerFactory.getLogger(GetCaseworkDelegate.class);

@Autowired
CaseworkClient caseworkClientForDelegate;


@Override
public void execute(DelegateExecution execution) throws Exception {
    try {
        String caseid = (String) execution.getVariable("caseId");
        log.info("Start REST service client for Get Casework {}", caseid);
        GetCaseworkRequest getCaseworkRequest = new GetCaseworkRequest();
        getCaseworkRequest.setCaseId(caseid);
        ResponseEntity<CreateCaseworkResponse> response = caseworkClientForDelegate.getCaseworkUsingCaseId(caseid);

    } catch (Exception e) {
        e.printStackTrace();
    }
    log.info("Start REST service with caseId {}", execution.getVariable("caseId"));
}

So your Bpmn file has two tasks, in your first you are using the class implementation and the second you use the delegate expression. The class impl won’t work with autowired as per the links above. So you need to remove that task or change it to a delegate expression impl

@StephenOTT

Hi, first of all, I want to thank you for your help.

I have tried to change it to a delegate expression. I added a applicationContext.xml and web.xml in path webapp/WEB-INF as described in the tutorial. I have also added a starter class to make it run. Have added all the files here. But, it seems like it is ignored when i start the spring boot app.

Is this the correct way to do it?

Starter class:
package test.starter;

import org.camunda.bpm.engine.RuntimeService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

public class Starter implements InitializingBean {

@Autowired
private RuntimeService runtimeService;

public void afterPropertiesSet() throws Exception {
    runtimeService.startProcessInstanceByKey("Process_1");
}

public void setRuntimeService(RuntimeService runtimeService) {
    this.runtimeService = runtimeService;
}

}

applicationContext.xml:

<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
    <property name="targetDataSource">
        <bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
            <property name="driverClass" value="org.h2.Driver"/>
            <property name="url"
                      value="jdbc:h2:mem:process-engine;DB_CLOSE_DELAY=1000"/>
            <property name="username" value="sa"/>
            <property name="password" value=""/>
        </bean>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="processEngineConfiguration" class="org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration">
    <property name="processEngineName" value="engine"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="transactionManager" ref="transactionManager"/>
    <property name="databaseSchemaUpdate" value="true"/>
    <property name="jobExecutorActivate" value="false"/>
    <property name="deploymentResources" value="classpath*:*.bpmn"/>
</bean>

<bean id="processEngine" class="org.camunda.bpm.engine.spring.ProcessEngineFactoryBean">
    <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
</bean>

<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService"/>
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService"/>

<context:annotation-config/>

<bean class="test.starter.Starter" />

<bean id="getCaseworkDelegate" class="test.GetCaseworkDelegate"/>

web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/src/main/webapp/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

@FrankReneSorensen if you are using the latest version of Camunda and Springboot, you are way over complicating this.

Here is a example of a delegate:

and here is it being implemented:

and here is the bootstrapping of the app:

Thats all you need.

If it is still not working, then you have a different conflict going on

Hi

So much work, and all I needed was the Delegate expression in the BPMN process and the @Component declaration in the delegate class…

Now it works like a dream!

Thanks a lot!

By the way, maybe you know how to add objects to the process. Not the be readable in the UI, but to store results of several calls to REST service API’s. I have made an Error object that will store fault I get from the different REST calls and in the end store the object in an error DB. The object is something like this:

class Case
: list of System (one for each system i call)
class System has list of error types (string) that the different systems can give

Anyway, you have helped me a lot.

Thanks

Better to open a new thread and ask your question.

@StephenOTT
Many thanks for the clarifications.

@camouflage if you are going to be storing lists of data using the same variable (such as multiple API calls updating the same variable), make sure to review what is Optismistic locking and concurrent locking in Camunda. As if you are trying to update the variable multiple times you will likely hit a lock exception (it’s a comment miss configuration)

For online documentation on the Modeler and how its Delegate works in EL:
https://docs.camunda.org/manual/7.10/user-guide/process-engine/expression-language/#delegation-code

With camunda:expression it is possible to evaluate a value expression or to invoke a method expression. You can use special variables which are available inside an expression or Spring and CDI beans. For more information about variables and Spring, respectively CDI beans, please see the corresponding sections.