Apache CamelTaking Camel for a ride
Hands on XkeAlexis KinsellaJune 11, 2010
www.xebia.fr / blog.xebia.fr 2
What is camel ?
What is camel ?
www.xebia.fr / blog.xebia.fr 3
It stands for :▶ Concise Application Message
Exchange Language (i.e. the Java DSL for routing)
▶ Its is a routing and mediation framework
Some people say that :▶ A camel is a horse designed by
committee (Not in that case ...)
What is camel ?
A powerful Spring based Integration Framework
It implements Enterprise Integration Patterns
Routing and mediation rules configured using :▶ Java DSL (or Fluent API)▶ Spring Xml Configuration files.
www.xebia.fr / blog.xebia.fr 4
What is camel ?
Apache Camel uses URIs▶ Works with any kind of transport or messaging such as:
» HTTP, ActiveMQ, JMS, MINA or CXF Bus API, …» Working with pluggable Data Format options
▶ Works with the same API regardless which kind of Transport used.
A Java library with minimal dependencies:▶ easy embedding in any Java application.
www.xebia.fr / blog.xebia.fr 5
www.xebia.fr / blog.xebia.fr 6
Some informations before you start ...
About the authors (Camel Riders)
James Strachan▶ Technical director at FuseSource▶ Blog: http://macstrac.blogspot.com/▶ Co-founder: Scalate, ActiveMQ, Camel, ServiceMix,
Lingo, Jencks, Groovy, dom4j▶ Committer: jaxen, taglibs, commons
Claus Ibsen▶ Software Engineer employed by Progress Software
working in The FUSE team▶ Blog: http://davsclaus.blogspot.com/▶ Leader: Camel
www.xebia.fr / blog.xebia.fr 7
About the authors (Camel Riders)
Jon Anstey▶ Senior Engineer at Progress Software▶ Blog: http://janstey.blogspot.com/▶ Committer: ActiveMQ, Camel, ServiceMix
Hiram Chirino▶ A Software Architect at FuseSource▶ Blog: http://hiramchirino.com/blog/▶ Co-founder: ActiveMQ, Camel▶ Committer: ServiceMix,Geronimo
www.xebia.fr / blog.xebia.fr 8
Camel and friends
ActiveMQ▶ Open source messaging provider (JMS provider).▶ Web site: http://activemq.apache.org▶ Current version: 5.3.2
CXF▶ Open Source Service Framework (WebServices)▶ Web site: http://cxf.apache.org▶ Current version: 2.2.9
ServiceMix▶ Open source ESB (Enterprise Service Bus)▶ Web site: http://servicemix.apache.org▶ Current versions: 3.3.2 / 4.2.0 (OSGI Based)
Apache Mina ...
www.xebia.fr / blog.xebia.fr 9
Professionnal Camel Support
FuseSource – Open Source SOA▶ Get supported and fixed versions for:
» ActiveMQ, Camel, Cxf, ServiceMix, FuseHQ
▶ Subscribe production and development support, Consulting, Virtual Training
▶ Provide first class support: Emploies many Apache project co-founders and commiters
▶ Provides Open Maven repositories (No need to contract support to gain access to supported and fixed versions)
www.xebia.fr / blog.xebia.fr 10
Competitors and Families of products
ESB (Entreprise Service Bus):▶ ServiceMix (JBI compliant, integrates Apache Camel)▶ JbossESB▶ WebSphereESB (SCA compliant) ▶ SonicESB, OpenESB, Petals...
Lightweight Enterprise Integration Patterns Frameworks:▶ Camel▶ MuleESB▶ Spring Integration
Process Servers, EAI, …▶ WPS (Websphere Process Server)▶ Biztalk Server
www.xebia.fr / blog.xebia.fr 11
Camel compared to Mule ...
Both based on Spring + POJO They can run in standalone mode or embedded in
an OSGI server, application server They are not based on based on JBI specification
▶ Where the messages send over the bus are XML normalized messages
Camel has more components than Mule now Camel proposes a DSL Language Mule has better monitoring & management tools
www.xebia.fr / blog.xebia.fr 12
Camel compared to Mule ...
You don't need to create a lot of XML config files with inbound/outbound stuff like you have to do in Mule.
The routing in Mule is not so intuitive and not appear directly in the XML or DSL
www.xebia.fr / blog.xebia.fr 13
www.xebia.fr / blog.xebia.fr 14
The basics
Message Routing
www.xebia.fr / blog.xebia.fr 15
Endpoint BEndpoint B
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna urna, varius a
facilisis nec, sagittis eget eros. Mauris scelerisque justo et ipsum scelerisque
aliquam. In at auctor diam. Suspendisse ut justo sed diam
Endpoint AEndpoint A
Message
Camel Components
www.xebia.fr / blog.xebia.fr 16
Kick Ass features
Integrates with almost everything … Support for:
▶ Scripting languages» Groovy» Scala
▶ Google App Engine & Google APIs▶ …
Built-in BAM Features
Extendable
www.xebia.fr / blog.xebia.fr 17
Simple Routing
www.xebia.fr / blog.xebia.fr 18
Endpoint BEndpoint B
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna urna, varius a
facilisis nec, sagittis eget eros. Mauris scelerisque justo et ipsum scelerisque
aliquam. In at auctor diam. Suspendisse ut justo sed diam
Endpoint AEndpoint A
Message FileJms
Pipeline
www.xebia.fr / blog.xebia.fr 19
Endpoint BEndpoint B
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna urna, varius a
facilisis nec, sagittis eget eros. Mauris scelerisque justo et ipsum scelerisque
aliquam. In at auctor diam. Suspendisse ut justo sed diam
Endpoint AEndpoint A
Message
Endpoint CEndpoint C
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna urna, varius a
facilisis nec, sagittis eget eros. Mauris scelerisque justo et ipsum scelerisque
aliquam. In at auctor diam. Suspendisse ut justo sed diam
Endpoint DEndpoint D
Message
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
magna urna, varius a facilisis nec, sagittis eget
Multicast
www.xebia.fr / blog.xebia.fr 20
Endpoint BEndpoint B
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna urna, varius a
facilisis nec, sagittis eget eros. Mauris scelerisque justo et ipsum scelerisque
aliquam. In at auctor diam. Suspendisse ut justo sed diam
Endpoint AEndpoint A
Message
Endpoint BEndpoint B
Endpoint BEndpoint B
File
IBatis
SMTP
SFTP
Message filter: Spring XML
www.xebia.fr / blog.xebia.fr 21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:schemaLocation="…">
<camelContext xmlns="http://activemq.apache.org/camel/schema/spring"><route>
<from uri="activemq:topic:Quotes" /><filter>
<xpath>/quote/product = ‘widget’</xpath><to uri="mqseries:WidgetQuotes" />
</filter></route>
</camelContext>
</beans>
Message filter: Java DSL
www.xebia.fr / blog.xebia.fr 22
package com.acme.quotes;
import org.apache.camel.builder.RouteBuilder;
public class MyRouteBuilder extends RouteBuilder {
public void configure() {// forward widget quotes to MQSeriesfrom("activemq:topic:Quotes").
filter().xpath("/quote/product = ‘widget’").to("mqseries:WidgetQuotes");
}}
Content Based Router: Java
www.xebia.fr / blog.xebia.fr 23
from("activemq:NewOrders").choice().when().xpath("/quote/product = 'widget'").
to("activemq:Orders.Widgets").choice().when().xpath("/quote/product = 'gadget'").
to("activemq:Orders.Gadgets").otherwise().
to("activemq:Orders.Bad");
Content Based Router: Spring XML
www.xebia.fr / blog.xebia.fr 24
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:schemaLocation="...">
<camelContext xmlns="http://activemq.apache.org/camel/schema/spring"><route>
<from uri="activemq:NewOrders" /><choice>
<when><xpath>/order/product = 'widget'</xpath><to uri="activemq:Orders.Widgets" />
</when><when>
<xpath>/order/product = 'gadget'</xpath><to uri="activemq:Orders.Gadgets" />
</when><otherwise>
<to uri="activemq:Orders.Bad" /></otherwise>
</choice></route>
</camelContext>
</beans>
How camel do this routing work ?
Camel Components▶ Camel Endpoints
» Camel Consumer» Camel Producer
Camel-Core
www.xebia.fr / blog.xebia.fr 25
Message processors
www.xebia.fr / blog.xebia.fr 26
from("direct:start").process(new Processor() {
public void process(Exchange exchange) {
Message in = exchange.getIn();
in.setBody(in.getBody(String.class) + " World!");
}
}).to("mock:result");
<bean id="myProcessor" class="com.acme.MyProcessor"/>
from("activemq:myQueue").to("myProcessor");
The Processor interface is used to implement:▶ Consumers of message exchanges▶ A Message Translator
Expressions
www.xebia.fr / blog.xebia.fr 27
Camel supports pluggable Expression strategies using a variety of different Languages (Dynamic rules):▶ Bean Language for using Java for expressions▶ The unified EL from JSP and JSF▶ JXPath, Mvel, OGNL▶ Scala DSL, Groovy▶ Python, PHP, Ruby, JavaScript▶ Simple, Constant▶ Xpath, Xquery, SQL
Predicates
www.xebia.fr / blog.xebia.fr 28
Camel supports a pluggable interface called Predicate▶ Can be used to integrate a dynamic predicate into
Enterprise Integration Patterns such as when using: » Message Filter» Content Based Router.
A Predicate is being evaluated to a boolean value so the result is either true or false. ▶ This makes Predicate so powerful as it is often used to
control the routing of message in which path they should be routed.
Predicates examples
www.xebia.fr / blog.xebia.fr 29
from("jms:queue:order")
.choice()
.when(header("type").isEqualTo("widget")).to("bean:widgetOrder")
.when(header("type").isEqualTo("wombat")).to("bean:wombatOrder")
.otherwise()
.to("bean:miscOrder")
.end();
Predicate isWidget = header("type").isEqualTo("widget");
from("jms:queue:order")
.choice()
.when(isWidget).to("bean:widgetOrder")
.when(isWombat).to("bean:wombatOrder")
.otherwise()
.to("bean:miscOrder")
.end();
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 30
Transaction Manager
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory" /> </bean> <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> </bean>
Transaction Policies
<bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionManager" ref="jmsTransactionManager"/> </bean>
<bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/>
</bean>
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 31
Using policies in Java DSL (1)
public void configure() { ... Policy requried = bean(SpringTransactionPolicy.class, "PROPAGATION_REQUIRED")); Policy requirenew = bean(SpringTransactionPolicy.class, "PROPAGATION_REQUIRES_NEW")); ... }
// Send to bar in a new transaction from("activemq:queue:foo").policy(requirenew).to("activemq:queue:bar");
// Send to bar without a transaction. from("activemq:queue:foo").policy(notsupported).to("activemq:queue:bar");
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 32
Using policies in Java DSL (2)
from("direct:mixed") // using required .transacted("PROPAGATION_REQUIRED") // all these steps will be okay .setBody(constant("Tiger in Action")).beanRef("bookService") .setBody(constant("Elephant in Action")).beanRef("bookService") .setBody(constant("Lion in Action")).beanRef("bookService") // continue on route 2 .to("direct:mixed2");
from("direct:mixed2") // using a different propagation which is requires new .transacted("PROPAGATION_REQUIRES_NEW") // tell Camel that if this route fails then only rollback this last route // by using (rollback only *last*) .onException(Exception.class).markRollbackOnlyLast().end() // this step will be okay .setBody(constant("Giraffe in Action")).beanRef("bookService") // this step will fail with donkey .setBody(constant("Donkey in Action")).beanRef("bookService");
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 33
Using policies in Spring XML (1) <!-- here we define our camel context --><camel:camelContext id="myroutes">
<!-- first route with transaction error handler --> <!-- here we refer to our transaction error handler we define in this Spring XML file --> <!-- in this route the transactionErrorHandler is used --> <camel:route errorHandlerRef="transactionErrorHandler"> <!-- 1: from the jms queue --> <camel:from uri="activemq:queue:okay"/> <!-- 2: setup the transactional boundaries to require a transaction --> <camel:transacted ref="required"/> <!-- 3: call our business logic that is myProcessor --> <camel:process ref="myProcessor"/> <!-- 4: if success then send it to the mock --> <camel:to uri="mock:result"/> </camel:route>
<!-- 2nd route with no error handling --> <!-- this route doens't use error handler, in fact the spring bean with id noErrorHandler --> <camel:route errorHandlerRef="noErrorHandler"> <camel:from uri="activemq:queue:bad"/> <camel:to uri="log:bad"/> </camel:route>
</camel:camelContext>
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 34
Using policies in Spring XML (2)
<!-- camel policy we refer to in our route --><bean id="required" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionTemplate" ref="PROPAGATION_REQUIRED"/></bean>
<!-- the standard spring transaction template for required --><bean id="PROPAGATION_REQUIRED"
class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="jmsTransactionManager"/></bean>
<!-- the transaction error handle we refer to from the route --><bean id="transactionErrorHandler"
class="org.apache.camel.spring.spi.TransactionErrorHandlerBuilder"> <property name="transactionTemplate" ref="PROPAGATION_REQUIRED"/></bean>
<!-- the no error handler --><bean id="noErrorHandler" class="org.apache.camel.builder.NoErrorHandlerBuilder"/>
Transaction Oriented Endpoints
www.xebia.fr / blog.xebia.fr 35
TransactionErrorHandler
onException(ValidationException.class).handled(true).transform(body(constant("INVALID ORDER")));
from("jetty:http://localhost/myservice/order").transacted().to("bean:validateOrder").to("jms:queue:order");
errorHandler(transactionErrorHandler().maximumRedeliveries(6));
onException(IllegalArgumentException.class).maximumRedeliveries(4);
from("direct:okay") .transacted() .setBody(constant("Tiger in Action")).beanRef("bookService") .setBody(constant("Elephant in Action")).beanRef("bookService");
DefaultErrorHandler & DeadLetterChannel
www.xebia.fr / blog.xebia.fr 36
Client notified (Default DLC behavior, not DEH )
errorHandler(deadLetterChannel("jms:queue:dead").
maximumRedeliveries(3).redeliverDealy(5000));
Client not notified errorHandler(deadLetterChannel("jms:queue:dead").
maximumRedeliveries(3).redeliverDealy(5000).handled(false));
Use original message errorHandler(deadLetterChannel("jms:queue:dead") .useOriginalMessage().mamimumRedeliveries(5).redeliverDelay(5000);
Using default error handleronException(ValidationException.class).handled(true).transform(body(constant("INVALID ORDER")));
Annotation support
www.xebia.fr / blog.xebia.fr 37
Camel supports annotations
public class RouterBean {
@Consume(uri = "activemq:foo")
@RecipientList
public String[] route(String body) {
return new String[]{"activemq:bar", "activemq:whatnot"};
}
}
public class Foo {
@MessageDriven(uri = "activemq:my.queue")
public void doSomething(@XPath("/foo/bar/text()") String correlationID, @Body String body) {
// process the inbound message here
}
}
@Bean annotation
www.xebia.fr / blog.xebia.fr 38
public class Foo {
@MessageDriven(uri = "activemq:my.queue")
public void doSomething(@Bean("myCorrelationIdGenerator") String correlationID, @Body String body) {
// process the inbound message here
}
}
public class MyIdGenerator {
private UserManager userManager;
public String generate(@Header(name = "user") String user, @Body String payload) throws Exception {
User user = userManager.lookupUser(user);
String id = user.getPrimaryId() + generateHashCodeForPayload(payload);
return id;
}
}
Groovy support
www.xebia.fr / blog.xebia.fr 39
Using Java DSL
// lets route if a line item is over $100
from("queue:foo").filter(groovy("request.lineItems.any { i -> i.value > 100 }")).to("queue:bar");
Using Spring XML
<route>
<from uri="queue:foo"/>
<filter>
<groovy>request.lineItems.any { i -> i.value > 100 }</groovy>
<to uri="queue:bar"/>
</filter>
</route>
Testing with camel
www.xebia.fr / blog.xebia.fr 40
Testing of distributed and asynchronous processing is notoriously difficult
Camel provides helpers:▶ Test URIs▶ Datasets▶ Mocks
Maven dependency for tests: <dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<version>${camel-version}</version>
<scope>test</scope>
</dependency>
Testing with camel
www.xebia.fr / blog.xebia.fr 41
public class FilterTest extends CamelTestSupport {
@EndpointInject(uri = "mock:result") protected MockEndpoint resultEndpoint;
@Produce(uri = "direct:start") protected ProducerTemplate template;
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
@Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
}
};
}
}
Spring Test with Java Config
www.xebia.fr / blog.xebia.fr 42
@ContextConfiguration( locations = "org.apache.camel.spring.javaconfig.patterns.FilterTest$ContextConfig", loader = JavaConfigContextLoader.class)
public class FilterTest extends AbstractJUnit4SpringContextTests {
@EndpointInject(uri = "mock:result") protected MockEndpoint resultEndpoint;
@Produce(uri = "direct:start") protected ProducerTemplate template;
@DirtiesContext
@Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
@Configuration
public static class ContextConfig extends SingleRouteCamelConfiguration {
@Bean
public RouteBuilder route() {
return new RouteBuilder() {
public void configure() {
from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
}
};
}
}
}
Camel riding from Java
/META-INF/spring/camelContext.xml
Set the CLASSPATH
Run Camel context from :▶ java org.apache.camel.spring.Main
www.xebia.fr / blog.xebia.fr 43
Camel & Maven
mvn camel:run, camel:dot
Maven declaration:
www.xebia.fr / blog.xebia.fr 44
<?xml version="1.0" encoding="UTF-8"?>
<project><build>
<plugins><plugin>
<groupId>org.apache.camel</groupId><artifactId>camel-maven-plugin</artifactId>
</plugin></plugins>
</build><reporting>
<plugins><plugin>
<groupId>org.apache.camel</groupId><artifactId>camel-maven-plugin</artifactId>
</plugin></plugins>
</reporting></project>
Maven site report
Result of report plugin (Dot & GraphViz)
www.xebia.fr / blog.xebia.fr 45
Where would I use Camel ?
Standalone or in any Spring application
Inside ActiveMQ’s JMS client or the broker
Inside your ESB such as ServiceMix via the servicemix-camel Service Unit
Inside CXF either as a transport or reusing CXF inside Camel
www.xebia.fr / blog.xebia.fr 46
www.xebia.fr / blog.xebia.fr 47
Going further with Apache Camel …
Books
Theme: ▶ This book is essentially the
Camel bible
Edition: ▶ Manning▶ Fall 2010 | 435 pages▶ ISBN: 9781935182368
Authors: ▶ Claus Ibsen, Committer and
lead▶ Jonathan Anstey, Committer▶ Hadrian Zbarcea, Committer,
Chair of PMC and Release Manager
www.xebia.fr / blog.xebia.fr 48
Books
Theme: ▶ catalog of sixty-five patterns,
with real-world solutions
Edition: ▶ Addison-Wesley Professional▶ October 2003 | 736 pages▶ ISBN: 9780321200686
Authors: ▶ Gregor Hohpe, Software
engineer at Google.▶ Bobby Woolf, WebSphere
consultant at IBM.
www.xebia.fr / blog.xebia.fr 49
www.xebia.fr / blog.xebia.fr 50
Twitter: @alexiskinsella
Thank You