CookBook
This document describes various recipes for working with Camel- Bean Integration describes how to work with beans and Camel in a loosely coupled way so that your beans do not have to depend on any Camel APIs
- Annotation Based Expression Language binds expressions to method parameters
- Bean Binding defines which methods are invoked and how the Message is converted into the parameters of the method when it is invoked
- Bean Injection for injecting Camel related resources into your POJOs
- Parameter Binding Annotations for extracting various headers, properties or payloads from a Message
- POJO Consuming for consuming and possibly routing messages from Camel
- POJO Producing for producing camel messages from your POJOs
- RecipientList Annotation for creating a Recipient List from a POJO method
- Using Exchange Pattern Annotations describes how pattern annotations can be used to change the behaviour of method invocations
- Hiding Middleware describes how to avoid your business logic being coupled to any particular middleware APIs allowing you to easily switch from in JVM SEDA to JMS, ActiveMQ, Hibernate, JPA, JDBC, iBatis or JavaSpace etc.
- Visualisation describes how to visualise your Enterprise Integration Patterns to help you understand your routing rules
- Business Activity Monitoring (BAM) for monitoring business processes across systems
- Extract Transform Load (ETL) to load data into systems or databases
- Testing for testing distributed and asynchronous systems using a messaging approach
- Camel Test for creating test cases using a single Java class for all your configuration and routing
- Spring Testing uses Spring Test together with either XML or Java Config to dependency inject your test classes
- Guice uses Guice to dependency inject your test classes
- Templating is a great way to create service stubs to be able to test your system without some back end system.
- Database for working with databases
- Parallel Processing and Ordering on how using parallel processing and SEDA or JMS based load balancing can be achieved.
- Asynchronous Processing in Camel Routes.
- Implementing Virtual Topics on other JMS providers shows how to get the effect of Virtual Topics and avoid issues with JMS durable topics
- Camel Transport for CXF describes how to put the Camel context into the CXF transport layer.
- Fine Grained Control Over a Channel describes how to deliver a sequence of messages over a single channel and then stopping any more messages being sent over that channel. Typically used for sending data over a socket and then closing the socket.
- EventNotifier to log details about all sent Exchanges shows how to let Camels
EventNotifier
log all sent to endpoint events and how long time it took. - Loading routes from XML files into an existing CamelContext.
- Using MDC logging with Camel
- Running Camel standalone and have it keep running shows how to keep Camel running when you run it standalone.
- Hazelcast Idempotent Repository Tutorial shows how to avoid to consume duplicated messages in a clustered environment.
- How to use Camel as a HTTP proxy between a client and server shows how to use Camel as a HTTP adapter/proxy between a client and HTTP service.
Bean Integration
Camel supports the integration of beans and POJOs in a number of ways
Annotations
If a bean is defined in Spring XML or scanned using the Spring component scanning mechanism and a <camelContext> is used or a CamelBeanPostProcessor
then we process a number of Camel annotations to do various things such as injecting resources or producing, consuming or routing messages.
The following annotations is supported and inject by Camel's CamelBeanPostProcessor
Annotation |
Description |
---|---|
|
To inject an endpoint, see more details at POJO Producing. |
|
Camel 2.13: To inject a bean obtained from the Registry. See Bean Injection. |
|
Camel 2.12: To inject a value using property placeholder. |
|
To inject a producer to send message to an endpoint. See POJO Producing. |
|
To inject a consumer on a method. See POJO Consuming. |
See more details at:
- POJO Consuming to consume and possibly route messages from Camel
- POJO Producing to make it easy to produce camel messages from your POJOs
- DynamicRouter Annotation for creating a Dynamic Router from a POJO method
- RecipientList Annotation for creating a Recipient List from a POJO method
- RoutingSlip Annotation for creating a Routing Slip for a POJO method
- Bean Injection to inject Camel related resources into your POJOs
- Using Exchange Pattern Annotations describes how the pattern annotations can be used to change the behaviour of method invocations with Spring Remoting or POJO Producing
Example
See the POJO Messaging Example for how to use the annotations for routing and messaging.
Bean Component
The Bean component allows one to invoke a particular method. Alternately the Bean component supports the creation of a proxy via ProxyHelper to a Java interface; which the implementation just sends a message containing a BeanInvocation to some Camel endpoint.
Spring Remoting
We support a Spring Remoting provider which uses Camel as the underlying transport mechanism. The nice thing about this approach is we can use any of the Camel transport Components to communicate between beans. It also means we can use Content Based Router and the other Enterprise Integration Patterns in between the beans; in particular we can use Message Translator to be able to convert what the on-the-wire messages look like in addition to adding various headers and so forth.
Bean binding
Whenever Camel invokes a bean method via one of the above methods (Bean component, Spring Remoting or POJO Consuming) then the Bean Binding mechanism is used to figure out what method to use (if it is not explicit) and how to bind the Message to the parameters possibly using the Parameter Binding Annotations or using a method name option.
Annotation Based Expression Language
You can also use any of the Languages supported in Camel to bind expressions to method parameters when using Bean Integration. For example you can use any of these annotations:
Annotation |
Description |
---|---|
Inject a Bean expression |
|
Inject a BeanShell expression |
|
Inject a Constant expression |
|
Inject an EL expression |
|
Inject a Groovy expression |
|
Inject a Header expression |
|
Inject a JavaScript expression |
|
Inject a MVEL expression |
|
Inject an OGNL expression |
|
Inject a PHP expression |
|
Inject a Python expression |
|
Inject a Ruby expression |
|
Inject an Simple expression |
|
Inject an XPath expression |
|
Inject an XQuery expression |
Example:
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 } }
Advanced example using @Bean
And an example of using the the @Bean binding annotation, where you can use a POJO where you can do whatever java code you like:
public class Foo { @MessageDriven(uri = "activemq:my.queue") public void doSomething(@Bean("myCorrelationIdGenerator") String correlationID, @Body String body) { // process the inbound message here } }
And then we can have a spring bean with the id myCorrelationIdGenerator where we can compute the id.
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 userId = user.getPrimaryId(); String id = userId + generateHashCodeForPayload(payload); return id; } }
The POJO MyIdGenerator has one public method that accepts two parameters. However we have also annotated this one with the @Header and @Body annotation to help Camel know what to bind here from the Message from the Exchange being processed.
Of course this could be simplified a lot if you for instance just have a simple id generator. But we wanted to demonstrate that you can use the Bean Binding annotations anywhere.
public class MySimpleIdGenerator { public static int generate() { // generate a unique id return 123; } }
And finally we just need to remember to have our bean registered in the Spring Registry:
<bean id="myCorrelationIdGenerator" class="com.mycompany.MySimpleIdGenerator"/>
Example using Groovy
In this example we have an Exchange that has a User object stored in the in header. This User object has methods to get some user information. We want to use Groovy to inject an expression that extracts and concats the fullname of the user into the fullName parameter.
public void doSomething(@Groovy("$request.header['user'].firstName $request.header['user'].familyName) String fullName, @Body String body) { // process the inbound message here }
Groovy supports GStrings that is like a template where we can insert $ placeholders that will be evaluated by Groovy.
Bean Binding
Bean Binding in Camel defines both which methods are invoked and also how the Message is converted into the parameters of the method when it is invoked.
Choosing the method to invoke
The binding of a Camel Message to a bean method call can occur in different ways, in the following order of importance:
- if the message contains the header CamelBeanMethodName then that method is invoked, converting the body to the type of the method's argument.
- From Camel 2.8 onwards you can qualify parameter types to select exactly which method to use among overloads with the same name (see below for more details).
- From Camel 2.9 onwards you can specify parameter values directly in the method option (see below for more details).
- you can explicitly specify the method name in the DSL or when using POJO Consuming or POJO Producing
- if the bean has a method marked with the
@Handler
annotation, then that method is selected - if the bean can be converted to a Processor using the Type Converter mechanism, then this is used to process the message. The ActiveMQ component uses this mechanism to allow any JMS MessageListener to be invoked directly by Camel without having to write any integration glue code. You can use the same mechanism to integrate Camel into any other messaging/remoting frameworks.
- if the body of the message can be converted to a BeanInvocation (the default payload used by the ProxyHelper) component - then that is used to invoke the method and pass its arguments
- otherwise the type of the body is used to find a matching method; an error is thrown if a single method cannot be chosen unambiguously.
- you can also use Exchange as the parameter itself, but then the return type must be void.
- if the bean class is private (or package-private), interface methods will be preferred (from Camel 2.9 onwards) since Camel can't invoke class methods on such beans
In cases where Camel cannot choose a method to invoke, an AmbiguousMethodCallException
is thrown.
By default the return value is set on the outbound message body.
Asynchronous processing
From Camel 2.18 onwards you can return a CompletionStage implementation (e.g. a CompletableFuture) to implement asynchronous processing.
Please be sure to properly complete the CompletionStage with the result or exception, including any timeout handling. Exchange processing would wait for completion and would not impose any timeouts automatically. It's extremely useful to monitor Inflight repository for any hanging messages.
Note that completing with "null" won't set outbody message body to null, but would keep message intact. This is useful to support methods that don't modify exchange and return CompletableFuture<Void>. To set body to null, just add Exchange method parameter and directly modify exchange messages.
Examples:
Simple asynchronous processor, modifying message body.
public CompletableFuture<String> doSomethingAsync(String body)
Composite processor that do not modify exchange
public CompletableFuture<Void> doSomethingAsync(String body) { return CompletableFuture.allOf(doA(body), doB(body), doC()); }
Parameter binding
When a method has been chosen for invocation, Camel will bind to the parameters of the method.
The following Camel-specific types are automatically bound:
org.apache.camel.Exchange
org.apache.camel.Message
org.apache.camel.CamelContext
org.apache.camel.TypeConverter
org.apache.camel.spi.Registry
java.lang.Exception
So, if you declare any of these types, they will be provided by Camel. Note that Exception
will bind to the caught exception of the Exchange - so it's often usable if you employ a Pojo to handle, e.g., an onException
route.
What is most interesting is that Camel will also try to bind the body of the Exchange to the first parameter of the method signature (albeit not of any of the types above). So if, for instance, we declare a parameter as String body
, then Camel will bind the IN body to this type. Camel will also automatically convert to the type declared in the method signature.
Let's review some examples:
Below is a simple method with a body binding. Camel will bind the IN body to the body
parameter and convert it to a String
.
public String doSomething(String body)
In the following sample we got one of the automatically-bound types as well - for instance, a Registry
that we can use to lookup beans.
public String doSomething(String body, Registry registry)
We can use Exchange as well:
public String doSomething(String body, Exchange exchange)
You can also have multiple types:
public String doSomething(String body, Exchange exchange, TypeConverter converter)
And imagine you use a Pojo to handle a given custom exception InvalidOrderException
- we can then bind that as well:
public String badOrder(String body, InvalidOrderException invalid)
Notice that we can bind to it even if we use a sub type of java.lang.Exception
as Camel still knows it's an exception and can bind the cause (if any exists).
So what about headers and other stuff? Well now it gets a bit tricky - so we can use annotations to help us, or specify the binding in the method name option.
See the following sections for more detail.
Binding Annotations
You can use the Parameter Binding Annotations to customize how parameter values are created from the Message
Examples
For example, a Bean such as:
public class Bar { public String doSomething(String body) { // process the in body and return whatever you want return "Bye World"; }
Or the Exchange example. Notice that the return type must be void when there is only a single parameter of the type org.apache.camel.Exchange
:
public class Bar { public void doSomething(Exchange exchange) { // process the exchange exchange.getIn().setBody("Bye World"); }
@Handler
You can mark a method in your bean with the @Handler annotation to indicate that this method should be used for Bean Binding.
This has an advantage as you need not specify a method name in the Camel route, and therefore do not run into problems after renaming the method in an IDE that can't find all its references.
public class Bar { @Handler public String doSomething(String body) { // process the in body and return whatever you want return "Bye World"; } }
Parameter binding using method option
Available as of Camel 2.9
Camel uses the following rules to determine if it's a parameter value in the method option
- The value is either
true
orfalse
which denotes a boolean value - The value is a numeric value such as
123
or7
- The value is a String enclosed with either single or double quotes
- The value is null which denotes a
null
value - It can be evaluated using the Simple language, which means you can use, e.g., body, header.foo and other Simple tokens. Notice the tokens must be enclosed with ${ }.
Any other value is consider to be a type declaration instead - see the next section about specifying types for overloaded methods.
When invoking a Bean you can instruct Camel to invoke a specific method by providing the method name:
.bean(OrderService.class, "doSomething")
Here we tell Camel to invoke the doSomething method - Camel handles the parameters' binding. Now suppose the method has 2 parameters, and the 2nd parameter is a boolean where we want to pass in a true value:
public void doSomething(String payload, boolean highPriority) { ... }
This is now possible in Camel 2.9 onwards:
.bean(OrderService.class, "doSomething(*, true)")
In the example above, we defined the first parameter using the wild card symbol *, which tells Camel to bind this parameter to any type, and let Camel figure this out. The 2nd parameter has a fixed value of true
. Instead of the wildcard symbol we can instruct Camel to use the message body as shown:
.bean(OrderService.class, "doSomething(${body}, true)")
The syntax of the parameters is using the Simple expression language so we have to use ${ } placeholders in the body to refer to the message body.
If you want to pass in a null
value, then you can explicit define this in the method option as shown below:
.to("bean:orderService?method=doSomething(null, true)")
Specifying null
as a parameter value instructs Camel to force passing a null
value.
Besides the message body, you can pass in the message headers as a java.util.Map
:
.bean(OrderService.class, "doSomethingWithHeaders(${body}, ${headers})")
You can also pass in other fixed values besides booleans. For example, you can pass in a String and an integer:
.bean(MyBean.class, "echo('World', 5)")
In the example above, we invoke the echo method with two parameters. The first has the content 'World' (without quotes), and the 2nd has the value of 5.
Camel will automatically convert these values to the parameters' types.
Having the power of the Simple language allows us to bind to message headers and other values such as:
.bean(OrderService.class, "doSomething(${body}, ${header.high})")
You can also use the OGNL support of the Simple expression language. Now suppose the message body is an object which has a method named asXml
. To invoke the asXml
method we can do as follows:
.bean(OrderService.class, "doSomething(${body.asXml}, ${header.high})")
Instead of using .bean
as shown in the examples above, you may want to use .to
instead as shown:
.to("bean:orderService?method=doSomething(${body.asXml}, ${header.high})")
Using type qualifiers to select among overloaded methods
Available as of Camel 2.8
If you have a Bean with overloaded methods, you can now specify parameter types in the method name so Camel can match the method you intend to use.
Given the following bean:
from("direct:start") .bean(MyBean.class, "hello(String)") .to("mock:result");
Then the MyBean
has 2 overloaded methods with the names hello
and times
. So if we want to use the method which has 2 parameters we can do as follows in the Camel route:
from("direct:start") .bean(MyBean.class, "hello(String,String)") .to("mock:result");
We can also use a *
as wildcard so we can just say we want to execute the method with 2 parameters we do
from("direct:start") .bean(MyBean.class, "hello(*,*)") .to("mock:result");
By default Camel will match the type name using the simple name, e.g. any leading package name will be disregarded. However if you want to match using the FQN, then specify the FQN type and Camel will leverage that. So if you have a com.foo.MyOrder
and you want to match against the FQN, and not the simple name "MyOrder", then follow this example:
.bean(OrderService.class, "doSomething(com.foo.MyOrder)")
Camel currently only supports either specifying parameter binding or type per parameter in the method name option. You cannot specify both at the same time, such as
doSomething(com.foo.MyOrder ${body}, boolean ${header.high})
This may change in the future.
@EndpointInject
To allow sending of messages from POJOs you can use the @EndpointInject annotation. This will inject a ProducerTemplate so that the bean can participate in message exchanges.
Example: send a message to the foo.bar
ActiveMQ queue:
public class Foo { @EndpointInject(uri="activemq:foo.bar") ProducerTemplate producer; public void doSomething() { if (whatever) { producer.sendBody("<hello>world!</hello>"); } } }
The downside of this is that your code is now dependent on a Camel API, the ProducerTemplate
. The next section describes how to remove this dependency.
See POJO Consuming for how to use a property on the bean as endpoint configuration, e.g., using the property
attribute on @Produce
, @EndpointInject
.
Hiding the Camel APIs From Your Code Using @Produce
We recommend Hiding Middleware APIs from your application code so the next option might be more suitable. You can add the @Produce
annotation to an injection point (a field or property setter) using a ProducerTemplate
or using some interface you use in your business logic. Example:
public interface MyListener { String sayHello(String name); } public class MyBean { @Produce(uri = "activemq:foo") protected MyListener producer; public void doSomething() { // lets send a message String response = producer.sayHello("James"); } }
Here Camel will automatically inject a smart client side proxy at the @Produce
annotation - an instance of the MyListener
instance. When we invoke methods on this interface the method call is turned into an object and using the Camel Spring Remoting mechanism it is sent to the endpoint - in this case the ActiveMQ endpoint to queue foo
; then the caller blocks for a response.
If you want to make asynchronous message sends then use an @InOnly annotation on the injection point.
@RecipientList Annotation
We support the use of @RecipientList on a bean method to easily create a dynamic Recipient List using a Java method.
Simple Example using @Consume and @RecipientList
package com.acme.foo; public class RouterBean { @Consume(uri = "activemq:foo") @RecipientList public String[] route(String body) { return new String[]{"activemq:bar", "activemq:whatnot"}; } }
For example if the above bean is configured in Spring when using a <camelContext> element as follows
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd "> <camelContext xmlns="http://activemq.apache.org/camel/schema/spring"/> <bean id="myRecipientList" class="com.acme.foo.RouterBean"/> </beans>
then a route will be created consuming from the foo queue on the ActiveMQ component which when a message is received the message will be forwarded to the endpoints defined by the result of this method call - namely the bar and whatnot queues.
How it works
The return value of the @RecipientList method is converted to either a java.util.Collection / java.util.Iterator or array of objects where each element is converted to an Endpoint or a String, or if you are only going to route to a single endpoint then just return either an Endpoint object or an object that can be converted to a String. So the following methods are all valid
@RecipientList public String[] route(String body) { ... } @RecipientList public List<String> route(String body) { ... } @RecipientList public Endpoint route(String body) { ... } @RecipientList public Endpoint[] route(String body) { ... } @RecipientList public Collection<Endpoint> route(String body) { ... } @RecipientList public URI route(String body) { ... } @RecipientList public URI[] route(String body) { ... }
Then for each endpoint or URI the message is forwarded a separate copy to that endpoint.
You can then use whatever Java code you wish to figure out what endpoints to route to; for example you can use the Bean Binding annotations to inject parts of the message body or headers or use Expression values on the message.
More Complex Example Using DSL
In this example we will use more complex Bean Binding, plus we will use a separate route to invoke the Recipient List
public class RouterBean2 { @RecipientList public String route(@Header("customerID") String custID String body) { if (custID == null) return null; return "activemq:Customers.Orders." + custID; } } public class MyRouteBuilder extends RouteBuilder { protected void configure() { from("activemq:Orders.Incoming").recipientList(bean("myRouterBean", "route")); } }
Notice how we are injecting some headers or expressions and using them to determine the recipients using Recipient List EIP.
See the Bean Integration for more details.
Visualisation
This functionality is deprecated and to be removed in future Camel releases.
Camel supports the visualisation of your Enterprise Integration Patterns using the GraphViz DOT files which can either be rendered directly via a suitable GraphViz tool or turned into HTML, PNG or SVG files via the Camel Maven Plugin.
Here is a typical example of the kind of thing we can generate
If you click on the actual generated htmlyou will see that you can navigate from an EIP node to its pattern page, along with getting hover-over tool tips ec.
How to generate
See Camel Dot Maven Goal or the other maven goals Camel Maven Plugin
For OS X users
If you are using OS X then you can open the DOT file using graphviz which will then automatically re-render if it changes, so you end up with a real time graphical representation of the topic and queue hierarchies!
Also if you want to edit the layout a little before adding it to a wiki to distribute to your team, open the DOT file with OmniGraffle then just edit away
Business Activity Monitoring
The Camel BAM module provides a Business Activity Monitoring (BAM) framework for testing business processes across multiple message exchanges on different Endpoint instances.
Consider, for example, a simple system in which you submit Purchase Orders into system A and then receive Invoices from system B. You might want to test that, for a given Purchase Order, you receive a matching Invoice from system B within a specific time period.
How Camel BAM Works
Camel BAM uses a Correlation Identifier on an input message to determine the Process Instance to which it belongs. The process instance is an entity bean which can maintain state for each Activity (where an activity typically maps to a single endpoint - such as the submission of Purchase Orders or the receipt of Invoices).
You can then add rules to be triggered when a message is received on any activity - such as to set time expectations or perform real time reconciliation of values across activities.
Simple Example
The following example shows how to perform some time based rules on a simple business process of 2 activities - A and B - which correspond with Purchase Orders and Invoices in the example above. If you would like to experiment with this scenario, you may edit this Test Case, which defines the activities and rules, and then tests that they work.
As you can see in the above example, we first define two activities, and then rules to specify when we expect them to complete for a process instance and when an error condition should be raised.p. The ProcessBuilder is a RouteBuilder and can be added to any CamelContext.
Complete Example
For a complete example please see the BAM Example, which is part of the standard Camel Examples
Use Cases
In the world of finance, a common requirement is tracking trades. Often a trader will submit a Front Office Trade which then flows through the Middle Office and Back Office through various systems to settle the trade so that money is exchanged. You may wish to test that the front and back office trades match up within a certain time period; if they don't match or a back office trade does not arrive within a required amount of time, you might signal an alarm.
Extract Transform Load (ETL)
The ETL (Extract, Transform, Load) is a mechanism for loading data into systems or databases using some kind of Data Format from a variety of sources; often files then using Pipes and Filters, Message Translator and possible other Enterprise Integration Patterns.
So you could query data from various Camel Components such as File, HTTP or JPA, perform multiple patterns such as Splitter or Message Translator then send the messages to some other Component.
To show how this all fits together, try the ETL Example
Mock Component
The Mock component provides a powerful declarative testing mechanism, which is similar to jMock in that it allows declarative expectations to be created on any Mock endpoint before a test begins. Then the test is run, which typically fires messages to one or more endpoints, and finally the expectations can be asserted in a test case to ensure the system worked as expected.
This allows you to test various things like:
- The correct number of messages are received on each endpoint,
- The correct payloads are received, in the right order,
- Messages arrive on an endpoint in order, using some Expression to create an order testing function,
- Messages arrive match some kind of Predicate such as that specific headers have certain values, or that parts of the messages match some predicate, such as by evaluating an XPath or XQuery Expression.
Note that there is also the Test endpoint which is a Mock endpoint, but which uses a second endpoint to provide the list of expected message bodies and automatically sets up the Mock endpoint assertions. In other words, it's a Mock endpoint that automatically sets up its assertions from some sample messages in a File or database, for example.
Remember that Mock is designed for testing. When you add Mock endpoints to a route, each Exchange sent to the endpoint will be stored (to allow for later validation) in memory until explicitly reset or the JVM is restarted. If you are sending high volume and/or large messages, this may cause excessive memory use. If your goal is to test deployable routes inline, consider using NotifyBuilder or AdviceWith in your tests instead of adding Mock endpoints to routes directly.
From Camel 2.10 onwards there are two new options retainFirst
, and retainLast
that can be used to limit the number of messages the Mock endpoints keep in memory.
URI format
Where someName can be any string that uniquely identifies the endpoint.
You can append query options to the URI in the following format, ?option=value&option=value&...
Options
Option | Default | Description |
---|---|---|
|
| A size to use a throughput logger for reporting |
|
| Camel 2.10: To only keep first X number of messages in memory. |
|
| Camel 2.10: To only keep last X number of messages in memory. |
Simple Example
Here's a simple example of Mock endpoint in use. First, the endpoint is resolved on the context. Then we set an expectation, and then, after the test has run, we assert that our expectations have been met.
You typically always call the assertIsSatisfied() method to test that the expectations were met after running a test.
Camel will by default wait 10 seconds when the assertIsSatisfied()
is invoked. This can be configured by setting the setResultWaitTime(millis)
method.
Using assertPeriod
Available as of Camel 2.7
When the assertion is satisfied then Camel will stop waiting and continue from the assertIsSatisfied
method. That means if a new message arrives on the mock endpoint, just a bit later, that arrival will not affect the outcome of the assertion. Suppose you do want to test that no new messages arrives after a period thereafter, then you can do that by setting the setAssertPeriod
method, for example:
Setting expectations
You can see from the javadoc of MockEndpoint the various helper methods you can use to set expectations. The main methods are as follows:
Method | Description |
---|---|
To define the expected message count on the endpoint. | |
To define the minimum number of expected messages on the endpoint. | |
To define the expected bodies that should be received (in order). | |
To define the expected header that should be received | |
To add an expectation that messages are received in order, using the given Expression to compare messages. | |
To add an expectation that messages are received in order, using the given Expression to compare messages. | |
To add an expectation that no duplicate messages are received; using an Expression to calculate a unique identifier for each message. This could be something like the |
Here's another example:
Adding expectations to specific messages
In addition, you can use the message(int messageIndex) method to add assertions about a specific message that is received.
For example, to add expectations of the headers or body of the first message (using zero-based indexing like java.util.List
), you can use the following code:
There are some examples of the Mock endpoint in use in the camel-core processor tests.
Mocking existing endpoints
Available as of Camel 2.7
Camel now allows you to automatically mock existing endpoints in your Camel routes.
Important: The endpoints are still in action. What happens differently is that a Mock endpoint is injected and receives the message first and then delegates the message to the target endpoint. You can view this as a kind of intercept and delegate or endpoint listener.
Suppose you have the given route below:
You can then use the adviceWith
feature in Camel to mock all the endpoints in a given route from your unit test, as shown below:
Notice that the mock endpoints is given the uri mock:<endpoint>
, for example mock:direct:foo
. Camel logs at INFO
level the endpoints being mocked:
Endpoints which are mocked will have their parameters stripped off. For example the endpoint "log:foo?showAll=true" will be mocked to the following endpoint "mock:log:foo". Notice the parameters have been removed.
Its also possible to only mock certain endpoints using a pattern. For example to mock all log
endpoints you do as shown:
The pattern supported can be a wildcard or a regular expression. See more details about this at Intercept as its the same matching function used by Camel.
Mind that mocking endpoints causes the messages to be copied when they arrive on the mock.
That means Camel will use more memory. This may not be suitable when you send in a lot of messages.
Mocking existing endpoints using the camel-test
component
Instead of using the adviceWith
to instruct Camel to mock endpoints, you can easily enable this behavior when using the camel-test
Test Kit.
The same route can be tested as follows. Notice that we return "*"
from the isMockEndpoints
method, which tells Camel to mock all endpoints.
If you only want to mock all log
endpoints you can return "log*"
instead.
Mocking existing endpoints with XML DSL
If you do not use the camel-test
component for unit testing (as shown above) you can use a different approach when using XML files for routes.
The solution is to create a new XML file used by the unit test and then include the intended XML file which has the route you want to test.
Suppose we have the route in the camel-route.xml
file:
Then we create a new XML file as follows, where we include the camel-route.xml
file and define a spring bean with the class org.apache.camel.impl.InterceptSendToMockEndpointStrategy
which tells Camel to mock all endpoints:
Then in your unit test you load the new XML file (test-camel-route.xml
) instead of camel-route.xml
.
To only mock all Log endpoints you can define the pattern in the constructor for the bean:
Mocking endpoints and skip sending to original endpoint
Available as of Camel 2.10
Sometimes you want to easily mock and skip sending to a certain endpoints. So the message is detoured and send to the mock endpoint only. From Camel 2.10 onwards you can now use the mockEndpointsAndSkip
method using AdviceWith or the Test Kit. The example below will skip sending to the two endpoints "direct:foo"
, and "direct:bar"
.
The same example using the Test Kit
Limiting the number of messages to keep
Available as of Camel 2.10
The Mock endpoints will by default keep a copy of every Exchange that it received. So if you test with a lot of messages, then it will consume memory.
From Camel 2.10 onwards we have introduced two options retainFirst
and retainLast
that can be used to specify to only keep N'th of the first and/or last Exchanges.
For example in the code below, we only want to retain a copy of the first 5 and last 5 Exchanges the mock receives.
Using this has some limitations. The getExchanges()
and getReceivedExchanges()
methods on the MockEndpoint
will return only the retained copies of the Exchanges. So in the example above, the list will contain 10 Exchanges; the first five, and the last five.
The retainFirst
and retainLast
options also have limitations on which expectation methods you can use. For example the expectedXXX methods that work on message bodies, headers, etc. will only operate on the retained messages. In the example above they can test only the expectations on the 10 retained messages.
Testing with arrival times
Available as of Camel 2.7
The Mock endpoint stores the arrival time of the message as a property on the Exchange.
You can use this information to know when the message arrived on the mock. But it also provides foundation to know the time interval between the previous and next message arrived on the mock. You can use this to set expectations using the arrives
DSL on the Mock endpoint.
For example to say that the first message should arrive between 0-2 seconds before the next you can do:
You can also define this as that 2nd message (0 index based) should arrive no later than 0-2 seconds after the previous:
You can also use between to set a lower bound. For example suppose that it should be between 1-4 seconds:
You can also set the expectation on all messages, for example to say that the gap between them should be at most 1 second:
In the example above we use seconds
as the time unit, but Camel offers milliseconds
, and minutes
as well.
Testing
Testing is a crucial activity in any piece of software development or integration. Typically Camel Riders use various different technologies wired together in a variety of patterns with different expression languages together with different forms of Bean Integration and Dependency Injection so its very easy for things to go wrong! . Testing is the crucial weapon to ensure that things work as you would expect.
Camel is a Java library so you can easily wire up tests in whatever unit testing framework you use (JUnit 3.x (deprecated), 4.x, or TestNG). However the Camel project has tried to make the testing of Camel as easy and powerful as possible so we have introduced the following features.
Testing Mechanisms
The following mechanisms are supported:
Name | Component | Description |
---|---|---|
| Is a standalone Java library letting you easily create Camel test cases using a single Java class for all your configuration and routing without using CDI, Spring or Guice for Dependency Injection which does not require an in-depth knowledge of Spring + Spring Test or Guice. Supports JUnit 3.x (deprecated) and JUnit 4.x based tests. | |
CDI Testing | camel-test-cdi | Provides a JUnit 4 runner that bootstraps a test environment using CDI so that you don't have to be familiar with any CDI testing frameworks and can concentrate on the testing logic of your Camel CDI applications. Testing frameworks like Arquillian or PAX Exam, can be used for more advanced test cases, where you need to configure your system under test in a very fine-grained way or target specific CDI containers. |
| Supports JUnit 3.x (deprecated) or JUnit 4.x based tests that bootstrap a test environment using Spring without needing to be familiar with Spring Test. The plain JUnit 3.x/4.x based tests work very similar to the test support classes in Also supports Spring Test based tests that use the declarative style of test configuration and injection common in Spring Test. The Spring Test based tests provide feature parity with the plain JUnit 3.x/4.x based testing approach. Note: | |
| Camel 2.10: Provides the ability to do unit testing on blueprint configurations | |
| Deprecated Uses Guice to dependency inject your test classes | |
Camel TestNG |
| Deprecated Supports plain TestNG based tests with or without CDI, Spring or Guice for Dependency Injection which does not require an in-depth knowledge of CDI, Spring + Spring Test or Guice. From Camel 2.10: this component supports Spring Test based tests that use the declarative style of test configuration and injection common in Spring Test and described in more detail under Spring Testing. |
In all approaches the test classes look pretty much the same in that they all reuse the Camel binding and injection annotations.
Camel Test Example
Here is the Camel Test example:CamelTestSupport
but has no CDI, Spring or Guice dependency injection configuration but instead overrides the createRouteBuilder()
method.
CDI Test Example
Here is the CDI Testing example:camel-example-cdi-test
example and the test classes that come with it.
Spring Test with XML Config Example
Here is the Spring Testing example using XML Config:@DirtiesContext
on the test methods to force Spring Testing to automatically reload the CamelContext
after each test method - this ensures that the tests don't clash with each other, e.g., one test method sending to an endpoint that is then reused in another test method.
Also note the use of @ContextConfiguration
to indicate that by default we should look for the FilterTest-context.xml
on the classpath to configure the test case which looks like this:
Spring Test with Java Config Example
Here is the Spring Testing example using Java Config.
For more information see Spring Java Config.ContextConfig
class does all of the configuration; so your entire test case is contained in a single Java class. We currently have to reference by class name this class in the @ContextConfiguration
which is a bit ugly. Please vote for SJC-238 to address this and make Spring Test work more cleanly with Spring JavaConfig.
Its totally optional but for the ContextConfig
implementation we derive from SingleRouteCamelConfiguration
which is a helper Spring Java Config class which will configure the CamelContext
for us and then register the RouteBuilder
we create.
Since Camel 2.11.0 you can use the CamelSpringJUnit4ClassRunner
with CamelSpringDelegatingTestContextLoader
like example using Java Config with CamelSpringJUnit4ClassRunner
:
Spring Test with XML Config and Declarative Configuration Example
Here is a Camel test support enhanced Spring Testing example using XML Config and pure Spring Test based configuration of the Camel Context:@RunWith
annotation to support the features of CamelTestSupport
through annotations on the test class. See Spring Testing for a list of annotations you can use in your tests.
Blueprint Test
Here is the Blueprint Testing example using XML Config:getBlueprintDescriptors
to indicate that by default we should look for the camelContext.xml
in the package to configure the test case which looks like this:
Testing Endpoints
Camel provides a number of endpoints which can make testing easier.
Name | Description |
---|---|
For load & soak testing this endpoint provides a way to create huge numbers of messages for sending to Components and asserting that they are consumed correctly | |
For testing routes and mediation rules using mocks and allowing assertions to be added to an endpoint | |
Creates a Mock endpoint which expects to receive all the message bodies that could be polled from the given underlying endpoint |
The main endpoint is the Mock endpoint which allows expectations to be added to different endpoints; you can then run your tests and assert that your expectations are met at the end.
Stubbing out physical transport technologies
If you wish to test out a route but want to avoid actually using a real physical transport (for example to unit test a transformation route rather than performing a full integration test) then the following endpoints can be useful.
Name | Description |
---|---|
Direct invocation of the consumer from the producer so that single threaded (non-SEDA) in VM invocation is performed which can be useful to mock out physical transports | |
Delivers messages asynchronously to consumers via a java.util.concurrent.BlockingQueue which is good for testing asynchronous transports | |
Works like SEDA but does not validate the endpoint URI, which makes stubbing much easier. |
Testing existing routes
Camel provides some features to aid during testing of existing routes where you cannot or will not use Mock etc. For example you may have a production ready route which you want to test with some 3rd party API which sends messages into this route.
Name | Description |
---|---|
Allows you to be notified when a certain condition has occurred. For example when the route has completed five messages. You can build complex expressions to match your criteria when to be notified. | |
Allows you to advice or enhance an existing route using a RouteBuilder style. For example you can add interceptors to intercept sending outgoing messages to assert those messages are as expected. |
Camel Test
As a simple alternative to using CDI Testing, Spring Testing or Guice the camel-test module was introduced so you can perform powerful Testing of your Enterprise Integration Patterns easily.
The camel-test
JAR is using JUnit. There is an alternative camel-testng
JAR (from Camel 2.8) using the TestNG test framework.
Adding to your pom.xml
To get started using Camel Test you will need to add an entry to your pom.xml
:
JUnit
TestNG
Available as of Camel 2.8
You might also want to add slf4j
and log4j
to ensure nice logging messages (and maybe adding a log4j.properties file into your src/test/resources
directory).
Writing your test
You firstly need to derive from the class CamelTestSupport
(org.apache.camel.test.CamelTestSupport
, org.apache.camel.test.junit4.CamelTestSupport
, or org.apache.camel.testng.CamelTestSupport
for JUnit 3.x, JUnit 4.x, and TestNG, respectively) and typically you will need to override the createRouteBuilder()
or createRouteBuilders()
method to create routes to be tested.
Here is an example.
Features Provided by CamelTestSupport
The various CamelTestSupport
classes provide a standard set of behaviors relating to the CamelContext
used to host the route(s) under test. The classes provide a number of methods that allow a test to alter the configuration of the CamelContext
used. The following table describes the available customization methods and the default behavior of tests that are built from a CamelTestSupport
class.
Method Name | Description | Default Behavior |
---|---|---|
| If the route builders returned from either | Returns
|
| If the
Its important to start the | Returns The |
| See Setup CamelContext once per class, or per every test method. | The |
| Triggers the auto-mocking of endpoints whose URIs match the provided filter. The default filter is Return See | Disabled |
| If this method returns
are invoked for each processor in the registered routes. | Disabled The methods are not invoked during the test. |
| Returns the number of seconds that Camel should wait for graceful shutdown. Useful for decreasing test times when a message is still in flight at the end of the test. | 10 seconds |
| If JMX should be disabled on the | JMX is disabled |
| Provides a hook for adding objects into the registry. Override this method to bind objects to the registry before test methods are invoked. | An empty registry is initialized |
| Camel 2.10: Allows to add/override properties when Using PropertyPlaceholder in Camel. |
|
| Camel 2.10: Allows to control if Camel should ignore missing locations for properties. |
|
| Camel 2.16: If enabled, then Camel will dump all route coverage statistics into XML files in the target/camel-route-coverage directory. These XML files contains information about "route coverage" of all the routes that was used during the unit test. This allows tooling to inspect these XML files and generate nice route coverage reports. | Disabled |
JNDI
Camel uses a Registry to allow you to configure Component or Endpoint instances or Beans used in your routes. If you are not using Spring or OSGi then JNDI is used as the default registry implementation.
So you will also need to create a jndi.properties
file in your src/test/resources
directory so that there is a default registry available to initialize the CamelContext.
Here is an example jndi.properties file
Dynamically Assigning Ports
Available as of Camel 2.7
Tests that use port numbers will fail if that port is already on use. AvailablePortFinder
provides methods for finding unused port numbers at run time.
Setup CamelContext once per class, or per every test method
Available as of Camel 2.8
The Camel Test kit will by default setup and shutdown CamelContext per every test method in your test class. So for example if you have 3 test methods, then CamelContext is started and shutdown after each test, that is 3 times.
This feature is also supported in camel-testng
When using this the CamelContext will keep state between tests, so have that in mind. So if your unit tests start to fail for no apparent reason, it could be due this fact. So use this feature with a bit of care.
You may want to do this once, to share the CamelContext between test methods, to speedup unit testing. This requires the use of JUnit 4! In your unit test method you have to extend the org.apache.camel.test.junit4.CamelTestSupport
or the org.apache.camel.test.junit4.CamelSpringTestSupport
test class and override the isCreateCamelContextPerClass
method and return true
as shown in the following example:
See Also
Spring Testing
Testing is a crucial part of any development or integration work. The Spring Framework offers a number of features that makes it easy to test while using Spring for Inversion of Control which works with JUnit 3.x, JUnit 4.x, and TestNG.
We can use Spring for IoC and the Camel Mock and Test endpoints to create sophisticated integration/unit tests that are easy to run and debug inside your IDE. There are three supported approaches for testing with Spring in Camel.
Name | Testing Frameworks Supported | Description | Required Camel Test Dependencies |
---|---|---|---|
|
| Provided by:
These base classes provide feature parity with the simple |
|
Plain Spring Test |
| Either extend the abstract base classes:
provided in Spring Test or use the Spring Test JUnit4 runner. These approaches support both the Camel annotations and Spring annotations. However, they do NOT have feature parity with:
|
|
Camel Enhanced Spring Test |
| Either:
| JUnit 3.x (deprecated) - JUnit 4.x - TestNG - |
CamelSpringTestSupport
The following Spring test support classes:
org.apache.camel.test.CamelSpringTestSupport
org.apache.camel.test.junit4.CamelSpringTestSupport
, andorg.apache.camel.testng.CamelSpringTestSupport
extend their non-Spring aware counterparts:
org.apache.camel.test.CamelTestSupport
org.apache.camel.test.junit4.CamelTestSupport
, andorg.apache.camel.testng.CamelTestSupport
and deliver integration with Spring into your test classes.
Instead of instantiating the CamelContext
and routes programmatically, these classes rely on a Spring context to wire the needed components together. If your test extends one of these classes, you must provide the Spring context by implementing the following method.
You are responsible for the instantiation of the Spring context in the method implementation. All of the features available in the non-Spring aware counterparts from Camel Test are available in your test.
Plain Spring Test
In this approach, your test classes directly inherit from the Spring Test abstract test classes or use the JUnit 4.x test runner provided in Spring Test. This approach supports dependency injection into your test class and the full suite of Spring Test annotations. However, it does not support the features provided by the CamelSpringTestSupport
classes.
Plain Spring Test using JUnit 3.x with XML Config Example
Here is a simple unit test using JUnit 3.x support from Spring Test using XML Config.@DirtiesContext
on the test methods to force Spring Testing to automatically reload the CamelContext after each test method - this ensures that the tests don't clash with each other, e.g., one test method sending to an endpoint that is then reused in another test method.
Also notice the use of @ContextConfiguration
to indicate that by default we should look for the file FilterTest-context.xml on the classpath to configure the test case. The test context looks like:FilterTest-context.xml
from the classpath in the same package structure as the FilterTest
class and initialize it along with any Camel routes we define inside it, then inject the CamelContext
instance into our test case.
For instance, like this maven folder layout:
Plain Spring Test Using JUnit 4.x With Java Config Example
You can completely avoid using an XML configuration file by using Spring Java Config. Here is a unit test using JUnit 4.x support from Spring Test using Java Config.ContextConfig
class does all of the configuration; so your entire test case is contained in a single Java class. We currently have to reference by class name this class in the @ContextConfiguration
which is a bit ugly. Please vote for SJC-238 to address this and make Spring Test work more cleanly with Spring JavaConfig.
Plain Spring Test Using JUnit 4.0.x Runner With XML Config
You can avoid extending Spring classes by using the SpringJUnit4ClassRunner
provided by Spring Test. This custom JUnit runner means you are free to choose your own class hierarchy while retaining all the capabilities of Spring Test.
This is for Spring 4.0.x. If you use Spring 4.1 or newer, then see the next section.
Plain Spring Test Using JUnit 4.1.x Runner With XML Config
You can avoid extending Spring classes by using the SpringJUnit4ClassRunner
provided by Spring Test. This custom JUnit runner means you are free to choose your own class hierarchy while retaining all the capabilities of Spring Test.
From Spring 4.1, you need to use the @BootstrapWith
annotation to configure it to use Camel testing, as shown below.
Camel Enhanced Spring Test
Using the org.apache.camel.test.junit4.CamelSpringJUnit4ClassRunner
runner with the @RunWith
annotation or extending org.apache.camel.testng.AbstractCamelTestNGSpringContextTests
provides the full feature set of Spring Test with support for the feature set provided in the CamelTestSupport
classes.
A number of Camel specific annotations have been developed in order to provide for declarative manipulation of the Camel context(s) involved in the test. These annotations free your test classes from having to inherit from the CamelSpringTestSupport
classes and also reduce the amount of code required to customize the tests.
Annotation Class | Applies To | Description | Default Behavioir If Not Present | Default Behavior If Present |
---|---|---|---|---|
|
| Indicates if JMX should be globally disabled in the CamelContexts that are bootstrapped during the test through the use of Spring Test loaded application contexts. | JMX is disabled | JMX is disabled |
|
| Indicates if certain route builder classes should be excluded from discovery. Initializes a | Not enabled and no routes are excluded | No routes are excluded |
|
| Deprecated. Indicates if the CamelContexts that are bootstrapped during the test through the use of Spring Test loaded application contexts should use lazy loading of type converters. | Type converters are not lazy loaded | Type converters are not lazy loaded |
|
| Triggers the auto-mocking of endpoints whose URIs match the provided filter. The default filter is | Not enabled | All endpoints are sniffed and recorded in a mock endpoint. |
|
| Triggers the auto-mocking of endpoints whose URIs match the provided filter. The default filter is | Not enabled | All endpoints are sniffed and recorded in a mock endpoint. The original endpoint is not invoked. |
|
| Indicates that the annotated method returns an | N/A | The returned |
|
| Indicates to set the shutdown timeout of all CamelContexts instantiated through the use of Spring Test loaded application contexts. If no annotation is used, the timeout is automatically reduced to 10 seconds by the test framework. | 10 seconds | 10 seconds |
|
| Indicates the use of The test author is responsible for injecting the Camel contexts into the test and executing | CamelContexts do not automatically start. | CamelContexts do not automatically start. |
|
| Camel 2.16:Indicates that the annotated method returns a | Override properties |
The following example illustrates the use of the @MockEndpoints
annotation in order to setup mock endpoints as interceptors on all endpoints using the Camel Log component and the @DisableJmx
annotation to enable JMX which is disabled during tests by default.
@DirtiesContext
annotation to ensure that the CamelContext, routes, and mock endpoints are reinitialized between test methods.Adding More Mock Expectations
If you wish to programmatically add any new assertions to your test you can easily do so with the following. Notice how we use @EndpointInject
to inject a Camel endpoint into our code then the Mock API to add an expectation on a specific message.
Further Processing the Received Messages
Sometimes once a Mock endpoint has received some messages you want to then process them further to add further assertions that your test case worked as you expect.
So you can then process the received message exchanges if you like...
Sending and Receiving Messages
It might be that the Enterprise Integration Patterns you have defined in either Spring XML or using the Java DSL do all of the sending and receiving and you might just work with the Mock endpoints as described above. However sometimes in a test case its useful to explicitly send or receive messages directly.
To send or receive messages you should use the Bean Integration mechanism. For example to send messages inject a ProducerTemplate
using the @EndpointInject
annotation then call the various send methods on this object to send a message to an endpoint. To consume messages use the @MessageDriven
annotation on a method to have the method invoked when a message is received.
See Also
- A real example test case using Mock and Spring along with its Spring XML
- Bean Integration
- Mock endpoint
- Test endpoint
Templating
When you are testing distributed systems its a very common requirement to have to stub out certain external systems with some stub so that you can test other parts of the system until a specific system is available or written etc.
A great way to do this is using some kind of Template system to generate responses to requests generating a dynamic message using a mostly-static body.
There are a number of templating components included in the Camel distribution you could use
or the following external Camel components
Example
Here's a simple example showing how we can respond to InOut requests on the My.Queue queue on ActiveMQ with a template generated response. The reply would be sent back to the JMSReplyTo Destination.
from("activemq:My.Queue"). to("velocity:com/acme/MyResponse.vm");
If you want to use InOnly and consume the message and send it to another destination you could use
from("activemq:My.Queue"). to("velocity:com/acme/MyResponse.vm"). to("activemq:Another.Queue");
See Also
- Mock for details of mock endpoint testing (as opposed to template based stubs).
Database
Camel can work with databases in a number of different ways. This document tries to outline the most common approaches.
Database endpoints
Camel provides a number of different endpoints for working with databases
- JPA for working with hibernate, openjpa or toplink. When consuming from the endpoints entity beans are read (and deleted/updated to mark as processed) then when producing to the endpoints they are written to the database (via insert/update).
- iBATIS similar to the above but using Apache iBATIS
- JDBC similar though using explicit SQL
- SQL uses spring-jdbc behind the scene for the actual SQL handling. The difference between this component and JDBC component is that in case of SQL the query is a property of the endpoint and it uses message payload as parameters passed to the query
Database pattern implementations
Various patterns can work with databases as follows
- Idempotent Consumer
- Aggregator
- BAM for business activity monitoring
Parallel Processing and Ordering
It is a common requirement to want to use parallel processing of messages for throughput and load balancing, while at the same time process certain kinds of messages in order.
How to achieve parallel processing
You can send messages to a number of Camel Components to achieve parallel processing and load balancing such as
- SEDA for in-JVM load balancing across a thread pool
- ActiveMQ or JMS for distributed load balancing and parallel processing
- JPA for using the database as a poor mans message broker
When processing messages concurrently, you should consider ordering and concurrency issues. These are described below
Concurrency issues
Note that there is no concurrency or locking issue when using ActiveMQ, JMS or SEDA by design; they are designed for highly concurrent use. However there are possible concurrency issues in the Processor of the messages i.e. what the processor does with the message?
For example if a processor of a message transfers money from one account to another account; you probably want to use a database with pessimistic locking to ensure that operation takes place atomically.
Ordering issues
As soon as you send multiple messages to different threads or processes you will end up with an unknown ordering across the entire message stream as each thread is going to process messages concurrently.
For many use cases the order of messages is not too important. However for some applications this can be crucial. e.g. if a customer submits a purchase order version 1, then amends it and sends version 2; you don't want to process the first version last (so that you loose the update). Your Processor might be clever enough to ignore old messages. If not you need to preserve order.
Recommendations
This topic is large and diverse with lots of different requirements; but from a high level here are our recommendations on parallel processing, ordering and concurrency
- for distributed locking, use a database by default, they are very good at it
- to preserve ordering across a JMS queue consider using Exclusive Consumers in the ActiveMQ component
- even better are Message Groups which allows you to preserve ordering across messages while still offering parallelisation via the JMSXGroupID header to determine what can be parallelized
- if you receive messages out of order you could use the Resequencer to put them back together again
A good rule of thumb to help reduce ordering problems is to make sure each single can be processed as an atomic unit in parallel (either without concurrency issues or using say, database locking); or if it can't, use a Message Group to relate the messages together which need to be processed in order by a single thread.
Using Message Groups with Camel
To use a Message Group with Camel you just need to add a header to the output JMS message based on some kind of Correlation Identifier to correlate messages which should be processed in order by a single thread - so that things which don't correlate together can be processed concurrently.
For example the following code shows how to create a message group using an XPath expression taking an invoice's product code as the Correlation Identifier
from("activemq:a").setHeader(JmsConstants.JMS_X_GROUP_ID, xpath("/invoice/productCode")).to("activemq:b");
You can of course use the Xml Configuration if you prefer
Asynchronous Processing
Overview
Supported versions
The information on this page applies for Camel 2.4 or later.
Before Camel 2.4 the asynchronous processing is only implemented for JBI where as in Camel 2.4 we have implemented it in many other areas. See more at Asynchronous Routing Engine.
Camel supports a more complex asynchronous processing model. The asynchronous processors implement the org.apache.camel.AsyncProcessor
interface which is derived from the more synchronous org.apache.camel.Processor
interface. There are advantages and disadvantages when using asynchronous processing when compared to using the standard synchronous processing model.
Advantages:
- Processing routes that are composed fully of asynchronous processors do not use up threads waiting for processors to complete on blocking calls. This can increase the scalability of your system by reducing the number of threads needed to process the same workload.
- Processing routes can be broken up into SEDA processing stages where different thread pools can process the different stages. This means that your routes can be processed concurrently.
Disadvantages:
- Implementing asynchronous processors is more complex than implementing the synchronous versions.
When to Use
We recommend that processors and components be implemented the more simple synchronous APIs unless you identify a performance of scalability requirement that dictates otherwise. A Processor whose process()
method blocks for a long time would be good candidates for being converted into an asynchronous processor.
Interface Details
public interface AsyncProcessor extends Processor { boolean process(Exchange exchange, AsyncCallback callback); }
The AsyncProcessor
defines a single process()
method which is very similar to it's synchronous Processor.process()
brethren.
Here are the differences:
- A non-null
AsyncCallback
MUST be supplied which will be notified when the exchange processing is completed. - It MUST not throw any exceptions that occurred while processing the exchange. Any such exceptions must be stored on the exchange's
Exception
property. - It MUST know if it will complete the processing synchronously or asynchronously. The method will return
true
if it does complete synchronously, otherwise it returnsfalse
. - When the processor has completed processing the exchange, it must call the
callback.done(boolean sync)
method. - The sync parameter MUST match the value returned by the
process()
method.
Implementing Processors that Use the AsyncProcessor API
All processors, even synchronous processors that do not implement the AsyncProcessor
interface, can be coerced to implement the AsyncProcessor
interface. This is usually done when you are implementing a Camel component consumer that supports asynchronous completion of the exchanges that it is pushing through the Camel routes. Consumers are provided a Processor
object when created. All Processor object can be coerced to a AsyncProcessor
using the following API:
Processor processor = ... AsyncProcessor asyncProcessor = AsyncProcessorTypeConverter.convert(processor);
For a route to be fully asynchronous and reap the benefits to lower Thread usage, it must start with the consumer implementation making use of the asynchronous processing API. If it called the synchronous process()
method instead, the consumer's thread would be forced to be blocked and in use for the duration that it takes to process the exchange.
It is important to take note that just because you call the asynchronous API, it does not mean that the processing will take place asynchronously. It only allows the possibility that it can be done without tying up the caller's thread. If the processing happens asynchronously is dependent on the configuration of the Camel route.
Normally, the the process call is passed in an inline inner AsyncCallback
class instance which can reference the exchange object that was declared final. This allows it to finish up any post processing that is needed when the called processor is done processing the exchange.
Example.
final Exchange exchange = ... AsyncProcessor asyncProcessor = ... asyncProcessor.process(exchange, new AsyncCallback() { public void done(boolean sync) { if (exchange.isFailed()) { ... // do failure processing.. perhaps rollback etc. } else { ... // processing completed successfully, finish up // perhaps commit etc. } } });
Asynchronous Route Sequence Scenarios
Now that we have understood the interface contract of the AsyncProcessor
, and have seen how to make use of it when calling processors, let's looks a what the thread model/sequence scenarios will look like for some sample routes.
The Jetty component's consumers support asynchronous processing through the use of continuations. Suffice to say it can take a HTTP request and pass it to a Camel route for asynchronous processing. If the processing is indeed asynchronous, it uses a Jetty continuation so that the HTTP request is 'parked' and the thread is released. Once the Camel route finishes processing the request, the Jetty component uses the AsyncCallback
to tell Jetty to 'un-park' the request. Jetty un-parks the request, the HTTP response returned using the result of the exchange processing.
Notice that the jetty continuations feature is only used "If the processing is indeed async". This is why AsyncProcessor.process()
implementations must accurately report if request is completed synchronously or not.
The jhc
component's producer allows you to make HTTP requests and implement the AsyncProcessor
interface. A route that uses both the jetty asynchronous consumer and the jhc
asynchronous producer will be a fully asynchronous route and has some nice attributes that can be seen if we take a look at a sequence diagram of the processing route.
For the route:
from("jetty:http://localhost:8080/service") .to("jhc:http://localhost/service-impl");
The sequence diagram would look something like this:
The diagram simplifies things by making it looks like processors implement the AsyncCallback
interface when in reality the AsyncCallback
interfaces are inline inner classes, but it illustrates the processing flow and shows how two separate threads are used to complete the processing of the original HTTP request. The first thread is synchronous up until processing hits the jhc
producer which issues the HTTP request. It then reports that the exchange processing will complete asynchronously using NIO to get the response back. Once the jhc
component has received a full response it uses AsyncCallback.done()
method to notify the caller. These callback notifications continue up until it reaches the original Jetty consumer which then un-parks the HTTP request and completes it by providing the response.
Mixing Synchronous and Asynchronous Processors
It is totally possible and reasonable to mix the use of synchronous and asynchronous processors/components. The pipeline processor is the backbone of a Camel processing route. It glues all the processing steps together. It is implemented as an AsyncProcessor
and supports interleaving synchronous and asynchronous processors as the processing steps in the pipeline.
Let's say we have two custom asynchronous processors, namely: MyValidator
and MyTransformation
. Let's say we want to load file from the data/in directory validate them with the MyValidator()
processor, transform them into JPA Java objects using MyTransformation
and then insert them into the database using the JPA component. Let's say that the transformation process takes quite a bit of time and we want to allocate 20
threads to do parallel transformations of the input files. The solution is to make use of the thread processor. The thread is AsyncProcessor
that forces subsequent processing in asynchronous thread from a thread pool.
The route might look like:
from("file:data/in") .process(new MyValidator()) .threads(20) .process(new MyTransformation()) .to("jpa:PurchaseOrder");
The sequence diagram would look something like this:
You would actually have multiple threads executing the second part of the thread sequence.
Staying Synchronous in an AsyncProcessor
Generally speaking you get better throughput processing when you process things synchronously. This is due to the fact that starting up an asynchronous thread and doing a context switch to it adds a little bit of of overhead. So it is generally encouraged that AsyncProcessor
's do as much work as they can synchronously. When they get to a step that would block for a long time, at that point they should return from the process call and let the caller know that it will be completing the call asynchronously.
Implementing Virtual Topics on other JMS providers
ActiveMQ supports Virtual Topics since durable topic subscriptions kinda suck (see this page for more detail) mostly since they don't support Competing Consumers.
Most folks want Queue semantics when consuming messages; so that you can support Competing Consumers for load balancing along with things like Message Groups and Exclusive Consumers to preserve ordering or partition the queue across consumers.
However if you are using another JMS provider you can implement Virtual Topics by switching to ActiveMQ or you can use the following Camel pattern.
First here's the ActiveMQ approach.
- send to activemq:topic:VirtualTopic.Orders
- for consumer A consume from activemq:Consumer.A.VirtualTopic.Orders
When using another message broker use the following pattern
- send to jms:Orders
- add this route with a to() for each logical durable topic subscriber
from("jms:Orders").to("jms:Consumer.A", "jms:Consumer.B", ...);
- for consumer A consume from jms:Consumer.A
What's the Camel Transport for CXF
In CXF you offer or consume a webservice by defining its address. The first part of the address specifies the protocol to use. For example address="http://localhost:9000" in an endpoint configuration means your service will be offered using the http protocol on port 9000 of localhost. When you integrate Camel Tranport into CXF you get a new transport "camel". So you can specify address="camel://direct:MyEndpointName" to bind the CXF service address to a camel direct endpoint.
Technically speaking Camel transport for CXF is a component which implements the CXF transport API with the Camel core library. This allows you to easily use Camel's routing engine and integration patterns support together with your CXF services.
Integrate Camel into CXF transport layer
To include the Camel Tranport into your CXF bus you use the CamelTransportFactory. You can do this in Java as well as in Spring.
Setting up the Camel Transport in Spring
You can use the following snippet in your applicationcontext if you want to configure anything special. If you only want to activate the camel transport you do not have to do anything in your application context. As soon as you include the camel-cxf-transport jar (or camel-cxf.jar if your camel version is less than 2.7.x) in your app, cxf will scan the jar and load a CamelTransportFactory for you.
Integrating the Camel Transport in a programmatic way
Camel transport provides a setContext method that you could use to set the Camel context into the transport factory. If you want this factory take effect, you need to register the factory into the CXF bus. Here is a full example for you.
Configure the destination and conduit with Spring
Namespace
The elements used to configure an Camel transport endpoint are defined in the namespace http://cxf.apache.org/transports/camel
. It is commonly referred to using the prefix camel
. In order to use the Camel transport configuration elements, you will need to add the lines shown below to the beans element of your endpoint's configuration file. In addition, you will need to add the configuration elements' namespace to the xsi:schemaLocation
attribute.
The destination
element
You configure an Camel transport server endpoint using the camel:destination
element and its children. The camel:destination
element takes a single attribute, name
, that specifies the WSDL port element that corresponds to the endpoint. The value for the name
attribute takes the form portQName.camel-destination
. The example below shows the camel:destination
element that would be used to add configuration for an endpoint that was specified by the WSDL fragment <port binding="widgetSOAPBinding" name="widgetSOAPPort">
if the endpoint's target namespace was http://widgets.widgetvendor.net
.
The camel:destination
element for Spring has a number of child elements that specify configuration information. They are described below.
Element | Description |
---|---|
| You can specify the camel context in the camel destination |
| The camel context id which you want inject into the camel destination |
The conduit
element
You configure a Camel transport client using the camel:conduit
element and its children. The camel:conduit
element takes a single attribute, name
, that specifies the WSDL port element that corresponds to the endpoint. The value for the name
attribute takes the form portQName.camel-conduit
. For example, the code below shows the camel:conduit
element that would be used to add configuration for an endpoint that was specified by the WSDL fragment <port binding="widgetSOAPBinding" name="widgetSOAPPort">
if the endpoint's target namespace was http://widgets.widgetvendor.net
.
The camel:conduit
element has a number of child elements that specify configuration information. They are described below.
Element | Description |
---|---|
| You can specify the camel context in the camel conduit |
| The camel context id which you want inject into the camel conduit |
Configure the destination and conduit with Blueprint
From Camel 2.11.x, Camel Transport supports to be configured with Blueprint.
If you are using blueprint, you should use the the namespace http://cxf.apache.org/transports/camel/blueprint
and import the schema like the blow.
In blueprint camel:conduit
camel:destination
only has one camelContextId attribute, they doesn't support to specify the camel context in the camel destination.
Example Using Camel as a load balancer for CXF
This example shows how to use the camel load balancing feature in CXF. You need to load the configuration file in CXF and publish the endpoints on the address "camel://direct:EndpointA" and "camel://direct:EndpointB"
Complete Howto and Example for attaching Camel to CXF
Better JMS Transport for CXF Webservice using Apache Camel