External Task Client Spring Boot Starter

Camunda provides a Spring Boot Starter for the External Task Client. This allows you to easily add the External Task Client to your Spring Boot application by adding the following Maven dependency to your pom.xml file:

<dependency>
  <groupId>org.camunda.bpm.springboot</groupId>
  <artifactId>camunda-bpm-spring-boot-starter-external-task-client</artifactId>
  <version>7.20.0</version>
</dependency>

Please check out our External Task Client Spring Boot Starter Examples.

The Client can subscribe to one or more topic names that are defined in your BPMN process model. When the execution waits in an External Task, the Client executes your custom business logic. For instance, the customer’s credit score is checked, and if successful, the External Task can be marked as completed and the execution proceeds.

Requirements

External Task Client Spring Boot Starter requires Java 17.

Topic Subscription

The interface which allows implementing the custom business logic and interacting with the Engine is called ExternalTaskHandler. A subscription is identified by a topic name and configured with a reference to the ExternalTaskHandler bean.

You can subscribe the Client to the topic name creditScoreChecker by defining a bean with the return type ExternalTaskHandler and annotate this bean with:

@ExternalTaskSubscription("creditScoreChecker")

The annotation requires at least the topic name. However, you can apply more configuration options by either referencing the topic name in your application.yml file:

camunda.bpm.client:
  base-url: http://localhost:8080/engine-rest
  subscriptions:
    creditScoreChecker:
        process-definition-key: loan_process
        include-extension-properties: true
        variable-names: defaultScore

Or, by defining configuration attributes in the annotation:

@ExternalTaskSubscription(
  topicName = "creditScoreChecker",
  processDefinitionKey = "loan_process",
  includeExtensionProperties = true,
  variableNames = "defaultScore"
)

Please find the complete list of attributes in the Javadocs .

Please Note: A property defined in the application.yml file always overrides the respective attribute defined programmatically via annotation.

Handler Configuration Example

Please consider the following complete handler bean example:

@Configuration
@ExternalTaskSubscription("creditScoreChecker")
public class CreditScoreCheckerHandler implements ExternalTaskHandler {

  @Override
  public void execute(ExternalTask externalTask, 
                      ExternalTaskService externalTaskService) {
    // add your business logic here
  }

}

If you want to define multiple handler beans within one configuration class, you can do it as follows:

@Configuration
public class HandlerConfiguration {

  @Bean
  @ExternalTaskSubscription("creditScoreChecker")
  public ExternalTaskHandler creditScoreCheckerHandler() {
    return (externalTask, externalTaskService) -> {
      // add your business logic here
      externalTaskService.complete(externalTask);
    };
  }

  @Bean
  @ExternalTaskSubscription("loanGranter")
  public ExternalTaskHandler loanGranterHandler() {
    return (externalTask, externalTaskService) -> {
      // add your business logic here
      externalTaskService.complete(externalTask);
    };
  }

}

Open/close a Topic Subscription

When not further configured, a topic subscription is automatically opened when the Spring Boot application starts, meaning the Client starts immediately to fetch External Tasks related to the topic name.

There might be situations in which a topic subscription should not be opened immediately when the application starts. You can control this via the auto-open flag.

The interface SpringTopicSubscription allows you to open or close a topic programmatically as soon as the subscription has been initialized. The initialization process is triggered as soon as the application is started.

When the subscription has been initialized, a SubscriptionInitializedEvent is emitted, and the topic subscription can be opened or closed:

@Configuration
public class SubscriptionInitializedListener 
    implements ApplicationListener<SubscriptionInitializedEvent> {

  @Override
  public void onApplicationEvent(SubscriptionInitializedEvent event) {

    SpringTopicSubscription topicSubscription = event.getSource();

    String topicName = topicSubscription.getTopicName();
    boolean isOpen = topicSubscription.isOpen();
    if ("creditScoreChecker".equals(topicName)) {

      if(!isOpen) {
        // Start fetching for External Tasks
        topicSubscription.open();

      } else {
        // Stop fetching for External Tasks
        topicSubscription.close();

      }
    }
  }

}

Configuration

application.yml file

The central configuration point is the application.yml file.

Client Bootstrapping

Please make sure to configure the properties together with the prefix: camunda.bpm.client

An example configuration could look as follows:

camunda.bpm.client:
  base-url: http://localhost:8080/engine-rest
  worker-id: spring-boot-worker
  basic-auth:
    username: admin
    password: admin

Available properties:

Property name Description Default value
base-url Mandatory: Base url of the Camunda 7 Runtime REST API.
worker-id A custom worker id the Workflow Engine is aware of.
Note: make sure to choose a unique worker id.
hostname + 128 bit UUID
max-tasks Specifies the maximum number of tasks that can be fetched within one request. 10
use-priority Specifies whether tasks should be fetched based on their priority or arbitrarily. true
async-response-timeout Asynchronous response (long polling) is enabled if a timeout is given. Specifies the maximum waiting time for the response of fetched and locked External Tasks. The response is performed immediately if External Tasks are available at the moment of the request. null
disable-auto-fetching Disables immediate fetching for external tasks after bootstrapping the Client. To start fetching ExternalTaskClient#start() must be called. false
disable-backoff-strategy Disables the client-side backoff strategy. When set to true, a BackoffStrategy bean is ignored.

Heads-up: Please bear in mind that disabling the client-side backoff can lead to heavy load situations on the engine side. To avoid this, please specify an appropriate async-response-timeout.
false
lock-duration Specifies for how many milliseconds an External Task is locked. Must be greater than zero. It is overridden by the lock duration configured on a topic subscription 20,000
date-format Specifies the date format to de-/serialize date variables. yyyy-MM-dd'T'HH:mm:ss.SSSZ
default-serialization-format Specifies the serialization format that is used to serialize objects when no specific format is requested. application/json
basic-auth.username Specifies the username credential of the REST API to be authenticated with.
basic-auth.password Specifies the password credential of the REST API to be authenticated with.

