Apache Camel is a well known Open Source framework solving a lot of integration problems and implementing the Enterprise Integration Patterns. So combining it with camunda BPM is a perfect match: solve workflow and BPM requirements with camunda and integration requirements with Camel.
Together with Rafael Cordones we took the existing Activiti Camel Module as a basis and did a huge refactoring. For everybody knowing this module I compiled the changes at the end of this article. For everybody else: Lean back and enjoy the show while I quickly walk you through the features. I do this based on our "camel use cases" example which is available on GitHub (by the way - you can discuss this process model on camunda share):
The process does not solve any real-life business problem but showcases different use cases:
We developed a Camel component to talk to camunda BPM from Camel. You can easily add this component to your own project as described here: https://github.com/camunda/camunda-bpm-camel. I currently work on a JBoss AS 7 distribution already containing Camel and this component - which would make it even more easy to get started. But that's for another time. Let's examine what the component can do for you:
Start a Process Instance
Starting a new process instance is really easy - just call the "camunda-bpm:start" endpoint within your Camel Route and specify which process definition should be used. In the example the File Component is used which can watch a drop folder for new files:
from("file://C:/temp/")
.convertBodyTo(String.class) // convert content into String
.to("camunda-bpm:start?processDefinitionKey=camel-use-cases");
As an alternative you can start a process instance by a message (then the message start event is triggered and the process definition has not to be specified). In the example the Twitter Component is used polling Twitter for new Tweets with the keyword 'camunda':
from("twitter://search?type=polling&delay=5&keywords=camunda&consumerKey="...")
.transform().groovy("def map=[:] \n" +
"map['tweet.what'] = request.body.text \n" +
"map['tweet.when'] = request.body.createdAt \n" +
"request.body = map")
.to("camunda-bpm:message?messageName=camel.start");
Note that I used a Groovy script to transform the data from what I get from Twitter to what I want to have in the process engine. As I am not a Groovy expert I am sure that there is a more elegant solution to do that - but it worked within minutes - so I was happy. Camel supports multiple possibilities to transform data easily .
Call a Camel Route from the Process Instance
In order to call a camel route you have to add a simple expression to your BPMN 2.0 process model:
In the background this will create the following XML:
<bpmn2:serviceTask id="ServiceTask_1" name="call some service"
camunda:expression="#{camel.sendTo('direct:syncService')}">
Which references a Camel route:
from("direct://syncService")
.onException(SampleException.class) // map exception to BPMN error
.throwException(new BpmnError("camel.error"))
.end()
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// always throwing error to demonstrate error event
throw new SampleException("some error occured in service");
}
});
Changes compared to Activiti Camel Module
And for everybody familiar with the Activiti Camel Module it might be interesting what we changed. In a nutshell:
Together with Rafael Cordones we took the existing Activiti Camel Module as a basis and did a huge refactoring. For everybody knowing this module I compiled the changes at the end of this article. For everybody else: Lean back and enjoy the show while I quickly walk you through the features. I do this based on our "camel use cases" example which is available on GitHub (by the way - you can discuss this process model on camunda share):
The process does not solve any real-life business problem but showcases different use cases:
- Start a process instance when a file is dropped into a folder
- Start a process instance when a new Tweet is shared on Twitter with the term 'camunda' in it
- Call a Camel route ("Service") from a ServiceTask. This Service always throws an Exception which we catch with a BPMN Error Event.
- Call a Camel route ("Service") from a SendTask and retrieve the response asynchronous in a following Message Event.
- Between all steps there is a UserTask so that you can click through the example using Tasklist.
We developed a Camel component to talk to camunda BPM from Camel. You can easily add this component to your own project as described here: https://github.com/camunda/camunda-bpm-camel. I currently work on a JBoss AS 7 distribution already containing Camel and this component - which would make it even more easy to get started. But that's for another time. Let's examine what the component can do for you:
Start a Process Instance
Starting a new process instance is really easy - just call the "camunda-bpm:start" endpoint within your Camel Route and specify which process definition should be used. In the example the File Component is used which can watch a drop folder for new files:
from("file://C:/temp/")
.convertBodyTo(String.class) // convert content into String
.to("camunda-bpm:start?processDefinitionKey=camel-use-cases");
As an alternative you can start a process instance by a message (then the message start event is triggered and the process definition has not to be specified). In the example the Twitter Component is used polling Twitter for new Tweets with the keyword 'camunda':
from("twitter://search?type=polling&delay=5&keywords=camunda&consumerKey="...")
.transform().groovy("def map=[:] \n" +
"map['tweet.what'] = request.body.text \n" +
"map['tweet.when'] = request.body.createdAt \n" +
"request.body = map")
.to("camunda-bpm:message?messageName=camel.start");
Note that I used a Groovy script to transform the data from what I get from Twitter to what I want to have in the process engine. As I am not a Groovy expert I am sure that there is a more elegant solution to do that - but it worked within minutes - so I was happy. Camel supports multiple possibilities to transform data easily .
Call a Camel Route from the Process Instance
In order to call a camel route you have to add a simple expression to your BPMN 2.0 process model:
In the background this will create the following XML:
<bpmn2:serviceTask id="ServiceTask_1" name="call some service"
camunda:expression="#{camel.sendTo('direct:syncService')}">
from("direct://syncService")
.onException(SampleException.class) // map exception to BPMN error
.throwException(new BpmnError("camel.error"))
.end()
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// always throwing error to demonstrate error event
throw new SampleException("some error occured in service");
}
});
What you do in the Camel route is completely up to you and not part of this blog post. What I did in the above route is to always throw an exception (do not consider this best practice ;-)). But this allows me to show how to map any exception a BPMN error which can be handled in the BPMN process model. Note that this should only be done in cases of errors you want to model in your process model - see How-To on Error Handling for details.
Get a response message
Calling the route with the SendTask is the same as with the ServiceTask. But interesting is how to get the response to the process instance waiting in the message event. The following screenshot shows one process instance waiting for the message in cockpit:
This is again easy:
from("seda:someQueue")
.to("camunda-bpm:message?messageName=camel.answer");
The message name corresponds to the message name in the BPMN 2.0 XML:
In the example expect the property CamundaBpmProcessInstanceId to be present in the Camel message properties, this is how correlation currently is done. You could hook in some logic in your Route to do correlation/matching yourself, as e.g. shown in the Bank Account Opening Example (this uses by the way a ReceiveTask instead of a Message Event - both is possible):
In the example expect the property CamundaBpmProcessInstanceId to be present in the Camel message properties, this is how correlation currently is done. You could hook in some logic in your Route to do correlation/matching yourself, as e.g. shown in the Bank Account Opening Example (this uses by the way a ReceiveTask instead of a Message Event - both is possible):
from("file://" + postidentFolder)
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
// extract key from file name
String businessKey =
exchange.getIn().getHeader("CamelFileName").toString()
.split("-")[1]
.substring(0, 4);
exchange.setProperty(CAMUNDA_BPM_BUSINESS_KEY, businessKey);
}
})
.to("camunda-bpm://message?activityId=wait_for_postident").
Where to get it?
- The camunda-bpm-camel component and further information can be found on GitHub: https://github.com/camunda/camunda-bpm-camel
- The sources for the example used in this blog post can be found on GitHub: https://github.com/camunda/camunda-bpm-examples/tree/master/camel-use-cases
- A business example (opening a bank account) using Camel can found on GitHub as well: https://github.com/camunda/camunda-bpm-examples/tree/master/bank-account-opening-camel
Why is it cool?
Personally I like Apache Camel especially because it introduced a common language and a well adopted Component concept having a simple solution available for a lot of integration problems out there. File handling, Streaming, huge CSV files processes in parallel? No problem with Camel. Twitter, Salesforce, Hadoop or whatever? There might be a component, see this amazing list here: https://github.com/apache/camel/tree/master/components.
Camel is no rocket-science. It is not a complex ESB. It is a down-to-earth Java framework making your live easier and reducing the amount of code for quite some problems. The philosophy is matching pretty well with ours. Together with camunda BPM you can already built some sophisticated BPM/SOA solution.
Further topics
Personally I like Apache Camel especially because it introduced a common language and a well adopted Component concept having a simple solution available for a lot of integration problems out there. File handling, Streaming, huge CSV files processes in parallel? No problem with Camel. Twitter, Salesforce, Hadoop or whatever? There might be a component, see this amazing list here: https://github.com/apache/camel/tree/master/components.
Camel is no rocket-science. It is not a complex ESB. It is a down-to-earth Java framework making your live easier and reducing the amount of code for quite some problems. The philosophy is matching pretty well with ours. Together with camunda BPM you can already built some sophisticated BPM/SOA solution.
Further topics
- Correlation: We have some Correlation Mechanism already built in camunda BPM. So we want to make that available in the Camel integration as well. Currently we discuss how this can be done best - basically which parameters to set in a Camel message in order to get that working. Join our forum to add your opinion!
- Retry Mechanism: Camel has some retry strategies if some external service is unavailable. camunda BPM has own retry strategies as well. I recommend to use retrying from camunda normally - as this makes monitoring easier. But I am happy to get feedback.
Ongoing discussions and next steps
If you follow the forum you will notice that we are still discussing. Join the discussion if you like! To give you an impression - there are a couple of things we ask ourselves:
- I would like to have Camel installed as a JBoss module and the route deployed within a Process Application. This should be easy doable - but currently we lack time for it.
- Should we rely on camel-cdi? As this has some drawbacks.
- Properties or Header for CamundaBpm properties? Currently we use properties. Properties are hanging off the Exchange and not off the messages that flow through a route. A processor in a route can copy the headers but may not do it. Properties are maintained all through the route. But normally headers are used. Maybe we should switch?
- Is it interesting to get a camunda-bpm://events?filter=… consumer endpoint that captures Audit Log events from the engine and send them down the route?
- Should we support multiple process engines? Ho do we expose this on the camel API? As a parameter (camunda-bpm://start?processEngine=...)?
Changes compared to Activiti Camel Module
And for everybody familiar with the Activiti Camel Module it might be interesting what we changed. In a nutshell:
- Removed the CamelBehavior (we prefer working with expressions, see examples above).
- Add support for Message Events (not only ReceiveTask).
- Separated the codebase to not force Spring dependencies when using CDI and vice-versa:
- Refactored the core code base to make it easier understandable.
- Added examples.