Invoking Services from the Process

Access business logic implemented for the Java VM as well as remote services by means of small "JavaDelegate" classes. This glue code maps process input/output to your business logic by means of the best-of-breed libraries of your own choice. Some use cases call for a pull approach, where external worker threads query Camunda for "external service tasks". In case you need/want to define self contained BPMN process definitions you may want to leverage scripts or expressions for small pieces of logic and "connectors" for calling complex/remote web services.
Invoking Services from the Process is also related to
Invoking Services from the Process

Understanding Push and Pull

The typical pattern when invoking services is push, which means that the process engine actively issues a service call (or executes a script) via the mechanisms described below. But some use cases call for a pull approach, where external worker threads query the process engine API for external service tasks. They then do the actual work and notify the process engine of works completion.

Selecting the Implementation Approach

Camunda offers several possibilities. Of course, you can mix those approaches by selecting different implementation techniques for different service tasks.

Java Delegate

Expression

Connector

External Task

Script Task

Named Bean

Java Class

Call a named bean or java class implementing the JavaDelegate interface.

Evaluate an expression using JUEL.

Use a configurable connector
(REST or SOAP services provided out-of-the-box).

Pull a service task into an external worker thread and inform process engine of completion.

Execute a script inside the engine.

Use with
BPMN elements

task service message intermediate send task send

task script

Communication Direction

Push
work item by issuing service call

Pull
task from worker thread

Push work item by executing a script

Technology

Use your preferred framework, e.g. a JAX-WS client to call SOAP Web Services

Use REST/SOAP Connector and Message Template

Use Camunda External Task Client or REST API to query for work

Use JSR-223 compliant scripting engine

Implement
via

Java (in same JVM)

Expression Language (can reference Java code)

BPMN configuration

BPMN configuration and external pull logic

E.g. Groovy, JavaScript, JRuby or Jython

Code Completion and Refactoring

Maybe

Depends on language / IDE

Compiler Checks

Depends on language / IDE

Dependency Injection


(when using Spring, CDI, …​)


(when using Spring, CDI, …​)

Forces on Testing

Register mocks instead of original beans

Mock business logic inside the JavaDelegate

Register mocks instead of original beans

Difficult because of lack of dependency injection

Easy, as service is not actively called

Consider external script resources

Configure via

BPMN Attribute
serviceTask
camunda:
delegate
Expression

BPMN Attribute
serviceTask
camunda:
class

BPMN Attribute
serviceTask
camunda:
expression

BPMN Ext. Element+ serviceTask
camunda:
connector

