Routing messages to different destinations (Simple)
In the previous tutorial, we routed all the messages through the same processing steps. Integrating real applications usually involves more complex routing scenarios where each message type is processed differently. In this tutorial, we are going to route a message into different locations based on the message content, using a Context-Based Router pattern.
Getting ready
The complete source code for this tutorial is located under the following project: camel-message-routing-examples/routing-different-destinations
.
How to do it...
We will extend the previous example by adding conditions to direct incoming files into different folders based on file names:
from("file://source") .choice() .when(simple("${in.header.CamelFileName} contains 'widget.txt'")) .to("file://widget") .when(simple("${in.header.CamelFileName} contains 'gadget.txt'")) .to("file://gadget") .otherwise() .to("log://org.apache.camel.howto?showAll=true");
How it works...
A Context-Based Router is similar to a switch
statement or to the if then
-elseif
statement in Java. The incoming message will be directed to one of the possible channels, depending on the condition that is evaluated, usually against the message content. In our example, there are three possible destinations for the incoming messages, and the conditions are written using Simple language. The condition has to be a predicate, that is, an expression that returns only true or false. If the result is true, the message is routed into that channel of the Context-Based Router, otherwise the next condition in order is evaluated. Similar to the default
case in a Java switch
statement, there is an optional otherwise
element for this pattern. If the message doesn't match any of the predicates, it will be directed to the default channel of the route.
Camel expressions operate on the routed message and are used as part of various integration patterns. There are two types of expressions: one that implements the Expression interface and can produce a value from any type, and the other that implements the Predicate interface and produces a Boolean result. Expressions give access to all parts of the routed message, which in Camel world is represented by the Exchange
class and has the following structure:
The Exchange
class wraps the mandatory In Message
representing the request data coming from an inbound channel, and an optional Out Message
for the response going into an outbound channel. Each message consists of a payload called Body
with Object type and Headers
, which is a Map for storing key-value pairs associated with the message. The Exchange
class also has Properties
for storing Camel specific data, Message Exchange Pattern
(MEP
), ExchangeId
, and Exception
field for tracking exceptions if any were encountered during routing. Usually, when a processor receives Exchange
, it reads the data from In Message
(including Body
and Headers
), and depending on MEP writes the processed results to Out Message
or updates In Message
, to be passed to the next processor in the route. In our example, file consumer reads a file from the source folder then puts its reference to the message body, and populates various headers with information about the file (this is where the CamelFileName
header gets populated). Then the expressions in the Context-Based Router retrieve the file name from the headers and do the comparison. If the file name contains the string widget.txt
the message is sent to the widget
folder, otherwise the next condition is evaluated which does a similar comparison with the gadget.txt
string. If the message doesn't satisfy any of the conditions, it is sent to the default channel where it is logged and not written to any folder.
There's more...
Next, we will have a look at the other expression languages that Camel supports, and how to make the routing even more dynamic, using Java beans for deciding which is the next endpoint for a message.
Expression languages
The Predicate
statement used in the previous example was created using Simple language, but there are many other supported languages. It includes popular scripting languages such as Beanshell, Groovy, Ruby, Python and languages for working with XML, such as XPath, XQuery, the Unified EL, OGNL, among others. The choice of language usually depends on the message format and personal preferences, but Simple language is flexible enough for most occasions. Simple language is a home grown language developed by the Camel community for writing powerful expressions. It gives easy access to different parts of the Exchange
, such as input body ${in.body}
or a specific message header ${in.header.userId}
, properties {property.someKey}
, environment variables ${sysenv.someOtherKet}
, and so on. It also has good operator support used mostly for predicates such as >=
, contains
, !=
, regex
, &&
, ||
, and so on.
Dynamic routing
A Context-Based Router can direct a message to the correct recipient if all the recipients are known in advance. All of the outbound channels of the Context-Based Router have to be specified as part of its definition, and only then it can choose one based on the message content. This introduces a dependency of the router to all possible destinations and prevents it from choosing a destination dynamically. Dynamic Router pattern solves this problem by choosing a destination for each message at runtime, and configuring itself using control messages from each participating destination.
In Camel, a Dynamic Router pattern is implemented with the dynamicRouter
statement that accepts an expression to determine where the message should go next. After routing the message, the expression is evaluated again using the updated message to determine where the message should be routed next. Evaluating the expression and routing the message continues until the expression returns null to indicate the end of routing for that message. So it is important for the expression to return null at some point, otherwise Camel will continue routing the same message endlessly. Because of this complex nature, dynamicRouter
expressions usually use Bean expressions:
from("direct:start") .dynamicRouter(method(DestinationChooser.class, "nextEndpoint"));
Note
For an example using the Dynamic Router, have a look at http://camel.apache.org/dynamic-router.html.