Topic Subscription

The properties for topic subscriptions go under: camunda.bpm.client.subscriptions

The configuration properties can be applied for each topic name as follows:

camunda.bpm.client:
  # ADD CLIENT CONFIGURATION HERE
  subscriptions:
    creditScoreChecker:
        process-definition-key: loan_process
        include-extension-properties: true
        variable-names: defaultScore
    loanGranter:
        process-definition-key: loan_process

Available properties:

Property name Description Default value
${TOPIC_NAME} The Service Task's topic name in the BPMN process model the Client subscribes to.
auto-open When false, topic subscription can be opened after the application starts calling SpringTopicSubscription#open(). Otherwise, the Client immediately starts to fetch for External Tasks. true
lock-duration Specifies for how many milliseconds an External Task is locked. Must be greater than zero. Overrides the lock duration configured on bootstrapping the Client. 20,000
variable-names Variable names of variables that are supposed to be retrieved. All variables are retrieved by default. null
local-variables Whether or not variables from greater scope than the External Task should be fetched. When false, all variables visible in the scope will be fetched. When true, only local variables to the scope of the External Task will be fetched. false
include-extension-properties Whether or not to include custom extension properties for fetched External Tasks. When true, all extensionProperties defined in the External Service Task will be provided. When false, extensionProperties defined in the External Service Task will be ignored. false
business-key Only External Tasks related to the specified business key are fetched.
process-definition-id Only External Tasks related to the specified process definition id are fetched.
process-definition-id-in Only External Tasks related to the specified list of process definition ids are fetched. List of ids have logical OR semantic.
process-definition-key Only External Tasks related to the specified process definition key are fetched.
process-definition-key-in Only External Tasks related to the specified list of process definition keys are fetched. List of keys have logical OR semantic.
process-definition-version-tag Only External Tasks related to the specified process definition version tag are fetched.
process-variables Only External Tasks related to the specified map of process variables (key: variable name, value: variable value) are fetched. Map of variables have logical OR semantic.
without-tenant-id Only External Tasks without a tenant id are fetched.
tenant-id-in Only External Tasks related to the specified list of tenant ids are fetched. List of ids have logical OR semantic.

Logging

To log the Client’s internal workings, you can set the level of the logger org.camunda.bpm.client.spring to DEBUG.

You can set the log level in your application.yml file as follows:

logging.level.org.camunda.bpm.client.spring: DEBUG

For debugging, it might be helpful to increase the level of the logger org.camunda.bpm.client as well.

Request Interceptor

A request interceptor is called whenever the Client performs an HTTP request. You can use this extension point, for example, to implement a custom authentication strategy like OAuth 2.0.

You can register one or more request interceptors by defining beans of type ClientRequestInterceptor:

@Configuration
public class RequestInterceptorConfiguration implements ClientRequestInterceptor {
  // ...
}

Backoff Strategy

By default, the Client uses an exponential backoff strategy. You can replace it with a custom strategy by defining a bean of type BackoffStrategy:

@Configuration
public class BackoffStrategyConfiguration implements BackoffStrategy {
  // ...
}

Resolving Properties

String-based Client configuration properties can be resolved from a custom properties file by defining a bean of type PropertySourcesPlaceholderConfigurer:

@Configuration
public class PropertyPlaceholderConfiguration 
    extends PropertySourcesPlaceholderConfigurer {

  public PropertyPlaceholderConfiguration() {
    // Specify the *.properties file name that contains the property placeholders
    Resource location = new ClassPathResource("client.properties");
    setLocation(location);
  }

}

When using the example shown above, the Client tries to resolve string-based properties from a client.properties file as follows:

client.baseUrl=http://localhost:8080/engine-rest
client.workerId=spring-boot-worker
client.dateFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ
client.serializationFormat=application/json

Make sure to reference the respective placeholders defined above in your application.yml file:

camunda.bpm.client:
  base-url: ${client.baseUrl}
  worker-id: ${client.workerId}
  date-format: ${client.dateFormat}
  default-serialization-format: ${client.serializationFormat}

Custom Client

You can bootstrap the Client programmatically, which skips the internal creation of the Client:

@Configuration
public class CustomClientConfiguration { 

  @Bean
  public ExternalTaskClient customClient() {
    return ExternalTaskClient.create()
        .baseUrl("http://localhost:8080/engine-rest")
        .build();
  }

}

Beans

You can define handler beans, but more beans are defined internally, and they are beyond your control. However, these beans can be accessed via auto wiring.

Client Bean

When not already defined by the user (see Custom Client), a bean with the name externalTaskClient of type ExternalTaskClient is constructed.

Subscription Bean

Based on a handler bean annotated with @ExternalTaskSubscription, a subscription bean of type SpringTopicSubscription is constructed. The bean name is composed of:

handler bean name + "Subscription"

For instance, the following handler bean definition:

@Bean
@ExternalTaskSubscription("creditScoreChecker")
public ExternalTaskHandler creditScoreCheckerHandler() {
  // ...
}

Will result in the subscription bean name:

creditScoreCheckerHandlerSubscription

Spring-only Module

If you want to use Spring instead of Spring Boot, you can add the following Maven dependency to your pom.xml file:

<dependency>
  <groupId>org.camunda.bpm</groupId>
  <artifactId>camunda-external-task-client-spring</artifactId>
  <version>7.20.0</version>
</dependency>

To bootstrap the Client, use the class annotation @EnableExternalTaskClient. You can find all configuration attributes in the Javadocs .

On this Page: