This Blog discussed how to install and use Camel in Flowable 6.4.1. Hopefully after reading this blog you will be able to set up the Flowable Task application to allow users to run workflows that have tasks which integrate with other applications through Camel routes. This blog is based on the Flowable Task documentation and uses examples from the Flowable Camel module. I wrote this blog as it took me a while to figure out how to get Camel going with Flowable. My thanks to Filip Hrisafov for help in figuring this out.
What is Camel
Camel is an apache project that provides a standardised integration platform between applications using Java. It provides transports (endpoints) and concrete implementations of Enterprise Integration Patterns (EIPS) in the form of components to solve integration problems. A domain specific language (DSL) is then used to create routes which wire transports and EIPs together. An example java route which loads files from the /tmp directory into memory and then passes their content to a JMS queue is shown below.
from (“file:/tmp”).to(“jms:aQueue”)
Flowable provides a Camel Task which when reached calls a defined camel route. The camel route is therefore in charge of the doing the application integrations and processing while the Camel Task is really just a mechanism to connect to camel routes or receive information back from a camel.
Flowable Camel Architecture
The architecture I decided to implement was to have Camel embedded into the Flowable Task application. As the Flowable Task application is now a Spring Boot application I installed the Camel Spring Starter into the Flowable Task application. I also needed to install the Flowable Camel module into the Flowable Task application. The Flowable Camel module provides the implementation classes and config for the Camel Task to connect to routes which are run by Camel from within the Flowable Task App. The Routes then contain endpoints to the external applications that they are integrating with. Note: This blog assumes that you have already downloaded and installed all of the flowable applications within a tomcat container. The diagram below gives a visual of this architecture:
Installing Camel
- Download Flowable 6.4.1 source code from git (Link). You can either download the zip and unpack it into a development directory or clone the repository. I downloaded the zip and then unpacked it into my ubuntu server.
- Navigate to the Flowable UI Task App module, in my case (~/workspace/flowable-engine-flowable-6.4.1/modules/flowable-ui-task/flowable-ui-task-app). Open the pom.xml and add the following to the dependency section. As you can see it adds version 2.23.0 of the camel spring boot starter to the Task app and also the camel module.:
<!-- Seed --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-boot-starter</artifactId> <version>2.23.0</version> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-camel</artifactId> </dependency>
- From the root of the flowable-ui-task-app build the mvn project using the command: mvn package install -DskipTests. I found I had to skip the tests as the main app was failing a number of tests.
- On successful build, a war for the Task app is created as target/flowable-task.war. Copy the new war into your tomcat webapps directory, (eg cp target/flowable-task.war /opt/tomcat/webapps/.). Delete the existing task app directory from the webapps directory( /opt/tomcat$ rm -Rf webapps/flowable-task) and restart tomcat.
- Verify that you can access the Task app on http://localhost:8080/flowable-task.
Using A Camel Task
Now that you have your system set up you can start using the Camel Task within your workflows. A Camel Task will run automatically when reached in a workflow and make a call to a camel route. The Route that is run is determined by matching the camel task ID to the from element of the Router rule. For example the route: from(“flowable:asyncPingProcess:serviceAsyncPing“).to(“seda:continueAsync”); matches to the process named asycnPingProcess which has a camel task with an id of serviceAsyncPing. This workflow and task is shown below:
By default, when the rule is run by camel, the camel task will receive a response straight away (synchronously) and then move onto the next task. The response includes any variable values that have been set by the camel route when it runs.
Async Camel Task
In the example we have created the camel task is set to be async. This means that the camel task will be run by a flowable job asynchronously and move straight to the Receive Task where it will wait for a signal. This scenario implements the integration use case where the camel routes cannot return straight away, for example if the camel route is doing some external application processing and needs to wait for the results and then let the Flowable process know that it has completed. In our example we have used a Receive Task but you can also use a Service Task with the triggerable flag set as the return route basically sends a trigger to the process/task instance. The full camel route for the from and to endpoints is shown below. You will note that the “to” endpoint identifies the flowable task to return to by the process name and task name.
from("flowable:asyncPingProcess:serviceAsyncPing").to("seda:continueAsync"); from("seda:continueAsync").to("flowable:asyncPingProcess:receiveAsyncPing");
One thing to note on this. If you set the camel task to be async, then the process may fail if camel responds straight away. From our analysis this is because the Receive Task has not been created yet when camel tries to respond and find the task.
Setting up the Camel Route
One of the nice features about Camel is that the DSL feature means we can define camel routes as java classes. We have set up Camel Spring Starter to be part of the Flowable Task application, therefore, we can define camel routes as spring components in the same class path as the Flowable Task App and these will be picked up by the default camel context as routes. To do this:
- Imported the Flowable Task App into eclipse (ie from eclipse import the mvn project from ~/workspace/flowable-engine-flowable-6.4.1/modules/flowable-ui-task/flowable-ui-task-app).
- Navigate to the org.flowable.ui.task.application package and create a class called AsyncPingRoute which extends RouteBuilder. Implement the class as follows:
Camel Variables
The workflow we implemented included a service task at the start of the workflow to set some variables into the process that would be sent to the camel route by the Camel Task. The task and implementation class are shown below. As you can see you pass variables in by setting a variable (in our case called “input”) into the process instance. Each process variable is then passed to the camel route and can be accessed in the route (eg showing how the “input”variable is used in the route.
from("flowable:PingPongProcess:ping").transform().simple("${property.input} World");
Return variables from the camel route are associated with the running process instance and are available in the variable named “camelBody”.
The code for the Save Variable Output service task is shown below:
public class SaveOutput implements JavaDelegate { @SuppressWarnings("unchecked") @Override public void execute(DelegateExecution execution) { Map<String, String> outputMap = (Map<String, String>) execution.getVariable("outputMap"); outputMap.put("outputValue", (String) execution.getVariable("camelBody")); System.out.println("CamelBody: " + (String) execution.getVariable("camelBody")); } }
Deploying the Solution
- Once you have implemented the Route and Service tasks then rebuild the Flowable Task Application using mvn package install and deploy the generated war to your tomcat instance and restart tomcat.
- Lastly, deploy the workflow instance. I have included the instance code below. You can import this via the Flowable Modeler app.
- Once deployed you can add the process to an Application and run the process to try out the process and integration to camel.
<?xml version=”1.0″ encoding=”UTF-8″?> <definitions xmlns=”http://www.omg.org/spec/BPMN/20100524/MODEL” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:flowable=”http://flowable.org/bpmn” xmlns:bpmndi=”http://www.omg.org/spec/BPMN/20100524/DI” xmlns:omgdc=”http://www.omg.org/spec/DD/20100524/DC” xmlns:omgdi=”http://www.omg.org/spec/DD/20100524/DI” typeLanguage=”http://www.w3.org/2001/XMLSchema” expressionLanguage=”http://www.w3.org/1999/XPath” targetNamespace=”http://www.flowable.org/processdef”> <process id=”CamelVariableTransmission” name=”CamelVariableTransmission” isExecutable=”true”> <startEvent id=”startEvent1″></startEvent> <serviceTask id=”ping” name=”Send Variable” flowable:type=”camel”></serviceTask> <sequenceFlow id=”sid-5D4FCE03-981A-4E93-91A1-02ECD6046D41″ sourceRef=”startEvent1″ targetRef=”sid-79D5FE99-60CA-424F-AAD3-7ABBA577BCBB”></sequenceFlow> <sequenceFlow id=”sid-F08E2B38-EC21-478A-ACAF-938027455F6D” sourceRef=”ping” targetRef=”saveOutput”></sequenceFlow> <serviceTask id=”saveOutput” name=”Save Variable Output” flowable:class=”camel.examples.pingpong.SaveOutput”></serviceTask> <endEvent id=”sid-490A8527-1FAD-4E60-B2CB-F395781A048D”></endEvent> <sequenceFlow id=”sid-D7FEDCBA-C1BE-4FC0-9C4F-CF1642C3D2CF” sourceRef=”saveOutput” targetRef=”sid-490A8527-1FAD-4E60-B2CB-F395781A048D”></sequenceFlow> <serviceTask id=”sid-79D5FE99-60CA-424F-AAD3-7ABBA577BCBB” name=”Initialise Variables” flowable:class=”camel.examples.pingpong.InitialiseVariables”></serviceTask> <sequenceFlow id=”sid-2B35BD26-8861-45FC-9A19-B83F5D4A72F2″ sourceRef=”sid-79D5FE99-60CA-424F-AAD3-7ABBA577BCBB” targetRef=”ping”></sequenceFlow> </process>
</definitions>
References
https://dzone.com/articles/open-source-integration-apache
https://flowable.org/docs/userguide/index.html#bpmnCamelTask
https://forum.flowable.org/t/executing-camel-task-through-flowable-task/2663