First steps with Apache Camel
Fabian Piau | Friday May 11th, 2012 - 09:05 AMTo learn more about Camel, you can read a free sample of the chapter 1 of “Camel in Action”, the reference book.
Raphaël Delaporte (@rafdelaporte) proposed us to tame a camel this week at Nantes JUG.
Don’t be fooled by the picture ! Our computerized camel is not lazy… Let’s focus back on an attractive presentation of Apache Camel.
Apache Camel relies on the Enterprise Integration Patterns – EIP (not to be confused with the concept of Design Patterns used for objects designing).
EIP Patterns
Here are the main patterns from simple to more complex (this is a slide from the presentation).
By combining multiple patterns, the possibilities are almost endless. You can find the list of Patterns implemented in Camel on the official website of the framework.
A comprehensive book on the subject exists “Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions”. The author is none other than the creator of this concept, needless to say that this is the reference book.
Make EIP with Apache Camel
There are three basic elements in Camel to implement the EIP :
- Route
- Processor
- Endpoint
A route is conceptualized as follows : the binding of two endpoints by a processor. Thus, we have a start endpoint and a end endpoint, an endpoint is associated with a resource. A route symbolizes a more or less complex treatment (processor) depending on what you want to achieve and the EIP you have chosen to implement.
To meet your needs, you can use nesting and chained routes, apply different treatments throughout the route, etc.
In Apache Camel, a route can be defined in two ways :
- Either by using the Java language (Domain Specific Language – DSL). Apache Camel provides an API. This is a very handy way, specially inside an IDE with autocomplete feature.
- Or using XML. More verbose so a little less readable and finally a bit less convenient. I recommend you to use DSL when routes become complex.
In the following, we will favor the Java syntax. Note that in all cases, some XML will be required for the configuration of Camel (Camel context, datasources, etc.).
Here is a simple example of route :
import org.apache.camel.builder.RouteBuilder; public class MyFirstRoute extends RouteBuilder { @Override public void configure() thwrows Exception { // My first route from('file:/Users/fabian/src').to('file:/Users/fabian/dest').end(); // We can define more routes here // ... } }
The
.end()
keyword is not mandatory, but highly advisable to clearly delineate routes (maintainability). In this example, the treatment is very simple, there is only one route, so it is not really necessary.
The presentation was mainly demos-based. Raphaël showed us some textbook cases starting from a correctly configured Maven project (Spring dependencies, Camel, contexts…) :
- Move files from one directory to another (this is the code snippet above). All files created in the
src
folder will be moved in thedest
folder. This route can be written in one single line… And shows us the power of such a tool. - Create files corresponding to pooled messages in a JMS queue. For the presentation, Raphaël has used Apache ActiveMQ, a JMS message broker. This case illustrates the Splitter pattern.
- Pool messages in a JMS queue according to their content. This case illustrates the Content-based Router pattern.
The route is as follows, assuming queue.in
exists in ActiveMQ and that they are pooled messages :
import org.apache.camel.builder.RouteBuilder; public class MyContentBasedRoute extends RouteBuilder { @Override public void configure() thwrows Exception { from('amq:queue.in') .choice() .when(header('JMSCorrelationID').isEqualTo('nantes')) .to('amq:queue.nantes') .when(header('JMSCorrelationID').isEqualTo('rennes')) .to('amq:queue.rennes') .otherwise() .to('amq:queue.others') .end(); } }
From the ActiveMQ admin console, we create a few messages to test the different scenarios. Message is routed to the right queue according to its header.
It is possible to chain predicates with the usual logical operators in order to create more complex routing criteria. In our example, the predicate is simple and is only based on the message’s header. It could also be based on the body to make a finer routing messages.
Content of messages_jug.xml
:
<jug> <nantes>Hello from Nantes</nantes> <paris>Hello from Paris</paris> <rennes>Hello from Rennes</nantes> <nantes>Hello again from Nantes</nantes> <paris>Hello again from Paris</paris> [...] </jug>
With Camel, we want to extract the various messages and send them into a queue.
Here is the code of the corresponding route :
import org.apache.camel.builder.RouteBuilder; public class MyDynamicRoute extends RouteBuilder { @Override public void configure() thwrows Exception { from('files:/Users/fabian/src/messages_jug.xml') .split(xpath('/jug/child::*')) .to('amq:queue.jugs'); } }
It could have been a different treatment. For example : filtering on messages from Nantes and routed them to another file. The possibilities are endless.
Camel Optimizations
What about unavailability ? What’s going on with the message processing if the machine crashes and does not have the time to route the message ? Unfortunately, you lose the message ! It was consumed in the source queue, but has never reach its destination. Solution is to pass the route in transactional mode. This is done by adding .transacted()
just after .from(...)
and making sure that your JMS datasource is in transactional mode too (see XML configuration).
We also note that the treatment is single-threaded by default. An improvement is to enable the processor’s parallelization (in our case, a split
processor) by adding this custom option .parallelProcessing()
just after .split(...)
.
We talked about file
and jms
, but there is so much more components available in Camel thanks to a very active community. This page lists all the components available. Including jdbc
, ftp
, gmail
, atom
, pop
, imap
among others.
Make EIP with Spring Integration
Raphaël told us about Spring Integration, an alternative to Camel. When using Spring Integration, you have no choice and you must use the XML notation exclusively.
According to Raphaël, SI fits better with Spring Batch, but the goal was not to blame one tool or the other. And each one has pros and cons.
Here is the example of transferring files from one directory to another, but written with Spring Integration :
<channel id='myFirstChannel' /> <file:inbound-channel-adapter id='filesIn' channel='myFirstChannel' directory='file:/Users/fabian/src' /> <file:outbound-channel-adapter id='filesOut' channel='myFirstChannel' directory='files:/Users/fabian/dest' />
The concept of route is not present, SI defines a channel which connects two components. This is clearly the Spring philosophy, which places the notion of component at the center. Components are injected and can be linked together (this is logical because SI is developped by SpringSource).
Actually, the term route is only used by Camel. Spring Integration is closer to the EIP concepts by using same terms, such as channel.
Another difference, the channel appears concretely in Spring Integration but the route is implicit in Camel (no route()
keyword). If we come back to our first route :
from('file:/Users/fabian/src').to('file:/Users/fabian/dest');
The route is symbolized by the “.” between the two endpoints. It is not possible to configure it. At this level, Spring Integration is more advanced with the ability to configure a particular type of channel.
Recent Comments