BPMN Attributes
serviceTask
camunda:
type=
'external' and
`camunda:topic

BPMN Element
script or
BPMN Attribute
scriptTask
camunda:
resource

Fault Tolerance and Retrying

Handled by Camunda retry strategies and incident management

Lock tasks for a defined time. Use Camunda’s retry and incident management

Handled by Camunda retry strategies and incident management

Scaling (having multiple Worker Threads)

Via load balancer in front of service

Multiple worker threads can be started

Via job executor configuration

Throttling (e.g. "one request at a time")

Not possible out-of-the-box, requires own throttling logic being implemented

Start/stop exactly as many worker threads you need

Not possible out-of-the-box

Reusable Tasks

Use field injection

Use method parameters

Build your own connector

Reuse external task topics and configure service via variables

Use when

Always if there is no reason against it

Defining small pieces of logic directly in BPMN

Defining a self-contained BPMN process without Java code

See use cases below

Defining BPMN processes without Java code.

Learn more

Learn more

Learn more

Learn more

Learn more

Understanding and Using Java Delegates

A Java Delegate is a simple Java Class that implements the Camunda JavaDelegate interface. It allows you to use dependency injection as long as it is constructed as a CDI or Spring bean and connected to your BPMN serviceTask via the camunda:delegateExpression attribute:

<serviceTask id="service_task_publish_on_twitter" camunda:delegateExpression="#{tweetPublicationDelegate}" name="Publish on Twitter">
</serviceTask>

Leverage dependency injection to get access to your business service beans from the delegate. Consider a delegate to be a semantical part of the process definition in a wider sense: it is taking care of the nuts and bolts needed to wire the business logic to your process. Typically it does the following:

  1. Data Input Mapping

  2. Calling a method on the business service

  3. Data Output Mapping

Avoid programming business logic into Java Delegates, separate this logic by calling one of your own classes as a "business service":

@Named
public class TweetPublicationDelegate implements JavaDelegate {

  @Inject
  private TweetPublicationService tweetPublicationService;

  public void execute(DelegateExecution execution) throws Exception {
    String tweet = new TwitterDemoProcessVariables(execution).getTweet(); (1)
    // ...
    try {
      tweetPublicationService.tweet(tweet); (2)
    } catch (DuplicateTweetException e) {
      throw new BpmnError("duplicateMessage"); (3)
    }
  }
// ...
1 Retrieving the value of this process variable belongs to what we call the input mapping of the delegate code, and is therefore considered to be part of the wider process definition. (For an explanation of the variable accessor class used here, see Handling Data in Processes)
2 This method executes process engine independent business logic, it is therefore not part of the wider process definition anymore and placed in a separate business service bean.
3 This exception is process engine specific and so is typically not produced by your business service method. It’s part of the output mapping that we need to translate the business exception to the exception needed to drive the process - again code being part of the "wider" process definition and to be implemented in the Java Delegate.
In case you want to create Java Delegates that are reusable for other process definitions, leverage field injection to pass configuration from the BPMN process definition to your Java Delegate.

Understanding and Using External Tasks

An external task is a task that waits to be completed by some external service worker without explicitly calling that service. It’s configured by declaring a so called topic (which characterizes the type of the service). The Camunda API must be polled to retrieve open external tasks for a certain service’s topic and must be informed about the completion of a task:

external task pattern

The interaction with the External Task API can be done in two different ways:

  • Use Camunda’s external task client libraries for Java and Node.js: introduced with Camunda 7.9, these libraries make it very easy to implement your custom external task worker.

  • Create your own external task client based on Camunda’s Java or REST API: this approach is particularly useful for every language that is not yet supported by an official Camunda client library. You can find an example for .NET on GitHub.

External Tasks support the following use cases:

  1. Temporal decoupling: the pattern can replace a message queue between the service task (the "consumer") and the service implementation (the "provider"). It can eliminate the need for operating a dedicated message bus while keeping the decoupling that messaging would provide.

  2. Polyglott architectures: the pattern can be used to integrate - for example - .NET based services, when it might not be that easy to write Java delegates to call them. Service implementations are possible in any language that can be used to interact with a REST API.

  3. Better scaling: the pattern allows you to start and stop workers as you like - and run as many of them as you need. By doing so, you can scale each service task (or to be precise: each "topic") individually.

  4. Connect Cloud BPM to On Premise services: the pattern supports you in running Camunda somewhere in the cloud (as our customers often do), because you can still have services on-premise, as they can now query their work via REST over SSL, which is also quite firewall-friendly.

  5. Avoid Timeouts: the pattern allows you to asynchronously call long-running services, which eventually block for hours (and would therefore cause transaction and connection timeouts when being called synchronously).

  6. Run services on specialized hardware: Each worker can run in the environment that is best suited for the specific task of that worker, for example CPU-optimized cloud instances for complex image processing and memory-optimized instances for other tasks.

Learn more about external tasks in the user guide as well as the reference and explore the video processing example shown above in greater detail by reading the blog post about it.

Dealing With Problems and Exceptions

When invoking services, you can experience faults and exceptions. See our separate best practice guide about Dealing With Problems and Exceptions.

Bonus  Example Technology Solutions

Calling SOAP Web Services

When you need to call a SOAP web service, you will typically be given access to a machine-readable WSDL based description of the service. You can then use JAX-WS and for example Apache CXF’s JAX-WS client generation to generate a Java Web Service Client by making use of a Maven plugin. That client can be called from within your JavaDelegate.

Find a full example that uses JAX-WS client generation in the Camunda examples repository.

We typically prefer the client code generation over using the Camunda SOAP Connector, because of the better IDE support to do the data mapping by using code completion. You also can leverage standard testing approaches and changes in the WSDL will re-trigger code-generation and your compiler will check for any problems that arise from a changed interface. However, if you need a 'self-contained' BPMN XML without any additional Java code, the connector could be the way to go. See SOAP connector example.

Calling REST Web Services

If you need to call a REST web service, you will typically be given access to a human-readable documentation of the service. You can use standard Java REST client libraries like RestEasy or JAX-RS to write a Java REST service client that can be called from within a JavaDelegate.

We typically prefer writing Java clients over the Camunda REST Connector, because of the better IDE support to do the data mapping by using code completion. This way, you also can leverage standard testing approaches. However, if you need a 'self-contained' BPMN XML without any additional Java code, the connector could be the way to go. See REST connector example.

Sending JMS Messages

When you need to send a JMS message, use a plain Java Client and invoke it from a service task in your process, e.g. by using a Camunda Java Delegate:

@Named("jmsSender")
public class SendJmsMessageDelegate implements JavaDelegate {

  @Resource(mappedName = "java:/queue/order")
  private Queue queue;

  @Resource(mappedName = "java:/JmsXA")
  private QueueConnectionFactory connectionFactory;

  public void execute(DelegateExecution execution) throws Exception {
    String correlationId = UUID.randomUUID().toString(); (1)
    execution.setVariable("jmsCorrelationId", correlationId);

    Connection connection = connectionFactory.createConnection(); (2)
    Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
    MessageProducer producer = session.createProducer(queue);

    TextMessage message = session.createTextMessage( (3)
      "someOwnContent, e.g. Tweet Object Data, plus " + correlationId); (4)
    producer.send(message);

    producer.close();
    session.close();
    connection.close();
  }

}
1 Consider what information you can use to correlate back an asynchronous response to your process instance. We typically prefer a generated, artificial UUID for communication, which the waiting process will also need to remember.
2 You will need to open and close JMS connections, sessions and producers. Note that this example just serves to get you started. In real life, you will need to decide which connections you need to open and of course properly close.
3 You will need to create and send your specific message.
4 Add relevant business data to your message together with correlation information.
This example just serves to get you started. In real life, consider whether you need to encapsulate the JMS client in a separate class and just wire it from the Java Delegate. Also decide which connections you need to open and close properly at which peristaltic points.

On GitHub, you can find a more complete example for asynchronous messaging with jms.

Using SQL to Access the Database

Use plain JDBC if you have simple requirements. Invoke your SQL statement from a service task in your process, e.g. by using a Camunda Java Delegate:

@Named("simpleSqlDelegate")
public class simpleSqlDelegate implements JavaDelegate {

  @Resource(name="customerDB")
  private javax.sql.DataSource customerDB;

  public void execute(DelegateExecution execution) throws Exception {
    Statement statement = null;
    Connection connection = null;

    try {
      connection = customerDB.getConnection();
      String query = "SELECT name " +  (1)
                     "FROM customer " +
                     "WHERE id = ?";
      statement = connection.createStatement();
      statement.setString(1, execution.getProcessBusinessKey()); (2)
      ResultSet resultSet = stmt.executeQuery(query);
      if (resultSet.next()) {
        execution.setVariable("customerName", resultSet.getString("name")); (3)
      }
    } finally {
      if (statement != null) statement.close();
      if (connection != null) connection.close();
    }

}
1 Of course, you will need to define your SQL statement. Consider using prepared statements, if you want to execute a statement object many times.
2 You will typically need to feed parameters into your SQL query that are already known during execution of the process instance …​
3 …​ and deliver back a potential result that maybe needed later in the process.
This example just serves to get you started. For real life, consider whether you need to encapsulate the JDBC code in a separate class and just wire it from the Java Delegate. Also decide which connections you need to open and close properly at which point.

Note that the Camunda process engine will have opened a database transaction for its own persistence purposes when calling the Java Delegate shown above. You will need to make a conscious decision whether you want to join that transaction (and setup your TX management accordingly) or not.

Instead of invoking SQL directly, consider using JPA if you have more complex requirements. Its object/relational mapping techniques will allow you to bind database tables to Java objects and abstract from specific database vendors and their specific SQL dialects.

Calling SAP Systems

To call a SAP system, you have the following options:

  • Use REST or SOAP client calls, connecting Camunda to SAP Netweaver Gateway or SAP Enterprise Services.

  • Use SAP’s Java Connectors (JCo). Maybe consider using the Open Source Frameworks Hibersap and the Cuckoo Resource Adapter to use those Java Connectors comfortably.

The German article Alternativen für die Integration von SAP-Systemen mit Java EE written by Carsten Erker and Torsten Fink was published in JavaSPEKTRUM 6/2014 and discusses various options in great detail. The authors also published an example application for Hibersap and Cuckoo on GitHub.

Executing a Groovy script

A script task …​

task script

 …​ is defined by specifying the script and the scriptFormat.

<scriptTask id='theScriptTask' scriptFormat='groovy' camunda:resultVariable="size">
  <script>anArray.size()</script>
</scriptTask>

For more extensive code (which should also be tested separately), consider using scripts external to your BPMN file and reference them with a camunda:resource attribute on the scriptTask.

Learn much more about the many ways scripts can be used with Camunda from the User Guide.

No guarantee - The statements made in this publication are recommendations based on the practical experience of the authors. They are not part of Camunda’s official product documentation. Camunda cannot accept any responsibility for the accuracy or timeliness of the statements made. If examples of source code are shown, a total absence of errors in the provided source code cannot be guaranteed. Liability for any damage resulting from the application of the recommendations presented here, is excluded.

Copyright © Camunda Services GmbH - All rights reserved. The disclosure of the information presented here is only permitted with written consent of Camunda Services GmbH.