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.22.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 |
use-create-time |
Specifies whether tasks should be fetched based on their create time in descending order. Use this property in disjunction with order-by-create-time property or a SpringExternalTaskClientException will be thrown. | false |
order-by-create-time |
Specifies whether tasks should be fetched based on their createTime with the given configured order. It can be either "asc" or "desc". Use this property in disjunction with use-create-time property or a SpringExternalTaskClientException will be thrown. | null |
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.22.0</version>
</dependency>
To bootstrap the Client, use the class annotation @EnableExternalTaskClient
. You can find all
configuration attributes in the
Javadocs
.