Apache Camel Development Guide

Red Hat Fuse 7.5

Develop applications with Apache Camel

Red Hat Fuse Documentation Team

Abstract

This guide describes how to develop JBoss Fuse applications with Apache Camel. It covers the basic building blocks, enterprise integration patterns, basic syntax for routing expression and predicate languages, creating web services with the Apache CXF component, using the Apache Camel API, and how to create a Camel component that wraps any Java API.

Part I. Implementing Enterprise Integration Patterns

This part describes how to build routes using Apache Camel. It covers the basic building blocks and EIP components.

Chapter 1. Building Blocks for Route Definitions

Abstract

Apache Camel supports two alternative Domain Specific Languages (DSL) for defining routes: a Java DSL and a Spring XML DSL. The basic building blocks for defining routes are endpoints and processors, where the behavior of a processor is typically modified by expressions or logical predicates. Apache Camel enables you to define expressions and predicates using a variety of different languages.

1.1. Implementing a RouteBuilder Class

Overview

To use the Domain Specific Language (DSL), you extend the RouteBuilder class and override its configure() method (where you define your routing rules).

You can define as many RouteBuilder classes as necessary. Each class is instantiated once and is registered with the CamelContext object. Normally, the lifecycle of each RouteBuilder object is managed automatically by the container in which you deploy the router.

RouteBuilder classes

As a router developer, your core task is to implement one or more RouteBuilder classes. There are two alternative RouteBuilder classes that you can inherit from:

  • org.apache.camel.builder.RouteBuilder — this is the generic RouteBuilder base class that is suitable for deploying into any container type. It is provided in the camel-core artifact.
  • org.apache.camel.spring.SpringRouteBuilder — this base class is specially adapted to the Spring container. In particular, it provides extra support for the following Spring specific features: looking up beans in the Spring registry (using the beanRef() Java DSL command) and transactions (see the Transactions Guide for details). It is provided in the camel-spring artifact.

The RouteBuilder class defines methods used to initiate your routing rules (for example, from(), intercept(), and exception()).

Implementing a RouteBuilder

Example 1.1, “Implementation of a RouteBuilder Class” shows a minimal RouteBuilder implementation. The configure() method body contains a routing rule; each rule is a single Java statement.

Example 1.1. Implementation of a RouteBuilder Class

import org.apache.camel.builder.RouteBuilder;

public class MyRouteBuilder extends RouteBuilder {

public void configure() {
  // Define routing rules here:
  from("file:src/data?noop=true").to("file:target/messages");

  // More rules can be included, in you like.
  // ...
}
}

The form of the rule from(URL1).to(URL2) instructs the router to read files from the directory src/data and send them to the directory target/messages. The option ?noop=true instructs the router to retain (not delete) the source files in the src/data directory.

Note

When you use the contextScan with Spring or Blueprint to filter RouteBuilder classes, by default Apache Camel will look for singleton beans. However, you can turn on the old behavior to include prototype scoped with the new option includeNonSingletons.

1.2. Basic Java DSL Syntax

What is a DSL?

A Domain Specific Language (DSL) is a mini-language designed for a special purpose. A DSL does not have to be logically complete but needs enough expressive power to describe problems adequately in the chosen domain. Typically, a DSL does not require a dedicated parser, interpreter, or compiler. A DSL can piggyback on top of an existing object-oriented host language, provided DSL constructs map cleanly to constructs in the host language API.

Consider the following sequence of commands in a hypothetical DSL:

command01;
command02;
command03;

You can map these commands to Java method invocations, as follows:

command01().command02().command03()

You can even map blocks to Java method invocations. For example:

command01().startBlock().command02().command03().endBlock()

The DSL syntax is implicitly defined by the data types of the host language API. For example, the return type of a Java method determines which methods you can legally invoke next (equivalent to the next command in the DSL).

Router rule syntax

Apache Camel defines a router DSL for defining routing rules. You can use this DSL to define rules in the body of a RouteBuilder.configure() implementation. Figure 1.1, “Local Routing Rules” shows an overview of the basic syntax for defining local routing rules.

Figure 1.1. Local Routing Rules

Local routing rules

A local rule always starts with a from("EndpointURL") method, which specifies the source of messages (consumer endpoint) for the routing rule. You can then add an arbitrarily long chain of processors to the rule (for example, filter()). You typically finish off the rule with a to("EndpointURL") method, which specifies the target (producer endpoint) for the messages that pass through the rule. However, it is not always necessary to end a rule with to(). There are alternative ways of specifying the message target in a rule.

Note

You can also define a global routing rule, by starting the rule with a special processor type (such as intercept(), exception(), or errorHandler()). Global rules are outside the scope of this guide.

Consumers and producers

A local rule always starts by defining a consumer endpoint, using from("EndpointURL"), and typically (but not always) ends by defining a producer endpoint, using to("EndpointURL"). The endpoint URLs, EndpointURL, can use any of the components configured at deploy time. For example, you could use a file endpoint, file:MyMessageDirectory, an Apache CXF endpoint, cxf:MyServiceName, or an Apache ActiveMQ endpoint, activemq:queue:MyQName. For a complete list of component types, see Apache Camel Component Reference.

Exchanges

An exchange object consists of a message, augmented by metadata. Exchanges are of central importance in Apache Camel, because the exchange is the standard form in which messages are propagated through routing rules. The main constituents of an exchange are, as follows:

  • In message — is the current message encapsulated by the exchange. As the exchange progresses through a route, this message may be modified. So the In message at the start of a route is typically not the same as the In message at the end of the route. The org.apache.camel.Message type provides a generic model of a message, with the following parts:

    • Body.
    • Headers.
    • Attachments.

    It is important to realize that this is a generic model of a message. Apache Camel supports a large variety of protocols and endpoint types. Hence, it is not possible to standardize the format of the message body or the message headers. For example, the body of a JMS message would have a completely different format to the body of a HTTP message or a Web services message. For this reason, the body and the headers are declared to be of Object type. The original content of the body and the headers is then determined by the endpoint that created the exchange instance (that is, the endpoint appearing in the from() command).

  • Out message — is a temporary holding area for a reply message or for a transformed message. Certain processing nodes (in particular, the to() command) can modify the current message by treating the In message as a request, sending it to a producer endpoint, and then receiving a reply from that endpoint. The reply message is then inserted into the Out message slot in the exchange.

    Normally, if an Out message has been set by the current node, Apache Camel modifies the exchange as follows before passing it to the next node in the route: the old In message is discarded and the Out message is moved to the In message slot. Thus, the reply becomes the new current message. For a more detailed discussion of how Apache Camel connects nodes together in a route, see Section 2.1, “Pipeline Processing”.

    There is one special case where an Out message is treated differently, however. If the consumer endpoint at the start of a route is expecting a reply message, the Out message at the very end of the route is taken to be the consumer endpoint’s reply message (and, what is more, in this case the final node must create an Out message or the consumer endpoint would hang) .

  • Message exchange pattern (MEP) — affects the interaction between the exchange and endpoints in the route, as follows:

    • Consumer endpoint — the consumer endpoint that creates the original exchange sets the initial value of the MEP. The initial value indicates whether the consumer endpoint expects to receive a reply (for example, the InOut MEP) or not (for example, the InOnly MEP).
    • Producer endpoints — the MEP affects the producer endpoints that the exchange encounters along the route (for example, when an exchange passes through a to() node). For example, if the current MEP is InOnly, a to() node would not expect to receive a reply from the endpoint. Sometimes you need to change the current MEP in order to customize the exchange’s interaction with a producer endpoint. For more details, see Section 1.4, “Endpoints”.
  • Exchange properties — a list of named properties containing metadata for the current message.

Message exchange patterns

Using an Exchange object makes it easy to generalize message processing to different message exchange patterns. For example, an asynchronous protocol might define an MEP that consists of a single message that flows from the consumer endpoint to the producer endpoint (an InOnly MEP). An RPC protocol, on the other hand, might define an MEP that consists of a request message and a reply message (an InOut MEP). Currently, Apache Camel supports the following MEPs:

  • InOnly
  • RobustInOnly
  • InOut
  • InOptionalOut
  • OutOnly
  • RobustOutOnly
  • OutIn
  • OutOptionalIn

Where these message exchange patterns are represented by constants in the enumeration type, org.apache.camel.ExchangePattern.

Grouped exchanges

Sometimes it is useful to have a single exchange that encapsulates multiple exchange instances. For this purpose, you can use a grouped exchange. A grouped exchange is essentially an exchange instance that contains a java.util.List of Exchange objects stored in the Exchange.GROUPED_EXCHANGE exchange property. For an example of how to use grouped exchanges, see Section 8.5, “Aggregator”.

Processors

A processor is a node in a route that can access and modify the stream of exchanges passing through the route. Processors can take expression or predicate arguments, that modify their behavior. For example, the rule shown in Figure 1.1, “Local Routing Rules” includes a filter() processor that takes an xpath() predicate as its argument.

Expressions and predicates

Expressions (evaluating to strings or other data types) and predicates (evaluating to true or false) occur frequently as arguments to the built-in processor types. For example, the following filter rule propagates In messages, only if the foo header is equal to the value bar:

from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");

Where the filter is qualified by the predicate, header("foo").isEqualTo("bar"). To construct more sophisticated predicates and expressions, based on the message content, you can use one of the expression and predicate languages (see Part II, “Routing Expression and Predicate Languages”).

1.3. Router Schema in a Spring XML File

Namespace

The router schema — which defines the XML DSL — belongs to the following XML schema namespace:

http://camel.apache.org/schema/spring

Specifying the schema location

The location of the router schema is normally specified to be http://camel.apache.org/schema/spring/camel-spring.xsd, which references the latest version of the schema on the Apache Web site. For example, the root beans element of an Apache Camel Spring file is normally configured as shown in Example 1.2, “Specifying the Router Schema Location”.

Example 1.2. Specifying the Router Schema Location

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:camel="http://camel.apache.org/schema/spring"
       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-2.0.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <!-- Define your routing rules here -->
  </camelContext>
</beans>

Runtime schema location

At run time, Apache Camel does not download the router schema from schema location specified in the Spring file. Instead, Apache Camel automatically picks up a copy of the schema from the root directory of the camel-spring JAR file. This ensures that the version of the schema used to parse the Spring file always matches the current runtime version. This is important, because the latest version of the schema posted up on the Apache Web site might not match the version of the runtime you are currently using.

Using an XML editor

Generally, it is recommended that you edit your Spring files using a full-feature XML editor. An XML editor’s auto-completion features make it much easier to author XML that complies with the router schema and the editor can warn you instantly, if the XML is badly-formed.

XML editors generally do rely on downloading the schema from the location that you specify in the xsi:schemaLocation attribute. In order to be sure you are using the correct schema version whilst editing, it is usually a good idea to select a specific version of the camel-spring.xsd file. For example, to edit a Spring file for the 2.3 version of Apache Camel, you could modify the beans element as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:camel="http://camel.apache.org/schema/spring"
       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-2.0.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.3.0.xsd">
...

Change back to the default, camel-spring.xsd, when you are finished editing. To see which schema versions are currently available for download, navigate to the Web page, http://camel.apache.org/schema/spring.

1.4. Endpoints

Overview

Apache Camel endpoints are the sources and sinks of messages in a route. An endpoint is a very general sort of building block: the only requirement it must satisfy is that it acts either as a source of messages (a producer endpoint) or as a sink of messages (a consumer endpoint). Hence, there are a great variety of different endpoint types supported in Apache Camel, ranging from protocol supporting endpoints, such as HTTP, to simple timer endpoints, such as Quartz, that generate dummy messages at regular time intervals. One of the major strengths of Apache Camel is that it is relatively easy to add a custom component that implements a new endpoint type.

Endpoint URIs

Endpoints are identified by endpoint URIs, which have the following general form:

scheme:contextPath[?queryOptions]

The URI scheme identifies a protocol, such as http, and the contextPath provides URI details that are interpreted by the protocol. In addition, most schemes allow you to define query options, queryOptions, which are specified in the following format:

?option01=value01&option02=value02&...

For example, the following HTTP URI can be used to connect to the Google search engine page:

http://www.google.com

The following File URI can be used to read all of the files appearing under the C:\temp\src\data directory:

file://C:/temp/src/data

Not every scheme represents a protocol. Sometimes a scheme just provides access to a useful utility, such as a timer. For example, the following Timer endpoint URI generates an exchange every second (=1000 milliseconds). You could use this to schedule activity in a route.

timer://tickTock?period=1000

Working with Long Endpoint URIs

Sometimes endpoint URIs can become quite long due to all the accompanying configuration information supplied. In JBoss Fuse 6.2 onwards, there are two approaches you can take to make your working with lengthy URIs more manageable.

Configure Endpoints Separately

You can configure the endpoint separately, and from the routes refer to the endpoints using their shorthand IDs.

<camelContext ...>

  <endpoint id="foo" uri="ftp://foo@myserver">
    <property name="password" value="secret"/>
    <property name="recursive" value="true"/>
    <property name="ftpClient.dataTimeout" value="30000"/>
    <property name="ftpClient.serverLanguageCode" value="fr"/>
  </endpoint>

  <route>
    <from uri="ref:foo"/>
    ...
  </route>
</camelContext>

You can also configure some options in the URI and then use the property attribute to specify additional options (or to override options from the URI).

<endpoint id="foo" uri="ftp://foo@myserver?recursive=true">
  <property name="password" value="secret"/>
  <property name="ftpClient.dataTimeout" value="30000"/>
  <property name="ftpClient.serverLanguageCode" value="fr"/>
</endpoint>
Split Endpoint Configuration Across New Lines

You can split URI attributes using new lines.

<route>
  <from uri="ftp://foo@myserver?password=secret&amp;
           recursive=true&amp;ftpClient.dataTimeout=30000&amp;
           ftpClientConfig.serverLanguageCode=fr"/>
  <to uri="bean:doSomething"/>
</route>
Note

You can specify one or more options on each line, each separated by &amp;.

Specifying time periods in a URI

Many of the Apache Camel components have options whose value is a time period (for example, for specifying timeout values and so on). By default, such time period options are normally specified as a pure number, which is interpreted as a millisecond time period. But Apache Camel also supports a more readable syntax for time periods, which enables you to express the period in hours, minutes, and seconds. Formally, the human-readable time period is a string that conforms to the following syntax:

[NHour(h|hour)][NMin(m|minute)][NSec(s|second)]

Where each term in square brackets, [], is optional and the notation, (A|B), indicates that A and B are alternatives.

For example, you can configure timer endpoint with a 45 minute period as follows:

from("timer:foo?period=45m")
  .to("log:foo");

You can also use arbitrary combinations of the hour, minute, and second units, as follows:

from("timer:foo?period=1h15m")
  .to("log:foo");
from("timer:bar?period=2h30s")
  .to("log:bar");
from("timer:bar?period=3h45m58s")
  .to("log:bar");

Specifying raw values in URI options

By default, the option values that you specify in a URI are automatically URI-encoded. In some cases this is undesirable behavior. For example, when setting a password option, it is preferable to transmit the raw character string without URI encoding.

It is possible to switch off URI encoding by specifying an option value with the syntax, RAW(RawValue). For example,

from("SourceURI")
 .to("ftp:joe@myftpserver.com?password=RAW(se+re?t&23)&binary=true")

In this example, the password value is transmitted as the literal value, se+re?t&23.

Case-insensitive enum options

Some endpoint URI options get mapped to Java enum constants. For example, the level option of the Log component, which can take the enum values, INFO, WARN, ERROR, and so on. This type conversion is case-insensitive, so any of the following alternatives could be used to set the logging level of a Log producer endpoint:

<to uri="log:foo?level=info"/>
<to uri="log:foo?level=INfo"/>
<to uri="log:foo?level=InFo"/>

Specifying URI Resources

From Camel 2.17, the resource based components such as XSLT, Velocity can load the resource file from the Registry by using ref: as prefix.

For example, ifmyvelocityscriptbean and mysimplescriptbean are the IDs of two beans in the registry, you can use the contents of these beans as follows:

Velocity endpoint:
------------------
from("velocity:ref:myvelocityscriptbean").<rest_of_route>.

Language endpoint (for invoking a scripting language):
-----------------------------------------------------
from("direct:start")
  .to("language:simple:ref:mysimplescriptbean")
 Where Camel implicitly converts the bean to a String.

Apache Camel components

Each URI scheme maps to an Apache Camel component, where an Apache Camel component is essentially an endpoint factory. In other words, to use a particular type of endpoint, you must deploy the corresponding Apache Camel component in your runtime container. For example, to use JMS endpoints, you would deploy the JMS component in your container.

Apache Camel provides a large variety of different components that enable you to integrate your application with various transport protocols and third-party products. For example, some of the more commonly used components are: File, JMS, CXF (Web services), HTTP, Jetty, Direct, and Mock. For the full list of supported components, see the Apache Camel component documentation.

Most of the Apache Camel components are packaged separately to the Camel core. If you use Maven to build your application, you can easily add a component (and its third-party dependencies) to your application simply by adding a dependency on the relevant component artifact. For example, to include the HTTP component, you would add the following Maven dependency to your project POM file:

<!-- Maven POM File -->
  <properties>
    <camel-version>{camelFullVersion}</camel-version>
    ...
  </properties>

  <dependencies>
    ...
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-http</artifactId>
      <version>${camel-version}</version>
    </dependency>
    ...
  </dependencies>

The following components are built-in to the Camel core (in the camel-core artifact), so they are always available:

  • Bean
  • Browse
  • Dataset
  • Direct
  • File
  • Log
  • Mock
  • Properties
  • Ref
  • SEDA
  • Timer
  • VM

Consumer endpoints

A consumer endpoint is an endpoint that appears at the start of a route (that is, in a from() DSL command). In other words, the consumer endpoint is responsible for initiating processing in a route: it creates a new exchange instance (typically, based on some message that it has received or obtained), and provides a thread to process the exchange in the rest of the route.

For example, the following JMS consumer endpoint pulls messages off the payments queue and processes them in the route:

from("jms:queue:payments")
  .process(SomeProcessor)
  .to("TargetURI");

Or equivalently, in Spring XML:

<camelContext id="CamelContextID"
              xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="jms:queue:payments"/>
    <process ref="someProcessorId"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

Some components are consumer only — that is, they can only be used to define consumer endpoints. For example, the Quartz component is used exclusively to define consumer endpoints. The following Quartz endpoint generates an event every second (1000 milliseconds):

from("quartz://secondTimer?trigger.repeatInterval=1000")
  .process(SomeProcessor)
  .to("TargetURI");

If you like, you can specify the endpoint URI as a formatted string, using the fromF() Java DSL command. For example, to substitute the username and password into the URI for an FTP endpoint, you could write the route in Java, as follows:

fromF("ftp:%s@fusesource.com?password=%s", username, password)
  .process(SomeProcessor)
  .to("TargetURI");

Where the first occurrence of %s is replaced by the value of the username string and the second occurrence of %s is replaced by the password string. This string formatting mechanism is implemented by String.format() and is similar to the formatting provided by the C printf() function. For details, see java.util.Formatter.

Producer endpoints

A producer endpoint is an endpoint that appears in the middle or at the end of a route (for example, in a to() DSL command). In other words, the producer endpoint receives an existing exchange object and sends the contents of the exchange to the specified endpoint.

For example, the following JMS producer endpoint pushes the contents of the current exchange onto the specified JMS queue:

from("SourceURI")
  .process(SomeProcessor)
  .to("jms:queue:orderForms");

Or equivalently in Spring XML:

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURI"/>
    <process ref="someProcessorId"/>
    <to uri="jms:queue:orderForms"/>
  </route>
</camelContext>

Some components are producer only — that is, they can only be used to define producer endpoints. For example, the HTTP endpoint is used exclusively to define producer endpoints.

from("SourceURI")
  .process(SomeProcessor)
  .to("http://www.google.com/search?hl=en&q=camel+router");

If you like, you can specify the endpoint URI as a formatted string, using the toF() Java DSL command. For example, to substitute a custom Google query into the HTTP URI, you could write the route in Java, as follows:

from("SourceURI")
  .process(SomeProcessor)
  .toF("http://www.google.com/search?hl=en&q=%s", myGoogleQuery);

Where the occurrence of %s is replaced by your custom query string, myGoogleQuery. For details, see java.util.Formatter.

1.5. Processors

Overview

To enable the router to do something more interesting than simply connecting a consumer endpoint to a producer endpoint, you can add processors to your route. A processor is a command you can insert into a routing rule to perform arbitrary processing of messages that flow through the rule. Apache Camel provides a wide variety of different processors, as shown in Table 1.1, “Apache Camel Processors”.

Table 1.1. Apache Camel Processors

Java DSLXML DSLDescription

aggregate()

aggregate

Section 8.5, “Aggregator”: Creates an aggregator, which combines multiple incoming exchanges into a single exchange.

aop()

aop

Use Aspect Oriented Programming (AOP) to do work before and after a specified sub-route.

bean(), beanRef()

bean

Process the current exchange by invoking a method on a Java object (or bean). See Section 2.4, “Bean Integration”.

choice()

choice

Section 8.1, “Content-Based Router”: Selects a particular sub-route based on the exchange content, using when and otherwise clauses.

convertBodyTo()

convertBodyTo

Converts the In message body to the specified type.

delay()

delay

Section 8.9, “Delayer”: Delays the propagation of the exchange to the latter part of the route.

doTry()

doTry

Creates a try/catch block for handling exceptions, using doCatch, doFinally, and end clauses.

end()

N/A

Ends the current command block.

enrich(),enrichRef()

enrich

Section 10.1, “Content Enricher”: Combines the current exchange with data requested from a specified producer endpoint URI.

filter()

filter

Section 8.2, “Message Filter”: Uses a predicate expression to filter incoming exchanges.

idempotentConsumer()

idempotentConsumer

Section 11.8, “Idempotent Consumer”: Implements a strategy to suppress duplicate messages.

inheritErrorHandler()

@inheritErrorHandler

Boolean option that can be used to disable the inherited error handler on a particular route node (defined as a sub-clause in the Java DSL and as an attribute in the XML DSL).

inOnly()

inOnly

Either sets the current exchange’s MEP to InOnly (if no arguments) or sends the exchange as an InOnly to the specified endpoint(s).

inOut()

inOut

Either sets the current exchange’s MEP to InOut (if no arguments) or sends the exchange as an InOut to the specified endpoint(s).

loadBalance()

loadBalance

Section 8.10, “Load Balancer”: Implements load balancing over a collection of endpoints.

log()

log

Logs a message to the console.

loop()

loop

Section 8.16, “Loop”: Repeatedly resends each exchange to the latter part of the route.

markRollbackOnly()

@markRollbackOnly

(Transactions) Marks the current transaction for rollback only (no exception is raised). In the XML DSL, this option is set as a boolean attribute on the rollback element. See Apache Karaf Transaction Guide.

markRollbackOnlyLast()

@markRollbackOnlyLast

(Transactions) If one or more transactions have previously been associated with this thread and then suspended, this command marks the latest transaction for rollback only (no exception is raised). In the XML DSL, this option is set as a boolean attribute on the rollback element. See Apache Karaf Transaction Guide.

marshal()

marshal

Transforms into a low-level or binary format using the specified data format, in preparation for sending over a particular transport protocol.

multicast()

multicast

Section 8.13, “Multicast”: Multicasts the current exchange to multiple destinations, where each destination gets its own copy of the exchange.

onCompletion()

onCompletion

Defines a sub-route (terminated by end() in the Java DSL) that gets executed after the main route has completed. See also Section 2.14, “OnCompletion”.

onException()

onException

Defines a sub-route (terminated by end() in the Java DSL) that gets executed whenever the specified exception occurs. Usually defined on its own line (not in a route).

pipeline()

pipeline

Section 5.4, “Pipes and Filters”: Sends the exchange to a series of endpoints, where the output of one endpoint becomes the input of the next endpoint. See also Section 2.1, “Pipeline Processing”.

policy()

policy

Apply a policy to the current route (currently only used for transactional policies — see Apache Karaf Transaction Guide.

pollEnrich(),pollEnrichRef()

pollEnrich

Section 10.1, “Content Enricher”: Combines the current exchange with data polled from a specified consumer endpoint URI.

process(),processRef

process

Execute a custom processor on the current exchange. See the section called “Custom processor” and Part III, “Advanced Camel Programming”.

recipientList()

recipientList

Section 8.3, “Recipient List”: Sends the exchange to a list of recipients that is calculated at runtime (for example, based on the contents of a header).

removeHeader()

removeHeader

Removes the specified header from the exchange’s In message.

removeHeaders()

removeHeaders

Removes the headers matching the specified pattern from the exchange’s In message. The pattern can have the form, prefix\* — in which case it matches every name starting with prefix — otherwise, it is interpreted as a regular expression.

removeProperty()

removeProperty

Removes the specified exchange property from the exchange.

removeProperties()

removeProperties

Removes the properties matching the specified pattern from the exchange. Takes a comma separated list of 1 or more strings as arguments. The first string is the pattern (see removeHeaders() above). Subsequent strings specify exceptions - these properties remain.

resequence()

resequence

Section 8.6, “Resequencer”: Re-orders incoming exchanges on the basis of a specified comparotor operation. Supports a batch mode and a stream mode.

rollback()

rollback

(Transactions) Marks the current transaction for rollback only (also raising an exception, by default). See Apache Karaf Transaction Guide.

routingSlip()

routingSlip

Section 8.7, “Routing Slip”: Routes the exchange through a pipeline that is constructed dynamically, based on the list of endpoint URIs extracted from a slip header.

sample()

sample

Creates a sampling throttler, allowing you to extract a sample of exchanges from the traffic on a route.

setBody()

setBody

Sets the message body of the exchange’s In message.

setExchangePattern()

setExchangePattern

Sets the current exchange’s MEP to the specified value. See the section called “Message exchange patterns”.

setHeader()

setHeader

Sets the specified header in the exchange’s In message.

setOutHeader()

setOutHeader

Sets the specified header in the exchange’s Out message.

setProperty()

setProperty()

Sets the specified exchange property.

sort()

sort

Sorts the contents of the In message body (where a custom comparator can optionally be specified).

split()

split

Section 8.4, “Splitter”: Splits the current exchange into a sequence of exchanges, where each split exchange contains a fragment of the original message body.

stop()

stop

Stops routing the current exchange and marks it as completed.

threads()

threads

Creates a thread pool for concurrent processing of the latter part of the route.

throttle()

throttle

Section 8.8, “Throttler”: Limit the flow rate to the specified level (exchanges per second).

throwException()

throwException

Throw the specified Java exception.

to()

to

Send the exchange to one or more endpoints. See Section 2.1, “Pipeline Processing”.

toF()

N/A

Send the exchange to an endpoint, using string formatting. That is, the endpoint URI string can embed substitutions in the style of the C printf() function.

transacted()

transacted

Create a Spring transaction scope that encloses the latter part of the route. See Apache Karaf Transaction Guide.

transform()

transform

Section 5.6, “Message Translator”: Copy the In message headers to the Out message headers and set the Out message body to the specified value.

unmarshal()

unmarshal

Transforms the In message body from a low-level or binary format to a high-level format, using the specified data format.

validate()

validate

Takes a predicate expression to test whether the current message is valid. If the predicate returns false, throws a PredicateValidationException exception.

wireTap()

wireTap

Section 12.3, “Wire Tap”: Sends a copy of the current exchange to the specified wire tap URI, using the ExchangePattern.InOnly MEP.

Some sample processors

To get some idea of how to use processors in a route, see the following examples:

Choice

The choice() processor is a conditional statement that is used to route incoming messages to alternative producer endpoints. Each alternative producer endpoint is preceded by a when() method, which takes a predicate argument. If the predicate is true, the following target is selected, otherwise processing proceeds to the next when() method in the rule. For example, the following choice() processor directs incoming messages to either Target1, Target2, or Target3, depending on the values of Predicate1 and Predicate2:

from("SourceURL")
    .choice()
        .when(Predicate1).to("Target1")
        .when(Predicate2).to("Target2")
        .otherwise().to("Target3");

Or equivalently in Spring XML:

<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <choice>
      <when>
        <!-- First predicate -->
        <simple>header.foo = 'bar'</simple>
        <to uri="Target1"/>
      </when>
      <when>
        <!-- Second predicate -->
        <simple>header.foo = 'manchu'</simple>
        <to uri="Target2"/>
      </when>
      <otherwise>
        <to uri="Target3"/>
      </otherwise>
    </choice>
  </route>
</camelContext>

In the Java DSL, there is a special case where you might need to use the endChoice() command. Some of the standard Apache Camel processors enable you to specify extra parameters using special sub-clauses, effectively opening an extra level of nesting which is usually terminated by the end() command. For example, you could specify a load balancer clause as loadBalance().roundRobin().to("mock:foo").to("mock:bar").end(), which load balances messages between the mock:foo and mock:bar endpoints. If the load balancer clause is embedded in a choice condition, however, it is necessary to terminate the clause using the endChoice() command, as follows:

from("direct:start")
    .choice()
        .when(bodyAs(String.class).contains("Camel"))
            .loadBalance().roundRobin().to("mock:foo").to("mock:bar").endChoice()
        .otherwise()
            .to("mock:result");

Filter

The filter() processor can be used to prevent uninteresting messages from reaching the producer endpoint. It takes a single predicate argument: if the predicate is true, the message exchange is allowed through to the producer; if the predicate is false, the message exchange is blocked. For example, the following filter blocks a message exchange, unless the incoming message contains a header, foo, with value equal to bar:

from("SourceURL").filter(header("foo").isEqualTo("bar")).to("TargetURL");

Or equivalently in Spring XML:

<camelContext id="filterRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <filter>
      <simple>header.foo = 'bar'</simple>
      <to uri="TargetURL"/>
    </filter>
  </route>
</camelContext>

Throttler

The throttle() processor ensures that a producer endpoint does not get overloaded. The throttler works by limiting the number of messages that can pass through per second. If the incoming messages exceed the specified rate, the throttler accumulates excess messages in a buffer and transmits them more slowly to the producer endpoint. For example, to limit the rate of throughput to 100 messages per second, you can define the following rule:

from("SourceURL").throttle(100).to("TargetURL");

Or equivalently in Spring XML:

<camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <throttle maximumRequestsPerPeriod="100" timePeriodMillis="1000">
      <to uri="TargetURL"/>
    </throttle>
  </route>
</camelContext>

Custom processor

If none of the standard processors described here provide the functionality you need, you can always define your own custom processor. To create a custom processor, define a class that implements the org.apache.camel.Processor interface and overrides the process() method. The following custom processor, MyProcessor, removes the header named foo from incoming messages:

Example 1.3. Implementing a Custom Processor Class

public class MyProcessor implements org.apache.camel.Processor {
public void process(org.apache.camel.Exchange exchange) {
  inMessage = exchange.getIn();
  if (inMessage != null) {
      inMessage.removeHeader("foo");
  }
}
};

To insert the custom processor into a router rule, invoke the process() method, which provides a generic mechanism for inserting processors into rules. For example, the following rule invokes the processor defined in Example 1.3, “Implementing a Custom Processor Class”:

org.apache.camel.Processor myProc = new MyProcessor();

from("SourceURL").process(myProc).to("TargetURL");

Chapter 2. Basic Principles of Route Building

Abstract

Apache Camel provides several processors and components that you can link together in a route. This chapter provides a basic orientation by explaining the principles of building a route using the provided building blocks.

2.1. Pipeline Processing

Overview

In Apache Camel, pipelining is the dominant paradigm for connecting nodes in a route definition. The pipeline concept is probably most familiar to users of the UNIX operating system, where it is used to join operating system commands. For example, ls | more is an example of a command that pipes a directory listing, ls, to the page-scrolling utility, more. The basic idea of a pipeline is that the output of one command is fed into the input of the next. The natural analogy in the case of a route is for the Out message from one processor to be copied to the In message of the next processor.

Processor nodes

Every node in a route, except for the initial endpoint, is a processor, in the sense that they inherit from the org.apache.camel.Processor interface. In other words, processors make up the basic building blocks of a DSL route. For example, DSL commands such as filter(), delayer(), setBody(), setHeader(), and to() all represent processors. When considering how processors connect together to build up a route, it is important to distinguish two different processing approaches.

The first approach is where the processor simply modifies the exchange’s In message, as shown in Figure 2.1, “Processor Modifying an In Message”. The exchange’s Out message remains null in this case.

Figure 2.1. Processor Modifying an In Message

Processor modifying an in message

The following route shows a setHeader() command that modifies the current In message by adding (or modifying) the BillingSystem heading:

from("activemq:orderQueue")
    .setHeader("BillingSystem", xpath("/order/billingSystem"))
    .to("activemq:billingQueue");

The second approach is where the processor creates an Out message to represent the result of the processing, as shown in Figure 2.2, “Processor Creating an Out Message”.

Figure 2.2. Processor Creating an Out Message

Processor creating an out message

The following route shows a transform() command that creates an Out message with a message body containing the string, DummyBody:

from("activemq:orderQueue")
    .transform(constant("DummyBody"))
    .to("activemq:billingQueue");

where constant("DummyBody") represents a constant expression. You cannot pass the string, DummyBody, directly, because the argument to transform() must be an expression type.

Pipeline for InOnly exchanges

Figure 2.3, “Sample Pipeline for InOnly Exchanges” shows an example of a processor pipeline for InOnly exchanges. Processor A acts by modifying the In message, while processors B and C create an Out message. The route builder links the processors together as shown. In particular, processors B and C are linked together in the form of a pipeline: that is, processor B’s Out message is moved to the In message before feeding the exchange into processor C, and processor C’s Out message is moved to the In message before feeding the exchange into the producer endpoint. Thus the processors' outputs and inputs are joined into a continuous pipeline, as shown in Figure 2.3, “Sample Pipeline for InOnly Exchanges”.

Figure 2.3. Sample Pipeline for InOnly Exchanges

sample pipeline for InOnly exchanges

Apache Camel employs the pipeline pattern by default, so you do not need to use any special syntax to create a pipeline in your routes. For example, the following route pulls messages from a userdataQueue queue, pipes the message through a Velocity template (to produce a customer address in text format), and then sends the resulting text address to the queue, envelopeAddresses:

from("activemq:userdataQueue")
    .to(ExchangePattern.InOut, "velocity:file:AdressTemplate.vm")
    .to("activemq:envelopeAddresses");

Where the Velocity endpoint, velocity:file:AddressTemplate.vm, specifies the location of a Velocity template file, file:AddressTemplate.vm, in the file system. The to() command changes the exchange pattern to InOut before sending the exchange to the Velocity endpoint and then changes it back to InOnly afterwards. For more details of the Velocity endpoint, see Velocity in the Apache Camel Component Reference Guide.

Pipeline for InOut exchanges

Figure 2.4, “Sample Pipeline for InOut Exchanges” shows an example of a processor pipeline for InOut exchanges, which you typically use to support remote procedure call (RPC) semantics. Processors A, B, and C are linked together in the form of a pipeline, with the output of each processor being fed into the input of the next. The final Out message produced by the producer endpoint is sent all the way back to the consumer endpoint, where it provides the reply to the original request.

Figure 2.4. Sample Pipeline for InOut Exchanges

Sample pipeline for InOut exchanges

Note that in order to support the InOut exchange pattern, it is essential that the last node in the route (whether it is a producer endpoint or some other kind of processor) creates an Out message. Otherwise, any client that connects to the consumer endpoint would hang and wait indefinitely for a reply message. You should be aware that not all producer endpoints create Out messages.

Consider the following route that processes payment requests, by processing incoming HTTP requests:

from("jetty:http://localhost:8080/foo")
    .to("cxf:bean:addAccountDetails")
    .to("cxf:bean:getCreditRating")
    .to("cxf:bean:processTransaction");

Where the incoming payment request is processed by passing it through a pipeline of Web services, cxf:bean:addAccountDetails, cxf:bean:getCreditRating, and cxf:bean:processTransaction. The final Web service, processTransaction, generates a response (Out message) that is sent back through the JETTY endpoint.

When the pipeline consists of just a sequence of endpoints, it is also possible to use the following alternative syntax:

from("jetty:http://localhost:8080/foo")
    .pipeline("cxf:bean:addAccountDetails", "cxf:bean:getCreditRating", "cxf:bean:processTransaction");

Pipeline for InOptionalOut exchanges

The pipeline for InOptionalOut exchanges is essentially the same as the pipeline in Figure 2.4, “Sample Pipeline for InOut Exchanges”. The difference between InOut and InOptionalOut is that an exchange with the InOptionalOut exchange pattern is allowed to have a null Out message as a reply. That is, in the case of an InOptionalOut exchange, a nullOut message is copied to the In message of the next node in the pipeline. By contrast, in the case of an InOut exchange, a nullOut message is discarded and the original In message from the current node would be copied to the In message of the next node instead.

2.2. Multiple Inputs

Overview

A standard route takes its input from just a single endpoint, using the from(EndpointURL) syntax in the Java DSL. But what if you need to define multiple inputs for your route? Apache Camel provides several alternatives for specifying multiple inputs to a route. The approach to take depends on whether you want the exchanges to be processed independently of each other or whether you want the exchanges from different inputs to be combined in some way (in which case, you should use the the section called “Content enricher pattern”).

Multiple independent inputs

The simplest way to specify multiple inputs is using the multi-argument form of the from() DSL command, for example:

from("URI1", "URI2", "URI3").to("DestinationUri");

Or you can use the following equivalent syntax:

from("URI1").from("URI2").from("URI3").to("DestinationUri");

In both of these examples, exchanges from each of the input endpoints, URI1, URI2, and URI3, are processed independently of each other and in separate threads. In fact, you can think of the preceding route as being equivalent to the following three separate routes:

from("URI1").to("DestinationUri");
from("URI2").to("DestinationUri");
from("URI3").to("DestinationUri");

Segmented routes

For example, you might want to merge incoming messages from two different messaging systems and process them using the same route. In most cases, you can deal with multiple inputs by dividing your route into segments, as shown in Figure 2.5, “Processing Multiple Inputs with Segmented Routes”.

Figure 2.5. Processing Multiple Inputs with Segmented Routes

Processing multiple inputs with segmented routes

The initial segments of the route take their inputs from some external queues — for example, activemq:Nyse and activemq:Nasdaq — and send the incoming exchanges to an internal endpoint, InternalUrl. The second route segment merges the incoming exchanges, taking them from the internal endpoint and sending them to the destination queue, activemq:USTxn. The InternalUrl is the URL for an endpoint that is intended only for use within a router application. The following types of endpoints are suitable for internal use:

The main purpose of these endpoints is to enable you to glue together different segments of a route. They all provide an effective way of merging multiple inputs into a single route.

Direct endpoints

The direct component provides the simplest mechanism for linking together routes. The event model for the direct component is synchronous, so that subsequent segments of the route run in the same thread as the first segment. The general format of a direct URL is direct:EndpointID, where the endpoint ID, EndpointID, is simply a unique alphanumeric string that identifies the endpoint instance.

For example, if you want to take the input from two message queues, activemq:Nyse and activemq:Nasdaq, and merge them into a single message queue, activemq:USTxn, you can do this by defining the following set of routes:

from("activemq:Nyse").to("direct:mergeTxns");
from("activemq:Nasdaq").to("direct:mergeTxns");

from("direct:mergeTxns").to("activemq:USTxn");

Where the first two routes take the input from the message queues, Nyse and Nasdaq, and send them to the endpoint, direct:mergeTxns. The last queue combines the inputs from the previous two queues and sends the combined message stream to the activemq:USTxn queue.

The implementation of the direct endpoint behaves as follows: whenever an exchange arrives at a producer endpoint (for example, to("direct:mergeTxns")), the direct endpoint passes the exchange directly to all of the consumers endpoints that have the same endpoint ID (for example, from("direct:mergeTxns")). Direct endpoints can only be used to communicate between routes that belong to the same CamelContext in the same Java virtual machine (JVM) instance.

SEDA endpoints

The SEDA component provides an alternative mechanism for linking together routes. You can use it in a similar way to the direct component, but it has a different underlying event and threading model, as follows:

  • Processing of a SEDA endpoint is not synchronous. That is, when you send an exchange to a SEDA producer endpoint, control immediately returns to the preceding processor in the route.
  • SEDA endpoints contain a queue buffer (of java.util.concurrent.BlockingQueue type), which stores all of the incoming exchanges prior to processing by the next route segment.
  • Each SEDA consumer endpoint creates a thread pool (the default size is 5) to process exchange objects from the blocking queue.
  • The SEDA component supports the competing consumers pattern, which guarantees that each incoming exchange is processed only once, even if there are multiple consumers attached to a specific endpoint.

One of the main advantages of using a SEDA endpoint is that the routes can be more responsive, owing to the built-in consumer thread pool. The stock transactions example can be re-written to use SEDA endpoints instead of direct endpoints, as follows:

from("activemq:Nyse").to("seda:mergeTxns");
from("activemq:Nasdaq").to("seda:mergeTxns");

from("seda:mergeTxns").to("activemq:USTxn");

The main difference between this example and the direct example is that when using SEDA, the second route segment (from seda:mergeTxns to activemq:USTxn) is processed by a pool of five threads.

Note

There is more to SEDA than simply pasting together route segments. The staged event-driven architecture (SEDA) encompasses a design philosophy for building more manageable multi-threaded applications. The purpose of the SEDA component in Apache Camel is simply to enable you to apply this design philosophy to your applications. For more details about SEDA, see http://www.eecs.harvard.edu/~mdw/proj/seda/.

VM endpoints

The VM component is very similar to the SEDA endpoint. The only difference is that, whereas the SEDA component is limited to linking together route segments from within the same CamelContext, the VM component enables you to link together routes from distinct Apache Camel applications, as long as they are running within the same Java virtual machine.

The stock transactions example can be re-written to use VM endpoints instead of SEDA endpoints, as follows:

from("activemq:Nyse").to("vm:mergeTxns");
from("activemq:Nasdaq").to("vm:mergeTxns");

And in a separate router application (running in the same Java VM), you can define the second segment of the route as follows:

from("vm:mergeTxns").to("activemq:USTxn");

Content enricher pattern

The content enricher pattern defines a fundamentally different way of dealing with multiple inputs to a route. When an exchange enters the enricher processor, the enricher contacts an external resource to retrieve information, which is then added to the original message. In this pattern, the external resource effectively represents a second input to the message.

For example, suppose you are writing an application that processes credit requests. Before processing a credit request, you need to augment it with the data that assigns a credit rating to the customer, where the ratings data is stored in a file in the directory, src/data/ratings. You can combine the incoming credit request with data from the ratings file using the pollEnrich() pattern and a GroupedExchangeAggregationStrategy aggregation strategy, as follows:

from("jms:queue:creditRequests")
    .pollEnrich("file:src/data/ratings?noop=true", new GroupedExchangeAggregationStrategy())
    .bean(new MergeCreditRequestAndRatings(), "merge")
    .to("jms:queue:reformattedRequests");

Where the GroupedExchangeAggregationStrategy class is a standard aggregation strategy from the org.apache.camel.processor.aggregate package that adds each new exchange to a java.util.List instance and stores the resulting list in the Exchange.GROUPED_EXCHANGE exchange property. In this case, the list contains two elements: the original exchange (from the creditRequests JMS queue); and the enricher exchange (from the file endpoint).

To access the grouped exchange, you can use code like the following:

public class MergeCreditRequestAndRatings {
    public void merge(Exchange ex) {
        // Obtain the grouped exchange
        List<Exchange> list = ex.getProperty(Exchange.GROUPED_EXCHANGE, List.class);

        // Get the exchanges from the grouped exchange
        Exchange originalEx = list.get(0);
        Exchange ratingsEx  = list.get(1);

        // Merge the exchanges
        ...
    }
}

An alternative approach to this application would be to put the merge code directly into the implementation of the custom aggregation strategy class.

For more details about the content enricher pattern, see Section 10.1, “Content Enricher”.

2.3. Exception Handling

Abstract

Apache Camel provides several different mechanisms, which let you handle exceptions at different levels of granularity: you can handle exceptions within a route using doTry, doCatch, and doFinally; or you can specify what action to take for each exception type and apply this rule to all routes in a RouteBuilder using onException; or you can specify what action to take for all exception types and apply this rule to all routes in a RouteBuilder using errorHandler.

For more details about exception handling, see Section 6.3, “Dead Letter Channel”.

2.3.1. onException Clause

Overview

The onException clause is a powerful mechanism for trapping exceptions that occur in one or more routes: it is type-specific, enabling you to define distinct actions to handle different exception types; it allows you to define actions using essentially the same (actually, slightly extended) syntax as a route, giving you considerable flexibility in the way you handle exceptions; and it is based on a trapping model, which enables a single onException clause to deal with exceptions occurring at any node in any route.

Trapping exceptions using onException

The onException clause is a mechanism for trapping, rather than catching exceptions. That is, once you define an onException clause, it traps exceptions that occur at any point in a route. This contrasts with the Java try/catch mechanism, where an exception is caught, only if a particular code fragment is explicitly enclosed in a try block.

What really happens when you define an onException clause is that the Apache Camel runtime implicitly encloses each route node in a try block. This is why the onException clause is able to trap exceptions at any point in the route. But this wrapping is done for you automatically; it is not visible in the route definitions.

Java DSL example

In the following Java DSL example, the onException clause applies to all of the routes defined in the RouteBuilder class. If a ValidationException exception occurs while processing either of the routes (from("seda:inputA") or from("seda:inputB")), the onException clause traps the exception and redirects the current exchange to the validationFailed JMS queue (which serves as a deadletter queue).

// Java
public class MyRouteBuilder extends RouteBuilder {

  public void configure() {
    onException(ValidationException.class)
      .to("activemq:validationFailed");

    from("seda:inputA")
      .to("validation:foo/bar.xsd", "activemq:someQueue");

    from("seda:inputB").to("direct:foo")
      .to("rnc:mySchema.rnc", "activemq:anotherQueue");
  }
}

XML DSL example

The preceding example can also be expressed in the XML DSL, using the onException element to define the exception clause, as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:camel="http://camel.apache.org/schema/spring"
    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-2.0.xsd
    http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <onException>
            <exception>com.mycompany.ValidationException</exception>
            <to uri="activemq:validationFailed"/>
        </onException>
        <route>
            <from uri="seda:inputA"/>
            <to uri="validation:foo/bar.xsd"/>
            <to uri="activemq:someQueue"/>
        </route>
        <route>
            <from uri="seda:inputB"/>
            <to uri="rnc:mySchema.rnc"/>
            <to uri="activemq:anotherQueue"/>
        </route>
    </camelContext>

</beans>

Trapping multiple exceptions

You can define multiple onException clauses to trap exceptions in a RouteBuilder scope. This enables you to take different actions in response to different exceptions. For example, the following series of onException clauses defined in the Java DSL define different deadletter destinations for ValidationException, IOException, and Exception:

onException(ValidationException.class).to("activemq:validationFailed");
onException(java.io.IOException.class).to("activemq:ioExceptions");
onException(Exception.class).to("activemq:exceptions");

You can define the same series of onException clauses in the XML DSL as follows:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
<onException>
    <exception>java.io.IOException</exception>
    <to uri="activemq:ioExceptions"/>
</onException>
<onException>
    <exception>java.lang.Exception</exception>
    <to uri="activemq:exceptions"/>
</onException>

You can also group multiple exceptions together to be trapped by the same onException clause. In the Java DSL, you can group multiple exceptions as follows:

onException(ValidationException.class, BuesinessException.class)
  .to("activemq:validationFailed");

In the XML DSL, you can group multiple exceptions together by defining more than one exception element inside the onException element, as follows:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <exception>com.mycompany.BuesinessException</exception>
    <to uri="activemq:validationFailed"/>
</onException>

When trapping multiple exceptions, the order of the onException clauses is significant. Apache Camel initially attempts to match the thrown exception against the first clause. If the first clause fails to match, the next onException clause is tried, and so on until a match is found. Each matching attempt is governed by the following algorithm:

  1. If the thrown exception is a chained exception (that is, where an exception has been caught and rethrown as a different exception), the most nested exception type serves initially as the basis for matching. This exception is tested as follows:

    1. If the exception-to-test has exactly the type specified in the onException clause (tested using instanceof), a match is triggered.
    2. If the exception-to-test is a sub-type of the type specified in the onException clause, a match is triggered.
  2. If the most nested exception fails to yield a match, the next exception in the chain (the wrapping exception) is tested instead. The testing continues up the chain until either a match is triggered or the chain is exhausted.
Note

The throwException EIP enables you to create a new exception instance from a simple language expression. You can make it dynamic, based on the available information from the current exchange. for example,

<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>

Deadletter channel

The basic examples of onException usage have so far all exploited the deadletter channel pattern. That is, when an onException clause traps an exception, the current exchange is routed to a special destination (the deadletter channel). The deadletter channel serves as a holding area for failed messages that have not been processed. An administrator can inspect the messages at a later time and decide what action needs to be taken.

For more details about the deadletter channel pattern, see Section 6.3, “Dead Letter Channel”.

Use original message

By the time an exception is raised in the middle of a route, the message in the exchange could have been modified considerably (and might not even by readable by a human). Often, it is easier for an administrator to decide what corrective actions to take, if the messages visible in the deadletter queue are the original messages, as received at the start of the route. The useOriginalMessage option is false by default, but will be auto-enabled if it is configured on an error handler.

Note

The useOriginalMessage option can result in unexpected behavior when applied to Camel routes that send messages to multiple endpoints, or split messages into parts. The original message might not be preserved in a Multicast, Splitter, or RecipientList route in which intermediate processing steps modify the original message.

In the Java DSL, you can replace the message in the exchange by the original message. Set the setAllowUseOriginalMessage() to true, then use the useOriginalMessage() DSL command, as follows:

onException(ValidationException.class)
  .useOriginalMessage()
  .to("activemq:validationFailed");

In the XML DSL, you can retrieve the original message by setting the useOriginalMessage attribute on the onException element, as follows:

<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
Note

If the setAllowUseOriginalMessage() option is set to true, Camel makes a copy of the original message at the start of the route, which ensures that the original message is available when you call useOriginalMessage(). However, if the setAllowUseOriginalMessage() option is set to false (this is the default) on the Camel context, the original message will not be accessible and you cannot call useOriginalMessage().

A reasons to exploit the default behaviour is to optimize performance when processing large messages.

In Camel versions prior to 2.18, the default setting of allowUseOriginalMessage is true.

Redelivery policy

Instead of interrupting the processing of a message and giving up as soon as an exception is raised, Apache Camel gives you the option of attempting to redeliver the message at the point where the exception occurred. In networked systems, where timeouts can occur and temporary faults arise, it is often possible for failed messages to be processed successfully, if they are redelivered shortly after the original exception was raised.

The Apache Camel redelivery supports various strategies for redelivering messages after an exception occurs. Some of the most important options for configuring redelivery are as follows:

maximumRedeliveries()
Specifies the maximum number of times redelivery can be attempted (default is 0). A negative value means redelivery is always attempted (equivalent to an infinite value).
retryWhile()

Specifies a predicate (of Predicate type), which determines whether Apache Camel ought to continue redelivering. If the predicate evaluates to true on the current exchange, redelivery is attempted; otherwise, redelivery is stopped and no further redelivery attempts are made.

This option takes precedence over the maximumRedeliveries() option.

In the Java DSL, redelivery policy options are specified using DSL commands in the onException clause. For example, you can specify a maximum of six redeliveries, after which the exchange is sent to the validationFailed deadletter queue, as follows:

onException(ValidationException.class)
  .maximumRedeliveries(6)
  .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN)
  .to("activemq:validationFailed");

In the XML DSL, redelivery policy options are specified by setting attributes on the redeliveryPolicy element. For example, the preceding route can be expressed in XML DSL as follows:

<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <redeliveryPolicy maximumRedeliveries="6"/>
    <to uri="activemq:validationFailed"/>
</onException>

The latter part of the route — after the redelivery options are set — is not processed until after the last redelivery attempt has failed. For detailed descriptions of all the redelivery options, see Section 6.3, “Dead Letter Channel”.

Alternatively, you can specify redelivery policy options in a redeliveryPolicyProfile instance. You can then reference the redeliveryPolicyProfile instance using the onException element’s redeliverPolicyRef attribute. For example, the preceding route can be expressed as follows:

<redeliveryPolicyProfile id="redelivPolicy" maximumRedeliveries="6" retryAttemptedLogLevel="WARN"/>

<onException useOriginalMessage="true" redeliveryPolicyRef="redelivPolicy">
    <exception>com.mycompany.ValidationException</exception>
    <to uri="activemq:validationFailed"/>
</onException>
Note

The approach using redeliveryPolicyProfile is useful, if you want to re-use the same redelivery policy in multiple onException clauses.

Conditional trapping

Exception trapping with onException can be made conditional by specifying the onWhen option. If you specify the onWhen option in an onException clause, a match is triggered only when the thrown exception matches the clause and the onWhen predicate evaluates to true on the current exchange.

For example, in the following Java DSL fragment,the first onException clause triggers, only if the thrown exception matches MyUserException and the user header is non-null in the current exchange:

// Java

// Here we define onException() to catch MyUserException when
// there is a header[user] on the exchange that is not null
onException(MyUserException.class)
    .onWhen(header("user").isNotNull())
    .maximumRedeliveries(2)
    .to(ERROR_USER_QUEUE);

// Here we define onException to catch MyUserException as a kind
// of fallback when the above did not match.
// Noitce: The order how we have defined these onException is
// important as Camel will resolve in the same order as they
// have been defined
onException(MyUserException.class)
    .maximumRedeliveries(2)
    .to(ERROR_QUEUE);

The preceding onException clauses can be expressed in the XML DSL as follows:

<redeliveryPolicyProfile id="twoRedeliveries" maximumRedeliveries="2"/>

<onException redeliveryPolicyRef="twoRedeliveries">
    <exception>com.mycompany.MyUserException</exception>
    <onWhen>
        <simple>${header.user} != null</simple>
    </onWhen>
    <to uri="activemq:error_user_queue"/>
</onException>

<onException redeliveryPolicyRef="twoRedeliveries">
    <exception>com.mycompany.MyUserException</exception>
    <to uri="activemq:error_queue"/>
</onException>

Handling exceptions

By default, when an exception is raised in the middle of a route, processing of the current exchange is interrupted and the thrown exception is propagated back to the consumer endpoint at the start of the route. When an onException clause is triggered, the behavior is essentially the same, except that the onException clause performs some processing before the thrown exception is propagated back.

But this default behavior is not the only way to handle an exception. The onException provides various options to modify the exception handling behavior, as follows:

  • Suppressing exception rethrow — you have the option of suppressing the rethrown exception after the onException clause has completed. In other words, in this case the exception does not propagate back to the consumer endpoint at the start of the route.
  • Continuing processing — you have the option of resuming normal processing of the exchange from the point where the exception originally occurred. Implicitly, this approach also suppresses the rethrown exception.
  • Sending a response — in the special case where the consumer endpoint at the start of the route expects a reply (that is, having an InOut MEP), you might prefer to construct a custom fault reply message, rather than propagating the exception back to the consumer endpoint.
Note

Using a custom processor, the Camel Exception Clause and Error Handler get invoked, soon after it throws an exception using the new onExceptionOccurred option.

Suppressing exception rethrow

To prevent the current exception from being rethrown and propagated back to the consumer endpoint, you can set the handled() option to true in the Java DSL, as follows:

onException(ValidationException.class)
  .handled(true)
  .to("activemq:validationFailed");

In the Java DSL, the argument to the handled() option can be of boolean type, of Predicate type, or of Expression type (where any non-boolean expression is interpreted as true, if it evaluates to a non-null value).

The same route can be configured to suppress the rethrown exception in the XML DSL, using the handled element, as follows:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <to uri="activemq:validationFailed"/>
</onException>

Continuing processing

To continue processing the current message from the point in the route where the exception was originally thrown, you can set the continued option to true in the Java DSL, as follows:

onException(ValidationException.class)
  .continued(true);

In the Java DSL, the argument to the continued() option can be of boolean type, of Predicate type, or of Expression type (where any non-boolean expression is interpreted as true, if it evaluates to a non-null value).

The same route can be configured in the XML DSL, using the continued element, as follows:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <continued>
        <constant>true</constant>
    </continued>
</onException>

Sending a response

When the consumer endpoint that starts a route expects a reply, you might prefer to construct a custom fault reply message, instead of simply letting the thrown exception propagate back to the consumer. There are two essential steps you need to follow in this case: suppress the rethrown exception using the handled option; and populate the exchange’s Out message slot with a custom fault message.

For example, the following Java DSL fragment shows how to send a reply message containing the text string, Sorry, whenever the MyFunctionalException exception occurs:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body as Sorry.
onException(MyFunctionalException.class)
    .handled(true)
    .transform().constant("Sorry");

If you are sending a fault response to the client, you will often want to incorporate the text of the exception message in the response. You can access the text of the current exception message using the exceptionMessage() builder method. For example, you can send a reply containing just the text of the exception message whenever the MyFunctionalException exception occurs, as follows:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body and return the exception message
onException(MyFunctionalException.class)
    .handled(true)
    .transform(exceptionMessage());

The exception message text is also accessible from the Simple language, through the exception.message variable. For example, you could embed the current exception text in a reply message, as follows:

// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client)
// but we want to return a fixed text response, so we transform OUT body and return a nice message
// using the simple language where we want insert the exception message
onException(MyFunctionalException.class)
    .handled(true)
    .transform().simple("Error reported: ${exception.message} - cannot process this message.");

The preceding onException clause can be expressed in XML DSL as follows:

<onException>
    <exception>com.mycompany.MyFunctionalException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <transform>
        <simple>Error reported: ${exception.message} - cannot process this message.</simple>
    </transform>
</onException>

Exception thrown while handling an exception

An exception that gets thrown while handling an existing exception (in other words, one that gets thrown in the middle of processing an onException clause) is handled in a special way. Such an exception is handled by the special fallback exception handler, which handles the exception as follows:

  • All existing exception handlers are ignored and processing fails immediately.
  • The new exception is logged.
  • The new exception is set on the exchange object.

The simple strategy avoids complex failure scenarios which could otherwise end up with an onException clause getting locked into an infinite loop.

Scopes

The onException clauses can be effective in either of the following scopes:

  • RouteBuilder scope — onException clauses defined as standalone statements inside a RouteBuilder.configure() method affect all of the routes defined in that RouteBuilder instance. On the other hand, these onException clauses have no effect whatsoever on routes defined inside any other RouteBuilder instance. The onException clauses must appear before the route definitions.

    All of the examples up to this point are defined using the RouteBuilder scope.

  • Route scope — onException clauses can also be embedded directly within a route. These onException clauses affect only the route in which they are defined.

Route scope

You can embed an onException clause anywhere inside a route definition, but you must terminate the embedded onException clause using the end() DSL command.

For example, you can define an embedded onException clause in the Java DSL, as follows:

// Java
from("direct:start")
  .onException(OrderFailedException.class)
    .maximumRedeliveries(1)
    .handled(true)
    .beanRef("orderService", "orderFailed")
    .to("mock:error")
  .end()
  .beanRef("orderService", "handleOrder")
  .to("mock:result");

You can define an embedded onException clause in the XML DSL, as follows:

<route errorHandlerRef="deadLetter">
    <from uri="direct:start"/>
    <onException>
        <exception>com.mycompany.OrderFailedException</exception>
        <redeliveryPolicy maximumRedeliveries="1"/>
        <handled>
            <constant>true</constant>
        </handled>
        <bean ref="orderService" method="orderFailed"/>
        <to uri="mock:error"/>
    </onException>
    <bean ref="orderService" method="handleOrder"/>
    <to uri="mock:result"/>
</route>

2.3.2. Error Handler

Overview

The errorHandler() clause provides similar features to the onException clause, except that this mechanism is not able to discriminate between different exception types. The errorHandler() clause is the original exception handling mechanism provided by Apache Camel and was available before the onException clause was implemented.

Java DSL example

The errorHandler() clause is defined in a RouteBuilder class and applies to all of the routes in that RouteBuilder class. It is triggered whenever an exception of any kind occurs in one of the applicable routes. For example, to define an error handler that routes all failed exchanges to the ActiveMQ deadLetter queue, you can define a RouteBuilder as follows:

public class MyRouteBuilder extends RouteBuilder {

    public void configure() {
        errorHandler(deadLetterChannel("activemq:deadLetter"));

        // The preceding error handler applies
        // to all of the following routes:
        from("activemq:orderQueue")
          .to("pop3://fulfillment@acme.com");
        from("file:src/data?noop=true")
          .to("file:target/messages");
        // ...
    }
}

Redirection to the dead letter channel will not occur, however, until all attempts at redelivery have been exhausted.

XML DSL example

In the XML DSL, you define an error handler within a camelContext scope using the errorHandler element. For example, to define an error handler that routes all failed exchanges to the ActiveMQ deadLetter queue, you can define an errorHandler element as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:camel="http://camel.apache.org/schema/spring"
    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-2.0.xsd
    http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <errorHandler type="DeadLetterChannel"
                      deadLetterUri="activemq:deadLetter"/>
        <route>
            <from uri="activemq:orderQueue"/>
            <to uri="pop3://fulfillment@acme.com"/>
        </route>
        <route>
            <from uri="file:src/data?noop=true"/>
            <to uri="file:target/messages"/>
        </route>
    </camelContext>

</beans>

Types of error handler

Table 2.1, “Error Handler Types” provides an overview of the different types of error handler you can define.

Table 2.1. Error Handler Types

Java DSL BuilderXML DSL Type AttributeDescription

defaultErrorHandler()

DefaultErrorHandler

Propagates exceptions back to the caller and supports the redelivery policy, but it does not support a dead letter queue.

deadLetterChannel()

DeadLetterChannel

Supports the same features as the default error handler and, in addition, supports a dead letter queue.

loggingErrorChannel()

LoggingErrorChannel

Logs the exception text whenever an exception occurs.

noErrorHandler()

NoErrorHandler

Dummy handler implementation that can be used to disable the error handler.

 

TransactionErrorHandler

An error handler for transacted routes. A default transaction error handler instance is automatically used for a route that is marked as transacted.

2.3.3. doTry, doCatch, and doFinally

Overview

To handle exceptions within a route, you can use a combination of the doTry, doCatch, and doFinally clauses, which handle exceptions in a similar way to Java’s try, catch, and finally blocks.

Similarities between doCatch and Java catch

In general, the doCatch() clause in a route definition behaves in an analogous way to the catch() statement in Java code. In particular, the following features are supported by the doCatch() clause:

  • Multiple doCatch clauses — you can have multiple doCatch clauses within a single doTry block. The doCatch clauses are tested in the order they appear, just like Java catch() statements. Apache Camel executes the first doCatch clause that matches the thrown exception.

    Note

    This algorithm is different from the exception matching algorithm used by the onException clause — see Section 2.3.1, “onException Clause” for details.

  • Rethrowing exceptions — you can rethrow the current exception from within a doCatch clause using the handled sub-clause (see the section called “Rethrowing exceptions in doCatch”).

Special features of doCatch

There are some special features of the doCatch() clause, however, that have no analogue in the Java catch() statement. The following features are specific to doCatch():

Example

The following example shows how to write a doTry block in the Java DSL, where the doCatch() clause will be executed, if either the IOException exception or the IllegalStateException exception are raised, and the doFinally() clause is always executed, irrespective of whether an exception is raised or not.

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class, IllegalStateException.class)
        .to("mock:catch")
    .doFinally()
        .to("mock:finally")
    .end();

Or equivalently, in Spring XML:

<route>
    <from uri="direct:start"/>
    <!-- here the try starts. its a try .. catch .. finally just as regular java code -->
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <!-- catch multiple exceptions -->
            <exception>java.io.IOException</exception>
            <exception>java.lang.IllegalStateException</exception>
            <to uri="mock:catch"/>
        </doCatch>
        <doFinally>
            <to uri="mock:finally"/>
        </doFinally>
    </doTry>
</route>

Rethrowing exceptions in doCatch

It is possible to rethrow an exception in a doCatch() clause by calling the handled() sub-clause with its argument set to false, as follows:

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class)
        // mark this as NOT handled, eg the caller will also get the exception
        .handled(false)
        .to("mock:io")
    .doCatch(Exception.class)
        // and catch all other exceptions
        .to("mock:error")
    .end();

In the preceding example, if the IOException is caught by doCatch(), the current exchange is sent to the mock:io endpoint, and then the IOException is rethrown. This gives the consumer endpoint at the start of the route (in the from() command) an opportunity to handle the exception as well.

The following example shows how to define the same route in Spring XML:

<route>
    <from uri="direct:start"/>
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <exception>java.io.IOException</exception>
            <!-- mark this as NOT handled, eg the caller will also get the exception -->
            <handled>
                <constant>false</constant>
            </handled>
            <to uri="mock:io"/>
        </doCatch>
        <doCatch>
            <!-- and catch all other exceptions they are handled by default (ie handled = true) -->
            <exception>java.lang.Exception</exception>
            <to uri="mock:error"/>
        </doCatch>
    </doTry>
</route>

Conditional exception catching using onWhen

A special feature of the Apache Camel doCatch() clause is that you can conditionalize the catching of exceptions based on an expression that is evaluated at run time. In other words, if you catch an exception using a clause of the form, doCatch(ExceptionList).doWhen(Expression), an exception will only be caught, if the predicate expression, Expression, evaluates to true at run time.

For example, the following doTry block will catch the exceptions, IOException and IllegalStateException, only if the exception message contains the word, Severe:

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class, IllegalStateException.class)
        .onWhen(exceptionMessage().contains("Severe"))
        .to("mock:catch")
    .doCatch(CamelExchangeException.class)
        .to("mock:catchCamel")
    .doFinally()
        .to("mock:finally")
    .end();

Or equivalently, in Spring XML:

<route>
    <from uri="direct:start"/>
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <exception>java.io.IOException</exception>
            <exception>java.lang.IllegalStateException</exception>
            <onWhen>
                <simple>${exception.message} contains 'Severe'</simple>
            </onWhen>
            <to uri="mock:catch"/>
        </doCatch>
        <doCatch>
            <exception>org.apache.camel.CamelExchangeException</exception>
            <to uri="mock:catchCamel"/>
        </doCatch>
        <doFinally>
            <to uri="mock:finally"/>
        </doFinally>
    </doTry>
</route>

Nested Conditions in doTry

There are various options available to add Camel exception handling to a JavaDSL route. dotry() creates a try or catch block for handling exceptions and is useful for route specific error handling.

If you want to catch the exception inside of ChoiceDefinition, you can use the following doTry blocks:

from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut)
           .doTry()
              .to("https4://wayne-token-service")
              .choice()
                  .when().simple("${header.CamelHttpResponseCode} == '200'")
                     .convertBodyTo(String.class)
.setHeader("wayne-token").groovy("body.replaceAll('\"','')")
                     .log(">> Wayne Token : ${header.wayne-token}")
                .endChoice()

doCatch(java.lang.Class (java.lang.Exception>)
              .log(">> Exception")
           .endDoTry();

from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut)
           .doTry()
              .to("https4://wayne-token-service")
           .doCatch(Exception.class)
              .log(">> Exception")
           .endDoTry();

2.3.4. Propagating SOAP Exceptions

Overview

The Camel CXF component provides an integration with Apache CXF, enabling you to send and receive SOAP messages from Apache Camel endpoints. You can easily define Apache Camel endpoints in XML, which can then be referenced in a route using the endpoint’s bean ID. For more details, see CXF in the Apache Camel Component Reference Guide.

How to propagate stack trace information

It is possible to configure a CXF endpoint so that, when a Java exception is thrown on the server side, the stack trace for the exception is marshalled into a fault message and returned to the client. To enable this feaure, set the dataFormat to PAYLOAD and set the faultStackTraceEnabled property to true in the cxfEndpoint element, as follows:

<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
    wsdlURL="ship.wsdl"
    endpointName="s:TestSoapEndpoint"
    serviceName="s:TestService"
    xmlns:s="http://test">
  <cxf:properties>
    <!-- enable sending the stack trace back to client; the default value is false-->
    <entry key="faultStackTraceEnabled" value="true" />
    <entry key="dataFormat" value="PAYLOAD" />
  </cxf:properties>
</cxf:cxfEndpoint>

For security reasons, the stack trace does not include the causing exception (that is, the part of a stack trace that follows Caused by). If you want to include the causing exception in the stack trace, set the exceptionMessageCauseEnabled property to true in the cxfEndpoint element, as follows:

<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
    wsdlURL="ship.wsdl"
    endpointName="s:TestSoapEndpoint"
    serviceName="s:TestService"
    xmlns:s="http://test">
  <cxf:properties>
    <!-- enable to show the cause exception message and the default value is false -->
    <entry key="exceptionMessageCauseEnabled" value="true" />
    <!-- enable to send the stack trace back to client,  the default value is false-->
    <entry key="faultStackTraceEnabled" value="true" />
    <entry key="dataFormat" value="PAYLOAD" />
  </cxf:properties>
</cxf:cxfEndpoint>
Warning

You should only enable the exceptionMessageCauseEnabled flag for testing and diagnostic purposes. It is normal practice for servers to conceal the original cause of an exception to make it harder for hostile users to probe the server.

2.4. Bean Integration

Overview

Bean integration provides a general purpose mechanism for processing messages using arbitrary Java objects. By inserting a bean reference into a route, you can call an arbitrary method on a Java object, which can then access and modify the incoming exchange. The mechanism that maps an exchange’s contents to the parameters and return values of a bean method is known as parameter binding. Parameter binding can use any combination of the following approaches in order to initialize a method’s parameters:

  • Conventional method signatures  —  If the method signature conforms to certain conventions, the parameter binding can use Java reflection to determine what parameters to pass.
  • Annotations and dependency injection  —  For a more flexible binding mechanism, employ Java annotations to specify what to inject into the method’s arguments. This dependency injection mechanism relies on Spring 2.5 component scanning. Normally, if you are deploying your Apache Camel application into a Spring container, the dependency injection mechanism will work automatically.
  • Explicitly specified parameters  —  You can specify parameters explicitly (either as constants or using the Simple language), at the point where the bean is invoked.

Bean registry

Beans are made accessible through a bean registry, which is a service that enables you to look up beans using either the class name or the bean ID as a key. The way that you create an entry in the bean registry depends on the underlying framework — for example, plain Java, Spring, Guice, or Blueprint. Registry entries are usually created implicitly (for example, when you instantiate a Spring bean in a Spring XML file).

Registry plug-in strategy

Apache Camel implements a plug-in strategy for the bean registry, defining an integration layer for accessing beans which makes the underlying registry implementation transparent. Hence, it is possible to integrate Apache Camel applications with a variety of different bean registries, as shown in Table 2.2, “Registry Plug-Ins”.

Table 2.2. Registry Plug-Ins

Registry ImplementationCamel Component with Registry Plug-In

Spring bean registry

camel-spring

Guice bean registry

camel-guice

Blueprint bean registry

camel-blueprint

OSGi service registry

deployed in OSGi container

JNDI registry

 

Normally, you do not have to worry about configuring bean registries, because the relevant bean registry is automatically installed for you. For example, if you are using the Spring framework to define your routes, the Spring ApplicationContextRegistry plug-in is automatically installed in the current CamelContext instance.

Deployment in an OSGi container is a special case. When an Apache Camel route is deployed into the OSGi container, the CamelContext automatically sets up a registry chain for resolving bean instances: the registry chain consists of the OSGi registry, followed by the Blueprint (or Spring) registry.

Accessing a bean created in Java

To process exchange objects using a Java bean (which is a plain old Java object or POJO), use the bean() processor, which binds the inbound exchange to a method on the Java object. For example, to process inbound exchanges using the class, MyBeanProcessor, define a route like the following:

from("file:data/inbound")
    .bean(MyBeanProcessor.class, "processBody")
    .to("file:data/outbound");

Where the bean() processor creates an instance of MyBeanProcessor type and invokes the processBody() method to process inbound exchanges. This approach is adequate if you only want to access the MyBeanProcessor instance from a single route. However, if you want to access the same MyBeanProcessor instance from multiple routes, use the variant of bean() that takes the Object type as its first argument. For example:

MyBeanProcessor myBean = new MyBeanProcessor();

from("file:data/inbound")
    .bean(myBean, "processBody")
    .to("file:data/outbound");
from("activemq:inboundData")
    .bean(myBean, "processBody")
    .to("activemq:outboundData");

Accessing overloaded bean methods

If a bean defines overloaded methods, you can choose which of the overloaded methods to invoke by specifying the method name along with its parameter types. For example, if the MyBeanBrocessor class has two overloaded methods, processBody(String) and processBody(String,String), you can invoke the latter overloaded method as follows:

from("file:data/inbound")
  .bean(MyBeanProcessor.class, "processBody(String,String)")
  .to("file:data/outbound");

Alternatively, if you want to identify a method by the number of parameters it takes, rather than specifying the type of each parameter explicitly, you can use the wildcard character, \*. For example, to invoke a method named processBody that takes two parameters, irrespective of the exact type of the parameters, invoke the bean() processor as follows:

from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody(*,*)")
.to("file:data/outbound");

When specifying the method, you can use either a simple unqualified type name—for example, processBody(Exchange)—or a fully qualified type name—for example, processBody(org.apache.camel.Exchange).

Note

In the current implementation, the specified type name must be an exact match of the parameter type. Type inheritance is not taken into account.

Specify parameters explicitly

You can specify parameter values explicitly, when you call the bean method. The following simple type values can be passed:

  • Boolean: true or false.
  • Numeric: 123, 7, and so on.
  • String: 'In single quotes' or "In double quotes".
  • Null object: null.

The following example shows how you can mix explicit parameter values with type specifiers in the same method invocation:

from("file:data/inbound")
  .bean(MyBeanProcessor.class, "processBody(String, 'Sample string value', true, 7)")
  .to("file:data/outbound");

In the preceding example, the value of the first parameter would presumably be determined by a parameter binding annotation (see the section called “Basic annotations”).

In addition to the simple type values, you can also specify parameter values using the Simple language (Chapter 30, The Simple Language). This means that the full power of the Simple language is available when specifying parameter values. For example, to pass the message body and the value of the title header to a bean method:

from("file:data/inbound")
  .bean(MyBeanProcessor.class, "processBodyAndHeader(${body},${header.title})")
  .to("file:data/outbound");

You can also pass the entire header hash map as a parameter. For example, in the following example, the second method parameter must be declared to be of type java.util.Map:

from("file:data/inbound")
  .bean(MyBeanProcessor.class, "processBodyAndAllHeaders(${body},${header})")
  .to("file:data/outbound");
Note

From Apache Camel 2.19 release, returning null from a bean method call now always ensures the message body has been set as a null value.

Basic method signatures

To bind exchanges to a bean method, you can define a method signature that conforms to certain conventions. In particular, there are two basic conventions for method signatures:

Method signature for processing message bodies

If you want to implement a bean method that accesses or modifies the incoming message body, you must define a method signature that takes a single String argument and returns a String value. For example:

// Java
package com.acme;

public class MyBeanProcessor {
    public String processBody(String body) {
        // Do whatever you like to 'body'...
        return newBody;
    }
}

Method signature for processing exchanges

For greater flexibility, you can implement a bean method that accesses the incoming exchange. This enables you to access or modify all headers, bodies, and exchange properties. For processing exchanges, the method signature takes a single org.apache.camel.Exchange parameter and returns void. For example:

// Java
package com.acme;

public class MyBeanProcessor {
    public void processExchange(Exchange exchange) {
        // Do whatever you like to 'exchange'...
        exchange.getIn().setBody("Here is a new message body!");
    }
}

Accessing a Spring bean from Spring XML

Instead of creating a bean instance in Java, you can create an instance using Spring XML. In fact, this is the only feasible approach if you are defining your routes in XML. To define a bean in XML, use the standard Spring bean element. The following example shows how to create an instance of MyBeanProcessor:

<beans ...>
    ...
    <bean id="myBeanId" class="com.acme.MyBeanProcessor"/>
</beans>

It is also possible to pass data to the bean’s constructor arguments using Spring syntax. For full details of how to use the Spring bean element, see The IoC Container from the Spring reference guide.

Where the beanRef() processor invokes the MyBeanProcessor.processBody() method on the specified bean instance. You can also invoke the bean from within a Spring XML route, using the Camel schema’s bean element. For example:

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="file:data/inbound"/>
    <bean ref="myBeanId" method="processBody"/>
    <to uri="file:data/outbound"/>
  </route>
</camelContext>

For a slight efficiency gain, you can set the cache option to true, which avoids looking up the registry every time a bean is used. For example, to enable caching, you can set the cache attribute on the bean element as follows:

<bean ref="myBeanId" method="processBody" cache="true"/>

Accessing a Spring bean from Java

When you create an object instance using the Spring bean element, you can reference it from Java using the bean’s ID (the value of the bean element’s id attribute). For example, given the bean element with ID equal to myBeanId, you can reference the bean in a Java DSL route using the beanRef() processor, as follows:

from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");

Alternatively, you can reference the Spring bean by injection, using the @BeanInject annotation as follows:

// Java
import org.apache.camel.@BeanInject;
...
public class MyRouteBuilder extends RouteBuilder {

   @BeanInject("myBeanId")
   com.acme.MyBeanProcessor bean;

   public void configure() throws Exception {
     ..
   }
}

If you omit the bean ID from the @BeanInject annotation, Camel looks up the registry by type, but this only works if there is just a single bean of the given type. For example, to look up and inject the bean of com.acme.MyBeanProcessor type:

@BeanInject
com.acme.MyBeanProcessor bean;

Bean shutdown order in Spring XML

For the beans used by a Camel context, the correct shutdown order is usually:

  1. Shut down the camelContext instance, followed by;
  2. Shut down the used beans.

If this shutdown order is reversed, then it could happen that the Camel context tries to access a bean that is already destroyed (either leading directly to an error; or the Camel context tries to create the missing bean while it is being destroyed, which also causes an error). The default shutdown order in Spring XML depends on the order in which the beans and the camelContext appear in the Spring XML file. In order to avoid random errors due to incorrect shutdown order, therefore, the camelContext is configured to shut down before any of the other beans in the Spring XML file. This is the default behaviour since Apache Camel 2.13.0.

If you need to change this behaviour (so that the Camel context is not forced to shut down before the other beans), you can set the shutdownEager attribute on the camelContext element to false. In this case, you could potentially exercise more fine-grained control over shutdown order using the Spring depends-on attribute.

Parameter binding annotations

The basic parameter bindings described in the section called “Basic method signatures” might not always be convenient to use. For example, if you have a legacy Java class that performs some data manipulation, you might want to extract data from an inbound exchange and map it to the arguments of an existing method signature. For this kind of parameter binding, Apache Camel provides the following kinds of Java annotation:

Basic annotations

Table 2.3, “Basic Bean Annotations” shows the annotations from the org.apache.camel Java package that you can use to inject message data into the arguments of a bean method.

Table 2.3. Basic Bean Annotations

AnnotationMeaningParameter?

@Attachments

Binds to a list of attachments.

 

@Body

Binds to an inbound message body.

 

@Header

Binds to an inbound message header.

String name of the header.

@Headers

Binds to a java.util.Map of the inbound message headers.

 

@OutHeaders

Binds to a java.util.Map of the outbound message headers.

 

@Property

Binds to a named exchange property.

String name of the property.

@Properties

Binds to a java.util.Map of the exchange properties.

 

For example, the following class shows you how to use basic annotations to inject message data into the processExchange() method arguments.

// Java
import org.apache.camel.*;

public class MyBeanProcessor {
    public void processExchange(
        @Header(name="user") String user,
        @Body String body,
        Exchange exchange
    ) {
        // Do whatever you like to 'exchange'...
        exchange.getIn().setBody(body + "UserName = " + user);
    }
}

Notice how you are able to mix the annotations with the default conventions. As well as injecting the annotated arguments, the parameter binding also automatically injects the exchange object into the org.apache.camel.Exchange argument.

Expression language annotations

The expression language annotations provide a powerful mechanism for injecting message data into a bean method’s arguments. Using these annotations, you can invoke an arbitrary script, written in the scripting language of your choice, to extract data from an inbound exchange and inject the data into a method argument. Table 2.4, “Expression Language Annotations” shows the annotations from the org.apache.camel.language package (and sub-packages, for the non-core annotations) that you can use to inject message data into the arguments of a bean method.

Table 2.4. Expression Language Annotations

AnnotationDescription

@Bean

Injects a Bean expression.

@Constant

Injects a Constant expression

@EL

Injects an EL expression.

@Groovy

Injects a Groovy expression.

@Header

Injects a Header expression.

@JavaScript

Injects a JavaScript expression.

@OGNL

Injects an OGNL expression.

@PHP

Injects a PHP expression.

@Python

Injects a Python expression.

@Ruby

Injects a Ruby expression.

@Simple

Injects a Simple expression.

@XPath

Injects an XPath expression.

@XQuery

Injects an XQuery expression.

For example, the following class shows you how to use the @XPath annotation to extract a username and a password from the body of an incoming message in XML format:

// Java
import org.apache.camel.language.*;

public class MyBeanProcessor {
    public void checkCredentials(
        @XPath("/credentials/username/text()") String user,
        @XPath("/credentials/password/text()") String pass
    ) {
        // Check the user/pass credentials...
        ...
    }
}

The @Bean annotation is a special case, because it enables you to inject the result of invoking a registered bean. For example, to inject a correlation ID into a method argument, you can use the @Bean annotation to invoke an ID generator class, as follows:

// Java
import org.apache.camel.language.*;

public class MyBeanProcessor {
    public void processCorrelatedMsg(
        @Bean("myCorrIdGenerator") String corrId,
        @Body String body
    ) {
        // Check the user/pass credentials...
        ...
    }
}

Where the string, myCorrIdGenerator, is the bean ID of the ID generator instance. The ID generator class can be instantiated using the spring bean element, as follows:

<beans ...>
    ...
    <bean id="myCorrIdGenerator" class="com.acme.MyIdGenerator"/>
</beans>

Where the MyIdGenerator class could be defined as follows:

// Java
package com.acme;

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;
   }
}

Notice that you can also use annotations in the referenced bean class, MyIdGenerator. The only restriction on the generate() method signature is that it must return the correct type to inject into the argument annotated by @Bean. Because the @Bean annotation does not let you specify a method name, the injection mechanism simply invokes the first method in the referenced bean that has the matching return type.

Note

Some of the language annotations are available in the core component (@Bean, @Constant, @Simple, and @XPath). For non-core components, however, you will have to make sure that you load the relevant component. For example, to use the OGNL script, you must load the camel-ognl component.

Inherited annotations

Parameter binding annotations can be inherited from an interface or from a superclass. For example, if you define a Java interface with a Header annotation and a Body annotation, as follows:

// Java
import org.apache.camel.*;

public interface MyBeanProcessorIntf {
    void processExchange(
        @Header(name="user") String user,
        @Body String body,
        Exchange exchange
    );
}

The overloaded methods defined in the implementation class, MyBeanProcessor, now inherit the annotations defined in the base interface, as follows:

// Java
import org.apache.camel.*;

public class MyBeanProcessor implements MyBeanProcessorIntf {
    public void processExchange(
        String user,  // Inherits Header annotation
        String body,  // Inherits Body annotation
        Exchange exchange
    ) {
        ...
    }
}

Interface implementations

The class that implements a Java interface is often protected, private or in package-only scope. If you try to invoke a method on an implementation class that is restricted in this way, the bean binding falls back to invoking the corresponding interface method, which is publicly accessible.

For example, consider the following public BeanIntf interface:

// Java
public interface BeanIntf {
    void processBodyAndHeader(String body, String title);
}

Where the BeanIntf interface is implemented by the following protected BeanIntfImpl class:

// Java
protected class BeanIntfImpl implements BeanIntf {
    void processBodyAndHeader(String body, String title) {
        ...
    }
}

The following bean invocation would fall back to invoking the public BeanIntf.processBodyAndHeader method:

from("file:data/inbound")
  .bean(BeanIntfImpl.class, "processBodyAndHeader(${body}, ${header.title})")
  .to("file:data/outbound");

Invoking static methods

Bean integration has the capability to invoke static methods without creating an instance of the associated class. For example, consider the following Java class that defines the static method, changeSomething():

// Java
...
public final class MyStaticClass {
    private MyStaticClass() {
    }

    public static String changeSomething(String s) {
        if ("Hello World".equals(s)) {
            return "Bye World";
        }
        return null;
    }

    public void doSomething() {
        // noop
    }
}

You can use bean integration to invoke the static changeSomething method, as follows:

from("direct:a")
  *.bean(MyStaticClass.class, "changeSomething")*
  .to("mock:a");

Note that, although this syntax looks identical to the invocation of an ordinary function, bean integration exploits Java reflection to identify the method as static and proceeds to invoke the method without instantiating MyStaticClass.

Invoking an OSGi service

In the special case where a route is deployed into a Red Hat JBoss Fuse container, it is possible to invoke an OSGi service directly using bean integration. For example, assuming that one of the bundles in the OSGi container has exported the service, org.fusesource.example.HelloWorldOsgiService, you could invoke the sayHello method using the following bean integration code:

from("file:data/inbound")
  .bean(org.fusesource.example.HelloWorldOsgiService.class, "sayHello")
  .to("file:data/outbound");

You could also invoke the OSGi service from within a Spring or blueprint XML file, using the bean component, as follows:

<to uri="bean:org.fusesource.example.HelloWorldOsgiService?method=sayHello"/>

The way this works is that Apache Camel sets up a chain of registries when it is deployed in the OSGi container. First of all, it looks up the specified class name in the OSGi service registry; if this lookup fails, it then falls back to the local Spring DM or blueprint registry.

2.5. Creating Exchange Instances

Overview

When processing messages with Java code (for example, in a bean class or in a processor class), it is often necessary to create a fresh exchange instance. If you need to create an Exchange object, the easiest approach is to invoke the methods of the ExchangeBuilder class, as described here.

ExchangeBuilder class

The fully qualified name of the ExchangeBuilder class is as follows:

org.apache.camel.builder.ExchangeBuilder

The ExchangeBuilder exposes the static method, anExchange, which you can use to start building an exchange object.

Example

For example, the following code creates a new exchange object containing the message body string, Hello World!, and with headers containing username and password credentials:

// Java
import org.apache.camel.Exchange;
import org.apache.camel.builder.ExchangeBuilder;
...
Exchange exch = ExchangeBuilder.anExchange(camelCtx)
                    .withBody("Hello World!")
                    .withHeader("username", "jdoe")
                    .withHeader("password", "pass")
                    .build();

ExchangeBuilder methods

The ExchangeBuilder class supports the following methods:

ExchangeBuilder anExchange(CamelContext context)
(static method) Initiate building an exchange object.
Exchange build()
Build the exchange.
ExchangeBuilder withBody(Object body)
Set the message body on the exchange (that is, sets the exchange’s In message body).
ExchangeBuilder withHeader(String key, Object value)
Set a header on the exchange (that is, sets a header on the exchange’s In message).
ExchangeBuilder withPattern(ExchangePattern pattern)
Sets the exchange pattern on the exchange.
ExchangeBuilder withProperty(String key, Object value)
Sets a property on the exchange.

2.6. Transforming Message Content

Abstract

Apache Camel supports a variety of approaches to transforming message content. In addition to a simple native API for modifying message content, Apache Camel supports integration with several different third-party libraries and transformation standards.

2.6.1. Simple Message Transformations

Overview

The Java DSL has a built-in API that enables you to perform simple transformations on incoming and outgoing messages. For example, the rule shown in Example 2.1, “Simple Transformation of Incoming Messages” appends the text, World!, to the end of the incoming message body.

Example 2.1. Simple Transformation of Incoming Messages

from("SourceURL").setBody(body().append(" World!")).to("TargetURL");

Where the setBody() command replaces the content of the incoming message’s body.

API for simple transformations

You can use the following API classes to perform simple transformations of the message content in a router rule:

  • org.apache.camel.model.ProcessorDefinition
  • org.apache.camel.builder.Builder
  • org.apache.camel.builder.ValueBuilder

ProcessorDefinition class

The org.apache.camel.model.ProcessorDefinition class defines the DSL commands you can insert directly into a router rule — for example, the setBody() command in Example 2.1, “Simple Transformation of Incoming Messages”. Table 2.5, “Transformation Methods from the ProcessorDefinition Class” shows the ProcessorDefinition methods that are relevant to transforming message content:

Table 2.5. Transformation Methods from the ProcessorDefinition Class

MethodDescription

Type convertBodyTo(Class type)

Converts the IN message body to the specified type.

Type removeFaultHeader(String name)

Adds a processor which removes the header on the FAULT message.

Type removeHeader(String name)

Adds a processor which removes the header on the IN message.

Type removeProperty(String name)

Adds a processor which removes the exchange property.

ExpressionClause<ProcessorDefinition<Type>> setBody()

Adds a processor which sets the body on the IN message.

Type setFaultBody(Expression expression)

Adds a processor which sets the body on the FAULT message.

Type setFaultHeader(String name, Expression expression)

Adds a processor which sets the header on the FAULT message.

ExpressionClause<ProcessorDefinition<Type>> setHeader(String name)

Adds a processor which sets the header on the IN message.

Type setHeader(String name, Expression expression)

Adds a processor which sets the header on the IN message.

ExpressionClause<ProcessorDefinition<Type>> setOutHeader(String name)

Adds a processor which sets the header on the OUT message.

Type setOutHeader(String name, Expression expression)

Adds a processor which sets the header on the OUT message.

ExpressionClause<ProcessorDefinition<Type>> setProperty(String name)

Adds a processor which sets the exchange property.

Type setProperty(String name, Expression expression)

Adds a processor which sets the exchange property.

ExpressionClause<ProcessorDefinition<Type>> transform()

Adds a processor which sets the body on the OUT message.

Type transform(Expression expression)

Adds a processor which sets the body on the OUT message.

Builder class

The org.apache.camel.builder.Builder class provides access to message content in contexts where expressions or predicates are expected. In other words, Builder methods are typically invoked in the arguments of DSL commands — for example, the body() command in Example 2.1, “Simple Transformation of Incoming Messages”. Table 2.6, “Methods from the Builder Class” summarizes the static methods available in the Builder class.

Table 2.6. Methods from the Builder Class

MethodDescription

static <E extends Exchange> ValueBuilder<E> body()

Returns a predicate and value builder for the inbound body on an exchange.

static <E extends Exchange,T> ValueBuilder<E> bodyAs(Class<T> type)

Returns a predicate and value builder for the inbound message body as a specific type.

static <E extends Exchange> ValueBuilder<E> constant(Object value)

Returns a constant expression.

static <E extends Exchange> ValueBuilder<E> faultBody()

Returns a predicate and value builder for the fault body on an exchange.

static <E extends Exchange,T> ValueBuilder<E> faultBodyAs(Class<T> type)

Returns a predicate and value builder for the fault message body as a specific type.

static <E extends Exchange> ValueBuilder<E> header(String name)

Returns a predicate and value builder for headers on an exchange.

static <E extends Exchange> ValueBuilder<E> outBody()

Returns a predicate and value builder for the outbound body on an exchange.

static <E extends Exchange> ValueBuilder<E> outBodyAs(Class<T> type)

Returns a predicate and value builder for the outbound message body as a specific type.

static ValueBuilder property(String name)

Returns a predicate and value builder for properties on an exchange.

static ValueBuilder regexReplaceAll(Expression content, String regex, Expression replacement)

Returns an expression that replaces all occurrences of the regular expression with the given replacement.

static ValueBuilder regexReplaceAll(Expression content, String regex, String replacement)

Returns an expression that replaces all occurrences of the regular expression with the given replacement.

static ValueBuilder sendTo(String uri)

Returns an expression processing the exchange to the given endpoint uri.

static <E extends Exchange> ValueBuilder<E> systemProperty(String name)

Returns an expression for the given system property.

static <E extends Exchange> ValueBuilder<E> systemProperty(String name, String defaultValue)

Returns an expression for the given system property.

ValueBuilder class

The org.apache.camel.builder.ValueBuilder class enables you to modify values returned by the Builder methods. In other words, the methods in ValueBuilder provide a simple way of modifying message content. Table 2.7, “Modifier Methods from the ValueBuilder Class” summarizes the methods available in the ValueBuilder class. That is, the table shows only the methods that are used to modify the value they are invoked on (for full details, see the API Reference documentation).

Table 2.7. Modifier Methods from the ValueBuilder Class

MethodDescription

ValueBuilder<E> append(Object value)

Appends the string evaluation of this expression with the given value.

Predicate contains(Object value)

Create a predicate that the left hand expression contains the value of the right hand expression.

ValueBuilder<E> convertTo(Class type)

Converts the current value to the given type using the registered type converters.

ValueBuilder<E> convertToString()

Converts the current value a String using the registered type converters.

Predicate endsWith(Object value)

 

<T> T evaluate(Exchange exchange, Class<T> type)

 

Predicate in(Object…​ values)

 

Predicate in(Predicate…​ predicates)

 

Predicate isEqualTo(Object value)

Returns true, if the current value is equal to the given value argument.

Predicate isGreaterThan(Object value)

Returns true, if the current value is greater than the given value argument.

Predicate isGreaterThanOrEqualTo(Object value)

Returns true, if the current value is greater than or equal to the given value argument.

Predicate isInstanceOf(Class type)

Returns true, if the current value is an instance of the given type.

Predicate isLessThan(Object value)

Returns true, if the current value is less than the given value argument.

Predicate isLessThanOrEqualTo(Object value)

Returns true, if the current value is less than or equal to the given value argument.

Predicate isNotEqualTo(Object value)

Returns true, if the current value is not equal to the given value argument.

Predicate isNotNull()

Returns true, if the current value is not null.

Predicate isNull()

Returns true, if the current value is null.

Predicate matches(Expression expression)

 

Predicate not(Predicate predicate)

Negates the predicate argument.

ValueBuilder prepend(Object value)

Prepends the string evaluation of this expression to the given value.

Predicate regex(String regex)

 

ValueBuilder<E> regexReplaceAll(String regex, Expression<E> replacement)

Replaces all occurrencies of the regular expression with the given replacement.

ValueBuilder<E> regexReplaceAll(String regex, String replacement)

Replaces all occurrencies of the regular expression with the given replacement.

ValueBuilder<E> regexTokenize(String regex)

Tokenizes the string conversion of this expression using the given regular expression.

ValueBuilder sort(Comparator comparator)

Sorts the current value using the given comparator.

Predicate startsWith(Object value)

Returns true, if the current value matches the string value of the value argument.

ValueBuilder<E> tokenize()

Tokenizes the string conversion of this expression using the comma token separator.

ValueBuilder<E> tokenize(String token)

Tokenizes the string conversion of this expression using the given token separator.

2.6.2. Marshalling and Unmarshalling

Java DSL commands

You can convert between low-level and high-level message formats using the following commands:

  • marshal() —  Converts a high-level data format to a low-level data format.
  • unmarshal()  —  Converts a low-level data format to a high-level data format.

Data formats

Apache Camel supports marshalling and unmarshalling of the following data formats:

  • Java serialization
  • JAXB
  • XMLBeans
  • XStream

Java serialization

Enables you to convert a Java object to a blob of binary data. For this data format, unmarshalling converts a binary blob to a Java object, and marshalling converts a Java object to a binary blob. For example, to read a serialized Java object from an endpoint, SourceURL, and convert it to a Java object, you use a rule like the following:

from("SourceURL").unmarshal().serialization()
.<FurtherProcessing>.to("TargetURL");

Or alternatively, in Spring XML:

<camelContext id="serialization" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <unmarshal>
      <serialization/>
    </unmarshal>
    <to uri="TargetURL"/>
  </route>
</camelContext>

JAXB

Provides a mapping between XML schema types and Java types (see https://jaxb.dev.java.net/). For JAXB, unmarshalling converts an XML data type to a Java object, and marshalling converts a Java object to an XML data type. Before you can use JAXB data formats, you must compile your XML schema using a JAXB compiler to generate the Java classes that represent the XML data types in the schema. This is called binding the schema. After the schema is bound, you define a rule to unmarshal XML data to a Java object, using code like the following:

org.apache.camel.spi.DataFormat jaxb = new org.apache.camel.model.dataformat.JaxbDataFormat("GeneratedPackageName");

from("SourceURL").unmarshal(jaxb)
.<FurtherProcessing>.to("TargetURL");

where GeneratedPackagename is the name of the Java package generated by the JAXB compiler, which contains the Java classes representing your XML schema.

Or alternatively, in Spring XML:

<camelContext id="jaxb" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <unmarshal>
      <jaxb prettyPrint="true" contextPath="GeneratedPackageName"/>
    </unmarshal>
    <to uri="TargetURL"/>
  </route>
</camelContext>

XMLBeans

Provides an alternative mapping between XML schema types and Java types (see http://xmlbeans.apache.org/). For XMLBeans, unmarshalling converts an XML data type to a Java object and marshalling converts a Java object to an XML data type. For example, to unmarshal XML data to a Java object using XMLBeans, you use code like the following:

from("SourceURL").unmarshal().xmlBeans()
.<FurtherProcessing>.to("TargetURL");

Or alternatively, in Spring XML:

<camelContext id="xmlBeans" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="SourceURL"/>
    <unmarshal>
      <xmlBeans prettyPrint="true"/>
    </unmarshal>
    <to uri="TargetURL"/>
  </route>
</camelContext>

XStream

Provides another mapping between XML types and Java types (see http://www.xml.com/pub/a/2004/08/18/xstream.html). XStream is a serialization library (like Java serialization), enabling you to convert any Java object to XML. For XStream, unmarshalling converts an XML data type to a Java object, and marshalling converts a Java object to an XML data type.

from("SourceURL").unmarshal().xstream()
.<FurtherProcessing>.to("TargetURL");
Note

The XStream data format is currently not supported in Spring XML.

2.6.3. Endpoint Bindings

What is a binding?

In Apache Camel, a binding is a way of wrapping an endpoint in a contract — for example, by applying a Data Format, a Content Enricher or a validation step. A condition or transformation is applied to the messages coming in, and a complementary condition or transformation is applied to the messages going out.

DataFormatBinding

The DataFormatBinding class is useful for the specific case where you want to define a binding that marshals and unmarshals a particular data format (see Section 2.6.2, “Marshalling and Unmarshalling”). In this case, all that you need to do to create a binding is to create a DataFormatBinding instance, passing a reference to the relevant data format in the constructor.

For example, the XML DSL snippet in Example 2.2, “JAXB Binding” shows a binding (with ID, jaxb) that is capable of marshalling and unmarshalling the JAXB data format when it is associated with an Apache Camel endpoint:

Example 2.2. JAXB Binding

<beans ... >
    ...
    <bean id="jaxb" class="org.apache.camel.processor.binding.DataFormatBinding">
        <constructor-arg ref="jaxbformat"/>
    </bean>

    <bean id="jaxbformat" class="org.apache.camel.model.dataformat.JaxbDataFormat">
        <property name="prettyPrint" value="true"/>
        <property name="contextPath" value="org.apache.camel.example"/>
    </bean>

</beans>

Associating a binding with an endpoint

The following alternatives are available for associating a binding with an endpoint:

Binding URI

To associate a binding with an endpoint, you can prefix the endpoint URI with binding:NameOfBinding, where NameOfBinding is the bean ID of the binding (for example, the ID of a binding bean created in Spring XML).

For example, the following example shows how to associate ActiveMQ endpoints with the JAXB binding defined in Example 2.2, “JAXB Binding”.

<beans ...>
    ...
    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <route>
            <from uri="binding:jaxb:activemq:orderQueue"/>
            <to uri="binding:jaxb:activemq:otherQueue"/>
        </route>
    </camelContext>
    ...
</beans>

BindingComponent

Instead of using a prefix to associate a binding with an endpoint, you can make the association implicit, so that the binding does not need to appear in the URI. For existing endpoints that do not have an implicit binding, the easiest way to achieve this is to wrap the endpoint using the BindingComponent class.

For example, to associate the jaxb binding with activemq endpoints, you could define a new BindingComponent instance as follows:

<beans ... >
    ...
    <bean id="jaxbmq" class="org.apache.camel.component.binding.BindingComponent">
        <constructor-arg ref="jaxb"/>
        <constructor-arg value="activemq:foo."/>
    </bean>

    <bean id="jaxb" class="org.apache.camel.processor.binding.DataFormatBinding">
        <constructor-arg ref="jaxbformat"/>
    </bean>

    <bean id="jaxbformat" class="org.apache.camel.model.dataformat.JaxbDataFormat">
        <property name="prettyPrint" value="true"/>
        <property name="contextPath" value="org.apache.camel.example"/>
    </bean>

</beans>

Where the (optional) second constructor argument to jaxbmq defines a URI prefix. You can now use the jaxbmq ID as the scheme for an endpoint URI. For example, you can define the following route using this binding component:

<beans ...>
    ...
    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <route>
            <from uri="jaxbmq:firstQueue"/>
            <to uri="jaxbmq:otherQueue"/>
        </route>
    </camelContext>
    ...
</beans>

The preceding route is equivalent to the following route, which uses the binding URI approach:

<beans ...>
    ...
    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <route>
            <from uri="binding:jaxb:activemq:foo.firstQueue"/>
            <to uri="binding:jaxb:activemq:foo.otherQueue"/>
        </route>
    </camelContext>
    ...
</beans>
Note

For developers that implement a custom Apache Camel component, it is possible to achieve this by implementing an endpoint class that inherits from the org.apache.camel.spi.HasBinding interface.

BindingComponent constructors

The BindingComponent class supports the following constructors:

public BindingComponent()
No arguments form. Use property injection to configure the binding component instance.
public BindingComponent(Binding binding)
Associate this binding component with the specified Binding object, binding.
public BindingComponent(Binding binding, String uriPrefix)
Associate this binding component with the specified Binding object, binding, and URI prefix, uriPrefix. This is the most commonly used constructor.
public BindingComponent(Binding binding, String uriPrefix, String uriPostfix)
This constructor supports the additional URI post-fix, uriPostfix, argument, which is automatically appended to any URIs defined using this binding component.

Implementing a custom binding

In addition to the DataFormatBinding, which is used for marshalling and unmarshalling data formats, you can implement your own custom bindings. Define a custom binding as follows:

  1. Implement an org.apache.camel.Processor class to perform a transformation on messages incoming to a consumer endpoint (appearing in a from element).
  2. Implement a complementary org.apache.camel.Processor class to perform the reverse transformation on messages outgoing from a producer endpoint (appearing in a to element).
  3. Implement the org.apache.camel.spi.Binding interface, which acts as a factory for the processor instances.

Binding interface

Example 2.3, “The org.apache.camel.spi.Binding Interface” shows the definition of the org.apache.camel.spi.Binding interface, which you must implement to define a custom binding.

Example 2.3. The org.apache.camel.spi.Binding Interface

// Java
package org.apache.camel.spi;

import org.apache.camel.Processor;

/**
 * Represents a <a href="http://camel.apache.org/binding.html">Binding</a> or contract
 * which can be applied to an Endpoint; such as ensuring that a particular
 * <a href="http://camel.apache.org/data-format.html">Data Format</a> is used on messages in and out of an endpoint.
 */
public interface Binding {

    /**
     * Returns a new {@link Processor} which is used by a producer on an endpoint to implement
     * the producer side binding before the message is sent to the underlying endpoint.
     */
    Processor createProduceProcessor();

    /**
     * Returns a new {@link Processor} which is used by a consumer on an endpoint to process the
     * message with the binding before its passed to the endpoint consumer producer.
     */
    Processor createConsumeProcessor();
}

When to use bindings

Bindings are useful when you need to apply the same kind of transformation to many different kinds of endpoint.

2.7. Property Placeholders

Overview

The property placeholders feature can be used to substitute strings into various contexts (such as endpoint URIs and attributes in XML DSL elements), where the placeholder settings are stored in Java properties files. This feature can be useful, if you want to share settings between different Apache Camel applications or if you want to centralize certain configuration settings.

For example, the following route sends requests to a Web server, whose host and port are substituted by the placeholders, {{remote.host}} and {{remote.port}}:

from("direct:start").to("http://{{remote.host}}:{{remote.port}}");

The placeholder values are defined in a Java properties file, as follows:

# Java properties file
remote.host=myserver.com
remote.port=8080
Note

Property Placeholders support an encoding option that enables you to read the .properties file, using a specific character set such as UTF-8. However, by default, it implements the ISO-8859-1 character set.

Apache Camel using the PropertyPlaceholders support the following:

  • Specify the default value together with the key to lookup.
  • No need to define the PropertiesComponent, if all the placeholder keys consist of default values, which are to be used.
  • Use third-party functions to lookup the property values. It enables you to implement your own logic.

    Note

    Provide three out of the box functions to lookup values from OS environmental variable, JVM system properties, or the service name idiom.

Property files

Property settings are stored in one or more Java properties files and must conform to the standard Java properties file format. Each property setting appears on its own line, in the format Key=Value. Lines with # or ! as the first non-blank character are treated as comments.

For example, a property file could have content as shown in Example 2.4, “Sample Property File”.

Example 2.4. Sample Property File

# Property placeholder settings
# (in Java properties file format)
cool.end=mock:result
cool.result=result
cool.concat=mock:{{cool.result}}
cool.start=direct:cool
cool.showid=true

cheese.end=mock:cheese
cheese.quote=Camel rocks
cheese.type=Gouda

bean.foo=foo
bean.bar=bar

Resolving properties

The properties component must be configured with the locations of one or more property files before you can start using it in route definitions. You must provide the property values using one of the following resolvers:

classpath:PathName,PathName,…​
(Default) Specifies locations on the classpath, where PathName is a file pathname delimited using forward slashes.
file:PathName,PathName,…​
Specifies locations on the file system, where PathName is a file pathname delimited using forward slashes.
ref:BeanID
Specifies the ID of a java.util.Properties object in the registry.
blueprint:BeanID
Specifies the ID of a cm:property-placeholder bean, which is used in the context of an OSGi blueprint file to access properties defined in the OSGi Configuration Admin service. For details, see the section called “Integration with OSGi blueprint property placeholders”.

For example, to specify the com/fusesource/cheese.properties property file and the com/fusesource/bar.properties property file, both located on the classpath, you would use the following location string:

com/fusesource/cheese.properties,com/fusesource/bar.properties
Note

You can omit the classpath: prefix in this example, because the classpath resolver is used by default.

Specifying locations using system properties and environment variables

You can embed Java system properties and O/S environment variables in a location PathName.

Java system properties can be embedded in a location resolver using the syntax, ${PropertyName}. For example, if the root directory of Red Hat JBoss Fuse is stored in the Java system property, karaf.home, you could embed that directory value in a file location, as follows:

file:${karaf.home}/etc/foo.properties

O/S environment variables can be embedded in a location resolver using the syntax, ${env:VarName}. For example, if the root directory of JBoss Fuse is stored in the environment variable, SMX_HOME, you could embed that directory value in a file location, as follows:

file:${env:SMX_HOME}/etc/foo.properties

Configuring the properties component

Before you can start using property placeholders, you must configure the properties component, specifying the locations of one or more property files.

In the Java DSL, you can configure the properties component with the property file locations, as follows:

// Java
import org.apache.camel.component.properties.PropertiesComponent;
...
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("com/fusesource/cheese.properties,com/fusesource/bar.properties");
context.addComponent("properties", pc);

As shown in the addComponent() call, the name of the properties component must be set to properties.

In the XML DSL, you can configure the properties component using the dedicated propertyPlacholder element, as follows:

<camelContext ...>
   <propertyPlaceholder
      id="properties"
      location="com/fusesource/cheese.properties,com/fusesource/bar.properties"
   />
</camelContext>

If you want the properties component to ignore any missing .properties files when it is being initialized, you can set the ignoreMissingLocation option to true (normally, a missing .properties file would result in an error being raised).

Additionally, if you want the properties component to ignore any missing locations that are specified using Java system properties or O/S environment variables, you can set the ignoreMissingLocation option to true.

Placeholder syntax

After it is configured, the property component automatically substitutes placeholders (in the appropriate contexts). The syntax of a placeholder depends on the context, as follows:

  • In endpoint URIs and in Spring XML files — the placeholder is specified as {{Key}}.
  • When setting XML DSL attributes — xs:string attributes are set using the following syntax:

    AttributeName="{{Key}}"

    Other attribute types (for example, xs:int or xs:boolean) must be set using the following syntax:

    prop:AttributeName="Key"

    Where prop is associated with the http://camel.apache.org/schema/placeholder namespace.

  • When setting Java DSL EIP options — to set an option on an Enterprise Integration Pattern (EIP) command in the Java DSL, add a placeholder() clause like the following to the fluent DSL:

    .placeholder("OptionName", "Key")
  • In Simple language expressions — the placeholder is specified as ${properties:Key}.

Substitution in endpoint URIs

Wherever an endpoint URI string appears in a route, the first step in parsing the endpoint URI is to apply the property placeholder parser. The placeholder parser automatically substitutes any property names appearing between double braces, {{Key}}. For example, given the property settings shown in Example 2.4, “Sample Property File”, you could define a route as follows:

from("{{cool.start}}")
    .to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
    .to("mock:{{cool.result}}");

By default, the placeholder parser looks up the properties bean ID in the registry to find the property component. If you prefer, you can explicitly specify the scheme in the endpoint URIs. For example, by prefixing properties: to each of the endpoint URIs, you can define the following equivalent route:

from("properties:{{cool.start}}")
    .to("properties:log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
    .to("properties:mock:{{cool.result}}");

When specifying the scheme explicitly, you also have the option of specifying options to the properties component. For example, to override the property file location, you could set the location option as follows:

from("direct:start").to("properties:{{bar.end}}?location=com/mycompany/bar.properties");

Substitution in Spring XML files

You can also use property placeholders in the XML DSL, for setting various attributes of the DSL elements. In this context, the placholder syntax also uses double braces, {{Key}}. For example, you could define a jmxAgent element using property placeholders, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <propertyPlaceholder id="properties" location="org/apache/camel/spring/jmx.properties"/>

    <!-- we can use property placeholders when we define the JMX agent -->
    <jmxAgent id="agent" registryPort="{{myjmx.port}}"
              usePlatformMBeanServer="{{myjmx.usePlatform}}"
              createConnector="true"
              statisticsLevel="RoutesOnly"
            />

    <route>
        <from uri="seda:start"/>
        <to uri="mock:result"/>
    </route>
</camelContext>

Substitution of XML DSL attribute values

You can use the regular placeholder syntax for specifying attribute values of xs:string type — for example, <jmxAgent registryPort="{{myjmx.port}}" …​>. But for attributes of any other type (for example, xs:int or xs:boolean), you must use the special syntax, prop:AttributeName="Key".

For example, given that a property file defines the stop.flag property to have the value, true, you can use this property to set the stopOnException boolean attribute, as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:prop="http://camel.apache.org/schema/placeholder"
       ... >

    <bean id="illegal" class="java.lang.IllegalArgumentException">
        <constructor-arg index="0" value="Good grief!"/>
    </bean>

    <camelContext xmlns="http://camel.apache.org/schema/spring">

        <propertyPlaceholder id="properties"
                             location="classpath:org/apache/camel/component/properties/myprop.properties"
                             xmlns="http://camel.apache.org/schema/spring"/>

        <route>
            <from uri="direct:start"/>
            <multicast prop:stopOnException="stop.flag">
                <to uri="mock:a"/>
                <throwException ref="damn"/>
                <to uri="mock:b"/>
            </multicast>
        </route>

    </camelContext>

</beans>
Important

The prop prefix must be explicitly assigned to the http://camel.apache.org/schema/placeholder namespace in your Spring file, as shown in the beans element of the preceding example.

Substitution of Java DSL EIP options

When invoking an EIP command in the Java DSL, you can set any EIP option using the value of a property placeholder, by adding a sub-clause of the form, placeholder("OptionName", "Key").

For example, given that a property file defines the stop.flag property to have the value, true, you can use this property to set the stopOnException option of the multicast EIP, as follows:

from("direct:start")
    .multicast().placeholder("stopOnException", "stop.flag")
        .to("mock:a").throwException(new IllegalAccessException("Damn")).to("mock:b");

Substitution in Simple language expressions

You can also substitute property placeholders in Simple language expressions, but in this case the syntax of the placeholder is ${properties:Key}. For example, you can substitute the cheese.quote placeholder inside a Simple expression, as follows:

from("direct:start")
    .transform().simple("Hi ${body} do you think ${properties:cheese.quote}?");

You can specify a default value for the property, using the syntax, ${properties:Key:DefaultVal}. For example:

from("direct:start")
    .transform().simple("Hi ${body} do you think ${properties:cheese.quote:cheese is good}?");

It is also possible to override the location of the property file using the syntax, ${properties-location:Location:Key}. For example, to substitute the bar.quote placeholder using the settings from the com/mycompany/bar.properties property file, you can define a Simple expression as follows:

from("direct:start")
    .transform().simple("Hi ${body}. ${properties-location:com/mycompany/bar.properties:bar.quote}.");

Using Property Placeholders in the XML DSL

In older releases, the xs:string type attributes were used to support placeholders in the XML DSL. For example, the timeout attribute would be a xs:int type. Therefore, you cannot set a string value as the placeholder key.

From Apache Camel 2.7 onwards, this is now possible by using a special placeholder namespace. The following example illustrates the prop prefix for the namespace. It enables you to use the prop prefix in the attributes in the XML DSLs.

Note

In the Multicast, set the option stopOnException as the value of the placeholder with the key stop. Also, in the properties file, define the value as

stop=true
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:prop="http://camel.apache.org/schema/placeholder"
       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
    ">

    <!-- Notice in the declaration above, we have defined the prop prefix as the Camel placeholder namespace -->

    <bean id="damn" class="java.lang.IllegalArgumentException">
        <constructor-arg index="0" value="Damn"/>
    </bean>

    <camelContext xmlns="http://camel.apache.org/schema/spring">

        <propertyPlaceholder id="properties"
                             location="classpath:org/apache/camel/component/properties/myprop.properties"
                             xmlns="http://camel.apache.org/schema/spring"/>

        <route>
            <from uri="direct:start"/>
            <!-- use prop namespace, to define a property placeholder, which maps to
                 option stopOnException={{stop}} -->
            <multicast prop:stopOnException="stop">
                <to uri="mock:a"/>
                <throwException ref="damn"/>
                <to uri="mock:b"/>
            </multicast>
        </route>

    </camelContext>

</beans>

Integration with OSGi blueprint property placeholders

If you deploy your route into the Red Hat JBoss Fuse OSGi container, you can integrate the Apache Camel property placeholder mechanism with JBoss Fuse’s blueprint property placeholder mechanism (in fact, the integration is enabled by default). There are two basic approaches to setting up the integration, as follows:

Implicit blueprint integration

If you define a camelContext element inside an OSGi blueprint file, the Apache Camel property placeholder mechanism automatically integrates with the blueprint property placeholder mechanism. That is, placeholders obeying the Apache Camel syntax (for example, {{cool.end}}) that appear within the scope of camelContext are implicitly resolved by looking up the blueprint property placeholder mechanism.

For example, consider the following route defined in an OSGi blueprint file, where the last endpoint in the route is defined by the property placeholder, {{result}}:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           xsi:schemaLocation="
           http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

    <!-- OSGI blueprint property placeholder -->
    <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
        <!-- list some properties for this test -->
        <cm:default-properties>
            <cm:property name="result" value="mock:result"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <!-- in the route we can use {{ }} placeholders which will look up in blueprint,
             as Camel will auto detect the OSGi blueprint property placeholder and use it -->
        <route>
            <from uri="direct:start"/>
            <to uri="mock:foo"/>
            <to uri="{{result}}"/>
        </route>
    </camelContext>

</blueprint>

The blueprint property placeholder mechanism is initialized by creating a cm:property-placeholder bean. In the preceding example, the cm:property-placeholder bean is associated with the camel.blueprint persistent ID, where a persistent ID is the standard way of referencing a group of related properties from the OSGi Configuration Admin service. In other words, the cm:property-placeholder bean provides access to all of the properties defined under the camel.blueprint persistent ID. It is also possible to specify default values for some of the properties (using the nested cm:property elements).

In the context of blueprint, the Apache Camel placeholder mechanism searches for an instance of cm:property-placeholder in the bean registry. If it finds such an instance, it automatically integrates the Apache Camel placeholder mechanism, so that placeholders like, {{result}}, are resolved by looking up the key in the blueprint property placeholder mechanism (in this example, through the myblueprint.placeholder bean).

Note

The default blueprint placeholder syntax (accessing the blueprint properties directly) is ${Key}. Hence, outside the scope of a camelContext element, the placeholder syntax you must use is ${Key}. Whereas, inside the scope of a camelContext element, the placeholder syntax you must use is {{Key}}.

Explicit blueprint integration

If you want to have more control over where the Apache Camel property placeholder mechanism finds its properties, you can define a propertyPlaceholder element and specify the resolver locations explicitly.

For example, consider the following blueprint configuration, which differs from the previous example in that it creates an explicit propertyPlaceholder instance:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           xsi:schemaLocation="
           http://www.osgi.org/xmlns/blueprint/v1.0.0 ">https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

    <!-- OSGI blueprint property placeholder -->
    <cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
        <!-- list some properties for this test -->
        <cm:default-properties>
            <cm:property name="result" value="mock:result"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">

        <!-- using Camel properties component and refer to the blueprint property placeholder by its id -->
        <propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder"/>

        <!-- in the route we can use {{ }} placeholders which will lookup in blueprint -->
        <route>
            <from uri="direct:start"/>
            <to uri="mock:foo"/>
            <to uri="{{result}}"/>
        </route>

    </camelContext>

</blueprint>

In the preceding example, the propertyPlaceholder element specifies explicitly which cm:property-placeholder bean to use by setting the location to blueprint:myblueprint.placeholder. That is, the blueprint: resolver explicitly references the ID, myblueprint.placeholder, of the cm:property-placeholder bean.

This style of configuration is useful, if there is more than one cm:property-placeholder bean defined in the blueprint file and you need to specify which one to use. It also makes it possible to source properties from multiple locations, by specifying a comma-separated list of locations. For example, if you wanted to look up properties both from the cm:property-placeholder bean and from the properties file, myproperties.properties, on the classpath, you could define the propertyPlaceholder element as follows:

<propertyPlaceholder id="properties"
  location="blueprint:myblueprint.placeholder,classpath:myproperties.properties"/>

Integration with Spring property placeholders

If you define your Apache Camel application using XML DSL in a Spring XML file, you can integrate the Apache Camel property placeholder mechanism with Spring property placeholder mechanism by declaring a Spring bean of type, org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer.

Define a BridgePropertyPlaceholderConfigurer, which replaces both Apache Camel’s propertyPlaceholder element and Spring’s ctx:property-placeholder element in the Spring XML file. You can then refer to the configured properties using either the Spring ${PropName} syntax or the Apache Camel {{PropName}} syntax.

For example, defining a bridge property placeholder that reads its property settings from the cheese.properties file:

<?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:ctx="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- Bridge Spring property placeholder with Camel -->
  <!-- Do not use <ctx:property-placeholder ... > at the same time -->
  <bean id="bridgePropertyPlaceholder"
        class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
    <property name="location"
              value="classpath:org/apache/camel/component/properties/cheese.properties"/>
  </bean>

  <!-- A bean that uses Spring property placeholder -->
  <!-- The ${hi} is a spring property placeholder -->
  <bean id="hello" class="org.apache.camel.component.properties.HelloBean">
    <property name="greeting" value="${hi}"/>
  </bean>

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <!-- Use Camel's property placeholder {{ }} style -->
    <route>
      <from uri="direct:{{cool.bar}}"/>
      <bean ref="hello"/>
      <to uri="{{cool.end}}"/>
    </route>
  </camelContext>

</beans>
Note

Alternatively, you can set the location attribute of the BridgePropertyPlaceholderConfigurer to point at a Spring properties file. The Spring properties file syntax is fully supported.

2.8. Threading Model

Java thread pool API

The Apache Camel threading model is based on the powerful Java concurrency API, Package java.util.concurrent, that first became available in Sun’s JDK 1.5. The key interface in this API is the ExecutorService interface, which represents a thread pool. Using the concurrency API, you can create many different kinds of thread pool, covering a wide range of scenarios.

Apache Camel thread pool API

The Apache Camel thread pool API builds on the Java concurrency API by providing a central factory (of org.apache.camel.spi.ExecutorServiceManager type) for all of the thread pools in your Apache Camel application. Centralising the creation of thread pools in this way provides several advantages, including:

  • Simplified creation of thread pools, using utility classes.
  • Integrating thread pools with graceful shutdown.
  • Threads automatically given informative names, which is beneficial for logging and management.

Component threading model

Some Apache Camel components — such as SEDA, JMS, and Jetty — are inherently multi-threaded. These components have all been implemented using the Apache Camel threading model and thread pool API.

If you are planning to implement your own Apache Camel component, it is recommended that you integrate your threading code with the Apache Camel threading model. For example, if your component needs a thread pool, it is recommended that you create it using the CamelContext’s ExecutorServiceManager object.

Processor threading model

Some of the standard processors in Apache Camel create their own thread pool by default. These threading-aware processors are also integrated with the Apache Camel threading model and they provide various options that enable you to customize the thread pools that they use.

Table 2.8, “Processor Threading Options” shows the various options for controlling and setting thread pools on the threading-aware processors built-in to Apache Camel.

Table 2.8. Processor Threading Options

ProcessorJava DSLXML DSL

aggregate

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

multicast

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

recipientList

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

split

parallelProcessing()
executorService()
executorServiceRef()
@parallelProcessing
@executorServiceRef

threads

executorService()
executorServiceRef()
poolSize()
maxPoolSize()
keepAliveTime()
timeUnit()
maxQueueSize()
rejectedPolicy()
@executorServiceRef
@poolSize
@maxPoolSize
@keepAliveTime
@timeUnit
@maxQueueSize
@rejectedPolicy

wireTap

wireTap(String uri, ExecutorService executorService)
wireTap(String uri, String executorServiceRef)
@executorServiceRef

threads DSL options

The threads processor is a general-purpose DSL command, which you can use to introduce a thread pool into a route. It supports the following options to customize the thread pool:

poolSize()
Minimum number of threads in the pool (and initial pool size).
maxPoolSize()
Maximum number of threads in the pool.
keepAliveTime()
If any threads are idle for longer than this period of time (specified in seconds), they are terminated.
timeUnit()
Time unit for keep alive, specified using the java.util.concurrent.TimeUnit type.
maxQueueSize()
Maximum number of pending tasks that this thread pool can store in its incoming task queue.
rejectedPolicy()
Specifies what course of action to take, if the incoming task queue is full. See Table 2.10, “Thread Pool Builder Options”
Note

The preceding thread pool options are not compatible with the executorServiceRef option (for example, you cannot use these options to override the settings in the thread pool referenced by an executorServiceRef option). Apache Camel validates the DSL to enforce this.

Creating a default thread pool

To create a default thread pool for one of the threading-aware processors, enable the parallelProcessing option, using the parallelProcessing() sub-clause, in the Java DSL, or the parallelProcessing attribute, in the XML DSL.

For example, in the Java DSL, you can invoke the multicast processor with a default thread pool (where the thread pool is used to process the multicast destinations concurrently) as follows:

from("direct:start")
  .multicast().parallelProcessing()
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

You can define the same route in XML DSL as follows

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <multicast parallelProcessing="true">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Default thread pool profile settings

The default thread pools are automatically created by a thread factory that takes its settings from the default thread pool profile. The default thread pool profile has the settings shown in Table 2.9, “Default Thread Pool Profile Settings” (assuming that these settings have not been modified by the application code).

Table 2.9. Default Thread Pool Profile Settings

Thread OptionDefault Value

maxQueueSize

1000

poolSize

10

maxPoolSize

20

keepAliveTime

60 (seconds)

rejectedPolicy

CallerRuns

Changing the default thread pool profile

It is possible to change the default thread pool profile settings, so that all subsequent default thread pools will be created with the custom settings. You can change the profile either in Java or in Spring XML.

For example, in the Java DSL, you can customize the poolSize option and the maxQueueSize option in the default thread pool profile, as follows:

// Java
import org.apache.camel.spi.ExecutorServiceManager;
import org.apache.camel.spi.ThreadPoolProfile;
...
ExecutorServiceManager manager = context.getExecutorServiceManager();
ThreadPoolProfile defaultProfile = manager.getDefaultThreadPoolProfile();

// Now, customize the profile settings.
defaultProfile.setPoolSize(3);
defaultProfile.setMaxQueueSize(100);
...

In the XML DSL, you can customize the default thread pool profile, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPoolProfile
        id="changedProfile"
        defaultProfile="true"
        poolSize="3"
        maxQueueSize="100"/>
    ...
</camelContext>

Note that it is essential to set the defaultProfile attribute to true in the preceding XML DSL example, otherwise the thread pool profile would be treated like a custom thread pool profile (see the section called “Creating a custom thread pool profile”), instead of replacing the default thread pool profile.

Customizing a processor’s thread pool

It is also possible to specify the thread pool for a threading-aware processor more directly, using either the executorService or executorServiceRef options (where these options are used instead of the parallelProcessing option). There are two approaches you can use to customize a processor’s thread pool, as follows:

  • Specify a custom thread pool — explicitly create an ExecutorService (thread pool) instance and pass it to the executorService option.
  • Specify a custom thread pool profile — create and register a custom thread pool factory. When you reference this factory using the executorServiceRef option, the processor automatically uses the factory to create a custom thread pool instance.

When you pass a bean ID to the executorServiceRef option, the threading-aware processor first tries to find a custom thread pool with that ID in the registry. If no thread pool is registered with that ID, the processor then attempts to look up a custom thread pool profile in the registry and uses the custom thread pool profile to instantiate a custom thread pool.

Creating a custom thread pool

A custom thread pool can be any thread pool of java.util.concurrent.ExecutorService type. The following approaches to creating a thread pool instance are recommended in Apache Camel:

  • Use the org.apache.camel.builder.ThreadPoolBuilder utility to build the thread pool class.
  • Use the org.apache.camel.spi.ExecutorServiceManager instance from the current CamelContext to create the thread pool class.

Ultimately, there is not much difference between the two approaches, because the ThreadPoolBuilder is actually defined using the ExecutorServiceManager instance. Normally, the ThreadPoolBuilder is preferred, because it offers a simpler approach. But there is at least one kind of thread (the ScheduledExecutorService) that can only be created by accessing the ExecutorServiceManager instance directory.

Table 2.10, “Thread Pool Builder Options” shows the options supported by the ThreadPoolBuilder class, which you can set when defining a new custom thread pool.

Table 2.10. Thread Pool Builder Options

Builder OptionDescription

maxQueueSize()

Sets the maximum number of pending tasks that this thread pool can store in its incoming task queue. A value of -1 specifies an unbounded queue. Default value is taken from default thread pool profile.

poolSize()

Sets the minimum number of threads in the pool (this is also the initial pool size). Default value is taken from default thread pool profile.

maxPoolSize()

Sets the maximum number of threads that can be in the pool. Default value is taken from default thread pool profile.

keepAliveTime()

If any threads are idle for longer than this period of time (specified in seconds), they are terminated. This allows the thread pool to shrink when the load is light. Default value is taken from default thread pool profile.

rejectedPolicy()

Specifies what course of action to take, if the incoming task queue is full. You can specify four possible values:

CallerRuns
(Default value) Gets the caller thread to run the latest incoming task. As a side effect, this option prevents the caller thread from receiving any more tasks until it has finished processing the latest incoming task.
Abort
Aborts the latest incoming task by throwing an exception.
Discard
Quietly discards the latest incoming task.
DiscardOldest
Discards the oldest unhandled task and then attempts to enqueue the latest incoming task in the task queue.

build()

Finishes building the custom thread pool and registers the new thread pool under the ID specified as the argument to build().

In Java DSL, you can define a custom thread pool using the ThreadPoolBuilder, as follows:

// Java
import org.apache.camel.builder.ThreadPoolBuilder;
import java.util.concurrent.ExecutorService;
...
ThreadPoolBuilder poolBuilder = new ThreadPoolBuilder(context);
ExecutorService customPool = poolBuilder.poolSize(5).maxPoolSize(5).maxQueueSize(100).build("customPool");
...

from("direct:start")
  .multicast().executorService(customPool)
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

Instead of passing the object reference, customPool, directly to the executorService() option, you can look up the thread pool in the registry, by passing its bean ID to the executorServiceRef() option, as follows:

// Java
from("direct:start")
  .multicast().executorServiceRef("customPool")
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

In XML DSL, you access the ThreadPoolBuilder using the threadPool element. You can then reference the custom thread pool using the executorServiceRef attribute to look up the thread pool by ID in the Spring registry, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPool id="customPool"
                poolSize="5"
                maxPoolSize="5"
                maxQueueSize="100" />

    <route>
        <from uri="direct:start"/>
        <multicast executorServiceRef="customPool">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Creating a custom thread pool profile

If you have many custom thread pool instances to create, you might find it more convenient to define a custom thread pool profile, which acts as a factory for thread pools. Whenever you reference a thread pool profile from a threading-aware processor, the processor automatically uses the profile to create a new thread pool instance. You can define a custom thread pool profile either in Java DSL or in XML DSL.

For example, in Java DSL you can create a custom thread pool profile with the bean ID, customProfile, and reference it from within a route, as follows:

// Java
import org.apache.camel.spi.ThreadPoolProfile;
import org.apache.camel.impl.ThreadPoolProfileSupport;
...
// Create the custom thread pool profile
ThreadPoolProfile customProfile = new ThreadPoolProfileSupport("customProfile");
customProfile.setPoolSize(5);
customProfile.setMaxPoolSize(5);
customProfile.setMaxQueueSize(100);
context.getExecutorServiceManager().registerThreadPoolProfile(customProfile);
...
// Reference the custom thread pool profile in a route
from("direct:start")
  .multicast().executorServiceRef("customProfile")
    .to("mock:first")
    .to("mock:second")
    .to("mock:third");

In XML DSL, use the threadPoolProfile element to create a custom pool profile (where you let the defaultProfile option default to false, because this is not a default thread pool profile). You can create a custom thread pool profile with the bean ID, customProfile, and reference it from within a route, as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <threadPoolProfile
                id="customProfile"
                poolSize="5"
                maxPoolSize="5"
                maxQueueSize="100" />

    <route>
        <from uri="direct:start"/>
        <multicast executorServiceRef="customProfile">
            <to uri="mock:first"/>
            <to uri="mock:second"/>
            <to uri="mock:third"/>
        </multicast>
    </route>
</camelContext>

Sharing a thread pool between components

Some of the standard poll-based components — such as File and FTP — allow you to specify the thread pool to use. This makes it possible for different components to share the same thread pool, reducing the overall number of threads in the JVM.

For example, the see File2 in the Apache Camel Component Reference Guide. and the Ftp2 in the Apache Camel Component Reference Guide both expose the scheduledExecutorService property, which you can use to specify the component’s ExecutorService object.

Customizing thread names

To make the application logs more readable, it is often a good idea to customize the thread names (which are used to identify threads in the log). To customize thread names, you can configure the thread name pattern by calling the setThreadNamePattern method on the ExecutorServiceStrategy class or the ExecutorServiceManager class. Alternatively, an easier way to set the thread name pattern is to set the threadNamePattern property on the CamelContext object.

The following placeholders can be used in a thread name pattern:

#camelId#
The name of the current CamelContext.
#counter#
A unique thread identifier, implemented as an incrementing counter.
#name#
The regular Camel thread name.
#longName#
The long thread name — which can include endpoint parameters and so on.

The following is a typical example of a thread name pattern:

Camel (#camelId#) thread #counter# - #name#

The following example shows how to set the threadNamePattern attribute on a Camel context using XML DSL:

<camelContext xmlns="http://camel.apache.org/schema/spring"
              threadNamePattern="Riding the thread #counter#" >
  <route>
    <from uri="seda:start"/>
    <to uri="log:result"/>
    <to uri="mock:result"/>
  </route>
</camelContext>

2.9. Controlling Start-Up and Shutdown of Routes

Overview

By default, routes are automatically started when your Apache Camel application (as represented by the CamelContext instance) starts up and routes are automatically shut down when your Apache Camel application shuts down. For non-critical deployments, the details of the shutdown sequence are usually not very important. But in a production environment, it is often crucial that existing tasks should run to completion during shutdown, in order to avoid data loss. You typically also want to control the order in which routes shut down, so that dependencies are not violated (which would prevent existing tasks from running to completion).

For this reason, Apache Camel provides a set of features to support graceful shutdown of applications. Graceful shutdown gives you full control over the stopping and starting of routes, enabling you to control the shutdown order of routes and enabling current tasks to run to completion.

Setting the route ID

It is good practice to assign a route ID to each of your routes. As well as making logging messages and management features more informative, the use of route IDs enables you to apply greater control over the stopping and starting of routes.

For example, in the Java DSL, you can assign the route ID, myCustomerRouteId, to a route by invoking the routeId() command as follows:

from("SourceURI").routeId("myCustomRouteId").process(...).to(TargetURI);

In the XML DSL, set the route element’s id attribute, as follows:

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route id="myCustomRouteId" >
    <from uri="SourceURI"/>
    <process ref="someProcessorId"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

Disabling automatic start-up of routes

By default, all of the routes that the CamelContext knows about at start time will be started automatically. If you want to control the start-up of a particular route manually, however, you might prefer to disable automatic start-up for that route.

To control whether a Java DSL route starts up automatically, invoke the autoStartup command, either with a boolean argument (true or false) or a String argument (true or false). For example, you can disable automatic start-up of a route in the Java DSL, as follows:

from("SourceURI")
  .routeId("nonAuto")
  .autoStartup(false)
  .to(TargetURI);

You can disable automatic start-up of a route in the XML DSL by setting the autoStartup attribute to false on the route element, as follows:

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route id="nonAuto" autoStartup="false">
    <from uri="SourceURI"/>
    <to uri="TargetURI"/>
  </route>
</camelContext>

Manually starting and stopping routes

You can manually start or stop a route at any time in Java by invoking the startRoute() and stopRoute() methods on the CamelContext instance. For example, to start the route having the route ID, nonAuto, invoke the startRoute() method on the CamelContext instance, context, as follows:

// Java
context.startRoute("nonAuto");

To stop the route having the route ID, nonAuto, invoke the stopRoute() method on the CamelContext instance, context, as follows:

// Java
context.stopRoute("nonAuto");

Startup order of routes

By default, Apache Camel starts up routes in a non-deterministic order. In some applications, however, it can be important to control the startup order. To control the startup order in the Java DSL, use the startupOrder() command, which takes a positive integer value as its argument. The route with the lowest integer value starts first, followed by the routes with successively higher startup order values.

For example, the first two routes in the following example are linked together through the seda:buffer endpoint. You can ensure that the first route segment starts after the second route segment by assigning startup orders (2 and 1 respectively), as follows:

Example 2.5. Startup Order in Java DSL

from("jetty:http://fooserver:8080")
    .routeId("first")
    .startupOrder(2)
    .to("seda:buffer");

from("seda:buffer")
    .routeId("second")
    .startupOrder(1)
    .to("mock:result");

// This route's startup order is unspecified
from("jms:queue:foo").to("jms:queue:bar");

Or in Spring XML, you can achieve the same effect by setting the route element’s startupOrder attribute, as follows:

Example 2.6. Startup Order in XML DSL

<route id="first" startupOrder="2">
    <from uri="jetty:http://fooserver:8080"/>
    <to uri="seda:buffer"/>
</route>

<route id="second" startupOrder="1">
    <from uri="seda:buffer"/>
    <to uri="mock:result"/>
</route>

<!-- This route's startup order is unspecified -->
<route>
    <from uri="jms:queue:foo"/>
    <to uri="jms:queue:bar"/>
</route>

Each route must be assigned a unique startup order value. You can choose any positive integer value that is less than 1000. Values of 1000 and over are reserved for Apache Camel, which automatically assigns these values to routes without an explicit startup value. For example, the last route in the preceding example would automatically be assigned the startup value, 1000 (so it starts up after the first two routes).

Shutdown sequence

When a CamelContext instance is shutting down, Apache Camel controls the shutdown sequence using a pluggable shutdown strategy. The default shutdown strategy implements the following shutdown sequence:

  1. Routes are shut down in the reverse of the start-up order.
  2. Normally, the shutdown strategy waits until the currently active exchanges have finshed processing. The treatment of running tasks is configurable, however.
  3. Overall, the shutdown sequence is bound by a timeout (default, 300 seconds). If the shutdown sequence exceeds this timeout, the shutdown strategy will force shutdown to occur, even if some tasks are still running.

Shutdown order of routes

Routes are shut down in the reverse of the start-up order. That is, when a start-up order is defined using the startupOrder() command (in Java DSL) or startupOrder attribute (in XML DSL), the first route to shut down is the route with the highest integer value assigned by the start-up order and the last route to shut down is the route with the lowest integer value assigned by the start-up order.

For example, in Example 2.5, “Startup Order in Java DSL”, the first route segment to be shut down is the route with the ID, first, and the second route segment to be shut down is the route with the ID, second. This example illustrates a general rule, which you should observe when shutting down routes: the routes that expose externally-accessible consumer endpoints should be shut down first, because this helps to throttle the flow of messages through the rest of the route graph.

Note

Apache Camel also provides the option shutdownRoute(Defer), which enables you to specify that a route must be amongst the last routes to shut down (overriding the start-up order value). But you should rarely ever need this option. This option was mainly needed as a workaround for earlier versions of Apache Camel (prior to 2.3), for which routes would shut down in the same order as the start-up order.

Shutting down running tasks in a route

If a route is still processing messages when the shutdown starts, the shutdown strategy normally waits until the currently active exchange has finished processing before shutting down the route. This behavior can be configured on each route using the shutdownRunningTask option, which can take either of the following values:

ShutdownRunningTask.CompleteCurrentTaskOnly
(Default) Usually, a route operates on just a single message at a time, so you can safely shut down the route after the current task has completed.
ShutdownRunningTask.CompleteAllTasks
Specify this option in order to shut down batch consumers gracefully. Some consumer endpoints (for example, File, FTP, Mail, iBATIS, and JPA) operate on a batch of messages at a time. For these endpoints, it is more appropriate to wait until all of the messages in the current batch have completed.

For example, to shut down a File consumer endpoint gracefully, you should specify the CompleteAllTasks option, as shown in the following Java DSL fragment:

// Java
public void configure() throws Exception {
    from("file:target/pending")
        .routeId("first").startupOrder(2)
        .shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
        .delay(1000).to("seda:foo");

    from("seda:foo")
        .routeId("second").startupOrder(1)
        .to("mock:bar");
}

The same route can be defined in the XML DSL as follows:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <!-- let this route complete all its pending messages when asked to shut down -->
    <route id="first"
           startupOrder="2"
           shutdownRunningTask="CompleteAllTasks">
        <from uri="file:target/pending"/>
        <delay><constant>1000</constant></delay>
        <to uri="seda:foo"/>
    </route>

    <route id="second" startupOrder="1">
        <from uri="seda:foo"/>
        <to uri="mock:bar"/>
    </route>
</camelContext>

Shutdown timeout

The shutdown timeout has a default value of 300 seconds. You can change the value of the timeout by invoking the setTimeout() method on the shutdown strategy. For example, you can change the timeout value to 600 seconds, as follows:

// Java
// context = CamelContext instance
context.getShutdownStrategy().setTimeout(600);

Integration with custom components

If you are implementing a custom Apache Camel component (which also inherits from the org.apache.camel.Service interface), you can ensure that your custom code receives a shutdown notification by implementing the org.apache.camel.spi.ShutdownPrepared interface. This gives the component an opportunity execute custom code in preparation for shutdown.

2.9.1. RouteIdFactory

Based on the consumer endpoints, you can add RouteIdFactory that can assign route ids with the logical names.

For example, when using the routes with seda or direct components as route inputs, then you may want to use their names as the route id, such as,

  • direct:foo- foo
  • seda:bar- bar
  • jms:orders- orders

Instead of using auto-assigned names, you can use the NodeIdFactory that can assign logical names for routes. Also, you can use the context-path of route URL as the name. For example, execute the following to use the RouteIDFactory:

context.setNodeIdFactory(new RouteIdFactory());
Note

It is possible to get the custom route id from rest endpoints.

2.10. Scheduled Route Policy

2.10.1. Overview of Scheduled Route Policies

Overview

A scheduled route policy can be used to trigger events that affect a route at runtime. In particular, the implementations that are currently available enable you to start, stop, suspend, or resume a route at any time (or times) specified by the policy.

Scheduling tasks

The scheduled route policies are capable of triggering the following kinds of event:

  • Start a route — start the route at the time (or times) specified. This event only has an effect, if the route is currently in a stopped state, awaiting activation.
  • Stop a route — stop the route at the time (or times) specified. This event only has an effect, if the route is currently active.
  • Suspend a route — temporarily de-activate the consumer endpoint at the start of the route (as specified in from()). The rest of the route is still active, but clients will not be able to send new messages into the route.
  • Resume a route — re-activate the consumer endpoint at the start of the route, returning the route to a fully active state.

Quartz component

The Quartz component is a timer component based on Terracotta’s Quartz, which is an open source implementation of a job scheduler. The Quartz component provides the underlying implementation for both the simple scheduled route policy and the cron scheduled route policy.

2.10.2. Simple Scheduled Route Policy

Overview

The simple scheduled route policy is a route policy that enables you to start, stop, suspend, and resume routes, where the timing of these events is defined by providing the time and date of an initial event and (optionally) by specifying a certain number of subsequent repititions. To define a simple scheduled route policy, create an instance of the following class:

org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy

Dependency

The simple scheduled route policy depends on the Quartz component, camel-quartz. For example, if you are using Maven as your build system, you would need to add a dependency on the camel-quartz artifact.

Java DSL example

Example 2.7, “Java DSL Example of Simple Scheduled Route” shows how to schedule a route to start up using the Java DSL. The initial start time, startTime, is defined to be 3 seconds after the current time. The policy is also configured to start the route a second time, 3 seconds after the initial start time, which is configured by setting routeStartRepeatCount to 1 and routeStartRepeatInterval to 3000 milliseconds.

In Java DSL, you attach the route policy to the route by calling the routePolicy() DSL command in the route.

Example 2.7. Java DSL Example of Simple Scheduled Route

// Java
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
long startTime = System.currentTimeMillis() + 3000L;
policy.setRouteStartDate(new Date(startTime));
policy.setRouteStartRepeatCount(1);
policy.setRouteStartRepeatInterval(3000);

from("direct:start")
   .routeId("test")
   .routePolicy(policy)
   .to("mock:success");
Note

You can specify multiple policies on the route by calling routePolicy() with multiple arguments.

XML DSL example

Example 2.8, “XML DSL Example of Simple Scheduled Route” shows how to schedule a route to start up using the XML DSL.

In XML DSL, you attach the route policy to the route by setting the routePolicyRef attribute on the route element.

Example 2.8. XML DSL Example of Simple Scheduled Route

<bean id="date" class="java.util.Data"/>

<bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy">
    <property name="routeStartDate" ref="date"/>
    <property name="routeStartRepeatCount" value="1"/>
    <property name="routeStartRepeatInterval" value="3000"/>
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="myroute" routePolicyRef="startPolicy">
        <from uri="direct:start"/>
        <to uri="mock:success"/>
    </route>
</camelContext>
Note

You can specify multiple policies on the route by setting the value of routePolicyRef as a comma-separated list of bean IDs.

Defining dates and times

The initial times of the triggers used in the simple scheduled route policy are specified using the java.util.Date type.The most flexible way to define a Date instance is through the java.util.GregorianCalendar class. Use the convenient constructors and methods of the GregorianCalendar class to define a date and then obtain a Date instance by calling GregorianCalendar.getTime().

For example, to define the time and date for January 1, 2011 at noon, call a GregorianCalendar constructor as follows:

// Java
import java.util.GregorianCalendar;
import java.util.Calendar;
...
GregorianCalendar gc = new GregorianCalendar(
    2011,
    Calendar.JANUARY,
    1,
    12,  // hourOfDay
    0,   // minutes
    0    // seconds
);

java.util.Date triggerDate = gc.getTime();

The GregorianCalendar class also supports the definition of times in different time zones. By default, it uses the local time zone on your computer.

Graceful shutdown

When you configure a simple scheduled route policy to stop a route, the route stopping algorithm is automatically integrated with the graceful shutdown procedure (see Section 2.9, “Controlling Start-Up and Shutdown of Routes”). This means that the task waits until the current exchange has finished processing before shutting down the route. You can set a timeout, however, that forces the route to stop after the specified time, irrespective of whether or not the route has finished processing the exchange.

Logging Inflight Exchanges on Timeout

If a graceful shutdown fails to shutdown cleanly within the given timeout period, then Apache Camel performs more aggressive shut down. It forces routes, threadpools etc to shutdown.

After the timeout, Apache Camel logs information about the current inflight exchanges. It logs the origin of the exchange and current route of exchange.

For example, the log below shows that there is one inflight exchange, that origins from route1 and is currently on the same route1 at the delay1 node.

During graceful shutdown, If you enable the DEBUG logging level on org.apache.camel.impl.DefaultShutdownStrategy, then it logs the same inflight exchange information.

2015-01-12 13:23:23,656 [- ShutdownTask] INFO DefaultShutdownStrategy - There are 1 inflight exchanges:
InflightExchange: [exchangeId=ID-davsclaus-air-62213-1421065401253-0-3, fromRouteId=route1, routeId=route1, nodeId=delay1, elapsed=2007, duration=2017]

If you do not want to see these logs, you can turn this off by setting the option logInflightExchangesOnTimeout to false.

  context.getShutdownStrategegy().setLogInflightExchangesOnTimeout(false);

Scheduling tasks

You can use a simple scheduled route policy to define one or more of the following scheduling tasks:

Starting a route

The following table lists the parameters for scheduling one or more route starts.

ParameterTypeDefaultDescription

routeStartDate

java.util.Date

None

Specifies the date and time when the route is started for the first time.

routeStartRepeatCount

int

0

When set to a non-zero value, specifies how many times the route should be started.

routeStartRepeatInterval

long

0

Specifies the time interval between starts, in units of milliseconds.

Stopping a route

The following table lists the parameters for scheduling one or more route stops.

ParameterTypeDefaultDescription

routeStopDate

java.util.Date

None

Specifies the date and time when the route is stopped for the first time.

routeStopRepeatCount

int

0

When set to a non-zero value, specifies how many times the route should be stopped.

routeStopRepeatInterval

long

0

Specifies the time interval between stops, in units of milliseconds.

routeStopGracePeriod

int

10000

Specifies how long to wait for the current exchange to finish processing (grace period) before forcibly stopping the route. Set to 0 for an infinite grace period.

routeStopTimeUnit

long

TimeUnit.MILLISECONDS

Specifies the time unit of the grace period.

Suspending a route

The following table lists the parameters for scheduling the suspension of a route one or more times.

ParameterTypeDefaultDescription

routeSuspendDate

java.util.Date

None

Specifies the date and time when the route is suspended for the first time.

routeSuspendRepeatCount

int

0

When set to a non-zero value, specifies how many times the route should be suspended.

routeSuspendRepeatInterval

long

0

Specifies the time interval between suspends, in units of milliseconds.

Resuming a route

The following table lists the parameters for scheduling the resumption of a route one or more times.

ParameterTypeDefaultDescription

routeResumeDate

java.util.Date

None

Specifies the date and time when the route is resumed for the first time.

routeResumeRepeatCount

int

0

When set to a non-zero value, specifies how many times the route should be resumed.

routeResumeRepeatInterval

long

0

Specifies the time interval between resumes, in units of milliseconds.

2.10.3. Cron Scheduled Route Policy

Overview

The cron scheduled route policy is a route policy that enables you to start, stop, suspend, and resume routes, where the timing of these events is specified using cron expressions. To define a cron scheduled route policy, create an instance of the following class:

org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy

Dependency

The simple scheduled route policy depends on the Quartz component, camel-quartz. For example, if you are using Maven as your build system, you would need to add a dependency on the camel-quartz artifact.

Java DSL example

Example 2.9, “Java DSL Example of a Cron Scheduled Route” shows how to schedule a route to start up using the Java DSL. The policy is configured with the cron expression, \*/3 * * * * ?, which triggers a start event every 3 seconds.

In Java DSL, you attach the route policy to the route by calling the routePolicy() DSL command in the route.

Example 2.9. Java DSL Example of a Cron Scheduled Route

// Java
CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy();
policy.setRouteStartTime("*/3 * * * * ?");

from("direct:start")
    .routeId("test")
    .routePolicy(policy)
    .to("mock:success");;
Note

You can specify multiple policies on the route by calling routePolicy() with multiple arguments.

XML DSL example

Example 2.10, “XML DSL Example of a Cron Scheduled Route”shows how to schedule a route to start up using the XML DSL.

In XML DSL, you attach the route policy to the route by setting the routePolicyRef attribute on the route element.

Example 2.10. XML DSL Example of a Cron Scheduled Route

<bean id="date" class="org.apache.camel.routepolicy.quartz.SimpleDate"/>

<bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy">
    <property name="routeStartTime" value="*/3 * * * * ?"/>
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="testRoute" routePolicyRef="startPolicy">
        <from uri="direct:start"/>
        <to uri="mock:success"/>
    </route>
</camelContext>
Note

You can specify multiple policies on the route by setting the value of routePolicyRef as a comma-separated list of bean IDs.

Defining cron expressions

The cron expression syntax has its origins in the UNIX cron utility, which schedules jobs to run in the background on a UNIX system. A cron expression is effectively a syntax for wildcarding dates and times that enables you to specify either a single event or multiple events that recur periodically.

A cron expression consists of 6 or 7 fields in the following order:

Seconds Minutes Hours DayOfMonth Month DayOfWeek [Year]

The Year field is optional and usually omitted, unless you want to define an event that occurs once and once only. Each field consists of a mixture of literals and special characters. For example, the following cron expression specifies an event that fires once every day at midnight:

0 0 24 * * ?

The * character is a wildcard that matches every value of a field. Hence, the preceding expression matches every day of every month. The ? character is a dummy placeholder that means *ignore this field*. It always appears either in the DayOfMonth field or in the DayOfWeek field, because it is not logically consistent to specify both of these fields at the same time. For example, if you want to schedule an event that fires once a day, but only from Monday to Friday, use the following cron expression:

0 0 24 ? * MON-FRI

Where the hyphen character specifies a range, MON-FRI. You can also use the forward slash character, /, to specify increments. For example, to specify that an event fires every 5 minutes, use the following cron expression:

0 0/5 * * * ?

For a full explanation of the cron expression syntax, see the Wikipedia article on CRON expressions.

Scheduling tasks

You can use a cron scheduled route policy to define one or more of the following scheduling tasks:

Starting a route

The following table lists the parameters for scheduling one or more route starts.

ParameterTypeDefaultDescription

routeStartString

String

None

Specifies a cron expression that triggers one or more route start events.

Stopping a route

The following table lists the parameters for scheduling one or more route stops.

ParameterTypeDefaultDescription

routeStopTime

String

None

Specifies a cron expression that triggers one or more route stop events.

routeStopGracePeriod

int

10000

Specifies how long to wait for the current exchange to finish processing (grace period) before forcibly stopping the route. Set to 0 for an infinite grace period.

routeStopTimeUnit

long

TimeUnit.MILLISECONDS

Specifies the time unit of the grace period.

Suspending a route

The following table lists the parameters for scheduling the suspension of a route one or more times.

ParameterTypeDefaultDescription

routeSuspendTime

String

None

Specifies a cron expression that triggers one or more route suspend events.

Resuming a route

The following table lists the parameters for scheduling the resumption of a route one or more times.

ParameterTypeDefaultDescription

routeResumeTime

String

None

Specifies a cron expression that triggers one or more route resume events.

2.10.4. Route Policy Factory

Using Route Policy Factory

Available as of Camel 2.14

If you want to use a route policy for every route, you can use a org.apache.camel.spi.RoutePolicyFactory as a factory for creating a RoutePolicy instance for each route. This can be used when you want to use the same kind of route policy for every route. Then you need to only configure the factory once, and every route created will have the policy assigned.

There is API on CamelContext to add a factory, as shown below:

context.addRoutePolicyFactory(new MyRoutePolicyFactory());

From XML DSL you only define a <bean> with the factory

<bean id="myRoutePolicyFactory" class="com.foo.MyRoutePolicyFactory"/>

The factory contains the createRoutePolicy method for creating route policies.

/**
 * Creates a new {@link org.apache.camel.spi.RoutePolicy} which will be assigned to the given route.
 *
 * @param camelContext the camel context
 * @param routeId      the route id
 * @param route        the route definition
 * @return the created {@link org.apache.camel.spi.RoutePolicy}, or <tt>null</tt> to not use a policy for this route
 */
RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition route);

Note you can have as many route policy factories as you want. Just call the addRoutePolicyFactory again, or declare the other factories as <bean> in XML.

2.11. Reloading Camel Routes

In Apache Camel 2.19 release, you can enable the live reload of your camel XML routes, which will trigger a reload, when you save the XML file from your editor. You can use this feature when using:

  • Camel standalone with Camel Main class
  • Camel Spring Boot
  • From the camel:run maven plugin

However, you can also enable this manually, by setting a ReloadStrategy on the CamelContext and by providing your own custom strategies.

2.12. Camel Maven Plugin

The Camel Maven Plugin supports the following goals:

  • camel:run - To run your Camel application
  • camel:validate - To validate your source code for invalid Camel endpoint URIs
  • camel:route-coverage - To report the coverage of your Camel routes after unit testing

2.12.1. camel:run

The camel:run goal of the Camel Maven Plugin is used to run your Camel Spring configurations in a forked JVM from Maven. A good example application to get you started is the Spring Example.

cd examples/camel-example-spring
mvn camel:run

This makes it very easy to spin up and test your routing rules without having to write a main(…​) method; it also lets you create multiple jars to host different sets of routing rules and easily test them independently. The Camel Maven plugin compiles the source code in the maven project, then boots up a Spring ApplicationContext using the XML configuration files on the classpath at META-INF/spring/*.xml. If you want to boot up your Camel routes a little faster, you can try the camel:embedded instead.

2.12.1.1. Options

The Camel Maven plugin run goal supports the following options which can be configured from the command line (use -D syntax), or defined in the pom.xml file in the <configuration> tag.

Parameter

Default Value

Description

duration

-1

Sets the time duration (seconds) that the application runs for before terminating. A value ⇐ 0 will run forever.

durationIdle

-1

Sets the idle time duration (seconds) duration that the application can be idle for before terminating. A value ⇐ 0 will run forever.

durationMaxMessages

-1

Sets the duration of maximum number of messages that the application processes before terminating.

logClasspath

false

Whether to log the classpath when starting

2.12.1.2. Running OSGi Blueprint

The camel:run plugin also supports running a Blueprint application, and by default it scans for OSGi blueprint files in OSGI-INF/blueprint/*.xml. You would need to configure the camel:run plugin to use blueprint, by setting useBlueprint to true as shown below:

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <useBlueprint>true</useBlueprint>
  </configuration>
</plugin>

This allows you to boot up any Blueprint services you wish, regardless of whether they are Camel-related, or any other Blueprint. The camel:run goal can auto detect if camel-blueprint is on the classpath or there are blueprint XML files in the project, and therefore you no longer have to configure the useBlueprint option.

2.12.1.3. Using limited Blueprint container

We use the Felix Connector project as the blueprint container. This project is not a full fledged blueprint container. For that you can use Apache Karaf or Apache ServiceMix. You can use the applicationContextUri configuration to specify an explicit blueprint XML file, such as:

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <useBlueprint>true</useBlueprint>
    <applicationContextUri>myBlueprint.xml</applicationContextUri>
    <!-- ConfigAdmin options which have been added since Camel 2.12.0 -->
    <configAdminPid>test</configAdminPid>
    <configAdminFileName>/user/test/etc/test.cfg</configAdminFileName>
  </configuration>
</plugin>

The applicationContextUri loads the file from the classpath, so in the example above the myBlueprint.xml file must be in the root of the classpath. The configAdminPid is the pid name which will be used as the pid name for configuration admin service when loading the persistence properties file. The configAdminFileName is the file name which will be used to load the configuration admin service properties file.

2.12.1.4. Running CDI

The camel:run plugin also supports running a CDI application. This allows you to boot up any CDI services you wish, whether they are Camel-related, or any other CDI enabled services. You should add the CDI container of your choice (e.g. Weld or OpenWebBeans) to the dependencies of the camel-maven-plugin such as in this example. From the source of Camel you can run a CDI example as follows:

cd examples/camel-example-cdi
mvn compile camel:run

2.12.1.5. Logging the classpath

You can configure whether the classpath should be logged when camel:run executes. You can enable this in the configuration using:

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <logClasspath>true</logClasspath>
  </configuration>
</plugin>

2.12.1.6. Using live reload of XML files

You can configure the plugin to scan for XML file changes and trigger a reload of the Camel routes which are contained in those XML files.

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <configuration>
    <fileWatcherDirectory>src/main/resources/META-INF/spring</fileWatcherDirectory>
  </configuration>
</plugin>

Then the plugin watches this directory. This allows you to edit the source code from your editor and save the file, and have the running Camel application utilize those changes. Note that only the changes of Camel routes, for example, <routes>, or <route> which are supported. You cannot change Spring or OSGi Blueprint <bean> elements.

2.12.2. camel:validate

For validating the source code for misconfigured Camel: * endpoint URIs * simple expressions or predicates * duplicate route ids

Then you can run the camel:validate goal from the command line or from within your Java editor such as IDEA or Eclipse.

mvn camel:validate

You can also enable the plugin to automatic run as part of the build to catch these errors.

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <executions>
    <execution>
      <phase>process-classes</phase>
      <goals>
        <goal>validate</goal>
      </goals>
    </execution>
  </executions>
</plugin>

The phase determines when the plugin runs. In the sample above the phase is process-classes which runs after the compilation of the main source code. The maven plugin can also be configured to validate the test source code, which means that the phase should be changed accordingly to process-test-classes as shown below:

<plugin>
  <groupId>org.jboss.redhat-fuse</groupId>
  <artifactId>camel-maven-plugin</artifactId>
  <executions>
    <execution>
      <configuration>
        <includeTest>true</includeTest>
      </configuration>
      <phase>process-test-classes</phase>
      <goals>
        <goal>validate</goal>
      </goals>
    </execution>
  </executions>
</plugin>

2.12.2.1. Running the goal on any Maven project

You can also run the validate goal on any Maven project without having to add the plugin to the pom.xml file. Doing so requires to specify the plugin using its fully qualified name. For example to run the goal on the camel-example-cdi from Apache Camel, you can run

$cd camel-example-cdi
$mvn org.apache.camel:camel-maven-plugin:2.20.0:validate

which then runs and outputs the following:

[INFO] ------------------------------------------------------------------------
[INFO] Building Camel :: Example :: CDI 2.20.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi ---
[INFO] Endpoint validation success: (4 = passed, 0 = invalid, 0 = incapable, 0 = unknown components)
[INFO] Simple validation success: (0 = passed, 0 = invalid)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

The validation passed, and 4 endpoints was validated. Now suppose we made a typo in one of the Camel endpoint URIs in the source code, such as:

@Uri("timer:foo?period=5000")

is changed to include a typo error in the period option

@Uri("timer:foo?perid=5000")

And when running the validate goal again reports the following:

[INFO] ------------------------------------------------------------------------
[INFO] Building Camel :: Example :: CDI 2.20.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- camel-maven-plugin:2.20.0:validate (default-cli) @ camel-example-cdi ---
[WARNING] Endpoint validation error at: org.apache.camel.example.cdi.MyRoutes(MyRoutes.java:32)

	timer:foo?perid=5000

	                   perid    Unknown option. Did you mean: [period]


[WARNING] Endpoint validation error: (3 = passed, 1 = invalid, 0 = incapable, 0 = unknown components)
[INFO] Simple validation success: (0 = passed, 0 = invalid)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

2.12.2.2. Options

The Camel Maven plugin validate goal supports the following options which can be configured from the command line (use -D syntax), or defined in the pom.xml file in the <configuration> tag.

Parameter

Default Value

Description

downloadVersion

true

Whether to allow downloading Camel catalog version from the internet. This is needed if the project uses a different Camel version than this plugin is using by default.

failOnError

false

Whether to fail if invalid Camel endpoints were found. By default the plugin logs the errors at WARN level.

logUnparseable

false

Whether to log endpoint URI which was un-parsable and therefore not possible to validate.

includeJava

true

Whether to include Java files to be validated for invalid Camel endpoints.

includeXml

true

Whether to include XML files to be validated for invalid Camel endpoints.

includeTest

false

Whether to include test source code.

includes

 

To filter the names of java and xml files to only include files matching any of the given list of patterns (wildcard and regular expression). Multiple values can be separated by comma.

excludes

 

To filter the names of java and xml files to exclude files matching any of the given list of patterns (wildcard and regular expression). Multiple values can be separated by comma.

ignoreUnknownComponent

true

Whether to ignore unknown components.

ignoreIncapable

true

Whether to ignore incapable of parsing the endpoint URI or simple expression.

ignoreLenientProperties

true

Whether to ignore components that uses lenient properties. When this is true, then the URI validation is stricter but would fail on properties that are not part of the component but in the URI because of using lenient properties. For example using the HTTP components to provide query parameters in the endpoint URI.

ignoreDeprecated

true

Camel 2.23 Whether to ignore deprecated options being used in the endpoint URI.

duplicateRouteId

true

Camel 2.20 Whether to validate for duplicate route ids. Route ids should be unique and if there are duplicates then Camel will fail to startup.

directOrSedaPairCheck

true

Camel 2.23 Whether to validate direct/seda endpoints sending to non existing consumers.

showAll

false

Whether to show all endpoints and simple expressions (both invalid and valid).

For example to turn off ignoring usage of deprecated options from the command line, you can run:

$mvn camel:validate -Dcamel.ignoreDeprecated=true

Note that you must prefix the -D command argument with camel., eg camel.ignoreDeprecated as the option name.

2.12.2.3. Validating Endpoints using include test

If you have a Maven project then you can run the plugin to validate the endpoints in the unit test source code as well. You can pass in the options using -D style as shown:

$cd myproject
$mvn org.apache.camel:camel-maven-plugin:2.20.0:validate -DincludeTest=true

2.12.3. camel:route-coverage

For generating a report of the coverage of your Camel routes from unit testing. You can use this to know which parts of your Camel routes has been used or not.

2.12.3.1. Enabling route coverage

You can enable route coverage while running unit tests either by:

  • setting global JVM system property enabling for all test classes
  • using @EnableRouteCoverage annotation per test class if using camel-test-spring module
  • overriding isDumpRouteCoverage method per test class if using camel-test module

2.12.3.2. Enabling Route Coverage by using JVM system property

You can turn on the JVM system property CamelTestRouteCoverage to enable route coverage for all tests cases. This can be done either in the configuration of the maven-surefire-plugin:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <systemPropertyVariables>
      <CamelTestRouteCoverage>true</CamelTestRouteCoverage>
    </systemPropertyVariables>
  </configuration>
</plugin>

Or from the command line when running tests:

mvn clean test -DCamelTestRouteCoverage=true

2.12.3.3. Enabling via @EnableRouteCoverage annotation

You can enable route coverage in the unit tests classes by adding the @EnableRouteCoverage annotation to the test class if you are testing using camel-test-spring:

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(classes = SampleCamelApplication.class)
@EnableRouteCoverage
public class FooApplicationTest {

2.12.3.4. Enabling via isDumpRouteCoverage method

However if you are using camel-test and your unit tests are extending CamelTestSupport then you can turn on route coverage as shown:

@Override
public boolean isDumpRouteCoverage() {
    return true;
}

Routes that can be coveraged under RouteCoverage method must have an unique id assigned, in other words you cannot use anonymous routes. You do this using routeId in Java DSL:

from("jms:queue:cheese").routeId("cheesy")
  .to("log:foo")
  ...

And in XML DSL you just assign the route id via the id attribute

<route id="cheesy">
  <from uri="jms:queue:cheese"/>
  <to uri="log:foo"/>
  ...
</route>

2.12.3.5. Generating route coverage report

TO generate the route coverage report, run the unit test with:

mvn test

You can then run the goal to report the route coverage as follows:

mvn camel:route-coverage

This reports which routes has missing route coverage with precise source code line reporting:

[INFO] --- camel-maven-plugin:2.21.0:route-coverage (default-cli) @ camel-example-spring-boot-xml ---
[INFO] Discovered 1 routes
[INFO] Route coverage summary:

File:	src/main/resources/my-camel.xml
RouteId:	hello

  Line #      Count   Route
  ------      -----   -----
      28          1   from
      29          1     transform
      32          1     filter
      34          0       to
      36          1     to

Coverage: 4 out of 5 (80.0%)

Here we can see that the 2nd last line with to has 0 in the count column, and therefore is not covered. We can also see that this is one line 34 in the source code file, which is in the my-camel.xml XML file.

2.12.3.6. Options

The Camel Maven plugin coverage goal supports the following options which can be configured from the command line (use -D syntax), or defined in the pom.xml file in the <configuration> tag.

Parameter

Default Value

Description

failOnError

false

Whether to fail if any of the routes does not have 100% coverage.

includeTest

false

Whether to include test source code.

includes

 

To filter the names of java and xml files to only include files matching any of the given list of patterns (wildcard and regular expression). Multiple values can be separated by comma.

excludes

 

To filter the names of java and xml files to exclude files matching any of the given list of patterns (wildcard and regular expression). Multiple values can be separated by comma.

anonymousRoutes

false

Whether to allow anonymous routes (routes without any route id assigned). By using route id’s then its safer to match the route cover data with the route source code. Anonymous routes are less safe to use for route coverage as its harder to know exactly which route that was tested corresponds to which of the routes from the source code.

2.13. Running Apache Camel Standalone

When you run camel as a standalone application, it provides the Main class that you can use to run the application and keep it running until the JVM terminates. You can find the MainListener class within the org.apache.camel.main Java package.

Following are the components of the Main class:

  • camel-core JAR in the org.apache.camel.Main class
  • camel-spring JAR in the org.apache.camel.spring.Main class

The following example shows how you can create and use the Main class from Camel:

public class MainExample {

    private Main main;

    public static void main(String[] args) throws Exception {
        MainExample example = new MainExample();
        example.boot();
    }

    public void boot() throws Exception {
        // create a Main instance
        main = new Main();
        // bind MyBean into the registry
        main.bind("foo", new MyBean());
        // add routes
        main.addRouteBuilder(new MyRouteBuilder());
        // add event listener
        main.addMainListener(new Events());
        // set the properties from a file
        main.setPropertyPlaceholderLocations("example.properties");
        // run until you terminate the JVM
        System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n");
        main.run();
    }

    private static class MyRouteBuilder extends RouteBuilder {
        @Override
        public void configure() throws Exception {
            from("timer:foo?delay={{millisecs}}")
                .process(new Processor() {
                    public void process(Exchange exchange) throws Exception {
                        System.out.println("Invoked timer at " + new Date());
                    }
                })
                .bean("foo");
        }
    }

    public static class MyBean {
        public void callMe() {
            System.out.println("MyBean.callMe method has been called");
        }
    }

    public static class Events extends MainListenerSupport {

        @Override
        public void afterStart(MainSupport main) {
            System.out.println("MainExample with Camel is now started!");
        }

        @Override
        public void beforeStop(MainSupport main) {
            System.out.println("MainExample with Camel is now being stopped!");
        }
    }
}

2.14. OnCompletion

Overview

The OnCompletion DSL name is used to define an action that is to take place when a Unit of Work is completed. A Unit of Work is a Camel concept that encompasses an entire exchange. See Section 34.1, “Exchanges”. The onCompletion command has the following features:

  • The scope of the OnCompletion command can be global or per route. A route scope overrides global scope.
  • OnCompletion can be configured to be triggered on success for failure.
  • The onWhen predicate can be used to only trigger the onCompletion in certain situations.
  • You can define whether or not to use a thread pool, though the default is no thread pool.

Route Only Scope for onCompletion

When an onCompletion DSL is specified on an exchange, Camel spins off a new thread. This allows the original thread to continue without interference from the onCompletion task. A route will only support one onCompletion. In the following example, the onCompletion is triggered whether the exchange completes with success or failure. This is the default action.

from("direct:start")
     .onCompletion()
         // This route is invoked when the original route is complete.
         // This is similar to a completion callback.
         .to("log:sync")
         .to("mock:sync")
     // Must use end to denote the end of the onCompletion route.
     .end()
     // here the original route contiues
     .process(new MyProcessor())
     .to("mock:result");

For XML the format is as follows:

<route>
    <from uri="direct:start"/>
    <!-- This onCompletion block is executed when the exchange is done being routed. -->
    <!-- This callback is always triggered even if the exchange fails. -->
    <onCompletion>
        <!-- This is similar to an after completion callback. -->
        <to uri="log:sync"/>
        <to uri="mock:sync"/>
    </onCompletion>
    <process ref="myProcessor"/>
    <to uri="mock:result"/>
</route>

To trigger the onCompletion on failure, the onFailureOnly parameter can be used. Similarly, to trigger the onCompletion on success, use the onCompleteOnly parameter.

from("direct:start")
     // Here onCompletion is qualified to invoke only when the exchange fails (exception or FAULT body).
     .onCompletion().onFailureOnly()
         .to("log:sync")
         .to("mock:sync")
     // Must use end to denote the end of the onCompletion route.
     .end()
     // here the original route continues
     .process(new MyProcessor())
     .to("mock:result");

For XML, onFailureOnly and onCompleteOnly are expressed as booleans on the onCompletion tag:

<route>
    <from uri="direct:start"/>
    <!-- this onCompletion block will only be executed when the exchange is done being routed -->
    <!-- this callback is only triggered when the exchange failed, as we have onFailure=true -->
    <onCompletion onFailureOnly="true">
        <to uri="log:sync"/>
        <to uri="mock:sync"/>
    </onCompletion>
    <process ref="myProcessor"/>
    <to uri="mock:result"/>
</route>

Global Scope for onCompletion

To define onCompletion for more than just one route:

// define a global on completion that is invoked when the exchange is complete
 onCompletion().to("log:global").to("mock:sync");

 from("direct:start")
     .process(new MyProcessor())
     .to("mock:result");

Using onWhen

To trigger the onCompletion under certain circumstances, use the onWhen predicate. The following example will trigger the onCompletion when the body of the message contains the word Hello:

/from("direct:start")
     .onCompletion().onWhen(body().contains("Hello"))
         // this route is only invoked when the original route is complete as a kind
         // of completion callback. And also only if the onWhen predicate is true
         .to("log:sync")
         .to("mock:sync")
     // must use end to denote the end of the onCompletion route
     .end()
     // here the original route contiues
     .to("log:original")
     .to("mock:result");

Using onCompletion with or without a thread pool

As of Camel 2.14, onCompletion will not use a thread pool by default. To force the use of a thread pool, either set an executorService or set parallelProcessing to true. For example, in Java DSL, use the following format:

onCompletion().parallelProcessing()
     .to("mock:before")
     .delay(1000)
     .setBody(simple("OnComplete:${body}"));

For XML the format is:

<onCompletion parallelProcessing="true">
   <to uri="before"/>
   <delay><constant>1000</constant></delay>
   <setBody><simple>OnComplete:${body}<simple></setBody>
 </onCompletion>

Use the executorServiceRef option to refer to a specific thread pool:

<onCompletion executorServiceRef="myThreadPool"
   <to uri="before"/>
   <delay><constant>1000</constant></delay>
   <setBody><simple>OnComplete:${body}</simple></setBody>
 </onCompletion>>

Run onCompletion before Consumer Sends Response

onCompletion can be run in two modes:

  • AfterConsumer - The default mode which runs after the consumer is finished
  • BeforeConsumer - Runs before the consumer writes a response back to the callee. This allows onCompletion to modify the Exchange, such as adding special headers, or to log the Exchange as a response logger.

For example, to add a created by header to the response, use modeBeforeConsumer() as shown below:

.onCompletion().modeBeforeConsumer()
     .setHeader("createdBy", constant("Someone"))
 .end()

For XML, set the mode attribute to BeforeConsumer:

<onCompletion mode="BeforeConsumer">
   <setHeader headerName="createdBy">
     <constant>Someone</constant>
   </setHeader>
 </onCompletion>

2.15. Metrics

Overview

Available as of Camel 2.14

While Camel provides a lot of existing metrics integration with Codahale metrics has been added for Camel routes. This allows end users to seamless feed Camel routing information together with existing data they are gathering using Codahale metrics.

To use the Codahale metrics you will need to:

  1. Add camel-metrics component
  2. Enable route metrics in XML or Java code

Note that performance metrics are only usable if you have a way of displaying them; any kind of monitoring tooling which can integrate with JMX can be used, as the metrics are available over JMX. In addition, the actual data is 100% Codehale JSON.

Metrics Route Policy

Obtaining Codahale metrics for a single route can be accomplished by defining a MetricsRoutePolicy on a per route basis.

From Java create an instance of MetricsRoutePolicy to be assigned as the route’s policy. This is shown below:

from("file:src/data?noop=true").routePolicy(new MetricsRoutePolicy()).to("jms:incomingOrders");

From XML DSL you define a <bean> which is specified as the route’s policy; for example:

<bean id="policy" class="org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicy"/>

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route routePolicyRef="policy">
    <from uri="file:src/data?noop=true"/>
[...]

Metrics Route Policy Factory

This factory allows one to add a RoutePolicy for each route which exposes route utilization statistics using Codahale metrics. This factory can be used in Java and XML as the examples below demonstrate.

From Java you just add the factory to the CamelContext as shown below:

context.addRoutePolicyFactory(new MetricsRoutePolicyFactory());

And from XML DSL you define a <bean> as follows:

<!-- use camel-metrics route policy to gather metrics for all routes -->
<bean id="metricsRoutePolicyFactory" class="org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicyFactory"/>

From Java code you can get hold of the com.codahale.metrics.MetricRegistry from the org.apache.camel.component.metrics.routepolicy.MetricsRegistryService as shown below:

MetricRegistryService registryService = context.hasService(MetricsRegistryService.class);
if (registryService != null) {
  MetricsRegistry registry = registryService.getMetricsRegistry();
  ...
}

Options

The MetricsRoutePolicyFactory and MetricsRoutePolicy supports the following options:

Name

Default

Description

durationUnit

TimeUnit.MILLISECONDS

The unit to use for duration in the metrics reporter or when dumping the statistics as json.

jmxDomain

org.apache.camel.metrics

The JXM domain name.

metricsRegistry

 

Allow to use a shared com.codahale.metrics.MetricRegistry. If none is provided then Camel will create a shared instance used by the this CamelContext.

prettyPrint

false

Whether to use pretty print when outputting statistics in json format.

rateUnit

TimeUnit.SECONDS

The unit to use for rate in the metrics reporter or when dumping the statistics as json.

useJmx

false

Whether to report fine grained statistics to JMX by using the com.codahale.metrics.JmxReporter.

Notice that if JMX is enabled on CamelContext then a MetricsRegistryService mbean is enlisted under the services type in the JMX tree. That mbean has a single operation to output the statistics using json. Setting useJmx to true is only needed if you want fine grained mbeans per statistics type.

2.16. JMX Naming

Overview

Apache Camel allows you to customize the name of a CamelContext bean as it appears in JMX, by defining a management name pattern for it. For example, you can customize the name pattern of an XML CamelContext instance, as follows:

<camelContext id="myCamel" managementNamePattern="#name#">
    ...
</camelContext>

If you do not explicitly set a name pattern for the CamelContext bean, Apache Camel reverts to a default naming strategy.

Default naming strategy

By default, the JMX name of a CamelContext bean deployed in an OSGi bundle is equal to the OSGi symbolic name of the bundle. For example, if the OSGi symbolic name is MyCamelBundle, the JMX name would be MyCamelBundle. In cases where there is more than one CamelContext in the bundle, the JMX name is disambiguated by adding a counter value as a suffix. For example, if there are multiple Camel contexts in the MyCamelBundle bundle, the corresponding JMX MBeans are named as follows:

MyCamelBundle-1
MyCamelBundle-2
MyCamelBundle-3
...

Customizing the JMX naming strategy

One drawback of the default naming strategy is that you cannot guarantee that a given CamelContext bean will have the same JMX name between runs. If you want to have greater consistency between runs, you can control the JMX name more precisely by defining a JMX name pattern for the CamelContext instances.

Specifying a name pattern in Java

To specify a name pattern on a CamelContext in Java, call the setNamePattern method, as follows:

// Java
context.getManagementNameStrategy().setNamePattern("#name#");

Specifying a name pattern in XML

To specify a name pattern on a CamelContext in XML, set the managementNamePattern attribute on the camelContext element, as follows:

<camelContext id="myCamel" managementNamePattern="#name#">

Name pattern tokens

You can construct a JMX name pattern by mixing literal text with any of the following tokens:

Table 2.11. JMX Name Pattern Tokens

TokenDescription

#camelId#

Value of the id attribute on the CamelContext bean.

#name#

Same as #camelId#.

#counter#

An incrementing counter (starting at 1).

#bundleId#

The OSGi bundle ID of the deployed bundle (OSGi only).

#symbolicName#

The OSGi symbolic name (OSGi only).

#version#

The OSGi bundle version (OSGi only).

Examples

Here are some examples of JMX name patterns you could define using the supported tokens:

<camelContext id="fooContext" managementNamePattern="FooApplication-#name#">
    ...
</camelContext>
<camelContext id="myCamel" managementNamePattern="#bundleID#-#symbolicName#-#name#">
    ...
</camelContext>

Ambiguous names

Because the customised naming pattern overrides the default naming strategy, it is possible to define ambiguous JMX MBean names using this approach. For example:

<camelContext id="foo" managementNamePattern="SameOldSameOld"> ... </camelContext>
...
<camelContext id="bar" managementNamePattern="SameOldSameOld"> ... </camelContext>

In this case, Apache Camel would fail on start-up and report an MBean already exists exception. You should, therefore, take extra care to ensure that you do not define ambiguous name patterns.

2.17. Performance and Optimization

Message copying

The allowUseOriginalMessage option default setting is false, to cut down on copies being made of the original message when they are not needed. To enable the allowUseOriginalMessage option use the following commands:

  • Set useOriginalMessage=true on any of the error handlers or on the onException element.
  • In Java application code, set AllowUseOriginalMessage=true, then use the getOriginalMessage method.
Note

In Camel versions prior to 2.18, the default setting of allowUseOriginalMessage is true.

Chapter 3. Introducing Enterprise Integration Patterns

Abstract

The Apache Camel’s Enterprise Integration Patterns are inspired by a book of the same name written by Gregor Hohpe and Bobby Woolf. The patterns described by these authors provide an excellent toolbox for developing enterprise integration projects. In addition to providing a common language for discussing integration architectures, many of the patterns can be implemented directly using Apache Camel’s programming interfaces and XML configuration.

3.1. Overview of the Patterns

Enterprise Integration Patterns book

Apache Camel supports most of the patterns from the book, Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf.

Messaging systems

The messaging systems patterns, shown in Table 3.1, “Messaging Systems”, introduce the fundamental concepts and components that make up a messaging system.

Table 3.1. Messaging Systems

IconNameUse Case

Message icon

Figure 5.1, “Message Pattern”

How can two applications connected by a message channel exchange a piece of information?

Message channel icon

Figure 5.2, “Message Channel Pattern”

How does one application communicate with another application using messaging?

Message endpoint icon

Figure 5.3, “Message Endpoint Pattern”

How does an application connect to a messaging channel to send and receive messages?

Pipes and filters icon

Figure 5.4, “Pipes and Filters Pattern”

How can we perform complex processing on a message while still maintaining independence and flexibility?

Message router icons

Figure 5.7, “Message Router Pattern”

How can you decouple individual processing steps so that messages can be passed to different filters depending on a set of defined conditions?

Message translator icon

Figure 5.8, “Message Translator Pattern”

How do systems using different data formats communicate with each other using messaging?

Messaging channels

A messaging channel is the basic component used for connecting the participants in a messaging system. The patterns in Table 3.2, “Messaging Channels” describe the different kinds of messaging channels available.

Table 3.2. Messaging Channels

IconNameUse Case

Point to point icon

Figure 6.1, “Point to Point Channel Pattern”

How can the caller be sure that exactly one receiver will receive the document or will perform the call?

Publish subscribe icon

Figure 6.2, “Publish Subscribe Channel Pattern”

How can the sender broadcast an event to all interested receivers?

Dead letter icon

Figure 6.3, “Dead Letter Channel Pattern”

What will the messaging system do with a message it cannot deliver?

Guaranteed delivery icon

Figure 6.4, “Guaranteed Delivery Pattern”

How does the sender make sure that a message will be delivered, even if the messaging system fails?

Message bus icon

Figure 6.5, “Message Bus Pattern”

What is an architecture that enables separate, decoupled applications to work together, such that one or more of the applications can be added or removed without affecting the others?

Message construction

The message construction patterns, shown in Table 3.3, “Message Construction”, describe the various forms and functions of the messages that pass through the system.

Table 3.3. Message Construction

IconNameUse Case

Correlation identifier icon

the section called “Overview”

How does a requestor identify the request that generated the received reply?

Return address icon

Section 7.3, “Return Address”

How does a replier know where to send the reply?

Message routing

The message routing patterns, shown in Table 3.4, “Message Routing”, describe various ways of linking message channels together, including various algorithms that can be applied to the message stream (without modifying the body of the message).

Table 3.4. Message Routing

IconNameUse Case

Content based router icon

Section 8.1, “Content-Based Router”

How do we handle a situation where the implementation of a single logical function (for example, inventory check) is spread across multiple physical systems?

Message filter icon

Section 8.2, “Message Filter”

How does a component avoid receiving uninteresting messages?

Recipient List icon

Section 8.3, “Recipient List”

How do we route a message to a list of dynamically specified recipients?

Splitter icon

Section 8.4, “Splitter”

How can we process a message if it contains multiple elements, each of which might have to be processed in a different way?

Aggregator icon

Section 8.5, “Aggregator”

How do we combine the results of individual, but related messages so that they can be processed as a whole?

Resequencer icon

Section 8.6, “Resequencer”

How can we get a stream of related, but out-of-sequence, messages back into the correct order?

distribution aggregate icon

Section 8.14, “Composed Message Processor”

How can you maintain the overall message flow when processing a message consisting of multiple elements, each of which may require different processing?

 

Section 8.15, “Scatter-Gather”

How do you maintain the overall message flow when a message needs to be sent to multiple recipients, each of which may send a reply?

Routing slip icon

Section 8.7, “Routing Slip”

How do we route a message consecutively through a series of processing steps when the sequence of steps is not known at design-time, and might vary for each message?

 

Section 8.8, “Throttler”

How can I throttle messages to ensure that a specific endpoint does not get overloaded, or that we don’t exceed an agreed SLA with some external service?

 

Section 8.9, “Delayer”

How can I delay the sending of a message?

 

Section 8.10, “Load Balancer”

How can I balance load across a number of endpoints?

 

Section 8.11, “Hystrix”

How can I use a Hystrix circuit breaker when calling an external service? New in Camel 2.18.

 

Section 8.12, “Service Call”

How can I call a remote service in a distributed system by looking up the service in a registry? New in Camel 2.18.

 

Section 8.13, “Multicast”

How can I route a message to a number of endpoints at the same time?

 

Section 8.16, “Loop”

How can I repeat processing a message in a loop?

 

Section 8.17, “Sampling”

How can I sample one message out of many in a given period to avoid overloading a ownstream route?

Message transformation

The message transformation patterns, shown in Table 3.5, “Message Transformation”, describe how to modify the contents of messages for various purposes.

Table 3.5. Message Transformation

IconNameUse Case

Content enricher icon

Section 10.1, “Content Enricher”

How do I communicate with another system if the message originator does not have all required data items?

Content filter icon

Section 10.2, “Content Filter”

How do you simplify dealing with a large message, when you are interested in only a few data items?

store in library icon

Section 10.4, “Claim Check EIP”

How can we reduce the data volume of messages sent across the system without sacrificing information content?

Normalizer icon

Section 10.3, “Normalizer”

How do you process messages that are semantically equivalent, but arrive in a different format?

 

Section 10.5, “Sort”

How can I sort the body of a message?

Messaging endpoints

A messaging endpoint denotes the point of contact between a messaging channel and an application. The messaging endpoint patterns, shown in Table 3.6, “Messaging Endpoints”, describe various features and qualities of service that can be configured on an endpoint.

Table 3.6. Messaging Endpoints

IconNameUse Case
 

Section 11.1, “Messaging Mapper”

How do you move data between domain objects and the messaging infrastructure while keeping the two independent of each other?

Event driven icon

Section 11.2, “Event Driven Consumer”

How can an application automatically consume messages as they become available?

Polling consumer icon

Section 11.3, “Polling Consumer”

How can an application consume a message when the application is ready?

Competing consumers icon

Section 11.4, “Competing Consumers”

How can a messaging client process multiple messages concurrently?

Message dispatcher icon

Section 11.5, “Message Dispatcher”

How can multiple consumers on a single channel coordinate their message processing?

Selective consumer icon

Section 11.6, “Selective Consumer”

How can a message consumer select which messages it wants to receive?

Durable subscriber icon

Section 11.7, “Durable Subscriber”

How can a subscriber avoid missing messages when it’s not listening for them?

 

Section 11.8, “Idempotent Consumer”

How can a message receiver deal with duplicate messages?

Transactional client icon

Section 11.9, “Transactional Client”

How can a client control its transactions with the messaging system?

Messaging gateway icon

Section 11.10, “Messaging Gateway”

How do you encapsulate access to the messaging system from the rest of the application?

Service activator icon

Section 11.11, “Service Activator”

How can an application design a service to be invoked by various messaging technologies as well as by non-messaging techniques?

System management

The system management patterns, shown in Table 3.7, “System Management”, describe how to monitor, test, and administer a messaging system.

Table 3.7. System Management

IconNameUse Case

Wire tap icon

Chapter 12, System Management

How do you inspect messages that travel on a point-to-point channel?

Chapter 4. Defining REST Services

Abstract

Apache Camel supports multiple approaches to defining REST services. In particular, Apache Camel provides the REST DSL (Domain Specific Language), which is a simple but powerful fluent API that can be layered over any REST component and provides integration with Swagger.

4.1. Overview of REST in Camel

Overview

Apache Camel provides many different approaches and components for defining REST services in your Camel applications. This section provides a quick overview of these different approaches and components, so that you can decide which implementation and API best suits your requirements.

What is REST?

Representational State Transfer (REST) is an architecture for distributed applications that centers around the transmission of data over HTTP, using only the four basic HTTP verbs: GET, POST, PUT, and DELETE.

In contrast to a protocol such as SOAP, which treats HTTP as a mere transport protocol for SOAP messages, the REST architecture exploits HTTP directly. The key insight is that the HTTP protocol itself, augmented by a few simple conventions, is eminently suitable to serve as the framework for distributed applications.

A sample REST invocation

Because the REST architecture is built around the standard HTTP verbs, in many cases you can use a regular browser as a REST client. For example, to invoke a simple Hello World REST service running on the host and port, localhost:9091, you could navigate to a URL like the following in your browser:

http://localhost:9091/say/hello/Garp

The Hello World REST service might then return a response string, such as:

Hello Garp

Which gets displayed in your browser window. The ease with which you can invoke REST services, using nothing more than a standard browser (or the curl command-line utility), is one of the many reasons why the REST protocol has rapidly gained popularity.

REST wrapper layers

The following REST wrapper layers offer a simplified syntax for defining REST services and can be layered on top of different REST implementations:

REST DSL

The REST DSL (in camel-core) is a facade or wrapper layer that provides a simplified builder API for defining REST services. The REST DSL does not itself provide a REST implementation: it must be combined with an underlying REST implementation. For example, the following Java code shows how to define a simple Hello World service using the REST DSL:

rest("/say")
    .get("/hello/{name}").route().transform().simple("Hello ${header.name}");

For more details, see Section 4.2, “Defining Services with REST DSL”.

Rest component

The Rest component (in camel-core) is a wrapper layer that enables you to define REST services using a URI syntax. Like the REST DSL, the Rest component does not itself provide a REST implementation. It must be combined with an underlying REST implementation.

If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is camel-http.

Note

The ability to automatically discover which HTTP component to use is new in Camel 2.18. It is not available in Camel 2.17.

The following Java code shows how to define a simple Hello World service using the camel-rest component:

from("rest:get:say:/hello/{name}").transform().simple("Hello ${header.name}");

REST implementations

Apache Camel provides several different REST implementations, through the following components:

Spark-Rest component

The Spark-Rest component (in camel-spark-rest) is a REST implementation that enables you to define REST services using a URI syntax. The Spark framework itself is a Java API, which is loosely based on the Sinatra framework (a Python API). For example, the following Java code shows how to define a simple Hello World service using the Spark-Rest component:

from("spark-rest:get:/say/hello/:name").transform().simple("Hello ${header.name}");

Notice that, in contrast to the Rest component, the syntax for a variable in the URI is :name instead of {name}.

Note

The Spark-Rest component requires Java 8.

Restlet component

The Restlet component (in camel-restlet) is a REST implementation that can, in principle, be layered above different transport protocols (although this component is only tested against the HTTP protocol). This component also provides an integration with the Restlet Framework, which is a commercial framework for developing REST services in Java. For example, the following Java code shows how to define a simple Hello World service using the Restlet component:

from("restlet:http://0.0.0.0:9091/say/hello/{name}?restletMethod=get")
    .transform().simple("Hello ${header.name}");

For more details, see Restlet in the Apache Camel Component Reference Guide.

Servlet component

The Servlet component (in camel-servlet) is a component that binds a Java servlet to a Camel route. In other words, the Servlet component enables you to package and deploy a Camel route as if it was a standard Java servlet. The Servlet component is therefore particularly useful, if you need to deploy a Camel route inside a servlet container (for example, into an Apache Tomcat HTTP server or into a JBoss Enterprise Application Platform container).

The Servlet component on its own, however, does not provide any convenient REST API for defining REST services. The easiest way to use the Servlet component, therefore, is to combine it with the REST DSL, so that you can define REST services with a user-friendly API.

For more details, see Servlet in the Apache Camel Component Reference Guide.

JAX-RS REST implementation

JAX-RS (Java API for RESTful Web Services) is a framework for binding REST requests to Java objects, where the Java classes must be decorated with JAX-RS annotations in order to define the binding. The JAX-RS framework is relatively mature and provides a sophisticated framework for developing REST services, but it is also somewhat complex to program.

The JAX-RS integration with Apache Camel is implemented by the CXFRS component, which is layered over Apache CXF. In outline, JAX-RS binds a REST request to a Java class using the following annotations (where this is only an incomplete sample of the many available annotations):

@Path
Annotation that can map a context path to a Java class or map a sub-path to a particular Java method.
@GET, @POST, @PUT, @DELETE
Annotations that map a HTTP method to a Java method.
@PathParam
Annotation that either maps a URI parameter to a Java method argument, or injects a URI parameter into a field.
@QueryParam
Annotation that either maps a query parameter to a Java method argument, or injects a query parameter into a field.

The body of a REST request or REST response is normally expected to be in JAXB (XML) data format. But Apache CXF also supports conversion of JSON format to JAXB format, so that JSON messages can also be parsed.

For more details, see CXFRS in the Apache Camel Component Reference Guide and Apache CXF Development Guide.

Note

The CXFRS component is not integrated with the REST DSL.

4.2. Defining Services with REST DSL

REST DSL is a facade

The REST DSL is effectively a facade that provides a simplified syntax for defining REST services in a Java DSL or an XML DSL (Domain Specific Language). REST DSL does not actually provide the REST implementation, it is just a wrapper around an existing REST implementation (of which there are several in Apache Camel).

Advantages of the REST DSL

The REST DSL wrapper layer offers the following advantages:

  • A modern easy-to-use syntax for defining REST services.
  • Compatible with multiple different Apache Camel components.
  • Swagger integration (through the camel-swagger component).

Components that integrate with REST DSL

Because the REST DSL is not an actual REST implementation, one of the first things you need to do is to choose a Camel component to provide the underlying implementation. The following Camel components are currently integrated with the REST DSL:

Note

The Rest component (part of camel-core) is not a REST implementation. Like the REST DSL, the Rest component is a facade, providing a simplified syntax to define REST services using a URI syntax. The Rest component also requires an underlying REST implementation.

Configuring REST DSL to use a REST implementation

To specify the REST implementation, you use either the restConfiguration() builder (in Java DSL) or the restConfiguration element (in XML DSL). For example, to configure REST DSL to use the Spark-Rest component, you would use a builder expression like the following in the Java DSL:

restConfiguration().component("spark-rest").port(9091);

And you would use an element like the following (as a child of camelContext) in the XML DSL:

<restConfiguration component="spark-rest" port="9091"/>

Syntax

The Java DSL syntax for defining a REST service is as follows:

rest("BasePath").Option().
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    ...
    .Verb("Path").Option().[to() | route().CamelRoute];

Where CamelRoute is an optional embedded Camel route (defined using the standard Java DSL syntax for routes).

The REST service definition starts with the rest() keyword, followed by one or more verb clauses that handle specific URL path segments. The HTTP verb can be one of get(), head(), put(), post(), delete(), patch() or verb(). Each verb clause can use either of the following syntaxes:

  • Verb clause ending in to() keyword. For example:

    get("...").Option()+.to("...")
  • Verb clause ending in route() keyword (for embedding a Camel route). For example:

    get("...").Option()+.route("...").CamelRoute.endRest()

REST DSL with Java

In Java, to define a service with the REST DSL, put the REST definition into the body of a RouteBuilder.configure() method, just like you do for regular Apache Camel routes. For example, to define a simple Hello World service using the REST DSL with the Spark-Rest component, define the following Java code:

restConfiguration().component("spark-rest").port(9091);

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

from("direct:hello")
    .transform().constant("Hello World");
from("direct:bye")
    .transform().constant("Bye World");

The preceding example features three different kinds of builder:

restConfiguration()
Configures the REST DSL to use a specific REST implementation (Spark-Rest).
rest()
Defines a service using the REST DSL. Each of the verb clauses are terminated by a to() keyword, which forwards the incoming message to a direct endpoint (the direct component splices routes together within the same application).
from()
Defines a regular Camel route.

REST DSL with XML

In XML, to define a service with the XML DSL, define a rest element as a child of the camelContext element. For example, to define a simple Hello World service using the REST DSL with the Spark-Rest component, define the following XML code (in Blueprint):

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <restConfiguration component="spark-rest" port="9091"/>

  <rest path="/say">
    <get uri="/hello">
      <to uri="direct:hello"/>
    </get>
    <get uri="/bye">
      <to uri="direct:bye"/>
    </get>
  </rest>

  <route>
    <from uri="direct:hello"/>
    <transform>
      <constant>Hello World</constant>
    </transform>
  </route>
  <route>
    <from uri="direct:bye"/>
    <transform>
      <constant>Bye World</constant>
    </transform>
  </route>
</camelContext>

Specifying a base path

The rest() keyword (Java DSL) or the path attribute of the rest element (XML DSL) allows you to define a base path, which is then prefixed to the paths in all of the verb clauses. For example, given the following snippet of Java DSL:

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

Or given the following snippet of XML DSL:

<rest path="/say">
  <get uri="/hello">
    <to uri="direct:hello"/>
  </get>
  <get uri="/bye" consumes="application/json">
    <to uri="direct:bye"/>
  </get>
</rest>

The REST DSL builder gives you the following URL mappings:

/say/hello
/say/bye

The base path is optional. If you prefer, you could (less elegantly) specify the full path in each of the verb clauses:

rest()
    .get("/say/hello").to("direct:hello")
    .get("/say/bye").to("direct:bye");

Using Dynamic To

The REST DSL supports the toD dynamic to parameter. Use this parameter to specify URIs.

For example, in JMS a dynamic endpoint URI could be defined in the following way:

public void configure() throws Exception {
   rest("/say")
     .get("/hello/{language}").toD("jms:queue:hello-${header.language}");
}

In XML DSL, the same details would look like this:

<rest uri="/say">
  <get uri="/hello//{language}">
    <toD uri="jms:queue:hello-${header.language}"/>
  </get>
<rest>

For more information about the toD dynamic to parameter, see the section called “Dynamic To”.

URI templates

In a verb argument, you can specify a URI template, which enables you to capture specific path segments in named properties (which are then mapped to Camel message headers). For example, if you would like to personalize the Hello World application so that it greets the caller by name, you could define a REST service like the following:

rest("/say")
    .get("/hello/{name}").to("direct:hello")
    .get("/bye/{name}").to("direct:bye");

from("direct:hello")
    .transform().simple("Hello ${header.name}");
from("direct:bye")
    .transform().simple("Bye ${header.name}");

The URI template captures the text of the {name} path segment and copies this captured text into the name message header. If you invoke the service by sending a GET HTTP Request with the URL ending in /say/hello/Joe, the HTTP Response is Hello Joe.

Embedded route syntax

Instead of terminating a verb clause with the to() keyword (Java DSL) or the to element (XML DSL), you have the option of embedding an Apache Camel route directly into the REST DSL, using the route() keyword (Java DSL) or the route element (XML DSL). The route() keyword enables you to embed a route into a verb clause, with the following syntax:

RESTVerbClause.route("...").CamelRoute.endRest()

Where the endRest() keyword (Java DSL only) is a necessary punctuation mark that enables you to separate the verb clauses (when there is more than one verb clause in the rest() builder).

For example, you could refactor the Hello World example to use embedded Camel routes, as follows in Java DSL:

rest("/say")
    .get("/hello").route().transform().constant("Hello World").endRest()
    .get("/bye").route().transform().constant("Bye World");

And as follows in XML DSL:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <get uri="/hello">
      <route>
        <transform>
          <constant>Hello World</constant>
        </transform>
      </route>
    </get>
    <get uri="/bye">
      <route>
        <transform>
          <constant>Bye World</constant>
        </transform>
      </route>
    </get>
  </rest>
</camelContext>
Note

If you define any exception clauses (using onException()) or interceptors (using intercept()) in the current CamelContext, these exception clauses and interceptors are also active in the embedded routes.

REST DSL and HTTP transport component

If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is camel-http.

Specifying the content type of requests and responses

You can filter the content type of HTTP requests and responses using the consumes() and produces() options in Java, or the consumes and produces attributes in XML. For example, some common content types (officially known as Internet media types) are the following:

  • text/plain
  • text/html
  • text/xml
  • application/json
  • application/xml

The content type is specified as an option on a verb clause in the REST DSL. For example, to restrict a verb clause to accept only text/plain HTTP requests, and to send only text/html HTTP responses, you would use Java code like the following:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");

And in XML, you can set the consumes and produces attributes, as follows:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/email">
    <post uri="/to/{recipient}" consumes="text/plain" produces="text/html">
      <to "direct:foo"/>
    </get>
  </rest>
</camelContext>

You can also specify the argument to consumes() or produces() as a comma-separated list. For example, consumes("text/plain, application/json").

Additional HTTP methods

Some HTTP server implementations support additional HTTP methods, which are not provided by the standard set of verbs in the REST DSL, get(), head(), put(), post(), delete(), patch(). To access additional HTTP methods, you can use the generic keyword, verb(), in Java DSL and the generic element, verb, in XML DSL.

For example, to implement the TRACE HTTP method in Java:

rest("/say")
    .verb("TRACE", "/hello").route().transform();

Where transform() copies the body of the IN message to the body of the OUT message, thus echoing the HTTP request.

To implement the TRACE HTTP method in XML:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <verb uri="/hello" method="TRACE">
      <route>
        <transform/>
      </route>
    </get>
</camelContext>

Defining custom HTTP error messages

If your REST service needs to send an error message as its response, you can define a custom HTTP error message as follows:

  1. Specify the HTTP error code by setting the Exchange.HTTP_RESPONSE_CODE header key to the error code value (for example, 400, 404, and so on). This setting indicates to the REST DSL that you want to send an error message reply, instead of a regular response.
  2. Populate the message body with your custom error message.
  3. Set the Content-Type header, if required.
  4. If your REST service is configured to marshal to and from Java objects (that is, bindingMode is enabled), you should ensure that the skipBindingOnErrorCode option is enabled (which it is, by default). This is to ensure that the REST DSL does not attempt to unmarshal the message body when sending the response.

    For more details about object binding, see Section 4.3, “Marshalling to and from Java Objects”.

The following Java example shows how to define a custom error message:

// Java
// Configure the REST DSL, with JSON binding mode
restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.json);

// Define the service with REST DSL
rest("/users/")
    .post("lives").type(UserPojo.class).outType(CountryPojo.class)
        .route()
            .choice()
                .when().simple("${body.id} < 100")
                    .bean(new UserErrorService(), "idTooLowError")
                .otherwise()
                    .bean(new UserService(), "livesWhere");

In this example, if the input ID is a number less than 100, we return a custom error message, using the UserErrorService bean, which is implemented as follows:

// Java
public class UserErrorService {
    public void idTooLowError(Exchange exchange) {
        exchange.getIn().setBody("id value is too low");
        exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain");
        exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
    }
}

In the UserErrorService bean we define the custom error message and set the HTTP error code to 400.

Parameter Default Values

Default values can be specified for the headers of an incoming Camel message.

You can specify a default value by using a key word such as verbose on the query parameter. For example, in the code below, the default value is false. This means that if no other value is provided for a header with the verbose key, false will be inserted as a default.

rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders")
      .param()
	.name("verbose")
	.type(RestParamType.query)
	.defaultValue("false")
	.description("Verbose order details")
      .endParam()
        .to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");

Wrapping a JsonParserException in a custom HTTP error message

A common case where you might want to return a custom error message is in order to wrap a JsonParserException exception. For example, you can conveniently exploit the Camel exception handling mechanism to create a custom HTTP error message, with HTTP error code 400, as follows:

// Java
onException(JsonParseException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
    .setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
    .setBody().constant("Invalid json data");

REST DSL options

In general, REST DSL options can be applied either directly to the base part of the service definition (that is, immediately following rest()), as follows:

rest("/email").consumes("text/plain").produces("text/html")
    .post("/to/{recipient}").to("direct:foo")
    .get("/for/{username}").to("direct:bar");

In which case the specified options apply to all of the subordinate verb clauses. Or the options can be applied to each individual verb clause, as follows:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo")
    .get("/for/{username}").consumes("text/plain").produces("text/html").to("direct:bar");

In which case the specified options apply only to the relevant verb clause, overriding any settings from the base part.

Table 4.1, “REST DSL Options” summarizes the options supported by the REST DSL.

Table 4.1. REST DSL Options

Java DSLXML DSLDescription

bindingMode()

@bindingMode

Specifies the binding mode, which can be used to marshal incoming messages to Java objects (and, optionally, unmarshal Java objects to outgoing messages). Can have the following values: off (default), auto, json, xml, json_xml.

consumes()

@consumes

Restricts the verb clause to accept only the specified Internet media type (MIME type) in a HTTP Request. Typical values are: text/plain, text/http, text/xml, application/json, application/xml.

customId()

@customId

Defines a custom ID for JMX management.

description()

description

Document the REST service or verb clause. Useful for JMX management and tooling.

enableCORS()

@enableCORS

If true, enables CORS (cross-origin resource sharing) headers in the HTTP response. Default is false.

id()

@id

Defines a unique ID for the REST service, which is useful to define for JMX management and other tooling.

method()

@method

Specifies the HTTP method processed by this verb clause. Usually used in conjunction with the generic verb() keyword.

outType()

@outType

When object binding is enabled (that is, when bindingMode option is enabled), this option specifies the Java type that represents a HTTP Response message.

produces()

produces

Restricts the verb clause to produce only the specified Internet media type (MIME type) in a HTTP Response. Typical values are: text/plain, text/http, text/xml, application/json, application/xml.

type()

@type

When object binding is enabled (that is, when bindingMode option is enabled), this option specifies the Java type that represents a HTTP Request message.

VerbURIArgument

@uri

Specifies a path segment or URI template as an argument to a verb. For example, get(VerbURIArgument).

BasePathArgument

@path

Specifies the base path in the rest() keyword (Java DSL) or in the rest element (XML DSL).

4.3. Marshalling to and from Java Objects

Marshalling Java objects for transmission over HTTP

One of the most common ways to use the REST protocol is to transmit the contents of a Java bean in the message body. In order for this to work, you need to have a mechanism for marshalling the Java object to and from a suitable data format. The following data formats, which are suitable for encoding Java objects, are supported by the REST DSL:

JSON

JSON (JavaScript object notation) is a lightweight data format that can easily be mapped to and from Java objects. The JSON syntax is compact, lightly typed, and easy for humans to read and write. For all of these reasons, JSON has become popular as a message format for REST services.

For example, the following JSON code could represent a User bean with two property fields, id and name:

{
    "id" : 1234,
    "name" : "Jane Doe"
}
JAXB

JAXB (Java Architecture for XML Binding) is an XML-based data format that can easily be mapped to and from Java objects. In order to marshal the XML to a Java object, you must also annotate the Java class that you want to use.

For example, the following JAXB code could represent a User bean with two property fields, id and name:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User>
  <Id>1234</Id>
  <Name>Jane Doe</Name>
</User>
Note

From Camel 2.17.0, JAXB data format and type converter supports the conversion from XML to POJO for classes, that use ObjectFactory instead of XmlRootElement. Also, the camel context should include the CamelJaxbObjectFactory property with value true. However, due to optimization the default value is false.

Integration of JSON and JAXB with the REST DSL

You could, of course, write the required code to convert the message body to and from a Java object yourself. But the REST DSL offers the convenience of performing this conversion automatically. In particular, the integration of JSON and JAXB with the REST DSL offers the following advantages:

  • Marshalling to and from Java objects is performed automatically (given the appropriate configuration).
  • The REST DSL can automatically detect the data format (either JSON or JAXB) and perform the appropriate conversion.
  • The REST DSL provides an abstraction layer, so that the code you write is not specific to a particular JSON or JAXB implementation. So you can switch the implementation later on, with minimum impact to your application code.

Supported data format components

Apache Camel provides a number of different implementations of the JSON and JAXB data formats. The following data formats are currently supported by the REST DSL:

  • JSON

    • Jackson data format (camel-jackson) (default)
    • GSon data format (camel-gson)
    • XStream data format (camel-xstream)
  • JAXB

    • JAXB data format (camel-jaxb)

How to enable object marshalling

To enable object marshalling in the REST DSL, observe the following points:

  1. Enable binding mode, by setting the bindingMode option (there are several levels at which you can set the binding mode — for details, see the section called “Configuring the binding mode”).
  2. Specify the Java type to convert to (or from), on the incoming message with the type option (required), and on the outgoing message with the outType option (optional).
  3. If you want to convert your Java object to and from the JAXB data format, you must remember to annotate the Java class with the appropriate JAXB annotations.
  4. Specify the underlying data format implementation (or implementations), using the jsonDataFormat option and/or the xmlDataFormat option (which can be specified on the restConfiguration builder).
  5. If your route provides a return value in JAXB format, you are normally expected to set the Out message of the exchange body to be an instance of a class with JAXB annotations (a JAXB element). If you prefer to provide the JAXB return value directly in XML format, however, set the dataFormatProperty with the key, xml.out.mustBeJAXBElement, to false (which can be specified on the restConfiguration builder). For example, in the XML DSL syntax:

    <restConfiguration ...>
      <dataFormatProperty key="xml.out.mustBeJAXBElement"
                          value="false"/>
      ...
    </restConfiguration>
  6. Add the required dependencies to your project build file. For example, if you are using the Maven build system and you are using the Jackson data format, you would add the following dependency to your Maven POM file:

    <?xml version="1.0" encoding="UTF-8"?>
    <project ...>
      ...
      <dependencies>
        ...
        <!-- use for json binding --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> </dependency>
        ...
      </dependencies>
    </project>
  7. When deploying your application to the OSGi container, remember to install the requisite feature for your chosen data format. For example, if you are using the Jackson data format (the default), you would install the camel-jackson feature, by entering the following Karaf console command:

    JBossFuse:karaf@root> features:install camel-jackson

    Alternatively, if you are deploying into a Fabric environment, you would add the feature to a Fabric profile. For example, if you are using the profile, MyRestProfile, you could add the feature by entering the following console command:

    JBossFuse:karaf@root> fabric:profile-edit --features camel-jackson MyRestProfile

Configuring the binding mode

The bindingMode option is off by default, so you must configure it explicitly, in order to enable marshalling of Java objects. TABLE shows the list of supported binding modes.

Note

From Camel 2.16.3 onwards the binding from POJO to JSon/JAXB will only happen if the content-type header includes json or xml. This allows you to specify a custom content-type if the message body should not attempt to be marshalled using the binding. This is useful if, for example, the message body is a custom binary payload.

Table 4.2. REST DSL BInding Modes

Binding ModeDescription

off

Binding is turned off (default).

auto

Binding is enabled for JSON and/or XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are not required to enable both kinds of data format, however: either a JSON implementation, an XML implementation, or both can be provided on the classpath.

json

Binding is enabled for JSON only. A JSON implementation must be provided on the classpath (by default, Camel tries to enable the camel-jackson implementation).

xml

Binding is enabled for XML only. An XML implementation must be provided on the classpath (by default, Camel tries to enable the camel-jaxb implementation).

json_xml

Binding is enabled for both JSON and XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are required to provide both kinds of data format on the classpath.

In Java, these binding mode values are represented as instances of the following enum type:

org.apache.camel.model.rest.RestBindingMode

There are several different levels at which you can set the bindingMode, as follows:

REST DSL configuration

You can set the bindingMode option from the restConfiguration builder, as follows:

restConfiguration().component("servlet").port(8181).bindingMode(RestBindingMode.json);
Service definition base part

You can set the bindingMode option immediately following the rest() keyword (before the verb clauses), as follows:

rest("/user").bindingMode(RestBindingMode.json).get("/{id}").VerbClause
Verb clause

You can set the bindingMode option in a verb clause, as follows:

rest("/user")
    .get("/{id}").bindingMode(RestBindingMode.json).to("...");

Example

For a complete code example, showing how to use the REST DSL, using the Servlet component as the REST implementation, take a look at the Apache Camel camel-example-servlet-rest-blueprint example. You can find this example by installing the standalone Apache Camel distribution, apache-camel-2.21.0.fuse-750033-redhat-00001.zip, which is provided in the extras/ subdirectory of your Fuse installation.

After installing the standalone Apache Camel distribution, you can find the example code under the following directory:

ApacheCamelInstallDir/examples/camel-example-servlet-rest-blueprint

Configure the Servlet component as the REST implementation

In the camel-example-servlet-rest-blueprint example, the underlying implementation of the REST DSL is provided by the Servlet component. The Servlet component is configured in the Blueprint XML file, as shown in Example 4.1, “Configure Servlet Component for REST DSL”.

Example 4.1. Configure Servlet Component for REST DSL

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>

  <!-- to setup camel servlet with OSGi HttpService -->
  <reference id="httpService" interface="org.osgi.service.http.HttpService"/>

  <bean class="org.apache.camel.component.servlet.osgi.OsgiServletRegisterer"
        init-method="register"
        destroy-method="unregister">
    <property name="alias" value="/camel-example-servlet-rest-blueprint/rest"/>
    <property name="httpService" ref="httpService"/>
    <property name="servlet" ref="camelServlet"/>
  </bean>

  <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

To configure the Servlet component with REST DSL, you need to configure a stack consisting of the following three layers:

REST DSL layer
The REST DSL layer is configured by the restConfiguration element, which integrates with the Servlet component by setting the component attribute to the value, servlet.
Servlet component layer
The Servlet component layer is implemented as an instance of the class, CamelHttpTransportServlet, where the example instance has the bean ID, camelServlet.
HTTP container layer

The Servlet component must be deployed into a HTTP container. The Karaf container is normally configured with a default HTTP container (a Jetty HTTP container), which listens for HTTP requests on the port, 8181. To deploy the Servlet component to the default Jetty container, you need to do the following:

  1. Get an OSGi reference to the org.osgi.service.http.HttpService OSGi service, where this service is a standardised OSGi interface that provides access to the default HTTP server in OSGi.
  2. Create an instance of the utility class, OsgiServletRegisterer, to register the Servlet component in the HTTP container. The OsgiServletRegisterer class is a utility that simplifies managing the lifecycle of the Servlet component. When an instance of this class is created, it automatically calls the registerServlet method on the HttpService OSGi service; and when the instance is destroyed, it automatically calls the unregister method.

Required dependencies

This example has two dependencies which are of key importance to the REST DSL, as follows:

Servlet component

Provides the underlying implementation of the REST DSL. This is specified in the Maven POM file, as follows:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-servlet</artifactId>
  <version>${camel-version}</version>
</dependency>

And before you deploy the application bundle to the OSGi container, you must install the Servlet component feature, as follows:

JBossFuse:karaf@root> features:install camel-servlet
Jackson data format

Provides the JSON data format implementation. This is specified in the Maven POM file, as follows:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jackson</artifactId>
  <version>${camel-version}</version>
</dependency>

And before you deploy the application bundle to the OSGi container, you must install the Jackson data format feature, as follows:

JBossFuse:karaf@root> features:install camel-jackson

Java type for responses

The example application passes User type objects back and forth in HTTP Request and Response messages. The User Java class is defined as shown in Example 4.2, “User Class for JSON Response”.

Example 4.2. User Class for JSON Response

// Java
package org.apache.camel.example.rest;

public class User {

    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The User class has a relatively simple representation in the JSON data format. For example, a typical instance of this class expressed in JSON format is:

{
    "id" : 1234,
    "name" : "Jane Doe"
}

Sample REST DSL route with JSON binding

The REST DSL configuration and the REST service definition for this example are shown in Example 4.3, “REST DSL Route with JSON Binding”.

Example 4.3. REST DSL Route with JSON Binding

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           ...>
  ...
  <!-- a bean for user services -->
  <bean id="userService" class="org.apache.camel.example.rest.UserService"/>

  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>

    <!-- defines the REST services using the  base path, /user -->
    <rest path="/user" consumes="application/json" produces="application/json">
      <description>User rest service</description>

      <!-- this is a rest GET to view a user with the given id -->
      <get uri="/{id}" outType="org.apache.camel.example.rest.User">
        <description>Find user by id</description>
        <to uri="bean:userService?method=getUser(${header.id})"/>
      </get>

      <!-- this is a rest PUT to create/update a user -->
      <put type="org.apache.camel.example.rest.User">
        <description>Updates or create a user</description>
        <to uri="bean:userService?method=updateUser"/>
      </put>

      <!-- this is a rest GET to find all users -->
      <get uri="/findAll" outType="org.apache.camel.example.rest.User[]">
        <description>Find all users</description>
        <to uri="bean:userService?method=listUsers"/>
      </get>

    </rest>

  </camelContext>

</blueprint>

REST operations

The REST service from Example 4.3, “REST DSL Route with JSON Binding” defines the following REST operations:

GET /camel-example-servlet-rest-blueprint/rest/user/{id}
Get the details for the user identified by {id}, where the HTTP response is returned in JSON format.
PUT /camel-example-servlet-rest-blueprint/rest/user
Create a new user, where the user details are contained in the body of the PUT message, encoded in JSON format (to match the User object type).
GET /camel-example-servlet-rest-blueprint/rest/user/findAll
Get the details for all users, where the HTTP response is returned as an array of users, in JSON format.

URLs to invoke the REST service

By inspecting the REST DSL definitions from Example 4.3, “REST DSL Route with JSON Binding”, you can piece together the URLs required to invoke each of the REST operations. For example, to invoke the first REST operation, which returns details of a user with a given ID, the URL is built up as follows:

http://localhost:8181
In restConfiguration, the protocol defaults to http and the port is set explicitly to 8181.
/camel-example-servlet-rest-blueprint/rest
Specified by the contextPath attribute of the restConfiguration element.
/user
Specified by the path attribute of the rest element.
/{id}
Specified by the uri attribute of the get verb element.

Hence, it is possible to invoke this REST operation with the curl utility, by entering the following command at the command line:

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/123

Similarly, the remaining REST operations could be invoked with curl, by entering the following sample commands:

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/findAll

curl -X PUT -d "{ \"id\": 666, \"name\": \"The devil\"}" -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user

4.4. Configuring the REST DSL

Configuring with Java

In Java, you can configure the REST DSL using the restConfiguration() builder API. For example, to configure the REST DSL to use the Servlet component as the underlying implementation:

restConfiguration().component("servlet").bindingMode("json").port("8181")
    .contextPath("/camel-example-servlet-rest-blueprint/rest");

Configuring with XML

In XML, you can configure the REST DSL using the restConfiguration element. For example, to configure the REST DSL to use the Servlet component as the underlying implementation:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    ...
    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

Configuration options

Table 4.3, “Options for Configuring REST DSL” shows options for configuring the REST DSL using the restConfiguration() builder (Java DSL) or the restConfiguration element (XML DSL).

Table 4.3. Options for Configuring REST DSL

Java DSLXML DSLDescription

component()

@component

Specifies the Camel component to use as the REST transport (for example, servlet, restlet, spark-rest, and so on). The value can either be the standard component name or the bean ID of a custom instance. If this option is not specified, Camel looks for an instance of RestConsumerFactory on the classpath or in the bean registry.

scheme()

@scheme

The protocol to use for exposing the REST service. Depends on the underlying REST implementation, but http and https are usually supported. Default is http.

host()

@host

The hostname to use for exposing the REST service.

port()

@port

The port number to use for exposing the REST service.

Note: This setting is ignored by the Servlet component, which uses the container’s standard HTTP port instead. In the case of the Apache Karaf OSGi container, the standard HTTP port is normally 8181. It is good practice to set the port value nonetheless, for the sake of JMX and tooling.

contextPath()

@contextPath

Sets a leading context path for the REST services. This can be used with components such as Servlet, where the deployed Web application is deployed using a context-path setting.

hostNameResolver()

@hostNameResolver

If a hostname is not set explicitly, this resolver determines the host for the REST service. Possible values are RestHostNameResolver.localHostName (Java DSL) or localHostName (XML DSL), which resolves to the host name format; and RestHostNameResolver.localIp (Java DSL) or localIp (XML DSL), which resolves to the dotted decimal IP address format. From Camel 2.17 RestHostNameResolver.allLocalIp can be used to resolve to all local IP addresses.

The default is localHostName up to Camel 2.16. From Camel 2.17 the default is allLocalIp.

bindingMode()

@bindingMode

Enables binding mode for JSON or XML format messages. Possible values are: off, auto, json, xml, or json_xml. Default is off.

skipBindingOnErrorCode()

@skipBindingOnErrorCode

Specifies whether to skip binding on output, if there is a custom HTTP error code header. This allows you to build custom error messages that do not bind to JSON or XML, as successful messages would otherwise do. Default is true.

enableCORS()

@enableCORS

If true, enables CORS (cross-origin resource sharing) headers in the HTTP response. Default is false.

jsonDataFormat()

@jsonDataFormat

Specifies the component that Camel uses to implement the JSON data format. Possible values are: json-jackson, json-gson, json-xstream. Default is json-jackson.

xmlDataFormat()

@xmlDataFormat

Specifies the component that Camel uses to implement the XML data format. Possible value is: jaxb. Default is jaxb.

componentProperty()

componentProperty

Enables you to set arbitrary component level properties on the underlying REST implementation.

endpointProperty()

endpointProperty

Enables you to set arbitrary endpoint level properties on the underlying REST implementation.

consumerProperty()

consumerProperty

Enables you to set arbitrary consumer endpoint properties on the underlying REST implementation.

dataFormatProperty()

dataFormatProperty

Enables you to set arbitrary properties on the underlying data format component (for example, Jackson or JAXB). From Camel 2.14.1 onwards, you can attach the following prefixes to the property keys:

  • json.in
  • json.out
  • xml.in
  • xml.out

To restrict the property setting to a specific format type (JSON or XML) and a particular message direction (IN or OUT).

corsHeaderProperty()

corsHeaders

Enables you to specify custom CORS headers, as key/value pairs.

Default CORS headers

If CORS (cross-origin resource sharing) is enabled, the following headers are set by default. You can optionally override the default settings, by invoking the corsHeaderProperty DSL command.

Table 4.4. Default CORS Headers

Header KeyHeader Value

Access-Control-Allow-Origin

\*

Access-Control-Allow-Methods

GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH

Access-Control-Allow-Headers

Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

Access-Control-Max-Age

3600

Enabling or disabling Jackson JSON features

You can enable or disable specific Jackson JSON features by configuring the following keys in the dataFormatProperty option:

  • json.in.disableFeatures
  • json.in.enableFeatures

For example, to disable Jackson’s FAIL_ON_UNKNOWN_PROPERTIES feature (which causes Jackson to fail if a JSON input has a property that cannot be mapped to a Java object):

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES");

You can disable multiple features by specifying a comma-separated list. For example:

.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE");

Here is an example that shows how to disable and enable Jackson JSON features in the Java DSL:

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
    .dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");

Here is an example that shows how to disable and enable Jackson JSON features in the XML DSL:

<restConfiguration component="jetty" host="localhost" port="9090" bindingMode="json">
  <dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"/>
  <dataFormatProperty key="json.in.enableFeatures" value="FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS"/>
</restConfiguration>

The Jackson features that can be disabled or enabled correspond to the enum IDs from the following Jackson classes

4.5. Swagger Integration

Overview

You can use a Swagger service to create API documentation for any REST-defined routes and endpoints in a CamelContext file. To do this, use the Camel REST DSL with the camel-swagger-java module, which is purely Java-based. The camel-swagger-java module creates a servlet that is integrated with the CamelContext and that pulls the information from each REST endpoint to generate the API documentation in JSON or YAML format.

If you use Maven then edit your pom.xml file to add a dependency on the camel-swagger-java component:

<dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-swagger-java</artifactId>
   <version>x.x.x</version>
   <!-- Specify the version of your camel-core module. -->
</dependency>

Configuring a CamelContext to enable Swagger

To enable the use of the Swagger API in the Camel REST DSL, invoke apiContextPath() to set the context path for the Swagger-generated API documentation. For example:

public class UserRouteBuilder extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        // Configure the Camel REST DSL to use the netty4-http component:
        restConfiguration().component("netty4-http").bindingMode(RestBindingMode.json)
            // Generate pretty print output:
            .dataFormatProperty("prettyPrint", "true")
            // Set the context path and port number that netty will use:
            .contextPath("/").port(8080)
            // Add the context path for the Swagger-generated API documentation:
            .apiContextPath("/api-doc")
                .apiProperty("api.title", "User API").apiProperty("api.version", "1.2.3")
                // Enable CORS:
                .apiProperty("cors", "true");

        // This user REST service handles only JSON files:
        rest("/user").description("User rest service")
            .consumes("application/json").produces("application/json")
            .get("/{id}").description("Find user by id").outType(User.class)
                .param().name("id").type(path).description("The id of the user to get").dataType("int").endParam()
                .to("bean:userService?method=getUser(${header.id})")
            .put().description("Updates or create a user").type(User.class)
                .param().name("body").type(body).description("The user to update or create").endParam()
                .to("bean:userService?method=updateUser")
            .get("/findAll").description("Find all users").outTypeList(User.class)
                .to("bean:userService?method=listUsers");
    }
}

Swagger module configuration options

The options described in the table below let you configure the Swagger module. Set an option as follows:

  • If you are using the camel-swagger-java module as a servlet, set an option by updating the web.xml file and specifying an init-param element for each configuration option you want to set.
  • If you are using the camel-swagger-java module from Camel REST components, set an option by invoking the appropriate RestConfigurationDefinition method, such as enableCORS(), host(), or contextPath(). Set the api.xxx options with the RestConfigurationDefinition.apiProperty() method.
OptionTypeDescription

api.contact.email

String

Email address to be used for API-related correspondence.

api.contact.name

String

Name of person or organization to contact.

api.contact.url

String

URL to a website for more contact information.

apiContextIdListing

Boolean

If your application uses more than one CamelContext object, the default behavior is to list the REST endpoints in only the current CamelContext. If you want a list of the REST endpoints in each CamelContext that is running in the JVM that is running the REST service then set this option to true. When apiContextIdListing is true then Swagger outputs the CamelContext IDs in the root path, for example, /api-docs, as a list of names in JSON format. To access the Swagger-generated documentation, append the REST context path to the CamelContext ID, for example, api-docs/myCamel. You can use the apiContextIdPattern option to filter the names in this output list.

apiContextIdPattern

String

Pattern that filters which CamelContext IDs appear in the context listing. You can specify regular expressions and use * as a wildcard. This is the same pattern matching facility as used by the Camel Intercept feature.

api.license.name

String

License name used for the API.

api.license.url

String

URL to the license used for the API.

api.path

String

Sets the path where the REST API to generate documentation for is available, for example, /api-docs. Specify a relative path. Do not specify, for example, http or https. The camel-swagger-java module calculates the absolute path at runtime in this format: protocol://host:port/context-path/api-path.

api.termsOfService

String

URL to the terms of service of the API.

api.title

String

Title of the application.

api.version

String

Version of the API. The default is 0.0.0.

base.path

String

Required. Sets the path where the REST services are available. Specify a relative path. That is, do not specify, for example, http or https. The camel-swagger-java modul calculates the absolute path at runtime in this format: protocol://host:port/context-path/base.path.

cors

Boolean

Whether to enable HTTP Access Control (CORS). This enable CORS only for viewing the REST API documentation, and not for access to the REST service. The default is false. The recommendation is to use the CorsFilter option instead, as described after this table.

host

String

Set the name of the host that the Swagger service is running on. The default is to calculate the host name based on localhost.

schemes

String

Protocol schemes to use. Separate multiple values with a comma, for example, "http,https". The default is http.

swagger.version

String

Swagger specification version. The default is 2.0.

Using the CORS filter to enable CORS support

If you use the Swagger user interface to view your REST API documentation then you probably need to enable support for HTTP Access Control (CORS). This support is required when the Swagger user interface is hosted and running on a hostname/port that is different from the hostname/port on which your REST APIs are running.

To enable support for CORS, add the RestSwaggerCorsFilter to your web.xml file. The CORS filter adds the HTTP headers that enable CORS. For example:

<!-- Enable CORS filter to allow use of Swagger UI for browsing and testing APIs. -->
<filter>
     <filter-name>RestSwaggerCorsFilter</filter-name>
     <filter-class>org.apache.camel.swagger.rest.RestSwaggerCorsFilter</filter-class>
</filter>
<filter-mapping>
     <filter-name>RestSwaggerCorsFilter</filter-name>
     <url-pattern>/api-docs/*</url-pattern>
     <url-pattern>/rest/*</url-pattern>
</filter-mapping>

The RestSwaggerCorsFilter sets the following headers for all requests:

  • Access-Control-Allow-Origin= *
  • Access-Control-Allow-Methods = GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH
  • Access-Control-Max-Age = 3600'
  • Access-Control-Allow-Headers = Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

RestSwaggerCorsFilter is a simple filter. You might need a more sophisticated filter if you need to block certain clients or set the header values differently for a given client.

Obtaining JSON or YAML output

Starting with Camel 2.17, the camel-swagger-java module supports both JSON and YAML formatted output. To specify the output you want, add /swagger.json or /swagger.yaml to the request URL. If a request URL does not specify a format then the camel-swagger-java module inspects the HTTP Accept header to detect whether JSON or YAML can be accepted. If both are accepted or if none was set as accepted then JSON is the default return format.

Examples

In the Apache Camel distribution, camel-example-swagger-cdi and camel-example-swagger-java demonstrate the use of the camel-swagger-java module.

Enhancing documentation generated by Swagger

Starting with Camel 2.16, you can enhance the documentation generated by Swagger by defining parameter details such as name, description, data type, parameter type and so on. If you are using XML, specify the param element to add this information. The following example shows how to provide information about the ID path parameter:

<!-- This is a REST GET request to view information for the user with the given ID: -->
<get uri="/{id}" outType="org.apache.camel.example.rest.User">
     <description>Find user by ID.</description>
     <param name="id" type="path" description="The ID of the user to get information about." dataType="int"/>
     <to uri="bean:userService?method=getUser(${header.id})"/>
</get>

Following is the same example in Java DSL:

.get("/{id}").description("Find user by ID.").outType(User.class)
   .param().name("id").type(path).description("The ID of the user to get information about.").dataType("int").endParam()
   .to("bean:userService?method=getUser(${header.id})")

If you define a parameter whose name is body then you must also specify body as the type of that parameter. For example:

<!-- This is a REST PUT request to create/update information about a user. -->
<put type="org.apache.camel.example.rest.User">
     <description>Updates or creates a user.</description>
     <param name="body" type="body" description="The user to update or create."/>
     <to uri="bean:userService?method=updateUser"/>
</put>

Following is the same example in Java DSL:

.put().description("Updates or create a user").type(User.class)
     .param().name("body").type(body).description("The user to update or create.").endParam()
     .to("bean:userService?method=updateUser")

See also: examples/camel-example-servlet-rest-tomcat in the Apache Camel distribution.

Chapter 5. Messaging Systems

Abstract

This chapter introduces the fundamental building blocks of a messaging system, such as endpoints, messaging channels, and message routers.

5.1. Message

Overview

A message is the smallest unit for transmitting data in a messaging system (represented by the grey dot in the figure below). The message itself might have some internal structure — for example, a message containing multiple parts — which is represented by geometrical figures attached to the grey dot in Figure 5.1, “Message Pattern”.

Figure 5.1. Message Pattern

Message pattern

Types of message

Apache Camel defines the following distinct message types:

  • In message  —  A message that travels through a route from a consumer endpoint to a producer endpoint (typically, initiating a message exchange).
  • Out message  —  A message that travels through a route from a producer endpoint back to a consumer endpoint (usually, in response to an In message).

All of these message types are represented internally by the org.apache.camel.Message interface.

Message structure

By default, Apache Camel applies the following structure to all message types:

  • Headers  —  Contains metadata or header data extracted from the message.
  • Body  —  Usually contains the entire message in its original form.
  • Attachments  —  Message attachments (required for integrating with certain messaging systems, such as JBI).

It is important to remember that this division into headers, body, and attachments is an abstract model of the message. Apache Camel supports many different components, that generate a wide variety of message formats. Ultimately, it is the underlying component implementation that decides what gets placed into the headers and body of a message.

Correlating messages

Internally, Apache Camel remembers the message IDs, which are used to correlate individual messages. In practice, however, the most important way that Apache Camel correlates messages is through exchange objects.

Exchange objects

An exchange object is an entity that encapsulates related messages, where the collection of related messages is referred to as a message exchange and the rules governing the sequence of messages are referred to as an exchange pattern. For example, two common exchange patterns are: one-way event messages (consisting of an In message), and request-reply exchanges (consisting of an In message, followed by an Out message).

Accessing messages

When defining a routing rule in the Java DSL, you can access the headers and body of a message using the following DSL builder methods:

  • header(String name), body()  —  Returns the named header and the body of the current In message.
  • outBody()  —  Returns the body of the current Out message.

For example, to populate the In message’s username header, you can use the following Java DSL route:

from(SourceURL).setHeader("username", "John.Doe").to(TargetURL);

5.2. Message Channel

Overview

A message channel is a logical channel in a messaging system. That is, sending messages to different message channels provides an elementary way of sorting messages into different message types. Message queues and message topics are examples of message channels. You should remember that a logical channel is not the same as a physical channel. There can be several different ways of physically realizing a logical channel.

In Apache Camel, a message channel is represented by an endpoint URI of a message-oriented component as shown in Figure 5.2, “Message Channel Pattern”.

Figure 5.2. Message Channel Pattern

Message channel pattern

Message-oriented components

The following message-oriented components in Apache Camel support the notion of a message channel:

ActiveMQ

In ActiveMQ, message channels are represented by queues or topics. The endpoint URI for a specific queue, QueueName, has the following format:

activemq:QueueName

The endpoint URI for a specific topic, TopicName, has the following format:

activemq:topic:TopicName

For example, to send messages to the queue, Foo.Bar, use the following endpoint URI:

activemq:Foo.Bar

See ActiveMQ in the Apache Camel Component Reference Guide for more details and instructions on setting up the ActiveMQ component.

JMS

The Java Messaging Service (JMS) is a generic wrapper layer that is used to access many different kinds of message systems (for example, you can use it to wrap ActiveMQ, MQSeries, Tibco, BEA, Sonic, and others). In JMS, message channels are represented by queues, or topics. The endpoint URI for a specific queue, QueueName, has the following format:

jms:QueueName

The endpoint URI for a specific topic, TopicName, has the following format:

jms:topic:TopicName

See Jms in the Apache Camel Component Reference Guide for more details and instructions on setting up the JMS component.

AMQP

In AMQP, message channels are represented by queues, or topics. The endpoint URI for a specific queue, QueueName, has the following format:

amqp:QueueName

The endpoint URI for a specific topic, TopicName, has the following format:

amqp:topic:TopicName

See Amqp in the Apache Camel Component Reference Guide. for more details and instructions on setting up the AMQP component.

5.3. Message Endpoint

Overview

A message endpoint is the interface between an application and a messaging system. As shown in Figure 5.3, “Message Endpoint Pattern”, you can have a sender endpoint, sometimes called a proxy or a service consumer, which is responsible for sending In messages, and a receiver endpoint, sometimes called an endpoint or a service, which is responsible for receiving In messages.

Figure 5.3. Message Endpoint Pattern

Message endpoint pattern

Types of endpoint

Apache Camel defines two basic types of endpoint:

  • Consumer endpoint  —  Appears at the start of a Apache Camel route and reads In messages from an incoming channel (equivalent to a receiver endpoint).
  • Producer endpoint  —  Appears at the end of a Apache Camel route and writes In messages to an outgoing channel (equivalent to a sender endpoint). It is possible to define a route with multiple producer endpoints.

Endpoint URIs

In Apache Camel, an endpoint is represented by an endpoint URI, which typically encapsulates the following kinds of data:

  • Endpoint URI for a consumer endpoint  —  Advertises a specific location (for example, to expose a service to which senders can connect). Alternatively, the URI can specify a message source, such as a message queue. The endpoint URI can include settings to configure the endpoint.
  • Endpoint URI for a producer endpoint  —  Contains details of where to send messages and includes the settings to configure the endpoint. In some cases, the URI specifies the location of a remote receiver endpoint; in other cases, the destination can have an abstract form, such as a queue name.

An endpoint URI in Apache Camel has the following general form:

ComponentPrefix:ComponentSpecificURI

Where ComponentPrefix is a URI prefix that identifies a particular Apache Camel component (see Apache Camel Component Reference for details of all the supported components). The remaining part of the URI, ComponentSpecificURI, has a syntax defined by the particular component. For example, to connect to the JMS queue, Foo.Bar, you can define an endpoint URI like the following:

jms:Foo.Bar

To define a route that connects the consumer endpoint, file://local/router/messages/foo, directly to the producer endpoint, jms:Foo.Bar, you can use the following Java DSL fragment:

from("file://local/router/messages/foo").to("jms:Foo.Bar");

Alternatively, you can define the same route in XML, as follows:

<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="file://local/router/messages/foo"/>
    <to uri="jms:Foo.Bar"/>
  </route>
</camelContext>

Dynamic To

The <toD> parameter allows you to send a message to a dynamically computed endpoint using one or more expressions that are concatenated together.

By default, the Simple language is used to compute the endpoint. The following example sends a message to an endpoint defined by a header:

<route>
  <from uri="direct:start"/>
  <toD uri="${header.foo}"/>
</route>

In Java DSL the format for the same command is:

from("direct:start")
  .toD("${header.foo}");

The URI can also be prefixed with a literal, as shown in the following example:

<route>
  <from uri="direct:start"/>
  <toD uri="mock:${header.foo}"/>
</route>

In Java DSL the format for the same command is:

from("direct:start")
  .toD("mock:${header.foo}");

In the example above, if the value of header.foo is orange, the URI will resolve as mock:orange.

To use a language other than Simple, you need to define the language: parameter. See Part II, “Routing Expression and Predicate Languages”.

The format for using a different language is to use language:languagename: in the URI. For example, to use Xpath use the following format:

<route>
  <from uri="direct:start"/>
  <toD uri="language:xpath:/order/@uri/">
</route>

Here is the same example in Java DSL:

from("direct:start")
  .toD("language:xpath:/order/@uri");

If you do not specify language: then the endpoint is a component name. In some cases a component and a language have the same name, such as xquery.

You can concatenate multiple languages using a + sign. In the example below, the URI is a combination of Simple and Xpath languages. Simple is the default so the language does not have to be defined. After the + sign is the Xpath instruction, indicated by language:xpath.

<route>
  <from uri="direct:start"/>
  <toD uri="jms:${header.base}+language:xpath:/order/@id"/>
</route>

In Java DSL the format is as follows:

from("direct:start")
  .toD("jms:${header.base}+language:xpath:/order/@id");

Many languages can be concatenated at one time, just separate each with a + and specify each language with language:languagename.

The following options are available with toD:

Name

Default Value

Description

uri

 

Mandatory: The URI to use.

pattern

 

Set a specific Exchange Pattern to use when sending to the endpoint. The original MEP is restored afterwards.

cacheSize

 

Configure the cache size of the ProducerCache, which caches producers for reuse. The default cache size is 1000, which will be used if no other value is specified. Setting the value to -1 turns off the cache completely.

ignoreInvalidEndpoint

false

Specifies whether to ignore an endpoint URI that could not be resolved. If disabled, Camel will throw an exception identifying the invalid endpoint URI.

5.4. Pipes and Filters

Overview

The pipes and filters pattern, shown in Figure 5.4, “Pipes and Filters Pattern”, describes a way of constructing a route by creating a chain of filters, where the output of one filter is fed into the input of the next filter in the pipeline (analogous to the UNIX pipe command). The advantage of the pipeline approach is that it enables you to compose services (some of which can be external to the Apache Camel application) to create more complex forms of message processing.

Figure 5.4. Pipes and Filters Pattern

Pipes and filters pattern

Pipeline for the InOut exchange pattern

Normally, all of the endpoints in a pipeline have an input (In message) and an output (Out message), which implies that they are compatible with the InOut message exchange pattern. A typical message flow through an InOut pipeline is shown in Figure 5.5, “Pipeline for InOut Exchanges”.

Figure 5.5. Pipeline for InOut Exchanges

Pipeline for InOut exchanges

The pipeline connects the output of each endpoint to the input of the next endpoint. The Out message from the final endpoint is sent back to the original caller. You can define a route for this pipeline, as follows:

from("jms:RawOrders").pipeline("cxf:bean:decrypt", "cxf:bean:authenticate", "cxf:bean:dedup", "jms:CleanOrders");

The same route can be configured in XML, as follows:

<camelContext id="buildPipeline" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="jms:RawOrders"/>
    <to uri="cxf:bean:decrypt"/>
    <to uri="cxf:bean:authenticate"/>
    <to uri="cxf:bean:dedup"/>
    <to uri="jms:CleanOrders"/>
  </route>
</camelContext>

There is no dedicated pipeline element in XML. The preceding combination of from and to elements is semantically equivalent to a pipeline. See the section called “Comparison of pipeline() and to() DSL commands”.

Pipeline for the InOnly and RobustInOnly exchange patterns

When there are no Out messages available from the endpoints in the pipeline (as is the case for the InOnly and RobustInOnly exchange patterns), a pipeline cannot be connected in the normal way. In this special case, the pipeline is constructed by passing a copy of the original In message to each of the endpoints in the pipeline, as shown in Figure 5.6, “Pipeline for InOnly Exchanges”. This type of pipeline is equivalent to a recipient list with fixed destinations (see Section 8.3, “Recipient List”).

Figure 5.6. Pipeline for InOnly Exchanges

Pipeline for InOnly exchanges

The route for this pipeline is defined using the same syntax as an InOut pipeline (either in Java DSL or in XML).

Comparison of pipeline() and to() DSL commands

In the Java DSL, you can define a pipeline route using either of the following syntaxes:

  • Using the pipeline() processor command  —  Use the pipeline processor to construct a pipeline route as follows:

    from(SourceURI).pipeline(FilterA, FilterB, TargetURI);
  • Using the to() command  — Use the to() command to construct a pipeline route as follows:

    from(SourceURI).to(FilterA, FilterB, TargetURI);

    Alternatively, you can use the equivalent syntax:

    from(SourceURI).to(FilterA).to(FilterB).to(TargetURI);

Exercise caution when using the to() command syntax, because it is not always equivalent to a pipeline processor. In Java DSL, the meaning of to() can be modified by the preceding command in the route. For example, when the multicast() command precedes the to() command, it binds the listed endpoints into a multicast pattern, instead of a pipeline pattern (see Section 8.13, “Multicast”).

5.5. Message Router

Overview

A message router, shown in Figure 5.7, “Message Router Pattern”, is a type of filter that consumes messages from a single consumer endpoint and redirects them to the appropriate target endpoint, based on a particular decision criterion. A message router is concerned only with redirecting messages; it does not modify the message content.

However, by default, whenever Camel routes a message exchange to a recipient endpoint, it sends is a shallow copy of the original exchange object. In a shallow copy, elements of the original exchange, such as the message body, headers, and attachments, are copied by reference only. By sending shallow copies that reuse resources, Camel optimizes for performance. But because these shallow copies are all linked, in cases where Camel routes a message to multiple endpoints, the tradeoff is that you lose the ability to apply custom logic to copies that are routed to different recipients. For information about how to enable Camel to route unique versions of a message to different endpoints, see "Apply custom processing to the outgoing messages".

Figure 5.7. Message Router Pattern

Message router pattern

A message router can easily be implemented in Apache Camel using the choice() processor, where each of the alternative target endpoints can be selected using a when() subclause (for details of the choice processor, see Section 1.5, “Processors”).

Java DSL example

The following Java DSL example shows how to route messages to three alternative destinations (either seda:a, seda:b, or seda:c) depending on the contents of the foo header:

from("seda:a").choice()
    .when(header("foo").isEqualTo("bar")).to("seda:b")
    .when(header("foo").isEqualTo("cheese")).to("seda:c")
    .otherwise().to("seda:d");

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <choice>
      <when>
        <xpath>$foo = 'bar'</xpath>
        <to uri="seda:b"/>
      </when>
      <when>
        <xpath>$foo = 'cheese'</xpath>
        <to uri="seda:c"/>
      </when>
      <otherwise>
        <to uri="seda:d"/>
      </otherwise>
    </choice>
  </route>
</camelContext>

Choice without otherwise

If you use choice() without an otherwise() clause, any unmatched exchanges are dropped by default.

5.6. Message Translator

Overview

The message translator pattern, shown in Figure 5.8, “Message Translator Pattern” describes a component that modifies the contents of a message, translating it to a different format. You can use Apache Camel’s bean integration feature to perform the message translation.

Figure 5.8. Message Translator Pattern

Message translator pattern

Bean integration

You can transform a message using bean integration, which enables you to call a method on any registered bean. For example, to call the method, myMethodName(), on the bean with ID, myTransformerBean:

from("activemq:SomeQueue")
  .beanRef("myTransformerBean", "myMethodName")
  .to("mqseries:AnotherQueue");

Where the myTransformerBean bean is defined in either a Spring XML file or in JNDI. If, you omit the method name parameter from beanRef(), the bean integration will try to deduce the method name to invoke by examining the message exchange.

You can also add your own explicit Processor instance to perform the transformation, as follows:

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");

Or, you can use the DSL to explicitly configure the transformation, as follows:

from("direct:start").setBody(body().append(" World!")).to("mock:result");

You can also use templating to consume a message from one destination, transform it with something like Velocity or XQuery and then send it on to another destination. For example, using the InOnly exchange pattern (one-way messaging) :

from("activemq:My.Queue").
  to("velocity:com/acme/MyResponse.vm").
  to("activemq:Another.Queue");

If you want to use InOut (request-reply) semantics to process requests on the My.Queue queue on ActiveMQ with a template generated response, then you could use a route like the following to send responses back to the JMSReplyTo destination:

from("activemq:My.Queue").
  to("velocity:com/acme/MyResponse.vm");

5.7. Message History

Overview

The Message History pattern enables you to analyze and debug the flow of messages in a loosely coupled system. If you attach a message history to the message, it displays a list of all applications that the message passed through since its origination.

In Apache Camel, using the getTracedRouteNodes method, you can trace a message flow using the Tracer or access information using the Java API from UnitOfWork.

Limiting Character Length in Logs

When you run Apache Camel with logging mechanism, it enables you to log the messages and its content from time to time.

Some messages may contain very big payloads. By default, Apache Camel will clip the log message and show only the first 1000 characters. For example, it displays the following log as:

[DEBUG ProducerCache  - >>>> Endpoint[direct:start] Exchange[Message: 01234567890123456789... [Body clipped after 20 characters, total length is 1000]

You can customize the limit when Apache Camel clips the body in the log. You can also set zero or a negative value, such as -1, means the message body is not logged.

Customizing the Limit using Java DSL

You can set the limit in Camel properties using Java DSL. For example,

 context.getProperties().put(Exchange.LOG_DEBUG_BODY_MAX_CHARS, "500");
Customizing the Limit using Spring DSL

You can set the limit in Camel properties using Spring DSL. For example,

<camelContext>
    <properties>
        <property key="CamelLogDebugBodyMaxChars" value="500"/>
   </properties>
</camelContext>

Chapter 6. Messaging Channels

Abstract

Messaging channels provide the plumbing for a messaging application. This chapter describes the different kinds of messaging channels available in a messaging system, and the roles that they play.

6.1. Point-to-Point Channel

Overview

A point-to-point channel, shown in Figure 6.1, “Point to Point Channel Pattern” is a message channel that guarantees that only one receiver consumes any given message. This is in contrast to a publish-subscribe channel, which allows multiple receivers to consume the same message. In particular, with a publish-subscribe channel, it is possible for multiple receivers to subscribe to the same channel. If more than one receiver competes to consume a message, it is up to the message channel to ensure that only one receiver actually consumes the message.

Figure 6.1. Point to Point Channel Pattern

Point to point channel pattern

Components that support point-to-point channel

The following Apache Camel components support the point-to-point channel pattern:

JMS

In JMS, a point-to-point channel is represented by a queue. For example, you can specify the endpoint URI for a JMS queue called Foo.Bar as follows:

jms:queue:Foo.Bar

The qualifier, queue:, is optional, because the JMS component creates a queue endpoint by default. Therefore, you can also specify the following equivalent endpoint URI:

jms:Foo.Bar

See Jms in the Apache Camel Component Reference Guide for more details.

ActiveMQ

In ActiveMQ, a point-to-point channel is represented by a queue. For example, you can specify the endpoint URI for an ActiveMQ queue called Foo.Bar as follows:

activemq:queue:Foo.Bar

See ActiveMQ in the Apache Camel Component Reference Guide for more details.

SEDA

The Apache Camel Staged Event-Driven Architecture (SEDA) component is implemented using a blocking queue. Use the SEDA component if you want to create a lightweight point-to-point channel that is internal to the Apache Camel application. For example, you can specify an endpoint URI for a SEDA queue called SedaQueue as follows:

seda:SedaQueue

JPA

The Java Persistence API (JPA) component is an EJB 3 persistence standard that is used to write entity beans out to a database. See JPA in the Apache Camel Component Reference Guide for more details.

XMPP

The XMPP (Jabber) component supports the point-to-point channel pattern when it is used in the person-to-person mode of communication. See XMPP in the Apache Camel Component Reference Guide for more details.

6.2. Publish-Subscribe Channel

Overview

A publish-subscribe channel, shown in Figure 6.2, “Publish Subscribe Channel Pattern”, is a Section 5.2, “Message Channel” that enables multiple subscribers to consume any given message. This is in contrast with a Section 6.1, “Point-to-Point Channel”. Publish-subscribe channels are frequently used as a means of broadcasting events or notifications to multiple subscribers.

Figure 6.2. Publish Subscribe Channel Pattern

Publish subscribe channel pattern

Components that support publish-subscribe channel

The following Apache Camel components support the publish-subscribe channel pattern:

  • JMS
  • ActiveMQ
  • XMPP
  • SEDA for working with SEDA in the same CamelContext which can work in pub-sub, but allowing multiple consumers.
  • see VM in the Apache Camel Component Reference Guide as SEDA, but for use within the same JVM.

JMS

In JMS, a publish-subscribe channel is represented by a topic. For example, you can specify the endpoint URI for a JMS topic called StockQuotes as follows:

jms:topic:StockQuotes

See Jms in the Apache Camel Component Reference Guide for more details.

ActiveMQ

In ActiveMQ, a publish-subscribe channel is represented by a topic. For example, you can specify the endpoint URI for an ActiveMQ topic called StockQuotes, as follows:

activemq:topic:StockQuotes

See ActiveMQ in the Apache Camel Component Reference Guide for more details.

XMPP

The XMPP (Jabber) component supports the publish-subscribe channel pattern when it is used in the group communication mode. See Xmpp in the Apache Camel Component Reference Guide for more details.

Static subscription lists

If you prefer, you can also implement publish-subscribe logic within the Apache Camel application itself. A simple approach is to define a static subscription list, where the target endpoints are all explicitly listed at the end of the route. However, this approach is not as flexible as a JMS or ActiveMQ topic.

Java DSL example

The following Java DSL example shows how to simulate a publish-subscribe channel with a single publisher, seda:a, and three subscribers, seda:b, seda:c, and seda:d:

from("seda:a").to("seda:b", "seda:c", "seda:d");
Note

This only works for the InOnly message exchange pattern.

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext id="buildStaticRecipientList" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <to uri="seda:b"/>
    <to uri="seda:c"/>
    <to uri="seda:d"/>
  </route>
</camelContext>

6.3. Dead Letter Channel

Overview

The dead letter channel pattern, shown in Figure 6.3, “Dead Letter Channel Pattern”, describes the actions to take when the messaging system fails to deliver a message to the intended recipient. This includes such features as retrying delivery and, if delivery ultimately fails, sending the message to a dead letter channel, which archives the undelivered messages.

Figure 6.3. Dead Letter Channel Pattern

Dead letter channel pattern

Creating a dead letter channel in Java DSL

The following example shows how to create a dead letter channel using Java DSL:

errorHandler(deadLetterChannel("seda:errors"));
from("seda:a").to("seda:b");

Where the errorHandler() method is a Java DSL interceptor, which implies that all of the routes defined in the current route builder are affected by this setting. The deadLetterChannel() method is a Java DSL command that creates a new dead letter channel with the specified destination endpoint, seda:errors.

The errorHandler() interceptor provides a catch-all mechanism for handling all error types. If you want to apply a more fine-grained approach to exception handling, you can use the onException clauses instead(see the section called “onException clause”).

XML DSL example

You can define a dead letter channel in the XML DSL, as follows:

 <route errorHandlerRef="myDeadLetterErrorHandler">
    ...
 </route>

 <bean id="myDeadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder">
     <property name="deadLetterUri" value="jms:queue:dead"/>
     <property name="redeliveryPolicy" ref="myRedeliveryPolicyConfig"/>
 </bean>

 <bean id="myRedeliveryPolicyConfig" class="org.apache.camel.processor.RedeliveryPolicy">
     <property name="maximumRedeliveries" value="3"/>
     <property name="redeliveryDelay" value="5000"/>
 </bean>

Redelivery policy

Normally, you do not send a message straight to the dead letter channel, if a delivery attempt fails. Instead, you re-attempt delivery up to some maximum limit, and after all redelivery attempts fail you would send the message to the dead letter channel. To customize message redelivery, you can configure the dead letter channel to have a redelivery policy. For example, to specify a maximum of two redelivery attempts, and to apply an exponential backoff algorithm to the time delay between delivery attempts, you can configure the dead letter channel as follows:

errorHandler(deadLetterChannel("seda:errors").maximumRedeliveries(2).useExponentialBackOff());
from("seda:a").to("seda:b");

Where you set the redelivery options on the dead letter channel by invoking the relevant methods in a chain (each method in the chain returns a reference to the current RedeliveryPolicy object). Table 6.1, “Redelivery Policy Settings” summarizes the methods that you can use to set redelivery policies.

Table 6.1. Redelivery Policy Settings

Method SignatureDefaultDescription

allowRedeliveryWhileStopping()

true

Controls whether redelivery is attempted during graceful shutdown or while a route is stopping. A delivery that is already in progress when stopping is initiated will not be interrupted.

backOffMultiplier(double multiplier)

2

If exponential backoff is enabled, let m be the backoff multiplier and let d be the initial delay. The sequence of redelivery attempts are then timed as follows:

d, m*d, m*m*d, m*m*m*d, ...

collisionAvoidancePercent(double collisionAvoidancePercent)

15

If collision avoidance is enabled, let p be the collision avoidance percent. The collision avoidance policy then tweaks the next delay by a random amount, up to plus/minus p% of its current value.

deadLetterHandleNewException

true

Camel 2.15: Specifies whether or not to handle an exception that occurs while processing a message in the dead letter channel. If true, the exception is handled and a logged at the WARN level (so that the dead letter channel is guaranteed to complete). If false, the exception is not handled, so the dead letter channel fails, and propagates the new exception.

delayPattern(String delayPattern)

None

Apache Camel 2.0: See the section called “Redeliver delay pattern”.

disableRedelivery()

true

Apache Camel 2.0: Disables the redelivery feature. To enable redelivery, set maximumRedeliveries() to a positive integer value.

handled(boolean handled)

true

Apache Camel 2.0: If true, the current exception is cleared when the message is moved to the dead letter channel; if false, the exception is propagated back to the client.

initialRedeliveryDelay(long initialRedeliveryDelay)

1000

Specifies the delay (in milliseconds) before attempting the first redelivery.

logNewException

true

Specifies whether to log at WARN level, when an exception is raised in the dead letter channel.

logStackTrace(boolean logStackTrace)

false

Apache Camel 2.0: If true, the JVM stack trace is included in the error logs.

maximumRedeliveries(int maximumRedeliveries)

0

Apache Camel 2.0: Maximum number of delivery attempts.

maximumRedeliveryDelay(long maxDelay)

60000

Apache Camel 2.0: When using an exponential backoff strategy (see useExponentialBackOff()), it is theoretically possible for the redelivery delay to increase without limit. This property imposes an upper limit on the redelivery delay (in milliseconds)

onRedelivery(Processor processor)

None

Apache Camel 2.0: Configures a processor that gets called before every redelivery attempt.

redeliveryDelay(long int)

0

Apache Camel 2.0: Specifies the delay (in milliseconds) between redelivery attempts. Apache Camel 2.16.0 : The default redelivery delay is one second.

retriesExhaustedLogLevel(LoggingLevel logLevel)

LoggingLevel.ERROR

Apache Camel 2.0: Specifies the logging level at which to log delivery failure (specified as an org.apache.camel.LoggingLevel constant).

retryAttemptedLogLevel(LoggingLevel logLevel)

LoggingLevel.DEBUG

Apache Camel 2.0: Specifies the logging level at which to redelivery attempts (specified as an org.apache.camel.LoggingLevel constant).

useCollisionAvoidance()

false

Enables collision avoidence, which adds some randomization to the backoff timings to reduce contention probability.

useOriginalMessage()

false

Apache Camel 2.0: If this feature is enabled, the message sent to the dead letter channel is a copy of the original message exchange, as it existed at the beginning of the route (in the from() node).

useExponentialBackOff()

false

Enables exponential backoff.

Redelivery headers

If Apache Camel attempts to redeliver a message, it automatically sets the headers described in Table 6.2, “Dead Letter Redelivery Headers” on the In message.

Table 6.2. Dead Letter Redelivery Headers

Header NameTypeDescription

CamelRedeliveryCounter

Integer

Apache Camel 2.0: Counts the number of unsuccessful delivery attempts. This value is also set in Exchange.REDELIVERY_COUNTER.

CamelRedelivered

Boolean

Apache Camel 2.0: True, if one or more redelivery attempts have been made. This value is also set in Exchange.REDELIVERED.

CamelRedeliveryMaxCounter

Integer

Apache Camel 2.6: Holds the maximum redelivery setting (also set in the Exchange.REDELIVERY_MAX_COUNTER exchange property). This header is absent if you use retryWhile or have unlimited maximum redelivery configured.

Redelivery exchange properties

If Apache Camel attempts to redeliver a message, it automatically sets the exchange properties described in Table 6.3, “Redelivery Exchange Properties”.

Table 6.3. Redelivery Exchange Properties

Exchange Property NameTypeDescription

Exchange.FAILURE_ROUTE_ID

String

Provides the route ID of the route that failed. The literal name of this property is CamelFailureRouteId.

Using the original message

Available as of Apache Camel 2.0 Because an exchange object is subject to modification as it passes through the route, the exchange that is current when an exception is raised is not necessarily the copy that you would want to store in the dead letter channel. In many cases, it is preferable to log the message that arrived at the start of the route, before it was subject to any kind of transformation by the route. For example, consider the following route:

from("jms:queue:order:input")
       .to("bean:validateOrder");
       .to("bean:transformOrder")
       .to("bean:handleOrder");

The preceding route listen for incoming JMS messages and then processes the messages using the sequence of beans: validateOrder, transformOrder, and handleOrder. But when an error occurs, we do not know in which state the message is in. Did the error happen before the transformOrder bean or after? We can ensure that the original message from jms:queue:order:input is logged to the dead letter channel by enabling the useOriginalMessage option as follows:

// will use original body
errorHandler(deadLetterChannel("jms:queue:dead")
       .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);

Redeliver delay pattern

Available as of Apache Camel 2.0 The delayPattern option is used to specify delays for particular ranges of the redelivery count. The delay pattern has the following syntax: limit1:delay1;limit2:delay2;limit3:delay3;…​, where each delayN is applied to redeliveries in the range limitNredeliveryCount < limitN+1

For example, consider the pattern, 5:1000;10:5000;20:20000, which defines three groups and results in the following redelivery delays:

  • Attempt number 1..4 = 0 milliseconds (as the first group starts with 5).
  • Attempt number 5..9 = 1000 milliseconds (the first group).
  • Attempt number 10..19 = 5000 milliseconds (the second group).
  • Attempt number 20.. = 20000 milliseconds (the last group).

You can start a group with limit 1 to define a starting delay. For example, 1:1000;5:5000 results in the following redelivery delays:

  • Attempt number 1..4 = 1000 millis (the first group)
  • Attempt number 5.. = 5000 millis (the last group)

There is no requirement that the next delay should be higher than the previous and you can use any delay value you like. For example, the delay pattern, 1:5000;3:1000, starts with a 5 second delay and then reduces the delay to 1 second.

Which endpoint failed?

When Apache Camel routes messages, it updates an Exchange property that contains the last endpoint the Exchange was sent to. Hence, you can obtain the URI for the current exchange’s most recent destination using the following code:

// Java
String lastEndpointUri = exchange.getProperty(Exchange.TO_ENDPOINT, String.class);

Where Exchange.TO_ENDPOINT is a string constant equal to CamelToEndpoint. This property is updated whenever Camel sends a message to any endpoint.

If an error occurs during routing and the exchange is moved into the dead letter queue, Apache Camel will additionally set a property named CamelFailureEndpoint, which identifies the last destination the exchange was sent to before the error occured. Hence, you can access the failure endpoint from within a dead letter queue using the following code:

// Java
String failedEndpointUri = exchange.getProperty(Exchange.FAILURE_ENDPOINT, String.class);

Where Exchange.FAILURE_ENDPOINT is a string constant equal to CamelFailureEndpoint.

Note

These properties remain set in the current exchange, even if the failure occurs after the given destination endpoint has finished processing. For example, consider the following route:

        from("activemq:queue:foo")
        .to("http://someserver/somepath")
        .beanRef("foo");

Now suppose that a failure happens in the foo bean. In this case the Exchange.TO_ENDPOINT property and the Exchange.FAILURE_ENDPOINT property still contain the value.

onRedelivery processor

When a dead letter channel is performing redeliveries, it is possible to configure a Processor that is executed just before every redelivery attempt. This can be used for situations where you need to alter the message before it is redelivered.

For example, the following dead letter channel is configured to call the MyRedeliverProcessor before redelivering exchanges:

// we configure our Dead Letter Channel to invoke
// MyRedeliveryProcessor before a redelivery is
// attempted. This allows us to alter the message before
errorHandler(deadLetterChannel("mock:error").maximumRedeliveries(5)
        .onRedelivery(new MyRedeliverProcessor())
        // setting delay to zero is just to make unit teting faster
        .redeliveryDelay(0L));

Where the MyRedeliveryProcessor process is implemented as follows:

// This is our processor that is executed before every redelivery attempt
// here we can do what we want in the java code, such as altering the message
public class MyRedeliverProcessor implements Processor {

    public void process(Exchange exchange) throws Exception {
        // the message is being redelivered so we can alter it

        // we just append the redelivery counter to the body
        // you can of course do all kind of stuff instead
        String body = exchange.getIn().getBody(String.class);
        int count = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class);

        exchange.getIn().setBody(body + count);

        // the maximum redelivery was set to 5
        int max = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class);
        assertEquals(5, max);
    }
}

Control redelivery during shutdown or stopping

If you stop a route or initiate graceful shutdown, the default behavior of the error handler is to continue attempting redelivery. Because this is typically not the desired behavior, you have the option of disabling redelivery during shutdown or stopping, by setting the allowRedeliveryWhileStopping option to false, as shown in the following example:

errorHandler(deadLetterChannel("jms:queue:dead")
    .allowRedeliveryWhileStopping(false)
    .maximumRedeliveries(20)
    .redeliveryDelay(1000)
    .retryAttemptedLogLevel(LoggingLevel.INFO));
Note

The allowRedeliveryWhileStopping option is true by default, for backwards compatibility reasons. During aggressive shutdown, however, redelivery is always suppressed, irrespective of this option setting (for example, after graceful shutdown has timed out).

Using onExceptionOccurred Processor

Dead Letter channel supports the onExceptionOccurred processor to allow the custom processing of a message, after an exception occurs. You can use it for custom logging too. Any new exceptions thrown from the onExceptionOccurred processor is logged as WARN and ignored, not to override the existing exception.

The difference between the onRedelivery processor and onExceptionOccurred processor is you can process the former exactly before the redelivery attempt. However, it does not happen immediately after an exception occurs. For example, If you configure the error handler to do five seconds delay between the redelivery attempts, then the redelivery processor is invoked five seconds later, after an exception occurs.

The following example explains how to do the custom logging when an exception occurs. You need to configure the onExceptionOccurred to use the custom processor.

errorHandler(defaultErrorHandler().maximumRedeliveries(3).redeliveryDelay(5000).onExceptionOccurred(myProcessor));

onException clause

Instead of using the errorHandler() interceptor in your route builder, you can define a series of onException() clauses that define different redelivery policies and different dead letter channels for various exception types. For example, to define distinct behavior for each of the NullPointerException, IOException, and Exception types, you can define the following rules in your route builder using Java DSL:

onException(NullPointerException.class)
    .maximumRedeliveries(1)
    .setHeader("messageInfo", "Oh dear! An NPE.")
    .to("mock:npe_error");

onException(IOException.class)
    .initialRedeliveryDelay(5000L)
    .maximumRedeliveries(3)
    .backOffMultiplier(1.0)
    .useExponentialBackOff()
    .setHeader("messageInfo", "Oh dear! Some kind of I/O exception.")
    .to("mock:io_error");

onException(Exception.class)
    .initialRedeliveryDelay(1000L)
    .maximumRedeliveries(2)
    .setHeader("messageInfo", "Oh dear! An exception.")
    .to("mock:error");

from("seda:a").to("seda:b");

Where the redelivery options are specified by chaining the redelivery policy methods (as listed in Table 6.1, “Redelivery Policy Settings”), and you specify the dead letter channel’s endpoint using the to() DSL command. You can also call other Java DSL commands in the onException() clauses. For example, the preceding example calls setHeader() to record some error details in a message header named, messageInfo.

In this example, the NullPointerException and the IOException exception types are configured specially. All other exception types are handled by the generic Exception exception interceptor. By default, Apache Camel applies the exception interceptor that most closely matches the thrown exception. If it fails to find an exact match, it tries to match the closest base type, and so on. Finally, if no other interceptor matches, the interceptor for the Exception type matches all remaining exceptions.

OnPrepareFailure

Before you pass the exchange to the dead letter queue, you can use the onPrepare option to allow a custom processor to prepare the exchange. It enables you to add information about the exchange, such as the cause of exchange failure. For example, the following processor adds a header with the exception message.

public class MyPrepareProcessor implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
        exchange.getIn().setHeader("FailedBecause", cause.getMessage());
    }
}

You can configue the error handler to use the processor as follows.

errorHandler(deadLetterChannel("jms:dead").onPrepareFailure(new MyPrepareProcessor()));

However, the onPrepare option is also available using the default error handler.

<bean id="myPrepare"
class="org.apache.camel.processor.DeadLetterChannelOnPrepareTest.MyPrepareProcessor"/>

<errorHandler id="dlc" type="DeadLetterChannel" deadLetterUri="jms:dead" onPrepareFailureRef="myPrepare"/>

6.4. Guaranteed Delivery

Overview

Guaranteed delivery means that once a message is placed into a message channel, the messaging system guarantees that the message will reach its destination, even if parts of the application should fail. In general, messaging systems implement the guaranteed delivery pattern, shown in Figure 6.4, “Guaranteed Delivery Pattern”, by writing messages to persistent storage before attempting to deliver them to their destination.

Figure 6.4. Guaranteed Delivery Pattern

Guaranteed delivery pattern

Components that support guaranteed delivery

The following Apache Camel components support the guaranteed delivery pattern:

JMS

In JMS, the deliveryPersistent query option indicates whether or not persistent storage of messages is enabled. Usually it is unnecessary to set this option, because the default behavior is to enable persistent delivery. To configure all the details of guaranteed delivery, it is necessary to set configuration options on the JMS provider. These details vary, depending on what JMS provider you are using. For example, MQSeries, TibCo, BEA, Sonic, and others, all provide various qualities of service to support guaranteed delivery.

See Jms in the Apache Camel Component Reference Guide> for more details.

ActiveMQ

In ActiveMQ, message persistence is enabled by default. From version 5 onwards, ActiveMQ uses the AMQ message store as the default persistence mechanism. There are several different approaches you can use to enabe message persistence in ActiveMQ.

The simplest option (different from Figure 6.4, “Guaranteed Delivery Pattern”) is to enable persistence in a central broker and then connect to that broker using a reliable protocol. After a message is been sent to the central broker, delivery to consumers is guaranteed. For example, in the Apache Camel configuration file, META-INF/spring/camel-context.xml, you can configure the ActiveMQ component to connect to the central broker using the OpenWire/TCP protocol as follows:

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="tcp://somehost:61616"/>
  </bean>
  ...
</beans>

If you prefer to implement an architecture where messages are stored locally before being sent to a remote endpoint (similar to Figure 6.4, “Guaranteed Delivery Pattern”), you do this by instantiating an embedded broker in your Apache Camel application. A simple way to achieve this is to use the ActiveMQ Peer-to-Peer protocol, which implicitly creates an embedded broker to communicate with other peer endpoints. For example, in the camel-context.xml configuration file, you can configure the ActiveMQ component to connect to all of the peers in group, GroupA, as follows:

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="peer://GroupA/broker1"/>
  </bean>
  ...
</beans>

Where broker1 is the broker name of the embedded broker (other peers in the group should use different broker names). One limiting feature of the Peer-to-Peer protocol is that it relies on IP multicast to locate the other peers in its group. This makes it unsuitable for use in wide area networks (and in some local area networks that do not have IP multicast enabled).

A more flexible way to create an embedded broker in the ActiveMQ component is to exploit ActiveMQ’s VM protocol, which connects to an embedded broker instance. If a broker of the required name does not already exist, the VM protocol automatically creates one. You can use this mechanism to create an embedded broker with custom configuration. For example:

<beans ... >
  ...
  <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="brokerURL" value="vm://broker1?brokerConfig=xbean:activemq.xml"/>
  </bean>
  ...
</beans>

Where activemq.xml is an ActiveMQ file which configures the embedded broker instance. Within the ActiveMQ configuration file, you can choose to enable one of the following persistence mechanisms:

See ActiveMQ in the Apache Camel Component Reference Guide for more details.

ActiveMQ Journal

The ActiveMQ Journal component is optimized for a special use case where multiple, concurrent producers write messages to queues, but there is only one active consumer. Messages are stored in rolling log files and concurrent writes are aggregated to boost efficiency.

6.5. Message Bus

Overview

Message bus refers to a messaging architecture, shown in Figure 6.5, “Message Bus Pattern”, that enables you to connect diverse applications running on diverse computing platforms. In effect, the Apache Camel and its components constitute a message bus.

Figure 6.5. Message Bus Pattern

Message bus pattern

The following features of the message bus pattern are reflected in Apache Camel:

  • Common communication infrastructure  —  The router itself provides the core of the common communication infrastructure in Apache Camel. However, in contrast to some message bus architectures, Apache Camel provides a heterogeneous infrastructure: messages can be sent into the bus using a wide variety of different transports and using a wide variety of different message formats.
  • Adapters  —  Where necessary, Apache Camel can translate message formats and propagate messages using different transports. In effect, Apache Camel is capable of behaving like an adapter, so that external applications can hook into the message bus without refactoring their messaging protocols.

    In some cases, it is also possible to integrate an adapter directly into an external application. For example, if you develop an application using Apache CXF, where the service is implemented using JAX-WS and JAXB mappings, it is possible to bind a variety of different transports to the service. These transport bindings function as adapters.

Chapter 7. Message Construction

Abstract

The message construction patterns describe the various forms and functions of the messages that pass through the system.

7.1. Correlation Identifier

Overview

The correlation identifier pattern, shown in Figure 7.1, “Correlation Identifier Pattern”, describes how to match reply messages with request messages, given that an asynchronous messaging system is used to implement a request-reply protocol. The essence of this idea is that request messages should be generated with a unique token, the request ID, that identifies the request message and reply messages should include a token, the correlation ID, that contains the matching request ID.

Apache Camel supports the Correlation Identifier from the EIP patterns by getting or setting a header on a Message.

When working with the ActiveMQ or JMS components, the correlation identifier header is called JMSCorrelationID. You can add your own correlation identifier to any message exchange to help correlate messages together in a single conversation (or business process). A correlation identifier is usually stored in a Apache Camel message header.

Some EIP patterns spin off a sub message and, in those cases, Apache Camel adds a correlation ID to the Exchanges as a property with they key, Exchange.CORRELATION_ID, which links back to the source Exchanges. For example, the splitter, multicast, recipient list, and wire tap EIPs do this.

Figure 7.1. Correlation Identifier Pattern

Correlation identifier pattern

7.2. Event Message

Event Message

Camel supports the Event Message from the Enterprise Integration Patterns by supporting the Exchange Pattern on a message which can be set to InOnly to indicate a oneway event message. Camel Apache Camel Component Reference then implement this pattern using the underlying transport or protocols.

event message solution

The default behavior of many Apache Camel Component Reference is InOnly such as for JMS, File or SEDA

Explicitly specifying InOnly

If you are using a component which defaults to InOut you can override the message exchange patterns for an endpoint using the pattern property.

foo:bar?exchangePattern=InOnly

From 2.0 onwards on Camel you can specify the message exchange patterns using the DSL.

Using the Fluent Builders

from("mq:someQueue").
  inOnly().
  bean(Foo.class);

or you can invoke an endpoint with an explicit pattern

from("mq:someQueue").
  inOnly("mq:anotherQueue");

Using the Spring XML Extensions

<route>
    <from uri="mq:someQueue"/>
    <inOnly uri="bean:foo"/>
</route>
<route>
    <from uri="mq:someQueue"/>
    <inOnly uri="mq:anotherQueue"/>
</route>

7.3. Return Address

Return Address

Apache Camel supports the Return Address from the Enterprise Exchange Patterns using the JMSReplyTo header.

return address solution

For example when using JMS with InOut, the component will by default be returned to the address given in JMSReplyTo.

Example

Requestor Code

 getMockEndpoint("mock:bar").expectedBodiesReceived("Bye World");
 template.sendBodyAndHeader("direct:start", "World", "JMSReplyTo", "queue:bar");

Route Using the Fluent Builders

 from("direct:start").to("activemq:queue:foo?preserveMessageQos=true");
 from("activemq:queue:foo").transform(body().prepend("Bye "));
 from("activemq:queue:bar?disableReplyTo=true").to("mock:bar");

Route Using the Spring XML Extensions

 <route>
   <from uri="direct:start"/>
   <to uri="activemq:queue:foo?preserveMessageQos=true"/>
 </route>

 <route>
   <from uri="activemq:queue:foo"/>
   <transform>
       <simple>Bye ${in.body}</simple>
   </transform>
 </route>

 <route>
   <from uri="activemq:queue:bar?disableReplyTo=true"/>
   <to uri="mock:bar"/>
 </route>

For a complete example of this pattern, see this JUnit test case

Chapter 8. Message Routing

Abstract

The message routing patterns describe various ways of linking message channels together. This includes various algorithms that can be applied to the message stream (without modifying the body of the message).

8.1. Content-Based Router

Overview

A content-based router, shown in Figure 8.1, “Content-Based Router Pattern”, enables you to route messages to the appropriate destination based on the message contents.

Figure 8.1. Content-Based Router Pattern

Content-based router pattern

Java DSL example

The following example shows how to route a request from an input, seda:a, endpoint to either seda:b, queue:c, or seda:d depending on the evaluation of various predicate expressions:

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("seda:a").choice()
          .when(header("foo").isEqualTo("bar")).to("seda:b")
          .when(header("foo").isEqualTo("cheese")).to("seda:c")
          .otherwise().to("seda:d");
    }
};

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <choice>
      <when>
        <xpath>$foo = 'bar'</xpath>
        <to uri="seda:b"/>
      </when>
      <when>
        <xpath>$foo = 'cheese'</xpath>
        <to uri="seda:c"/>
      </when>
      <otherwise>
        <to uri="seda:d"/>
      </otherwise>
    </choice>
  </route>
</camelContext>

8.2. Message Filter

Overview

A message filter is a processor that eliminates undesired messages based on specific criteria. In Apache Camel, the message filter pattern, shown in Figure 8.2, “Message Filter Pattern”, is implemented by the filter() Java DSL command. The filter() command takes a single predicate argument, which controls the filter. When the predicate is true, the incoming message is allowed to proceed, and when the predicate is false, the incoming message is blocked.

Figure 8.2. Message Filter Pattern

Message filter pattern

Java DSL example

The following example shows how to create a route from endpoint, seda:a, to endpoint, seda:b, that blocks all messages except for those messages whose foo header have the value, bar:

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");
    }
};

To evaluate more complex filter predicates, you can invoke one of the supported scripting languages, such as XPath, XQuery, or SQL (see Part II, “Routing Expression and Predicate Languages”). The following example defines a route that blocks all messages except for those containing a person element whose name attribute is equal to James:

from("direct:start").
        filter().xpath("/person[@name='James']").
        to("mock:result");

XML configuration example

The following example shows how to configure the route with an XPath predicate in XML (see Part II, “Routing Expression and Predicate Languages”):

<camelContext id="simpleFilterRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <filter>
      <xpath>$foo = 'bar'</xpath>
      <to uri="seda:b"/>
    </filter>
  </route>
  </camelContext>
Filtered endpoint required inside </filter> tag

Make sure you put the endpoint you want to filter (for example, <to uri="seda:b"/>) before the closing </filter> tag or the filter will not be applied (in 2.8+, omitting this will result in an error).

Filtering with beans

Here is an example of using a bean to define the filter behavior:

from("direct:start")
     .filter().method(MyBean.class, "isGoldCustomer").to("mock:result").end()
     .to("mock:end");

public static class MyBean {
    public boolean isGoldCustomer(@Header("level") String level) {
        return level.equals("gold");
    }
}

Using stop()

Available as of Camel 2.0

Stop is a special type of filter that filters out all messages. Stop is convenient to use in a content-based router when you need to stop further processing in one of the predicates.

In the following example, we do not want messages with the word Bye in the message body to propagate any further in the route. We prevent this in the when() predicate using .stop().

from("direct:start")
    .choice()
        .when(bodyAs(String.class).contains("Hello")).to("mock:hello")
        .when(bodyAs(String.class).contains("Bye")).to("mock:bye").stop()
        .otherwise().to("mock:other")
    .end()
    .to("mock:result");

Knowing if Exchange was filtered or not

Available as of Camel 2.5

The message filter EIP will add a property on the Exchange which states if it was filtered or not.

The property has the key Exchange.FILTER_MATCHED which has the String value of CamelFilterMatched. Its value is a boolean indicating true or false. If the value is true then the Exchange was routed in the filter block.

8.3. Recipient List

Overview

A recipient list, shown in Figure 8.3, “Recipient List Pattern”, is a type of router that sends each incoming message to multiple different destinations. In addition, a recipient list typically requires that the list of recipients be calculated at run time.

Figure 8.3. Recipient List Pattern

Recipient list pattern

Recipient list with fixed destinations

The simplest kind of recipient list is where the list of destinations is fixed and known in advance, and the exchange pattern is InOnly. In this case, you can hardwire the list of destinations into the to() Java DSL command.

Note

The examples given here, for the recipient list with fixed destinations, work only with the InOnly exchange pattern (similar to a pipes and filters pattern). If you want to create a recipient list for exchange patterns with Out messages, use the multicast pattern instead.

Java DSL example

The following example shows how to route an InOnly exchange from a consumer endpoint, queue:a, to a fixed list of destinations:

from("seda:a").to("seda:b", "seda:c", "seda:d");

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext id="buildStaticRecipientList" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <to uri="seda:b"/>
    <to uri="seda:c"/>
    <to uri="seda:d"/>
  </route>
</camelContext>

Recipient list calculated at run time

In most cases, when you use the recipient list pattern, the list of recipients should be calculated at runtime. To do this use the recipientList() processor, which takes a list of destinations as its sole argument. Because Apache Camel applies a type converter to the list argument, it should be possible to use most standard Java list types (for example, a collection, a list, or an array). For more details about type converters, see Section 34.3, “Built-In Type Converters”.

The recipients receive a copy of the same exchange instance and Apache Camel executes them sequentially.

Java DSL example

The following example shows how to extract the list of destinations from a message header called recipientListHeader, where the header value is a comma-separated list of endpoint URIs:

from("direct:a").recipientList(header("recipientListHeader").tokenize(","));

In some cases, if the header value is a list type, you might be able to use it directly as the argument to recipientList(). For example:

from("seda:a").recipientList(header("recipientListHeader"));

However, this example is entirely dependent on how the underlying component parses this particular header. If the component parses the header as a simple string, this example will not work. The header must be parsed into some type of Java list.

XML configuration example

The following example shows how to configure the preceding route in XML, where the header value is a comma-separated list of endpoint URIs:

<camelContext id="buildDynamicRecipientList" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <recipientList delimiter=",">
      <header>recipientListHeader</header>
    </recipientList>
  </route>
</camelContext>

Sending to multiple recipients in parallel

Available as of Camel 2.2

The recipient list pattern supports parallelProcessing, which is similar to the corresponding feature in the splitter pattern. Use the parallel processing feature to send the exchange to multiple recipients concurrently — for example:

from("direct:a").recipientList(header("myHeader")).parallelProcessing();

In Spring XML, the parallel processing feature is implemented as an attribute on the recipientList tag — for example:

<route>
  <from uri="direct:a"/>
  <recipientList parallelProcessing="true">
    <header>myHeader</header>
  </recipientList>
</route>

Stop on exception

Available as of Camel 2.2

The recipient list supports the stopOnException feature, which you can use to stop sending to any further recipients, if any recipient fails.

from("direct:a").recipientList(header("myHeader")).stopOnException();

And in Spring XML its an attribute on the recipient list tag.

In Spring XML, the stop on exception feature is implemented as an attribute on the recipientList tag — for example:

<route>
  <from uri="direct:a"/>
  <recipientList stopOnException="true">
    <header>myHeader</header>
  </recipientList>
</route>
Note

You can combine parallelProcessing and stopOnException in the same route.

Ignore invalid endpoints

Available as of Camel 2.3

The recipient list pattern supports the ignoreInvalidEndpoints option, which enables the recipient list to skip invalid endpoints (the routing slips pattern also supports this option). For example:

from("direct:a").recipientList(header("myHeader")).ignoreInvalidEndpoints();

And in Spring XML, you can enable this option by setting the ignoreInvalidEndpoints attribute on the recipientList tag, as follows

<route>
  <from uri="direct:a"/>
  <recipientList ignoreInvalidEndpoints="true">
    <header>myHeader</header>
  </recipientList>
</route>

Consider the case where myHeader contains the two endpoints, direct:foo,xxx:bar. The first endpoint is valid and works. The second is invalid and, therefore, ignored. Apache Camel logs at INFO level whenever an invalid endpoint is encountered.

Using custom AggregationStrategy

Available as of Camel 2.2

You can use a custom AggregationStrategy with the recipient list pattern, which is useful for aggregating replies from the recipients in the list. By default, Apache Camel uses the UseLatestAggregationStrategy aggregation strategy, which keeps just the last received reply. For a more sophisticated aggregation strategy, you can define your own implementation of the AggregationStrategy interface — see Section 8.5, “Aggregator” for details. For example, to apply the custom aggregation strategy, MyOwnAggregationStrategy, to the reply messages, you can define a Java DSL route as follows:

from("direct:a")
    .recipientList(header("myHeader")).aggregationStrategy(new MyOwnAggregationStrategy())
    .to("direct:b");

In Spring XML, you can specify the custom aggregation strategy as an attribute on the recipientList tag, as follows:

<route>
  <from uri="direct:a"/>
  <recipientList strategyRef="myStrategy">
    <header>myHeader</header>
  </recipientList>
  <to uri="direct:b"/>
</route>

<bean id="myStrategy" class="com.mycompany.MyOwnAggregationStrategy"/>

Using custom thread pool

Available as of Camel 2.2

This is only needed when you use parallelProcessing. By default Camel uses a thread pool with 10 threads. Notice this is subject to change when we overhaul thread pool management and configuration later (hopefully in Camel 2.2).

You configure this just as you would with the custom aggregation strategy.

Using method call as recipient list

You can use a bean integration to provide the recipients, for example:

from("activemq:queue:test").recipientList().method(MessageRouter.class, "routeTo");

Where the MessageRouter bean is defined as follows:

public class MessageRouter {

    public String routeTo() {
        String queueName = "activemq:queue:test2";
        return queueName;
    }
}

Bean as recipient list

You can make a bean behave as a recipient list by adding the @RecipientList annotation to a methods that returns a list of recipients. For example:

public class MessageRouter {

    @RecipientList
    public String routeTo() {
        String queueList = "activemq:queue:test1,activemq:queue:test2";
        return queueList;
    }
}

In this case, do not include the recipientList DSL command in the route. Define the route as follows:

from("activemq:queue:test").bean(MessageRouter.class, "routeTo");

Using timeout

Available as of Camel 2.5

If you use parallelProcessing, you can configure a total timeout value in milliseconds. Camel will then process the messages in parallel until the timeout is hit. This allows you to continue processing if one message is slow.

In the example below, the recipientlist header has the value, direct:a,direct:b,direct:c, so that the message is sent to three recipients. We have a timeout of 250 milliseconds, which means only the last two messages can be completed within the timeframe. The aggregation therefore yields the string result, BC.

from("direct:start")
    .recipientList(header("recipients"), ",")
    .aggregationStrategy(new AggregationStrategy() {
            public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
                if (oldExchange == null) {
                    return newExchange;
                }

                String body = oldExchange.getIn().getBody(String.class);
                oldExchange.getIn().setBody(body + newExchange.getIn().getBody(String.class));
                return oldExchange;
            }
        })
        .parallelProcessing().timeout(250)
    // use end to indicate end of recipientList clause
    .end()
    .to("mock:result");

from("direct:a").delay(500).to("mock:A").setBody(constant("A"));

from("direct:b").to("mock:B").setBody(constant("B"));

from("direct:c").to("mock:C").setBody(constant("C"));
Note

This timeout feature is also supported by splitter and both multicast and recipientList.

By default if a timeout occurs the AggregationStrategy is not invoked. However you can implement a specialized version

// Java
public interface TimeoutAwareAggregationStrategy extends AggregationStrategy {

    /**
     * A timeout occurred
     *
     * @param oldExchange  the oldest exchange (is <tt>null</tt> on first aggregation as we only have the new exchange)
     * @param index        the index
     * @param total        the total
     * @param timeout      the timeout value in millis
     */
    void timeout(Exchange oldExchange, int index, int total, long timeout);

This allows you to deal with the timeout in the AggregationStrategy if you really need to.

Timeout is total

The timeout is total, which means that after X time, Camel will aggregate the messages which has completed within the timeframe. The remainders will be cancelled. Camel will also only invoke the timeout method in the TimeoutAwareAggregationStrategy once, for the first index which caused the timeout.

Apply custom processing to the outgoing messages

Before recipientList sends a message to one of the recipient endpoints, it creates a message replica, which is a shallow copy of the original message. In a shallow copy, the headers and payload of the original message are copied by reference only. Each new copy does not contain its own instance of those elements. As a result, shallow copies of a message are linked and you cannot apply custom processing when routing them to different endpoints.

If you want to perform some custom processing on each message replica before the replica is sent to its endpoint, you can invoke the onPrepare DSL command in the recipientList clause. The onPrepare command inserts a custom processor just after the message has been shallow-copied and just before the message is dispatched to its endpoint. For example, in the following route, the CustomProc processor is invoked on the message replica for each recipient endpoint:

from("direct:start")
  .recipientList().onPrepare(new CustomProc());

A common use case for the onPrepare DSL command is to perform a deep copy of some or all elements of a message. This allows each message replica to be modified independently of the others. For example, the following CustomProc processor class performs a deep copy of the message body, where the message body is presumed to be of type, BodyType, and the deep copy is performed by the method, BodyType.deepCopy().

// Java
import org.apache.camel.*;
...
public class CustomProc implements Processor {

    public void process(Exchange exchange) throws Exception {
        BodyType body = exchange.getIn().getBody(BodyType.class);

        // Make a _deep_ copy of of the body object
        BodyType clone =  BodyType.deepCopy();
        exchange.getIn().setBody(clone);

        // Headers and attachments have already been
        // shallow-copied. If you need deep copies,
        // add some more code here.
    }
}

Options

The recipientList DSL command supports the following options:

Name

Default Value

Description

delimiter

,

Delimiter used if the Expression returned multiple endpoints.

strategyRef

 

Refers to an AggregationStrategy to be used to assemble the replies from the recipients, into a single outgoing message from the Section 8.3, “Recipient List”. By default Camel will use the last reply as the outgoing message.

strategyMethodName

 

This option can be used to explicitly specify the method name to use, when using POJOs as the AggregationStrategy.

strategyMethodAllowNull

false

This option can be used, when using POJOs as the AggregationStrategy. If false, the aggregate method is not used, when there is no data to enrich. If true, null values are used for the oldExchange, when there is no data to enrich.

parallelProcessing

false

Camel 2.2: If enables then sending messages to the recipients occurs concurrently. Note the caller thread will still wait until all messages has been fully processed, before it continues. Its only the sending and processing the replies from the recipients which happens concurrently.

parallelAggregate

false

If enabled, the aggregate method on AggregationStrategy can be called concurrently. Note that this requires the implementation of AggregationStrategy to be thread-safe. By default, this option is false, which means that Camel automatically synchronizes calls to the aggregate method. In some use-cases, however, you can improve performance by implementing AggregationStrategy as thread-safe and setting this option to true.

executorServiceRef

 

Camel 2.2: Refers to a custom Thread Pool to be used for parallel processing. Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well.

stopOnException

false

Camel 2.2: Whether or not to stop continue processing immediately when an exception occurred. If disable, then Camel will send the message to all recipients regardless if one of them failed. You can deal with exceptions in the AggregationStrategy class where you have full control how to handle that.

ignoreInvalidEndpoints

false

Camel 2.3: If an endpoint uri could not be resolved, should it be ignored. Otherwise Camel will thrown an exception stating the endpoint uri is not valid.

streaming

false

Camel 2.5: If enabled then Camel will process replies out-of-order, eg in the order they come back. If disabled, Camel will process replies in the same order as the Expression specified.

timeout

 

Camel 2.5: Sets a total timeout specified in millis. If the Section 8.3, “Recipient List” hasn’t been able to send and process all replies within the given timeframe, then the timeout triggers and the Section 8.3, “Recipient List” breaks out and continues. Notice if you provide a TimeoutAwareAggregationStrategy then the timeout method is invoked before breaking out.

onPrepareRef

 

Camel 2.8: Refers to a custom Processor to prepare the copy of the Exchange each recipient will receive. This allows you to do any custom logic, such as deep-cloning the message payload if that’s needed etc.

shareUnitOfWork

false

Camel 2.8: Whether the unit of work should be shared. See the same option on Section 8.4, “Splitter” for more details.

cacheSize

0

Camel 2.13.1/2.12.4: Allows to configure the cache size for the ProducerCache which caches producers for reuse in the routing slip. Will by default use the default cache size which is 0. Setting the value to -1 allows to turn off the cache all together.

Using Exchange Pattern in Recipient List

By default, the Recipient List uses the current exchange pattern. However, there may be few cases where you can send a message to a recipient using a different exchange pattern.

For example, you may have a route that initiates as a InOnly route. Now, If you want to use InOut exchange pattern with a recipient list, you need to configure the exchange pattern directly in the recipient endpoints.

The following example illustrates the route where the new files will start as InOnly and then route to a recipient list. If you want to use InOut with the ActiveMQ (JMS) endpoint, you need to specify this using the exchangePattern equals to InOut option. However, the response form the JMS request or reply will then be continued routed, and thus the response is stored in as a file in the outbox directory.

from("file:inbox")
  // the exchange pattern is InOnly initially when using a file route
  .recipientList().constant("activemq:queue:inbox?exchangePattern=InOut")
  .to("file:outbox");
Note

The InOut exchange pattern must get a response during the timeout. However, it fails if the response is not recieved.

8.4. Splitter

Overview

A splitter is a type of router that splits an incoming message into a series of outgoing messages. Each of the outgoing messages contains a piece of the original message. In Apache Camel, the splitter pattern, shown in Figure 8.4, “Splitter Pattern”, is implemented by the split() Java DSL command.

Figure 8.4. Splitter Pattern

Splitter pattern

The Apache Camel splitter actually supports two patterns, as follows:

  • Simple splitter — implements the splitter pattern on its own.
  • Splitter/aggregator — combines the splitter pattern with the aggregator pattern, such that the pieces of the message are recombined after they have been processed.

Before the splitter separates the original message into parts, it makes a shallow copy of the original message. In a shallow copy, the headers and payload of the original message are copied as references only. Although the splitter does not itself route the resulting message parts to different endpoints, parts of the split message might undergo secondary routing.

Because the message parts are shallow copies, they remain linked to the original message. As a result, they cannot be modified independently. If you want to apply custom logic to different copies of a message part before routing it to a set of endpoints, you must use the onPrepareRef DSL option in the splitter clause to make a deep copy of the original message. For information about using options, see the section called “Options”.

Java DSL example

The following example defines a route from seda:a to seda:b that splits messages by converting each line of an incoming message into a separate outgoing message:

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("seda:a")
          .split(bodyAs(String.class).tokenize("\n"))
          .to("seda:b");
    }
};

The splitter can use any expression language, so you can split messages using any of the supported scripting languages, such as XPath, XQuery, or SQL (see Part II, “Routing Expression and Predicate Languages”). The following example extracts bar elements from an incoming message and insert them into separate outgoing messages:

from("activemq:my.queue")
  .split(xpath("//foo/bar"))
  .to("file://some/directory")

XML configuration example

The following example shows how to configure a splitter route in XML, using the XPath scripting language:

<camelContext id="buildSplitter" xmlns="http://camel.apache.org/schema/spring">
    <route>
      <from uri="seda:a"/>
      <split>
        <xpath>//foo/bar</xpath>
        <to uri="seda:b"/>
      </split>
    </route>
</camelContext>

You can use the tokenize expression in the XML DSL to split bodies or headers using a token, where the tokenize expression is defined using the tokenize element. In the following example, the message body is tokenized using the \n separator character. To use a regular expression pattern, set regex=true in the tokenize element.

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <split>
            <tokenize token="\n"/>
            <to uri="mock:result"/>
        </split>
    </route>
    </camelContext>

Splitting into groups of lines

To split a big file into chunks of 1000 lines, you can define a splitter route as follows in the Java DSL:

from("file:inbox")
    .split().tokenize("\n", 1000).streaming()
       .to("activemq:queue:order");

The second argument to tokenize specifies the number of lines that should be grouped into a single chunk. The streaming() clause directs the splitter not to read the whole file at once (resulting in much better performance if the file is large).

The same route can be defined in XML DSL as follows:

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="\n" group="1000"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

The output when using the group option is always of java.lang.String type.

Skip first item

To skip the first item in the message you can use the skipFirst option.

In Java DSL, make the third option in the tokenize parameter true:

from("direct:start")
 // split by new line and group by 3, and skip the very first element
      .split().tokenize("\n", 3, true).streaming()
         .to("mock:group");

The same route can be defined in XML DSL as follows:

<route>
  <from uri="file:inbox"/>
    <split streaming="true">
    <tokenize token="\n" group="1000" skipFirst="true" />
    <to uri="activemq:queue:order"/>
  </split>
</route>

Splitter reply

If the exchange that enters the splitter has the InOut message-exchange pattern (that is, a reply is expected), the splitter returns a copy of the original input message as the reply message in the Out message slot. You can override this default behavior by implementing your own aggregation strategy.

Parallel execution

If you want to execute the resulting pieces of the message in parallel, you can enable the parallel processing option, which instantiates a thread pool to process the message pieces. For example:

XPathBuilder xPathBuilder = new XPathBuilder("//foo/bar");
from("activemq:my.queue").split(xPathBuilder).parallelProcessing().to("activemq:my.parts");

You can customize the underlying ThreadPoolExecutor used in the parallel splitter. For example, you can specify a custom executor in the Java DSL as follows:

XPathBuilder xPathBuilder = new XPathBuilder("//foo/bar");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
from("activemq:my.queue")
  .split(xPathBuilder)
  .parallelProcessing()
  .executorService(threadPoolExecutor)
  .to("activemq:my.parts");

You can specify a custom executor in the XML DSL as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:parallel-custom-pool"/>
    <split executorServiceRef="threadPoolExecutor">
      <xpath>/invoice/lineItems</xpath>
      <to uri="mock:result"/>
    </split>
  </route>
</camelContext>

<bean id="threadPoolExecutor" class="java.util.concurrent.ThreadPoolExecutor">
  <constructor-arg index="0" value="8"/>
  <constructor-arg index="1" value="16"/>
  <constructor-arg index="2" value="0"/>
  <constructor-arg index="3" value="MILLISECONDS"/>
  <constructor-arg index="4"><bean class="java.util.concurrent.LinkedBlockingQueue"/></constructor-arg>
</bean>

Using a bean to perform splitting

As the splitter can use any expression to do the splitting, you can use a bean to perform splitting, by invoking the method() expression. The bean should return an iterable value such as: java.util.Collection, java.util.Iterator, or an array.

The following route defines a method() expression that calls a method on the mySplitterBean bean instance:

from("direct:body")
        // here we use a POJO bean mySplitterBean to do the split of the payload
        .split()
        .method("mySplitterBean", "splitBody")
        .to("mock:result");
from("direct:message")
        // here we use a POJO bean mySplitterBean to do the split of the message
        // with a certain header value
        .split()
        .method("mySplitterBean", "splitMessage")
        .to("mock:result");

Where mySplitterBean is an instance of the MySplitterBean class, which is defined as follows:

public class MySplitterBean {

    /**
     * The split body method returns something that is iteratable such as a java.util.List.
     *
     * @param body the payload of the incoming message
     * @return a list containing each part split
     */
    public List<String> splitBody(String body) {
        // since this is based on an unit test you can of couse
        // use different logic for splitting as {router} have out
        // of the box support for splitting a String based on comma
        // but this is for show and tell, since this is java code
        // you have the full power how you like to split your messages
        List<String> answer = new ArrayList<String>();
        String[] parts = body.split(",");
        for (String part : parts) {
            answer.add(part);
        }
        return answer;
    }

    /**
     * The split message method returns something that is iteratable such as a java.util.List.
     *
     * @param header the header of the incoming message with the name user
     * @param body the payload of the incoming message
     * @return a list containing each part split
     */
    public List<Message> splitMessage(@Header(value = "user") String header, @Body String body) {
        // we can leverage the Parameter Binding Annotations
        // http://camel.apache.org/parameter-binding-annotations.html
        // to access the message header and body at same time,
        // then create the message that we want, splitter will
        // take care rest of them.
        // *NOTE* this feature requires {router} version >= 1.6.1
        List<Message> answer = new ArrayList<Message>();
        String[] parts = header.split(",");
        for (String part : parts) {
            DefaultMessage message = new DefaultMessage();
            message.setHeader("user", part);
            message.setBody(body);
            answer.add(message);
        }
        return answer;
    }
}

You can use aBeanIOSplitter object with the Splitter EIP to split big payloads by using a stream mode to avoid reading the entire content into memory. The following example shows how to set up a BeanIOSplitter object by using the mapping file, which is loaded from the classpath:

Note

The BeanIOSplitter class is new in Camel 2.18. It is not available in Camel 2.17.

BeanIOSplitter splitter = new BeanIOSplitter();
   splitter.setMapping("org/apache/camel/dataformat/beanio/mappings.xml");
   splitter.setStreamName("employeeFile");

    // Following is a route that uses the beanio data format to format CSV data
    // in Java objects:
    from("direct:unmarshal")
        // Here the message body is split to obtain a message for each row:
         .split(splitter).streaming()
         .to("log:line")
         .to("mock:beanio-unmarshal");

The following example adds an error handler:

BeanIOSplitter splitter = new BeanIOSplitter();
   splitter.setMapping("org/apache/camel/dataformat/beanio/mappings.xml");
   splitter.setStreamName("employeeFile");
   splitter.setBeanReaderErrorHandlerType(MyErrorHandler.class);
   from("direct:unmarshal")
      .split(splitter).streaming()
      .to("log:line")
      .to("mock:beanio-unmarshal");

Exchange properties

The following properties are set on each split exchange:

headertypedescription

CamelSplitIndex

int

Apache Camel 2.0: A split counter that increases for each Exchange being split. The counter starts from 0.

CamelSplitSize

int

Apache Camel 2.0: The total number of Exchanges that was split. This header is not applied for stream based splitting.

CamelSplitComplete

boolean

Apache Camel 2.4: Whether or not this Exchange is the last.

Splitter/aggregator pattern

It is a common pattern for the message pieces to be aggregated back into a single exchange, after processing of the individual pieces has completed. To support this pattern, the split() DSL command lets you provide an AggregationStrategy object as the second argument.

Java DSL example

The following example shows how to use a custom aggregation strategy to recombine a split message after all of the message pieces have been processed:

from("direct:start")
    .split(body().tokenize("@"), new MyOrderStrategy())
        // each split message is then send to this bean where we can process it
        .to("bean:MyOrderService?method=handleOrder")
        // this is important to end the splitter route as we do not want to do more routing
        // on each split message
    .end()
    // after we have split and handled each message we want to send a single combined
    // response back to the original caller, so we let this bean build it for us
    // this bean will receive the result of the aggregate strategy: MyOrderStrategy
    .to("bean:MyOrderService?method=buildCombinedResponse")

AggregationStrategy implementation

The custom aggregation strategy, MyOrderStrategy, used in the preceding route is implemented as follows:

/**
 * This is our own order aggregation strategy where we can control
 * how each split message should be combined. As we do not want to
 * lose any message, we copy from the new to the old to preserve the
 * order lines as long we process them
 */
public static class MyOrderStrategy implements AggregationStrategy {

    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        // put order together in old exchange by adding the order from new exchange

        if (oldExchange == null) {
            // the first time we aggregate we only have the new exchange,
            // so we just return it
            return newExchange;
        }

        String orders = oldExchange.getIn().getBody(String.class);
        String newLine = newExchange.getIn().getBody(String.class);

        LOG.debug("Aggregate old orders: " + orders);
        LOG.debug("Aggregate new order: " + newLine);

        // put orders together separating by semi colon
        orders = orders + ";" + newLine;
        // put combined order back on old to preserve it
        oldExchange.getIn().setBody(orders);

        // return old as this is the one that has all the orders gathered until now
        return oldExchange;
    }
}

Stream based processing

When parallel processing is enabled, it is theoretically possible for a later message piece to be ready for aggregation before an earlier piece. In other words, the message pieces might arrive at the aggregator out of order. By default, this does not happen, because the splitter implementation rearranges the message pieces back into their original order before passing them into the aggregator.

If you would prefer to aggregate the message pieces as soon as they are ready (and possibly out of order), you can enable the streaming option, as follows:

from("direct:streaming")
  .split(body().tokenize(","), new MyOrderStrategy())
    .parallelProcessing()
    .streaming()
    .to("activemq:my.parts")
  .end()
  .to("activemq:all.parts");

You can also supply a custom iterator to use with streaming, as follows:

// Java
import static org.apache.camel.builder.ExpressionBuilder.beanExpression;
...
from("direct:streaming")
     .split(beanExpression(new MyCustomIteratorFactory(),  "iterator"))
     .streaming().to("activemq:my.parts")
Streaming and XPath

You cannot use streaming mode in conjunction with XPath. XPath requires the complete DOM XML document in memory.

Stream based processing with XML

If an incoming messages is a very large XML file, you can process the message most efficiently using the tokenizeXML sub-command in streaming mode.

For example, given a large XML file that contains a sequence of order elements, you can split the file into order elements using a route like the following:

from("file:inbox")
  .split().tokenizeXML("order").streaming()
  .to("activemq:queue:order");

You can do the same thing in XML, by defining a route like the following:

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="order" xml="true"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

It is often the case that you need access to namespaces that are defined in one of the enclosing (ancestor) elements of the token elements. You can copy namespace definitions from one of the ancestor elements into the token element, by specifing which element you want to inherit namespace definitions from.

In the Java DSL, you specify the ancestor element as the second argument of tokenizeXML. For example, to inherit namespace definitions from the enclosing orders element:

from("file:inbox")
  .split().tokenizeXML("order", "orders").streaming()
  .to("activemq:queue:order");

In the XML DSL, you specify the ancestor element using the inheritNamespaceTagName attribute. For example:

<route>
  <from uri="file:inbox"/>
  <split streaming="true">
    <tokenize token="order"
              xml="true"
              inheritNamespaceTagName="orders"/>
    <to uri="activemq:queue:order"/>
  </split>
</route>

Options

The split DSL command supports the following options:

Name

Default Value

Description

strategyRef

 

Refers to an AggregationStrategy to be used to assemble the replies from the sub-messages, into a single outgoing message from the Section 8.4, “Splitter”. See the section titled What does the splitter return below for whats used by default.

strategyMethodName

 

This option can be used to explicitly specify the method name to use, when using POJOs as the AggregationStrategy.

strategyMethodAllowNull

false

This option can be used, when using POJOs as the AggregationStrategy. If false, the aggregate method is not used, when there is no data to enrich. If true, null values are used for the oldExchange, when there is no data to enrich.

parallelProcessing

false

If enables then processing the sub-messages occurs concurrently. Note the caller thread will still wait until all sub-messages has been fully processed, before it continues.

parallelAggregate

false

If enabled, the aggregate method on AggregationStrategy can be called concurrently. Note that this requires the implementation of AggregationStrategy to be thread-safe. By default, this option is false, which means that Camel automatically synchronizes calls to the aggregate method. In some use-cases, however, you can improve performance by implementing AggregationStrategy as thread-safe and setting this option to true.

executorServiceRef

 

Refers to a custom Thread Pool to be used for parallel processing. Notice if you set this option, then parallel processing is automatic implied, and you do not have to enable that option as well.

stopOnException

false

Camel 2.2: Whether or not to stop continue processing immediately when an exception occurred. If disable, then Camel continue splitting and process the sub-messages regardless if one of them failed. You can deal with exceptions in the AggregationStrategy class where you have full control how to handle that.

streaming

false

If enabled then Camel will split in a streaming fashion, which means it will split the input message in chunks. This reduces the memory overhead. For example if you split big messages its recommended to enable streaming. If streaming is enabled then the sub-message replies will be aggregated out-of-order, eg in the order they come back. If disabled, Camel will process sub-message replies in the same order as they where splitted.

timeout

 

Camel 2.5: Sets a total timeout specified in millis. If the Section 8.3, “Recipient List” hasn’t been able to split and process all replies within the given timeframe, then the timeout triggers and the Section 8.4, “Splitter” breaks out and continues. Notice if you provide a TimeoutAwareAggregationStrategy then the timeout method is invoked before breaking out.

onPrepareRef

 

Camel 2.8: Refers to a custom Processor to prepare the sub-message of the Exchange, before its processed. This allows you to do any custom logic, such as deep-cloning the message payload if that’s needed etc.

shareUnitOfWork

false

Camel 2.8: Whether the unit of work should be shared. See further below for more details.

8.5. Aggregator

Overview

The aggregator pattern, shown in Figure 8.5, “Aggregator Pattern”, enables you to combine a batch of related messages into a single message.

Figure 8.5. Aggregator Pattern

Aggregator pattern

To control the aggregator’s behavior, Apache Camel allows you to specify the properties described in Enterprise Integration Patterns, as follows:

  • Correlation expression  —  Determines which messages should be aggregated together. The correlation expression is evaluated on each incoming message to produce a correlation key. Incoming messages with the same correlation key are then grouped into the same batch. For example, if you want to aggregate all incoming messages into a single message, you can use a constant expression.
  • Completeness condition  —  Determines when a batch of messages is complete. You can specify this either as a simple size limit or, more generally, you can specify a predicate condition that flags when the batch is complete.
  • Aggregation algorithm  —  Combines the message exchanges for a single correlation key into a single message exchange.

For example, consider a stock market data system that receives 30,000 messages per second. You might want to throttle down the message flow if your GUI tool cannot cope with such a massive update rate. The incoming stock quotes can be aggregated together simply by choosing the latest quote and discarding the older prices. (You can apply a delta processing algorithm, if you prefer to capture some of the history.)

Note

The Aggregator now enlists in JMX using a ManagedAggregateProcessorMBean that includes more information. It enables you to use the aggregate controller to control it.

How the aggregator works

Figure 8.6, “Aggregator Implementation” shows an overview of how the aggregator works, assuming it is fed with a stream of exchanges that have correlation keys such as A, B, C, or D.

Figure 8.6. Aggregator Implementation

message routing 02

The incoming stream of exchanges shown in Figure 8.6, “Aggregator Implementation” is processed as follows:

  1. The correlator is responsible for sorting exchanges based on the correlation key. For each incoming exchange, the correlation expression is evaluated, yielding the correlation key. For example, for the exchange shown in Figure 8.6, “Aggregator Implementation”, the correlation key evaluates to A.
  2. The aggregation strategy is responsible for merging exchanges with the same correlation key. When a new exchange, A, comes in, the aggregator looks up the corresponding aggregate exchange, A', in the aggregation repository and combines it with the new exchange.

    Until a particular aggregation cycle is completed, incoming exchanges are continuously aggregated with the corresponding aggregate exchange. An aggregation cycle lasts until terminated by one of the completion mechanisms.

    Note

    From Camel 2.16, the new XSLT Aggregation Strategy allows you to merge two messages with an XSLT file. You can access the AggregationStrategies.xslt() file from the toolbox.

  3. If a completion predicate is specified on the aggregator, the aggregate exchange is tested to determine whether it is ready to be sent to the next processor in the route. Processing continues as follows:

    • If complete, the aggregate exchange is processed by the latter part of the route. There are two alternative models for this: synchronous (the default), which causes the calling thread to block, or asynchronous (if parallel processing is enabled), where the aggregate exchange is submitted to an executor thread pool (as shown in Figure 8.6, “Aggregator Implementation”).
    • If not complete, the aggregate exchange is saved back to the aggregation repository.
  4. In parallel with the synchronous completion tests, it is possible to enable an asynchronous completion test by enabling either the completionTimeout option or the completionInterval option. These completion tests run in a separate thread and, whenever the completion test is satisfied, the corresponding exchange is marked as complete and starts to be processed by the latter part of the route (either synchronously or asynchronously, depending on whether parallel processing is enabled or not).
  5. If parallel processing is enabled, a thread pool is responsible for processing exchanges in the latter part of the route. By default, this thread pool contains ten threads, but you have the option of customizing the pool (the section called “Threading options”).

Java DSL example

The following example aggregates exchanges with the same StockSymbol header value, using the UseLatestAggregationStrategy aggregation strategy. For a given StockSymbol value, if more than three seconds elapse since the last exchange with that correlation key was received, the aggregated exchange is deemed to be complete and is sent to the mock endpoint.

from("direct:start")
    .aggregate(header("id"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

XML DSL example

The following example shows how to configure the same route in XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy"
                   completionTimeout="3000">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.aggregate.UseLatestAggregationStrategy"/>

Specifying the correlation expression

In the Java DSL, the correlation expression is always passed as the first argument to the aggregate() DSL command. You are not limited to using the Simple expression language here. You can specify a correlation expression using any of the expression languages or scripting languages, such as XPath, XQuery, SQL, and so on.

For exampe, to correlate exchanges using an XPath expression, you could use the following Java DSL route:

from("direct:start")
    .aggregate(xpath("/stockQuote/@symbol"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

If the correlation expression cannot be evaluated on a particular incoming exchange, the aggregator throws a CamelExchangeException by default. You can suppress this exception by setting the ignoreInvalidCorrelationKeys option. For example, in the Java DSL:

from(...).aggregate(...).ignoreInvalidCorrelationKeys()

In the XML DSL, you can set the ignoreInvalidCorrelationKeys option is set as an attribute, as follows:

<aggregate strategyRef="aggregatorStrategy"
           ignoreInvalidCorrelationKeys="true"
           ...>
    ...
</aggregate>

Specifying the aggregation strategy

In Java DSL, you can either pass the aggregation strategy as the second argument to the aggregate() DSL command or specify it using the aggregationStrategy() clause. For example, you can use the aggregationStrategy() clause as follows:

from("direct:start")
    .aggregate(header("id"))
        .aggregationStrategy(new UseLatestAggregationStrategy())
        .completionTimeout(3000)
    .to("mock:aggregated");

Apache Camel provides the following basic aggregation strategies (where the classes belong to the org.apache.camel.processor.aggregate Java package):

UseLatestAggregationStrategy
Return the last exchange for a given correlation key, discarding all earlier exchanges with this key. For example, this strategy could be useful for throttling the feed from a stock exchange, where you just want to know the latest price of a particular stock symbol.
UseOriginalAggregationStrategy
Return the first exchange for a given correlation key, discarding all later exchanges with this key. You must set the first exchange by calling UseOriginalAggregationStrategy.setOriginal() before you can use this strategy.
GroupedExchangeAggregationStrategy
Concatenates all of the exchanges for a given correlation key into a list, which is stored in the Exchange.GROUPED_EXCHANGE exchange property. See the section called “Grouped exchanges”.

Implementing a custom aggregation strategy

If you want to apply a different aggregation strategy, you can implement one of the following aggregation strategy base interfaces:

org.apache.camel.processor.aggregate.AggregationStrategy
The basic aggregation strategy interface.
org.apache.camel.processor.aggregate.TimeoutAwareAggregationStrategy

Implement this interface, if you want your implementation to receive a notification when an aggregation cycle times out. The timeout notification method has the following signature:

void timeout(Exchange oldExchange, int index, int total, long timeout)
org.apache.camel.processor.aggregate.CompletionAwareAggregationStrategy

Implement this interface, if you want your implementation to receive a notification when an aggregation cycle completes normally. The notification method has the following signature:

void onCompletion(Exchange exchange)

For example, the following code shows two different custom aggregation strategies, StringAggregationStrategy and ArrayListAggregationStrategy::

 //simply combines Exchange String body values using '' as a delimiter
 class StringAggregationStrategy implements AggregationStrategy {

     public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
         if (oldExchange == null) {
             return newExchange;
         }

         String oldBody = oldExchange.getIn().getBody(String.class);
         String newBody = newExchange.getIn().getBody(String.class);
         oldExchange.getIn().setBody(oldBody + "" + newBody);
         return oldExchange;
     }
 }

 //simply combines Exchange body values into an ArrayList<Object>
 class ArrayListAggregationStrategy implements AggregationStrategy {

     public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
 	    Object newBody = newExchange.getIn().getBody();
     	ArrayList<Object> list = null;
         if (oldExchange == null) {
 		    list = new ArrayList<Object>();
 		    list.add(newBody);
 		    newExchange.getIn().setBody(list);
 		    return newExchange;
         } else {
 	        list = oldExchange.getIn().getBody(ArrayList.class);
 	    	list.add(newBody);
 		    return oldExchange;
 	    }
     }
 }
Note

Since Apache Camel 2.0, the AggregationStrategy.aggregate() callback method is also invoked for the very first exchange. On the first invocation of the aggregate method, the oldExchange parameter is null and the newExchange parameter contains the first incoming exchange.

To aggregate messages using the custom strategy class, ArrayListAggregationStrategy, define a route like the following:

from("direct:start")
    .aggregate(header("StockSymbol"), new ArrayListAggregationStrategy())
    .completionTimeout(3000)
    .to("mock:result");

You can also configure a route with a custom aggregation strategy in XML, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <aggregate strategyRef="aggregatorStrategy"
               completionTimeout="3000">
      <correlationExpression>
        <simple>header.StockSymbol</simple>
      </correlationExpression>
      <to uri="mock:aggregated"/>
    </aggregate>
  </route>
</camelContext>

<bean id="aggregatorStrategy" class="com.my_package_name.ArrayListAggregationStrategy"/>

Controlling the lifecycle of a custom aggregation strategy

You can implement a custom aggregation strategy so that its lifecycle is aligned with the lifecycle of the enterprise integration pattern that is controlling it. This can be useful for ensuring that the aggregation strategy can shut down gracefully.

To implement an aggregation strategy with lifecycle support, you must implement the org.apache.camel.Service interface (in addition to the AggregationStrategy interface) and provide implementations of the start() and stop() lifecycle methods. For example, the following code example shows an outline of an aggregation strategy with lifecycle support:

// Java
import org.apache.camel.processor.aggregate.AggregationStrategy;
import org.apache.camel.Service;
import java.lang.Exception;
...
class MyAggStrategyWithLifecycleControl
       implements AggregationStrategy, Service {

    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        // Implementation not shown...
        ...
    }

    public void start() throws Exception {
        // Actions to perform when the enclosing EIP starts up
        ...
    }

    public void stop() throws Exception {
        // Actions to perform when the enclosing EIP is stopping
        ...
    }
}

Exchange properties

The following properties are set on each aggregated exchange:

HeaderTypeDescription Aggregated Exchange Properties

Exchange.AGGREGATED_SIZE

int

The total number of exchanges aggregated into this exchange.

Exchange.AGGREGATED_COMPLETED_BY

String

Indicates the mechanism responsible for completing the aggregate exchange. Possible values are: predicate, size, timeout, interval, or consumer.

The following properties are set on exchanges redelivered by the SQL Component aggregation repository (see the section called “Persistent aggregation repository”):

HeaderTypeDescription Redelivered Exchange Properties

Exchange.REDELIVERY_COUNTER

int

Sequence number of the current redelivery attempt (starting at 1).

Specifying a completion condition

It is mandatory to specify at least one completion condition, which determines when an aggregate exchange leaves the aggregator and proceeds to the next node on the route. The following completion conditions can be specified:

completionPredicate
Evaluates a predicate after each exchange is aggregated in order to determine completeness. A value of true indicates that the aggregate exchange is complete. Alternatively, instead of setting this option, you can define a custom AggregationStrategy that implements the Predicate interface, in which case the AggregationStrategy will be used as the completion predicate.
completionSize
Completes the aggregate exchange after the specified number of incoming exchanges are aggregated.
completionTimeout

(Incompatible with completionInterval) Completes the aggregate exchange, if no incoming exchanges are aggregated within the specified timeout.

In other words, the timeout mechanism keeps track of a timeout for each correlation key value. The clock starts ticking after the latest exchange with a particular key value is received. If another exchange with the same key value is not received within the specified timeout, the corresponding aggregate exchange is marked complete and sent to the next node on the route.

completionInterval

(Incompatible with completionTimeout) Completes all outstanding aggregate exchanges, after each time interval (of specified length) has elapsed.

The time interval is not tailored to each aggregate exchange. This mechanism forces simultaneous completion of all outstanding aggregate exchanges. Hence, in some cases, this mechanism could complete an aggregate exchange immediately after it started aggregating.

completionFromBatchConsumer
When used in combination with a consumer endpoint that supports the batch consumer mechanism, this completion option automatically figures out when the current batch of exchanges is complete, based on information it receives from the consumer endpoint. See the section called “Batch consumer”.
forceCompletionOnStop
When this option is enabled, it forces completion of all outstanding aggregate exchanges when the current route context is stopped.

The preceding completion conditions can be combined arbitrarily, except for the completionTimeout and completionInterval conditions, which cannot be simultaneously enabled. When conditions are used in combination, the general rule is that the first completion condition to trigger is the effective completion condition.

Specifying the completion predicate

You can specify an arbitrary predicate expression that determines when an aggregated exchange is complete. There are two possible ways of evaluating the predicate expression:

  • On the latest aggregate exchange — this is the default behavior.
  • On the latest incoming exchange — this behavior is selected when you enable the eagerCheckCompletion option.

For example, if you want to terminate a stream of stock quotes every time you receive an ALERT message (as indicated by the value of a MsgType header in the latest incoming exchange), you can define a route like the following:

from("direct:start")
    .aggregate(
      header("id"),
      new UseLatestAggregationStrategy()
    )
        .completionPredicate(
          header("MsgType").isEqualTo("ALERT")
         )
        .eagerCheckCompletion()
    .to("mock:result");

The following example shows how to configure the same route using XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <aggregate strategyRef="aggregatorStrategy"
               eagerCheckCompletion="true">
      <correlationExpression>
          <simple>header.StockSymbol</simple>
      </correlationExpression>
      <completionPredicate>
          <simple>$MsgType = 'ALERT'</simple>
      </completionPredicate>
      <to uri="mock:result"/>
    </aggregate>
  </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.aggregate.UseLatestAggregationStrategy"/>

Specifying a dynamic completion timeout

It is possible to specify a dynamic completion timeout, where the timeout value is recalculated for every incoming exchange. For example, to set the timeout value from the timeout header in each incoming exchange, you could define a route as follows:

from("direct:start")
    .aggregate(header("StockSymbol"), new UseLatestAggregationStrategy())
        .completionTimeout(header("timeout"))
    .to("mock:aggregated");

You can configure the same route in the XML DSL, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <completionTimeout>
                <header>timeout</header>
            </completionTimeout>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.UseLatestAggregationStrategy"/>
Note

You can also add a fixed timeout value and Apache Camel will fall back to use this value, if the dynamic value is null or 0.

Specifying a dynamic completion size

It is possible to specify a dynamic completion size, where the completion size is recalculated for every incoming exchange. For example, to set the completion size from the mySize header in each incoming exchange, you could define a route as follows:

from("direct:start")
    .aggregate(header("StockSymbol"), new UseLatestAggregationStrategy())
        .completionSize(header("mySize"))
    .to("mock:aggregated");

And the same example using Spring XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:start"/>
        <aggregate strategyRef="aggregatorStrategy">
            <correlationExpression>
                <simple>header.StockSymbol</simple>
            </correlationExpression>
            <completionSize>
                <header>mySize</header>
            </completionSize>
            <to uri="mock:aggregated"/>
        </aggregate>
    </route>
</camelContext>

<bean id="aggregatorStrategy"
      class="org.apache.camel.processor.UseLatestAggregationStrategy"/>
Note

You can also add a fixed size value and Apache Camel will fall back to use this value, if the dynamic value is null or 0.

Forcing completion of a single group from within an AggregationStrategy

If you implement a custom AggregationStrategy class, there is a mechanism available to force the completion of the current message group, by setting the Exchange.AGGREGATION_COMPLETE_CURRENT_GROUP exchange property to true on the exchange returned from the AggregationStrategy.aggregate() method. This mechanism only affects the current group: other message groups (with different correlation IDs) are not forced to complete. This mechanism overrides any other completion mechanisms, such as predicate, size, timeout, and so on.

For example, the following sample AggregationStrategy class completes the current group, if the message body size is larger than 5:

// Java
public final class MyCompletionStrategy implements AggregationStrategy {
    @Override
    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        if (oldExchange == null) {
            return newExchange;
        }
        String body = oldExchange.getIn().getBody(String.class) + "+"
            + newExchange.getIn().getBody(String.class);
        oldExchange.getIn().setBody(body);
        if (body.length() >= 5) {
            oldExchange.setProperty(Exchange.AGGREGATION_COMPLETE_CURRENT_GROUP, true);
        }
        return oldExchange;
    }
}

Forcing completion of all groups with a special message

It is possible to force completion of all outstanding aggregate messages, by sending a message with a special header to the route. There are two alternative header settings you can use to force completion:

Exchange.AGGREGATION_COMPLETE_ALL_GROUPS
Set to true, to force completion of the current aggregation cycle. This message acts purely as a signal and is not included in any aggregation cycle. After processing this signal message, the content of the message is discarded.
Exchange.AGGREGATION_COMPLETE_ALL_GROUPS_INCLUSIVE
Set to true, to force completion of the current aggregation cycle. This message is included in the current aggregation cycle.

Using AggregateController

The org.apache.camel.processor.aggregate.AggregateController enables you to control the aggregate at runtime using Java or JMX API. This can be used to force completing groups of exchanges, or query the current runtime statistics.

If no custom have been configured, the aggregator provides a default implementation which you can access using the getAggregateController() method. However, it is easy to configure a controller in the route using aggregateController.

private AggregateController controller = new DefaultAggregateController();

from("direct:start")
   .aggregate(header("id"), new MyAggregationStrategy()).completionSize(10).id("myAggregator")
      .aggregateController(controller)
      .to("mock:aggregated");

Also, you can use the API on AggregateControllerto force completion. For example, to complete a group with key foo

int groups = controller.forceCompletionOfGroup("foo");

The number return would be the number of groups completed. Following is an API to complete all groups:

 int groups = controller.forceCompletionOfAllGroups();

Enforcing unique correlation keys

In some aggregation scenarios, you might want to enforce the condition that the correlation key is unique for each batch of exchanges. In other words, when the aggregate exchange for a particular correlation key completes, you want to make sure that no further aggregate exchanges with that correlation key are allowed to proceed. For example, you might want to enforce this condition, if the latter part of the route expects to process exchanges with unique correlation key values.

Depending on how the completion conditions are configured, there might be a risk of more than one aggregate exchange being generated with a particular correlation key. For example, although you might define a completion predicate that is designed to wait until all the exchanges with a particular correlation key are received, you might also define a completion timeout, which could fire before all of the exchanges with that key have arrived. In this case, the late-arriving exchanges could give rise to a second aggregate exchange with the same correlation key value.

For such scenarios, you can configure the aggregator to suppress aggregate exchanges that duplicate previous correlation key values, by setting the closeCorrelationKeyOnCompletion option. In order to suppress duplicate correlation key values, it is necessary for the aggregator to record previous correlation key values in a cache. The size of this cache (the number of cached correlation keys) is specified as an argument to the closeCorrelationKeyOnCompletion() DSL command. To specify a cache of unlimited size, you can pass a value of zero or a negative integer. For example, to specify a cache size of 10000 key values:

from("direct:start")
    .aggregate(header("UniqueBatchID"), new MyConcatenateStrategy())
        .completionSize(header("mySize"))
        .closeCorrelationKeyOnCompletion(10000)
    .to("mock:aggregated");

If an aggregate exchange completes with a duplicate correlation key value, the aggregator throws a ClosedCorrelationKeyException exception.

Stream based processing using Simple expressions

You can use Simple language expressions as the token with the tokenizeXML sub-command in streaming mode. Using Simple language expressions will enable support for dynamic tokens.

For example, to use Java to split a sequence of names delineated up by the tag person, you can split the file into name elements using the tokenizeXML bean and a Simple language token.

public void testTokenizeXMLPairSimple() throws Exception {
        Expression exp = TokenizeLanguage.tokenizeXML("${header.foo}", null);

Get the input string of names delineated by <person> and set <person> as the token.

        exchange.getIn().setHeader("foo", "<person>");
        exchange.getIn().setBody("<persons><person>James</person><person>Claus</person><person>Jonathan</person><person>Hadrian</person></persons>");

List the names split from the input.

        List<?> names = exp.evaluate(exchange, List.class);
        assertEquals(4, names.size());

        assertEquals("<person>James</person>", names.get(0));
        assertEquals("<person>Claus</person>", names.get(1));
        assertEquals("<person>Jonathan</person>", names.get(2));
        assertEquals("<person>Hadrian</person>", names.get(3));
    }

Grouped exchanges

You can combine all of the aggregated exchanges in an outgoing batch into a single org.apache.camel.impl.GroupedExchange holder class. To enable grouped exchanges, specify the groupExchanges() option, as shown in the following Java DSL route:

from("direct:start")
    .aggregate(header("StockSymbol"))
        .completionTimeout(3000)
        .groupExchanges()
    .to("mock:result");

The grouped exchange sent to mock:result contains the list of aggregated exchanges in the message body. The following line of code shows how a subsequent processor can access the contents of the grouped exchange in the form of a list:

// Java
List<Exchange> grouped = ex.getIn().getBody(List.class);
Note

When you enable the grouped exchanges feature, you must not configure an aggregation strategy (the grouped exchanges feature is itself an aggregation strategy).

Note

The old approach of accessing the grouped exchanges from a property on the outgoing exchange is now deprecated and will be removed in a future release.

Batch consumer

The aggregator can work together with the batch consumer pattern to aggregate the total number of messages reported by the batch consumer (a batch consumer endpoint sets the CamelBatchSize, CamelBatchIndex , and CamelBatchComplete properties on the incoming exchange). For example, to aggregate all of the files found by a File consumer endpoint, you could use a route like the following:

from("file://inbox")
    .aggregate(xpath("//order/@customerId"), new AggregateCustomerOrderStrategy())
    .completionFromBatchConsumer()
    .to("bean:processOrder");

Currently, the following endpoints support the batch consumer mechanism: File, FTP, Mail, iBatis, and JPA.

Persistent aggregation repository

The default aggregator uses an in-memory only AggregationRepository. If you want to store pending aggregated exchanges persistently, you can use the SQL Component as a persistent aggregation repository. The SQL Component includes a JdbcAggregationRepository that persists aggregated messages on-the-fly, and ensures that you do not lose any messages.

When an exchange has been successfully processed, it is marked as complete when the confirm method is invoked on the repository. This means that if the same exchange fails again, it will be retried until it is successful.

Add a dependency on camel-sql

To use the SQL Component, you must include a dependency on camel-sql in your project. For example, if you are using a Maven pom.xml file:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-sql</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

Create the aggregation database tables

You must create separate aggregation and completed database tables for persistence. For example, the following query creates the tables for a database named my_aggregation_repo:

CREATE TABLE my_aggregation_repo (
 id varchar(255) NOT NULL,
 exchange blob NOT NULL,
 constraint aggregation_pk PRIMARY KEY (id)
);

CREATE TABLE my_aggregation_repo_completed (
 id varchar(255) NOT NULL,
 exchange blob NOT NULL,
 constraint aggregation_completed_pk PRIMARY KEY (id)
);
}

Configure the aggregation repository

You must also configure the aggregation repository in your framework XML file (for example, Spring or Blueprint):

<bean id="my_repo"
    class="org.apache.camel.processor.aggregate.jdbc.JdbcAggregationRepository">
    <property name="repositoryName" value="my_aggregation_repo"/>
    <property name="transactionManager" ref="my_tx_manager"/>
    <property name="dataSource" ref="my_data_source"/>
    ...
</bean>

The repositoryName, transactionManager, and dataSource properties are required. For details on more configuration options for the persistent aggregation repository, see SQL Component in the Apache Camel Component Reference Guide.

Threading options

As shown in Figure 8.6, “Aggregator Implementation”, the aggregator is decoupled from the latter part of the route, where the exchanges sent to the latter part of the route are processed by a dedicated thread pool. By default, this pool contains just a single thread. If you want to specify a pool with multiple threads, enable the parallelProcessing option, as follows:

from("direct:start")
    .aggregate(header("id"), new UseLatestAggregationStrategy())
        .completionTimeout(3000)
        .parallelProcessing()
    .to("mock:aggregated");

By default, this creates a pool with 10 worker threads.

If you want to exercise more control over the created thread pool, specify a custom java.util.concurrent.ExecutorService instance using the executorService option (in which case it is unnecessary to enable the parallelProcessing option).

Aggregating into a List

A common aggregation scenario involves aggregating a series of incoming message bodies into a List object. To facilitate this scenario, Apache Camel provides the AbstractListAggregationStrategy abstract class, which you can quickly extend to create an aggregation strategy for this case. Incoming message bodies of type, T, are aggregated into a completed exchange, with a message body of type List<T>.

For example, to aggregate a series of Integer message bodies into a List<Integer> object, you could use an aggregation strategy defined as follows:

import org.apache.camel.processor.aggregate.AbstractListAggregationStrategy;
...
/**
 * Strategy to aggregate integers into a List<Integer>.
 */
public final class MyListOfNumbersStrategy extends AbstractListAggregationStrategy<Integer> {
 
    @Override
    public Integer getValue(Exchange exchange) {
        // the message body contains a number, so just return that as-is
        return exchange.getIn().getBody(Integer.class);
    }
}

Aggregator options

The aggregator supports the following options:

Table 8.1. Aggregator Options

OptionDefaultDescription

correlationExpression

 

Mandatory Expression which evaluates the correlation key to use for aggregation. The Exchange which has the same correlation key is aggregated together. If the correlation key could not be evaluated an Exception is thrown. You can disable this by using the ignoreBadCorrelationKeys option.

aggregationStrategy

 

Mandatory AggregationStrategy which is used to merge the incoming Exchange with the existing already merged exchanges. At first call the oldExchange parameter is null. On subsequent invocations the oldExchange contains the merged exchanges and newExchange is of course the new incoming Exchange. From Camel 2.9.2 onwards, the strategy can optionally be a TimeoutAwareAggregationStrategy implementation, which supports a timeout callback. From Camel 2.16 onwards, the strategy can also be a PreCompletionAwareAggregationStrategy implementation. It runs the completion check in a pre-completion mode.

strategyRef

 

A reference to lookup the AggregationStrategy in the Registry.

completionSize

 

Number of messages aggregated before the aggregation is complete. This option can be set as either a fixed value or using an Expression which allows you to evaluate a size dynamically - will use Integer as result. If both are set Camel will fallback to use the fixed value if the Expression result was null or 0.

completionTimeout

 

Time in millis that an aggregated exchange should be inactive before its complete. This option can be set as either a fixed value or using an Expression which allows you to evaluate a timeout dynamically - will use Long as result. If both are set Camel will fallback to use the fixed value if the Expression result was null or 0. You cannot use this option together with completionInterval, only one of the two can be used.

completionInterval

 

A repeating period in millis by which the aggregator will complete all current aggregated exchanges. Camel has a background task which is triggered every period. You cannot use this option together with completionTimeout, only one of them can be used.

completionPredicate

 

Specifies a predicate (of org.apache.camel.Predicate type), which signals when an aggregated exchange is complete. Alternatively, instead of setting this option, you can define a custom AggregationStrategy that implements the Predicate interface, in which case the AggregationStrategy will be used as the completion predicate.

completionFromBatchConsumer

false

This option is if the exchanges are coming from a Batch Consumer. Then when enabled the Section 8.5, “Aggregator” will use the batch size determined by the Batch Consumer in the message header CamelBatchSize. See more details at Batch Consumer. This can be used to aggregate all files consumed from a see File endpoint in that given poll.

eagerCheckCompletion

false

Whether or not to eager check for completion when a new incoming Exchange has been received. This option influences the behavior of the completionPredicate option as the Exchange being passed in changes accordingly. When false the Exchange passed in the Predicate is the aggregated Exchange which means any information you may store on the aggregated Exchange from the AggregationStrategy is available for the Predicate. When true the Exchange passed in the Predicate is the incoming Exchange, which means you can access data from the incoming Exchange.

forceCompletionOnStop

false

If true, complete all aggregated exchanges when the current route context is stopped.

groupExchanges

false

If enabled then Camel will group all aggregated Exchanges into a single combined org.apache.camel.impl.GroupedExchange holder class that holds all the aggregated Exchanges. And as a result only one Exchange is being sent out from the aggregator. Can be used to combine many incoming Exchanges into a single output Exchange without coding a custom AggregationStrategy yourself.

ignoreInvalidCorrelationKeys

false

Whether or not to ignore correlation keys which could not be evaluated to a value. By default Camel will throw an Exception, but you can enable this option and ignore the situation instead.

closeCorrelationKeyOnCompletion

 

Whether or not late Exchanges should be accepted or not. You can enable this to indicate that if a correlation key has already been completed, then any new exchanges with the same correlation key be denied. Camel will then throw a closedCorrelationKeyException exception. When using this option you pass in a integer which is a number for a LRUCache which keeps that last X number of closed correlation keys. You can pass in 0 or a negative value to indicate a unbounded cache. By passing in a number you are ensured that cache wont grown too big if you use a log of different correlation keys.

discardOnCompletionTimeout

false

Camel 2.5: Whether or not exchanges which complete due to a timeout should be discarded. If enabled, then when a timeout occurs the aggregated message will not be sent out but dropped (discarded).

aggregationRepository

 

Allows you to plug in you own implementation of org.apache.camel.spi.AggregationRepository which keeps track of the current inflight aggregated exchanges. Camel uses by default a memory based implementation.

aggregationRepositoryRef

 

Reference to lookup a aggregationRepository in the Registry.

parallelProcessing

false

When aggregated are completed they are being send out of the aggregator. This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency. If no custom thread pool has been specified then Camel creates a default pool with 10 concurrent threads.

executorService

 

If using parallelProcessing you can specify a custom thread pool to be used. In fact also if you are not using parallelProcessing this custom thread pool is used to send out aggregated exchanges as well.

executorServiceRef

 

Reference to lookup a executorService in the Registry

timeoutCheckerExecutorService

 

If using one of the completionTimeout, completionTimeoutExpression, or completionInterval options, a background thread is created to check for the completion for every aggregator. Set this option to provide a custom thread pool to be used rather than creating a new thread for every aggregator.

timeoutCheckerExecutorServiceRef

 

Reference to look up a timeoutCheckerExecutorService in the registry.

completeAllOnStop

 

When you stop the Aggregator, this option allows it to complete all pending exchanges from the aggregation repository.

optimisticLocking

false

Turns on optimistic locking, which can be used in combination with an aggregation repository.

optimisticLockRetryPolicy

 

Configures the retry policy for optimistic locking.

8.6. Resequencer

Overview

The resequencer pattern, shown in Figure 8.7, “Resequencer Pattern”, enables you to resequence messages according to a sequencing expression. Messages that generate a low value for the sequencing expression are moved to the front of the batch and messages that generate a high value are moved to the back.

Figure 8.7. Resequencer Pattern

Resequencer pattern

Apache Camel supports two resequencing algorithms:

  • Batch resequencing  —  Collects messages into a batch, sorts the messages and sends them to their output.
  • Stream resequencing  —  Re-orders (continuous) message streams based on the detection of gaps between messages.

By default the resequencer does not support duplicate messages and will only keep the last message, in cases where a message arrives with the same message expression. However, in batch mode you can enable the resequencer to allow duplicates.

Batch resequencing

The batch resequencing algorithm is enabled by default. For example, to resequence a batch of incoming messages based on the value of a timestamp contained in the TimeStamp header, you can define the following route in Java DSL:

from("direct:start").resequence(header("TimeStamp")).to("mock:result");

By default, the batch is obtained by collecting all of the incoming messages that arrive in a time interval of 1000 milliseconds (default batch timeout), up to a maximum of 100 messages (default batch size). You can customize the values of the batch timeout and the batch size by appending a batch() DSL command, which takes a BatchResequencerConfig instance as its sole argument. For example, to modify the preceding route so that the batch consists of messages collected in a 4000 millisecond time window, up to a maximum of 300 messages, you can define the Java DSL route as follows:

import org.apache.camel.model.config.BatchResequencerConfig;

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("direct:start").resequence(header("TimeStamp")).batch(new BatchResequencerConfig(300,4000L)).to("mock:result");
    }
};

You can also specify a batch resequencer pattern using XML configuration. The following example defines a batch resequencer with a batch size of 300 and a batch timeout of 4000 milliseconds:

<camelContext id="resequencerBatch" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start" />
    <resequence>
      <!--
        batch-config can be omitted for default (batch) resequencer settings
      -->
      <batch-config batchSize="300" batchTimeout="4000" />
      <simple>header.TimeStamp</simple>
      <to uri="mock:result" />
    </resequence>
  </route>
</camelContext>

Batch options

Table 8.2, “Batch Resequencer Options” shows the options that are available in batch mode only.

Table 8.2. Batch Resequencer Options

Java DSLXML DSLDefaultDescription

allowDuplicates()

batch-config/@allowDuplicates

false

If true, do not discard duplicate messages from the batch (where duplicate means that the message expression evaluates to the same value).

reverse()

batch-config/@reverse

false

If true, put the messages in reverse order (where the default ordering applied to a message expression is based on Java’s string lexical ordering, as defined by String.compareTo()).

For example, if you want to resequence messages from JMS queues based on JMSPriority, you would need to combine the options, allowDuplicates and reverse, as follows:

from("jms:queue:foo")
        // sort by JMSPriority by allowing duplicates (message can have same JMSPriority)
        // and use reverse ordering so 9 is first output (most important), and 0 is last
        // use batch mode and fire every 3th second
        .resequence(header("JMSPriority")).batch().timeout(3000).allowDuplicates().reverse()
        .to("mock:result");

Stream resequencing

To enable the stream resequencing algorithm, you must append stream() to the resequence() DSL command. For example, to resequence incoming messages based on the value of a sequence number in the seqnum header, you define a DSL route as follows:

from("direct:start").resequence(header("seqnum")).stream().to("mock:result");

The stream-processing resequencer algorithm is based on the detection of gaps in a message stream, rather than on a fixed batch size. Gap detection, in combination with timeouts, removes the constraint of needing to know the number of messages of a sequence (that is, the batch size) in advance. Messages must contain a unique sequence number for which a predecessor and a successor is known. For example a message with the sequence number 3 has a predecessor message with the sequence number 2 and a successor message with the sequence number 4. The message sequence 2,3,5 has a gap because the successor of 3 is missing. The resequencer therefore must retain message 5 until message 4 arrives (or a timeout occurs).

By default, the stream resequencer is configured with a timeout of 1000 milliseconds, and a maximum message capacity of 100. To customize the stream’s timeout and message capacity, you can pass a StreamResequencerConfig object as an argument to stream(). For example, to configure a stream resequencer with a message capacity of 5000 and a timeout of 4000 milliseconds, you define a route as follows:

// Java
import org.apache.camel.model.config.StreamResequencerConfig;

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("direct:start").resequence(header("seqnum")).
            stream(new StreamResequencerConfig(5000, 4000L)).
            to("mock:result");
    }
};

If the maximum time delay between successive messages (that is, messages with adjacent sequence numbers) in a message stream is known, the resequencer’s timeout parameter should be set to this value. In this case, you can guarantee that all messages in the stream are delivered in the correct order to the next processor. The lower the timeout value that is compared to the out-of-sequence time difference, the more likely it is that the resequencer will deliver messages out of sequence. Large timeout values should be supported by sufficiently high capacity values, where the capacity parameter is used to prevent the resequencer from running out of memory.

If you want to use sequence numbers of some type other than long, you would must define a custom comparator, as follows:

// Java
ExpressionResultComparator<Exchange> comparator = new MyComparator();
StreamResequencerConfig config = new StreamResequencerConfig(5000, 4000L, comparator);
from("direct:start").resequence(header("seqnum")).stream(config).to("mock:result");

You can also specify a stream resequencer pattern using XML configuration. The following example defines a stream resequencer with a message capacity of 5000 and a timeout of 4000 milliseconds:

<camelContext id="resequencerStream" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <resequence>
      <stream-config capacity="5000" timeout="4000"/>
      <simple>header.seqnum</simple>
      <to uri="mock:result" />
    </resequence>
  </route>
</camelContext>

Ignore invalid exchanges

The resequencer EIP throws a CamelExchangeException exception, if the incoming exchange is not valid — that is, if the sequencing expression cannot be evaluated for some reason (for example, due to a missing header). You can use the ignoreInvalidExchanges option to ignore these exceptions, which means the resequencer will skip any invalid exchanges.

from("direct:start")
  .resequence(header("seqno")).batch().timeout(1000)
    // ignore invalid exchanges (they are discarded)
    .ignoreInvalidExchanges()
  .to("mock:result");

Reject old messages

The rejectOld option can be used to prevent messages being sent out of order, regardless of the mechanism used to resequence messages. When the rejectOld option is enabled, the resequencer rejects an incoming message (by throwing a MessageRejectedException exception), if the incoming messages is older (as defined by the current comparator) than the last delivered message.

from("direct:start")
    .onException(MessageRejectedException.class).handled(true).to("mock:error").end()
    .resequence(header("seqno")).stream().timeout(1000).rejectOld()
    .to("mock:result");

8.7. Routing Slip

Overview

The routing slip pattern, shown in Figure 8.8, “Routing Slip Pattern”, enables you to route a message consecutively through a series of processing steps, where the sequence of steps is not known at design time and can vary for each message. The list of endpoints through which the message should pass is stored in a header field (the slip), which Apache Camel reads at run time to construct a pipeline on the fly.

Figure 8.8. Routing Slip Pattern

routing slip

The slip header

The routing slip appears in a user-defined header, where the header value is a comma-separated list of endpoint URIs. For example, a routing slip that specifies a sequence of security tasks — decrypting, authenticating, and de-duplicating a message — might look like the following:

cxf:bean:decrypt,cxf:bean:authenticate,cxf:bean:dedup

The current endpoint property

From Camel 2.5 the Routing Slip will set a property (Exchange.SLIP_ENDPOINT) on the exchange which contains the current endpoint as it advanced though the slip. This enables you to find out how far the exchange has progressed through the slip.

The Section 8.7, “Routing Slip” will compute the slip beforehand which means, the slip is only computed once. If you need to compute the slip on-the-fly then use the Section 8.18, “Dynamic Router” pattern instead.

Java DSL example

The following route takes messages from the direct:a endpoint and reads a routing slip from the aRoutingSlipHeader header:

from("direct:b").routingSlip("aRoutingSlipHeader");

You can specify the header name either as a string literal or as an expression.

You can also customize the URI delimiter using the two-argument form of routingSlip(). The following example defines a route that uses the aRoutingSlipHeader header key for the routing slip and uses the # character as the URI delimiter:

from("direct:c").routingSlip("aRoutingSlipHeader", "#");

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext id="buildRoutingSlip" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:c"/>
    <routingSlip uriDelimiter="#">
      <headerName>aRoutingSlipHeader</headerName>
    </routingSlip>
  </route>
</camelContext>

Ignore invalid endpoints

The Section 8.7, “Routing Slip” now supports ignoreInvalidEndpoints, which the Section 8.3, “Recipient List” pattern also supports. You can use it to skip endpoints that are invalid. For example:

    from("direct:a").routingSlip("myHeader").ignoreInvalidEndpoints();

In Spring XML, this feature is enabled by setting the ignoreInvalidEndpoints attribute on the <routingSlip> tag:

   <route>
       <from uri="direct:a"/>
       <routingSlip ignoreInvalidEndpoints="true">
         <headerName>myHeader</headerName>
       </routingSlip>
   </route>

Consider the case where myHeader contains the two endpoints, direct:foo,xxx:bar. The first endpoint is valid and works. The second is invalid and, therefore, ignored. Apache Camel logs at INFO level whenever an invalid endpoint is encountered.

Options

The routingSlip DSL command supports the following options:

Name

Default Value

Description

uriDelimiter

,

Delimiter used if the Expression returned multiple endpoints.

ignoreInvalidEndpoints

false

If an endpoint uri could not be resolved, should it be ignored. Otherwise Camel will thrown an exception stating the endpoint uri is not valid.

cacheSize

0

Camel 2.13.1/2.12.4: Allows to configure the cache size for the ProducerCache which caches producers for reuse in the routing slip. Will by default use the default cache size which is 0. Setting the value to -1 allows to turn off the cache all together.

8.8. Throttler

Overview

A throttler is a processor that limits the flow rate of incoming messages. You can use this pattern to protect a target endpoint from getting overloaded. In Apache Camel, you can implement the throttler pattern using the throttle() Java DSL command.

Java DSL example

To limit the flow rate to 100 messages per second, define a route as follows:

from("seda:a").throttle(100).to("seda:b");

If necessary, you can customize the time period that governs the flow rate using the timePeriodMillis() DSL command. For example, to limit the flow rate to 3 messages per 30000 milliseconds, define a route as follows:

from("seda:a").throttle(3).timePeriodMillis(30000).to("mock:result");

XML configuration example

The following example shows how to configure the preceding route in XML:

<camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="seda:a"/>
    <!-- throttle 3 messages per 30 sec -->
    <throttle timePeriodMillis="30000">
      <constant>3</constant>
      <to uri="mock:result"/>
    </throttle>
  </route>
</camelContext>

Dynamically changing maximum requests per period

Available os of Camel 2.8 Since we use an Expression, you can adjust this value at runtime, for example you can provide a header with the value. At runtime Camel evaluates the expression and converts the result to a java.lang.Long type. In the example below we use a header from the message to determine the maximum requests per period. If the header is absent, then the Section 8.8, “Throttler” uses the old value. So that allows you to only provide a header if the value is to be changed:

<camelContext id="throttleRoute" xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:expressionHeader"/>
    <throttle timePeriodMillis="500">
      <!-- use a header to determine how many messages to throttle per 0.5 sec -->
      <header>throttleValue</header>
      <to uri="mock:result"/>
    </throttle>
  </route>
</camelContext>

Asynchronous delaying

The throttler can enable non-blocking asynchronous delaying, which means that Apache Camel schedules a task to be executed in the future. The task is responsible for processing the latter part of the route (after the throttler). This allows the caller thread to unblock and service further incoming messages. For example:

from("seda:a").throttle(100).asyncDelayed().to("seda:b");
Note

From Camel 2.17, the Throttler will use the rolling window for time periods that give a better flow of messages. However, It will enhance the performance of a throttler.

Options

The throttle DSL command supports the following options:

Name

Default Value

Description

maximumRequestsPerPeriod

 

Maximum number of requests per period to throttle. This option must be provided and a positive number. Notice, in the XML DSL, from Camel 2.8 onwards this option is configured using an Expression instead of an attribute.

timePeriodMillis

1000

The time period in millis, in which the throttler will allow at most maximumRequestsPerPeriod number of messages.

asyncDelayed

false

Camel 2.4: If enabled then any messages which is delayed happens asynchronously using a scheduled thread pool.

executorServiceRef

 

Camel 2.4: Refers to a custom Thread Pool to be used if asyncDelay has been enabled.

callerRunsWhenRejected

true

Camel 2.4: Is used if asyncDelayed was enabled. This controls if the caller thread should execute the task if the thread pool rejected the task.

8.9. Delayer

Overview

A delayer is a processor that enables you to apply a relative time delay to incoming messages.

Java DSL example

You can use the delay() command to add a relative time delay, in units of milliseconds, to incoming messages. For example, the following route delays all incoming messages by 2 seconds:

from("seda:a").delay(2000).to("mock:result");

Alternatively, you can specify the time delay using an expression:

from("seda:a").delay(header("MyDelay")).to("mock:result");

The DSL commands that follow delay() are interpreted as sub-clauses of delay(). Hence, in some contexts it is necessary to terminate the sub-clauses of delay() by inserting the end() command. For example, when delay() appears inside an onException() clause, you would terminate it as follows:

from("direct:start")
    .onException(Exception.class)
        .maximumRedeliveries(2)
        .backOffMultiplier(1.5)
        .handled(true)
        .delay(1000)
            .log("Halting for some time")
            .to("mock:halt")
        .end()
    .end()
    .to("mock:result");

XML configuration example

The following example demonstrates the delay in XML DSL:

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="seda:a"/>
        <delay>
            <header>MyDelay</header>
        </delay>
        <to uri="mock:result"/>
    </route>
    <route>
        <from uri="seda:b"/>
        <delay>
            <constant>1000</constant>
        </delay>
        <to uri="mock:result"/>
    </route>
</camelContext>

Creating a custom delay

You can use an expression combined with a bean to determine the delay as follows:

from("activemq:foo").
  delay().expression().method("someBean", "computeDelay").
  to("activemq:bar");

Where the bean class could be defined as follows:

public class SomeBean {
  public long computeDelay() {
     long delay = 0;
     // use java code to compute a delay value in millis
     return delay;
 }
}

Asynchronous delaying

You can let the delayer use non-blocking asynchronous delaying, which means that Apache Camel schedules a task to be executed in the future. The task is responsible for processing the latter part of the route (after the delayer). This allows the caller thread to unblock and service further incoming messages. For example:

from("activemq:queue:foo")
    .delay(1000)
    .asyncDelayed()
    .to("activemq:aDelayedQueue");

The same route can be written in the XML DSL, as follows:

<route>
   <from uri="activemq:queue:foo"/>
   <delay asyncDelayed="true">
       <constant>1000</constant>
   </delay>
   <to uri="activemq:aDealyedQueue"/>
   </route>

Options

The delayer pattern supports the following options:

Name

Default Value

Description

asyncDelayed

false

Camel 2.4: If enabled then delayed messages happens asynchronously using a scheduled thread pool.

executorServiceRef

 

Camel 2.4: Refers to a custom Thread Pool to be used if asyncDelay has been enabled.

callerRunsWhenRejected

true

Camel 2.4: Is used if asyncDelayed was enabled. This controls if the caller thread should execute the task if the thread pool rejected the task.

8.10. Load Balancer

Overview

The load balancer pattern allows you to delegate message processing to one of several endpoints, using a variety of different load-balancing policies.

Java DSL example

The following route distributes incoming messages between the target endpoints, mock:x, mock:y, mock:z, using a round robin load-balancing policy:

from("direct:start").loadBalance().roundRobin().to("mock:x", "mock:y", "mock:z");

XML configuration example

The following example shows how to configure the same route in XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <roundRobin/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

Load-balancing policies

The Apache Camel load balancer supports the following load-balancing policies:

Round robin

The round robin load-balancing policy cycles through all of the target endpoints, sending each incoming message to the next endpoint in the cycle. For example, if the list of target endpoints is, mock:x, mock:y, mock:z, then the incoming messages are sent to the following sequence of endpoints: mock:x, mock:y, mock:z, mock:x, mock:y, mock:z, and so on.

You can specify the round robin load-balancing policy in Java DSL, as follows:

from("direct:start").loadBalance().roundRobin().to("mock:x", "mock:y", "mock:z");

Alternatively, you can configure the same route in XML, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <roundRobin/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

Random

The random load-balancing policy chooses the target endpoint randomly from the specified list.

You can specify the random load-balancing policy in Java DSL, as follows:

from("direct:start").loadBalance().random().to("mock:x", "mock:y", "mock:z");

Alternatively, you can configure the same route in XML, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <random/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

Sticky

The sticky load-balancing policy directs the In message to an endpoint that is chosen by calculating a hash value from a specified expression. The advantage of this load-balancing policy is that expressions of the same value are always sent to the same server. For example, by calculating the hash value from a header that contains a username, you ensure that messages from a particular user are always sent to the same target endpoint. Another useful approach is to specify an expression that extracts the session ID from an incoming message. This ensures that all messages belonging to the same session are sent to the same target endpoint.

You can specify the sticky load-balancing policy in Java DSL, as follows:

from("direct:start").loadBalance().sticky(header("username")).to("mock:x", "mock:y", "mock:z");

Alternatively, you can configure the same route in XML, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
      <sticky>
        <correlationExpression>
          <simple>header.username</simple>
        </correlationExpression>
      </sticky>
      <to uri="mock:x"/>
      <to uri="mock:y"/>
      <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>
Note

When you add the sticky option to the failover load balancer, the load balancer starts from the last known good endpoint.

Topic

The topic load-balancing policy sends a copy of each In message to all of the listed destination endpoints (effectively broadcasting the message to all of the destinations, like a JMS topic).

You can use the Java DSL to specify the topic load-balancing policy, as follows:

from("direct:start").loadBalance().topic().to("mock:x", "mock:y", "mock:z");

Alternatively, you can configure the same route in XML, as follows:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <topic/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

Failover

Available as of Apache Camel 2.0 The failover load balancer is capable of trying the next processor in case an Exchange failed with an exception during processing. You can configure the failover with a list of specific exceptions that trigger failover. If you do not specify any exceptions, failover is triggered by any exception. The failover load balancer uses the same strategy for matching exceptions as the onException exception clause.

Enable stream caching if using streams

If you use streaming, you should enable Stream Caching when using the failover load balancer. This is needed so the stream can be re-read when failing over.

The failover load balancer supports the following options:

Option

Type

Default

Description

inheritErrorHandler

boolean

true

Camel 2.3: Specifies whether to use the errorHandler configured on the route. If you want to fail over immediately to the next endpoint, you should disable this option (value of false). If you enable this option, Apache Camel will first attempt to process the message using the errorHandler.

For example, the errorHandler might be configured to redeliver messages and use delays between attempts. Apache Camel will initially try to redeliver to the original endpoint, and only fail over to the next endpoint when the errorHandler is exhausted.

maximumFailoverAttempts

int

-1

Camel 2.3: Specifies the maximum number of attempts to fail over to a new endpoint. The value, 0, implies that no failover attempts are made and the value, -1, implies an infinite number of failover attempts.

roundRobin

boolean

false

Camel 2.3: Specifies whether the failover load balancer should operate in round robin mode or not. If not, it will always start from the first endpoint when a new message is to be processed. In other words it restarts from the top for every message. If round robin is enabled, it keeps state and continues with the next endpoint in a round robin fashion. When using round robin it will not stick to last known good endpoint, it will always pick the next endpoint to use.

The following example is configured to fail over, only if an IOException exception is thrown:

from("direct:start")
    // here we will load balance if IOException was thrown
    // any other kind of exception will result in the Exchange as failed
    // to failover over any kind of exception we can just omit the exception
    // in the failOver DSL
    .loadBalance().failover(IOException.class)
        .to("direct:x", "direct:y", "direct:z");

You can optionally specify multiple exceptions to fail over, as follows:

// enable redelivery so failover can react
errorHandler(defaultErrorHandler().maximumRedeliveries(5));

from("direct:foo")
    .loadBalance()
    .failover(IOException.class, MyOtherException.class)
    .to("direct:a", "direct:b");

You can configure the same route in XML, as follows:

<route errorHandlerRef="myErrorHandler">
    <from uri="direct:foo"/>
    <loadBalance>
        <failover>
            <exception>java.io.IOException</exception>
            <exception>com.mycompany.MyOtherException</exception>
        </failover>
        <to uri="direct:a"/>
        <to uri="direct:b"/>
    </loadBalance>
</route>

The following example shows how to fail over in round robin mode:

from("direct:start")
    // Use failover load balancer in stateful round robin mode,
    // which means it will fail over immediately in case of an exception
    // as it does NOT inherit error handler. It will also keep retrying, as
    // it is configured to retry indefinitely.
    .loadBalance().failover(-1, false, true)
    .to("direct:bad", "direct:bad2", "direct:good", "direct:good2");

You can configure the same route in XML, as follows:

<route>
    <from uri="direct:start"/>
    <loadBalance>
        <!-- failover using stateful round robin,
        which will keep retrying the 4 endpoints indefinitely.
        You can set the maximumFailoverAttempt to break out after X attempts -->
        <failover roundRobin="true"/>
        <to uri="direct:bad"/>
        <to uri="direct:bad2"/>
        <to uri="direct:good"/>
        <to uri="direct:good2"/>
    </loadBalance>
</route>

If you want to failover to the next endpoint as soon as possible, you can disable the inheritErrorHandler by configuring inheritErrorHandler=false. By disabling the Error Handler you can ensure that it does not intervene. This allows the failover load balancer to handle failover as soon as possible. If you also enable the roundRobin mode, then it keeps retrying until it successes. You can then configure the maximumFailoverAttempts option to a high value to let it eventually exhaust and fail.

Weighted round robin and weighted random

In many enterprise environments, where server nodes of unequal processing power are hosting services, it is usually preferable to distribute the load in accordance with the individual server processing capacities. A weighted round robin algorithm or a weighted random algorithm can be used to address this problem.

The weighted load balancing policy allows you to specify a processing load distribution ratio for each server with respect to the others. You can specify this value as a positive processing weight for each server. A larger number indicates that the server can handle a larger load. The processing weight is used to determine the payload distribution ratio of each processing endpoint with respect to the others.

The parameters that can be used are described in the following table:

Table 8.3. Weighted Options

OptionTypeDefaultDescription

roundRobin

boolean

false

The default value for round-robin is false. In the absence of this setting or parameter, the load-balancing algorithm used is random.

distributionRatioDelimiter

String

,

The distributionRatioDelimiter is the delimiter used to specify the distributionRatio. If this attribute is not specified, comma , is the default delimiter.

The following Java DSL examples show how to define a weighted round-robin route and a weighted random route:

// Java
// round-robin
from("direct:start")
  .loadBalance().weighted(true, "4:2:1" distributionRatioDelimiter=":")
  .to("mock:x", "mock:y", "mock:z");

//random
from("direct:start")
  .loadBalance().weighted(false, "4,2,1")
  .to("mock:x", "mock:y", "mock:z");

You can configure the round-robin route in XML, as follows:

<!-- round-robin -->
<route>
  <from uri="direct:start"/>
  <loadBalance>
    <weighted roundRobin="true" distributionRatio="4:2:1" distributionRatioDelimiter=":" />
    <to uri="mock:x"/>
    <to uri="mock:y"/>
    <to uri="mock:z"/>
  </loadBalance>
</route>

Custom Load Balancer

You can use a custom load balancer (eg your own implementation) also.

An example using Java DSL:

from("direct:start")
     // using our custom load balancer
     .loadBalance(new MyLoadBalancer())
     .to("mock:x", "mock:y", "mock:z");

And the same example using XML DSL:

<!-- this is the implementation of our custom load balancer -->
 <bean id="myBalancer" class="org.apache.camel.processor.CustomLoadBalanceTest$MyLoadBalancer"/>

 <camelContext xmlns="http://camel.apache.org/schema/spring">
   <route>
     <from uri="direct:start"/>
     <loadBalance>
       <!-- refer to my custom load balancer -->
       <custom ref="myBalancer"/>
       <!-- these are the endpoints to balancer -->
       <to uri="mock:x"/>
       <to uri="mock:y"/>
       <to uri="mock:z"/>
     </loadBalance>
   </route>
 </camelContext>

Notice in the XML DSL above we use <custom> which is only available in Camel 2.8 onwards. In older releases you would have to do as follows instead:

       <loadBalance ref="myBalancer">
         <!-- these are the endpoints to balancer -->
         <to uri="mock:x"/>
         <to uri="mock:y"/>
         <to uri="mock:z"/>
       </loadBalance>

To implement a custom load balancer you can extend some support classes such as LoadBalancerSupport and SimpleLoadBalancerSupport. The former supports the asynchronous routing engine, and the latter does not. Here is an example:

public static class MyLoadBalancer extends LoadBalancerSupport {

     public boolean process(Exchange exchange, AsyncCallback callback) {
         String body = exchange.getIn().getBody(String.class);
         try {
             if ("x".equals(body)) {
                 getProcessors().get(0).process(exchange);
             } else if ("y".equals(body)) {
                 getProcessors().get(1).process(exchange);
             } else {
                 getProcessors().get(2).process(exchange);
             }
         } catch (Throwable e) {
             exchange.setException(e);
         }
         callback.done(true);
         return true;
     }
 }

Circuit Breaker

The Circuit Breaker load balancer is a stateful pattern that is used to monitor all calls for certain exceptions. Initially, the Circuit Breaker is in closed state and passes all messages. If there are failures and the threshold is reached, it moves to open state and rejects all calls until halfOpenAfter timeout is reached. After the timeout, if there is a new call, the Circuit Breaker passes all the messages. If the result is success, the Circuit Breaker moves to a closed state, if not, it moves back to open state.

Java DSL example:

from("direct:start").loadBalance()
    .circuitBreaker(2, 1000L, MyCustomException.class)
    .to("mock:result");

Spring XML example:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <route>
    <from uri="direct:start"/>
    <loadBalance>
        <circuitBreaker threshold="2" halfOpenAfter="1000">
            <exception>MyCustomException</exception>
        </circuitBreaker>
        <to uri="mock:result"/>
    </loadBalance>
</route>
</camelContext>

8.11. Hystrix

Overview

Available as of Camel 2.18.

The Hystrix pattern lets an application integrate with Netflix Hystrix, which can provide a circuit breaker in Camel routes. Hystrix is a latency and fault tolerance library designed to

  • Isolate points of access to remote systems, services and third-party libraries
  • Stop cascading failure
  • Enable resilience in complex distributed systems where failure is inevitable

If you use maven then add the following dependency to your pom.xml file to use Hystrix:

<dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-hystrix</artifactId>
      <version>x.x.x</version>
      <!-- Specify the same version as your Camel core version. -->
</dependency>

Java DSL example

Below is an example route that shows a Hystrix endpoint that protects against slow operation by falling back to the in-lined fallback route. By default, the timeout request is just 1000ms so the HTTP endpoint has to be fairly quick to succeed.

from("direct:start")
    .hystrix()
        .to("http://fooservice.com/slow")
    .onFallback()
        .transform().constant("Fallback message")
    .end()
    .to("mock:result");

XML configuration example

Following is the same example but in XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <hystrix>
      <to uri="http://fooservice.com/slow"/>
      <onFallback>
        <transform>
          <constant>Fallback message</constant>
        </transform>
      </onFallback>
    </hystrix>
    <to uri="mock:result"/>
  </route>
</camelContext>

Using the Hystrix fallback feature

The onFallback() method is for local processing where you can transform a message or call a bean or something else as the fallback. If you need to call an external service over the network then you should use the onFallbackViaNetwork() method, which runs in an independent HystrixCommand object that uses its own thread pool so it does not exhaust the first command object.

Hystrix configuration examples

Hystrix has many options as listed in the next section. The example below shows the Java DSL for setting the execution timeout to 5 seconds rather than the default 1 second and for letting the circuit breaker wait 10 seconds rather than 5 seconds (the default) before attempting a request again when the state was tripped to be open.

from("direct:start")
    .hystrix()
        .hystrixConfiguration()
             .executionTimeoutInMilliseconds(5000).circuitBreakerSleepWindowInMilliseconds(10000)
        .end()
        .to("http://fooservice.com/slow")
    .onFallback()
        .transform().constant("Fallback message")
    .end()
    .to("mock:result");

Following is the same example but in XML:

<camelContext xmlns="http://camel.apache.org/schema/spring">
<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <hystrix>
      <hystrixConfiguration executionTimeoutInMilliseconds="5000" circuitBreakerSleepWindowInMilliseconds="10000"/>
      <to uri="http://fooservice.com/slow"/>
      <onFallback>
        <transform>
          <constant>Fallback message</constant>
        </transform>
      </onFallback>
    </hystrix>
    <to uri="mock:result"/>
  </route>
</camelContext>
 You can also configure Hystrix globally and then refer to that
configuration. For example:
<camelContext xmlns="http://camel.apache.org/schema/spring">
   <!-- This is a shared config that you can refer to from all Hystrix patterns. -->
   <hystrixConfiguration id="sharedConfig" executionTimeoutInMilliseconds="5000" circuitBreakerSleepWindowInMilliseconds="10000"/>

   <route>
         <from uri="direct:start"/>
         <hystrix hystrixConfigurationRef="sharedConfig">
         <to uri="http://fooservice.com/slow"/>
         <onFallback>
            <transform>
               <constant>Fallback message</constant>
            </transform>
         </onFallback>
      </hystrix>
      <to uri="mock:result"/>
   </route>
</camelContext>

Options

Ths Hystrix component supports the following options. Hystrix provides the default values.

NameDefault ValueTypeDescription

circuitBreakerEnabled

true

Boolean

Determines whether a circuit breaker will be used to track health and to short-circuit requests if it trips.

circuitBreakerErrorThresholdPercentage

50

Integer

Sets the error percentage at or above which the circuit should trip open and start short-circuiting requests to fallback logic.

circuitBreakerForceClosed

false

Boolean

A value of true forces the circuit breaker into a closed state in which it allows requests regardless of the error percentage.

circuitBreakerForceOpen

false

Boolean

A value of true forces the circuit breaker into an open (tripped) state in which it rejects all requests.

circuitBreakerRequestVolumeThreshold

20

Integer

Sets the minimum number of requests in a rolling window that will trip the circuit.

circuitBreakerSleepWindownInMilliseconds

5000

Integer

Sets the amount of time, after tripping the circuit, to reject requests. After this time elapses, request attempts are allowed to determine if the circuit should again be closed.

commandKey

Node ID

String

Identifies the Hystrix command. You cannot configure this option. it is always the node ID to make the command unique.

corePoolSize

10

Integer

Sets the core thread-pool size. This is the maximum number of HystrixCommand objects that can execute concurrently.