Red Hat 6.2.1 Apache Camel Development Guide
Apache Camel 6.2.1 is a powerful integration framework that enables you to connect different systems and applications together. With Camel, you can easily define routing rules for messages, and transform data between different formats. This guide will help you understand the basics of Camel and get started building your first applications.
Advertisement
Advertisement
Red Hat JBoss Fuse 6.2.1
Apache Camel Development Guide
Develop applications with Apache Camel
JBoss A-MQ Docs Team
Red Hat JBoss Fuse 6.2.1 Apache Camel Development Guide
Develop applications with Apache Camel
JBo ss A-MQ Do cs Team
Co ntent Services fuse-do cs-suppo [email protected] m
Legal Notice
Co pyright © 20 15 Red Hat.
The text o f and illustratio ns in this do cument are licensed by Red Hat under a Creative
Co mmo ns Attributio n–Share Alike 3.0 Unpo rted license ("CC-BY-SA"). An explanatio n o f CC-
BY-SA is available at http://creativeco mmo ns.o rg/licenses/by-sa/3.0 /
. In acco rdance with CC-BY-SA, if yo u distribute this do cument o r an adaptatio n o f it, yo u must pro vide the URL fo r the o riginal versio n.
Red Hat, as the licenso r o f this do cument, waives the right to enfo rce, and agrees no t to assert,
Sectio n 4 d o f CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shado wman lo go , JBo ss, OpenShift, Fedo ra, the Infinity lo go , and RHCE are trademarks o f Red Hat, Inc., registered in the United States and o ther co untries.
Linux ® is the registered trademark o f Linus To rvalds in the United States and o ther co untries.
Java ® is a registered trademark o f Oracle and/o r its affiliates.
XFS ® is a trademark o f Silico n Graphics Internatio nal Co rp. o r its subsidiaries in the United
States and/o r o ther co untries.
MySQL ® is a registered trademark o f MySQL AB in the United States, the Euro pean Unio n and o ther co untries.
No de.js ® is an o fficial trademark o f Jo yent. Red Hat So ftware Co llectio ns is no t fo rmally related to o r endo rsed by the o fficial Jo yent No de.js o pen so urce o r co mmercial pro ject.
The OpenStack ® Wo rd Mark and OpenStack lo go are either registered trademarks/service marks o r trademarks/service marks o f the OpenStack Fo undatio n, in the United States and o ther co untries and are used with the OpenStack Fo undatio n's permissio n. We are no t affiliated with, endo rsed o r spo nso red by the OpenStack Fo undatio n, o r the OpenStack co mmunity.
All o ther trademarks are the pro perty o f their respective o wners.
Abstract
REVISIT -
Table of Contents
1.1. IMPLEMENTING A RO UTEBUILDER CLASS
1.3. RO UTER SCHEMA IN A SPRING XML FILE
2.5. CREATING EXCHANG E INSTANCES
2.6 . TRANSFO RMING MESSAG E CO NTENT
2.9 . CO NTRO LLING START-UP AND SHUTDO WN O F RO UTES
2.10 . SCHEDULED RO UTE PO LICY
2.13. PERFO RMANCE AND O PTIMIZATIO N
3.1. O VERVIEW O F THE PATTERNS
4.1. O VERVIEW O F REST IN CAMEL
4.2. DEFINING SERVICES WITH REST DSL
4.3. MARSHALLING TO AND FRO M JAVA O BJECTS
4.4. CO NFIG URING THE REST DSL
6 .1. PO INT-TO -PO INT CHANNEL
6 .2. PUBLISH-SUBSCRIBE CHANNEL
T able of Cont ent s
35
38
42
6 0
72
73
8 6
9 7
10 7
112
124
126
128
11
12
16
17
23
18 3
18 4
18 7
19 7
20 0
171
172
174
176
178
179
18 1
130
138
141
151
16 2
16 7
20 1
20 2
20 3
1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
7.3. RETURN ADDRESS
8 .12. CO MPO SED MESSAG E PRO CESSO R
CREATING A SERVICE CO MPO NENT
EXAMPLE O F A SERVICE CO MPO NENT CLASS
EXAMPLE O F A RO UTEBUILDER CLASS
USING APACHE CAMEL SCR BUNDLE AS A TEMPLATE
USING APACHE CAMEL-ARCHETYPE-SCR
2
337
338
339
346
346
346
347
348
349
350
313
314
315
316
318
320
322
325
332
333
334
29 8
30 4
30 5
30 7
30 9
311
20 3
28 4
28 6
29 0
29 2
29 4
258
26 1
26 3
26 6
276
20 5
20 6
20 8
219
231
254
13.1. O VERVIEW O F THE LANG UAG ES
13.2. HO W TO INVO KE AN EXPRESSIO N LANG UAG E
16 .1. WHEN TO USE THE FILE LANG UAG E
USING THE PRO PERTIES CO MPO NENT
USING THE PRO PERTIES CO MPO NENT
T able of Cont ent s
353
354
36 8
36 8
36 8
36 8
36 9
36 9
370
371
371
371
372
372
372
372
373
373
359
359
359
36 0
36 0
36 0
36 0
36 1
36 2
36 3
36 5
375
375
375
375
376
377
377
377
377
3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
XML EXAMPLE
USING THE PRO PERTIES CO MPO NENT
USING THE PRO PERTIES CO MPO NENT
4
38 8
38 8
38 8
38 8
38 9
38 9
39 1
39 1
39 1
39 2
39 2
39 2
39 2
39 3
39 3
39 5
39 5
39 5
39 5
39 6
39 6
38 5
38 5
38 5
38 5
38 5
38 6
38 2
38 2
38 2
38 2
38 3
377
378
378
379
38 0
38 0
38 0
38 1
USING THE PRO PERTIES CO MPO NENT
30 .3. INVO KING AN EXTERNAL SCRIPT
32.8 . USING VARIABLES AND FUNCTIO NS
34.1. DO WNLO ADING AND INSTALLING THE DEMO NSTRATIO NS
34.2. RUNNING THE DEMO NSTRATIO NS
35.2. DEFINE SEI AND RELATED CLASSES
35.3. ANNO TATE SEI FO R JAX-WS
35.4. INSTANTIATE THE WS ENDPO INT
35.5. JAVA-TO -WSDL MAVEN PLUG -IN
T able of Cont ent s
39 6
39 6
39 7
39 7
414
414
414
414
415
415
39 9
40 0
40 1
40 2
40 5
40 7
411
417
419
420
422
423
425
429
430
431
432
434
434
434
434
435
435
435
438
438
442
443
447
450
452
5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
36 .2. CUSTO MERSERVICE WSDL CO NTRACT
36 .3. WSDL-TO -JAVA MAVEN PLUG -IN
36 .4. INSTANTIATE THE WS ENDPO INT
36 .5. DEPLO Y TO AN O SG I CO NTAINER
37.2. WSDL-TO -JAVA MAVEN PLUG -IN
37.3. INSTANTIATE THE WS CLIENT PRO XY
37.5. DEPLO Y TO AN O SG I CO NTAINER
38 .1. PRO CESSING MESSAG ES IN PO JO FO RMAT
38 .2. WSDL-TO -JAVA MAVEN PLUG -IN
38 .3. INSTANTIATE THE WS ENDPO INT
38 .4. SO RT MESSAG ES BY O PERATIO N NAME
38 .5. PRO CESS O PERATIO N PARAMETERS
39 .1. PRO CESSING MESSAG ES IN PAYLO AD FO RMAT
39 .2. INSTANTIATE THE WS ENDPO INT
39 .3. SO RT MESSAG ES BY O PERATIO N NAME
39 .4. SO AP/HTTP-TO -JMS BRIDG E USE CASE
39 .5. G ENERATING RESPO NSES USING TEMPLATES
39 .6 . TYPECO NVERTER FO R CXFPAYLO AD
40 .1. PRO VIDER-BASED JAX-WS ENDPO INT
40 .2. CREATE A PRO VIDER< ?> IMPLEMENTATIO N CLASS
40 .3. INSTANTIATE THE WS ENDPO INT
40 .4. SO RT MESSAG ES BY O PERATIO N NAME
40 .5. SO AP/HTTP-TO -JMS BRIDG E USE CASE
40 .6 . G ENERATING RESPO NSES USING TEMPLATES
40 .7. TYPECO NVERTER FO R SAXSO URCE
41.2. PRO XYING WITH PO JO FO RMAT
41.3. PRO XYING WITH PAYLO AD FO RMAT
42.3. IMPLEMENTING A CUSTO M FILTER
6
48 9
49 1
49 3
49 4
49 7
50 0
50 1
475
477
479
48 2
48 3
48 6
46 6
46 7
46 9
471
472
455
456
459
46 1
46 3
520
522
524
526
529
532
533
535
50 5
50 7
50 8
50 9
510
513
516
517
43.3. BUILT-IN TYPE CO NVERTERS
43.4. BUILT-IN UUID G ENERATO RS
44.2. IMPLEMENTING A SIMPLE PRO CESSO R
44.3. ACCESSING MESSAG E CO NTENT
44.4. THE EXCHANG EHELPER CLASS
45.1. TYPE CO NVERTER ARCHITECTURE
45.2. IMPLEMENTING TYPE CO NVERTER USING ANNO TATIO NS
45.3. IMPLEMENTING A TYPE CO NVERTER DIRECTLY
46 .1. USING THE PRO DUCER TEMPLATE
46 .2. USING THE CO NSUMER TEMPLATE
47.1. CO MPO NENT ARCHITECTURE
47.2. HO W TO IMPLEMENT A CO MPO NENT
47.3. AUTO -DISCO VERY AND CO NFIG URATIO N
48 .1. THE CO MPO NENT INTERFACE
48 .2. IMPLEMENTING THE CO MPO NENT INTERFACE
49 .1. THE ENDPO INT INTERFACE
49 .2. IMPLEMENTING THE ENDPO INT INTERFACE
50 .1. THE CO NSUMER INTERFACE
50 .2. IMPLEMENTING THE CO NSUMER INTERFACE
51.2. IMPLEMENTING THE PRO DUCER INTERFACE
53.2. IMPLEMENTING THE MESSAG E INTERFACE
54.1. WHAT IS THE API CO MPO NENT FRAMEWO RK?
54.2. HO W TO USE THE FRAMEWO RK
55.1. G ENERATE CO DE WITH THE MAVEN ARCHETYPE
55.2. G ENERATED API SUB-PRO JECT
T able of Cont ent s
6 0 5
6 0 8
6 17
6 22
6 32
6 34
6 38
6 42
6 44
58 3
59 2
59 4
59 8
59 9
557
559
56 3
56 5
58 0
539
540
545
548
551
551
553
554
6 48
6 50
6 54
6 56
7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
55.2. G ENERATED API SUB-PRO JECT
55.3. G ENERATED CO MPO NENT SUB-PRO JECT
55.5. SAMPLE CO MPO NENT IMPLEMENTATIO NS
56 .1. O VERVIEW O F THE PLUG -IN CO NFIG URATIO N
56 .5. ARG UMENT NAME SUBSTITUTIO N
6 56
6 58
6 6 9
6 73
6 75
6 79
6 8 1
6 8 2
6 8 4
6 8 7
6 8 8
8
T able of Cont ent s
9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
PART I. IMPLEMENTING ENTERPRISE INTEGRATION
PATTERNS
Ab st ract
This part describes how to build routes using Apache Camel. It covers the basic building blocks and EIP components.
10
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
CHAPTER 1. BUILDING BLOCKS FOR ROUTE DEFINITIONS
Ab st ract
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. IMPLEMENT ING A ROUT EBUILDER CLASS
Overview
To use the Domain Specific Language (DSL), you extend the R o uteBui l d er class and override its co nfi g ure() method (where you define your routing rules).
You can define as many R o uteBui l d er classes as necessary. Each class is instantiated once and is registered with the C amel C o ntext object. Normally, the lifecycle of each
R o uteBui l d er object is managed automatically by the container in which you deploy the router.
Rout eBuilder classes
As a router developer, your core task is to implement one or more R o uteBui l d er classes.
There are two alternative R o uteBui l d er classes that you can inherit from:
o rg . apache. camel . bui l d er. R o uteBui l d er—this is the generic R o uteBui l d er base class that is suitable for deploying into any container type. It is provided in the
camel -co re artifact.
o rg . apache. camel . spri ng . Spri ng R o uteBui l d er—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 beanR ef()
Java DSL command) and transactions (see the Transactions Guide for details). It is provided in the camel -spri ng artifact.
The R o uteBui l d er class defines methods used to initiate your routing rules (for example,
fro m(), i ntercept(), and excepti o n()).
Implement ing a Rout eBuilder
Example 1.1, “ Implementation of a RouteBuilder Class” shows a minimal R o uteBui l d er
implementation. The co nfi g ure() method body contains a routing rule; each rule is a single Java statement.
Examp le 1.1. Imp lemen t at io n o f a Ro u t eBu ild er Class
11
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
import org.apache.camel.builder.RouteBuilder; public class MyR o uteBui l d er extends RouteBuilder { public void co nfi g ure() {
// Define routing rules here:
from("file:src/data?noop=true").to("file:target/messages");
}
}
// More rules can be included, in you like.
// ...
12
The form of the rule fro m(URL1). to (URL2) instructs the router to read files from the directory src/d ata and send them to the directory targ et/messag es. The option ?
no o p= true instructs the router to retain (not delete) the source files in the src/d ata directory.
No t e
When you use the contextScan with Spring or Blueprint to filter R o uteBui l d er 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
i ncl ud eNo nSi ng l eto ns.
1.2. BASIC JAVA DSL SYNT AX
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:
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
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).
Rout er rule synt ax
Apache Camel defines a router DSL for defining routing rules. You can use this DSL to define
rules in the body of a R o uteBui l d er. co nfi g ure() implementation. Figure 1.1, “ Local
Routing Rules” shows an overview of the basic syntax for defining local routing rules.
Fig u re 1.1. Lo cal Ro u t in g Ru les
A local rule always starts with a fro m("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, fi l ter()). 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.
No t e
You can also define a global routing rule, by starting the rule with a special processor type (such as i ntercept(), excepti o n(), or erro rHand l er()).
Global rules are outside the scope of this guide.
Consumers and producers
A local rule always starts by defining a consumer endpoint, using fro m("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,
fi l e: MyMessag eD i recto ry, an Apache CXF endpoint, cxf: MyServi ceName, or an
Apache ActiveMQ endpoint, acti vemq : q ueue: MyQ Name. For a complete list of component types, see "Apache Camel Component Reference" .
Exchanges
13
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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
o rg . apache. camel . Messag e 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
O bject 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
fro m() 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).
14
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
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
.
Exchange properties—a list of named properties containing metadata for the current message.
Message exchange pat t erns
Using an Exchang e 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:
InO nl y
R o bustInO nl y
InO ut
InO pti o nal O ut
O utO nl y
R o bustO utO nl y
O utIn
O utO pti o nal In
Where these message exchange patterns are represented by constants in the enumeration type, o rg . apache. camel . Exchang eP attern.
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. uti l . Li st of Exchang e objects stored in the Exchang e. G R O UP ED _EXC HANG E exchange property. For an example of how
to use grouped exchanges, see Section 8.5, “ Aggregator” .
Processors
15
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 fi l ter() processor that takes an xpath() predicate as its argument.
Expressions and predicat es
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 fo o 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, head er("fo o "). i sEq ual T o ("bar"). To construct more sophisticated predicates and expressions, based on the message content,
you can use one of the expression and predicate languages (see Expression and Predicate
1.3. ROUT ER 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 t he schema locat ion
The location of the router schema is normally specified to be
http: //camel . apache. o rg /schema/spri ng /camel -spri ng . 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” .
Examp le 1.2. Sp ecif yin g t h e Ro u t er Sch ema Lo cat io n
<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. o rg /schema/spri ng /camel -spri ng . xsd ">
<camelContext id="camel"
16
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
xmlns="http://camel.apache.org/schema/spring">
<!-- Define your routing rules here -->
</camelContext>
</beans>
Runt ime schema locat ion
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 -spri ng 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 edit or
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 : schemaLo cati o n 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 -spri ng . 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. o rg /schema/spri ng /camel -spri ng -2. 3. 0 . xsd ">
...
Change back to the default, camel -spri ng . 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 . ENDPOINT S
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 consumer endpoint) or as a sink of messages (a producer endpoint). Hence, there are a great variety of different endpoint types supported in Apache
17
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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\d ata 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 wit h 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.
Co n f ig u re En d p o in t s Sep arat ely
You can configure the endpoint separately, and from the routes refer to the endpoints using their shorthand IDs.
<camelContext ...>
18
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
<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 pro perty 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>
Sp lit En d p o in t Co n f ig u rat io n Acro ss New Lin es
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>
No t e
You can specify one or more options on each line, each separated by
& amp;.
Specifying t ime 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)]
19
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 ti mer 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 opt ions
By default, the option values that you specify in a URI are automatically URI-encoded. In some cases this is undesirable beahavior. For example, when setting a password option, it is preferable to transmit the raw character string without URI encoding.
It is possible to switch of URI encoding by specifying an option value with the syntax,
R AW(RawValue). For example, from("SourceURI")
.to("ftp:joe@ myftpserver.com?password=R AW(se+ re?t& 23)& binary=true")
In this example, the password value is transmitted as the literal value, se+ re?t& 23.
Case-insensit ive enum opt ions
Some endpoint URI options get mapped to Java enum constants. For example, the l evel option of the Log component, which can take the enum values, INFO , WAR N, ER R O R , 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"/>
Apache Camel component s
Each URI scheme maps to a Apache Camel component, where a 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.
20
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
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>2.15.1.redhat-621084</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 -co re artifact), so they are always available:
Bean
Browse
Dataset
Direct
File
Log
Mock
Properties
Ref
21
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
SEDA
Timer
VM
Consumer endpoint s
A consumer endpoint is an endpoint that appears at the start of a route (that is, in a fro m()
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 fro mF() 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");
22
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
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 passwo rd string. This string formatting mechanism is implemented by Stri ng . fo rmat() and is similar to the formatting provided by the C pri ntf() function. For details, see java.util.Formatter
.
Producer endpoint s
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 to F() 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, myG o o g l eQ uery. For details, see java.util.Formatter
.
1.5. PROCESSORS
23
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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”
.
T ab le 1.1. Ap ach e Camel Pro cesso rs
Java DSL ag g reg ate()
XML DSL ag g reg ate
Descript ion
Ag g reg ato r EIP : Creates an
ag g reg ato r, which co mb ines multip le inco ming exchang es into a sing le exchang e.
ao p() ao p
Use Asp ect O riented
Pro g ramming (AO P) to d o wo rk b efo re and after a sp ecified sub -ro ute. See
???
.
bean()
,
beanR ef() bean
Pro cess the current exchang e b y invo king a metho d o n a Java o b ject (o r
cho i ce() cho i ce
:
Selects a p articular sub ro ute b ased o n the exchang e co ntent, using
when
and
o therwi se
clauses.
co nvertBo d yT o () co nvertBo d yT o
Co nverts the In messag e b o d y to the sp ecified typ e.
d el ay() d el ay
: Delays the p ro p ag atio n o f the exchang e to the latter p art o f the ro ute.
24
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
Java DSL d o T ry()
XML DSL d o T ry
Descript ion end ()
N/A
Creates a try/catch b lo ck fo r hand ling excep tio ns, using
d o C atch
,
d o Fi nal l y
, and
end
clauses.
End s the current co mmand b lo ck.
enri ch()
,
enri chR ef() enri ch
:
Co mb ines the current exchang e with d ata req uested fro m a sp ecified
producer end p o int URI.
fi l ter() fi l ter
p red icate exp ressio n to filter inco ming exchang es.
i d empo tentC o nsumer() i d empo tentC o nsumer i nheri tErro rHand l er(
)
@ i nheri tErro rHand l er
Imp lements a strateg y to sup p ress d up licate messag es.
Bo o lean o p tio n that can b e used to d isab le the inherited erro r hand ler o n a p articular ro ute no d e (d efined as a sub -clause in the Java DSL and as an attrib ute in the
XML DSL).
i nO nl y() i nO nl y
Either sets the current exchang e's MEP to InOnly (if no arg uments) o r send s the exchang e as an InOnly to the sp ecified end p o int(s).
i nO ut() i nO ut
Either sets the current exchang e's MEP to InOut (if no arg uments) o r send s the exchang e as an InOut to the sp ecified end p o int(s).
25
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java DSL l o ad Bal ance()
XML DSL l o ad Bal ance
Descript ion l o g () l o g
:
Imp lements lo ad b alancing o ver a co llectio n o f end p o ints.
Lo g s a messag e to the co nso le.
l o o p() l o o p markR o l l backO nl y() @ markR o l l backO nl y
: Rep eated ly resend s each exchang e to the latter p art o f the ro ute.
(Transactions) Marks the current transactio n fo r ro llb ack o nly (no excep tio n is raised ). In the XML DSL, this o p tio n is set as a b o o lean attrib ute o n the
ro l l back
element. See
" Transactio n G uid e" .
markR o l l backO nl yLast
()
@ markR o l l backO nl yLas t
(Transactions) If o ne o r mo re transactio ns have p revio usly b een asso ciated with this thread and then susp end ed , this co mmand marks the latest transactio n fo r ro llb ack o nly (no excep tio n is raised ). In the XML DSL, this o p tio n is set as a b o o lean attrib ute o n the
ro l l back
element. See
" Transactio n G uid e" .
marshal () marshal
Transfo rms into a lo w-level o r b inary fo rmat using the sp ecified d ata fo rmat, in p rep aratio n fo r send ing o ver a p articular transp o rt p ro to co l.
mul ti cast() mul ti cast
: Multicasts the current exchang e to multip le d estinatio ns, where each d estinatio n g ets its o wn co p y o f the exchang e.
26
Java DSL o nC o mpl eti o n() o nExcepti o n() pi pel i ne() po l i cy()
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
Descript ion XML DSL o nC o mpl eti o n
Defines a sub -ro ute
(terminated b y
end ()
in the
Java DSL) that g ets executed after the main ro ute has co mp leted . Fo r co nd itio nal executio n, use the
o nWhen
sub -clause. Can also b e d efined o n its o wn line (no t in a ro ute).
o nExcepti o n
Defines a sub -ro ute
(terminated b y
end ()
in the
Java DSL) that g ets executed whenever the sp ecified excep tio n o ccurs. Usually d efined o n its o wn line (no t in a ro ute).
pi pel i ne
Pip es and Filters EIP : Send s
the exchang e to a series o f end p o ints, where the o utp ut o f o ne end p o int b eco mes the inp ut o f the next end p o int. See also
po l i cy
Ap p ly a p o licy to the current ro ute (currently o nly used fo r transactio nal p o licies—see
" Transactio n G uid e" ).
po l l Enri ch()
,
po l l Enr i chR ef() po l l Enri ch
:
Co mb ines the current exchang e with d ata p o lled fro m a sp ecified consumer end p o int URI.
pro cess()
,
pro cessR ef pro cess
Execute a custo m p ro cesso r o n the current exchang e. See
the sectio n called “ Custo m p ro cesso r” and Part IV,
27
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java DSL reci pi entLi st()
XML DSL reci pi entLi st
Descript ion
Recip ient List EIP : Send s the
exchang e to a list o f recip ients that is calculated at runtime (fo r examp le, b ased o n the co ntents o f a head er).
remo veHead er() remo veHead er remo veHead ers() remo veHead ers
Remo ves the sp ecified head er fro m the exchang e's
In messag e.
Remo ves the head ers matching the sp ecified p attern fro m the exchang e's
In messag e. The p attern can have the fo rm,
prefi x*
—in which case it matches every name starting with p refix— o therwise, it is interp reted as a reg ular exp ressio n.
remo veP ro perty() remo veP ro perty
Remo ves the sp ecified exchang e p ro p erty fro m the exchang e.
remo veP ro perti es() remo veP ro perti es
Remo ves the p ro p erties matching the sp ecified p attern fro m the exchang e.
Takes a co mma sep arated list o f 1 o r mo re string s as arg uments. The first string is the p attern (see
remo veHead ers()
ab o ve).
Sub seq uent string s sp ecify excep tio ns - these p ro p erties remain.
reseq uence() reseq uence
Reseq uencer EIP : Re-o rd ers
inco ming exchang es o n the b asis o f a sp ecified co mp aro to r o p eratio n.
Sup p o rts a batch mo d e and a stream mo d e.
28
Java DSL ro l l back() ro uti ng Sl i p() sampl e() setBo d y()
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
Descript ion XML DSL ro l l back
(Transactions) Marks the current transactio n fo r ro llb ack o nly (also raising an excep tio n, b y d efault).
See " Transactio n G uid e" .
ro uti ng Sl i p sampl e
: Ro utes the exchang e thro ug h a p ip eline that is co nstructed d ynamically, b ased o n the list o f end p o int URIs extracted fro m a slip head er.
Creates a samp ling thro ttler, allo wing yo u to extract a samp le o f exchang es fro m the traffic o n a ro ute.
setBo d y
Sets the messag e b o d y o f the exchang e's In messag e.
setExchang eP attern() setExchang eP attern
Sets the current exchang e's
MEP to the sp ecified value.
“ Messag e exchang e p atterns” .
setHead er() setHead er
Sets the sp ecified head er in the exchang e's In messag e.
setO utHead er() setO utHead er
Sets the sp ecified head er in the exchang e's Out messag e.
setP ro perty() setP ro perty()
Sets the sp ecified exchang e p ro p erty.
so rt() so rt
So rts the co ntents o f the In messag e b o d y (where a custo m co mp arato r can o p tio nally b e sp ecified ).
29
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java DSL spl i t()
XML DSL spl i t
Descript ion
: Sp lits the current exchang e into a seq uence o f exchang es, where each sp lit exchang e co ntains a frag ment o f the o rig inal messag e b o d y.
sto p() sto p
Sto p s ro uting the current exchang e and marks it as co mp leted .
thread s() thread s
Creates a thread p o o l fo r co ncurrent p ro cessing o f the latter p art o f the ro ute.
thro ttl e() thro ttl e
: Limit the flo w rate to the sp ecified level
(exchang es p er seco nd ).
thro wExcepti o n() thro wExcepti o n
Thro w the sp ecified Java excep tio n.
to () to to F()
N/A
Send the exchang e to o ne o r mo re end p o ints. See
Send the exchang e to an end p o int, using string fo rmatting . That is, the end p o int URI string can emb ed sub stitutio ns in the style o f the C
pri ntf()
functio n.
transacted () transacted
Create a Sp ring transactio n sco p e that enclo ses the latter p art o f the ro ute. See
" Transactio n G uid e" .
30
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
Java DSL transfo rm()
XML DSL transfo rm
Descript ion
Co p y the In messag e head ers to the Out messag e head ers and set the Out messag e b o d y to the sp ecified value.
unmarshal () unmarshal val i d ate() val i d ate
Transfo rms the In messag e b o d y fro m a lo w-level o r b inary fo rmat to a hig h-level fo rmat, using the sp ecified d ata fo rmat.
Takes a p red icate exp ressio n to test whether the current messag e is valid .
If the p red icate returns
fal se
, thro ws a
P red i cateVal i d ati o nE xcepti o n
excep tio n.
wi reT ap() wi reT ap
: Send s a co p y o f the current exchang e to the sp ecified wire tap URI, using the
Exchang eP attern. InO n l y
MEP.
Some sample processors
To get some idea of how to use processors in a route, see the following examples:
.
the section called “ Filter” .
the section called “ Throttler” .
the section called “ Custom processor”
.
Choice
The cho i ce() 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
31
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
target is selected, otherwise processing proceeds to the next when() method in the rule. For example, the following cho i ce() 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 end C ho i ce() 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
l o ad Bal ance(). ro und R o bi n(). to ("mo ck: fo o "). to ("mo ck: bar"). end (), which load balances messages between the mo ck: fo o and mo ck: bar endpoints. If the load balancer clause is embedded in a choice condition, however, it is necessary to terminate the clause using the end C ho i ce() 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");
Filt er
32
CHAPT ER 1 . BUILDING BLO CKS FO R RO UT E DEFINIT IO NS
The fi l ter() 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, fo o , 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>
T hrot t ler
The thro ttl e() 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>
Cust om 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 o rg . apache. camel . P ro cesso r interface and overrides the
pro cess() method. The following custom processor, MyP ro cesso r, removes the header named fo o from incoming messages:
33
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Examp le 1.3. Imp lemen t in g a Cu st o m Pro cesso r Class
public class MyP ro cesso r implements org.apache.camel.P ro cesso r { 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 pro cess() 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
org.apache.camel.Processor myProc = new MyProcessor(); from("SourceURL").process(myProc).to("TargetURL");
34
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
CHAPTER 2. BASIC PRINCIPLES OF ROUTE BUILDING
Ab st ract
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, l s | mo re is an example of a command that pipes a directory listing, l s, to the page-scrolling utility, mo re.
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 o rg . apache. camel . P ro cesso r interface. In other words, processors make up the basic building blocks of a DSL route. For example, DSL commands such as
fi l ter(), d el ayer(), setBo d y(), setHead er(), 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 nul l in this case.
Fig u re 2.1. Pro cesso r Mo d if yin g an In Messag e
The following route shows a setHead er() command that modifies the current In message by adding (or modifying) the Bi l l i ng System heading: from("activemq:orderQueue")
.setHeader("BillingSystem", xpath("/order/billingSystem"))
.to("activemq:billingQueue");
35
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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”
.
Fig u re 2.2. Pro cesso r Creat in g an O u t Messag e
The following route shows a transfo rm() command that creates an Out message with a message body containing the string, D ummyBo d y: from("activemq:orderQueue")
.transform(constant("DummyBody"))
.to("activemq:billingQueue"); where co nstant("D ummyBo d y") represents a constant expression. You cannot pass the string, D ummyBo d y, directly, because the argument to transfo rm() 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”
.
Fig u re 2.3. Samp le Pip elin e f o r In O n ly Exch an g es
36
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 userd ataQ ueue 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, envel o peAd d ressQ ueue:
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
from("activemq:userdataQueue")
.to(ExchangePattern.InOut, "velocity:file:AdressTemplate.vm")
.to("activemq:envelopeAddresses");
Where the Velocity endpoint, vel o ci ty: fi l e: Ad ressT empl ate. vm, specifies the location of a Velocity template file, fi l e: Ad ressT empl ate. 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.
Fig u re 2.4 . Samp le Pip elin e f o r In O u t Exch an g es
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: ad d Acco untD etai l s, cxf: bean: g etC red i tR ati ng , and
cxf: bean: pro cessT ransacti o n. The final Web service, pro cessT ransacti o n, 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:
37
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
from("jetty:http://localhost:8080/foo")
.pipeline("cxf:bean:addAccountDetails",
"cxf:bean:getCreditRating", "cxf:bean:processTransaction");
Pipeline for InOpt ionalOut 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 nul l Out message is copied to the In message of the next node in the pipeline. By contrast, in the case of an
InOut exchange, a nul l Out 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. MULT IPLE INPUT S
Overview
A standard route takes its input from just a single endpoint, using the fro m(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”
).
Mult iple independent input s
The simplest way to specify multiple inputs is using the multi-argument form of the fro m()
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");
Segment ed rout es
38
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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” .
Fig u re 2.5. Pro cessin g Mu lt ip le In p u t s wit h Seg men t ed Ro u t es
The initial segments of the route take their inputs from some external queues—for example,
acti vemq : Nyse and acti vemq : Nasd aq —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,
acti vemq : UST xn. 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 section called “ Direct endpoints”
.
the section called “ SEDA endpoints”
.
the section called “ VM endpoints” .
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 endpoint s
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
d i rect: 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, acti vemq : Nyse and
acti vemq : Nasd aq , and merge them into a single message queue, acti vemq : UST xn, 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");
39
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where the first two routes take the input from the message queues, Nyse and Nasd aq , and send them to the endpoint, d i rect: merg eT xns. The last queue combines the inputs from the previous two queues and sends the combined message stream to the acti vemq : UST xn queue.
The implementation of the direct endpoint behaves as follows: whenever an exchange arrives at a producer endpoint (for example, to ("d i rect: merg eT xns")), the direct endpoint passes the exchange directly to all of the consumers endpoints that have the same endpoint ID (for example, fro m("d i rect: merg eT xns")). Direct endpoints can only be used to communicate between routes that belong to the same C amel C o ntext in the same
Java virtual machine (JVM) instance.
SEDA endpoint s
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. uti l . co ncurrent. Bl o cki ng Q ueue 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 sed a: merg eT xns to acti vemq : UST xn) is processed by a pool of five threads.
4 0
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
No t e
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 endpoint s
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
C amel C o ntext, 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");
Cont ent enricher pat t ern
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/d ata/rati ng s. You can combine the incoming credit request with data from the ratings file using the po l l Enri ch() pattern and a
G ro uped Exchang eAg g reg ati o nStrateg y 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 G ro uped Exchang eAg g reg ati o nStrateg y class is a standard aggregation strategy from the o rg . apache. camel . pro cesso r. ag g reg ate package that adds each
4 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
new exchange to a java. uti l . Li st instance and stores the resulting list in the
Exchang e. G R O UP ED _EXC HANG E exchange property. In this case, the list contains two elements: the original exchange (from the cred i tR eq uests 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(Exchang e. G R O UP ED _EXC HANG E, 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 9.1, “ Content Enricher”
.
2.3. EXCEPT ION HANDLING
Ab st ract
Apache Camel provides several different mechanisms, which let you handle exceptions at different levels of granularity: you can handle exceptions within a route using d o T ry,
d o C atch, and d o Fi nal l y; or you can specify what action to take for each exception type and apply this rule to all routes in a R o uteBui l d er using o nExcepti o n; or you can specify what action to take for all exception types and apply this rule to all routes in a
R o uteBui l d er using erro rHand l er.
For more details about exception handling, see
Section 6.3, “ Dead Letter Channel” .
2.3.1. onExcept ion Clause
Ove rvie w
The o nExcepti o n 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
4 2
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
handle exceptions; and it is based on a trapping model, which enables a single
o nExcepti o n clause to deal with exceptions occurring at any node in any route.
T rapping e xce pt io ns using o nExce pt io n
The o nExcepti o n clause is a mechanism for trapping, rather than catching exceptions. That is, once you define an o nExcepti o n 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 o nExcepti o n clause is that the Apache Camel runtime implicitly encloses each route node in a try block. This is why the o nExcepti o n 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 e xam ple
In the following Java DSL example, the o nExcepti o n clause applies to all of the routes defined in the R o uteBui l d er class. If a Val i d ati o nExcepti o n exception occurs while processing either of the routes (fro m("sed a: i nputA") or fro m("sed a: i nputB")), the
o nExcepti o n clause traps the exception and redirects the current exchange to the
val i d ati o nFai l ed 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 e xam ple
The preceding example can also be expressed in the XML DSL, using the o nExcepti o n 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">
4 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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>
T rapping m ult iple e xce pt io ns
You can define multiple o nExcepti o n clauses to trap exceptions in a R o uteBui l d er scope. This enables you to take different actions in response to different exceptions. For example, the following series of o nExcepti o n clauses defined in the Java DSL define different deadletter destinations for Val i d ati o nExcepti o n, Val i d ati o nExcepti o n, and Excepti o n: 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 o nExcepti o n 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 o nExcepti o n clause. In the Java DSL, you can group multiple exceptions as follows:
4 4
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
onException(ValidationException.class, BuesinessException.class)
.to("activemq:validationFailed");
In the XML DSL, you can group multiple exceptions together by defining more than one
excepti o n element inside the o nExcepti o n 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 o nExcepti o n 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 o nExcepti o n 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: a. If the exception-to-test has exactly the type specified in the o nExcepti o n clause (tested using i nstanceo f), a match is triggered.
b. If the exception-to-test is a sub-type of the type specified in the o nExcepti o n 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.
No t e
The thro wExcepti o n 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}" />
De adle t t e r channe l
The basic examples of o nExcepti o n usage have so far all exploited the deadletter channel pattern. That is, when an o nExcepti o n 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.
4 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
For more details about the deadletter channel pattern, see Section 6.3, “ Dead Letter
Use o riginal m e ssage
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.
In the Java DSL, you can replace the message in the exchange by the original message, using the useO ri g i nal Messag e() DSL command, as follows: onException(ValidationException.class)
.useOriginalMessage()
.to("activemq:validationFailed");
In the XML DSL, you can retrieve the original message by setting the
useO ri g i nal Messag e attribute on the o nExcepti o n element, as follows:
<onException useOriginalMessage="true">
<exception>com.mycompany.ValidationException</exception>
<to uri="activemq:validationFailed"/>
</onException>
No t e
By default, 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
useO ri g i nal Messag e(). But if the setAl l o wUseO ri g i nal Messag e() option is set to fal se on the Camel context, the original message will not be accessible and you cannot call useO ri g i nal Messag e() (for example, you might want to choose this behaviour to optimize performance when processing large messages).
Re de live ry po licy
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:
maxi mumR ed el i veri es()
4 6
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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).
retryWhi l e()
Specifies a predicate (of P red i cate 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 maxi mumR ed el i veri es() option.
In the Java DSL, redelivery policy options are specified using DSL commands in the
o nExcepti o n clause. For example, you can specify a maximum of six redeliveries, after which the exchange is sent to the val i d ati o nFai l ed 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
red el i veryP o l i cy 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 red el i veryP o l i cyP ro fi l e instance. You can then reference the red el i veryP o l i cyP ro fi l e instance using the
o nExcepti o n element's red el i verP o l i cyR ef 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>
4 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
The approach using red el i veryP o l i cyP ro fi l e is useful, if you want to reuse the same redelivery policy in multiple o nExcepti o n clauses.
Co ndit io nal t rapping
Exception trapping with o nExcepti o n can be made conditional by specifying the o nWhen option. If you specify the o nWhen option in an o nExcepti o n clause, a match is triggered only when the thrown exception matches the clause and the o nWhen predicate evaluates to
true on the current exchange.
For example, in the following Java DSL fragment,the first o nExcepti o n clause triggers, only if the thrown exception matches MyUserExcepti o n 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 o nExcepti o n 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>
4 8
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Handling e xce pt io ns
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 o nExcepti o n clause is triggered, the behavior is essentially the same, except that the o nExcepti o n 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 o nExcepti o n provides various options to modify the exception handling behavior, as follows:
the section called “ Suppressing exception rethrow”
—you have the option of suppressing the rethrown exception after the o nExcepti o n 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.
the section called “ 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.
the section called “ 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.
Suppre ssing e xce pt io n re t hro w
To prevent the current exception from being rethrown and propagated back to the consumer endpoint, you can set the hand l ed () 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 hand l ed () option can be of boolean type, of
P red i cate type, or of Expressi o n 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 hand l ed element, as follows:
<onException>
<exception>com.mycompany.ValidationException</exception>
<handled>
<constant>true</constant>
</handled>
<to uri="activemq:validationFailed"/>
</onException>
4 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Co nt inuing pro ce ssing
To continue processing the current message from the point in the route where the exception was originally thrown, you can set the co nti nued option to true in the Java DSL, as follows: onException(ValidationException.class)
.continued(true);
In the Java DSL, the argument to the co nti nued () option can be of boolean type, of
P red i cate type, or of Expressi o n 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 co nti nued element, as follows:
<onException>
<exception>com.mycompany.ValidationException</exception>
<continued>
<constant>true</constant>
</continued>
</onException>
Se nding a re spo nse
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 hand l ed 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, So rry, whenever the MyFuncti o nal Excepti o n 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 excepti o nMessag e() builder method. For example, you can send a reply containing just the text of the exception message whenever the
MyFuncti o nal Excepti o n exception occurs, as follows:
// we catch MyFunctionalException and want to mark it as handled (=
50
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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
excepti o n. messag e 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 o nExcepti o n 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>
Exce pt io n t hro wn while handling an e xce pt io n
An exception that gets thrown while handling an existing exception (in other words, one that gets thrown in the middle of processing an o nExcepti o n 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
o nExcepti o n clause getting locked into an infinite loop.
51
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Sco pe s
The o nExcepti o n clauses can be effective in either of the following scopes:
RouteBuilder scope—o nExcepti o n clauses defined as standalone statements inside a
R o uteBui l d er. co nfi g ure() method affect all of the routes defined in that
R o uteBui l d er instance. On the other hand, these o nExcepti o n clauses have no effect
whatsoever on routes defined inside any other R o uteBui l d er instance. The
o nExcepti o n clauses must appear before the route definitions.
All of the examples up to this point are defined using the R o uteBui l d er scope.
Route scope—o nExcepti o n clauses can also be embedded directly within a route.
These onException clauses affect only the route in which they are defined.
Ro ut e sco pe
You can embed an o nExcepti o n clause anywhere inside a route definition, but you must terminate the embedded o nExcepti o n clause using the end () DSL command.
For example, you can define an embedded o nExcepti o n 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 o nExcepti o n 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>
52
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
2.3.2. Error Handler
Ove rvie w
The erro rHand l er() clause provides similar features to the o nExcepti o n clause, except that this mechanism is not able to discriminate between different exception types. The
erro rHand l er() clause is the original exception handling mechanism provided by Apache
Camel and was available before the o nExcepti o n clause was implemented.
Java DSL e xam ple
The erro rHand l er() clause is defined in a R o uteBui l d er class and applies to all of the routes in that R o uteBui l d er 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 d ead Letter queue, you can define a R o uteBui l d er 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 e xam ple
In the XML DSL, you define an error handler within a camel C o ntext scope using the
erro rHand l er element. For example, to define an error handler that routes all failed exchanges to the ActiveMQ d ead Letter queue, you can define an erro rHand l er 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">
53
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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>
T ype s o f e rro r handle r
Table 2.1, “ Error Handler Types” provides an overview of the different types of error handler
you can define.
T ab le 2.1. Erro r Han d ler T yp es
Java DSL Builder d efaul tErro rHand l er(
) d ead LetterC hannel () l o g g i ng Erro rC hannel
() no Erro rHand l er()
XML DSL T ype At t ribut e
D efaul tErro rHand l er
D ead LetterC hannel
Lo g g i ng Erro rC hannel
No Erro rHand l er
T ransacti o nErro rHand l er
Descript ion
Pro p ag ates excep tio ns b ack to the caller and sup p o rts the red elivery p o licy, b ut it d o es no t sup p o rt a d ead letter q ueue.
Sup p o rts the same features as the d efault erro r hand ler and , in ad d itio n, sup p o rts a d ead letter q ueue.
Lo g s the excep tio n text whenever an excep tio n o ccurs.
Dummy hand ler imp lementatio n that can b e used to d isab le the erro r hand ler.
An erro r hand ler fo r transacted ro utes. A d efault transactio n erro r hand ler instance is auto matically used fo r a ro ute that is marked as transacted .
54
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
2.3.3. doT ry, doCat ch, and doFinally
Ove rvie w
To handle exceptions within a route, you can use a combination of the d o T ry, d o C atch, and d o Fi nal l y clauses, which handle exceptions in a similar way to Java's try, catch, and fi nal l y blocks.
Sim ilarit ie s be t we e n do Cat ch and Java cat ch
In general, the d o C atch() 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
d o C atch() clause:
Multiple doCatch clauses—you can have multiple d o C atch clauses within a single d o T ry block. The d o C atch clauses are tested in the order they appear, just like Java catch() statements. Apache Camel executes the first d o C atch clause that matches the thrown exception.
No t e
This algorithm is different from the exception matching algorithm used by the
Section 2.3.1, “ onException Clause”
for details.
Rethrowing exceptions—you can rethrow the current exception from within a d o C atch
clause using the hand l ed sub-clause (see the section called “ Rethrowing exceptions in doCatch” ).
Spe cial fe at ure s o f do Cat ch
There are some special features of the d o C atch() clause, however, that have no analogue in the Java catch() statement. The following features are specific to d o C atch():
Catching multiple exceptions—the d o C atch clause allows you to specify a list of exceptions to catch, in contrast to the Java catch() statement, which catches only one exception (see
the section called “ Example” ).
Conditional catching—you can catch an exception conditionally, by appending an o nWhen
Exam ple
The following example shows how to write a d o T ry block in the Java DSL, where the
d o C atch() clause will be executed, if either the IO Excepti o n exception or the
Il l eg al StateExcepti o n exception are raised, and the d o Fi nal l y() clause is always executed, irrespective of whether an exception is raised or not.
55
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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>
Re t hro wing e xce pt io ns in do Cat ch
It is possible to rethrow an exception in a d o C atch() clause by calling the hand l ed () sub-clause with its argument set to fal se, 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
.hand l ed (false)
.to("mock:io")
.doCatch(Exception.class)
// and catch all other exceptions
.to("mock:error")
.end();
56
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
In the preceding example, if the IO Excepti o n is caught by d o C atch(), the current exchange is sent to the mo ck: i o endpoint, and then the IO Excepti o n is rethrown. This gives the consumer endpoint at the start of the route (in the fro m() 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>
Co ndit io nal e xce pt io n cat ching using o nWhe n
A special feature of the Apache Camel d o C atch() 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,
d o C atch(ExceptionList). d o When(Expression), an exception will only be caught, if the predicate expression, Expression, evaluates to true at run time.
For example, the following d o T ry block will catch the exceptions, IO Excepti o n and
Il l eg al StateExcepti o n, only if the exception message contains the word, Severe: from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class, IllegalStateException.class)
.o nWhen(exceptionMessage().contains("Severe"))
.to("mock:catch")
.doCatch(CamelExchangeException.class)
.to("mock:catchCamel")
.doFinally()
.to("mock:finally")
.end();
57
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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>
<o nWhen>
<simple>${exception.message} contains
'Severe'</simple>
</o nWhen>
<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>
Ne st e d Co ndit io ns in do T ry
There are various options available to add Camel exception handling to a JavaDSL route.
d o try() 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 C ho i ceD efi ni ti o n, you can use the following
d o T ry blocks: from( "direct:wayne-gettoken" ).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-gettoken" ).setExchangePattern(ExchangePattern.InOut)
58
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
.doTry()
.to( "https4://wayne-token-service" )
.doCatch(Exception.class)
.log( ">> Exception" )
.endDoTry();
2.3.4 . Propagat ing SOAP Except ions
Ove rvie w
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 For detailed information, see
CXF
in the Apache Camel Component
Reference Guide.
Ho w t o pro pagat e st ack t race info rm at io n
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 d ataFo rmat to P AY LO AD and set the
faul tStackT raceEnabl ed property to true in the cxfEnd po i nt 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= "faul tStackT raceEnabl ed " val ue= "true" />
<entry key= "d ataFo rmat" val ue= "P AY LO AD " />
</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 C aused by). If you want to include the causing exception in the stack trace, set the excepti o nMessag eC auseEnabl ed property to true in the
cxfEnd po i nt 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
59
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
value is false -->
<entry key= "excepti o nMessag eC auseEnabl ed " val ue= "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>
Warn in g
You should only enable the excepti o nMessag eC auseEnabl ed 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.
60
2.4 . BEAN INT EGRAT ION
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 regist ry
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).
Regist ry plug-in st rat egy
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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” .
T ab le 2.2. Reg ist ry Plu g - In s
Regist ry Implement at ion
Sp ring b ean reg istry
G uice b ean reg istry
Bluep rint b ean reg istry
O SG i service reg istry
JNDI reg istry
Camel Component wit h Regist ry Plug- In camel -spri ng camel -g ui ce camel -bl uepri nt
d ep lo yed in OSGi container
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 Appl i cati o nC o ntextR eg i stry plug-in is automatically installed in the current C amel C o ntext instance.
Deployment in an OSGi container is a special case. When an Apache Camel route is deployed into the OSGi container, the C amel C o ntext 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 creat ed 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, MyBeanP ro cesso r, 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 MyBeanP ro cesso r type and invokes the pro cessBo d y() method to process inbound exchanges. This approach is adequate if you only want to access the MyBeanP ro cesso r instance from a single route. However, if you want to access the same MyBeanP ro cesso r instance from multiple routes, use the variant of bean() that takes the O bject type as its first argument. For example:
MyBeanProcessor myBean = new MyBeanProcessor(); from("file:data/inbound")
61
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
.bean(myBean, "processBody")
.to("file:data/outbound"); from("activemq:inboundData")
.bean(myBean, "processBody")
.to("activemq:outboundData");
Accessing overloaded bean met hods
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
MyBeanBro cesso r class has two overloaded methods, pro cessBo d y(Stri ng ) and
pro cessBo d y(Stri ng ,Stri ng ), 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 pro cessBo d y 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, pro cessBo d y(Exchang e)—or a fully qualified type name—for example,
pro cessBo d y(o rg . apache. camel . Exchang e).
No t e
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 paramet ers explicit ly
You can specify parameter values explicitly, when you call the bean method. The following simple type values can be passed:
Boolean: true or fal se.
Numeric: 123, 7, and so on.
String: ' In si ng l e q uo tes' or "In d o ubl e q uo tes".
Null object: nul l .
62
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 ti tl e 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. uti l . Map: from("file:data/inbound")
.bean(MyBeanProcessor.class,
"processBodyAndAllHeaders(${body},${header})")
.to("file:data/outbound");
Basic met hod signat ures
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:
the section called “ Method signature for processing message bodies” .
the section called “ Method signature for processing exchanges”
.
Met hod signat ure 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 Stri ng argument and returns a Stri ng value. For example:
// Java package com.acme; public class MyBeanProcessor {
public String processBody(String body) {
63
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Do whatever you like to 'body'...
return newBody;
}
}
Met hod signat ure 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
o rg . apache. camel . Exchang e parameter and returns vo i d . 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 MyBeanP ro cesso r:
<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 beanR ef() processor invokes the MyBeanP ro cesso r. pro cessBo d y() 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>
64
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 i d attribute). For example, given the bean element with ID equal to myBeanId , you can reference the bean in a Java
DSL route using the beanR ef() 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 co m. acme. MyBeanP ro cesso r type:
@ BeanInject com.acme.MyBeanProcessor bean;
Bean shut down order in Spring XML
For the beans used by a Camel context, the correct shutdown order is usually:
1. Shut down the camel C o ntext 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
65
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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
camel C o ntext appear in the Spring XML file. In order to avoid random errors due to incorrect shutdown order, therefore, the camel C o ntext 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 shutd o wnEag er attribute on the camel C o ntext element to fal se. In this case, you could potentially exercise more fine-grained control over shutdown order using the Spring d epend s-o n attribute.
Paramet er binding annot at ions
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:
the section called “ Basic annotations” .
the section called “ Expression language annotations”
.
the section called “ Inherited annotations” .
Basic annot at ions
Table 2.3, “ Basic Bean Annotations” shows the annotations from the o rg . apache. camel
Java package that you can use to inject message data into the arguments of a bean method.
T ab le 2.3. Basic Bean An n o t at io n s
Annot at ion
@ Attachments
@ Bo d y
@ Head er
@ Head ers
Meaning
Bind s to a list o f attachments.
Bind s to an inb o und messag e b o d y.
Bind s to an inb o und messag e head er.
Bind s to a
java. uti l . Map
o f the inb o und messag e head ers.
Paramet er?
String name o f the head er.
66
Annot at ion
@ O utHead ers
@ P ro perty
@ P ro perti es
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Meaning
Bind s to a
java. uti l . Map
o f the o utb o und messag e head ers.
Paramet er?
String name o f the p ro p erty.
Bind s to a named exchang e p ro p erty.
Bind s to a
java. uti l . Map
o f the exchang e p ro p erties.
For example, the following class shows you how to use basic annotations to inject message data into the pro cessExchang e() 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 o rg . apache. camel . Exchang e argument.
Expression language annot at ions
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 o rg . apache. camel . l ang uag e package
(and sub-packages, for the non-core annotations) that you can use to inject message data into the arguments of a bean method.
T ab le 2.4 . Exp ressio n Lan g u ag e An n o t at io n s
Annot at ion
@ Bean
Descript ion
Injects a Bean exp ressio n.
67
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Annot at ion
@ C o nstant
@ EL
@ G ro o vy
@ Head er
@ JavaScri pt
@ O G NL
@ P HP
@ P ytho n
@ R uby
@ Si mpl e
@ XP ath
@ XQ uery
Descript ion
Injects a Co nstant exp ressio n
Injects an EL exp ressio n.
Injects a G ro o vy exp ressio n.
Injects a Head er exp ressio n.
Injects a JavaScrip t exp ressio n.
Injects an O G NL exp ressio n.
Injects a PHP exp ressio n.
Injects a Pytho n exp ressio n.
Injects a Rub y exp ressio n.
Injects a Simp le exp ressio n.
Injects an XPath exp ressio n.
Injects an XQ uery exp ressio n.
For example, the following class shows you how to use the @ XP ath 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...
...
}
}
68
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(
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
@ Bean("myCorrIdGenerator") String corrId,
@ Body String body
) {
// Check the user/pass credentials...
...
}
}
Where the string, myC o rrId G enerato r, 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 MySi mpl eId G enerato r 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, MyId G enerato r.
The only restriction on the g enerate() 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.
No t e
Some of the language annotations are available in the core component (@ Bean,
@ C o nstant, @ Si mpl e, and @ XP ath). 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 -o g nl component.
Inherit ed annot at ions
69
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Parameter binding annotations can be inherited from an interface or from a superclass. For example, if you define a Java interface with a Head er annotation and a Bo d y 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, MyBeanP ro cesso r, 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, // Inheri ts Head er anno tati o n
String body, // Inheri ts Bo d y anno tati o n
Exchange exchange
) {
...
}
}
Int erface implement at ions
The class that implements a Java interface is often pro tected , pri vate or in packag e-
o nl y 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);
}
70
Where the BeanIntf interface is implemented by the following protected BeanIntfImpl class:
// Java protected class BeanIntfImpl implements BeanIntf {
void processBodyAndHeader(String body, String title) {
...
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
}
}
The following bean invocation would fall back to invoking the public
BeanIntf. pro cessBo d yAnd Head er method: from("file:data/inbound")
.bean(BeanIntfImpl.class, "processBodyAndHeader(${body},
${header.title})")
.to("file:data/outbound");
Invoking st at ic met hods
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, chang eSo methi ng ():
// 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 chang eSo methi ng method, as follows: from("direct:a")
. bean(MyStati cC l ass. cl ass, "chang eSo methi ng ")
.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 MyStati cC l ass.
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,
71
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
o rg . fuseso urce. exampl e. Hel l o Wo rl d O sg i Servi ce, you could invoke the
sayHel l o 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. CREAT ING EXCHANGE INST ANCES
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
Exchang e object, the easiest approach is to invoke the methods of the Exchang eBui l d er class, as described here.
ExchangeBuilder class
The fully qualified name of the Exchang eBui l d er class is as follows: org.apache.camel.builder.ExchangeBuilder
The Exchang eBui l d er exposes the static method, anExchang e, 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, Hel l o Wo rl d ! , 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)
72
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
.withBody("Hello World!")
.withHeader("username", "jdoe")
.withHeader("password", "pass")
.build();
ExchangeBuilder met hods
The Exchang eBui l d er class supports the following methods:
Exchang eBui l d er anExchang e(C amel C o ntext co ntext)
(static method) Initiate building an exchange object.
Exchang e bui l d ()
Build the exchange.
Exchang eBui l d er wi thBo d y(O bject bo d y)
Set the message body on the exchange (that is, sets the exchange's In message body).
Exchang eBui l d er wi thHead er(Stri ng key, O bject val ue)
Set a header on the exchange (that is, sets a header on the exchange's In message).
Exchang eBui l d er wi thP attern(Exchang eP attern pattern)
Sets the exchange pattern on the exchange.
Exchang eBui l d er wi thP ro perty(Stri ng key, O bject val ue)
Sets a property on the exchange.
2.6. T RANSFORMING MESSAGE CONT ENT
Ab st ract
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 T ransformat ions
Ove rvie w
73
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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, Wo rl d ! , to the end of the
incoming message body.
Examp le 2.1. Simp le T ran sf o rmat io n o f In co min g Messag es
from("SourceURL").setBody(body().append("
World!")).to("TargetURL");
Where the setBo d y() command replaces the content of the incoming message's body.
API fo r sim ple t ransfo rm at io ns
You can use the following API classes to perform simple transformations of the message content in a router rule:
o rg . apache. camel . mo d el . P ro cesso rD efi ni ti o n o rg . apache. camel . bui l d er. Bui l d er o rg . apache. camel . bui l d er. Val ueBui l d er
Pro ce sso rDe finit io n class
The o rg . apache. camel . mo d el . P ro cesso rD efi ni ti o n class defines the DSL commands you can insert directly into a router rule—for example, the setBo d y() command
in Example 2.1, “ Simple Transformation of Incoming Messages”
Methods from the ProcessorDefinition Class” shows the P ro cesso rD efi ni ti o n methods
that are relevant to transforming message content:
T ab le 2.5. T ran sf o rmat io n Met h o d s f ro m t h e Pro cesso rDef in it io n Class
Met hod
T ype co nvertBo d yT o (C l ass type)
T ype remo veFaul tHead er(Stri ng name)
T ype remo veHead er(Stri ng name)
Descript ion
Co nverts the IN messag e b o d y to the sp ecified typ e.
Ad d s a p ro cesso r which remo ves the head er o n the FAULT messag e.
Ad d s a p ro cesso r which remo ves the head er o n the IN messag e.
74
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Met hod Descript ion
T ype remo veP ro perty(Stri ng name)
Ad d s a p ro cesso r which remo ves the exchang e p ro p erty.
Expressi o nC l ause<P ro cesso rD efi n i ti o n<T ype>> setBo d y()
T ype setFaul tBo d y(Expressi o n expressi o n)
Ad d s a p ro cesso r which sets the b o d y o n the IN messag e.
Ad d s a p ro cesso r which sets the b o d y o n the FAULT messag e.
T ype setFaul tHead er(Stri ng name,
Expressi o n expressi o n)
Ad d s a p ro cesso r which sets the head er o n the FAULT messag e.
Expressi o nC l ause<P ro cesso rD efi n i ti o n<T ype>> setHead er(Stri ng name)
T ype setHead er(Stri ng name,
Expressi o n expressi o n)
Ad d s a p ro cesso r which sets the head er o n the IN messag e.
Ad d s a p ro cesso r which sets the head er o n the IN messag e.
Expressi o nC l ause<P ro cesso rD efi n i ti o n<T ype>> setO utHead er(Stri ng name)
Ad d s a p ro cesso r which sets the head er o n the O UT messag e.
T ype setO utHead er(Stri ng name,
Expressi o n expressi o n)
Ad d s a p ro cesso r which sets the head er o n the O UT messag e.
Expressi o nC l ause<P ro cesso rD efi n i ti o n<T ype>> setP ro perty(Stri ng name)
Ad d s a p ro cesso r which sets the exchang e p ro p erty.
T ype setP ro perty(Stri ng name,
Expressi o n expressi o n)
Expressi o nC l ause<P ro cesso rD efi n i ti o n<T ype>> transfo rm()
Ad d s a p ro cesso r which sets the exchang e p ro p erty.
Ad d s a p ro cesso r which sets the b o d y o n the O UT messag e.
T ype transfo rm(Expressi o n expressi o n)
Ad d s a p ro cesso r which sets the b o d y o n the O UT messag e.
Builde r class
The o rg . apache. camel . bui l d er. Bui l d er class provides access to message content in contexts where expressions or predicates are expected. In other words, Bui l d er methods are typically invoked in the arguments of DSL commands—for example, the bo d y()
command in Example 2.1, “ Simple Transformation of Incoming Messages”
“ Methods from the Builder Class” summarizes the static methods available in the Bui l d er
class.
T ab le 2.6 . Met h o d s f ro m t h e Bu ild er Class
75
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Met hod Descript ion stati c <E extend s Exchang e>
Val ueBui l d er<E> bo d y()
Returns a p red icate and value b uild er fo r the inb o und b o d y o n an exchang e.
stati c <E extend s Exchang e,T >
Val ueBui l d er<E> bo d yAs(C l ass<T > type)
Returns a p red icate and value b uild er fo r the inb o und messag e b o d y as a sp ecific typ e.
stati c <E extend s Exchang e>
Val ueBui l d er<E> co nstant(O bject val ue)
Returns a co nstant exp ressio n.
Returns a p red icate and value b uild er fo r the fault b o d y o n an exchang e.
stati c <E extend s Exchang e>
Val ueBui l d er<E> faul tBo d y() stati c <E extend s Exchang e,T >
Val ueBui l d er<E> faul tBo d yAs(C l ass<T > type)
Returns a p red icate and value b uild er fo r the fault messag e b o d y as a sp ecific typ e.
stati c <E extend s Exchang e>
Val ueBui l d er<E> head er(Stri ng name) stati c <E extend s Exchang e>
Val ueBui l d er<E> o utBo d y()
Returns a p red icate and value b uild er fo r head ers o n an exchang e.
Returns a p red icate and value b uild er fo r the o utb o und b o d y o n an exchang e.
stati c <E extend s Exchang e>
Val ueBui l d er<E> o utBo d yAs(C l ass<T > type) stati c Val ueBui l d er pro perty(Stri ng name) stati c Val ueBui l d er reg exR epl aceAl l (Expressi o n co ntent, Stri ng reg ex,
Expressi o n repl acement) stati c Val ueBui l d er reg exR epl aceAl l (Expressi o n co ntent, Stri ng reg ex, Stri ng repl acement) stati c Val ueBui l d er send T o (Stri ng uri ) stati c <E extend s Exchang e>
Val ueBui l d er<E> systemP ro perty(Stri ng name)
Returns a p red icate and value b uild er fo r the o utb o und messag e b o d y as a sp ecific typ e.
Returns a p red icate and value b uild er fo r p ro p erties o n an exchang e.
Returns an exp ressio n that rep laces all o ccurrences o f the reg ular exp ressio n with the g iven rep lacement.
Returns an exp ressio n that rep laces all o ccurrences o f the reg ular exp ressio n with the g iven rep lacement.
Returns an exp ressio n p ro cessing the exchang e to the g iven end p o int uri.
Returns an exp ressio n fo r the g iven system p ro p erty.
76
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Met hod stati c <E extend s Exchang e>
Val ueBui l d er<E> systemP ro perty(Stri ng name,
Stri ng d efaul tVal ue)
Descript ion
Returns an exp ressio n fo r the g iven system p ro p erty.
Value Builde r class
The o rg . apache. camel . bui l d er. Val ueBui l d er class enables you to modify values returned by the Bui l d er methods. In other words, the methods in Val ueBui l d er provide
a simple way of modifying message content. Table 2.7, “ Modifier Methods from the
ValueBuilder Class” summarizes the methods available in the Val ueBui l d er 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).
T ab le 2.7. Mo d if ier Met h o d s f ro m t h e Valu eBu ild er Class
Met hod
Val ueBui l d er<E> append (O bject val ue)
P red i cate co ntai ns(O bject val ue)
Descript ion
Ap p end s the string evaluatio n o f this exp ressio n with the g iven value.
Create a p red icate that the left hand exp ressio n co ntains the value o f the rig ht hand exp ressio n.
Val ueBui l d er<E> co nvertT o (C l ass type)
Co nverts the current value to the g iven typ e using the reg istered typ e co nverters.
Val ueBui l d er<E> co nvertT o Stri ng ()
P red i cate end sWi th(O bject val ue)
Co nverts the current value a String using the reg istered typ e co nverters.
<T > T eval uate(Exchang e exchang e, C l ass<T > type)
P red i cate i n(O bject. . . val ues)
P red i cate i n(P red i cate. . . pred i cates)
P red i cate i sEq ual T o (O bject val ue)
P red i cate i sG reaterT han(O bject val ue)
Returns true, if the current value is eq ual to the g iven
val ue
arg ument.
Returns true, if the current value is g reater than the g iven
val ue
arg ument.
77
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Met hod
P red i cate i sG reaterT hanO rEq ual T o (O bject val ue)
P red i cate i sInstanceO f(C l ass type)
P red i cate i sLessT han(O bject val ue)
P red i cate i sLessT hanO rEq ual T o (O bject val ue)
P red i cate i sNo tEq ual T o (O bject val ue)
P red i cate i sNo tNul l ()
P red i cate i sNul l ()
P red i cate matches(Expressi o n expressi o n)
P red i cate no t(P red i cate pred i cate)
Val ueBui l d er prepend (O bject val ue)
P red i cate reg ex(Stri ng reg ex)
Val ueBui l d er<E> reg exR epl aceAl l (Stri ng reg ex,
Expressi o n<E> repl acement)
Val ueBui l d er<E> reg exR epl aceAl l (Stri ng reg ex,
Stri ng repl acement)
Val ueBui l d er<E> reg exT o keni ze(Stri ng reg ex)
Descript ion
Returns true, if the current value is g reater than o r eq ual to the g iven
val ue
arg ument.
Returns true, if the current value is an instance o f the g iven typ e.
Returns true, if the current value is less than the g iven
val ue
arg ument.
Returns true, if the current value is less than o r eq ual to the g iven
val ue
arg ument.
Returns true, if the current value is no t eq ual to the g iven
val ue
arg ument.
Returns true, if the current value is no t
nul l
.
Returns true, if the current value is
nul l
.
Neg ates the p red icate arg ument.
Prep end s the string evaluatio n o f this exp ressio n to the g iven value.
Rep laces all o ccurrencies o f the reg ular exp ressio n with the g iven rep lacement.
Rep laces all o ccurrencies o f the reg ular exp ressio n with the g iven rep lacement.
Val ueBui l d er so rt(C o mparato r co mparato r)
P red i cate startsWi th(O bject val ue)
To kenizes the string co nversio n o f this exp ressio n using the g iven reg ular exp ressio n.
So rts the current value using the g iven co mp arato r.
Returns true, if the current value matches the string value o f the
val ue
arg ument.
78
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Met hod
Val ueBui l d er<E> to keni ze()
Descript ion
To kenizes the string co nversio n o f this exp ressio n using the co mma to ken sep arato r.
Val ueBui l d er<E> to keni ze(Stri ng to ken)
To kenizes the string co nversio n o f this exp ressio n using the g iven to ken sep arato r.
2.6.2. Marshalling and Unmarshalling
Java DSL co m m ands
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.
Dat a fo rm at s
Apache Camel supports marshalling and unmarshalling of the following data formats:
Java serialization
JAXB
XMLBeans
XStream
Java se rializat io n
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">
79
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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("GeneratedPackageNam
e"); 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>
XMLBe ans
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");
80
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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>
XSt re am
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");
No t e
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.
Dat aFo rm at Binding
The D ataFo rmatBi nd i ng 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 D ataFo rmatBi nd i ng instance, passing a reference to the relevant data format in the constructor.
For example, the XML DSL snippet in
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:
81
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Examp le 2.2. JAXB Bin d in g
<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>
82
Asso ciat ing a binding wit h an e ndpo int
The following alternatives are available for associating a binding with an endpoint:
the section called “ Binding URI”
the section called “ BindingComponent”
Binding URI
To associate a binding with an endpoint, you can prefix the endpoint URI with
bi nd i ng : 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
.
<beans ...>
...
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="bi nd i ng : jaxb: activemq:orderQueue"/>
<to uri="bi nd i ng : jaxb: activemq:otherQueue"/>
</route>
</camelContext>
...
</beans>
BindingCo m po ne nt
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 Bi nd i ng C o mpo nent class.
For example, to associate the jaxb binding with acti vemq endpoints, you could define a new Bi nd i ng C o mpo nent 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">
83
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<route>
<from uri="bi nd i ng : jaxb: acti vemq : fo o . firstQueue"/>
<to uri="bi nd i ng : jaxb: acti vemq : fo o . otherQueue"/>
</route>
</camelContext>
...
</beans>
No t e
For developers that implement a custom Apache Camel component, it is possible to achieve this by implementing an endpoint class that inherits from the
o rg . apache. camel . spi . HasBi nd i ng interface.
BindingCo m po ne nt co nst ruct o rs
The Bi nd i ng C o mpo nent class supports the following constructors:
publ i c Bi nd i ng C o mpo nent()
No arguments form. Use property injection to configure the binding component instance.
publ i c Bi nd i ng C o mpo nent(Bi nd i ng bi nd i ng )
Associate this binding component with the specified Bi nd i ng object, bi nd i ng .
publ i c Bi nd i ng C o mpo nent(Bi nd i ng bi nd i ng , Stri ng uri P refi x)
Associate this binding component with the specified Bi nd i ng object, bi nd i ng , and URI prefix, uri P refi x. This is the most commonly used constructor.
publ i c Bi nd i ng C o mpo nent(Bi nd i ng bi nd i ng , Stri ng uri P refi x, Stri ng uri P o stfi x)
This constructor supports the additional URI post-fix, uri P o stfi x, argument, which is automatically appended to any URIs defined using this binding component.
Im ple m e nt ing a cust o m binding
In addition to the D ataFo rmatBi nd i ng , 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 o rg . apache. camel . P ro cesso r class to perform a transformation on messages incoming to a consumer endpoint (appearing in a fro m element).
84
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
2. Implement a complementary o rg . apache. camel . P ro cesso r class to perform the reverse transformation on messages outgoing from a producer endpoint (appearing in a to element).
3. Implement the o rg . apache. camel . spi . Bi nd i ng interface, which acts as a factory for the processor instances.
Binding int e rface
Example 2.3, “ The org.apache.camel.spi.Binding Interface”
shows the definition of the
o rg . apache. camel . spi . Bi nd i ng interface, which you must implement to define a custom binding.
Examp le 2.3. T h e o rg .ap ach e.camel.sp i.Bin d in g In t erf ace
// 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();
}
Whe n t o use bindings
85
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Bindings are useful when you need to apply the same kind of transformation to many different kinds of endpoint.
2.7. PROPERT Y 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, {{remo te. ho st}} and {{remo te. po rt}}: 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
No t e
Property Placeholders support an encoding option that enables you to read the
. pro perti es 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 P ro pertyP l aceho l d ers support the following:
Specify the default value together with the key to lookup.
No need to define the P ro perti esC o mpo nent, 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.
No t e
Provide three out of the box functions to lookup values from OS environmental variable, JVM system properties, or the service name idiom.
Propert y files
86
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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
Examp le 2.4 . Samp le Pro p ert y 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 propert ies
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:
cl asspath: PathName,PathName,. . .
(Default) Specifies locations on the classpath, where PathName is a file pathname delimited using forward slashes.
fi l e: 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. uti l . P ro perti es object in the registry.
bl uepri nt: BeanID
Specifies the ID of a cm: pro perty-pl aceho l d er bean, which is used in the context of an OSGi blueprint file to access properties defined in the OSGi
87
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
context of an OSGi blueprint file to access properties defined in the OSGi
For example, to specify the co m/fuseso urce/cheese. pro perti es property file and the
co m/fuseso urce/bar. pro perti es property file, both located on the classpath, you would use the following location string: com/fusesource/cheese.properties,com/fusesource/bar.properties
No t e
You can omit the cl asspath: prefix in this example, because the classpath resolver is used by default.
Specifying locat ions using syst em propert ies 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. ho me, 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_HO ME, you could embed that directory value in a file location, as follows: file:${env:SMX_HOME}/etc/foo.properties
Configuring t he propert ies 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();
88
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
pc.setLocation("com/fusesource/cheese.properties,com/fusesource/bar.pro
perties"); context.addComponent("properties", pc);
As shown in the ad d C o mpo nent() call, the name of the properties component must be set to pro perti es.
In the XML DSL, you can configure the properties component using the dedicated
pro pertyP l acho l d er element, as follows:
<camelContext ...>
<propertyPlaceholder
id="properties" location="com/fusesource/cheese.properties,com/fusesource/bar.properti
es"
/>
</camelContext>
If you want the properties component to ignore any missing . pro perti es files when it is being initialized, you can set the i g no reMi ssi ng Lo cati o n option to true (normally, a missing . pro perti es 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
i g no reMi ssi ng Lo cati o n option to true.
Placeholder synt ax
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: stri ng attributes are set using the following syntax:
AttributeName="{{Key}}"
Other attribute types (for example, xs: i nt or xs: bo o l ean) must be set using the following syntax: prop:AttributeName="Key"
Where pro p is associated with the
http: //camel . apache. o rg /schema/pl aceho l d er namespace.
89
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
When setting Java DSL EIP options—to set an option on an Enterprise Integration Pattern
(EIP) command in the Java DSL, add a pl aceho l d er() clause like the following to the fluent DSL:
.placeholder("OptionName", "Key")
In Simple language expressions—the placeholder is specified as ${pro perti es: Key}.
Subst it ut ion 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 pro perti es 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 pro perti es: 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
l o cati o n option as follows: from("direct:start").to("properties:{{bar.end}}?
location=com/mycompany/bar.properties");
Subst it ut ion 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 jmxAg ent 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
90
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
-->
<jmxAgent id="agent" registryPort="{{myjmx.port}}"
usePlatformMBeanServer="{{myjmx.usePlatform}}"
createConnector="true"
statisticsLevel="RoutesOnly"
/>
<route>
<from uri="seda:start"/>
<to uri="mock:result"/>
</route>
</camelContext>
Subst it ut ion of XML DSL at t ribut e values
You can use the regular placeholder syntax for specifying attribute values of xs: stri ng type—for example, <jmxAg ent reg i stryP o rt= "{{myjmx. po rt}}" . . . >. But for attributes of any other type (for example, xs: i nt or xs: bo o l ean), you must use the special syntax, pro p: AttributeName= "Key".
For example, given that a property file defines the sto p. fl ag property to have the value,
true, you can use this property to set the sto pO nExcepti o n boolean attribute, as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xml ns: pro p= "http: //camel . apache. o rg /schema/pl aceho l d er"
... >
<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.prope
rties" xmlns="http://camel.apache.org/schema/spring"/>
<route>
<from uri="direct:start"/>
<multicast pro p: sto pO nExcepti o n= "sto p. fl ag ">
<to uri="mock:a"/>
<throwException ref="damn"/>
<to uri="mock:b"/>
</multicast>
</route>
</camelContext>
</beans>
91
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Imp o rt an t
The pro p prefix must be explicitly assigned to the
http: //camel . apache. o rg /schema/pl aceho l d er namespace in your
Spring file, as shown in the beans element of the preceding example.
Subst it ut ion of Java DSL EIP opt ions
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,
pl aceho l d er("OptionName", "Key").
For example, given that a property file defines the sto p. fl ag property to have the value,
true, you can use this property to set the sto pO nExcepti o n 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");
Subst it ut ion in Simple language expressions
You can also substitute property placeholders in Simple language expressions, but in this case the syntax of the placeholder is ${pro perti es: Key}. For example, you can substitute the cheese. q uo te 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,
${pro perti es: 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,
${pro perti es-l o cati o n: Location: Key}. For example, to substitute the bar. q uo te placeholder using the settings from the co m/myco mpany/bar. pro perti es property file, you can define a Simple expression as follows: from("direct:start")
.transform().simple("Hi ${body}. ${propertieslocation:com/mycompany/bar.properties:bar.quote}.");
Using Propert y Placeholders in t he XML DSL
92
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
In older releases, the xs: stri ng type attributes were used to support placeholders in the
XML DSL. For example, the timeout attribute would be a xs: i nt 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.
No t e
In the Multicast, set the option sto pO nExcepti o n as the value of the placeholder with the key sto p. 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.prope
rties" 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>
93
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
</camelContext>
</beans>
94
Int egrat ion wit h OSGi blueprint propert y 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:
the section called “ Implicit blueprint integration” .
the section called “ Explicit blueprint integration”
.
Implicit blueprint int egrat ion
If you define a camel C o ntext 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, {{co o l . end }}) that appear within the scope of camel C o ntext 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, {{resul t}}:
<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 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- OSGI blueprint property placeholder -->
<cm:property-placeholder id="myblueprint.placeholder" persistentid="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>
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
<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: pro perty-
pl aceho l d er bean. In the preceding example, the cm: pro perty-pl aceho l d er bean is associated with the camel . bl uepri nt 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: pro perty-pl aceho l d er bean provides access to all of the properties defined under the camel . bl uepri nt persistent ID. It is also possible to specify default values for some of the properties (using the nested cm: pro perty elements).
In the context of blueprint, the Apache Camel placeholder mechanism searches for an instance of cm: pro perty-pl aceho l d er in the bean registry. If it finds such an instance, it automatically integrates the Apache Camel placeholder mechanism, so that placeholders like, {{resul t}}, are resolved by looking up the key in the blueprint property placeholder mechanism (in this example, through the mybl uepri nt. pl aceho l d er bean).
No t e
The default blueprint placeholder syntax (accessing the blueprint properties directly) is ${Key}. Hence, outside the scope of a camel C o ntext element, the placeholder syntax you must use is ${Key}. Whereas, inside the scope of a
camel C o ntext element, the placeholder syntax you must use is {{Key}}.
Explicit blueprint int egrat ion
If you want to have more control over where the Apache Camel property placeholder mechanism finds its properties, you can define a pro pertyP l aceho l d er 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 pro pertyP l aceho l d er 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 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- OSGI blueprint property placeholder -->
<cm:property-placeholder id="mybl uepri nt. pl aceho l d er" persistentid="camel.blueprint">
<!-- list some properties for this test -->
<cm:default-properties>
95
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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 -->
<pro pertyP l aceho l d er i d = "pro perti es"
l o cati o n= "bl uepri nt: mybl uepri nt. pl aceho l d er"/>
<!-- 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 pro pertyP l aceho l d er element specifies explicitly which
cm: pro perty-pl aceho l d er bean to use by setting the location to
bl uepri nt: mybl uepri nt. pl aceho l d er. That is, the bl uepri nt: resolver explicitly references the ID, mybl uepri nt. pl aceho l d er, of the cm: pro perty-pl aceho l d er bean.
This style of configuration is useful, if there is more than one cm: pro perty-pl aceho l d er 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: pro perty-
pl aceho l d er bean and from the properties file, mypro perti es. pro perti es, on the classpath, you could define the pro pertyP l aceho l d er element as follows:
<propertyPlaceholder id="properties" location="blueprint:myblueprint.placeholder,classpath:myproperties.pro
perties"/>
Int egrat ion wit h Spring propert y 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,
o rg . apache. camel . spri ng . spi . Bri d g eP ro pertyP l aceho l d erC o nfi g urer.
Define a Bri d g eP ro pertyP l aceho l d erC o nfi g urer, which replaces both Apache
Camel's pro pertyP l aceho l d er element and Spring's ctx: pro perty-pl aceho l d er 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.
96
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
For example, defining a bridge property placeholder that reads its property settings from the
cheese. pro perti es 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.properti
es"/>
</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>
No t e
Alternatively, you can set the l o cati o n attribute of the
Bri d g eP ro pertyP l aceho l d erC o nfi g urer to point at a Spring properties file.
The Spring properties file syntax is fully supported.
2.8. T HREADING MODEL
Java t hread pool API
97
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java t hread 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 Executo rServi ce 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 t hread pool API
The Apache Camel thread pool API builds on the Java concurrency API by providing a central factory (of o rg . apache. camel . spi . Executo rServi ceManag er 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 t hreading model
Some Apache Camel components—such as SEDA, JMS, and Jetty—are inherently multithreaded. 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 Executo rServi ceManag er object.
Processor t hreading 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.
T ab le 2.8. Pro cesso r T h read in g O p t io n s
98
Processor ag g reg ate mul ti cast reci pi entLi st spl i t thread s wi reT ap
Java DSL
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
XML DSL
parallelProcessing() executorService() executorServiceRef()
@ parallelProcessing
@ executorServiceRef parallelProcessing() executorService() executorServiceRef()
@ parallelProcessing
@ executorServiceRef parallelProcessing() executorService() executorServiceRef()
@ parallelProcessing
@ executorServiceRef parallelProcessing() executorService() executorServiceRef()
@ parallelProcessing
@ executorServiceRef executorService() executorServiceRef() poolSize() maxPoolSize() keepAliveTime() timeUnit() maxQueueSize() rejectedPolicy()
@ executorServiceRef
@ poolSize
@ maxPoolSize
@ keepAliveTime
@ timeUnit
@ maxQueueSize
@ rejectedPolicy wireTap(String uri,
ExecutorService executorService) wireTap(String uri,
String executorServiceRef)
@ executorServiceRef
t hreads DSL opt ions
99
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The thread s 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:
po o l Si ze()
Minimum number of threads in the pool (and initial pool size).
maxP o o l Si ze()
Maximum number of threads in the pool.
keepAl i veT i me()
If any threads are idle for longer than this period of time (specified in seconds), they are terminated.
ti meUni t()
Time unit for keep alive, specified using the java. uti l . co ncurrent. T i meUni t type.
maxQ ueueSi ze()
Maximum number of pending tasks that this thread pool can store in its incoming task queue.
rejected P o l i cy()
Specifies what course of action to take, if the incoming task queue is full. See
Table 2.10, “ Thread Pool Builder Options”
No t e
The preceding thread pool options are not compatible with the
executo rServi ceR ef option (for example, you cannot use these options to override the settings in the thread pool referenced by an executo rServi ceR ef option). Apache Camel validates the DSL to enforce this.
Creat ing a default t hread pool
To create a default thread pool for one of the threading-aware processors, enable the
paral l el P ro cessi ng option, using the paral l el P ro cessi ng () sub-clause, in the
Java DSL, or the paral l el P ro cessi ng 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:
100
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 t hread pool profile set t ings
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).
T ab le 2.9 . Def au lt T h read Po o l Pro f ile Set t in g s
T hread O pt ion maxQ ueueSi ze po o l Si ze maxP o o l Si ze keepAl i veT i me rejected P o l i cy
Default Value
10 0 0
10
20
6 0
(seconds)
C al l erR uns
Changing t he default t hread 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.
101
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
For example, in the Java DSL, you can customize the po o l Si ze option and the
maxQ ueueSi ze 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"
d efaul tP ro fi l e= "true"
poolSize="3"
maxQueueSize="100"/>
...
</camelContext>
Note that it is essential to set the d efaul tP ro fi l e 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.
Cust omiz ing a processor's t hread pool
It is also possible to specify the thread pool for a threading-aware processor more directly, using either the executo rServi ce or executo rServi ceR ef options (where these options are used instead of the paral l el P ro cessi ng 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 Executo rServi ce (thread pool) instance and pass it to the executo rServi ce option.
Specify a custom thread pool profile—create and register a custom thread pool factory.
When you reference this factory using the executo rServi ceR ef option, the processor automatically uses the factory to create a custom thread pool instance.
When you pass a bean ID to the executo rServi ceR ef 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.
102
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Creat ing a cust om t hread 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 o rg . apache. camel . bui l d er. T hread P o o l Bui l d er utility to build the thread pool class.
Use the o rg . apache. camel . spi . Executo rServi ceManag er instance from the current C amel C o ntext to create the thread pool class.
Ultimately, there is not much difference between the two approaches, because the
T hread P o o l Bui l d er is actually defined using the Executo rServi ceManag er instance.
Normally, the T hread P o o l Bui l d er is preferred, because it offers a simpler approach. But there is at least one kind of thread (the Sched ul ed Executo rServi ce) that can only be created by accessing the Executo rServi ceManag er instance directory.
Table 2.10, “ Thread Pool Builder Options”
shows the options supported by the
T hread P o o l Bui l d er class, which you can set when defining a new custom thread pool.
T ab le 2.10. T h read Po o l Bu ild er O p t io n s
Builder O pt ion maxQ ueueSi ze() po o l Si ze() maxP o o l Si ze() keepAl i veT i me()
Descript ion
Sets the maximum numb er o f p end ing tasks that this thread p o o l can sto re in its inco ming task q ueue. A value o f
-1
sp ecifies an unb o und ed q ueue. Default value is taken fro m d efault thread p o o l p ro file.
Sets the minimum numb er o f thread s in the p o o l (this is also the initial p o o l size).
Default value is taken fro m d efault thread p o o l p ro file.
Sets the maximum numb er o f thread s that can b e in the p o o l. Default value is taken fro m d efault thread p o o l p ro file.
If any thread s are id le fo r lo ng er than this p erio d o f time (sp ecified in seco nd s), they are terminated . This allo ws the thread p o o l to shrink when the lo ad is lig ht. Default value is taken fro m d efault thread p o o l p ro file.
103
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Builder O pt ion rejected P o l i cy()
Descript ion bui l d ()
Sp ecifies what co urse o f actio n to take, if the inco ming task q ueue is full. Yo u can sp ecify fo ur p o ssib le values:
C al l erR uns
(Default value) G ets the caller thread to run the latest inco ming task. As a sid e effect, this o p tio n p revents the caller thread fro m receiving any mo re tasks until it has finished p ro cessing the latest inco ming task.
Abo rt
Ab o rts the latest inco ming task b y thro wing an excep tio n.
D i scard
Q uietly d iscard s the latest inco ming task.
D i scard O l d est
Discard s the o ld est unhand led task and then attemp ts to enq ueue the latest inco ming task in the task q ueue.
Finishes b uild ing the custo m thread p o o l and reg isters the new thread p o o l und er the
ID sp ecified as the arg ument to
bui l d ()
.
In Java DSL, you can define a custom thread pool using the T hread P o o l Bui l d er, 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("custo mPool");
...
from("direct:start")
104
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
.multicast().executorService(customPool)
.to("mock:first")
.to("mock:second")
.to("mock:third");
Instead of passing the object reference, custo mP o o l , directly to the executo rServi ce() option, you can look up the thread pool in the registry, by passing its bean ID to the
executo rServi ceR ef() 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 T hread P o o l Bui l d er using the thread P o o l element. You can then reference the custom thread pool using the executo rServi ceR ef 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>
Creat ing a cust om t hread 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,
custo mP ro fi l e, and reference it from within a route, as follows:
// Java import org.apache.camel.spi.ThreadPoolProfile; import org.apache.camel.impl.ThreadPoolProfileSupport;
...
105
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Create the custom thread pool profile
ThreadPoolProfile customProfile = new
ThreadPoolProfileSupport("customProfile"); customProfile.setPoolSize(5); customProfile.setMaxPoolSize(5); customProfile.setMaxQueueSize(100); context.getExecutorServiceManager().registerThreadPoolProfile(customPr ofile);
...
// 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 thread P o o l P ro fi l e element to create a custom pool profile (where you let the d efaul tP ro fi l e option default to fal se, because this is not a default thread pool profile). You can create a custom thread pool profile with the bean ID,
custo mP ro fi l e, 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 t hread pool bet ween component s
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
sched ul ed Executo rServi ce property, which you can use to specify the component's
Executo rServi ce object.
Cust omiz ing t hread names
106
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 setT hread NameP attern method on the
Executo rServi ceStrateg y class or the Executo rServi ceManag er class. Alternatively, an easier way to set the thread name pattern is to set the thread NameP attern property on the C amel C o ntext object.
The following placeholders can be used in a thread name pattern:
#camel Id #
The name of the current C amel C o ntext.
#co unter#
#name#
A unique thread identifier, implemented as an incrementing counter.
The regular Camel thread name.
#l o ng Name#
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 thread NameP attern attribute on a Camel context using XML DSL:
<camelContext xmlns="http://camel.apache.org/schema/spring"
thread NameP attern= "R i d i ng the thread #co unter#" >
<route>
<from uri="seda:start"/>
<to uri="log:result"/>
<to uri="mock:result"/>
</route>
</camelContext>
2.9. CONT ROLLING ST ART -UP AND SHUT DOWN OF ROUT ES
Overview
By default, routes are automatically started when your Apache Camel application (as
107
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
represented by the C amel C o ntext 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.
Set t ing t he rout e 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, myC usto merR o uteId , to a route by invoking the ro uteId () command as follows: from("SourceURI").routeId("myCustomRouteId").process(...).to(TargetURI
);
In the XML DSL, set the ro ute element's i d attribute, as follows:
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
<route i d ="myCustomRouteId" >
<from uri="SourceURI"/>
<process ref="someProcessorId"/>
<to uri="TargetURI"/>
</route>
</camelContext>
Disabling aut omat ic st art -up of rout es
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 auto Startup command, either with a bo o l ean argument (true or fal se) or a Stri ng argument (true or fal se). For example, you can disable automatic start-up of a route in the Java DSL, as follows:
108
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
from("SourceURI")
.routeId("nonAuto")
.autoStartup(false)
.to(TargetURI);
You can disable automatic start-up of a route in the XML DSL by setting the auto Startup attribute to fal se on the ro ute 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 st art ing and st opping rout es
You can manually start or stop a route at any time in Java by invoking the startR o ute() and sto pR o ute() methods on the C amel C o ntext instance. For example, to start the route having the route ID, no nAuto , invoke the startR o ute() method on the C amel C o ntext instance, co ntext, as follows:
// Java context.startRoute("nonAuto");
To stop the route having the route ID, no nAuto , invoke the sto pR o ute() method on the
C amel C o ntext instance, co ntext, as follows:
// Java context.stopRoute("nonAuto");
St art up order of rout es
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 startupO rd er() 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
sed a: 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:
Examp le 2.5. St art u p O rd er in Java DSL
from("jetty:http://fooserver:8080")
.routeId("first")
109
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
.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 ro ute element's
startupO rd er attribute, as follows:
Examp le 2.6 . St art u p O rd er 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>
110
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).
Shut down sequence
When a C amel C o ntext 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.
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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.
Shut down order of rout es
Routes are shut down in the reverse of the start-up order. That is, when a start-up order is defined using the startupO rd er() command (in Java DSL) or startupO rd er 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, fi rst, and the second route segment to be shut down is the route with the ID, seco nd . 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.
No t e
Apache Camel also provides the option shutd o wnR o ute(D efer), 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.
Shut t ing down running t asks in a rout e
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
shutd o wnR unni ng T ask option, which can take either of the following values:
Shutd o wnR unni ng T ask. C o mpl eteC urrentT askO nl y
(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.
Shutd o wnR unni ng T ask. C o mpl eteAl l T asks
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
C o mpl eteAl l T asks option, as shown in the following Java DSL fragment:
111
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Java public void configure() throws Exception {
from("file:target/pending")
.routeId("first").startupOrder(2)
.shutd o wnR unni ng T ask(Shutd o wnR unni ng T ask. C o mpl eteAl l T asks)
.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"
shutd o wnR unni ng T ask= "C o mpl eteAl l T asks">
<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>
Shut down t imeout
The shutdown timeout has a default value of 300 seconds. You can change the value of the timeout by invoking the setT i meo ut() 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);
Int egrat ion wit h cust om component s
If you are implementing a custom Apache Camel component (which also inherits from the
o rg . apache. camel . Servi ce interface), you can ensure that your custom code receives a shutdown notification by implementing the
o rg . apache. camel . spi . Shutd o wnP repared interface. This gives the component an opportunity execute custom code in preparation for shutdown.
112
2.10. SCHEDULED ROUT E POLICY
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
2.10.1. Overview of Scheduled Rout e Policies
Ove rvie w
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.
Sche duling t asks
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 fro m()). 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.
Quart z co m po ne nt
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 Rout e Policy
Ove rvie w
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
De pe nde ncy
113
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The simple scheduled route policy depends on the Quartz component, camel -q uartz. For example, if you are using Maven as your build system, you would need to add a dependency on the camel -q uartz artifact.
Java DSL e xam ple
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, startT i me, 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 ro uteStartR epeatC o unt to 1 and ro uteStartR epeatInterval to 3000 milliseconds.
In Java DSL, you attach the route policy to the route by calling the ro uteP o l i cy() DSL command in the route.
Examp le 2.7. Java DSL Examp le o f Simp le Sch ed u led Ro u t e
// 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")
.ro uteP o l i cy(po l i cy)
.to("mock:success");
114
No t e
You can specify multiple policies on the route by calling ro uteP o l i cy() with multiple arguments.
XML DSL e xam ple
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 ro uteP o l i cyR ef attribute on the ro ute element.
Examp le 2.8. XML DSL Examp le o f Simp le Sch ed u led Ro u t e
<bean id="date" class="java.util.Data"/>
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
<bean id="startPolicy" class="org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePoli
cy">
<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" ro uteP o l i cyR ef= "startP o l i cy">
<from uri="direct:start"/>
<to uri="mock:success"/>
</route>
</camelContext>
No t e
You can specify multiple policies on the route by setting the value of
ro uteP o l i cyR ef as a comma-separated list of bean IDs.
De fining dat e s and t im e s
The initial times of the triggers used in the simple scheduled route policy are specified using the java. uti l . D ate type.The most flexible way to define a D ate instance is through the java.util.GregorianCalendar
class. Use the convenient constructors and methods of the
G reg o ri anC al end ar class to define a date and then obtain a D ate instance by calling
G reg o ri anC al end ar. g etT i me().
For example, to define the time and date for January 1, 2011 at noon, call a
G reg o ri anC al end ar 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 G reg o ri anC al end ar class also supports the definition of times in different time zones.
By default, it uses the local time zone on your computer.
115
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Grace ful shut do wn
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.
Lo gging Inflight Exchange s o n T im e o ut
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
o rg . apache. camel . i mpl . D efaul tShutd o wnStrateg y, 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
l o g Infl i g htExchang esO nT i meo ut to false.
context.getShutdownStrategegy().setLogInflightExchangesOnTimeout(false
);
Sche duling t asks
You can use a simple scheduled route policy to define one or more of the following scheduling tasks:
the section called “ Starting a route”
.
the section called “ Stopping a route”
.
the section called “ Suspending a route”
.
116
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
the section called “ Resuming a route”
.
St art ing a ro ut e
The following table lists the parameters for scheduling one or more route starts.
Paramet er ro uteStartD ate
T ype Default java. uti l . D ate
None
ro uteStartR epea tC o unt i nt ro uteStartR epea tInterval l o ng
Paramet er ro uteSto pD ate
T ype Default java. uti l . D ate
None
ro uteSto pR epeat
C o unt i nt ro uteSto pR epeat
Interval l o ng
0
0
0
0
Descript ion
Sp ecifies the d ate and time when the ro ute is started fo r the first time.
When set to a no nzero value, sp ecifies ho w many times the ro ute sho uld b e started .
Sp ecifies the time interval b etween starts, in units o f milliseco nd s.
St o pping a ro ut e
The following table lists the parameters for scheduling one or more route stops.
Descript ion
Sp ecifies the d ate and time when the ro ute is sto p p ed fo r the first time.
When set to a no nzero value, sp ecifies ho w many times the ro ute sho uld b e sto p p ed .
Sp ecifies the time interval b etween sto p s, in units o f milliseco nd s.
117
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Paramet er ro uteSto pG race
P eri o d ro uteSto pT i meU ni t
T ype i nt l o ng
Default
10 0 0 0
T i meUni t. MILLI
SEC O ND S
Descript ion
Sp ecifies ho w lo ng to wait fo r the current exchang e to finish p ro cessing (g race p erio d ) b efo re fo rcib ly sto p p ing the ro ute. Set to 0 fo r an infinite g race p erio d .
Sp ecifies the time unit o f the g race p erio d .
Suspe nding a ro ut e
The following table lists the parameters for scheduling the suspension of a route one or more times.
Paramet er ro uteSuspend D a te
T ype Default java. uti l . D ate
None
ro uteSuspend R e peatC o unt i nt ro uteSuspend R e peatInterval l o ng
0
0
Descript ion
Sp ecifies the d ate and time when the ro ute is susp end ed fo r the first time.
When set to a no nzero value, sp ecifies ho w many times the ro ute sho uld b e susp end ed .
Sp ecifies the time interval b etween susp end s, in units o f milliseco nd s.
Re sum ing a ro ut e
The following table lists the parameters for scheduling the resumption of a route one or more times.
Paramet er T ype Default Descript ion
118
Paramet er ro uteR esumeD at e ro uteR esumeR ep eatC o unt ro uteR esumeR ep eatInterval
T ype java. uti l . D ate i nt l o ng
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
Default
None
0
0
Descript ion
Sp ecifies the d ate and time when the ro ute is resumed fo r the first time.
When set to a no nzero value, sp ecifies ho w many times the ro ute sho uld b e resumed .
Sp ecifies the time interval b etween resumes, in units o f milliseco nd s.
2.10.3. Cron Scheduled Rout e Policy
Ove rvie w
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
De pe nde ncy
The simple scheduled route policy depends on the Quartz component, camel -q uartz. For example, if you are using Maven as your build system, you would need to add a dependency on the camel -q uartz artifact.
Java DSL e xam ple
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 ro uteP o l i cy() DSL command in the route.
Examp le 2.9 . Java DSL Examp le o f a Cro n Sch ed u led Ro u t e
// Java
CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy();
119
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
policy.setRouteStartTime("*/3 * * * * ?"); from("direct:start")
.routeId("test")
.routePolicy(policy)
.to("mock:success");;
No t e
You can specify multiple policies on the route by calling ro uteP o l i cy() with multiple arguments.
XML DSL e xam ple
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 ro uteP o l i cyR ef attribute on the ro ute element.
Examp le 2.10. XML DSL Examp le o f a Cro n Sch ed u led Ro u t e
<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>
No t e
You can specify multiple policies on the route by setting the value of
ro uteP o l i cyR ef as a comma-separated list of bean IDs.
De fining cro n e xpre ssio ns
120
The cron expression syntax has its origins in the UNIX cro n utility, which schedules jobs to
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
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 Y ear 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 D ayO fMo nth field or in the D ayO fWeek 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, MO N-FR I. 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 .
Sche duling t asks
You can use a cron scheduled route policy to define one or more of the following scheduling tasks:
the section called “ Starting a route”
.
the section called “ Stopping a route”
.
the section called “ Suspending a route”
.
the section called “ Resuming a route”
.
St art ing a ro ut e
121
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The following table lists the parameters for scheduling one or more route starts.
Paramet er ro uteStartStri n g
T ype
Stri ng
Default
None
Descript ion
Sp ecifies a cro n exp ressio n that trig g ers o ne o r mo re ro ute start events.
St o pping a ro ut e
The following table lists the parameters for scheduling one or more route stops.
Paramet er ro uteSto pT i me
T ype
Stri ng ro uteSto pG race
P eri o d i nt ro uteSto pT i meU ni t l o ng
Default
None
10 0 0 0
T i meUni t. MILLI
SEC O ND S
Descript ion
Sp ecifies a cro n exp ressio n that trig g ers o ne o r mo re ro ute sto p events.
Sp ecifies ho w lo ng to wait fo r the current exchang e to finish p ro cessing (g race p erio d ) b efo re fo rcib ly sto p p ing the ro ute. Set to 0 fo r an infinite g race p erio d .
Sp ecifies the time unit o f the g race p erio d .
Suspe nding a ro ut e
The following table lists the parameters for scheduling the suspension of a route one or more times.
Paramet er ro uteSuspend T i me
T ype
Stri ng
Default
None
Descript ion
Sp ecifies a cro n exp ressio n that trig g ers o ne o r mo re ro ute susp end events.
122
Re sum ing a ro ut e
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
The following table lists the parameters for scheduling the resumption of a route one or more times.
Paramet er ro uteR esumeT i m e
T ype
Stri ng
Default
None
Descript ion
Sp ecifies a cro n exp ressio n that trig g ers o ne o r mo re ro ute resume events.
2.10.4 . Rout e Policy Fact ory
Using Ro ut e Po licy Fact o ry
Availab le as o f Camel 2.14
If you want to use a route policy for every route, you can use a
o rg . apache. camel . spi . R o uteP o l i cyFacto ry as a factory for creating a
R o uteP o l i cy 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
ad d R o uteP o l i cyFacto ry again, or declare the other factories as <bean> in XML.
123
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
2.11. MET RICS
Overview
Availab le as o f 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.
Met rics Rout e Policy
Obtaining Codahale metrics for a single route can be accomplished by defining a
Metri csR o uteP o l i cy on a per route basis.
From Java create an instance of Metri csR o uteP o l i cy 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.MetricsRoutePoli
cy"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route routePolicyRef="policy">
<from uri="file:src/data?noop=true"/>
[...]
124
Met rics Rout e Policy Fact ory
This factory allows one to add a R o uteP o l i cy 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.
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
From Java you just add the factory to the C amel C o ntext 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.MetricsRoutePoli
cyFactory"/>
From Java code you can get hold of the co m. co d ahal e. metri cs. Metri cR eg i stry from the
o rg . apache. camel . co mpo nent. metri cs. ro utepo l i cy. Metri csR eg i stryServi c
e as shown below:
MetricRegistryService registryService = context.hasService(MetricsRegistryService.class); if (registryService != null) {
MetricsRegistry registry = registryService.getMetricsRegistry();
...
}
Opt ions
The Metri csR o uteP o l i cyFacto ry and Metri csR o uteP o l i cy supports the following options:
Name d urati o nUni t jmxD o mai n metri csR eg i stry prettyP ri nt
Default Descript ion
T i meUni t. MILLISEC O ND
S
The unit to use fo r d uratio n in the metrics rep o rter o r when d ump ing the statistics as jso n.
o rg . apache. camel . met ri cs
The JXM d o main name.
Allo w to use a shared
co m. co d ahal e. metri cs
. Metri cR eg i stry
. If no ne is p ro vid ed then Camel will create a shared instance used b y the this
CamelCo ntext.
fal se
Whether to use p retty p rint when o utp utting statistics in jso n fo rmat.
125
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide rateUni t T i meUni t. SEC O ND S
The unit to use fo r rate in the metrics rep o rter o r when d ump ing the statistics as jso n.
useJmx fal se
Whether to rep o rt fine g rained statistics to JMX b y using the
co m. co d ahal e. metri cs
. JmxR epo rter
.
No tice that if JMX is enab led o n CamelCo ntext then a
Metri csR eg i stryServi ce
mb ean is enlisted und er the services typ e in the JMX tree. That mb ean has a sing le o p eratio n to o utp ut the statistics using jso n.
Setting useJmx to true is o nly need ed if yo u want fine g rained mb eans p er statistics typ e.
2.12. JMX NAMING
Overview
Apache Camel allows you to customise the name of a C amel C o ntext bean as it appears in
JMX, by defining a management name pattern for it. For example, you can customise the name pattern of an XML C amel C o ntext instance, as follows:
<camelContext id="myCamel" manag ementNameP attern= "#name#">
...
</camelContext>
If you do not explicitly set a name pattern for the C amel C o ntext bean, Apache Camel reverts to a default naming strategy.
Default naming st rat egy
By default, the JMX name of a C amel C o ntext bean deployed in an OSGi bundle is equal to the OSGi symbolic name of the bundle. For example, if the OSGi symbolic name is
MyC amel Bund l e, the JMX name would be MyC amel Bund l e. In cases where there is more than one C amel C o ntext 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
MyC amel Bund l e bundle, the corresponding JMX MBeans are named as follows:
126
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
MyCamelBundle-1
MyCamelBundle-2
MyCamelBundle-3
...
Cust omising t he JMX naming st rat egy
One drawback of the default naming strategy is that you cannot guarantee that a given
C amel C o ntext 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 C amel C o ntext instances.
Specifying a name pat t ern in Java
To specify a name pattern on a C amel C o ntext in Java, call the setNameP attern method, as follows:
// Java
context.getManagementNameStrategy().setNamePattern( "#name#" );
Specifying a name pat t ern in XML
To specify a name pattern on a C amel C o ntext in XML, set the manag ementNameP attern attribute on the camel C o ntext element, as follows:
<camelContext id= "myCamel" managementNamePattern= "#name#" >
Name pat t ern t okens
You can construct a JMX name pattern by mixing literal text with any of the following tokens:
T ab le 2.11. JMX Name Pat t ern T o ken s
T oken
#camel Id #
#name#
#co unter#
#bund l eId #
#symbo l i cName#
Descript ion
Value o f the
i d
attrib ute o n the
C amel C o ntext
b ean.
Same as
#camel Id #
.
An incrementing co unter (starting at
1
).
The O SG i b und le ID o f the d ep lo yed b und le (OSGi only).
The O SG i symb o lic name (OSGi only).
127
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
T oken
#versi o n#
Descript ion
The O SG i b und le versio n (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="SameO l d SameO l d "> ...
</camelContext>
...
<camelContext id="bar" managementNamePattern="SameO l d SameO l d "> ...
</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.13. PERFORMANCE AND OPT IMIZAT ION
Avoid unnecessary message copying
You can avoid making an unnecessary copy of the original message, by setting the
al l o wUseO ri g i nal Messag e option to fal se on the C amel C o ntext object. For example, in Blueprint XML you can set this option as follows:
<camelContext xmlns="http://camel.apache.org/schema/blueprint"
al l o wUseO ri g i nal Messag e= "fal se">
...
</camelContext>
128
CHAPT ER 2 . BASIC PRINCIPLES O F RO UT E BUILDING
You can safely set al l o wUseO ri g i nal Messag e to fal se, if the following conditions are satisfied:
You do not set useO ri g i nal Messag e= true on any of the error handlers or on the
o nExcepti o n element.
You do not use the g etO ri g i nal Messag e method anywhere in your Java application code.
129
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 3. INTRODUCING ENTERPRISE INTEGRATION
PATTERNS
Ab st ract
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 T HE PAT T ERNS
Ent erprise Int egrat ion Pat t erns book
Apache Camel supports most of the patterns from the book, Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf.
Messaging syst ems
The messaging systems patterns, shown in Table 3.1, “ Messaging Systems”
, introduce the fundamental concepts and components that make up a messaging system.
T ab le 3.1. Messag in g Syst ems
Icon Name
Use Case
Ho w can two ap p licatio ns co nnected b y a messag e channel exchang e a p iece o f info rmatio n?
Ho w d o es o ne ap p licatio n co mmunicate with ano ther ap p licatio n using messag ing ?
Ho w d o es an ap p licatio n co nnect to a messag ing channel to send and receive messag es?
Ho w can we p erfo rm co mp lex p ro cessing o n a messag e while still maintaining ind ep end ence and flexib ility?
130
Icon
CHAPT ER 3. INT RO DUCING ENT ERPRISE INT EG RAT IO N PAT T ERNS
Name
Use Case
Ho w can yo u d eco up le ind ivid ual p ro cessing step s so that messag es can b e p assed to d ifferent filters d ep end ing o n a set o f d efined co nd itio ns?
Ho w d o systems using d ifferent d ata fo rmats co mmunicate with each o ther using messag ing ?
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.
T ab le 3.2. Messag in g Ch an n els
Icon Name
Use Case
Ho w can the caller b e sure that exactly o ne receiver will receive the d o cument o r will p erfo rm the call?
Ho w can the send er b ro ad cast an event to all interested receivers?
What will the messag ing system d o with a messag e it canno t d eliver?
Ho w d o es the send er make sure that a messag e will b e d elivered , even if the messag ing system fails?
131
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Icon Name
Use Case
What is an architecture that enab les sep arate, d eco up led ap p licatio ns to wo rk to g ether, such that o ne o r mo re o f the ap p licatio ns can b e ad d ed o r remo ved witho ut affecting the o thers?
Message const ruct ion
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.
T ab le 3.3. Messag e Co n st ru ct io n
Icon Name
Use Case
Ho w d o es a req uesto r id entify the req uest that g enerated the received rep ly?
Ho w d o es a rep lier kno w where to send the rep ly?
132
Message rout ing
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).
T ab le 3.4 . Messag e Ro u t in g
Icon Name
Use Case
Ho w d o we hand le a situatio n where the imp lementatio n o f a sing le lo g ical functio n (e.g ., invento ry check) is sp read acro ss multip le p hysical systems?
Icon
CHAPT ER 3. INT RO DUCING ENT ERPRISE INT EG RAT IO N PAT T ERNS
Name
Use Case
Ho w d o es a co mp o nent avo id receiving uninteresting messag es?
Ho w d o we ro ute a messag e to a list o f d ynamically sp ecified recip ients?
Ho w can we p ro cess a messag e if it co ntains multip le elements, each o f which mig ht have to b e p ro cessed in a d ifferent way?
Ho w d o we co mb ine the results o f ind ivid ual, b ut related messag es so that they can b e p ro cessed as a who le?
Ho w can we g et a stream o f related , b ut o ut-o f-seq uence, messag es b ack into the co rrect o rd er?
Ho w can yo u maintain the o verall messag e flo w when p ro cessing a messag e co nsisting o f multip le elements, each o f which may req uire d ifferent p ro cessing ?
Ho w d o yo u maintain the o verall messag e flo w when a messag e need s to b e sent to multip le recip ients, each o f which may send a rep ly?
Ho w d o we ro ute a messag e co nsecutively thro ug h a series o f p ro cessing step s when the seq uence o f step s is no t kno wn at d esig n-time, and mig ht vary fo r each messag e?
133
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Icon Name
Use Case
Ho w can I thro ttle messag es to ensure that a sp ecific end p o int d o es no t g et o verlo ad ed , o r that we d o n't exceed an ag reed SLA with so me external service?
Ho w can I d elay the send ing o f a messag e?
Ho w can I b alance lo ad acro ss a numb er o f end p o ints?
Ho w can I ro ute a messag e to a numb er o f end p o ints at the same time?
Ho w can I rep eat p ro cessing a messag e in a lo o p ?
Ho w can I samp le o ne messag e o ut o f many in a g iven p erio d to avo id d o wnstream ro ute d o es no t g et o verlo ad ed ?
Message t ransformat ion
The message transformation patterns, shown in
Table 3.5, “ Message Transformation”
, describe how to modify the contents of messages for various purposes.
T ab le 3.5. Messag e T ran sf o rmat io n
Icon Name
Use Case
Ho w d o we co mmunicate with ano ther system if the messag e o rig inato r d o es no t have all the req uired d ata items availab le?
Ho w d o yo u simp lify d ealing with a larg e messag e, when yo u are interested in o nly a few d ata items?
134
Icon
CHAPT ER 3. INT RO DUCING ENT ERPRISE INT EG RAT IO N PAT T ERNS
Name
Use Case
Ho w can we red uce the d ata vo lume o f messag e sent acro ss the system witho ut sacrificing info rmatio n co ntent?
Ho w d o yo u p ro cess messag es that are semantically eq uivalent, b ut arrive in a d ifferent fo rmat?
Ho w can I so rt the b o d y o f a messag e?
Messaging endpoint s
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.
T ab le 3.6 . Messag in g En d p o in t s
Icon Name
Use Case
Ho w d o yo u mo ve d ata b etween d o main o b jects and the messag ing infrastructure while keep ing the two ind ep end ent o f each o ther?
Ho w can an ap p licatio n auto matically co nsume messag es as they b eco me availab le?
Ho w can an ap p licatio n co nsume a messag e when the ap p licatio n is read y?
Ho w can a messag ing client p ro cess multip le messag es co ncurrently?
135
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Icon Name
Use Case
Ho w can multip le co nsumers o n a sing le channel co o rd inate their messag e p ro cessing ?
Ho w can a messag e co nsumer select which messag es it wants to receive?
Ho w can a sub scrib er avo id missing messag es when it's no t listening fo r them?
Ho w can a messag e receiver d eal with d up licate messag es?
Ho w can a client co ntro l its transactio ns with the messag ing system?
Ho w d o yo u encap sulate access to the messag ing system fro m the rest o f the ap p licatio n?
Ho w can an ap p licatio n d esig n a service to b e invo ked b o th via vario us messag ing techno lo g ies and via no n-messag ing techniq ues?
Syst em management
The system management patterns, shown in Table 3.7, “ System Management” , describe how
to monitor, test, and administer a messaging system.
T ab le 3.7. Syst em Man ag emen t
136
Icon
CHAPT ER 3. INT RO DUCING ENT ERPRISE INT EG RAT IO N PAT T ERNS
Name
Use Case
Ho w d o yo u insp ect messag es that travel o n a p o int-to -p o int channel?
137
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 4. DEFINING REST SERVICES
Ab st ract
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 sections aims to give you 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:
G ET , P O ST , P UT , and D ELET E.
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 invocat ion
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, l o cal ho st: 9 0 9 1, 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.
138
CHAPT ER 4 . DEFINING REST SERVICES
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 -co re) 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 -co re) 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. For example, the following Java code shows how to define a simple
Hello World service using the Rest component: from("rest:get:say:/hello/{name}").transform().simple("Hello
${header.name}");
REST implement at ions
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}.
139
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
The Spark-Rest component requires Java 8.
Restlet component
The Restlet component (in camel -restl et) 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 see
Restlet
in the Apache Camel Component Reference Guide.
Servlet component
The Servlet component (in camel -servl et) 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 see
Servlet
in the Apache Camel Component Reference Guide. .
JAX-RS REST implement at ion
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):
@ Pat h
14 0
CHAPT ER 4 . DEFINING REST SERVICES
Annotation that can map a context path to a Java class or map a sub-path to a particular Java method.
@ G ET , @ PO ST , @ PUT , @ DELET E
Annotations that map a HTTP method to a Java method.
@ Pat h Param
Annotation that either maps a URI parameter to a Java method argument, or injects a
URI parameter into a field.
@ Q u eryParam
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 see
CXFRS
in the Apache Camel Component Reference Guide. and part
"Developing RESTful Web Services" in "Apache CXF Development Guide" .
No t e
The CXFRS component is not integrated with the REST DSL.
4 .2. DEFINING SERVICES WIT H 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).
Advant ages of t he 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 -swag g er component).
14 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Component s t hat int egrat e wit h 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:
Servlet component (camel -servl et).
Spark-Rest component (camel -spark-rest).
Netty HTTP component (camel -netty-http).
Netty4 HTTP component (camel -netty4 -http).
Jetty component (camel -jetty).
Restlet component (camel -restl et).
No t e
The Rest component (part of camel -co re) 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 t o use a REST implement at ion
To specify the REST implementation, you use either the restC o nfi g urati o n() builder (in
Java DSL) or the restC o nfi g urati o n 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 camel C o ntext) in the XML
DSL:
<restConfiguration component="spark-rest" port="9091"/>
Synt ax
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()]
14 2
CHAPT ER 4 . DEFINING REST SERVICES
...
.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 g et(),
head (), put(), po st(), d el ete(), 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 ro ute() keyword (for embedding a Camel route). For example: get("...").Option()+.route("...").CamelRoute.endRest()
REST DSL wit h Java
In Java, to define a service with the REST DSL, put the REST definition into the body of a
R o uteBui l d er. co nfi g ure() 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:
restC o nfi g urati o n() rest()
Configures the REST DSL to use a specific REST implementation (Spark-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 d i rect endpoint (the
d i rect component splices routes together within the same application).
14 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide fro m()
Defines a regular Camel route.
REST DSL wit h XML
In XML, to define a service with the XML DSL, define a rest element as a child of the
camel C o ntext 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 pat h
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"/>
14 4
CHAPT ER 4 . DEFINING REST SERVICES
</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/hel l o " ).to( "direct:hello" )
.get( "/say/bye" ).to( "direct:bye" );
URI t emplat es
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/hel l o /Jo e, the HTTP Response is Hel l o Jo e.
Embedded rout e synt ax
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 ro ute() keyword (Java DSL) or the ro ute element (XML DSL). The
ro ute() keyword enables you to embed a route into a verb clause, with the following syntax:
RESTVerbClause.route("...").CamelRoute.endRest()
14 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where the end R est() 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>
No t e
If you define any exception clauses (using o nExcepti o n()) or interceptors (using
i ntercept()) in the current C amel C o ntext, these exception clauses and interceptors are also active in the embedded routes.
14 6
Specifying t he cont ent t ype of request s and responses
You can filter the content type of HTTP requests and responses using the co nsumes() and
pro d uces() options in Java, or the co nsumes and pro d uces attributes in XML. For example, some common content types (officially known as Internet media types) are the following:
text/pl ai n text/html
CHAPT ER 4 . DEFINING REST SERVICES text/xml appl i cati o n/jso n appl i cati o n/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/pl ai n 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 co nsumes and pro d uces 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 co nsumes() or pro d uces() as a comma-separated list. For example, co nsumes("text/pl ai n, appl i cati o n/jso n").
Addit ional HT T P met hods
Some HTTP server implementations support additional HTTP methods, which are not provided by the standard set of verbs in the REST DSL, g et(), head (), put(), po st(),
d el ete(). 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 transfo rm() 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" >
...
14 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<rest path= "/say" >
<verb uri= "/hello" method= "TRACE" >
<route>
<transform/>
</route>
</get>
</camelContext>
Defining cust om HT T P 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 Exchang e. HT T P _R ESP O NSE_C O D E header key to the error code value (for example, 4 0 0 , 4 0 4 , 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 C o ntent-T ype header, if required.
4. If your REST service is configured to marshal to and from Java objects (that is,
bi nd i ng Mo d e is enabled), you should ensure that the
ski pBi nd i ng O nErro rC o d e 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
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(portNu m).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" );
14 8
In this example, if the input ID is a number less than 100, we return a custom error message, using the UserErro rServi ce bean, which is implemented as follows:
CHAPT ER 4 . DEFINING REST SERVICES
// 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 UserErro rServi ce bean we define the custom error message and set the HTTP error code to 4 0 0 .
Wrapping a JsonParserExcept ion in a cust om HT T P error message
A common case where you might want to return a custom error message is in order to wrap a
Jso nP arserExcepti o n 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 opt ions
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").co nsumes("text/pl ai n"). pro d uces("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}").co nsumes("text/pl ai n"). pro d uces("text/html ").
to("direct:foo")
.get("/for/{username}").co nsumes("text/pl ai n"). pro d uces("text/html ").t
o("direct:bar");
In which case the specified options apply only to the relevant verb clause, overriding any settings from the base part.
14 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Table 4.1, “ REST DSL Options”
summarizes the options supported by the REST DSL.
T ab le 4 .1. REST DSL O p t io n s
Java DSL bi nd i ng Mo d e() co nsumes() custo mId () d escri pti o n() enabl eC O R S() i d () metho d ()
XML DSL
@ bi nd i ng Mo d e
@ co nsumes
@ custo mId d escri pti o n
@ enabl eC O R S
@ i d
@ metho d
Descript ion
Sp ecifies the b ind ing mo d e, which can b e used to marshal inco ming messag es to Java o b jects (and , o p tio nally, unmarshal Java o b jects to o utg o ing messag es). Can have the fo llo wing values:
o ff
(d efault),
auto
,
jso n
,
xml
,
jso n_xml
.
Restricts the verb clause to accep t o nly the sp ecified
Internet med ia typ e (MIME typ e) in a HTTP Req uest.
Typ ical values are:
text/pl ai n
,
text/http
,
text/xml
,
appl i cati o n/jso n
,
appl i cati o n/xml
.
Defines a custo m ID fo r JMX manag ement.
Do cument the REST service o r verb clause. Useful fo r
JMX manag ement and to o ling .
If
true
, enab les CO RS
(cro ss-o rig in reso urce sharing ) head ers in the
HTTP resp o nse. Default is
fal se
.
Defines a uniq ue ID fo r the
REST service, which is useful to d efine fo r JMX manag ement and o ther to o ling .
Sp ecifies the HTTP metho d p ro cessed b y this verb clause. Usually used in co njunctio n with the g eneric
verb()
keywo rd .
150
Java DSL o utT ype() pro d uces() type()
VerbURIArgument
BasePathArgument
XML DSL
@ o utT ype pro d uces
@ type
@ uri
@ path
CHAPT ER 4 . DEFINING REST SERVICES
Descript ion
When o b ject b ind ing is enab led (that is, when
bi nd i ng Mo d e
o p tio n is enab led ), this o p tio n sp ecifies the Java typ e that rep resents a HT T P
Response messag e.
Restricts the verb clause to p ro d uce o nly the sp ecified
Internet med ia typ e (MIME typ e) in a HTTP Resp o nse.
Typ ical values are:
text/pl ai n
,
text/http
,
text/xml
,
appl i cati o n/jso n
,
appl i cati o n/xml
.
When o b ject b ind ing is enab led (that is, when
bi nd i ng Mo d e
o p tio n is enab led ), this o p tio n sp ecifies the Java typ e that rep resents a HT T P Request messag e.
Sp ecifies a p ath seg ment o r
URI temp late as an arg ument to a verb . Fo r examp le,
g et(VerbURIArgument)
.
Sp ecifies the b ase p ath in the
rest()
keywo rd (Java
DSL) o r in the
rest
element
(XML DSL).
4 .3. MARSHALLING T O AND FROM JAVA OBJECT S
Marshalling Java object s for t ransmission over HT T P
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:
JSO N
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
151
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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, i d 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, i d and name:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User>
<Id>1234</Id>
<Name>Jane Doe</Name>
</User>
Int egrat ion of JSON and JAXB wit h t he 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.
Support ed dat a format component s
152
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
CHAPT ER 4 . DEFINING REST SERVICES
Jackson data format (camel -jackso n) (default)
GSon data format (camel -g so n)
XStream data format (camel -xstream)
JAXB
JAXB data format (camel -jaxb)
How t o enable object marshalling
To enable object marshalling in the REST DSL, observe the following points:
1. Enable binding mode, by setting the bi nd i ng Mo d e 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 o utT ype 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
jso nD ataFo rmat option and/or the xml D ataFo rmat option (which can be specified on the restC o nfi g urati o n 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 d ataFo rmatP ro perty with the key,
xml . o ut. mustBeJAXBEl ement, to fal se (which can be specified on the
restC o nfi g urati o n 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>
153
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
...
<! -- use fo r jso n bi nd i ng -->
<d epend ency>
<g ro upId >o rg . apache. camel </g ro upId >
<arti factId >camel -jackso n</arti factId >
</d epend ency>
...
</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 -jackso n 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,
MyR estP ro fi l e, you could add the feature by entering the following console command:
JBossFuse:karaf@ root> fabric:profile-edit --features cameljackson MyRestProfile
Configuring t he binding mode
The bi nd i ng Mo d e option is o ff by default, so you must configure it explicitly, in order to enable marshalling of Java objects. TABLE shows the list of supported binding modes.
T ab le 4 .2. REST DSL BIn d in g Mo d es
Binding Mode o ff
Descript ion
Bind ing is turned o ff (default).
auto
Bind ing is enab led fo r JSO N and /o r XML. In this mo d e, Camel auto -selects either JSO N o r XML (JAXB), b ased o n the fo rmat o f the inco ming messag e. Yo u are not req uired to enab le b o th kind s o f d ata fo rmat, ho wever: either a JSO N imp lementatio n, an XML imp lementatio n, o r b o th can b e p ro vid ed o n the classp ath.
154
Binding Mode jso n
CHAPT ER 4 . DEFINING REST SERVICES
Descript ion
Bind ing is enab led fo r JSO N o nly. A JSO N imp lementatio n must b e p ro vid ed o n the classp ath (b y d efault, Camel tries to enab le the
camel -jackso n
imp lementatio n).
xml
Bind ing is enab led fo r XML o nly. An XML imp lementatio n must b e p ro vid ed o n the classp ath (b y d efault, Camel tries to enab le the
camel -jaxb
imp lementatio n).
jso n_xml
Bind ing is enab led fo r b o th JSO N and XML.
In this mo d e, Camel auto -selects either JSO N o r XML (JAXB), b ased o n the fo rmat o f the inco ming messag e. Yo u are req uired to p ro vid e both kind s o f d ata fo rmat o n the classp ath.
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 bi nd i ng Mo d e, as follows:
REST DSL co n f ig u rat io n
You can set the bi nd i ng Mo d e option from the restC o nfi g urati o n builder, as follows: restConfiguration().component("servlet").port(8181).bi nd i ng Mo d e
(R estBi nd i ng Mo d e. jso n);
Service d ef in it io n b ase p art
You can set the bi nd i ng Mo d e option immediately following the rest() keyword
(before the verb clauses), as follows: rest("/user").bi nd i ng Mo d e(R estBi nd i ng Mo d e. jso n).get("/{id}").V
erbClause
Verb clau se
You can set the bi nd i ng Mo d e option in a verb clause, as follows:
155
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can set the bi nd i ng Mo d e option in a verb clause, as follows: rest("/user")
.get("/{id}").bi nd i ng Mo d e(R estBi nd i ng Mo d e. jso n).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 -
exampl e-servl et-rest-bl uepri nt example. You can find this example by installing the standalone Apache Camel distribution, apache-camel -2. 15. 1. red hat-6 210 84 . zi p, which is provided in the extras/ subdirectory of your JBoss 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 t he Servlet component as t he REST implement at ion
In the camel -exampl e-servl et-rest-bl uepri nt 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” .
Examp le 4 .1. Co n f ig u re Servlet Co mp o n en t f o r 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-restblueprint/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" >
156
CHAPT ER 4 . DEFINING REST SERVICES
<restConfiguration component= "servlet"
bindingMode= "json"
contextPath= "/camel-example-servlet-restblueprint/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 restC o nfi g urati o n element, which integrates with the Servlet component by setting the co mpo nent attribute to the value, servl et.
Servlet co mp o n en t layer
The Servlet component layer is implemented as an instance of the class,
C amel HttpT ranspo rtServl et, where the example instance has the bean ID,
camel Servl et.
HT T P co n t ain er 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 o rg . o sg i . servi ce. http. HttpServi ce
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, O sg i Servl etR eg i sterer, to register the Servlet component in the HTTP container. The
O sg i Servl etR eg i sterer 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 reg i sterServl et method on the HttpServi ce
OSGi service; and when the instance is destroyed, it automatically calls the
unreg i ster method.
Required dependencies
157
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
This example has two dependencies which are of key importance to the REST DSL, as follows:
Servlet co mp o n en t
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
Jackso n d at a f o rmat
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 t ype for responses
The example application passes User type objects back and forth in HTTP Request and
Examp le 4 .2. User Class f o r JSO N Resp o n se
// Java
package org.apache.camel.example.rest; public class User {
158
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;
}
}
CHAPT ER 4 . DEFINING REST SERVICES
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 rout e wit h 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” .
Examp le 4 .3. REST DSL Ro u t e wit h JSO N Bin d in g
<?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"/>
159
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<restConfiguration component="servlet"
bindingMode="json"
contextPath="/camel-example-servlet-restblueprint/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 operat ions
The REST service from Example 4.3, “ REST DSL Route with JSON Binding”
defines the following REST operations:
G ET /camel -exampl e-servl et-rest-bl uepri nt/rest/user/{i d }
Get the details for the user identified by {i d }, where the HTTP response is returned in JSON format.
160
CHAPT ER 4 . DEFINING REST SERVICES
P UT /camel -exampl e-servl et-rest-bl uepri nt/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).
G ET /camel -exampl e-servl et-rest-bl uepri nt/rest/user/fi nd Al l
Get the details for all users, where the HTTP response is returned as an array of users, in JSON format.
URLs t o invoke t he 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: //l o cal ho st: 8181
In restC o nfi g urati o n, the protocol defaults to http and the port is set explicitly to 8181.
/camel -exampl e-servl et-rest-bl uepri nt/rest
/user
Specified by the co ntextP ath attribute of the restC o nfi g urati o n element.
/{i d }
Specified by the path attribute of the rest element.
Specified by the uri attribute of the g et 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/camelexample-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/camelexample-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-restblueprint/rest/user
161
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
4 .4 . CONFIGURING T HE REST DSL
Configuring wit h Java
In Java, you can configure the REST DSL using the restC o nfi g urati o n() builder API.
For example, to configure the REST DSL to use the Servlet component as the underlying implementation: restConfiguration().component( "servlet" ).bindingMode( "json" ).port( "818
1" )
.contextPath( "/camel-example-servlet-rest-blueprint/rest" );
Configuring wit h XML
In XML, you can configure the REST DSL using the restC o nfi g urati o n 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-restblueprint/rest"
port= "8181" >
<dataFormatProperty key= "prettyPrint" value= "true" />
</restConfiguration>
...
</camelContext>
</blueprint>
Configurat ion opt ions
Table 4.3, “ Options for Configuring REST DSL”
shows options for configuring the REST
DSL using the restC o nfi g urati o n() builder (Java DSL) or the restC o nfi g urati o n element (XML DSL).
T ab le 4 .3. O p t io n s f o r Co n f ig u rin g REST DSL
Java DSL XML DSL Descript ion
162
Java DSL co mpo nent() scheme() ho st() po rt() co ntextP ath()
XML DSL
@ co mpo nent
CHAPT ER 4 . DEFINING REST SERVICES
Descript ion
Sp ecifies the Camel co mp o nent to use as the
REST transp o rt (fo r examp le,
servl et
,
restl et
,
spark-rest
, and so o n). The value can either b e the stand ard co mp o nent name o r the b ean ID o f a custo m instance. If this o p tio n is no t sp ecified ,
Camel lo o ks fo r an instance o f
R estC o nsumerFacto ry
o n the classp ath o r in the b ean reg istry.
@ scheme
@ ho st
@ po rt
@ co ntextP ath
The p ro to co l to use fo r exp o sing the REST service.
Dep end s o n the und erlying
REST imp lementatio n, b ut
http
and
https
are usually sup p o rted . Default is
http
.
The ho stname to use fo r exp o sing the REST service.
The p o rt numb er to use fo r exp o sing the REST service.
Not e: This setting is ignored b y the Servlet co mp o nent, which uses the co ntainer's stand ard HTTP p o rt instead .
In the case o f the Ap ache
Karaf O SG i co ntainer, the stand ard HTTP p o rt is no rmally 8 18 1. It is g o o d p ractice to set the p o rt value no netheless, fo r the sake o f
JMX and to o ling .
Sets a lead ing co ntext p ath fo r the REST services. This can b e used with co mp o nents such as Servlet, where the d ep lo yed Web ap p licatio n is d ep lo yed using a
co ntext-path
setting .
163
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java DSL ho stNameR eso l ver() bi nd i ng Mo d e() ski pBi nd i ng O nErro rC o d e() enabl eC O R S() jso nD ataFo rmat()
XML DSL
@ ho stNameR eso l ver
Descript ion
If a ho stname is no t set exp licitly, this reso lver d etermines the ho st fo r the
REST service. Po ssib le values are
R estHo stNameR eso l ver
. l o cal Ho stName
(Java
DSL) o r
l o cal Ho stName
(XML DSL), which reso lves to the ho st name fo rmat; and
R estHo stNameR eso l ver
. l o cal Ip
(Java DSL) o r
l o cal Ip
(XML DSL), which reso lves to the d o tted d ecimal IP ad d ress fo rmat.
Default is
l o cal Ho stName
.
@ bi nd i ng Mo d e
Enab les b ind ing mo d e fo r
JSO N o r XML fo rmat messag es. Po ssib le values are:
o ff
,
auto
,
jso n
,
xml
, o r
jso n_xml
. Default is
o ff
.
@ ski pBi nd i ng O nErro rC o d e
@ enabl eC O R S
Sp ecifies whether to skip b ind ing o n o utp ut, if there is a custo m HTTP erro r co d e head er. This allo ws yo u to b uild custo m erro r messag es that d o no t b ind to JSO N o r XML, as successful messag es wo uld o therwise d o . Default is
true
.
If
true
, enab les CO RS
(cro ss-o rig in reso urce sharing ) head ers in the
HTTP resp o nse. Default is
fal se
.
@ jso nD ataFo rmat
Sp ecifies the co mp o nent that
Camel uses to imp lement the
JSO N d ata fo rmat. Po ssib le values are:
jso n-jackso n
,
jso n-g so n
,
jso nxstream
. Default is
jso njackso n
.
164
Java DSL xml D ataFo rmat() co mpo nentP ro perty() end po i ntP ro perty() co nsumerP ro perty()
XML DSL
@ xml D ataFo rmat
CHAPT ER 4 . DEFINING REST SERVICES
Descript ion co mpo nentP ro perty end po i ntP ro perty co nsumerP ro perty
Sp ecifies the co mp o nent that
Camel uses to imp lement the
XML d ata fo rmat. Po ssib le value is:
jaxb
. Default is
jaxb
.
Enab les yo u to set arb itrary
component level p ro p erties o n the und erlying REST imp lementatio n.
Enab les yo u to set arb itrary
endpoint level p ro p erties o n the und erlying REST imp lementatio n.
Enab les yo u to set arb itrary
consumer endpoint p ro p erties o n the und erlying REST imp lementatio n.
d ataFo rmatP ro perty() d ataFo rmatP ro perty co rsHead erP ro perty() co rsHead ers
Enab les yo u to set arb itrary p ro p erties o n the und erlying d ata fo rmat co mp o nent (fo r examp le, Jackso n o r JAXB).
Fro m Camel 2.14.1 o nward s, yo u can attach the fo llo wing p refixes to the p ro p erty keys:
jso n. i n jso n. o ut xml . i n xml . o ut
To restrict the p ro p erty setting to a sp ecific fo rmat typ e (JSO N o r XML) and a p articular messag e d irectio n
(IN o r OUT).
Enab les yo u to sp ecify custo m CO RS head ers, as key/value p airs.
Default CORS headers
165
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 co rsHead erP ro perty
DSL command.
T ab le 4 .4 . Def au lt CO RS Head ers
Header Key
Access-C o ntro l -Al l o w-O ri g i n
Access-C o ntro l -Al l o w-Metho d s
Header Value
*
G ET
,
HEAD
,
P O ST
,
P UT
,
D ELET E
,
T R AC E
,
O P T IO NS
,
C O NNEC T
,
P AT C H
Access-C o ntro l -Al l o w-Head ers
Access-C o ntro l -Max-Ag e
O ri g i n
,
Accept
,
X-R eq uested -Wi th
,
C o ntent-T ype
,
Access-C o ntro l -
R eq uest-Metho d
,
Access-C o ntro l -
R eq uest-Head ers
36 0 0
Enabling or disabling Jackson JSON feat ures
You can enable or disable specific Jackson JSON features by configuring the following keys in the d ataFo rmatP ro perty option:
jso n. i n. d i sabl eFeatures jso n. i n. enabl eFeatures
For example, to disable Jackson's FAIL_O N_UNKNO WN_P R O P ER T IES 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");
166
Here is an example that shows how to disable and enable Jackson JSON features in the
Java DSL:
CHAPT ER 4 . DEFINING REST SERVICES
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 com.fasterxml.jackson.databind.SerializationFeature
com.fasterxml.jackson.databind.DeserializationFeature
com.fasterxml.jackson.databind.MapperFeature
4 .5. SWAGGER INT EGRAT ION
Overview
Integrated with the REST DSL, the camel -swag g er component enables users to create API docs for any REST-defined routes and endpoints in a CamelContext file. The camel -
swag g er component creates a servlet integrated with the CamelContext that pulls the information from each REST endpoint to generate the API docs (JSON file).
If using Maven, you need to add the camel -swag g er component to your po m. xml file:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-swagger</artifactId>
<version>x.x.x</version>
<!-- Use the same version as your Camel core version -->
</dependency>
167
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Configuring t he camelCont ext
To enable Swagger, add to the CamelContext file:
The servi ce element, which exposes the camel-swagger servlet and initializes its parameters.
In the service element, add the servlet
(o rg . apache. camel . co mpo nent. swag g er. D efaul tC amel Swag g erServl et) and the servi ce-pro perti es that configure the servlet's parameters.
For details on servlet parameters, see
Swagger
in the Apache Camel Component Reference
Guide.
Configure the REST implementation
Define the REST service within the camel C o ntext element using the
restC o nfi g urati o n and rest elements.
For details on configuring REST services in the CamelContext, see Section 4.2, “ Defining
For OSGi deployments, you need to configure the servlet options and REST configuration in the bl uepri nt. xml file; for example:
<?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"
xsi:schemaLocation= "
http://www.osgi.org/xmlns/blueprint/v1.0.0
http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint
http://camel.apache.org/schema/blueprint/camelblueprint.xsd" >
<service interface= "javax.servlet.http.HttpServlet" >
<service-properties>
<entry key= "alias" value= "/api-docs/*" />
<entry key= "init-prefix" value= "init." />
<entry key= "init.base.path" value= "//localhost:8080/" />
<entry key= "init.api.path" value= "//localhost:8181/apidocs" />
<entry key= "init.api.title" value= "Camel Rest Example
API" />
<entry key= "init.api.version" value= "1.2" />
<entry key= "init.api.description"
value= "Camel Rest Example with Swagger that provides an User REST service" />
</service-properties>
<bean class= "org.apache.camel.component.swagger.DefaultCamelSwaggerServlet"
168
CHAPT ER 4 . DEFINING REST SERVICES
/>
</service>
<camelContext id= "log-example-context"
xmlns= "http://camel.apache.org/schema/blueprint" >
<restConfiguration component= "jetty" port= "8080" />
<rest path= "/say" >
<get uri= "/hello" >
<to uri= "direct:hello" />
</get>
<get uri= "/bye" consumes= "application/json" >
<to uri= "direct:bye" />
</get>
<post uri= "/bye" >
<to uri= "mock:update" />
</post>
</rest>
<route id= "rte1-log-example" >
<from uri= "direct:hello" />
<transform>
<constant> Hello World </constant>
</transform>
</route>
<route id= "rte2-log-example" >
<from uri= "direct:bye" />
<transform>
<constant> Bye World </constant>
</transform>
</route>
</camelContext>
</blueprint>
The following parameters are used in the above example as described here:
servi ce al i as
The servi ce element exposes the camel swagger servlet (<bean
cl ass= "o rg . apache. camel . co mpo nent. swag g er. D efaul tC amel Swag g er
Servl et"/>) and initializes several servlet properties.
The al i as property binds the camel swagger servlet to /api -d o cs/*.
i ni t-prefi x
169
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The i ni t-prefi x property sets the prefix for all camel swagger servlet properties to
i ni t. . This is analogous to using i ni t-param elements in the web. xml configuration for WAR implementations (see chapter "Swagger" in "Apache Camel
Component Reference" ).
restC o nfi g urati o n
In the camel C o ntext element, the restC o nfi g urati o n element specifies the
REST implementation to use. In this case, it is Jetty web servlet on port 8080.
rest
In the camel C o ntext element, the rest element defines a REST service and provides the base path (/say) to it. In this case, the service consists of two REST endpoints, hel l o and bye, which are routed to their corresponding camel endpoints defined in the ro ute elements.
170
CHAPT ER 5. MESSAG ING SYST EMS
CHAPTER 5. MESSAGING SYSTEMS
Ab st ract
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”
.
Fig u re 5.1. Messag e Pat t ern
T ypes 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 o rg . apache. camel . Messag e interface.
Message st ruct ure
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.
171
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.
Correlat ing 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 object s
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:
head er(Stri ng name), bo d y() — Returns the named header and the body of the current In message.
o utBo d y() — 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.
172
CHAPT ER 5. MESSAG ING SYST EMS
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”
.
Fig u re 5.2. Messag e Ch an n el Pat t ern
Message-orient ed component s
The following message-oriented components in Apache Camel support the notion of a message channel:
Act iveMQ
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, Fo o . Bar, use the following endpoint URI: activemq:Foo.Bar
See see
ActiveMQ
in the Apache Camel Component Reference Guide for more details and instructions on setting up the ActiveMQ component.
173
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 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.
Fig u re 5.3. Messag e En d p o in t Pat t ern
174
CHAPT ER 5. MESSAG ING SYST EMS
T ypes 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, Fo o . Bar, you can define an endpoint URI like the following: jms:Foo.Bar
175
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To define a route that connects the consumer endpoint,
fi l e: //l o cal /ro uter/messag es/fo o , directly to the producer endpoint,
jms: Fo o . 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>
5.4 . PIPES AND FILT ERS
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 pi pe 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.
Fig u re 5.4 . Pip es an d Filt ers Pat t ern
176
Pipeline for t he InOut exchange pat t ern
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
Fig u re 5.5. Pip elin e f o r In O u t Exch an g es
CHAPT ER 5. MESSAG ING SYST EMS
Where the pipeline connects the output of each endpoint to the input of the next one. The Out message from the final endpoint gets 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 fro m and to
Pipeline for t he InOnly and Robust InOnly exchange pat t erns
When there are no Out messages available from the endpoints in the pipeline (as is the case for the InO nl y and R o bustInO nl y 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” ).
Fig u re 5.6 . Pip elin e f o r In O n ly Exch an g es
177
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 t o() 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 mul ti cast() command precedes the to () command, it binds the listed endpoints into a multicast pattern, instead of a pipeline pattern(see
).
5.5. MESSAGE ROUT ER
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.
Fig u re 5.7. Messag e Ro u t er Pat t ern
178
CHAPT ER 5. MESSAG ING SYST EMS
A message router can easily be implemented in Apache Camel using the cho i ce() 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 sed a: a, sed a: b, or sed a: c) depending on the contents of the fo o 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 configurat ion 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 wit hout ot herwise
If you use cho i ce() without an o therwi se() clause, any unmatched exchanges are dropped by default.
5.6. MESSAGE T RANSLAT OR
Overview
179
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.
Fig u re 5.8. Messag e T ran slat o r Pat t ern
180
Bean int egrat ion
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, myMetho d Name(), on the bean with ID, myT ransfo rmerBean: from("activemq:SomeQueue")
.beanRef("myTransformerBean", "myMethodName")
.to("mqseries:AnotherQueue");
Where the myT ransfo rmerBean bean is defined in either a Spring XML file or in JNDI. If, you omit the method name parameter from beanR ef(), the bean integration will try to deduce the method name to invoke by examining the message exchange.
You can also add your own explicit P ro cesso r 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) :
CHAPT ER 5. MESSAG ING SYST EMS
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. Q ueue queue on ActiveMQ with a template generated response, then you could use a route like the following to send responses back to the JMSR epl yT o destination: from("activemq:My.Queue").
to("velocity:com/acme/MyResponse.vm");
5.7. MESSAGE HIST ORY
Overview
The Message History from EIP 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.
Limit ing Charact er Lengt h 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.
Cu st o miz in g t h e Limit u sin g 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");
Cu st o miz in g t h e Limit u sin g Sp rin g DSL
181
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can set the limit in Camel properties using Spring DSL. For example,
<camelContext>
<properties>
<property key="CamelLogDebugBodyMaxChars" value="500"/>
</properties>
</camelContext>
182
CHAPT ER 6 . MESSAG ING CHANNELS
CHAPTER 6. MESSAGING CHANNELS
Ab st ract
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 -T O-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 with a
, which allows multiple receivers to consume the same message. In particular, with a point-to-point 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.
Fig u re 6 .1. Po in t t o Po in t Ch an n el Pat t ern
Component s t hat support point -t o-point channel
The following Apache Camel components support the point-to-point channel pattern:
JMS
183
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 Fo o . Bar as follows: jms:queue:Foo.Bar
The qualifier, q ueue: , 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.
Act iveMQ
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 Fo o . 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 pointto-point channel that is internal to the Apache Camel application. For example, you can specify an endpoint URI for a SEDA queue called Sed aQ ueue 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
184
CHAPT ER 6 . MESSAG ING CHANNELS
A publish-subscribe channel, shown in
Figure 6.2, “ Publish Subscribe Channel Pattern” , is a
that enables multiple subscribers to consume any given message. This is
in contrast with a point-to-point channel
. Publish-subscribe channels are frequently used as a means of broadcasting events or notifications to multiple subscribers.
Fig u re 6 .2. Pu b lish Su b scrib e Ch an n el Pat t ern
Component s t hat support publish-subscribe channel
The following Apache Camel components support the publish-subscribe channel pattern:
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
185
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
In JMS, a publish-subscribe channel is represented by a topic. For example, you can specify the endpoint URI for a JMS topic called Sto ckQ uo tes as follows: jms:topic:StockQuotes
See
Jms
in the Apache Camel Component Reference Guide for more details.
Act iveMQ
In ActiveMQ, a publish-subscribe channel is represented by a topic. For example, you can specify the endpoint URI for an ActiveMQ topic called Sto ckQ uo tes, 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.
St at ic subscript ion list s
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, sed a: a, and three subscribers, sed a: b, sed a: c, and sed a: d : from("seda:a").to("seda:b", "seda:c", "seda:d");
No t e
This only works for the InOnly message exchange pattern.
XML configurat ion example
186
The following example shows how to configure the same route in XML:
<camelContext id="buildStaticRecipientList" xmlns="http://camel.apache.org/schema/spring">
CHAPT ER 6 . MESSAG ING CHANNELS
<route>
<from uri="seda:a"/>
<to uri="seda:b"/>
<to uri="seda:c"/>
<to uri="seda:d"/>
</route>
</camelContext>
6.3. DEAD LET T ER 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.
Fig u re 6 .3. Dead Let t er Ch an n el Pat t ern
Creat ing a dead let t er 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");
187
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where the erro rHand l er() 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
d ead LetterC hannel () method is a Java DSL command that creates a new dead letter channel with the specified destination endpoint, sed a: erro rs.
The erro rHand l er() 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 o nExcepti o n 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).u
seExponentialBackOff()); 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
R ed el i veryP o l i cy object).
Table 6.1, “ Redelivery Policy Settings” summarizes the
methods that you can use to set redelivery policies.
T ab le 6 .1. Red elivery Po licy Set t in g s
188
Met hod Signat ure al l o wR ed el i veryWhi l eSto ppi ng ()
Default true backO ffMul ti pl i er(d o ubl e mul ti pl i er)
2 co l l i si o nAvo i d anceP ercent(d o ubl e co l l i si o nAvo i d anceP ercent)
15 d ead LetterHand l eNewE xcepti o n true d el ayP attern(Stri ng d el ayP attern) d i sabl eR ed el i very()
None
true
CHAPT ER 6 . MESSAG ING CHANNELS
Descript ion
Co ntro ls whether red elivery is attemp ted d uring g raceful shutd o wn o r while a ro ute is sto p p ing . A d elivery that is alread y in p ro g ress when sto p p ing is initiated will not b e interrup ted .
If exp o nential b acko ff is enab led , let
m
b e the b acko ff multip lier and let
d
b e the initial d elay. The seq uence o f red elivery attemp ts are then timed as fo llo ws: d, m*d, m*m*d, m*m*m*d, ...
If co llisio n avo id ance is enab led , let
p
b e the co llisio n avo id ance p ercent.
The co llisio n avo id ance p o licy then tweaks the next d elay b y a rand o m amo unt, up to p lus/minus
p%
o f its current value.
Camel 2.15: Sp ecifies whether o r no t to hand le an excep tio n that o ccurs while p ro cessing a messag e in the d ead letter channel. If
true
, the excep tio n is hand led and a lo g g ed at the WARN level
(so that the d ead letter channel is g uaranteed to co mp lete). If
fal se
, the excep tio n is no t hand led , so the d ead letter channel fails, and p ro p ag ates the new excep tio n.
Ap ache Camel 2.0 :
Ap ache Camel 2.0 : Disab les the red elivery feature. To enab le red elivery, set
maxi mumR ed el i veri es(
)
to a p o sitive integ er value.
189
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Met hod Signat ure hand l ed (bo o l ean hand l ed )
Default true i ni ti al R ed el i veryD e l ay(l o ng i ni ti al R ed el i veryD e l ay) l o g NewExcepti o n l o g StackT race(bo o l ea n l o g StackT race) maxi mumR ed el i veri es( i nt maxi mumR ed el i veri es) maxi mumR ed el i veryD e l ay(l o ng maxD el ay) o nR ed el i very(P ro cess o r pro cesso r) red el i veryD el ay(l o n g i nt)
10 0 0 true fal se
0
6 0 0 0 0
None
0
Descript ion
Ap ache Camel 2.0 : If
true
, the current excep tio n is cleared when the messag e is mo ved to the d ead letter channel; if
fal se
, the excep tio n is p ro p ag ated b ack to the client.
Sp ecifies the d elay (in milliseco nd s) b efo re attemp ting the first red elivery.
Sp ecifies whether to lo g at
WARN level, when an excep tio n is raised in the d ead letter channel.
Ap ache Camel 2.0 : If
true
, the JVM stack trace is includ ed in the erro r lo g s.
Ap ache Camel 2.0 : Maximum numb er o f d elivery attemp ts.
Ap ache Camel 2.0 : When using an exp o nential b acko ff strateg y (see
useExpo nenti al BackO f f()
), it is theo retically p o ssib le fo r the red elivery d elay to increase witho ut limit. This p ro p erty imp o ses an up p er limit o n the red elivery d elay (in milliseco nd s)
Ap ache Camel 2.0 :
Co nfig ures a p ro cesso r that g ets called b efo re every red elivery attemp t.
Ap ache Camel 2.0 : Sp ecifies the d elay (in milliseco nd s) b etween red elivery attemp ts.
190
CHAPT ER 6 . MESSAG ING CHANNELS
Met hod Signat ure retri esExhausted Lo g L evel (Lo g g i ng Level l o g Level )
Default
Lo g g i ng Level . ER R O R retryAttempted Lo g Leve l (Lo g g i ng Level l o g Level ) useC o l l i si o nAvo i d an ce() useO ri g i nal Messag e() useExpo nenti al BackO f f()
Lo g g i ng Level . D EBUG fal se fal se fal se
Descript ion
Ap ache Camel 2.0 : Sp ecifies the lo g g ing level at which to lo g d elivery failure
(sp ecified as an
o rg . apache. camel . Lo g g i ng Level
co nstant).
Ap ache Camel 2.0 : Sp ecifies the lo g g ing level at which to red elivery attemp ts
(sp ecified as an
o rg . apache. camel . Lo g g i ng Level
co nstant).
Enab les co llisio n avo id ence, which ad d s so me rand o mizatio n to the b acko ff timing s to red uce co ntentio n p ro b ab ility.
Ap ache Camel 2.0 : If this feature is enab led , the messag e sent to the d ead letter channel is a co p y o f the original messag e exchang e, as it existed at the b eg inning o f the ro ute (in the
fro m()
no d e).
Enab les exp o nential b acko ff.
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.
T ab le 6 .2. Dead Let t er Red elivery Head ers
Header Name
C amel R ed el i veryC o un ter
T ype
Integ er
Descript ion
Ap ache Camel 2.0 : Co unts the numb er o f unsuccessful d elivery attemp ts. This value is also set in
Exchang e. R ED ELIVER Y _
C O UNT ER
.
191
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Header Name
C amel R ed el i vered
T ype
Bo o l ean
C amel R ed el i veryMaxC o unter
Integ er
Descript ion
Ap ache Camel 2.0 : True, if o ne o r mo re red elivery attemp ts have b een mad e.
This value is also set in
.
Exchang e. R ED ELIVER ED
Ap ache Camel 2.6 : Ho ld s the maximum red elivery setting (also set in the
Exchang e. R ED ELIVER Y _
MAX_C O UNT ER
exchang e p ro p erty). This head er is ab sent if yo u use
retryWhi l e
o r have unlimited maximum red elivery co nfig ured .
Redelivery exchange propert ies
If Apache Camel attempts to redeliver a message, it automatically sets the exchange
properties described in Table 6.3, “ Redelivery Exchange Properties”
.
T ab le 6 .3. Red elivery Exch an g e Pro p ert ies
Exchange Propert y Name T ype
Exchang e. FAILUR E_R O U
T E_ID
Stri ng
Descript ion
Pro vid es the ro ute ID o f the ro ute that failed . The literal name o f this p ro p erty is
C amel Fai l ureR o uteId
.
192
Using t he original message
Availab le as o f Ap ach e 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
CHAPT ER 6 . MESSAG ING CHANNELS
using the sequence of beans: val i d ateO rd er, transfo rmO rd er, and hand l eO rd er. But when an error occurs, we do not know in which state the message is in. Did the error happen before the transfo rmO rd er bean or after? We can ensure that the original message from
jms: q ueue: o rd er: i nput is logged to the dead letter channel by enabling the
useO ri g i nal Messag e option as follows:
// will use original body errorHandler(deadLetterChannel("jms:queue:dead")
.useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);
Redeliver delay pat t ern
Availab le as o f Ap ach e Camel 2.0 The d el ayP attern 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 limitN <= redeliveryCount < limitN+1
For example, consider the pattern, 5: 10 0 0 ;10 : 50 0 0 ;20 : 20 0 0 0 , 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: 10 0 0 ;5: 50 0 0 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: 50 0 0 ;3: 10 0 0 , 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:
193
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Java
String lastEndpointUri = exchange.getProperty(Exchange.TO_ENDPOINT,
String.class);
Where Exchang e. T O _END P O INT is a string constant equal to C amel T o End po i nt. 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 C amel Fai l ureEnd po i nt, which identifies the last destination the exchange was sent to before the error occcured. 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 Exchang e. FAILUR E_END P O INT is a string constant equal to
C amel Fai l ureEnd po i nt.
No t e
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 fo o bean. In this case the
Exchang e. T O _END P O INT property and the Exchang e. FAILUR E_END P O INT property still contain the value.
onRedelivery processor
When a dead letter channel is performing redeliveries, it is possible to configure a
P ro cesso r 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.
194
For example, the following dead letter channel is configured to call the
MyR ed el i verP ro cesso r 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));
CHAPT ER 6 . MESSAG ING CHANNELS
Where the MyR ed el i veryP ro cesso r 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 MyR ed el i verP ro cesso r 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);
}
}
Cont rol redelivery during shut down or st opping
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
al l o wR ed el i veryWhi l eSto ppi ng option to fal se, as shown in the following example: errorHandler(deadLetterChannel("jms:queue:dead")
. al l o wR ed el i veryWhi l eSto ppi ng (fal se)
.maximumRedeliveries(20)
.redeliveryDelay(1000)
.retryAttemptedLogLevel(LoggingLevel.INFO));
No t e
The al l o wR ed el i veryWhi l eSto ppi ng 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).
onExcept ion clause
195
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Instead of using the erro rHand l er() interceptor in your route builder, you can define a series of o nExcepti o n() 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 Nul l P o i nterExcepti o n, IO Excepti o n, and Excepti o n 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
o nExcepti o n() clauses. For example, the preceding example calls setHead er() to record some error details in a message header named, messag eInfo .
In this example, the Nul l P o i nterExcepti o n and the IO Excepti o n exception types are configured specially. All other exception types are handled by the generic Excepti o n 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
Excepti o n type matches all remaining exceptions.
OnPrepareFailure
Before you pass the exchange to the dead letter queue, you can use the o nP repare 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 static class MyPrepareProcessor implements Processor {
@ Override public void process(Exchange exchange) throws Exception {
Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
196
CHAPT ER 6 . MESSAG ING CHANNELS
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 o nP repare option is also available using the default error handler.
<bean id= "myPrepare" class = "org.apache.camel.processor.DeadLetterChannelOnPrepareTest.MyPre
pareProcessor" />
<errorHandler id= "dlc" type= "DeadLetterChannel" deadLetterUri= "jms:dead" onPrepareFailureRef= "myPrepare" />
6.4 . GUARANT EED 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.
Fig u re 6 .4 . G u aran t eed Delivery Pat t ern
Component s t hat support guarant eed delivery
The following Apache Camel components support the guaranteed delivery pattern:
197
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
File Component
in the Apache Camel Component Reference Guide
JMS
In JMS, the d el i veryP ersi stent 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.
Act iveMQ
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, MET A-INF/spri ng /camel -
co ntext. 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>
198
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 -
co ntext. xml configuration file, you can configure the ActiveMQ component to connect to all of the peers in group, G ro upA, as follows:
<beans ... >
CHAPT ER 6 . MESSAG ING CHANNELS
...
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="peer://GroupA/broker1"/>
</bean>
...
</beans>
Where bro ker1 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 acti vemq . 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:
AMQ persistence(the default) — A fast and reliable message store that is native to
ActiveMQ. For details, see amqPersistenceAdapter and AMQ Message Store .
JDBC persistence — Uses JDBC to store messages in any JDBC-compatible database.
For details, see jdbcPersistenceAdapter and ActiveMQ Persistence .
Journal persistence — A fast persistence mechanism that stores messages in a rolling log file. For details, see journalPersistenceAdapter and ActiveMQ Persistence .
Kaha persistence — A persistence mechanism developed specifically for ActiveMQ. For details, see kahaPersistenceAdapter and ActiveMQ Persistence .
See
ActiveMQ
in the Apache Camel Component Reference Guide for more details.
Act iveMQ 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.
199
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.
Fig u re 6 .5. Messag e Bu s Pat t ern
200
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.
CHAPT ER 7 . MESSAG E CO NST RUCT IO N
CHAPTER 7. MESSAGE CONSTRUCTION
Ab st ract
The message construction patterns describe the various forms and functions of the messages that pass through the system.
7.1. CORRELAT ION IDENT IFIER
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 JMSC o rrel ati o nID . 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,
Exchang e. C O R R ELAT IO N_ID , which links back to the source Exchanges. For example, the
,
Fig u re 7.1. Co rrelat io n Id en t if ier Pat t ern
201
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
7.2. EVENT MESSAGE
Event Message
Camel supports the Event Message from the
Introducing Enterprise Integration Patterns
by
supporting the Exchange Pattern on a Message which can be set to In O n ly to indicate a
oneway event message. Camel Components then implement this pattern using the underlying transport or protocols.
202
The default behaviour of many Components is InOnly such as for JMS , File or SEDA
Explicit ly specifying InOnly
If you are using a component which defaults to InOut you can override the Message
Exchange Pattern for an endpoint using the pattern property.
foo:bar?exchangePattern=InOnly
From 2.0 onwards on Camel you can specify the
Message Exchange Pattern using the dsl.
Usin g t h e Flu en t Bu ild ers
from("mq:someQueue").
inOnly().
bean(Foo.class); or you can invoke an endpoint with an explicit pattern
CHAPT ER 7 . MESSAG E CO NST RUCT IO N
from("mq:someQueue").
inOnly("mq:anotherQueue");
Usin g t h e Sp rin g XML Ext en sio n s
<route>
<from uri="mq:someQueue"/>
<inOnly uri="bean:foo"/>
</route>
<route>
<from uri="mq:someQueue"/>
<inOnly uri="mq:anotherQueue"/>
</route>
7.3. RET URN ADDRESS
Ret urn Address
Apache Camel supports the Return Address from the Introducing Enterprise Integration
Patterns using the JMSR epl yT o header.
For example when using JMS with InOut, the component will by default be returned to the address given in JMSR epl yT o .
Example
Req u est o r Co d e
203
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
getMockEndpoint("mock:bar").expectedBodiesReceived("Bye World");
template.sendBodyAndHeader("direct:start", "World", "JMSReplyTo",
"queue:bar");
Ro u t e Usin g t h e Flu en t Bu ild ers
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");
Ro u t e Usin g t h e Sp rin g XML Ext en sio n s
<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
204
CHAPT ER 8 . MESSAG E RO UT ING
CHAPTER 8. MESSAGE ROUTING
Ab st ract
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. CONT ENT -BASED ROUT ER
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.
Fig u re 8.1. Co n t en t - Based Ro u t er Pat t ern
Java DSL example
The following example shows how to route a request from an input, sed a: a, endpoint to either sed a: b, q ueue: c, or sed a: 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 configurat ion example
The following example shows how to configure the same route in XML:
<camelContext id="buildSimpleRouteWithChoice" xmlns="http://camel.apache.org/schema/spring">
<route>
205
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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 FILT ER
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 fi l ter() Java DSL command. The fi l ter() 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 fal se, the incoming message is blocked.
Fig u re 8.2. Messag e Filt er Pat t ern
206
Java DSL example
The following example shows how to create a route from endpoint, sed a: a, to endpoint,
sed a: b, that blocks all messages except for those messages whose fo o header have the value, bar:
RouteBuilder builder = new RouteBuilder() {
public void configure() { from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");
}
};
CHAPT ER 8 . MESSAG E RO UT ING
To evaluate more complex filter predicates, you can invoke one of the supported scripting
languages, such as XPath, XQuery, or SQL (see Expression and Predicate Languages ). The
following example defines a route that blocks all messages except for those containing a
perso n element whose name attribute is equal to James: from("direct:start").
filter().xpath("/person[@ name='James']").
to("mock:result");
XML configurat ion example
The following example shows how to configure the route with an XPath predicate in XML (see
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>
Filt ered en d p o in t req u ired in sid e </f ilt er> t ag
Make sure you put the endpoint you want to filter (for example, <to
uri = "sed a: b"/>) before the closing </fi l ter> tag or the filter will not be applied (in 2.8+, omitting this will result in an error).
Filt ering wit h 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 st op()
Availab le as o f Camel 2.0
207
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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
. sto p().
from("direct:start")
.choice()
.when(bodyAs(String.class).contains("Hello")).to("mock:hello")
.when(bodyAs(String.class).contains("Bye")).to("mock:bye").sto p()
.otherwise().to("mock:other")
.end()
.to("mock:result");
Knowing if Exchange was filt ered or not
Availab le as o f Camel 2.5
The
EIP will add a property on the Exchange which states if it was filtered or not.
The property has the key Exchanng e. FILT ER _MAT C HED which has the String value of
C amel Fi l terMatched . Its value is a boolean indicating true or fal se. 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.
Fig u re 8.3. Recip ien t List Pat t ern
208
CHAPT ER 8 . MESSAG E RO UT ING
Recipient list wit h fixed dest inat ions
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.
No t e
The examples given here, for the recipient list with fixed destinations, work only with
the InOnly exchange pattern (similar to a pipeline ). If you want to create a recipient
list for exchange patterns with Out messages, use the
pattern instead.
Java DSL example
The following example shows how to route an InOnly exchange from a consumer endpoint,
q ueue: a, to a fixed list of destinations: from("seda:a").to("seda:b", "seda:c", "seda:d");
XML configurat ion 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 calculat ed at run t ime
209
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
In most cases, when you use the recipient list pattern, the list of recipients should be calculated at runtime. To do this use the reci pi entLi st() 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 43.3,
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 reci pi entLi stHead er, 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 reci pi entLi st(). 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 configurat ion 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 t o mult iple recipient s in parallel
Availab le as o f Camel 2.2
210
CHAPT ER 8 . MESSAG E RO UT ING
The
supports paral l el P ro cessi ng , which is similar to the corresponding
feature in Splitter . 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
reci pi entLi st tag—for example:
<route>
<from uri="direct:a"/>
<recipientList parallelProcessing="true">
<header>myHeader</header>
</recipientList>
</route>
St op on except ion
Availab le as o f Camel 2.2
The
supports the sto pO nExcepti o n 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
reci pi entLi st tag—for example:
<route>
<from uri="direct:a"/>
<recipientList stopOnException="true">
<header>myHeader</header>
</recipientList>
</route>
No t e
You can combine paral l el P ro cessi ng and sto pO nExcepti o n in the same route.
Ignore invalid endpoint s
Availab le as o f Camel 2.3
211
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The
supports the i g no reInval i d End po i nts option, which enables the recipient list to skip invalid endpoints (
Routing Slip also supports this option). For example:
from("direct:a").recipientList(header("myHeader")).ignoreInvalidEndpoi
nts();
And in Spring XML, you can enable this option by setting the i g no reInval i d End po i nts attribute on the reci pi entLi st tag, as follows
<route>
<from uri="direct:a"/>
<recipientList ignoreInvalidEndpoints="true">
<header>myHeader</header>
</recipientList>
</route>
Consider the case where myHead er contains the two endpoints, d i rect: fo o ,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 cust om Aggregat ionSt rat egy
Availab le as o f Camel 2.2
You can use a custom Ag g reg ati o nStrateg y with the Recipient List
, which is useful for aggregating replies from the recipients in the list. By default, Apache Camel uses the
UseLatestAg g reg ati o nStrateg y aggregation strategy, which keeps just the last received reply. For a more sophisticated aggregation strategy, you can define your own
implementation of the Ag g reg ati o nStrateg y interface—see Aggregator
EIP for details.
For example, to apply the custom aggregation strategy, MyO wnAg g reg ati o nStrateg y, 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
reci pi entLi st 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"/>
212
CHAPT ER 8 . MESSAG E RO UT ING
Using cust om t hread pool
Availab le as o f Camel 2.2
This is only needed when you use paral l el P ro cessi ng . 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 met hod call as recipient list
You can use a Bean to provide the recipients, for example: from("activemq:queue:test").recipientList().method(MessageRouter.class
, "routeTo");
Where the Messag eR o uter 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 @ R eci pi entLi st 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 reci pi entLi st DSL command in the route. Define the route as follows: from("activemq:queue:test").bean(MessageRouter.class, "routeTo");
Using t imeout
213
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Availab le as o f Camel 2.5
If you use paral l el P ro cessi ng , you can configure a total ti meo ut 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 reci pi entl i st header has the value,
d i rect: a,d i rect: b,d i rect: 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"));
No t e
This ti meo ut feature is also supported by spl i tter and both mul ti cast and
reci pi entLi st.
By default if a timeout occurs the Ag g reg ati o nStrateg y is not invoked. However you can implement a specialized version
// Java public interface TimeoutAwareAggregationStrategy extends
AggregationStrategy {
/**
* A timeout occurred
214
CHAPT ER 8 . MESSAG E RO UT ING
*
* @ param oldExchange the oldest exchange (is
& lt;tt>null& lt;/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 Ag g reg ati o nStrateg y if you really need to.
T imeo u t is t o t al
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 ti meo ut method in the
T i meo utAwareAg g reg ati o nStrateg y once, for the first index which caused the timeout.
Apply cust om processing t o t he out going messages
Before reci pi entLi st sends a message to one of the recipient endpoints, it creates a message replica, which is a shallow copy of the original message. If you want to perform some custom processing on each message replica before the replica is sent to its endpoint, you can invoke the o nP repare DSL command in the reci pi entLi st clause. The
o nP repare command inserts a custom processor just after the message has been shallowcopied and just before the message is dispatched to its endpoint. For example, in the following route, the C usto mP ro c processor is invoked on the message replica for each
recipient endpoint: from("direct:start")
.recipientList().onPrepare(new CustomProc());
A common use case for the o nP repare 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 C usto mP ro c 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. d eepC o py().
// 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();
215
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
exchange.getIn().setBody(clone);
// Headers and attachments have already been
// shallow-copied. If you need deep copies,
// add some more code here.
}
}
Opt ions
The reci pi entLi st DSL command supports the following options:
Name d el i mi ter strateg yR ef strateg yMetho d Name
Default Value
, strateg yMetho d Al l o wN ul l fal se
Descript ion
Delimiter used if the
Exp ressio n returned multip le end p o ints.
Refers to an
Ag g reg atio nStrateg y to b e used to assemb le the rep lies fro m the recip ients, into a sing le o utg o ing messag e fro m the
. By d efault Camel will use the last rep ly as the o utg o ing messag e.
This o p tio n can b e used to exp licitly sp ecify the metho d name to use, when using
PO JO s as the
Ag g reg ati o nStrateg y
.
This o p tio n can b e used , when using PO JO s as the
Ag g reg ati o nStrateg y
. If
fal se
, the
ag g reg ate
metho d is no t used , when there is no d ata to enrich. If
true
,
nul l
values are used fo r the
o l d Exchang e
, when there is no d ata to enrich.
216
paral l el P ro cessi ng fal se paral l el Ag g reg ate fal se executo rServi ceR ef sto pO nExcepti o n fal se
CHAPT ER 8 . MESSAG E RO UT ING
Camel 2 .2 : If enab les then send ing messag es to the recip ients o ccurs co ncurrently. No te the caller thread will still wait until all messag es has b een fully p ro cessed , b efo re it co ntinues. Its o nly the send ing and p ro cessing the rep lies fro m the recip ients which hap p ens co ncurrently.
If enab led , the
ag g reg ate
metho d o n
Ag g reg ati o nStrateg y
can b e called co ncurrently.
No te that this req uires the imp lementatio n o f
Ag g reg ati o nStrateg y
to b e thread -safe. By d efault, this o p tio n is
fal se
, which means that Camel auto matically synchro nizes calls to the
ag g reg ate
metho d . In so me use-cases, ho wever, yo u can imp ro ve p erfo rmance b y imp lementing
Ag g reg ati o nStrateg y
as thread -safe and setting this o p tio n to
true
.
Camel 2 .2 : Refers to a custo m Thread Po o l to b e used fo r p arallel p ro cessing . No tice if yo u set this o p tio n, then p arallel p ro cessing is auto matic imp lied , and yo u d o no t have to enab le that o p tio n as well.
Camel 2 .2 : Whether o r no t to sto p co ntinue p ro cessing immed iately when an excep tio n o ccurred . If d isab le, then Camel will send the messag e to all recip ients reg ard less if o ne o f them failed . Yo u can d eal with excep tio ns in the
Ag g reg atio nStrateg y class where yo u have full co ntro l ho w to hand le that.
217
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide i g no reInval i d End po i nts fal se streami ng ti meo ut o nP repareR ef shareUni tO fWo rk cacheSi ze fal se fal se
0
Camel 2 .3: If an end p o int uri co uld no t b e reso lved , sho uld it b e ig no red .
O therwise Camel will thro wn an excep tio n stating the end p o int uri is no t valid .
Camel 2 .5: If enab led then
Camel will p ro cess rep lies o ut-o f-o rd er, eg in the o rd er they co me b ack. If d isab led ,
Camel will p ro cess rep lies in the same o rd er as the
Exp ressio n sp ecified .
Camel 2 .5: Sets a to tal timeo ut sp ecified in millis. If
b een ab le to send and p ro cess all rep lies within the g iven timeframe, then the timeo ut trig g ers and the
and co ntinues. No tice if yo u p ro vid e a
Timeo utAwareAg g reg atio nSt rateg y then the
ti meo ut
metho d is invo ked b efo re b reaking o ut.
Camel 2 .8 : Refers to a custo m Pro cesso r to p rep are the co p y o f the
Exchang e each recip ient will receive. This allo ws yo u to d o any custo m lo g ic, such as d eep -clo ning the messag e p aylo ad if that's need ed etc.
Camel 2 .8 : Whether the unit o f wo rk sho uld b e shared .
See the same o p tio n o n
fo r mo re d etails.
Camel 2 .1 3.1 /2 .1 2 .4 : Allo ws to co nfig ure the cache size fo r the ProducerCache which caches p ro d ucers fo r reuse in the ro uting slip . Will b y d efault use the d efault cache size which is 0 . Setting the value to -1 allo ws to turn o ff the cache all to g ether.
218
CHAPT ER 8 . MESSAG E RO UT ING
Using Exchange Pat t ern 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 InO nl y route. Now, If you want to use
InO ut 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" );
No t e
The InO ut exchange pattern must get a response during the timeout. However, it fails if the response is not recieved.
8.4 . SPLIT T ER
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 spl i t() Java DSL command.
Fig u re 8.4 . Sp lit t er Pat t ern
219
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.
Java DSL example
The following example defines a route from sed a: a to sed a: 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 configurat ion 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 to keni ze element. In the following example, the message body is tokenized using the \n separator character. To use a regular expression pattern, set reg ex= true in the to keni ze element.
220
CHAPT ER 8 . MESSAG E RO UT ING
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<split>
<tokenize token="\n"/>
<to uri="mock:result"/>
</split>
</route>
</camelContext>
Split t ing int o 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 to keni ze specifies the number of lines that should be grouped into a single chunk. The streami ng () 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 g ro up option is always of java. l ang . Stri ng type.
The group option < to keni ze to ken= "\n" g ro up= "10 0 0 "/> (XML DSL) or
. to keni ze("\n", 10 0 0 )") (Java DSL) does not exist if group option is used after a body expression. For example, as shown in the code snippet below, the group option does not work.
from("file:inbox")
.split(body(String.class).tokenize("\n",
1000)).streaming()
To be able to use group option, you need to create a T o keni zerExpressi o n, as shown below:
221
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
TokenizerExpression token = new TokenizerExpression();
token.setGroup(1000);
token.setToken("\n");
Skip first it em
To skip the first item in the message you can use the ski pFi rst option.
In Java DSL, make the third option in the to keni ze 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>
Split t er 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 execut ion
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");
222
You can customize the underlying T hread P o o l Executo r 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,
CHAPT ER 8 . MESSAG E RO UT ING
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 t o perform split t ing
As the splitter can use any expression to do the splitting, we can use a bean to perform splitting, by invoking the metho d () expression. The bean should return an iterable value such as: java. uti l . C o l l ecti o n, java. uti l . Iterato r, or an array.
The following route defines a metho d () expression that calls a method on the
mySpl i tterBean 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");
223
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where mySpl i tterBean is an instance of the MySpl i tterBean 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 Apache Camel 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 Apache Camel 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;
}
}
224
Exchange propert ies
CHAPT ER 8 . MESSAG E RO UT ING
Exchange propert ies
The following properties are set on each split exchange:
header
C amel Spl i tInd ex t ype i nt
C amel Spl i tSi ze i nt
C amel Spl i tC o mpl ete bo o l ean descript ion
Ap ache Camel 2.0 : A sp lit co unter that increases fo r each Exchang e b eing sp lit.
The co unter starts fro m 0 .
Ap ache Camel 2.0 : The to tal numb er o f Exchang es that was sp lit. This head er is no t ap p lied fo r stream b ased sp litting .
Ap ache Camel 2.4: Whether o r no t this Exchang e is the last.
Split t er/aggregat or pat t ern
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
spl i t() DSL command lets you provide an Ag g reg ati o nStrateg y 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")
Aggregat ionSt rat egy implement at ion
225
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Aggregat ionSt rat egy implement at ion
The custom aggregation strategy, MyO rd erStrateg y, 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;
}
}
St ream 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.
226
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")
CHAPT ER 8 . MESSAG E RO UT ING
.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")
St reamin g an d XPat h
You cannot use streaming mode in conjunction with XPath. XPath requires the complete DOM XML document in memory.
St ream based processing wit h XML
If an incoming messages is a very large XML file, you can process the message most efficiently using the to keni zeXML sub-command in streaming mode.
For example, given a large XML file that contains a sequence of o rd er elements, you can split the file into o rd er 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.
227
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
In the Java DSL, you specify the ancestor element as the second argument of
to keni zeXML. For example, to inherit namespace definitions from the enclosing o rd ers element: from("file:inbox")
.split().tokenizeXML("order", "o rd ers").streaming()
.to("activemq:queue:order");
In the XML DSL, you specify the ancestor element using the i nheri tNamespaceT ag Name attribute. For example:
<route>
<from uri="file:inbox"/>
<split streaming="true">
<tokenize token="order"
xml="true"
i nheri tNamespaceT ag Name= "o rd ers"/>
<to uri="activemq:queue:order"/>
</split>
</route>
Opt ions
The spl i t DSL command supports the following options:
Name strateg yR ef strateg yMetho d Name
Default Value Descript ion
Refers to an
Ag g reg atio nStrateg y to b e used to assemb le the rep lies fro m the sub -messag es, into a sing le o utg o ing messag e fro m the
sectio n titled What does the
splitter return b elo w fo r whats used b y d efault.
This o p tio n can b e used to exp licitly sp ecify the metho d name to use, when using
PO JO s as the
Ag g reg ati o nStrateg y
.
228
strateg yMetho d Al l o wN ul l fal se paral l el P ro cessi ng paral l el Ag g reg ate executo rServi ceR ef fal se fal se
CHAPT ER 8 . MESSAG E RO UT ING
This o p tio n can b e used , when using PO JO s as the
Ag g reg ati o nStrateg y
. If
fal se
, the
ag g reg ate
metho d is no t used , when there is no d ata to enrich. If
true
,
nul l
values are used fo r the
o l d Exchang e
, when there is no d ata to enrich.
If enab les then p ro cessing the sub -messag es o ccurs co ncurrently. No te the caller thread will still wait until all sub -messag es has b een fully p ro cessed , b efo re it co ntinues.
If enab led , the
ag g reg ate
metho d o n
Ag g reg ati o nStrateg y
can b e called co ncurrently.
No te that this req uires the imp lementatio n o f
Ag g reg ati o nStrateg y
to b e thread -safe. By d efault, this o p tio n is
fal se
, which means that Camel auto matically synchro nizes calls to the
ag g reg ate
metho d . In so me use-cases, ho wever, yo u can imp ro ve p erfo rmance b y imp lementing
Ag g reg ati o nStrateg y
as thread -safe and setting this o p tio n to
true
.
Refers to a custo m Thread
Po o l to b e used fo r p arallel p ro cessing . No tice if yo u set this o p tio n, then p arallel p ro cessing is auto matic imp lied , and yo u d o no t have to enab le that o p tio n as well.
229
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide sto pO nExcepti o n fal se streami ng ti meo ut fal se
Camel 2 .2 : Whether o r no t to sto p co ntinue p ro cessing immed iately when an excep tio n o ccurred . If d isab le, then Camel co ntinue sp litting and p ro cess the sub -messag es reg ard less if o ne o f them failed . Yo u can d eal with excep tio ns in the
Ag g reg atio nStrateg y class where yo u have full co ntro l ho w to hand le that.
If enab led then Camel will sp lit in a streaming fashio n, which means it will sp lit the inp ut messag e in chunks.
This red uces the memo ry o verhead . Fo r examp le if yo u sp lit b ig messag es its reco mmend ed to enab le streaming . If streaming is enab led then the sub messag e rep lies will b e ag g reg ated o ut-o f-o rd er, eg in the o rd er they co me b ack.
If d isab led , Camel will p ro cess sub -messag e rep lies in the same o rd er as they where sp litted .
Camel 2 .5: Sets a to tal timeo ut sp ecified in millis. If
b een ab le to sp lit and p ro cess all rep lies within the g iven timeframe, then the timeo ut trig g ers and the
b reaks o ut and co ntinues. No tice if yo u p ro vid e a
Timeo utAwareAg g reg atio nSt rateg y then the
ti meo ut
metho d is invo ked b efo re b reaking o ut.
230
o nP repareR ef shareUni tO fWo rk fal se
CHAPT ER 8 . MESSAG E RO UT ING
Camel 2 .8 : Refers to a custo m Pro cesso r to p rep are the sub -messag e o f the Exchang e, b efo re its p ro cessed . This allo ws yo u to d o any custo m lo g ic, such as d eep -clo ning the messag e p aylo ad if that's need ed etc.
Camel 2 .8 : Whether the unit o f wo rk sho uld b e shared .
See further b elo w fo r mo re d etails.
8.5. AGGREGAT OR
Overview
The aggregator pattern, shown in
Figure 8.5, “ Aggregator Pattern”
, enables you to combine a batch of related messages into a single message.
Fig u re 8.5. Ag g reg at o r Pat t ern
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.
231
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.)
No t e
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 t he aggregat or 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.
Fig u re 8.6 . Ag g reg at o r Imp lemen t at io n
232
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
CHAPT ER 8 . MESSAG E RO UT ING
aggregation cycle lasts until terminated by one of the completion mechanisms.
No t e
From Camel 2.16, the new XSLT Aggregation Strategy allows you to merge two messages with an XSLT file. You can access the
Ag g reg ati o nStrateg i es. xsl t() 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 co mpl eti o nT i meo ut option or the co mpl eti o nInterval 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
Java DSL example
The following example aggregates exchanges with the same Sto ckSymbo l header value, using the UseLatestAg g reg ati o nStrateg y aggregation strategy. For a given
Sto ckSymbo l 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 mo ck endpoint.
from("direct:start")
.aggregate(header("id"), new UseLatestAggregationStrategy())
.completionTimeout(3000)
.to("mock:aggregated");
XML DSL example
233
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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.UseLatestAggregationStrate
gy"/>
Specifying t he correlat ion expression
In the Java DSL, the correlation expression is always passed as the first argument to the
ag g reg ate() 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 C amel Exchang eExcepti o n by default. You can suppress this exception by setting the i g no reInval i d C o rrel ati o nKeys option. For example, in the
Java DSL: from(...).aggregate(...).ignoreInvalidCorrelationKeys()
In the XML DSL, you can set the i g no reInval i d C o rrel ati o nKeys option is set as an attribute, as follows:
<aggregate strategyRef="aggregatorStrategy"
ignoreInvalidCorrelationKeys="true"
...>
...
</aggregate>
234
CHAPT ER 8 . MESSAG E RO UT ING
Specifying t he aggregat ion st rat egy
In Java DSL, you can either pass the aggregation strategy as the second argument to the
ag g reg ate() DSL command or specify it using the ag g reg ati o nStrateg y() clause. For example, you can use the ag g reg ati o nStrateg y() 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 o rg . apache. camel . pro cesso r. ag g reg ate Java package):
UseLatestAg g reg ati o nStrateg y
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.
UseO ri g i nal Ag g reg ati o nStrateg y
Return the first exchange for a given correlation key, discarding all later exchanges with this key. You must set the first exchange by calling
UseO ri g i nal Ag g reg ati o nStrateg y. setO ri g i nal () before you can use this strategy.
G ro uped Exchang eAg g reg ati o nStrateg y
Concatenates all of the exchanges for a given correlation key into a list, which is
Implement ing a cust om aggregat ion st rat egy
If you want to apply a different aggregation strategy, you can implement one of the following aggregation strategy base interfaces:
o rg . apache. camel . pro cesso r. ag g reg ate. Ag g reg ati o nStrateg y
The basic aggregation strategy interface.
o rg . apache. camel . pro cesso r. ag g reg ate. T i meo utAwareAg g reg ati o nStrateg y
Implement this interface, if you want your implementation to receive a notification when an aggregation cycle times out. The ti meo ut notification method has the following signature:
235
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
void timeout(Exchange oldExchange, int index, int total, long timeout)
o rg . apache. camel . pro cesso r. ag g reg ate. C o mpl eti o nAwareAg g reg ati o nStrat eg y
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,
Stri ng Ag g reg ati o nStrateg y and ArrayLi stAg g reg ati o nStrateg y::
//simply combines Exchange String body values using '+' as a delimiter
class Stri ng Ag g reg ati o nStrateg y 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 ArrayLi stAg g reg ati o nStrateg y 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;
}
}
}
236
CHAPT ER 8 . MESSAG E RO UT ING
No t e
Since Apache Camel 2.0, the Ag g reg ati o nStrateg y. ag g reg ate() callback method is also invoked for the very first exchange. On the first invocation of the
ag g reg ate method, the o l d Exchang e parameter is nul l and the newExchang e parameter contains the first incoming exchange.
To aggregate messages using the custom strategy class,
ArrayLi stAg g reg ati o nStrateg y, 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"/>
Cont rolling t he lifecycle of a cust om aggregat ion st rat egy
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
o rg . apache. camel . Servi ce interface (in addition to the Ag g reg ati o nStrateg y interface) and provide implementations of the start() and sto p() 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;
237
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
...
class MyAggStrategyWithLifecycleControl
implements AggregationStrategy, Servi ce {
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 sto p() throws Exception {
// Actions to perform when the enclosing EIP is stopping
...
}
}
Exchange propert ies
The following properties are set on each aggregated exchange:
T ab le 8.1. Ag g reg at ed Exch an g e Pro p ert ies
Header
Exchang e. AG G R EG AT ED
_SIZE
Exchang e. AG G R EG AT ED
_C O MP LET ED _BY
T ype i nt
Stri ng
Descript ion
The to tal numb er o f exchang es ag g reg ated into this exchang e.
Ind icates the mechanism resp o nsib le fo r co mp leting the ag g reg ate exchang e.
Po ssib le values are:
pred i cate
,
si ze
,
ti meo ut
,
i nterval
, o r
co nsumer
.
The following properties are set on exchanges redelivered by the HawtDB aggregation
repository (see the section called “ Persistent aggregation repository” ):
T ab le 8.2. Red elivered Exch an g e Pro p ert ies
238
CHAPT ER 8 . MESSAG E RO UT ING
Header
Exchang e. R ED ELIVER Y
_C O UNT ER
T ype i nt
Descript ion
Seq uence numb er o f the current red elivery attemp t
(starting at
1
).
Specifying a complet ion condit ion
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:
co mpl eti o nP red i cate
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
Ag g reg ati o nStrateg y that implements the P red i cate interface, in which case the Ag g reg ati o nStrateg y will be used as the completion predicate.
co mpl eti o nSi ze
Completes the aggregate exchange after the specified number of incoming exchanges are aggregated.
co mpl eti o nT i meo ut
(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.
co mpl eti o nInterval
(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.
co mpl eti o nFro mBatchC o nsumer
239
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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” .
fo rceC o mpl eti o nO nSto p
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
co mpl eti o nT i meo ut and co mpl eti o nInterval 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 t he complet ion predicat e
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
eag erC heckC o mpl eti o n option.
For example, if you want to terminate a stream of stock quotes every time you receive an
ALER T message (as indicated by the value of a Msg T ype 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");
24 0
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>
CHAPT ER 8 . MESSAG E RO UT ING
<simple>$MsgType = 'ALERT'</simple>
</completionPredicate>
<to uri="mock:result"/>
</aggregate>
</route>
</camelContext>
<bean id="aggregatorStrategy" class="org.apache.camel.processor.aggregate.UseLatestAggregationStrate
gy"/>
Specifying a dynamic complet ion t imeout
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 ti meo ut 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"/>
No t e
You can also add a fixed timeout value and Apache Camel will fall back to use this value, if the dynamic value is nul l or 0 .
Specifying a dynamic complet ion siz e
24 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 mySi ze 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"/>
No t e
You can also add a fixed size value and Apache Camel will fall back to use this value, if the dynamic value is nul l or 0 .
24 2
Forcing complet ion of a single group from wit hin an
Aggregat ionSt rat egy
If you implement a custom Ag g reg ati o nStrateg y class, there is a mechanism available to force the completion of the current message group, by setting the
Exchang e. AG G R EG AT IO N_C O MP LET E_C UR R ENT _G R O UP exchange property to true on the exchange returned from the Ag g reg ati o nStrateg y. ag g reg ate() 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 Ag g reg ati o nStrateg y class completes the current group, if the message body size is larger than 5:
// Java
CHAPT ER 8 . MESSAG E RO UT ING
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 complet ion of all groups wit h 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:
Exchang e. AG G R EG AT IO N_C O MP LET E_ALL_G R O UP S
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.
Exchang e. AG G R EG AT IO N_C O MP LET E_ALL_G R O UP S_INC LUSIVE
Set to true, to force completion of the current aggregation cycle. This message is
included in the current aggregation cycle.
Using Aggregat eCont roller
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();
24 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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 correlat ion 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
cl o seC o rrel ati o nKeyO nC o mpl eti o n 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 cl o seC o rrel ati o nKeyO nC o mpl eti o n() 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 10 0 0 0 key values: from("direct:start")
.aggregate(header("UniqueBatchID"), new MyConcatenateStrategy())
.completionSize(header("mySize"))
.closeCorrelationKeyOnCompletion(10000)
.to("mock:aggregated");
24 4
CHAPT ER 8 . MESSAG E RO UT ING
If an aggregate exchange completes with a duplicate correlation key value, the aggregator throws a C l o sed C o rrel ati o nKeyExcepti o n exception.
Grouped exchanges
You can combine all of the aggregated exchanges in an outgoing batch into a single
o rg . apache. camel . i mpl . G ro uped Exchang e holder class. To enable grouped exchanges, specify the g ro upExchang es() 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 mo ck: resul t 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);
No t e
When you enable the grouped exchanges feature, you must not configure an aggregation strategy (the grouped exchanges feature is itself an aggregation strategy).
No t e
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.
Bat ch 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
C amel BatchSi ze, C amel BatchInd ex , and C amel BatchC o mpl ete 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");
24 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Currently, the following endpoints support the batch consumer mechanism: File, FTP, Mail, iBatis, and JPA.
Persist ent aggregat ion reposit ory
If you want pending aggregated exchanges to be stored persistently, you can use either the
HawtDB
component or the
SQL Component
for persistence support as a persistent aggregation repository. For example, if using HawtDB, you need to include a dependency on the camel -hawtd b component in your Maven POM. You can then configure a route to use the HawtDB aggregation repository as follows: public void configure() throws Exception {
HawtDBAggregationRepository repo = new
AggregationRepository("repo1", "target/data/hawtdb.dat");
from("direct:start")
.aggregate(header("id"), new UseLatestAggregationStrategy())
.completionTimeout(3000)
.aggregationRepository(repo)
.to("mock:aggregated");
}
The HawtDB aggregation repository has a feature that enables it to recover and retry any failed exchanges (that is, any exchange that raised an exception while it was being
processed by the latter part of the route). Figure 8.7, “ Recoverable Aggregation Repository”
shows an overview of the recovery mechanism.
Fig u re 8.7. Reco verab le Ag g reg at io n Rep o sit o ry
24 6
The recovery mechanism works as follows:
1. The aggregator creates a dedicated recovery thread, which runs in the background, scanning the aggregation repository to find any failed exchanges.
2. Each failed exchange is checked to see whether its current redelivery count exceeds the maximum redelivery limit. If it is under the limit, the recovery task resubmits the exchange for processing in the latter part of the route.
3. If the current redelivery count is over the limit, the failed exchange is passed to the dead letter queue.
CHAPT ER 8 . MESSAG E RO UT ING
For more details about the HawtDB component, see
HawtDB
in the Apache Camel Component
Reference Guide.
T hreading opt ions
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 paral l el P ro cessi ng 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. uti l . co ncurrent. Executo rServi ce
instance using the executo rServi ce option (in which case it is unnecessary to enable the paral l el P ro cessi ng option).
Aggregat ing int o a List
A common aggregation scenario involves aggregating a series of incoming message bodies into a Li st object. To facilitate this scenario, Apache Camel provides the
AbstractLi stAg g reg ati o nStrateg y 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 Li st<T >.
For example, to aggregate a series of Integ er message bodies into a Li st<Integ er> 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 asis
return exchange.getIn().getBody(Integer.class);
}
}
24 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Aggregat or opt ions
The aggregator supports the following options:
T ab le 8.3. Ag g reg at o r O p t io n s
O pt ion co rrel ati o nExpressi o n
Default ag g reg ati o nStrateg y strateg yR ef
Descript ion
Mand ato ry Exp ressio n which evaluates the co rrelatio n key to use fo r ag g reg atio n. The
Exchang e which has the same co rrelatio n key is ag g reg ated to g ether. If the co rrelatio n key co uld no t b e evaluated an Excep tio n is thro wn. Yo u can d isab le this b y using the
i g no reBad C o rrel ati o n
Keys
o p tio n.
Mand ato ry
Ag g reg ati o nStrateg y
which is used to merge the inco ming Exchang e with the existing alread y merg ed exchang es. At first call the
o l d Exchang e
p arameter is
nul l
. O n sub seq uent invo catio ns the
o l d Exchang e
co ntains the merg ed exchang es and
newExchang e
is o f co urse the new inco ming Exchang e.
Fro m Camel 2.9 .2 o nward s, the strateg y can o p tio nally b e a
T i meo utAwareAg g reg at i o nStrateg y
imp lementatio n, which sup p o rts a timeo ut callb ack.
Fro m Camel 2.16 o nward s, the strateg y can also b e a
PreCompletionAwareAg gregationStrategy
imp lementatio n. It runs the co mp letio n check in a p reco mp letio n mo d e.
A reference to lo o kup the
Ag g reg ati o nStrateg y
in the Reg istry.
24 8
O pt ion co mpl eti o nSi ze
Default co mpl eti o nT i meo ut co mpl eti o nInterval
CHAPT ER 8 . MESSAG E RO UT ING
Descript ion
Numb er o f messag es ag g reg ated b efo re the ag g reg atio n is co mp lete.
This o p tio n can b e set as either a fixed value o r using an Exp ressio n which allo ws yo u to evaluate a size d ynamically - will use
Integ er
as result. If b o th are set Camel will fallb ack to use the fixed value if the
Exp ressio n result was
nul l
o r
0
.
Time in millis that an ag g reg ated exchang e sho uld b e inactive b efo re its co mp lete. This o p tio n can b e set as either a fixed value o r using an Exp ressio n which allo ws yo u to evaluate a timeo ut d ynamically - will use
Lo ng
as result. If b o th are set Camel will fallb ack to use the fixed value if the
Exp ressio n result was
nul l
o r
0
. Yo u canno t use this o p tio n to g ether with co mp letio nInterval, o nly o ne o f the two can b e used .
A rep eating p erio d in millis b y which the ag g reg ato r will co mp lete all current ag g reg ated exchang es.
Camel has a b ackg ro und task which is trig g ered every p erio d . Yo u canno t use this o p tio n to g ether with co mp letio nTimeo ut, o nly o ne o f them can b e used .
24 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
O pt ion co mpl eti o nP red i cate
Default co mpl eti o nFro mBatchC o nsumer fal se
Descript ion
Sp ecifies a p red icate (o f
o rg . apache. camel . P re d i cate
typ e), which sig nals when an ag g reg ated exchang e is co mp lete.
Alternatively, instead o f setting this o p tio n, yo u can d efine a custo m
Ag g reg ati o nStrateg y
that imp lements the
P red i cate
interface, in which case the
Ag g reg ati o nStrateg y
will b e used as the co mp letio n p red icate.
This o p tio n is if the exchang es are co ming fro m a Batch Co nsumer. Then when enab led the
will use the b atch size d etermined b y the
Batch Co nsumer in the messag e head er
C amel BatchSi ze
. See mo re d etails at Batch
Co nsumer. This can b e used to ag g reg ate all files co nsumed fro m a see
File
end p o int in that g iven p o ll.
250
O pt ion Default eag erC heckC o mpl eti o n fal se fo rceC o mpl eti o nO nSto p fal se g ro upExchang es fal se
CHAPT ER 8 . MESSAG E RO UT ING
Descript ion
Whether o r no t to eag er check fo r co mp letio n when a new inco ming Exchang e has b een received . This o p tio n influences the b ehavio r o f the
co mpl eti o nP red i cate
o p tio n as the Exchang e b eing p assed in chang es acco rd ing ly. When
fal se
the Exchang e p assed in the
Pred icate is the aggregated
Exchang e which means any info rmatio n yo u may sto re o n the ag g reg ated Exchang e fro m the
Ag g reg ati o nStrateg y
is availab le fo r the Pred icate.
When
true
the Exchang e p assed in the Pred icate is the incoming Exchang e, which means yo u can access d ata fro m the inco ming Exchang e.
If
true
, co mp lete all ag g reg ated exchang es when the current ro ute co ntext is sto p p ed .
If enab led then Camel will g ro up all ag g reg ated
Exchang es into a sing le co mb ined
o rg . apache. camel . i mp l . G ro uped Exchang e
ho ld er class that ho ld s all the ag g reg ated Exchang es.
And as a result o nly o ne
Exchang e is b eing sent o ut fro m the ag g reg ato r. Can b e used to co mb ine many inco ming Exchang es into a sing le o utp ut Exchang e witho ut co d ing a custo m
Ag g reg ati o nStrateg y
yo urself.
251
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
O pt ion i g no reInval i d C o rrel ati o nKeys
Default fal se cl o seC o rrel ati o nKeyO nC o mpl eti o n d i scard O nC o mpl eti o n
T i meo ut ag g reg ati o nR epo si to r y fal se
Descript ion
Whether o r no t to ig no re co rrelatio n keys which co uld no t b e evaluated to a value.
By d efault Camel will thro w an Excep tio n, b ut yo u can enab le this o p tio n and ig no re the situatio n instead .
Whether o r no t late
Exchang es sho uld b e accep ted o r no t. Yo u can enab le this to ind icate that if a co rrelatio n key has alread y b een co mp leted , then any new exchang es with the same co rrelatio n key b e d enied .
Camel will then thro w a
cl o sed C o rrel ati o nKey
Excepti o n
excep tio n.
When using this o p tio n yo u p ass in a
i nteg er
which is a numb er fo r a LRUCache which keep s that last X numb er o f clo sed co rrelatio n keys. Yo u can p ass in 0 o r a neg ative value to ind icate a unb o und ed cache. By p assing in a numb er yo u are ensured that cache wo nt g ro wn to o b ig if yo u use a lo g o f d ifferent co rrelatio n keys.
Camel 2 .5: Whether o r no t exchang es which co mp lete d ue to a timeo ut sho uld b e d iscard ed . If enab led , then when a timeo ut o ccurs the ag g reg ated messag e will
not b e sent o ut b ut d ro p p ed
(d iscard ed ).
Allo ws yo u to p lug in yo u o wn imp lementatio n o f
o rg . apache. camel . spi
. Ag g reg ati o nR epo si t o ry
which keep s track o f the current inflig ht ag g reg ated exchang es. Camel uses b y d efault a memo ry b ased imp lementatio n.
252
O pt ion ag g reg ati o nR epo si to r yR ef
Default paral l el P ro cessi ng fal se executo rServi ce executo rServi ceR ef ti meo utC heckerExecut o rServi ce ti meo utC heckerExecut o rServi ceR ef
CHAPT ER 8 . MESSAG E RO UT ING
Descript ion
Reference to lo o kup a
ag g reg ati o nR epo si to r y
in the Reg istry.
When ag g reg ated are co mp leted they are b eing send o ut o f the ag g reg ato r.
This o p tio n ind icates whether o r no t Camel sho uld use a thread p o o l with multip le thread s fo r co ncurrency. If no custo m thread p o o l has b een sp ecified then Camel creates a d efault p o o l with 10 co ncurrent thread s.
If using
paral l el P ro cessi ng
yo u can sp ecify a custo m thread p o o l to b e used . In fact also if yo u are no t using
paral l el P ro cessi ng
this custo m thread p o o l is used to send o ut ag g reg ated exchang es as well.
Reference to lo o kup a
executo rServi ce
in the
Reg istry
If using o ne o f the
co mpl eti o nT i meo ut
,
co mpl eti o nT i meo utExp ressi o n
, o r
co mpl eti o nInterval
o p tio ns, a b ackg ro und thread is created to check fo r the co mp letio n fo r every ag g reg ato r. Set this o p tio n to p ro vid e a custo m thread p o o l to b e used rather than creating a new thread fo r every ag g reg ato r.
Reference to lo o k up a
ti meo utC heckerExecut o rServi ce
in the reg istry.
253
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
O pt ion co mpl eteAl l O nSto p
Default o pti mi sti cLo cki ng o pti mi sti cLo ckR etryP o l i cy
Usi ng a
Ag g reg ateC o ntro l l er fal se
Descript ion
When yo u sto p the
Ag g reg ato r, this o p tio n allo ws it to co mp lete all p end ing exchang es fro m the ag g reg atio n rep o sito ry.
Turns o n o p timistic lo cking , which can b e used in co mb inatio n with an ag g reg atio n rep o sito ry.
Co nfig ures the retry p o licy fo r o p timistic lo cking .
Camel 2.16 allo ws yo u to use an external so urce to co mp lete g ro up s o r all g ro up s. This can b e d o ne using Java o r JMX API.
8.6. RESEQUENCER
Overview
The resequencer pattern, shown in
Figure 8.8, “ 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.
Fig u re 8.8. Reseq u en cer Pat t ern
254
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.
CHAPT ER 8 . MESSAG E RO UT ING
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.
Bat ch 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
T i meStamp 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 BatchR eseq uencerC o nfi g 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>
Bat ch opt ions
255
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Table 8.4, “ Batch Resequencer Options”
shows the options that are available in batch mode only.
T ab le 8.4 . Bat ch Reseq u en cer O p t io n s
Java DSL XML DSL al l o wD upl i cate s() batchco nfi g /@ al l o w
D upl i cates
Default fal se reverse() batchco nfi g /@ revers e fal se
Descript ion
If
true
, d o no t d iscard d up licate messag es fro m the b atch (where
duplicate means that the messag e exp ressio n evaluates to the same value).
If
true
, p ut the messag es in reverse o rd er (where the d efault o rd ering ap p lied to a messag e exp ressio n is b ased o n Java's string lexical o rd ering , as d efined b y
.
String .co mp areTo () )
For example, if you want to resequence messages from JMS queues based on
JMSP ri o ri ty, you would need to combine the options, al l o wD upl i cates 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).allowDuplica
tes().reverse()
.to("mock:result");
St ream resequencing
To enable the stream resequencing algorithm, you must append stream() to the
reseq uence() DSL command. For example, to resequence incoming messages based on the value of a sequence number in the seq num header, you define a DSL route as follows:
256
CHAPT ER 8 . MESSAG E RO UT ING
from("direct:start").resequence(header("seqnum")).stream().to("mock:re sult");
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 StreamR eseq uencerC o nfi g 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 l o ng , 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("m ock: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:
257
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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 C amel Exchang eExcepti o n 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
i g no reInval i d Exchang es 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)
.i g no reInval i d Exchang es()
.to("mock:result");
Reject old messages
The rejectO l d option can be used to prevent messages being sent out of order, regardless of the mechanism used to resequence messages. When the rejectO l d option is enabled, the resequencer rejects an incoming message (by throwing a
Messag eR ejected Excepti o n 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("moc k:error").end()
.resequence(header("seqno")).stream().timeout(1000). rejectO l d ()
.to("mock:result");
258
8.7. ROUT ING SLIP
Overview
The routing slip pattern, shown in Figure 8.9, “ 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.
Fig u re 8.9 . Ro u t in g Slip Pat t ern
CHAPT ER 8 . MESSAG E RO UT ING
T he slip header
The routing slip appears in a user-defined header, where the header value is a commaseparated 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
T he current endpoint propert y
From Camel 2.5 the Routing Slip will set a property (Exchang e. SLIP _END P O INT ) 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
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 Dynamic Router pattern instead.
Java DSL example
The following route takes messages from the d i rect: a endpoint and reads a routing slip from the aR o uti ng Sl i pHead er 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 ro uti ng Sl i p().
The following example defines a route that uses the aR o uti ng Sl i pHead er header key for the routing slip and uses the # character as the URI delimiter: from("direct:c").routingSlip("aRoutingSlipHeader", "#");
259
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
XML configurat ion 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 endpoint s
The
Routing Slip now supports i g no reInval i d End po i nts, which the
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 i g no reInval i d End po i nts attribute on the <ro uti ng Sl i p> tag:
<route>
<from uri="direct:a"/>
<routingSlip ignoreInvalidEndpoints="true">
<headerName>myHeader</headerName>
</routingSlip>
</route>
Consider the case where myHead er contains the two endpoints, d i rect: fo o ,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.
Opt ions
The ro uti ng Sl i p DSL command supports the following options:
Name uri D el i mi ter
Default Value
,
Descript ion
Delimiter used if the
Exp ressio n returned multip le end p o ints.
260
i g no reInval i d End po i nts fal se cacheSi ze 0
CHAPT ER 8 . MESSAG E RO UT ING
If an end p o int uri co uld no t b e reso lved , sho uld it b e ig no red . O therwise Camel will thro wn an excep tio n stating the end p o int uri is no t valid .
Camel 2 .1 3.1 /2 .1 2 .4 : Allo ws to co nfig ure the cache size fo r the ProducerCache which caches p ro d ucers fo r reuse in the ro uting slip . Will b y d efault use the d efault cache size which is 0 . Setting the value to -1 allo ws to turn o ff the cache all to g ether.
8.8. T HROT T LER
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 thro ttl e() 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
ti meP eri o d Mi l l i s() 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 configurat ion 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>
261
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri="mock:result"/>
</throttle>
</route>
</camelContext>
Dynamically changing maximum request s per period
Availab le o s o f 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. l ang . Lo ng type. In the example below we use a header from the message to determine the maximum requests per period. If the header
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");
No t e
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.
Opt ions
The thro ttl e DSL command supports the following options:
Name Default Value Descript ion
262
maxi mumR eq uestsP erP e ri o d ti meP eri o d Mi l l i s asyncD el ayed executo rServi ceR ef
10 0 0 fal se cal l erR unsWhenR eject ed true
CHAPT ER 8 . MESSAG E RO UT ING
Maximum numb er o f req uests p er p erio d to thro ttle. This o p tio n must b e p ro vid ed and a p o sitive numb er. No tice, in the XML
DSL, fro m Camel 2 .8 o nward s this o p tio n is co nfig ured using an
Exp ressio n instead o f an attrib ute.
The time p erio d in millis, in which the thro ttler will allo w at mo st
maxi mumR eq uestsP erP e ri o d
numb er o f messag es.
Camel 2 .4 : If enab led then any messag es which is d elayed hap p ens asynchro no usly using a sched uled thread p o o l.
Camel 2 .4 : Refers to a custo m Thread Po o l to b e used if
asyncD el ay
has b een enab led .
Camel 2 .4 : Is used if
asyncD el ayed
was enab led . This co ntro ls if the caller thread sho uld execute the task if the thread p o o l 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 d el ay() 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");
263
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Alternatively, you can specify the time delay using an expression: from("seda:a").delay(header("MyDelay")).to("mock:result");
The DSL commands that follow d el ay() are interpreted as sub-clauses of d el ay().
Hence, in some contexts it is necessary to terminate the sub-clauses of delay() by inserting the end () command. For example, when d el ay() appears inside an o nExcepti o n() 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 configurat ion 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>
Creat ing a cust om 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");
264
Where the bean class could be defined as follows:
CHAPT ER 8 . MESSAG E RO UT ING
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& lt;/constant>
</delay>
<to uri="activemq:aDealyedQueue"/>
</route>
Opt ions
The delayer pattern supports the following options:
Name asyncD el ayed executo rServi ceR ef
Default Value fal se
Descript ion
Camel 2 .4 : If enab led then d elayed messag es hap p ens asynchro no usly using a sched uled thread p o o l.
Camel 2 .4 : Refers to a custo m Thread Po o l to b e used if
asyncD el ay
has b een enab led .
265
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide cal l erR unsWhenR eject ed true
Camel 2 .4 : Is used if
asyncD el ayed
was enab led . This co ntro ls if the caller thread sho uld execute the task if the thread p o o l 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, mo ck: x,
mo ck: y, mo ck: z, using a round robin load-balancing policy: from("direct:start").loadBalance().roundRobin().to("mock:x", "mock:y",
"mock:z");
XML configurat ion 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:
266
CHAPT ER 8 . MESSAG E RO UT ING
the section called “ Failover”
the section called “ Weighted round robin and weighted random”
the section called “ Custom Load Balancer”
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, mo ck: x, mo ck: y, mo ck: z, then the incoming messages are sent to the following sequence of endpoints: mo ck: x, mo ck: y, mo ck: z, mo ck: x, mo ck: y, mo ck: 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");
267
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
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>
St icky
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>
268
CHAPT ER 8 . MESSAG E RO UT ING
No t e
When you add the sticky option to the failover load balancer, the load balancer starts from the last known good endpoint.
T opic
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
Availab le as o f Ap ach e Camel 2.0 The fai l o ver load balancer is capable of trying the next processor in case an Exchange failed with an excepti o n during processing. You can configure the fai l o ver 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 o nExcepti o n exception clause.
En ab le st ream cach in g if u sin g st reams
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 fai l o ver load balancer supports the following options:
O pt ion T ype Default Descript ion
269
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide i nheri tErro rHan d l er bo o l ean true maxi mumFai l o ve rAttempts i nt -1
Camel 2 .3: Sp ecifies whether to use the
erro rHand l er
co nfig ured o n the ro ute. If yo u want to fail o ver immed iately to the next end p o int, yo u sho uld d isab le this o p tio n (value o f
fal se
). If yo u enab le this o p tio n,
Ap ache Camel will first attemp t to p ro cess the messag e using the
erro rHand l er
.
Fo r examp le, the
erro rHand l er
mig ht b e co nfig ured to red eliver messag es and use d elays b etween attemp ts. Ap ache
Camel will initially try to red eliver to the
original end p o int, and o nly fail o ver to the next end p o int when the
erro rHand l er
is exhausted .
Camel 2 .3: Sp ecifies the maximum numb er o f attemp ts to fail o ver to a new end p o int. The value,
0
, imp lies that no failo ver attemp ts are mad e and the value,
-1
, imp lies an infinite numb er o f failo ver attemp ts.
270
ro und R o bi n bo o l ean fal se
CHAPT ER 8 . MESSAG E RO UT ING
Camel 2 .3: Sp ecifies whether the
fai l o ver
lo ad b alancer sho uld o p erate in ro und ro b in mo d e o r no t. If no t, it will always start fro m the first end p o int when a new messag e is to b e p ro cessed . In o ther wo rd s it restarts fro m the to p fo r every messag e. If ro und ro b in is enab led , it keep s state and co ntinues with the next end p o int in a ro und ro b in fashio n. When using ro und ro b in it will no t stick to last kno wn g o o d end p o int, it will always p ick the next end p o int to use.
The following example is configured to fail over, only if an IO Excepti o n 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:
271
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<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>
Weight ed round robin and weight ed 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.
272
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
CHAPT ER 8 . MESSAG E RO UT ING
endpoint with respect to the others.
The parameters that can be used are
T ab le 8.5. Weig h t ed O p t io n s
O pt ion ro und R o bi n
T ype bo o l ean d i stri buti o nR a ti o D el i mi ter
Stri ng
,
Default fal se
Descript ion
The d efault value fo r ro und -ro b in is
fal se
. In the ab sence o f this setting o r p arameter, the lo ad -b alancing alg o rithm used is rand o m.
The
d i stri buti o nR a ti o D el i mi ter
is the d elimiter used to sp ecify the
d i stri buti o nR a ti o
. If this attrib ute is no t sp ecified , co mma
,
is the d efault d elimiter.
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=":" />
273
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri="mock:x"/>
<to uri="mock:y"/>
<to uri="mock:z"/>
</loadBalance>
</route>
Cust om 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>
274
To implement a custom load balancer you can extend some support classes such as
Lo ad Bal ancerSuppo rt and Si mpl eLo ad Bal ancerSuppo rt. The former supports the asynchronous routing engine, and the latter does not. Here is an example:
CHAPT ER 8 . MESSAG E RO UT ING
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 hal fO penAfter 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>
275
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
8.11. MULT ICAST
Overview
The multicast pattern, shown in Figure 8.10, “ Multicast Pattern”
pattern. This is in contrast to recipient list, which is only compatible with the InOnly exchange pattern.
Fig u re 8.10. Mu lt icast Pat t ern
276
Mult icast wit h a cust om aggregat ion st rat egy
Whereas the multicast processor receives multiple Out messages in response to the original request (one from each of the recipients), the original caller is only expecting to receive a
single reply. Thus, there is an inherent mismatch on the reply leg of the message exchange, and to overcome this mismatch, you must provide a custom aggregation strategy to the multicast processor. The aggregation strategy class is responsible for aggregating all of the
Out messages into a single reply message.
Consider the example of an electronic auction service, where a seller offers an item for sale to a list of buyers. The buyers each put in a bid for the item, and the seller automatically selects the bid with the highest price. You can implement the logic for distributing an offer to a fixed list of buyers using the mul ti cast() DSL command, as follows: from("cxf:bean:offer").multicast(new HighestBidAggregationStrategy()).
to("cxf:bean:Buyer1", "cxf:bean:Buyer2", "cxf:bean:Buyer3");
Where the seller is represented by the endpoint, cxf: bean: o ffer, and the buyers are represented by the endpoints, cxf: bean: Buyer1, cxf: bean: Buyer2,
cxf: bean: Buyer3. To consolidate the bids received from the various buyers, the multicast processor uses the aggregation strategy, Hi g hestBi d Ag g reg ati o nStrateg y. You can implement the Hi g hestBi d Ag g reg ati o nStrateg y in Java, as follows:
// Java
CHAPT ER 8 . MESSAG E RO UT ING
import org.apache.camel.processor.aggregate.AggregationStrategy; import org.apache.camel.Exchange; public class HighestBidAggregationStrategy implements
AggregationStrategy {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
float oldBid = oldExchange.getOut().getHeader("Bid",
Float.class);
float newBid = newExchange.getOut().getHeader("Bid",
Float.class);
return (newBid > oldBid) ? newExchange : oldExchange;
}
}
Where it is assumed that the buyers insert the bid price into a header named, Bi d . For more
details about custom aggregation strategies, see Section 8.5, “ Aggregator” .
Parallel processing
By default, the multicast processor invokes each of the recipient endpoints one after another
(in the order listed in the to () command). In some cases, this might cause unacceptably long latency. To avoid these long latency times, you have the option of enabling parallel processing by adding the paral l el P ro cessi ng () clause. For example, to enable parallel processing in the electronic auction example, define the route as follows: from("cxf:bean:offer")
.multicast(new HighestBidAggregationStrategy())
.parallelProcessing()
.to("cxf:bean:Buyer1", "cxf:bean:Buyer2", "cxf:bean:Buyer3");
Where the multicast processor now invokes the buyer endpoints, using a thread pool that has one thread for each of the endpoints.
If you want to customize the size of the thread pool that invokes the buyer endpoints, you can invoke the executo rServi ce() method to specify your own custom executor service.
For example: from("cxf:bean:offer")
.multicast(new HighestBidAggregationStrategy())
.executorService(MyExecutor)
.to("cxf:bean:Buyer1", "cxf:bean:Buyer2", "cxf:bean:Buyer3");
Where MyExecutor is an instance of java.util.concurrent.ExecutorService
type.
When the exchange has an InOut pattern, an aggregation strategy is used to aggregate reply messages. The default aggregation strategy takes the latest reply message and discards earlier replies. For example, in the following route, the custom strategy,
MyAg g reg ati o nStrateg y, is used to aggregate the replies from the endpoints, d i rect: a,
d i rect: b, and d i rect: c:
277
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
from("direct:start")
.multicast(new MyAggregationStrategy())
.parallelProcessing()
.timeout(500)
.to("direct:a", "direct:b", "direct:c")
.end()
.to("mock:result");
XML configurat ion example
The following example shows how to configure a similar route in XML, where the route uses a custom aggregation strategy and a custom thread executor:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.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">
<route>
<from uri="cxf:bean:offer"/>
<multicast strategyRef="highestBidAggregationStrategy"
parallelProcessing="true"
threadPoolRef="myThreadExcutor">
<to uri="cxf:bean:Buyer1"/>
<to uri="cxf:bean:Buyer2"/>
<to uri="cxf:bean:Buyer3"/>
</multicast>
</route>
</camelContext>
<bean id="highestBidAggregationStrategy" class="com.acme.example.HighestBidAggregationStrategy"/>
<bean id="myThreadExcutor" class="com.acme.example.MyThreadExcutor"/>
</beans>
278
Where both the paral l el P ro cessi ng attribute and the thread P o o l R ef attribute are optional. It is only necessary to set them if you want to customize the threading behavior of the multicast processor.
Apply cust om processing t o t he out going messages
Before multicast sends a message to one of the recipient endpoints, it creates a message replica, which is a shallow copy of the original message. If you want to perform some custom processing on each message replica before the replica is sent to its endpoint, you can
CHAPT ER 8 . MESSAG E RO UT ING
invoke the o nP repare DSL command in the mul ti cast clause. The o nP repare 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
C usto mP ro c processor is invoked on the message sent to d i rect: a and the C usto mP ro c processor is also invoked on the message sent to d i rect: b.
from("direct:start")
.multicast().onPrepare(new CustomProc())
.to("direct:a").to("direct:b");
A common use case for the o nP repare DSL command is to perform a deep copy of some or all elements of a message. For example, the following C usto mP ro c 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. d eepC o py().
// 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.
}
}
No t e
Although the mul ti cast syntax allows you to invoke the pro cess DSL command in the mul ti cast clause, this does not make sense semantically and it does not have the same effect as o nP repare (in fact, in this context, the pro cess DSL command has no effect).
Using onPrepare t o execut e cust om logic when preparing messages
The
will copy the source Exchange and multicast each copy. However the copy is a shallow copy, so in case you have mutateable message bodies, then any changes will be visible by the other copied messages. If you want to use a deep clone copy then you need to use a custom o nP repare which allows you to do this using the Processor interface.
Notice the o nP repare can be used for any kind of custom logic which you would like to execute before the Exchang e is being multicasted.
279
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
Its best practice to design for immutable objects.
280
For example if you have a mutable message body as this Animal class: public class Animal implements Serializable {
private int id;
private String name;
public Animal() {
}
public Animal(int id, String name) {
this.id = id;
this.name = name;
}
public Animal deepClone() {
Animal clone = new Animal();
clone.setId(getId());
clone.setName(getName());
return clone;
}
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;
}
@ Override
public String toString() {
return id + " " + name;
}
}
Then we can create a deep clone processor which clones the message body: public class AnimalDeepClonePrepare implements Processor {
public void process(Exchange exchange) throws Exception {
CHAPT ER 8 . MESSAG E RO UT ING
Animal body = exchange.getIn().getBody(Animal.class);
// do a deep clone of the body which wont affect when doing multicasting
Animal clone = body.deepClone();
exchange.getIn().setBody(clone);
}
}
Then we can use the AnimalDeepClonePrepare class in the
route using the
o nP repare option as shown: from("direct:start")
.multicast().onPrepare(new
AnimalDeepClonePrepare()).to("direct:a").to("direct:b");
And the same example in XML DSL
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<!-- use on prepare with multicast -->
<multicast onPrepareRef="animalDeepClonePrepare">
<to uri="direct:a"/>
<to uri="direct:b"/>
</multicast>
</route>
<route>
<from uri="direct:a"/>
<process ref="processorA"/>
<to uri="mock:a"/>
</route>
<route>
<from uri="direct:b"/>
<process ref="processorB"/>
<to uri="mock:b"/>
</route>
</camelContext>
<!-- the on prepare Processor which performs the deep cloning -->
<bean id="animalDeepClonePrepare" class="org.apache.camel.processor.AnimalDeepClonePrepare"/>
<!-- processors used for the last two routes, as part of unit test -->
<bean id="processorA" class="org.apache.camel.processor.MulticastOnPrepareTest$ProcessorA"/>
<bean id="processorB" class="org.apache.camel.processor.MulticastOnPrepareTest$ProcessorB"/>
Opt ions
281
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The mul ti cast DSL command supports the following options:
Name strateg yR ef strateg yMetho d Name strateg yMetho d Al l o wN ul l paral l el P ro cessi ng
Default Value fal se fal se
Descript ion
Refers to an
Ag g reg atio nStrateg y to b e used to assemb le the rep lies fro m the multicasts, into a sing le o utg o ing messag e fro m the
Camel will use the last rep ly as the o utg o ing messag e.
This o p tio n can b e used to exp licitly sp ecify the metho d name to use, when using
PO JO s as the
Ag g reg ati o nStrateg y
.
This o p tio n can b e used , when using PO JO s as the
Ag g reg ati o nStrateg y
. If
fal se
, the
ag g reg ate
metho d is no t used , when there is no d ata to enrich. If
true
,
nul l
values are used fo r the
o l d Exchang e
, when there is no d ata to enrich.
If enab led , send ing messag es to the multicasts o ccurs co ncurrently. No te the caller thread will still wait until all messag es has b een fully p ro cessed , b efo re it co ntinues. Its o nly the send ing and p ro cessing the rep lies fro m the multicasts which hap p ens co ncurrently.
282
paral l el Ag g reg ate fal se executo rServi ceR ef sto pO nExcepti o n streami ng fal se fal se
CHAPT ER 8 . MESSAG E RO UT ING
If enab led , the
ag g reg ate
metho d o n
Ag g reg ati o nStrateg y
can b e called co ncurrently.
No te that this req uires the imp lementatio n o f
Ag g reg ati o nStrateg y
to b e thread -safe. By d efault, this o p tio n is
fal se
, which means that Camel auto matically synchro nizes calls to the
ag g reg ate
metho d . In so me use-cases, ho wever, yo u can imp ro ve p erfo rmance b y imp lementing
Ag g reg ati o nStrateg y
as thread -safe and setting this o p tio n to
true
.
Refers to a custo m Thread
Po o l to b e used fo r p arallel p ro cessing . No tice if yo u set this o p tio n, then p arallel p ro cessing is auto matic imp lied , and yo u d o no t have to enab le that o p tio n as well.
Camel 2 .2 : Whether o r no t to sto p co ntinue p ro cessing immed iately when an excep tio n o ccurred . If d isab le, then Camel will send the messag e to all multicasts reg ard less if o ne o f them failed . Yo u can d eal with excep tio ns in the
Ag g reg atio nStrateg y class where yo u have full co ntro l ho w to hand le that.
If enab led then Camel will p ro cess rep lies o ut-o f-o rd er, eg in the o rd er they co me b ack. If d isab led , Camel will p ro cess rep lies in the same o rd er as multicasted .
283
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide ti meo ut o nP repareR ef shareUni tO fWo rk fal se
Camel 2 .5: Sets a to tal timeo ut sp ecified in millis. If
hasn't b een ab le to send and p ro cess all rep lies within the g iven timeframe, then the timeo ut trig g ers and the
b reaks o ut and co ntinues.
No tice if yo u p ro vid e a
Timeo utAwareAg g reg atio nSt rateg y then the
ti meo ut
metho d is invo ked b efo re b reaking o ut.
Camel 2 .8 : Refers to a custo m Pro cesso r to p rep are the co p y o f the
Exchang e each multicast will receive. This allo ws yo u to d o any custo m lo g ic, such as d eep -clo ning the messag e p aylo ad if that's need ed etc.
Camel 2 .8 : Whether the unit o f wo rk sho uld b e shared .
See the same o p tio n o n
fo r mo re d etails.
8.12. COMPOSED MESSAGE PROCESSOR
Composed Message Processor
The composed message processor pattern, as shown in Figure 8.11, “ Composed Message
Processor Pattern” , allows you to process a composite message by splitting it up, routing
the sub-messages to appropriate destinations, and then re-aggregating the responses back into a single message.
Fig u re 8.11. Co mp o sed Messag e Pro cesso r Pat t ern
284
CHAPT ER 8 . MESSAG E RO UT ING
Java DSL example
The following example checks that a multipart order can be filled, where each part of the order requires a check to be made at a different inventory:
// split up the order so individual OrderItems can be validated by the appropriate bean from("direct:start")
.split().body()
.choice()
.when().method("orderItemHelper", "isWidget")
.to("bean:widgetInventory")
.otherwise()
.to("bean:gadgetInventory")
.end()
.to("seda:aggregate");
// collect and re-assemble the validated OrderItems into an order again from("seda:aggregate")
.aggregate(new MyOrderAggregationStrategy())
.header("orderId")
.completionTimeout(1000L)
.to("mock:result");
XML DSL example
The preceding route can also be written in XML DSL, as follows:
<route>
<from uri="direct:start"/>
<split>
<simple>body</simple>
<choice>
<when>
<method bean="orderItemHelper" method="isWidget"/>
<to uri="bean:widgetInventory"/>
</when>
<otherwise>
285
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri="bean:gadgetInventory"/>
</otherwise>
</choice>
<to uri="seda:aggregate"/>
</split>
</route>
<route>
<from uri="seda:aggregate"/>
<aggregate strategyRef="myOrderAggregatorStrategy" completionTimeout="1000">
<correlationExpression>
<simple>header.orderId</simple>
</correlationExpression>
<to uri="mock:result"/>
</aggregate>
</route>
Processing st eps
Processing starts by splitting the order, using a Splitter . The Splitter then sends individual
, which routes messages based on the item type.
Widget items get sent for checking in the wi d g etInvento ry bean and gadget items get sent to the g ad g etInvento ry bean. Once these O rd erItems have been validated by the
appropriate bean, they are sent on to the Aggregator which collects and re-assembles the
validated O rd erItems into an order again.
Each received order has a header containing an order ID. We make use of the order ID during the aggregation step: the . head er("o rd erId ") qualifier on the ag g reg ate() DSL command instructs the aggregator to use the header with the key, o rd erId , as the correlation expression.
For full details, check the example source here:
8.13. SCAT T ER-GAT HER
Scat t er-Gat her
The scatter-gather pattern, as shown in Figure 8.12, “ Scatter-Gather Pattern” , enables you to
route messages to a number of dynamically specified recipients and re-aggregate the responses back into a single message.
Fig u re 8.12. Scat t er- G at h er Pat t ern
286
CHAPT ER 8 . MESSAG E RO UT ING
Dynamic scat t er-gat her example
The following example outlines an application that gets the best quote for beer from several
different vendors. The examples uses a dynamic Recipient List to request a quote from all
vendors and an
Aggregator to pick the best quote out of all the responses. The routes for
this application are defined as follows:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<recipientList>
<header>listOfVendors</header>
</recipientList>
</route>
<route>
<from uri="seda:quoteAggregator"/>
<aggregate strategyRef="aggregatorStrategy" completionTimeout="1000">
<correlationExpression>
<header>quoteRequestId</header>
</correlationExpression>
<to uri="mock:result"/>
</aggregate>
</route>
</camelContext>
In the first route, the Recipient List looks at the l i stO fVend o rs header to obtain the list of
recipients. Hence, the client that sends messages to this application needs to add a
l i stO fVend o rs header to the message.
Example 8.1, “ Messaging Client Sample”
shows some sample code from a messaging client that adds the relevant header data to outgoing messages.
Examp le 8.1. Messag in g Clien t Samp le
287
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Map<String, Object> headers = new HashMap<String, Object>(); headers.put("listOfVendors", "bean:vendor1, bean:vendor2, bean:vendor3"); headers.put("quoteRequestId", "quoteRequest-1"); template.sendBodyAndHeaders("direct:start", "<quote_request item=\"beer\"/>", headers);
288
The message would be distributed to the following endpoints: bean: vend o r1,
bean: vend o r2, and bean: vend o r3. These beans are all implemented by the following class: public class MyVendor {
private int beerPrice;
@ Produce(uri = "seda:quoteAggregator")
private ProducerTemplate quoteAggregator;
public MyVendor(int beerPrice) {
this.beerPrice = beerPrice;
}
public void getQuote(@ XPath("/quote_request/@ item") String item,
Exchange exchange) throws Exception {
if ("beer".equals(item)) {
exchange.getIn().setBody(beerPrice);
quoteAggregator.send(exchange);
} else {
throw new Exception("No quote available for " + item);
}
}
}
The bean instances, vend o r1, vend o r2, and vend o r3, are instantiated using Spring XML syntax, as follows:
<bean id="aggregatorStrategy" class="org.apache.camel.spring.processor.scattergather.LowestQuoteAggr
egationStrategy"/>
<bean id="vendor1" class="org.apache.camel.spring.processor.scattergather.MyVendor">
<constructor-arg>
<value>1</value>
</constructor-arg>
</bean>
<bean id="vendor2" class="org.apache.camel.spring.processor.scattergather.MyVendor">
<constructor-arg>
<value>2</value>
</constructor-arg>
</bean>
CHAPT ER 8 . MESSAG E RO UT ING
<bean id="vendor3" class="org.apache.camel.spring.processor.scattergather.MyVendor">
<constructor-arg>
<value>3</value>
</constructor-arg>
</bean>
Each bean is initialized with a different price for beer (passed to the constructor argument).
When a message is sent to each bean endpoint, it arrives at the MyVend o r. g etQ uo te method. This method does a simple check to see whether this quote request is for beer and then sets the price of beer on the exchange for retrieval at a later step. The message is forwarded to the next step using POJO Producing (see the @Produce annotation).
At the next step, we want to take the beer quotes from all vendors and find out which one was
the best (that is, the lowest). For this, we use an Aggregator
with a custom aggregation strategy. The
Aggregator needs to identify which messages are relevant to the current quote,
which is done by correlating messages based on the value of the q uo teR eq uestId header
(passed to the co rrel ati o nExpressi o n). As shown in Example 8.1, “ Messaging Client
Sample” , the correlation ID is set to q uo teR eq uest-1 (the correlation ID should be
unique). To pick the lowest quote out of the set, you can use a custom aggregation strategy like the following: public class LowestQuoteAggregationStrategy implements
AggregationStrategy {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
// the first time we only have the new exchange
if (oldExchange == null) {
return newExchange;
}
if (oldExchange.getIn().getBody(int.class) < newExchange.getIn().getBody(int.class)) {
return oldExchange;
} else {
return newExchange;
}
}
}
St at ic scat t er-gat her example
You can specify the recipients explicitly in the scatter-gather application by employing a
static Recipient List . The following example shows the routes you would use to implement a
static scatter-gather scenario: from("direct:start").multicast().to("seda:vendor1", "seda:vendor2",
"seda:vendor3"); from("seda:vendor1").to("bean:vendor1").to("seda:quoteAggregator"); from("seda:vendor2").to("bean:vendor2").to("seda:quoteAggregator"); from("seda:vendor3").to("bean:vendor3").to("seda:quoteAggregator");
289
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
290
from("seda:quoteAggregator")
.aggregate(header("quoteRequestId"), new
LowestQuoteAggregationStrategy()).to("mock:result")
8.14 . LOOP
Loop
The loop pattern enables you to process a message multiple times. It is used mainly for testing.
Def au lt mo d e
Notice by default the loop uses the same exchange throughout the looping. So the
result from the previous iteration is used for the next (eg Pipes and Filters ). From
Camel 2.8 onwards you can enable copy mode instead. See the options table for more details.
Exchange propert ies
On each loop iteration, two exchange properties are set, which can optionally be read by any processors included in the loop.
Propert y
C amel Lo o pSi ze
C amel Lo o pInd ex
Descript ion
Ap ache Camel 2.0 : To tal numb er o f lo o p s
Ap ache Camel 2.0 : Ind ex o f the current iteratio n (0 b ased )
Java DSL examples
The following examples show how to take a request from the d i rect: x endpoint and then send the message repeatedly to mo ck: resul t. The number of loop iterations is specified either as an argument to l o o p() or by evaluating an expression at run time, where the expression must evaluate to an i nt (or else a R unti meC amel Excepti o n is thrown).
The following example passes the loop count as a constant: from("direct:a").loop(8).to("mock:result");
The following example evaluates a simple expression to determine the loop count: from("direct:b").loop(header("loop")).to("mock:result");
CHAPT ER 8 . MESSAG E RO UT ING
The following example evaluates an XPath expression to determine the loop count: from("direct:c").loop().xpath("/hello/@ times").to("mock:result");
XML configurat ion example
You can configure the same routes in Spring XML.
The following example passes the loop count as a constant:
<route>
<from uri="direct:a"/>
<loop>
<constant>8</constant>
<to uri="mock:result"/>
</loop>
</route>
The following example evaluates a simple expression to determine the loop count:
<route>
<from uri="direct:b"/>
<loop>
<header>loop</header>
<to uri="mock:result"/>
</loop>
</route>
Using copy mode
Now suppose we send a message to d i rect: start endpoint containing the letter A. The output of processing this route will be that, each mo ck: l o o p endpoint will receive AB as message.
from("direct:start")
// instruct loop to use copy mode, which mean it will use a copy of the input exchange
// for each loop iteration, instead of keep using the same exchange all over
.loop(3).copy()
.transform(body().append("B"))
.to("mock:loop")
.end()
.to("mock:result");
However if we do not enable copy mode then mo ck: l o o p will receive AB, ABB, ABBB messages.
from("direct:start")
// by default loop will keep using the same exchange so on the
291
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
2nd and 3rd iteration its
// the same exchange that was previous used that are being looped all over
.loop(3)
.transform(body().append("B"))
.to("mock:loop")
.end()
.to("mock:result");
The equivalent example in XML DSL in copy mode is as follows:
<route>
<from uri="direct:start"/>
<!-- enable copy mode for loop eip -->
<loop copy="true">
<constant>3</constant>
<transform>
<simple>${body}B</simple>
</transform>
<to uri="mock:loop"/>
</loop>
<to uri="mock:result"/>
</route>
Opt ions
The l o o p DSL command supports the following options:
Name co py
Default Value fal se
Descript ion
Camel 2 .8 : Whether o r no t co p y mo d e is used . If
fal se
then the same
Exchang e is b eing used thro ug ho ut the lo o p ing . So the result fro m the p revio us iteratio n will b e visible fo r the next iteratio n. Instead yo u can enab le co p y mo d e, and then each iteratio n is
restarting with a fresh co p y o f the inp ut
.
8.15. SAMPLING
Sampling T hrot t ler
292
CHAPT ER 8 . MESSAG E RO UT ING
A sampling throttler allows you to extract a sample of exchanges from the traffic through a route. It is configured with a sampling period during which only a single exchange is allowed to pass through. All other exchanges will be stopped.
By default, the sample period is 1 second.
Java DSL example
Use the sampl e() DSL command to invoke the sampler as follows:
// Sample with default sampling period (1 second) from("direct:sample")
.sample()
.to("mock:result");
// Sample with explicitly specified sample period from("direct:sample-configured")
.sample(1, TimeUnit.SECONDS)
.to("mock:result");
// Alternative syntax for specifying sampling period from("direct:sample-configured-via-dsl")
.sample().samplePeriod(1).timeUnits(TimeUnit.SECONDS)
.to("mock:result"); from("direct:sample-messageFrequency")
.sample(10)
.to("mock:result"); from("direct:sample-messageFrequency-via-dsl")
.sample().sampleMessageFrequency(5)
.to("mock:result");
Spring XML example
In Spring XML, use the sample element to invoke the sampler, where you have the option of specifying the sampling period using the sampl eP eri o d and uni ts attributes:
<route>
<from uri="direct:sample"/>
<sample samplePeriod="1" units="seconds">
<to uri="mock:result"/>
</sample>
</route>
<route>
<from uri="direct:sample-messageFrequency"/>
<sample messageFrequency="10">
<to uri="mock:result"/>
</sample>
</route>
<route>
<from uri="direct:sample-messageFrequency-via-dsl"/>
293
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<sample messageFrequency="5">
<to uri="mock:result"/>
</sample>
</route>
Opt ions
The sampl e DSL command supports the following options:
Name messag eFreq uency sampl eP eri o d uni ts
Default Value
1
SEC O ND
Descript ion
Samp les the messag e every
N'th messag e. Yo u can o nly use either freq uency o r p erio d .
Samp les the messag e every
N'th p erio d . Yo u can o nly use either freq uency o r p erio d .
Time unit as an enum o f
java. uti l . co ncurrent
. T i meUni t
fro m the JDK.
8.16. DYNAMIC ROUT ER
Dynamic Rout er
The Dynamic Router pattern, as shown in
Figure 8.13, “ Dynamic Router 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. The list of endpoints through which the message should pass is calculated dynamically at run time. Each time the message returns from an endpoint, the dynamic router calls back on a bean to discover the next endpoint in the route.
Fig u re 8.13. Dyn amic Ro u t er Pat t ern
294
CHAPT ER 8 . MESSAG E RO UT ING
In Camel 2.5 we introduced a d ynami cR o uter in the DSL, which is like a dynamic Routing
Slip that evaluates the slip on-the-fly.
Beware
You must ensure the expression used for the d ynami cR o uter (such as a bean), returns nul l to indicate the end. Otherwise, the d ynami cR o uter will continue in an endless loop.
Dynamic Rout er in Camel 2.5 onwards
From Camel 2.5, the Dynamic Router
updates the exchange property,
Exchang e. SLIP _END P O INT , with the current endpoint as it advances through the slip.
This enables you to find out how far the exchange has progressed through the slip. (It's a slip because the
implementation is based on Routing Slip ).
Java DSL
In Java DSL you can use the d ynami cR o uter as follows: from("direct:start")
// use a bean as the dynamic router
.dynamicRouter(bean(DynamicRouterTest.class, "slip"));
Which will leverage a Bean to compute the slip on-the-fly, which could be implemented as follows:
// Java
/**
* Use this method to compute dynamic where we should route next.
*
295
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
* @ param body the message body
* @ return endpoints to go, or <tt>null</tt> to indicate the end
*/ public String slip(String body) {
bodies.add(body);
invoked++;
if (invoked == 1) {
return "mock:a";
} else if (invoked == 2) {
return "mock:b,mock:c";
} else if (invoked == 3) {
return "direct:foo";
} else if (invoked == 4) {
return "mock:result";
}
// no more so return null
return null;
}
No t e
The preceding example is not thread safe. You would have to store the state on the
Exchang e to ensure thread safety.
Spring XML
The same example in Spring XML would be:
<bean id="mySlip" class="org.apache.camel.processor.DynamicRouterTest"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<dynamicRouter>
<!-- use a method call on a bean as dynamic router -->
<method ref="mySlip" method="slip"/>
</dynamicRouter>
</route>
<route>
<from uri="direct:foo"/>
<transform><constant>Bye World</constant></transform>
<to uri="mock:foo"/>
</route>
</camelContext>
Opt ions
296
CHAPT ER 8 . MESSAG E RO UT ING
The d ynami cR o uter DSL command supports the following options:
Name uri D el i mi ter i g no reInval i d End po i nts
Default Value
, fal se
Descript ion
Delimiter used if the
Exp ressio n returned multip le
end p o ints.
If an end p o int uri co uld no t b e reso lved , sho uld it b e ig no red . O therwise Camel will thro wn an excep tio n stating the end p o int uri is no t valid .
@DynamicRout er annot at ion
You can also use the @ D ynami cR o uter annotation. For example:
// Java public class MyDynamicRouter {
@ Consume(uri = "activemq:foo")
@ DynamicRouter
public String route(@ XPath("/customer/id") String customerId,
@ Header("Location") String location, Document body) {
// query a database to find the best match of the endpoint based on the input parameteres
// return the next endpoint uri, where to go. Return null to indicate the end.
}
}
The ro ute method is invoked repeatedly as the message progresses through the slip. The idea is to return the endpoint URI of the next destination. Return nul l to indicate the end.
You can return multiple endpoints if you like, just as the
Routing Slip , where each endpoint
is separated by a delimiter.
297
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 9. MESSAGE TRANSFORMATION
Ab st ract
The message transformation patterns describe how to modify the contents of messages for various purposes.
9.1. CONT ENT ENRICHER
Overview
The content enricher pattern describes a scenario where the message destination requires more data than is present in the original message. In this case, you would use a content enricher to pull in the extra data from an external resource.
Fig u re 9 .1. Co n t en t En rich er Pat t ern
298
Models of cont ent enrichment
Apache Camel supports two kinds of content enricher, as follows:
enri ch()—obtains additional data from the resource by sending a copy of the current exchange to a producer endpoint and then using the data from the resulting reply (the exchange created by the enricher is always an InOut exchange).
po l l Enri ch()—obtains the additional data by polling a consumer endpoint for data.
Effectively, the consumer endpoint from the main route and the consumer endpoint in
po l l Enri ch() are coupled, such that exchanges incoming on the main route trigger a poll of the po l l Enri ch() endpoint.
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
No t e
Content Enricher with enrich and pollEnrich pattern supports dynamic endpoint
uri s. You can compute uri s using an expression that enables you to use values from the current exchange. For example, you can poll a file with a name that is computed from the data exchange. This change breaks the XML DSL and enables you to migrate easily. The Java DSL stays backwards compatible.
Cont ent enrichment using enrich()
AggregationStrategy aggregationStrategy = ...
from("direct:start")
.enrich("direct:resource", aggregationStrategy)
.to("direct:result"); from("direct:resource")
...
The content enricher (enri ch) retrieves additional data from a resource endpoint in order to enrich an incoming message (contained in the orginal exchange). An aggregation strategy combines the original exchange and the resource exchange. The first parameter of the
Ag g reg ati o nStrateg y. ag g reg ate(Exchang e, Exchang e) method corresponds to the the original exchange, and the second parameter corresponds to the resource exchange.
The results from the resource endpoint are stored in the resource exchange's Out message.
Here is a sample template for implementing your own aggregation strategy class: public class ExampleAggregationStrategy implements AggregationStrategy
{
public Exchange aggregate(Exchange original, Exchange resource) {
Object originalBody = original.getIn().getBody();
Object resourceResponse = resource.getOut().getBody();
Object mergeResult = ... // combine original body and resource response
if (original.getPattern().isOutCapable()) {
original.getOut().setBody(mergeResult);
} else {
original.getIn().setBody(mergeResult);
}
return original;
}
}
Using this template, the original exchange can have any exchange pattern. The resource exchange created by the enricher is always an InOut exchange.
Spring XML Enrich Example
The preceding example can also be implemented in Spring XML:
299
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<camelContext id= "camel" xmlns= "http://camel.apache.org/schema/spring" >
<route>
<from uri= "direct:start" />
<enrich strategyRef= "aggregationStrategy" >
<constant>direct:resource</constant>
<to uri= "direct:result" />
</route>
<route>
<from uri= "direct:resource" />
...
</route>
</camelContext>
<bean id= "aggregationStrategy" class = "..." />
Default aggregat ion st rat egy
The aggregation strategy is optional. If you do not provide it, Apache Camel will use the body obtained from the resource by default. For example: from("direct:start")
.enrich("direct:resource")
.to("direct:result");
In the preceding route, the message sent to the d i rect: resul t endpoint contains the output from the d i rect: reso urce, because this example does not use any custom aggregation.
In XML DSL, just omit the strateg yR ef attribute, as follows:
<route>
<from uri="direct:start"/>
<enrich uri="direct:resource"/>
<to uri="direct:result"/>
</route>
Enrich Opt ions
The enri ch DSL command supports the following options:
Name uri
Default Value Descript ion
The end p o int uri fo r the external servie to enrich fro m. Yo u must use either
uri
o r
ref
.
300
ref strateg yR ef ag g reg ateO nExcepti o n shareUni tO fWo rk
false
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
Refers to the end p o int fo r the external servie to enrich fro m. Yo u must use either
uri
o r
ref
.
Refers to an
Ag g reg atio nStrateg y to b e used to merg e the rep ly fro m the external service, into a sing le o utg o ing messag e.
By d efault Camel will use the rep ly fro m the external service as o utg o ing messag e.
Refers to the Ag g reg ate metho d . The ag g reg ateO nExcep tio n enab les yo u to d eal with excep tio ns. Fo r examp le, yo u can sup p ress the excep tio n o r set a custo m messag e.
Camel 2.16 : Shares the unit o f wo rk with the p arent and the reso urce exchang e.
Ho wever, b y d efault, Enrich d o es no t share the unit o f wo rk b etween the p arent exchang e and the reso urce exchang e. Also , the reso urce exchang e has its o wn ind ivid ual unit o f wo rk.
Cont ent enrich using pollEnrich
The po l l Enri ch command treats the resource endpoint as a consumer. Instead of sending an exchange to the resource endpoint, it polls the endpoint. By default, the poll returns immediately, if there is no exchange available from the resource endpoint. For example, the following route reads a file whose name is extracted from the header of an incoming JMS message: from("activemq:queue:order")
.pollEnrich("file://order/data/additional?fileName=orderId")
.to("bean:processOrder");
And if you want to wait at most 20 seconds for the file to be ready, you can use a timeout as follows:
301
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
from("activemq:queue:order")
.pollEnrich("file://order/data/additional?fileName=orderId", 20000)
// timeout is in milliseconds
.to("bean:processOrder");
You can also specify an aggregation strategy for po l l Enri ch, as follows:
.pollEnrich("file://order/data/additional?fileName=orderId", 20000, aggregationStrategy)
No t e
The resource exchange passed to the aggregation strategy's ag g reg ate() method might be nul l , if the poll times out before an exchange is received.
Dat a f ro m cu rren t Exch an g e n o t u sed
po l l Enri ch does not access any data from the current Exchange, so that, when polling, it cannot use any of the existing headers you may have set on the
Exchange. For example, you cannot set a filename in the Exchang e. FILE_NAME header and use po l l Enri ch to consume only that file. For that, you must set the filename in the endpoint URI.
302
Polling met hods used by pollEnrich()
In general, the po l l Enri ch() enricher polls the consumer endpoint using one of the following polling methods:
recei veNo Wai t() (used by default)
recei ve() recei ve(l o ng ti meo ut)
The po l l Enri ch() command's timeout argument (specified in milliseconds) determines which method gets called, as follows:
Timeout is 0 or not specified, recei veNo Wai t is called.
Timeout is negative, recei ve is called.
Otherwise, recei ve(ti meo ut) is called.
pollEnrich example
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
In this example we enrich the message by loading the content from the file named inbox/data.txt.
from("direct:start")
.pollEnrich("file:inbox?fileName=data.txt")
.to("direct:result");
And in XML DSL you do:
<route>
<from uri="direct:start"/>
<pollEnrich uri="file:inbox?fileName=data.txt"/>
<to uri="direct:result"/>
</route>
If there is no file then the message is empty. We can use a timeout to either wait (potential forever) until a file exists, or use a timeout to wait a period. For example to wait up til 5 seconds you can do:
<route>
<from uri="direct:start"/>
<pollEnrich uri="file:inbox?fileName=data.txt" timeout="5000"/>
<to uri="direct:result"/>
</route>
PollEnrich Opt ions
The po l l Enri ch DSL command supports the following options:
Name uri ref
Default Value Descript ion
The end p o int uri fo r the external servie to enrich fro m. Yo u must use either
uri
o r
ref
.
Refers to the end p o int fo r the external servie to enrich fro m. Yo u must use either
uri
o r
ref
.
303
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide strateg yR ef ti meo ut 0
Refers to an
Ag g reg atio nStrateg y to b e used to merg e the rep ly fro m the external service, into a sing le o utg o ing messag e.
By d efault Camel will use the rep ly fro m the external service as o utg o ing messag e.
Timeo ut in millis to use when p o lling fro m the external service. See b elo w fo r imp o rtant d etails ab o ut the timeo ut.
9.2. CONT ENT FILT ER
Overview
The content filter pattern describes a scenario where you need to filter out extraneous content from a message before delivering it to its intended recipient. For example, you might employ a content filter to strip out confidential information from a message.
Fig u re 9 .2. Co n t en t Filt er Pat t ern
304
A common way to filter messages is to use an expression in the DSL, written in one of the supported scripting languages (for example, XSLT, XQuery or JoSQL).
Implement ing a cont ent filt er
A content filter is essentially an application of a message processing technique for a particular purpose. To implement a content filter, you can employ any of the following message processing techniques:
Message translator—see message translators
.
Processors—see Chapter 44, Implementing a Processor .
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
Bean integration .
XML configurat ion example
The following example shows how to configure the same route in XML:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:My.Queue"/>
<to uri="xslt:classpath:com/acme/content_filter.xsl"/>
<to uri="activemq:Another.Queue"/>
</route>
</camelContext>
Using an XPat h filt er
You can also use XPath to filter out part of the message you are interested in:
<route>
<from uri="activemq:Input"/>
<setBody><xpath resultType="org.w3c.dom.Document">//foo:bar</xpath>
</setBody>
<to uri="activemq:Output"/>
</route>
9.3. NORMALIZER
Overview
The normalizer pattern is used to process messages that are semantically equivalent, but arrive in different formats. The normalizer transforms the incoming messages into a common format.
translators , which transform the different incoming formats into a common format.
Fig u re 9 .3. No rmaliz er Pat t ern
305
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
306
Java DSL example
This example shows a Message Normalizer that converts two types of XML messages into a common format. Messages in this common format are then filtered.
Usin g t h e Flu en t Bu ild ers
// we need to normalize two types of incoming messages from("direct:start")
.choice()
.when().xpath("/employee").to("bean:normalizer?
method=employeeToPerson")
.when().xpath("/customer").to("bean:normalizer?
method=customerToPerson")
.end()
.to("mock:result");
In this case we're using a Java bean as the normalizer. The class looks like this
// Java public class MyNormalizer {
public void employeeToPerson(Exchange exchange,
@ XPath("/employee/name/text()") String name) {
exchange.getOut().setBody(createPerson(name));
}
public void customerToPerson(Exchange exchange,
@ XPath("/customer/@ name") String name) {
exchange.getOut().setBody(createPerson(name));
}
private String createPerson(String name) {
return "& lt;person name=\"" + name + "\"/>";
}
}
XML configurat ion example
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
The same example in the XML DSL
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<choice>
<when>
<xpath>/employee</xpath>
<to uri="bean:normalizer?method=employeeToPerson"/>
</when>
<when>
<xpath>/customer</xpath>
<to uri="bean:normalizer?method=customerToPerson"/>
</when>
</choice>
<to uri="mock:result"/>
</route>
</camelContext>
<bean id="normalizer" class="org.apache.camel.processor.MyNormalizer"/>
9.4 . CLAIM CHECK
Claim Check
The claim check pattern, shown in
Figure 9.4, “ Claim Check Pattern”
, allows you to replace message content with a claim check (a unique key), which can be used to retrieve the message content at a later time. The message content is stored temporarily in a persistent store like a database or file system. This pattern is very useful when message content is very large (thus it would be expensive to send around) and not all components require all information.
It can also be useful in situations where you cannot trust the information with an outside party; in this case, you can use the Claim Check to hide the sensitive portions of data.
Fig u re 9 .4 . Claim Ch eck Pat t ern
307
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Java DSL example
The following example shows how to replace a message body with a claim check and restore the body at a later step.
from("direct:start").to("bean:checkLuggage", "mock:testCheckpoint",
"bean:dataEnricher", "mock:result");
The next step in the pipeline is the mo ck: testC heckpo i nt endpoint, which checks that the message body has been removed, the claim check added, and so on.
XML DSL example
The preceding example can also be written in XML, as follows:
<route>
<from uri="direct:start"/>
<pipeline>
<to uri="bean:checkLuggage"/>
<to uri="mock:testCheckpoint"/>
<to uri="bean:dataEnricher"/>
<to uri="mock:result"/>
</pipeline>
</route>
checkLuggage bean
The message is first sent to the checkLug g ag e bean which is implemented as follows: public static final class CheckLuggageBean {
public void checkLuggage(Exchange exchange, @ Body String body,
@ XPath("/order/@ custId") String custId) {
// store the message body into the data store, using the custId as the claim check
dataStore.put(custId, body);
// add the claim check as a header
exchange.getIn().setHeader("claimCheck", custId);
// remove the body from the message
exchange.getIn().setBody(null);
}
}
This bean stores the message body into the data store, using the custId as the claim check. In this example, we are using a HashMap to store the message body; in a real application you would use a database or the file system. The claim check is added as a message header for later use and, finally, we remove the body from the message and pass it down the pipeline.
t est Checkpoint endpoint
308
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
The example route is just a
Pipeline . In a real application, you would substitute some other
steps for the mo ck: testC heckpo i nt endpoint.
dat aEnricher bean
To add the message body back into the message, we use the d ataEnri cher bean, which is implemented as follows: public static final class DataEnricherBean {
public void addDataBackIn(Exchange exchange,
@ Header("claimCheck") String claimCheck) {
// query the data store using the claim check as the key and add the data
// back into the message body
exchange.getIn().setBody(dataStore.get(claimCheck));
// remove the message data from the data store
dataStore.remove(claimCheck);
// remove the claim check header
exchange.getIn().removeHeader("claimCheck");
}
}
This bean queries the data store, using the claim check as the key, and then adds the recovered data back into the message body. The bean then deletes the message data from the data store and removes the cl ai mC heck header from the message.
9.5. SORT
Sort
The sort pattern is used to sort the contents of a message body, assuming that the message body contains a list of items that can be sorted.
By default, the contents of the message are sorted using a default comparator that handles numeric values or strings. You can provide your own comparator and you can specify an expression that returns the list to be sorted (the expression must be convertible to
java. uti l . Li st).
Java DSL example
The following example generates the list of items to sort by tokenizing on the line break character: from("file://inbox").sort(body().tokenize("\n")).to("bean:MyServiceBea n.processLine");
You can pass in your own comparator as the second argument to so rt():
309
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
from("file://inbox").sort(body().tokenize("\n"), new
MyReverseComparator()).to("bean:MyServiceBean.processLine");
XML configurat ion example
You can configure the same routes in Spring XML.
The following example generates the list of items to sort by tokenizing on the line break character:
<route>
<from uri="file://inbox"/>
<sort>
<simple>body</simple>
</sort>
<beanRef ref="myServiceBean" method="processLine"/>
</route>
And to use a custom comparator, you can reference it as a Spring bean:
<route>
<from uri="file://inbox"/>
<sort comparatorRef="myReverseComparator">
<simple>body</simple>
</sort>
<beanRef ref="MyServiceBean" method="processLine"/>
</route>
<bean id="myReverseComparator" class="com.mycompany.MyReverseComparator"/>
Besides <si mpl e>, you can supply an expression using any language you like, so long as it returns a list.
Opt ions
The so rt DSL command supports the following options:
Name co mparato rR ef
Default Value Descript ion
Refers to a custo m
java. uti l . C o mparato r
to use fo r so rting the messag e b o d y. Camel will b y d efault use a co mp arato r which d o es a A..Z so rting .
310
CHAPT ER 9 . MESSAG E T RANSFO RMAT IO N
9.6. VALIDAT E
Overview
The validate pattern provides a convenient syntax to check whether the content of a message is valid. The validate DSL command takes a predicate expression as its sole argument: if the predicate evaluates to true, the route continues processing normally; if the predicate evaluates to fal se, a P red i cateVal i d ati o nExcepti o n is thrown.
Java DSL example
The following route validates the body of the current message using a regular expression: from("jms:queue:incoming")
.validate(body(String.class).regex("^\\w{10}\\,\\d{2}\\,\\w{24}$"))
.to("bean:MyServiceBean.processLine");
You can also validate a message header—for example: from("jms:queue:incoming")
.validate(header("bar").isGreaterThan(100))
.to("bean:MyServiceBean.processLine");
And you can use validate with the simple expression language: from("jms:queue:incoming")
.validate(simple("${in.header.bar} == 100"))
.to("bean:MyServiceBean.processLine");
XML DSL example
To use validate in the XML DSL, the recommended approach is to use the simple expression language:
<route>
<from uri="jms:queue:incoming"/>
<validate>
<simple>${body} regex ^\\w{10}\\,\\d{2}\\,\\w{24}$</simple>
</validate>
<beanRef ref="myServiceBean" method="processLine"/>
</route>
<bean id="myServiceBean" class="com.mycompany.MyServiceBean"/>
You can also validate a message header—for example:
<route>
<from uri="jms:queue:incoming"/>
311
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<validate>
<simple>${in.header.bar} == 100</simple>
</validate>
<beanRef ref="myServiceBean" method="processLine"/>
</route>
<bean id="myServiceBean" class="com.mycompany.MyServiceBean"/>
312
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
CHAPTER 10. MESSAGING ENDPOINTS
Ab st ract
The messaging endpoint patterns describe various features and qualities of service that can be configured on an endpoint.
10.1. MESSAGING MAPPER
Overview
The messaging mapper pattern describes how to map domain objects to and from a canonical message format, where the message format is chosen to be as platform neutral as possible.
The chosen message format should be suitable for transmission through a message bus
, where the message bus is the backbone for integrating a variety of different systems, some of which might not be object-oriented.
Many different approaches are possible, but not all of them fulfill the requirements of a messaging mapper. For example, an obvious way to transmit an object is to use object
serialization, which enables you to write an object to a data stream using an unambiguous encoding (supported natively in Java). However, this is not a suitable approach to use for the messaging mapper pattern, however, because the serialization format is understood only by Java applications. Java object serialization creates an impedance mismatch between the original application and the other applications in the messaging system.
The requirements for a messaging mapper can be summarized as follows:
The canonical message format used to transmit domain objects should be suitable for consumption by non-object oriented applications.
The mapper code should be implemented separately from both the domain object code and the messaging infrastructure. Apache Camel helps fulfill this requirement by providing hooks that can be used to insert mapper code into a route.
The mapper might need to find an effective way of dealing with certain object-oriented concepts such as inheritance, object references, and object trees. The complexity of these issues varies from application to application, but the aim of the mapper implementation must always be to create messages that can be processed effectively by non-objectoriented applications.
Finding object s t o map
You can use one of the following mechanisms to find the objects to map:
Find a registered bean. — For singleton objects and small numbers of objects, you could use the C amel C o ntext registry to store references to beans. For example, if a bean instance is instantiated using Spring XML, it is automatically entered into the registry, where the bean is identified by the value of its i d attribute.
313
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Select objects using the JoSQL language. — If all of the objects you want to access are already instantiated at runtime, you could use the JoSQL language to locate a specific object (or objects). For example, if you have a class,
o rg . apache. camel . bui l d er. sq l . P erso n, with a name bean property and the incoming message has a UserName header, you could select the object whose name property equals the value of the UserName header using the following code: import static org.apache.camel.builder.sql.SqlBuilder.sql; import org.apache.camel.Expression;
...
Expression expression = sql( "SELECT * FROM org.apache.camel.builder.sql.Person where name = :UserName" );
Object value = expression.evaluate(exchange);
Where the syntax, : HeaderName, is used to substitute the value of a header in a JoSQL expression.
Dynamic — For a more scalable solution, it might be necessary to read object data from a database. In some cases, the existing object-oriented application might already provide a finder object that can load objects from the database. In other cases, you might have to write some custom code to extract objects from a database, and in these cases the JDBC component and the SQL component might be useful.
10.2. EVENT DRIVEN CONSUMER
Overview
The event-driven consumer pattern, shown in Figure 10.1, “ Event Driven Consumer Pattern” ,
is a pattern for implementing the consumer endpoint in a Apache Camel component, and is only relevant to programmers who need to develop a custom component in Apache Camel.
Existing components already have a consumer implementation pattern hard-wired into them.
Fig u re 10.1. Even t Driven Co n su mer Pat t ern
314
Consumers that conform to this pattern provide an event method that is automatically called by the messaging channel or transport layer whenever an incoming message is received.
One of the characteristics of the event-driven consumer pattern is that the consumer endpoint itself does not provide any threads to process the incoming messages. Instead, the underlying transport or messaging channel implicitly provides a processor thread when it invokes the exposed event method (which blocks for the duration of the message processing).
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
10.3. POLLING CONSUMER
Overview
The polling consumer pattern, shown in Figure 10.2, “ Polling Consumer Pattern” , is a pattern
for implementing the consumer endpoint in a Apache Camel component, so it is only relevant to programmers who need to develop a custom component in Apache Camel. Existing components already have a consumer implementation pattern hard-wired into them.
Consumers that conform to this pattern expose polling methods, recei ve(),
recei ve(l o ng ti meo ut), and recei veNo Wai t() that return a new exchange object, if one is available from the monitored resource. A polling consumer implementation must provide its own thread pool to perform the polling.
Fig u re 10.2. Po llin g Co n su mer Pat t ern
Scheduled poll consumer
Many of the Apache Camel consumer endpoints employ a scheduled poll pattern to receive messages at the start of a route. That is, the endpoint appears to implement an event-driven consumer interface, but internally a scheduled poll is used to monitor a resource that provides the incoming messages for the endpoint.
See
Section 50.2, “ Implementing the Consumer Interface”
for details of how to implement this pattern.
Quart z component
You can use the quartz component to provide scheduled delivery of messages using the
Quartz enterprise scheduler. See
Quartz
in the Apache Camel Component Reference Guide and
Quartz Component for details.
315
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
10.4 . COMPET ING CONSUMERS
Overview
The competing consumers pattern, shown in
Figure 10.3, “ Competing Consumers Pattern”
, enables multiple consumers to pull messages from the same queue, with the guarantee that
each message is consumed once only. This pattern can be used to replace serial message processing with concurrent message processing (bringing a corresponding reduction in response latency).
Fig u re 10.3. Co mp et in g Co n su mers Pat t ern
316
The following components demonstrate the competing consumers pattern:
JMS based compet ing consumers
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
A regular JMS queue implicitly guarantees that each message can only be consumed at once. Hence, a JMS queue automatically supports the competing consumers pattern. For example, you could define three competing consumers that pull messages from the JMS queue, Hi g hVo l umeQ , as follows: from("jms:HighVolumeQ").to("cxf:bean:replica01"); from("jms:HighVolumeQ").to("cxf:bean:replica02"); from("jms:HighVolumeQ").to("cxf:bean:replica03");
Where the CXF (Web services) endpoints, repl i ca0 1, repl i ca0 2, and repl i ca0 3, process messages from the Hi g hVo l umeQ queue in parallel.
Alternatively, you can set the JMS query option, co ncurrentC o nsumers, to create a thread pool of competing consumers. For example, the following route creates a pool of three competing threads that pick messages from the specified queue: from("jms:HighVolumeQ?
concurrentConsumers=3").to("cxf:bean:replica01");
And the co ncurrentC o nsumers option can also be specified in XML DSL, as follows:
<route>
<from uri="jms:HighVolumeQ?concurrentConsumers=3"/>
<to uri="cxf:bean:replica01"/>
</route>
No t e
JMS topics cannot support the competing consumers pattern. By definition, a JMS topic is intended to send multiple copies of the same message to different consumers. Therefore, it is not compatible with the competing consumers pattern.
SEDA based compet ing consumers
The purpose of the SEDA component is to simplify concurrent processing by breaking the computation into stages. A SEDA endpoint essentially encapsulates an in-memory blocking queue (implemented by java. uti l . co ncurrent. Bl o cki ng Q ueue). Therefore, you can use a SEDA endpoint to break a route into stages, where each stage might use multiple threads. For example, you can define a SEDA route consisting of two stages, as follows:
// Stage 1: Read messages from file system.
from("file://var/messages").to("seda:fanout");
// Stage 2: Perform concurrent processing (3 threads).
from("seda:fanout").to("cxf:bean:replica01"); from("seda:fanout").to("cxf:bean:replica02"); from("seda:fanout").to("cxf:bean:replica03");
Where the first stage contains a single thread that consumes message from a file endpoint,
317
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
fi l e: //var/messag es, and routes them to a SEDA endpoint, sed a: fano ut. The second stage contains three threads: a thread that routes exchanges to cxf: bean: repl i ca0 1, a thread that routes exchanges to cxf: bean: repl i ca0 2, and a thread that routes exchanges to cxf: bean: repl i ca0 3. These three threads compete to take exchange instances from the SEDA endpoint, which is implemented using a blocking queue. Because the blocking queue uses locking to prevent more than one thread from accessing the queue at a time, you are guaranteed that each exchange instance can only be consumed once.
For a discussion of the differences between a SEDA endpoint and a thread pool created by
thread (), see chapter "SEDA" in "Apache Camel Component Reference" .
10.5. MESSAGE DISPAT CHER
Overview
The message dispatcher pattern, shown in
Figure 10.4, “ Message Dispatcher Pattern”
, is used to consume messages from a channel and then distribute them locally to performers, which are responsible for processing the messages. In a Apache Camel application, performers are usually represented by in-process endpoints, which are used to transfer messages to another section of the route.
Fig u re 10.4 . Messag e Disp at ch er Pat t ern
318
You can implement the message dispatcher pattern in Apache Camel using one of the following approaches:
the section called “ JMS selectors”
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
the section called “ JMS selectors in ActiveMQ” the section called “ Content-based router”
JMS select ors
If your application consumes messages from a JMS queue, you can implement the message dispatcher pattern using JMS selectors. A JMS selector is a predicate expression involving
JMS headers and JMS properties. If the selector evaluates to true, the JMS message is allowed to reach the consumer, and if the selector evaluates to fal se, the JMS message is blocked. In many respects, a JMS selector is like a
filter processor , but it has the additional
advantage that the filtering is implemented inside the JMS provider. This means that a JMS selector can block messages before they are transmitted to the Apache Camel application.
This provides a significant efficiency advantage.
In Apache Camel, you can define a JMS selector on a consumer endpoint by setting the
sel ecto r query option on a JMS endpoint URI. For example: from("jms:dispatcher?
selector=CountryCode='US'").to("cxf:bean:replica01"); from("jms:dispatcher?
selector=CountryCode='IE'").to("cxf:bean:replica02"); from("jms:dispatcher?
selector=CountryCode='DE'").to("cxf:bean:replica03");
Where the predicates that appear in a selector string are based on a subset of the SQL92 conditional expression syntax (for full details, see the JMS specification ). The identifiers appearing in a selector string can refer either to JMS headers or to JMS properties. For example, in the preceding routes, the sender sets a JMS property called C o untryC o d e.
If you want to add a JMS property to a message from within your Apache Camel application, you can do so by setting a message header (either on In message or on Out messages).
When reading or writing to JMS endpoints, Apache Camel maps JMS headers and JMS properties to, and from, its native message headers.
Technically, the selector strings must be URL encoded according to the appl i cati o n/x-
www-fo rm-url enco d ed MIME format (see the
HTML specification ). In practice, the
& (ampersand) character might cause difficulties because it is used to delimit each query option in the URI. For more complex selector strings that might need to embed the & character, you can encode the strings using the java. net. UR LEnco d er utility class. For example: from("jms:dispatcher?selector=" + java.net.URLEncoder.encode("CountryCode='US'","UTF-8")).
to("cxf:bean:replica01");
Where the UTF-8 encoding must be used.
JMS select ors in Act iveMQ
319
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can also define JMS selectors on ActiveMQ endpoints. For example: from("activemq:dispatcher?
selector=CountryCode='US'").to("cxf:bean:replica01"); from("activemq:dispatcher?
selector=CountryCode='IE'").to("cxf:bean:replica02"); from("activemq:dispatcher?
selector=CountryCode='DE'").to("cxf:bean:replica03");
For more details, see ActiveMQ: JMS Selectors and ActiveMQ Message Properties .
Cont ent -based rout er
The essential difference between the content-based router pattern and the message dispatcher pattern is that a content-based router dispatches messages to physically separate destinations (remote endpoints), and a message dispatcher dispatches messages locally, within the same process space. In Apache Camel, the distinction between these two patterns is determined by the target endpoint. The same router logic is used to implement both a content-based router and a message dispatcher. When the target endpoint is remote, the route defines a content-based router. When the target endpoint is in-process, the route defines a message dispatcher.
For details and examples of how to use the content-based router pattern see Section 8.1,
10.6. SELECT IVE CONSUMER
Overview
The selective consumer pattern, shown in Figure 10.5, “ Selective Consumer Pattern”
, describes a consumer that applies a filter to incoming messages, so that only messages meeting specific selection criteria are processed.
Fig u re 10.5. Select ive Co n su mer Pat t ern
320
You can implement the selective consumer pattern in Apache Camel using one of the following approaches:
the section called “ JMS selector”
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
the section called “ JMS selector in ActiveMQ”
the section called “ Message filter”
JMS select or
A JMS selector is a predicate expression involving JMS headers and JMS properties. If the selector evaluates to true, the JMS message is allowed to reach the consumer, and if the selector evaluates to fal se, the JMS message is blocked. For example, to consume messages from the queue, sel ecti ve, and select only those messages whose country code property is equal to US, you can use the following Java DSL route: from("jms:selective?selector=" + java.net.URLEncoder.encode("CountryCode='US'","UTF-8")).
to("cxf:bean:replica01");
Where the selector string, C o untryC o d e= ' US' , must be URL encoded (using UTF-8 characters) to avoid trouble with parsing the query options. This example presumes that the
JMS property, C o untryC o d e, is set by the sender. For more details about JMS selectors, see
the section called “ JMS selectors”
.
No t e
If a selector is applied to a JMS queue, messages that are not selected remain on the queue and are potentially available to other consumers attached to the same queue.
JMS select or in Act iveMQ
You can also define JMS selectors on ActiveMQ endpoints. For example: from("acivemq:selective?selector=" + java.net.URLEncoder.encode("CountryCode='US'","UTF-8")).
to("cxf:bean:replica01");
For more details, see ActiveMQ: JMS Selectors and ActiveMQ Message Properties .
Message filt er
If it is not possible to set a selector on the consumer endpoint, you can insert a filter processor into your route instead. For example, you can define a selective consumer that processes only messages with a US country code using Java DSL, as follows: from("seda:a").filter(header("CountryCode").isEqualTo("US")).process(m yProcessor);
The same route can be defined using XML configuration, as follows:
321
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<camelContext id="buildCustomProcessorWithFilter" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="seda:a"/>
<filter>
<xpath>$CountryCode = 'US'</xpath>
<process ref="#myProcessor"/>
</filter>
</route>
</camelContext>
For more information about the Apache Camel filter processor, see Message Filter .
Warn in g
Be careful about using a message filter to select messages from a JMS queue.
When using a filter processor, blocked messages are simply discarded. Hence, if the messages are consumed from a queue (which allows each message to be consumed only once—see
Section 10.4, “ Competing Consumers” ), then blocked
messages are not processed at all. This might not be the behavior you want.
10.7. DURABLE SUBSCRIBER
Overview
A durable subscriber, as shown in Figure 10.6, “ Durable Subscriber Pattern”
, is a consumer
that wants to receive all of the messages sent over a particular publish-subscribe
channel, including messages sent while the consumer is disconnected from the messaging system.
This requires the messaging system to store messages for later replay to the disconnected consumer. There also has to be a mechanism for a consumer to indicate that it wants to establish a durable subscription. Generally, a publish-subscribe channel (or topic) can have both durable and non-durable subscribers, which behave as follows:
n o n - d u rab le su b scrib er—Can have two states: connected and disconnected. While a non-durable subscriber is connected to a topic, it receives all of the topic's messages in real time. However, a non-durable subscriber never receives messages sent to the topic while the subscriber is disconnected.
d u rab le su b scrib er—Can have two states: connected and inactive. The inactive state means that the durable subscriber is disconnected from the topic, but wants to receive the messages that arrive in the interim. When the durable subscriber reconnects to the topic, it receives a replay of all the messages sent while it was inactive.
Fig u re 10.6 . Du rab le Su b scrib er Pat t ern
322
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
JMS durable subscriber
The JMS component implements the durable subscriber pattern. In order to set up a durable subscription on a JMS endpoint, you must specify a client ID, which identifies this particular connection, and a durable subscription name, which identifies the durable subscriber. For example, the following route sets up a durable subscription to the JMS topic, news, with a client ID of co nn0 1 and a durable subscription name of Jo hn. D o e: from("jms:topic:news?
clientId=conn01& durableSubscriptionName=John.Doe").
to("cxf:bean:newsprocessor");
You can also set up a durable subscription using the ActiveMQ endpoint: from("activemq:topic:news?
clientId=conn01& durableSubscriptionName=John.Doe").
to("cxf:bean:newsprocessor");
If you want to process the incoming messages concurrently, you can use a SEDA endpoint to fan out the route into multiple, parallel segments, as follows: from("jms:topic:news?
clientId=conn01& durableSubscriptionName=John.Doe").
to("seda:fanout"); from("seda:fanout").to("cxf:bean:newsproc01"); from("seda:fanout").to("cxf:bean:newsproc02"); from("seda:fanout").to("cxf:bean:newsproc03");
Where each message is processed only once, because the SEDA component supports the
323
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Alt ernat ive example
Another alternative is to combine the
or
with File component or JPA component components for durable subscribers then something like
SEDA component for non-durable.
Here is a simple example of creating durable subscribers to a chapter "JMS" in "Apache
Camel Component Reference" topic
Usin g t h e Flu en t Bu ild ers
from("direct:start").to("activemq:topic:foo");
from("activemq:topic:foo?
clientId=1& durableSubscriptionName=bar1").to("mock:result1");
from("activemq:topic:foo?
clientId=2& durableSubscriptionName=bar2").to("mock:result2");
Usin g t h e Sp rin g XML Ext en sio n s
<route>
<from uri="direct:start"/>
<to uri="activemq:topic:foo"/>
</route>
<route>
<from uri="activemq:topic:foo?
clientId=1& durableSubscriptionName=bar1"/>
<to uri="mock:result1"/>
</route>
<route>
<from uri="activemq:topic:foo?
clientId=2& durableSubscriptionName=bar2"/>
<to uri="mock:result2"/>
</route>
Here is another example of JMS durable subscribers, but this time using virtual topics
(recommended by AMQ over durable subscriptions)
Usin g t h e Flu en t Bu ild ers
from("direct:start").to("activemq:topic:VirtualTopic.foo");
from("activemq:queue:Consumer.1.VirtualTopic.foo").to("mock:result1")
;
from("activemq:queue:Consumer.2.VirtualTopic.foo").to("mock:result2")
;
324
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
Usin g t h e Sp rin g XML Ext en sio n s
<route>
<from uri="direct:start"/>
<to uri="activemq:topic:VirtualTopic.foo"/>
</route>
<route>
<from uri="activemq:queue:Consumer.1.VirtualTopic.foo"/>
<to uri="mock:result1"/>
</route>
<route>
<from uri="activemq:queue:Consumer.2.VirtualTopic.foo"/>
<to uri="mock:result2"/>
</route>
10.8. IDEMPOT ENT CONSUMER
Overview
The idempotent consumer pattern is used to filter out duplicate messages. For example, consider a scenario where the connection between a messaging system and a consumer endpoint is abruptly lost due to some fault in the system. If the messaging system was in the middle of transmitting a message, it might be unclear whether or not the consumer received the last message. To improve delivery reliability, the messaging system might decide to redeliver such messages as soon as the connection is re-established. Unfortunately, this entails the risk that the consumer might receive duplicate messages and, in some cases, the effect of duplicating a message may have undesirable consequences (such as debiting a sum of money twice from your account). In this scenario, an idempotent consumer could be used to weed out undesired duplicates from the message stream.
Camel provides the following Idempotent Consumer implementations:
Memo ryId empo tentR epo si to ry
File
HazelcastIdempotentRepository
JdbcMessageIdRepository
JpaMessageIdRepository
Idempot ent consumer wit h in-memory cache
In Apache Camel, the idempotent consumer pattern is implemented by the
i d empo tentC o nsumer() processor, which takes two arguments:
325
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
messag eId Expressi o n — An expression that returns a message ID string for the current message.
messag eId R epo si to ry — A reference to a message ID repository, which stores the IDs of all the messages received.
As each message comes in, the idempotent consumer processor looks up the current message ID in the repository to see if this message has been seen before. If yes, the message is discarded; if no, the message is allowed to pass and its ID is added to the repository.
The code shown in
Example 10.1, “ Filtering Duplicate Messages with an In-memory Cache”
uses the T ransacti o nID header to filter out duplicates.
Examp le 10.1. Filt erin g Du p licat e Messag es wit h an In - memo ry Cach e
import static org.apache.camel.processor.idempotent.MemoryMessageIdRepository.memo
ryMessageIdRepository;
...
RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("seda:a")
.idempotentConsumer(
header("TransactionID"),
memoryMessageIdRepository(200)
).to("seda:b");
}
};
326
Where the call to memo ryMessag eId R epo si to ry(20 0 ) creates an in-memory cache that can hold up to 200 message IDs.
You can also define an idempotent consumer using XML configuration. For example, you can define the preceding route in XML, as follows:
<camelContext id="buildIdempotentConsumer" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="seda:a"/>
<idempotentConsumer messageIdRepositoryRef="MsgIDRepos">
<simple>header.TransactionID</simple>
<to uri="seda:b"/>
</idempotentConsumer>
</route>
</camelContext>
<bean id="MsgIDRepos" class="org.apache.camel.processor.idempotent.MemoryMessageIdRepository
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
">
<!-- Specify the in-memory cache size. -->
<constructor-arg type="int" value="200"/>
</bean>
Idempot ent consumer wit h JPA reposit ory
The in-memory cache suffers from the disadvantages of easily running out of memory and not working in a clustered environment. To overcome these disadvantages, you can use a
Java Persistent API (JPA) based repository instead. The JPA message ID repository uses an object-oriented database to store the message IDs. For example, you can define a route that uses a JPA repository for the idempotent consumer, as follows: import org.springframework.orm.jpa.JpaTemplate; import org.apache.camel.spring.SpringRouteBuilder; import static org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository.jpaMe
ssageIdRepository;
...
RouteBuilder builder = new SpringRouteBuilder() {
public void configure() {
from("seda:a").idempotentConsumer(
header("TransactionID"),
jpaMessageIdRepository(bean(JpaTemplate.class),
"myProcessorName")
).to("seda:b");
}
};
The JPA message ID repository is initialized with two arguments:
JpaT empl ate instance—Provides the handle for the JPA database.
processor name—Identifies the current idempotent consumer processor.
The Spri ng R o uteBui l d er. bean() method is a shortcut that references a bean defined in the Spring XML file. The JpaT empl ate bean provides a handle to the underlying JPA database. See the JPA documentation for details of how to configure this bean.
For more details about setting up a JPA repository, see JPA Component documentation, the
Spring JPA documentation, and the sample code in the Camel JPA unit test .
Spring XML example
The following example uses the myMessag eId header to filter out duplicates:
<!-- repository for the idempotent consumer -->
<bean id="myRepo"
327
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
class="org.apache.camel.processor.idempotent.MemoryIdempotentRepositor
y"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<idempotentConsumer messageIdRepositoryRef="myRepo">
<!-- use the messageId header as key for identifying duplicate messages -->
<header>messageId</header>
<!-- if not a duplicate send it to this mock endpoint -->
<to uri="mock:result"/>
</idempotentConsumer>
</route>
</camelContext>
Idempot ent consumer wit h JDBC reposit ory
A JDBC repository is also supported for storing message IDs in the idempotent consumer pattern. The implementation of the JDBC repository is provided by the SQL component, so if you are using the Maven build system, add a dependency on the camel -sq l artifact.
You can use the Si ng l eC o nnecti o nD ataSo urce JDBC wrapper class from the Spring persistence API in order to instantiate the connection to a SQL database. For example, to instantiate a JDBC connection to a HyperSQL database instance, you could define the following JDBC data source:
<bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource"
>
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:camel_jdbc"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
No t e
The preceding JDBC data source uses the HyperSQL mem protocol, which creates a memory-only database instance. This is a toy implementation of the HyperSQL database which is not actually persistent.
Using the preceding data source, you can define an idempotent consumer pattern that uses the JDBC message ID repository, as follows:
<bean id="messageIdRepository" class="org.apache.camel.processor.idempotent.jdbc.JdbcMessageIdReposit
ory">
<constructor-arg ref="dataSource" />
<constructor-arg value="myProcessorName" />
</bean>
328
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
<camel:camelContext>
<camel:errorHandler id="deadLetterChannel" type="DeadLetterChannel" deadLetterUri="mock:error">
<camel:redeliveryPolicy maximumRedeliveries="0" maximumRedeliveryDelay="0" logStackTrace="false" />
</camel:errorHandler>
<camel:route id="JdbcMessageIdRepositoryTest" errorHandlerRef="deadLetterChannel">
<camel:from uri="direct:start" />
<camel:idempotentConsumer messageIdRepositoryRef="messageIdRepository">
<camel:header>messageId</camel:header>
<camel:to uri="mock:result" />
</camel:idempotentConsumer>
</camel:route>
</camel:camelContext>
How t o handle duplicat e messages in t he rout e
Availab le as o f Camel 2.8
You can now set the ski pD upl i cate option to fal se which instructs the idempotent consumer to route duplicate messages as well. However the duplicate message has been
marked as duplicate by having a property on the Exchange set to true. We can leverage this
fact by using a Content-Based Router
or Message Filter to detect this and handle duplicate
messages.
For example in the following example we use the Message Filter to send the message to a
duplicate endpoint, and then stop continue routing that message.
from("direct:start")
// instruct idempotent consumer to not skip duplicates as we will filter then our self
.idempotentConsumer(header("messageId")).messageIdRepository(repo).ski
pDuplicate(false)
.filter(property(Exchange.DUPLICATE_MESSAGE).isEqualTo(true))
// filter out duplicate messages by sending them to someplace else and then stop
.to("mock:duplicate")
.stop()
.end()
// and here we process only new messages (no duplicates)
.to("mock:result");
The sample example in XML DSL would be:
<!-- idempotent repository, just use a memory based for testing -->
<bean id="myRepo" class="org.apache.camel.processor.idempotent.MemoryIdempotentRepositor
329
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
y"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<!-- we do not want to skip any duplicate messages -->
<idempotentConsumer messageIdRepositoryRef="myRepo" skipDuplicate="false">
<!-- use the messageId header as key for identifying duplicate messages -->
<header>messageId</header>
<!-- we will to handle duplicate messages using a filter
-->
<filter>
<!-- the filter will only react on duplicate messages, if this property is set on the Exchange -->
<property>CamelDuplicateMessage</property>
<!-- and send the message to this mock, due its part of an unit test -->
<!-- but you can of course do anything as its part of the route -->
<to uri="mock:duplicate"/>
<!-- and then stop -->
<stop/>
</filter>
<!-- here we route only new messages -->
<to uri="mock:result"/>
</idempotentConsumer>
</route>
</camelContext>
How t o handle duplicat e message in a clust ered environment wit h a dat a grid
If you have running Camel in a clustered environment, a in memory idempotent repository doesn't work (see above). You can setup either a central database or use the idempotent consumer implementation based on the Hazelcast data grid. Hazelcast finds the nodes over multicast (which is default - configure Hazelcast for tcp-ip) and creates automatically a map based repository:
HazelcastIdempotentRepository idempotentRepo = new
HazelcastIdempotentRepository("myrepo"); from("direct:in").idempotentConsumer(header("messageId"), idempotentRepo).to("mock:out");
You have to define how long the repository should hold each message id (default is to delete it never). To avoid that you run out of memory you should create an eviction strategy based on the Hazelcast configuration . For additional information see camel-hazelcast .
See this Idempotent Repository tutorial to learn more about how to setup such an idempotent repository on two cluster nodes using Apache Karaf.
330
Opt ions
The Idempotent Consumer has the following options:
O pt ion eag er
Default true
CHAPT ER 1 0 . MESSAG ING ENDPO INT S messag eId R epo si to ryR ef nul l ski pD upl i cate true
Descript ion
Camel 2 .0 : Eag er co ntro ls whether Camel ad d s the messag e to the rep o sito ry b efo re o r after the exchang e has b een p ro cessed . If enab led b efo re then Camel will b e ab le to d etect d up licate messag es even when messag es are currently in p ro g ress. By d isab ling
Camel will o nly d etect d up licates when a messag e has successfully b een p ro cessed .
A reference to a
Id empo tentR epo si to ry
to lo o kup in the reg istry.
This o p tio n is mand ato ry when using XML DSL.
Camel 2 .8 : Sets whether to skip d up licate messag es. If set to
fal se
then the messag e will b e co ntinued .
Ho wever the
has b een marked as a d up licate b y having the
Exchang e. D UP LIC AT E_M
ESSAG
exchang e p ro p erty set to a
Bo o l ean. T R UE
value.
331
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide co mpl eti o nEag er fal se
Camel 2 .1 6 : Sets whether to co mp lete the Id emp o tent co nsumer eag er, when the exchang e is d o ne. If yo u set the
co mpl eteEag er
o p tio n true, then the Id emp o tent
Co nsumer trig g ers its co mp letio n when the exchang e reaches till the end o f the id emp o tent co nsumer p attern b lo ck. Ho wever, if the exchang e co ntinues to ro ute even after the end b lo ck, then it d o es no t affect the state o f the id emp o tent co nsumer. If yo u set the
co mpl eteEag er
o p tio n false, then the Id emp o tent
Co nsumer trig g ers its co mp letio n after the exchang e is d o ne and is b eing ro uted . Ho wever, if the exchang e co ntinues to ro ute even after the b lo ck end s, then it also affects the state o f the id emp o tent co nsumer.
Fo r examp le, d ue to an excep tio n if the exchang e fails, then the state o f the id emp o tent co nsumer will b e a ro llb ack.
10.9. T RANSACT IONAL CLIENT
Overview
The transactional client pattern, shown in
Figure 10.7, “ Transactional Client Pattern”
, refers to messaging endpoints that can participate in a transaction. Apache Camel supports transactions using Spring transaction management .
Fig u re 10.7. T ran sact io n al Clien t Pat t ern
332
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
T ransact ion orient ed endpoint s
Not all Apache Camel endpoints support transactions. Those that do are called transaction
oriented endpoints (or TOEs). For example, both the JMS component and the ActiveMQ component support transactions.
To enable transactions on a component, you must perform the appropriate initialization before adding the component to the C amel C o ntext. This entails writing code to initialize your transactional components explicitly.
References
The details of configuring transactions in Apache Camel are beyond the scope of this guide.
For full details of how to use transactions, see the Apache Camel Transaction Guide.
10.10. MESSAGING GAT EWAY
Overview
The messaging gateway pattern, shown in Figure 10.8, “ Messaging Gateway Pattern” ,
describes an approach to integrating with a messaging system, where the messaging system's API remains hidden from the programmer at the application level. One of the more common example is when you want to translate synchronous method calls into request/reply message exchanges, without the programmer being aware of this.
Fig u re 10.8. Messag in g G at eway Pat t ern
333
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The following Apache Camel components provide this kind of integration with the messaging system: chapter "CXF" in "Apache Camel Component Reference" chapter "Bean" in "Apache Camel Component Reference"
10.11. SERVICE ACT IVAT OR
Overview
The service activator pattern, shown in
Figure 10.9, “ Service Activator Pattern”
, describes the scenario where a service's operations are invoked in response to an incoming request message. The service activator identifies which operation to call and extracts the data to use as the operation's parameters. Finally, the service activator invokes an operation using the data extracted from the message. The operation invocation can be either oneway (request only) or two-way (request/reply).
Fig u re 10.9 . Service Act ivat o r Pat t ern
334
In many respects, a service activator resembles a conventional remote procedure call (RPC), where operation invocations are encoded as messages. The main difference is that a service
CHAPT ER 1 0 . MESSAG ING ENDPO INT S
activator needs to be more flexible. An RPC framework standardizes the request and reply message encodings (for example, Web service operations are encoded as SOAP messages), whereas a service activator typically needs to improvise the mapping between the messaging system and the service's operations.
Bean int egrat ion
The main mechanism that Apache Camel provides to support the service activator pattern is
bean integration. Bean integration provides a general framework for mapping incoming messages to method invocations on Java objects. For example, the Java fluent DSL provides the processors bean() and beanR ef() that you can insert into a route to invoke methods on a registered Java bean. The detailed mapping of message data to Java method parameters is determined by the bean binding, which can be implemented by adding annotations to the bean class.
For example, consider the following route which calls the Java method,
BankBean. g etUserAccBal ance(), to service requests incoming on a JMS/ActiveMQ queue: from("activemq:BalanceQueries")
.setProperty("userid", xpath("/Account/BalanceQuery/UserID").stringResult())
.beanRef("bankBean", "getUserAccBalance")
.to("velocity:file:src/scripts/acc_balance.vm")
.to("activemq:BalanceResults");
The messages pulled from the ActiveMQ endpoint, acti vemq : Bal anceQ ueri es, have a simple XML format that provides the user ID of a bank account. For example:
<?xml version='1.0' encoding='UTF-8'?>
<Account>
<BalanceQuery>
<UserID>James.Strachan</UserID>
</BalanceQuery>
</Account>
The first processor in the route, setP ro perty(), extracts the user ID from the In message and stores it in the useri d exchange property. This is preferable to storing it in a header, because the In headers are not available after invoking the bean.
The service activation step is performed by the beanR ef() processor, which binds the incoming message to the g etUserAccBal ance() method on the Java object identified by the bankBean bean ID. The following code shows a sample implementation of the
BankBean class: package tutorial;
i mpo rt o rg . apache. camel . l ang uag e. XP ath;
public class BankBean { public int
335
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
getUserAccBalance(@ XP ath(
"/Acco unt/Bal anceQ uery/UserID "
) String user)
{ if (user.equals( "James.Strachan" )) { return 1200 ;
} else { return 0 ;
}
}
}
Where the binding of message data to method parameter is enabled by the @ XP ath annotation, which injects the content of the UserID XML element into the user method parameter. On completion of the call, the return value is inserted into the body of the Out message which is then copied into the In message for the next step in the route. In order for the bean to be accessible to the beanR ef() processor, you must instantiate an instance in
Spring XML. For example, you can add the following lines to the MET A-
INF/spri ng /camel -co ntext. xml configuration file to instantiate the bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
...
<bean id="bankBean" class="tutorial.BankBean"/>
</beans>
Where the bean ID, bankBean, identifes this bean instance in the registry.
The output of the bean invocation is injected into a Velocity template, to produce a properly formatted result message. The Velocity endpoint,
vel o ci ty: fi l e: src/scri pts/acc_bal ance. vm, specifies the location of a velocity script with the following contents:
<?xml version='1.0' encoding='UTF-8'?>
<Account>
<BalanceResult>
<UserID>${exchange.getProperty("userid")}</UserID>
<Balance>${body}</Balance>
</BalanceResult>
</Account>
The exchange instance is available as the Velocity variable, exchang e, which enables you to retrieve the useri d exchange property, using
${exchang e. g etP ro perty("useri d ")}. The body of the current In message, ${bo d y}, contains the result of the g etUserAccBal ance() method invocation.
336
CHAPT ER 1 1 . SYST EM MANAG EMENT
CHAPTER 11. SYSTEM MANAGEMENT
Ab st ract
The system management patterns describe how to monitor, test, and administer a messaging system.
11.1. DET OUR
Det our
The Detour from the
Introducing Enterprise Integration Patterns
allows you to send messages through additional steps if a control condition is met. It can be useful for turning on extra validation, testing, debugging code when needed.
Example
In this example we essentially have a route like
fro m("d i rect: start"). to ("mo ck: resul t") with a conditional detour to the
mo ck: d eto ur endpoint in the middle of the route..
from("direct:start").choice()
.when().method("controlBean", "isDetour").to("mock:detour").end()
.to("mock:result");
Usin g t h e Sp rin g XML Ext en sio n s
<route>
<from uri="direct:start"/>
<choice>
<when>
<method bean="controlBean" method="isDetour"/>
<to uri="mock:detour"/>
</when>
</choice>
337
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri="mock:result"/>
</split>
</route> whether the detour is turned on or off is decided by the C o ntro l Bean. So, when the detour is on the message is routed to mo ck: d eto ur and then mo ck: resul t. When the detour is off, the message is routed to mo ck: resul t.
For full details, check the example source here: camel-core/src/test/java/org/apache/camel/processor/DetourTest.java
11.2. LOGEIP
Overview
Apache Camel provides several ways to perform logging in a route:
Using the l o g DSL command.
Using the Log component, which can log the message content.
Using the Tracer, which traces message flow.
Using a P ro cesso r or a Bean endpoint to perform logging in Java.
Dif f eren ce b et ween t h e lo g DSL co mman d an d t h e lo g co mp o n en t
The l o g DSL is much lighter and meant for logging human logs such as
Starti ng to d o . . . . It can only log a message based on the
Simple language. In contrast, the Log component is a fully featured logging component.
The Log component is capable of logging the message itself and you have many
URI options to control the logging.
Java DSL example
Since Ap ach e Camel 2.2, you can use the l o g DSL command to construct a log message at run time using the Simple expression language. For example, you can create a log message within a route, as follows: from("direct:start").log("Processing ${id}").to("bean:foo");
338
CHAPT ER 1 1 . SYST EM MANAG EMENT
This route constructs a Stri ng format message at run time. The log message will by logged at INFO level, using the route ID as the log name. By default, routes are named consecutively, ro ute-1, ro ute-2 and so on. But you can use the DSL command,
ro uteId ("myC o o l R o ute"), to specify a custom route ID.
The log DSL also provides variants that enable you to set the logging level and the log name explicitly. For example, to set the logging level explicitly to Lo g g i ng Level . D EBUG , you can invoke the log DSL as follows: has overloaded methods to set the logging level and/or name as well.
from("direct:start").log(LoggingLevel.DEBUG, "Processing
${id}").to("bean:foo");
To set the log name to fi l eR o ute, you can invoke the log DSL as follows: from("file://target/files").log(LoggingLevel.DEBUG, "fileRoute",
"Processing file ${file:name}").to("bean:foo");
XML DSL example
In XML DSL, the log DSL is represented by the l o g element and the log message is specified by setting the messag e attribute to a Simple expression, as follows:
<route id="foo">
<from uri="direct:foo"/>
<log message="Got ${body}"/>
<to uri="mock:foo"/>
</route>
The l o g element supports the messag e, l o g g i ng Level and l o g Name attributes. For example:
<route id="baz">
<from uri="direct:baz"/>
<log message="Me Got ${body}" loggingLevel="FATAL" logName="cool"/>
<to uri="mock:baz"/>
</route>
11.3. WIRE T AP
Wire T ap
The wire tap pattern, as shown in Figure 11.1, “ Wire Tap Pattern”
, enables you to route a copy of the message to a separate tap location, while the original message is forwarded to the ultimate destination.
339
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Fig u re 11.1. Wire T ap Pat t ern
St reams
If you Wire Tap a stream message body, you should consider enabling
Stream
Caching to ensure the message body can be re-read. See more details at Stream
Caching
34 0
WireT ap node
Apache Camel 2.0 introduces the wi reT ap node for doing wire taps. The wi reT ap node copies the original exchange to a tapped exchange, whose exchange pattern is set to InOnly, because the tapped exchange should be propagated in a oneway style. The tapped exchange is processed in a separate thread, so that it can run concurrently with the main route.
The wi reT ap supports two different approaches to tapping an exchange:
Tap a copy of the original exchange.
Tap a new exchange instance, enabling you to customize the tapped exchange.
T ap a copy of t he original exchange
Using the Java DSL: from("direct:start")
.to("log:foo")
.wireTap("direct:tap")
.to("mock:result");
Using Spring XML extensions:
<route>
<from uri="direct:start"/>
CHAPT ER 1 1 . SYST EM MANAG EMENT
<to uri="log:foo"/>
<wireTap uri="direct:tap"/>
<to uri="mock:result"/>
</route>
T ap and modify a copy of t he original exchange
Using the Java DSL, Apache Camel supports using either a processor or an expression to modify a copy of the original exchange. Using a processor gives you full power over how the exchange is populated, because you can set properties, headers and so on. The expression approach can only be used to modify the In message body.
For example, to modify a copy of the original exchange using the processor approach: from("direct:start")
.wireTap("direct:foo", new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().setHeader("foo", "bar");
}
}).to("mock:result"); from("direct:foo").to("mock:foo");
And to modify a copy of the original exchange using the expression approach: from("direct:start")
.wireTap("direct:foo", constant("Bye World"))
.to("mock:result"); from("direct:foo").to("mock:foo");
Using the Spring XML extensions, you can modify a copy of the original exchange using the
processor approach, where the pro cesso rR ef attribute references a spring bean with the
myP ro cesso r ID:
<route>
<from uri="direct:start2"/>
<wireTap uri="direct:foo" processorRef="myProcessor"/>
<to uri="mock:result"/>
</route>
And to modify a copy of the original exchange using the expression approach:
<route>
<from uri="direct:start"/>
<wireTap uri="direct:foo">
<body><constant>Bye World</constant></body>
</wireTap>
<to uri="mock:result"/>
</route>
34 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
34 2
T ap a new exchange inst ance
You can define a wiretap with a new exchange instance by setting the copy flag to fal se
(the default is true). In this case, an initially empty exchange is created for the wiretap.
For example, to create a new exchange instance using the processor approach: from("direct:start")
.wireTap("direct:foo", false, new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody("Bye World");
exchange.getIn().setHeader("foo", "bar");
}
}).to("mock:result"); from("direct:foo").to("mock:foo");
Where the second wi reT ap argument sets the copy flag to fal se, indicating that the original exchange is not copied and an empty exchange is created instead.
To create a new exchange instance using the expression approach: from("direct:start")
.wireTap("direct:foo", false, constant("Bye World"))
.to("mock:result"); from("direct:foo").to("mock:foo");
Using the Spring XML extensions, you can indicate that a new exchange is to be created by setting the wi reT ap element's co py attribute to fal se.
To create a new exchange instance using the processor approach, where the
pro cesso rR ef attribute references a spring bean with the myP ro cesso r ID, as follows:
<route>
<from uri="direct:start2"/>
<wireTap uri="direct:foo" processorRef="myProcessor" copy="false"/>
<to uri="mock:result"/>
</route>
And to create a new exchange instance using the expression approach:
<route>
<from uri="direct:start"/>
<wireTap uri="direct:foo" copy="false">
<body><constant>Bye World</constant></body>
CHAPT ER 1 1 . SYST EM MANAG EMENT
</wireTap>
<to uri="mock:result"/>
</route>
Sending a new Exchange and set headers in DSL
Availab le as o f Camel 2.8
If you send a new messages using the
then you could only set the message body
from the DSL. If you also need to set new headers you would have to use a
Processors for that. So in Camel 2.8 onwards we have improved this situation so you
can now set headers as well in the DSL.
The following example sends a new message which has
"Bye World" as message body a header with key "id" with the value 123 a header with key "date" which has current date as value
Java DSL
from("direct:start")
// tap a new message and send it to direct:tap
// the new message should be Bye World with 2 headers
.wireTap("direct:tap")
// create the new tap message body and headers
.newExchangeBody(constant("Bye World"))
.newExchangeHeader("id", constant(123))
.newExchangeHeader("date", simple("${date:now:yyyyMMdd}"))
.end()
// here we continue routing the original messages
.to("mock:result");
// this is the tapped route
from("direct:tap")
.to("mock:tap");
XML DSL
The XML DSL is slightly different than Java DSL as how you configure the message body and headers. In XML you use <body> and <setHeader> as shown:
<route>
<from uri="direct:start"/>
<!-- tap a new message and send it to direct:tap -->
<!-- the new message should be Bye World with 2 headers -->
<wireTap uri="direct:tap">
34 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<!-- create the new tap message body and headers -->
<body><constant>Bye World</constant></body>
<setHeader headerName="id"><constant>123</constant>
</setHeader>
<setHeader headerName="date"><simple>${date:now:yyyyMMdd}
</simple></setHeader>
</wireTap>
<!-- here we continue routing the original message -->
<to uri="mock:result"/>
</route>
Using onPrepare t o execut e cust om logic when preparing messages
Availab le as o f Camel 2.8
Opt ions
The wi reT ap DSL command supports the following options:
Name uri ref executo rServi ceR ef pro cesso rR ef co py
Default Value true
Descript ion
The end p o int uri where to send the wire tap p ed messag e. Yo u sho uld use either
uri
o r
ref
.
Refers to the end p o int where to send the wire tap p ed messag e. Yo u sho uld use either
uri
o r
ref
.
p ro cessing the wire tap p ed messag es. If no t set then
Camel uses a d efault thread p o o l.
Refers to a custo m
creating a new messag e (eg the send a new messag e mo d e). See b elo w.
Camel 2 .3: Sho uld a co p y o f
to used when wire tap p ing the messag e.
34 4
o nP repareR ef
CHAPT ER 1 1 . SYST EM MANAG EMENT
Camel 2 .8 : Refers to a custo m
to p rep are the co p y o f the
to b e wire tap p ed .
This allo ws yo u to d o any custo m lo g ic, such as d eep clo ning the messag e p aylo ad if that's need ed etc.
34 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 12. SERVICE COMPONENT RUNTIME
ABST RACT
Service Component Runtime (SCR) is an implementation of OSGi Declarative Services specification. It enables any plain old Java object to expose and use OSGi services with no boilerplate code.
WORKING WIT H CAMEL AND SCR
Apache Camel SCR component provides an integration of Camel with the OSGi Service
Component Runtime.
OSGi framework has ability to know the object by looking at SCR descriptor file in its bundle which are generated from Java annotations by a plugin such as
o rg . apache. fel i x: maven-scr-pl ug i n. Using SCR, the bundle remains completely in
Java world. There is no need to edit XML or properties file. It offers you full control over the project.
CREAT ING A SERVICE COMPONENT
Following are the steps to create a service component out of your java class.
1. Add the required o rg . apache. fel i x. scr. anno tati o ns at class level. For example,
@ Component
@ References({
@ Reference(name = "camelComponent" ,referenceInterface =
ComponentResolver.class,
cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY, bind =
"gotCamelComponent" , unbind = "lostCamelComponent" )
})
2. Implement the g etR o uteBui l d ers() method that returns the Camel route you want to run. For example,
@ Override protected List<RoutesBuilder> getRouteBuilders() {
List<RoutesBuilder> routesBuilders = new ArrayList<>();
routesBuilders.add( new YourRouteBuilderHere(registry));
routesBuilders.add( new AnotherRouteBuilderHere(registry)); return routesBuilders;
}
34 6
CHAPT ER 1 2 . SERVICE CO MPO NENT RUNT IME
3. Finally, enter the default configuration in annotations.
@ Properties({
@ Property(name = "camelContextId" , value = "my-test" ),
@ Property(name = "active" , value = "true" ),
@ Property(name = "..." , value = "..." ),
...
})
EXAMPLE OF A SERVICE COMPONENT CLASS
The following example illustrates the use of camel -archetype-scr to generate a complete service component class:
// This file was generated from org.apache.camel.archetypes/camelarchetype-scr/2.15-SNAPSHOT
package example; import java.util.ArrayList; import java.util.List; import org.apache.camel.scr.AbstractCamelRunner; import example.internal.CamelScrExampleRoute; import org.apache.camel.RoutesBuilder; import org.apache.camel.spi.ComponentResolver; import org.apache.felix.scr.annotations.*;
@ Component(label = CamelScrExample.COMPONENT_LABEL, description =
CamelScrExample.COMPONENT_DESCRIPTION, immediate = true , metatype = true )
@ Properties({
@ Property(name = "camelContextId" , value = "camel-scr-example" ),
@ Property(name = "camelRouteId" , value = "foo/timer-log" ),
@ Property(name = "active" , value = "true" ),
@ Property(name = "from" , value = "timer:foo?period=5000" ),
@ Property(name = "to" , value = "log:foo?showHeaders=true" ),
@ Property(name = "messageOk" , value = "Success: {{from}} ->
{{to}}" ),
@ Property(name = "messageError" , value = "Failure: {{from}} ->
{{to}}" ),
@ Property(name = "maximumRedeliveries" , value = "0" ),
@ Property(name = "redeliveryDelay" , value = "5000" ),
@ Property(name = "backOffMultiplier" , value = "2" ),
@ Property(name = "maximumRedeliveryDelay" , value = "60000" )
})
@ References({
@ Reference(name = "camelComponent" ,referenceInterface =
ComponentResolver.class,
cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy
= ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY, bind =
34 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
"gotCamelComponent" , unbind = "lostCamelComponent" )
}) public class CamelScrExample extends AbstractCamelRunner { public static final String COMPONENT_LABEL =
"example.CamelScrExample" ; public static final String COMPONENT_DESCRIPTION = "This is the description for camel-scr-example." ;
@ Override protected List<RoutesBuilder> getRouteBuilders() {
List<RoutesBuilder> routesBuilders = new ArrayList<>();
routesBuilders.add( new CamelScrExampleRoute(registry)); return routesBuilders;
}
}
34 8
EXAMPLE OF A ROUT EBUILDER CLASS
The following example illustrates the use of camel -archetype-scr to generate a
R o uteBui l d er class:
// This file was generated from org.apache.camel.archetypes/camelarchetype-scr/2.15-SNAPSHOT
package example.internal; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.SimpleRegistry; import org.apache.commons.lang.Validate; public class CamelScrExampleRoute extends RouteBuilder {
SimpleRegistry registry;
// Configured fields
private String camelRouteId; private Integer maximumRedeliveries; private Long redeliveryDelay; private Double backOffMultiplier; private Long maximumRedeliveryDelay; public CamelScrExampleRoute( final SimpleRegistry registry) { this .registry = registry;
}
@ Override public void configure() throws Exception {
checkProperties();
// Add a bean to Camel context registry
registry.put( "test" , "bean" );
CHAPT ER 1 2 . SERVICE CO MPO NENT RUNT IME
errorHandler(defaultErrorHandler()
.retryAttemptedLogLevel(LoggingLevel.WARN)
.maximumRedeliveries(maximumRedeliveries)
.redeliveryDelay(redeliveryDelay)
.backOffMultiplier(backOffMultiplier)
.maximumRedeliveryDelay(maximumRedeliveryDelay));
from( "{{from}}" )
.startupOrder( 2 )
.routeId(camelRouteId)
.onCompletion()
.to( "direct:processCompletion" )
.end()
.removeHeaders( "CamelHttp*" )
.to( "{{to}}" );
from( "direct:processCompletion" )
.startupOrder( 1 )
.routeId(camelRouteId + ".completion" )
.choice()
.when(simple( "${exception} == null" ))
.log( "{{messageOk}}" )
.otherwise()
.log(LoggingLevel.ERROR, "{{messageError}}" )
.end();
}
} public void checkProperties() {
Validate.notNull(camelRouteId, "camelRouteId property is not set" );
Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set" );
Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set" );
Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set" );
Validate.notNull(maximumRedeliveryDelay,
"maximumRedeliveryDelay property is not set" );
}
}
USING APACHE CAMEL SCR BUNDLE AS A T EMPLAT E
If you have a Camel SCR bundle that implements an integration pattern that you use frequently, then, probably there is no need to create a separate bundle for every instance.
The following example illustrates how to use a Camel SCR bundle as a template. It includes the following steps:
1. Create a configuration PID for your service component and add a tail with a dash.
34 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
2. Camel SCR will use the configuration to create a new instance of your component.
3. Finally, you can start a new C amel C o ntext with your overridden properties.
# Create a PID with a tail karaf@ root> config:edit example.CamelScrExample-anotherone
# Override some properties karaf@ root> config:propset camelContextId my-other-context karaf@ root> config:propset to "file://removeme?
fileName=removemetoo.txt"
# Save the PID karaf@ root> config:update
No t e
Make sure that your service component does not start with the default configuration. To prevent this, add po l i cy =
C o nfi g urati o nP o l i cy. R EQ UIR E to the class level at component annotation.
USING APACHE CAMEL-ARCHET YPE-SCR
350
With the help of Apache camel -archetype-scr and maven, you can easily create Apache
Camel SCR bundle project. It includes the following steps:
1. Run the following command:
$ mvn archetype:generate -
Dfilter=org.apache.camel.archetypes:camel-archetype-scr
Choose archetype: local -> org.apache.camel.archetypes:camel-archetype-scr
(Creates a new Camel SCR bundle project for Karaf)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
Define value for property 'groupId': : example
[INFO] Using property: groupId = example
Define value for property 'artifactId' : : camel-scr-example
Define value for property 'version' : 1.0
-SNAPSHOT: :
Define value for property 'package' : example: :
[INFO] Using property: archetypeArtifactId = camel-archetype-scr
[INFO] Using property: archetypeGroupId = org.apache.camel.archetypes
[INFO] Using property: archetypeVersion = 2.15
-SNAPSHOT
Define value for property 'className' : : CamelScrExample
Confirm properties configuration: groupId: example artifactId: camel-scr-example version: 1.0
-SNAPSHOT package : example
CHAPT ER 1 2 . SERVICE CO MPO NENT RUNT IME
archetypeArtifactId: camel-archetype-scr archetypeGroupId: org.apache.camel.archetypes
archetypeVersion: 2.15
-SNAPSHOT className: CamelScrExample
Y: :
2. Run Apache Maven.
No t e
For details on setting up Apache Maven to work with Red Hat JBoss Fuse, see Building with Maven in Red Hat JBoss Fuse Deploying into the
Container on the Red Hat Customer Portal
3. You can now deploy the bundle. To deploy the bundle on Apache Karaf, perform the following steps on Karaf command line:
# Add Camel feature repository karaf@ root> features:chooseurl camel 2.15
-SNAPSHOT
# Install camel-scr feature karaf@ root> features:install camel-scr
# Install commons-lang, used to validate parameters karaf@ root> osgi:install mvn:commons-lang/commons-lang/ 2.6
# Install and start your bundle karaf@ root> osgi:install -s mvn:example/camel-scr-example/ 1.0
-
SNAPSHOT
# View the log.
karaf@ root> log:tail -n 10
Press ctrl-c to stop the log.
4. By default, the Service Component's configuration PID equals the fully qualified name of its class. You can change the properties of a bundle with Apache Karaf's
co nfi g . * commands:
# Override the messageOk property karaf@ root> config:propset -p example.CamelScrExample messageOk
"This is better logging" .
You can also change the configuration by editing the property file in Apache Karaf's
etc directory.
351
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
PART II. ROUTING EXPRESSION AND PREDICATE
LANGUAGES
Ab st ract
This guide describes the basic syntax used by the evaluative languages supported by
Apache Camel.
352
CHAPT ER 1 3. INT RO DUCT IO N
CHAPTER 13. INTRODUCTION
Ab st ract
This chapter provides an overview of all the expression languages supported by Apache
Camel.
13.1. OVERVIEW OF T HE LANGUAGES
T able of expression and predicat e languages
Table 13.1, “ Expression and Predicate Languages”
gives an overview of the different syntaxes for invoking expression and predicate languages.
T ab le 13.1. Exp ressio n an d Pred icat e Lan g u ag es
Language
St at ic
Met hod bean()
Fluent DSL
Met hod
EIP(). met ho d ()
XML
Element metho d
Annot at ion Art ifact
@ Bean
Camel core
co nstant(
) el () g ro o vy()
EIP(). co n stant() co nstant
EIP(). el (
) el
EIP(). g ro o vy() g ro o vy head er() javaScri p t() sq l ()
EIP(). hea d er() head er
EIP(). jav aScri pt() javaScri p t
EIP(). sq l () sq l
None
None
EIP(). jso npath()
EIP(). jxp ath() jso npath jxpath
@ C o nstant
@ EL
@ G ro o vy
@ Head er
Camel core
camel juel camel g ro o vy
Camel core
@ JavaScri pt
@ SQ L camel scri pt camel jo sq l
@ JSo nP ath camel jso npath
@ JXP ath camel jxpath
353
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Language
St at ic
Met hod mvel () o g nl () php() pro perty(
) pytho n() ref() ruby() si mpl e() spel () xpath() xq uery()
Fluent DSL
Met hod
XML
Element
EIP(). mve l () mvel
EIP(). o g nl () o g nl
EIP(). php
() php
EIP(). pro perty() pro perty
EIP(). pyt ho n() pytho n ref EIP(). ref
()
EIP(). rub y() ruby
EIP(). si m pl e() si mpl e
EIP(). spe l () spel
EIP(). xpa th() xpath
EIP(). xq u ery() xq uery
Annot at ion Art ifact
@ MVEL
@ O G NL camel mvel camel o g nl
@ P HP camel scri pt
@ P ro perty
Camel core
@ P ytho n
N/A
@ R uby
@ Si mpl e
@ SpEL
@ XP ath
@ XQ uery camel scri pt
Camel core
camel scri pt
Camel core
camel spri ng
Camel core
camel saxo n
13.2. HOW T O INVOKE AN EXPRESSION LANGUAGE
Prerequisit es
Before you can use a particular expression language, you must ensure that the required JAR files are available on the classpath. If the language you want to use is not included in the
Apache Camel core, you must add the relevant JARs to your classpath.
354
If you are using the Maven build system, you can modify the build-time classpath simply by adding the relevant dependency to your POM file. For example, if you want to use the Ruby language, add the following dependency to your POM file:
<dependency>
<groupId>org.apache.camel</groupId>
CHAPT ER 1 3. INT RO DUCT IO N
<artifactId>camel-script</artifactId>
<!-- Use the same version as your Camel core version -->
<version>${camel.version}</version>
</dependency>
If you are going to deploy your application in a Red Hat JBoss Fuse OSGi container, you also need to ensure that the relevant language features are installed (features are named after the corresponding Maven artifact). For example, to use the Groovy language in the
OSGi container, you must first install the camel -g ro o vy feature by entering the following
OSGi console command: karaf@ root> features:install camel-groovy
No t e
If you are using an expression or predicate in the routes, refer the value as an external resource by using reso urce: cl asspath: path or
reso urce: fi l e: path. For example,
reso urce: cl asspath: co m/fo o /myscri pt. g ro o vy.
Camel on EAP deployment
The camel -scri pt component and the camel -g ro o vy component are both supported by the Camel on EAP (Wildfly Camel) framework, which offers a simplified deployment model on the Red Hat JBoss Enterprise Application Platform (JBoss EAP) container. For details of this model, see chapter "Apache Camel on Red Hat JBoss EAP" in "Deploying into a Web
Server" .
Approaches t o invoking
As shown in Table 13.1, “ Expression and Predicate Languages” , there are several different
syntaxes for invoking an expression language, depending on the context in which it is used.
You can invoke an expression language:
the section called “ As a static method”
.
the section called “ As a fluent DSL method”
.
the section called “ As an XML element”
.
the section called “ As an annotation”
.
As a st at ic met hod
355
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Most of the languages define a static method that can be used in any context where an
o rg . apache. camel . Expressi o n type or an o rg . apache. camel . P red i cate type is expected. The static method takes a string expression (or predicate) as its argument and returns an Expressi o n object (which is usually also a P red i cate object).
For example, to implement a content-based router that processes messages in XML format, you could route messages based on the value of the /o rd er/ad d ress/co untryC o d e element, as follows: from("SourceURL")
.choice
.when(xpath("/o rd er/ad d ress/co untryC o d e = ' us' "))
.to("file://countries/us/")
.when(xpath("/o rd er/ad d ress/co untryC o d e = ' uk' "))
.to("file://countries/uk/")
.otherwise()
.to("file://countries/other/")
.to("TargetURL");
As a fluent DSL met hod
The Java fluent DSL supports another style of invoking expression languages. Instead of providing the expression as an argument to an Enterprise Integration Pattern (EIP), you can provide the expression as a sub-clause of the DSL command. For example, instead of invoking an XPath expression as fi l ter(xpath("Expression")), you can invoke the expression as, fi l ter(). xpath("Expression").
For example, the preceding content-based router can be re-implemented in this style of invocation, as follows: from("SourceURL")
.choice
.when().xpath("/o rd er/ad d ress/co untryC o d e = ' us' ")
.to("file://countries/us/")
.when().xpath("/o rd er/ad d ress/co untryC o d e = ' uk' ")
.to("file://countries/uk/")
.otherwise()
.to("file://countries/other/")
.to("TargetURL");
As an XML element
You can also invoke an expression language in XML, by putting the expression string inside the relevant XML element.
356
For example, the XML element for invoking XPath in XML is xpath (which belongs to the standard Apache Camel namespace). You can use XPath expressions in a XML DSL content-based router, as follows:
<from uri="file://input/orders"/>
<choice>
CHAPT ER 1 3. INT RO DUCT IO N
<when>
<xpath>/o rd er/ad d ress/co untryC o d e = ' us' </xpath>
<to uri="file://countries/us/"/>
</when>
<when>
<xpath>/o rd er/ad d ress/co untryC o d e = ' uk' </xpath>
<to uri="file://countries/uk/"/>
</when>
<otherwise>
<to uri="file://countries/other/"/>
</otherwise>
</choice>
Alternatively, you can specify a language expression using the l ang uag e element, where you specify the name of the language in the l ang uag e attribute. For example, you can define an XPath expression using the l ang uag e element as follows:
<language language="xpath">/order/address/countryCode =
'us'</language>
As an annot at ion
Language annotations are used in the context of bean integration (see Section 2.4, “ Bean
Integration” ). The annotations provide a convenient way of extracting information from a
message or header and then injecting the extracted data into a bean's method parmeters.
For example, consider the bean, myBeanP ro c, which is invoked as a predicate of the
fi l ter() EIP. If the bean's checkC red enti al s method returns true, the message is allowed to proceed; but if the method returns fal se, the message is blocked by the filter. The filter pattern is implemented as follows:
// Java
MyBeanProcessor myBeanProc = new MyBeanProcessor(); from("SourceURL")
.filter().method(myBeanProc, "checkCredentials")
.to("TargetURL");
The implementation of the MyBeanP ro cesso r class exploits the @ XP ath annotation to extract the username and passwo rd from the underlying XML message, as follows:
// Java import org.apache.camel.language.XPath; public class MyBeanProcessor {
boolean void checkCredentials(
@ XP ath("/cred enti al s/username/text()") String user,
@ XP ath("/cred enti al s/passwo rd /text()") String pass
) {
357
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Check the user/pass credentials...
...
}
}
The @ XP ath annotation is placed just before the parameter into which it gets injected. Notice how the XPath expression explicitly selects the text node, by appending /text() to the path, which ensures that just the content of the element is selected, not the enclosing tags.
As a Camel endpoint URI
Using the Camel Language component, you can invoke a supported language in an endpoint URI. There are two alternative syntaxes.
To invoke a language script stored in a file (or other resource type defined by Scheme), use the following URI syntax: language://LanguageName:resource:Scheme:Location[?Options]
Where the scheme can be fi l e: , cl asspath: , or http: .
For example, the following route executes the mysi mpl escri pt. txt from the classpath: from("direct:start")
.to("language:simple:classpath:org/apache/camel/component/language/mys implescript.txt")
.to("mock:result");
To invoke an embedded language script, use the following URI syntax: language://LanguageName[:Script][?Options]
For example, to run the Simple language script stored in the scri pt string:
String script = URLEncoder.encode("Hello ${body}", "UTF-8"); from("direct:start")
.to("language:simple:" + script)
.to("mock:result");
For more details about the Language component, see
Language
in the Apache Camel
Component Reference Guide.
358
CHAPT ER 1 4 . CO NST ANT
CHAPTER 14. CONSTANT
OVERVIEW
The constant language is a trivial built-in language that is used to specify a plain text string.
This makes it possible to provide a plain text string in any context where an expression type is expected.
XML EXAMPLE
In XML, you can set the username header to the value, Jane D o e as follows:
<camelContext>
<route>
<from uri= "SourceURL" />
<setHeader headerName= "username" >
<constant> Jane Doe </constant>
</setHeader>
<to uri= "TargetURL" />
</route>
</camelContext>
JAVA EXAMPLE
In Java, you can set the username header to the value, Jane D o e as follows: from("SourceURL")
.setHeader("username", constant("Jane Doe"))
.to("TargetURL");
359
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 15. EL
OVERVIEW
The Unified Expression Language (EL) was originally specified as part of the JSP 2.1
standard (JSR-245), but it is now available as a standalone language. Apache Camel integrates with JUEL ( http://juel.sourceforge.net/ ), which is an open source implementation of the EL language.
ADDING JUEL PACKAGE
To use EL in your routes you need to add a dependency on camel -juel to your project as shown in
Example 15.1, “ Adding the camel-juel dependency” .
Examp le 15.1. Ad d in g t h e camel- ju el d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-juel </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
360
ST AT IC IMPORT
To use the el () static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.language.juel.JuelExpression.el;
VARIABLES
lists the variables that are accessible when using EL.
T ab le 15.1. EL variab les
CHAPT ER 1 5. EL
Variable exchang e i n o ut
T ype Value o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
EXAMPLE
Example 15.2, “ Routes using EL” shows two routes that use EL.
Examp le 15.2. Ro u t es u sin g EL
<camelContext>
<route>
<from uri= "seda:foo" />
<filter>
<language language= "el" > ${in.headers.foo == 'bar'} </language>
<to uri= "seda:bar" />
</filter>
</route>
<route>
<from uri= "seda:foo2" />
<filter>
<language language= "el" > ${in.headers['My Header'] ==
'bar'} </language>
<to uri= "seda:bar" />
</filter>
</route>
</camelContext>
361
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 16. THE FILE LANGUAGE
Ab st ract
The file language is an extension to the simple language, not an independent language in its own right. But the file language extension can only be used in conjunction with File or
FTP endpoints.
16.1. WHEN T O USE T HE FILE LANGUAGE
Overview
The file language is an extension to the simple language which is not always available. You can use it under the following circumstances:
the section called “ In a File or FTP consumer endpoint”
.
the section called “ On exchanges created by a File or FTP consumer”
.
No t e
The escape character, \, is not available in the file language.
In a File or FT P consumer endpoint
There are several URI options that you can set on a File or FTP consumer endpoint, which take a file language expression as their value. For example, in a File consumer endpoint URI you can set the fi l eName, mo ve, preMo ve, mo veFai l ed , and so rtBy options using a file expression.
In a File consumer endpoint, the fi l eName option acts as a filter, determining which file will actually be read from the starting directory. If a plain text string is specified (for example,
fi l eName= repo rt. txt), the File consumer reads the same file each time it is updated. You can make this option more dynamic, however, by specifying a simple expression. For example, you could use a counter bean to select a different file each time the File consumer polls the starting directory, as follows: file://target/filelanguage/bean/?
fileName=${bean:counter.next}.txt& delete=true
Where the ${bean: co unter. next} expression invokes the next() method on the bean registered under the ID, co unter.
The mo ve option is used to move files to a backup location after then have been read by a
File consumer endpoint. For example, the following endpoint moves files to a backup directory, after they have been processed:
362
CHAPT ER 1 6 . T HE FILE LANG UAG E
file://target/filelanguage/?
move=backup/${date:now:yyyyMMdd}/${file:name.noext}.bak& recursive=fals e
Where the ${fi l e: name. no ext}. bak expression modifies the original file name, replacing the file extension with . bak.
You can use the so rtBy option to specify the order in which file should be processed. For example, to process files according to the alphabetical order of their file name, you could use the following File consumer endpoint: file://target/filelanguage/?sortBy=file:name
To process file according to the order in which they were last modified, you could use the following File consumer endpoint: file://target/filelanguage/?sortBy=file:modified
You can reverse the order by adding the reverse: prefix—for example: file://target/filelanguage/?sortBy=reverse:file:modified
On exchanges creat ed by a File or FT P consumer
When an exchange originates from a File or FTP consumer endpoint, it is possible to apply file language expressions to the exchange throughout the route (as long as the original message headers are not erased). For example, you could define a content-based router, which routes messages according to their file extension, as follows:
<from uri="file://input/orders"/>
<choice>
<when>
<simple>${file:ext} == 'txt'</simple>
<to uri="bean:orderService?method=handleTextFiles"/>
</when>
<when>
<simple>${file:ext} == 'xml'</simple>
<to uri="bean:orderService?method=handleXmlFiles"/>
</when>
<otherwise>
<to uri="bean:orderService?method=handleOtherFiles"/>
</otherwise>
</choice>
16.2. FILE VARIABLES
Overview
363
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
File variables can be used whenever a route starts with a File or FTP consumer endpoint, which implies that the underlying message body is of java. i o . Fi l e type. The file variables enable you to access various parts of the file pathname, almost as if you were invoking the methods of the java. i o . Fi l e class (in fact, the file language extracts the information it needs from message headers that have been set by the File or FTP endpoint).
St art ing direct ory
Some of file variables return paths that are defined relative to a starting directory, which is just the directory that is specified in the File or FTP endpoint. For example, the following File consumer endpoint has the starting directory, . /fi l etransfer (a relative path): file:filetransfer
The following FTP consumer endpoint has the starting directory, . /ftptransfer (a relative path): ftp://myhost:2100/ftptransfer
Naming convent ion of file variables
In general, the file variables are named after corresponding methods on the
java. i o . Fi l e class. For example, the fi l e: abso l ute variable gives the value that would be returned by the java. i o . Fi l e. g etAbso l ute() method.
No t e
This naming convention is not strictly followed, however. For example, there is no such method as java. i o . Fi l e. g etSi ze().
T able of variables
Table 16.1, “ Variables for the File Language”
shows all of the variable supported by the file language.
T ab le 16 .1. Variab les f o r t h e File Lan g u ag e
Variable fi l e: name fi l e: name. ext
T ype
Stri ng
Stri ng
Descript ion
The p athname relative to the starting d irecto ry.
The file extensio n
(characters fo llo wing the last
.
character in the p athname).
364
CHAPT ER 1 6 . T HE FILE LANG UAG E
Variable fi l e: name. no ext fi l e: o nl yname fi l e: o nl yname. no ext Stri ng fi l e: ext fi l e: parent fi l e: path fi l e: abso l ute fi l e: abso l ute. path Stri ng fi l e: l eng th
T ype
Stri ng
Stri ng
Stri ng
Stri ng
Stri ng
Bo o l ean
Lo ng
Descript ion
The p athname relative to the starting d irecto ry, o mitting the file extensio n.
The final seg ment o f the p athname. That is, the file name witho ut the p arent d irecto ry p ath.
The final seg ment o f the p athname, o mitting the file extensio n.
The file extensio n (same as
fi l e: name. ext
).
The p athname o f the p arent d irecto ry, includ ing the starting d irecto ry in the p ath.
The file p athname, includ ing the starting d irecto ry in the p ath.
true
, if the starting d irecto ry was sp ecified as an ab so lute p ath;
fal se
, o therwise.
The ab so lute p athname o f the file.
The size o f the referenced file.
Same as
file:length
.
Date last mo d ified .
fi l e: si ze fi l e: mo d i fi ed
Lo ng java. uti l . D ate
16.3. EXAMPLES
Relat ive pat hname
Consider a File consumer endpoint, where the starting directory is specified as a relative
pathname. For example, the following File endpoint has the starting directory,
. /fi l el ang uag e: file://filelanguage
365
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Now, while scanning the fi l el ang uag e directory, suppose that the endpoint has just consumed the following file:
./filelanguage/test/hello.txt
And, finally, assume that the fi l el ang uag e directory itself has the following absolute location:
/workspace/camel/camel-core/target/filelanguage
Given the preceding scenario, the file language variables return the following values, when applied to the current exchange:
Expression fi l e: name fi l e: name. ext fi l e: name. no ext fi l e: o nl yname fi l e: o nl yname. no ext fi l e: ext fi l e: parent fi l e: path fi l e: abso l ute fi l e: abso l ute. path
Result test/hel l o . txt txt test/hel l o hel l o . txt hel l o txt fi l el ang uag e/test fi l el ang uag e/test/hel l o . txt fal se
/wo rkspace/camel /camel co re/targ et/fi l el ang uag e/test/he l l o . txt
Absolut e pat hname
Consider a File consumer endpoint, where the starting directory is specified as an absolute
pathname. For example, the following File endpoint has the starting directory,
/wo rkspace/camel /camel -co re/targ et/fi l el ang uag e: file:///workspace/camel/camel-core/target/filelanguage
Now, while scanning the fi l el ang uag e directory, suppose that the endpoint has just consumed the following file:
366
CHAPT ER 1 6 . T HE FILE LANG UAG E
./filelanguage/test/hello.txt
Given the preceding scenario, the file language variables return the following values, when applied to the current exchange:
Expression fi l e: name fi l e: name. ext fi l e: name. no ext fi l e: o nl yname fi l e: o nl yname. no ext fi l e: ext fi l e: parent fi l e: path fi l e: abso l ute fi l e: abso l ute. path
Result test/hel l o . txt txt test/hel l o hel l o . txt hel l o txt
/wo rkspace/camel /camel co re/targ et/fi l el ang uag e/test
/wo rkspace/camel /camel co re/targ et/fi l el ang uag e/test/he l l o . txt true
/wo rkspace/camel /camel co re/targ et/fi l el ang uag e/test/he l l o . txt
367
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 17. GROOVY
OVERVIEW
Groovy is a Java-based scripting language that allows quick parsing of object. The Groovy support is part of the camel -scri pt module.
ADDING T HE SCRIPT MODULE
To use Groovy in your routes you need to add a dependency on camel -scri pt to your
project as shown in Example 17.1, “ Adding the camel-script dependency”
.
Examp le 17.1. Ad d in g t h e camel- scrip t d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-script </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the g ro o vy() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.camel.script.ScriptBuilder.*;
BUILT -IN AT T RIBUT ES
Table 17.1, “ Groovy attributes”
lists the built-in attributes that are accessible when using
Groovy.
T ab le 17.1. G ro o vy at t rib u t es
368
At t ribut e co ntext exchang e req uest respo nse pro perti es
CHAPT ER 1 7 . G RO O VY
T ype Value o rg . apache. camel . C am el C o ntext
The Camel Co ntext
o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
o rg . apache. camel . bui l d er. scri pt. P ro perti esFuncti o n
Functio n with a
reso l ve
metho d to make it easier to use the p ro p erties co mp o nent insid e scrip ts.
The attributes all set at ENG INE_SC O P E.
EXAMPLE
Example 17.2, “ Routes using Groovy” shows two routes that use Groovy scripts.
Examp le 17.2. Ro u t es u sin g G ro o vy
<camelContext>
<route>
<from uri= "direct:items" />
<filter>
<language language= "groovy" > request.lineItems.any { i -> i.value > 100 } </language>
<to uri= "mock:mock1" />
</filter>
</route>
<route>
<from uri= "direct:in" />
<setHeader headerName= "firstName" >
<language language= "groovy" > $user.firstName
$user.lastName
</language>
</setHeader>
<to uri= "seda:users" />
</route>
</camelContext>
USING T HE PROPERT IES COMPONENT
369
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To access a property value from the properties component, invoke the reso l ve method on the built-in pro perti es attribute, as follows:
.setHeader("myHeader").groovy("properties.resolve(PropKey)")
Where PropKey is the key of the property you want to resolve, where the key value is of
Stri ng type.
For more details about the properties component, see
Properties
in the Apache Camel
Component Reference Guide.
CUST OMIZING GROOVY SHELL
Sometimes, you might need to use the custom G ro o vyShel l instance, in your Groovy expressions. To provide custom G ro o vyShel l , add an implementation of the
o rg . apache. camel . l ang uag e. g ro o vy. G ro o vyShel l Facto ry SP I interface to your Camel registry.
For example, when you add the following bean to your Spring context, Apache Camel will use the custom G ro o vyShel l instance that includes the custom static imports, instead of the default one.
public class CustomGroovyShellFactory implements GroovyShellFactory { public GroovyShell createGroovyShell(Exchange exchange) {
ImportCustomizer importCustomizer = new ImportCustomizer();
importCustomizer.addStaticStars( "com.example.Utils" );
CompilerConfiguration configuration = new CompilerConfiguration();
configuration.addCompilationCustomizers(importCustomizer); return new GroovyShell(configuration);
}
}
370
CHAPT ER 1 8 . HEADER
CHAPTER 18. HEADER
OVERVIEW
The header language provides a convenient way of accessing header values in the current message. When you supply a header name, the header language performs a caseinsensitive lookup and returns the corresponding header value.
The header language is part of camel -co re.
XML EXAMPLE
For example, to resequence incoming exchanges according to the value of a
Seq uenceNumber header (where the sequence number must be a positive integer), you can define a route as follows:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="SourceURL"/>
<resequence>
<language language="header">SequenceNumber</language>
</resequence>
<to uri="TargetURL"/>
</route>
</camelContext>
JAVA EXAMPLE
The same route can be defined in Java, as follows: from("SourceURL")
.resequence(header("SequenceNumber"))
.to("TargetURL");
371
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 19. JAVASCRIPT
OVERVIEW
JavaScript, also known as ECMAScript is a Java-based scripting language that allows quick parsing of object. The JavaScript support is part of the camel -scri pt module.
ADDING T HE SCRIPT MODULE
To use JavaScript in your routes you need to add a dependency on camel -scri pt to your
project as shown in Example 19.1, “ Adding the camel-script dependency”
.
Examp le 19 .1. Ad d in g t h e camel- scrip t d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-script </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the javaScri pt() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.camel.script.ScriptBuilder.*;
BUILT -IN AT T RIBUT ES
Table 19.1, “ JavaScript attributes” lists the built-in attributes that are accessible when using
JavaScript.
T ab le 19 .1. JavaScrip t at t rib u t es
372
At t ribut e co ntext exchang e req uest respo nse pro perti es
CHAPT ER 1 9 . JAVASCRIPT
T ype Value o rg . apache. camel . C am el C o ntext
The Camel Co ntext
o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
o rg . apache. camel . bui l d er. scri pt. P ro perti esFuncti o n
Functio n with a
reso l ve
metho d to make it easier to use the p ro p erties co mp o nent insid e scrip ts.
The attributes all set at ENG INE_SC O P E.
EXAMPLE
Example 19.2, “ Route using JavaScript”
shows a route that uses JavaScript.
Examp le 19 .2. Ro u t e u sin g JavaScrip t
<camelContext>
<route>
<from uri= "direct:start" />
<choice>
<when>
<langauge langauge= "javaScript" > request.headers.get('user')
== 'admin' </langauge>
<to uri= "seda:adminQueue" />
</when>
<otherwise>
<to uri= "seda:regularQueue" />
</otherwise>
</choice>
</route>
</camelContext>
USING T HE PROPERT IES COMPONENT
To access a property value from the properties component, invoke the reso l ve method on the built-in pro perti es attribute, as follows:
373
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
.setHeader("myHeader").javaScript("properties.resolve(PropKey)")
Where PropKey is the key of the property you want to resolve, where the key value is of
Stri ng type.
For more details about the properties component, see
Properties
in the Apache Camel
Component Reference Guide.
374
CHAPT ER 2 0 . JO SQ L
CHAPTER 20. JOSQL
OVERVIEW
The JoSQL (SQL for Java objects) language enables you to evaluate predicates and expressions in Apache Camel. JoSQL employs a SQL-like query syntax to perform selection and ordering operations on data from in-memory Java objects—however, JoSQL is not a database. In the JoSQL syntax, each Java object instance is treated like a table row and each object method is treated like a column name. Using this syntax, it is possible to construct powerful statements for extracting and compiling data from collections of Java objects. For details, see http://josql.sourceforge.net/ .
ADDING T HE JOSQL MODULE
To use JoSQL in your routes you need to add a dependency on camel -jo sq l to your
project as shown in Example 20.1, “ Adding the camel-josql dependency” .
Examp le 20.1. Ad d in g t h e camel- jo sq l d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-josql </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the sq l () static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.sql.SqlBuilder.sql;
VARIABLES
lists the variables that are accessible when using JoSQL.
T ab le 20.1. SQ L variab les
375
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Name exchang e i n o ut
property header variable
T ype o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
O bject
the Exchang e p ro p erty who se key is property
O bject
O bject
Descript ion
the IN messag e head er who se key is header the variab le who se key is
variable
EXAMPLE
Example 20.2, “ Route using JoSQL” shows a route that uses JoSQL.
Examp le 20.2. Ro u t e u sin g Jo SQ L
<camelContext>
<route>
<from uri= "direct:start" />
<setBody>
<language language= "sql" > select * from MyType </language>
</setBody>
<to uri= "seda:regularQueue" />
</route>
</camelContext>
376
CHAPT ER 2 1 . JSO NPAT H
CHAPTER 21. JSONPATH
OVERVIEW
The JSonPath language provides a convenient syntax for extracting portions of a JSon message. The syntax of JSon is similar to XPath, but it is used to extract JSon objects from a
JSon message, instead of acting on XML. The jso npath DSL command can be used either as an expression or as a predicate (where an empty result gets interpreted as boolean
fal se).
ADDING T HE JSONPAT H PACKAGE
To use JSonPath in your Camel routes, you need to add a dependency on camel -
jso npath to your project, as follows:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jsonpath</artifactId>
<version>${camel-version}</version>
</dependency>
JAVA EXAMPLE
The following Java example shows how to use the jso npath() DSL command to select items in a certain price range: from("queue:books.new")
.choice()
.when().jsonpath("$.store.book[?(@ .price < 10)]")
.to("jms:queue:book.cheap")
.when().jsonpath("$.store.book[?(@ .price < 30)]")
.to("jms:queue:book.average")
.otherwise()
.to("jms:queue:book.expensive")
If the JSonPath query returns an empty set, the result is interpreted as fal se. In this way, you can use a JSonPath query as a predicate.
XML EXAMPLE
The following XML example shows how to use the jso npath DSL element to define predicates in a route:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<choice>
377
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<when>
<jsonpath>$.store.book[?(@ .price & lt; 10)]</jsonpath>
<to uri="mock:cheap"/>
</when>
<when>
<jsonpath>$.store.book[?(@ .price & lt; 30)]</jsonpath>
<to uri="mock:average"/>
</when>
<otherwise>
<to uri="mock:expensive"/>
</otherwise>
</choice>
</route>
</camelContext>
SUPPRESS EXCEPT IONS
JSonPath will throw an exception if the path configured by the jso npath expression is not found. The exception can be ignored by setting the SuppressExcepti o ns option to true.
For example, in the code below, adding the true option as part of the jso npath parameters: from("direct:start")
.choice()
// use true to suppress exceptions
.when().jsonpath("person.middlename", true)
.to("mock:middle")
.otherwise()
.to("mock:other");
In XML DSL use the following syntax:
<route>
<from uri= "direct:start" />
<choice>
<when>
<jsonpath suppressExceptions= "true" > person.middlename
</jsonpath>
<to uri= "mock:middle" />
</when>
<otherwise>
<to uri= "mock:other" />
</otherwise>
</choice>
</route>
378
JSONPAT H INJECT ION
When using bean integration to invoke a bean method, you can use JSonPath to extract a value from the message and bind it to a method parameter. For example:
// Java public class Foo {
CHAPT ER 2 1 . JSO NPAT H
@ Consume(uri = "activemq:queue:books.new")
public void doSomething(@ JsonPath("$.store.book[*].author") String author, @ Body String json) {
// process the inbound message here
}
}
REFERENCE
For more details about JSonPath, see the JSonPath project page .
379
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 22. JXPATH
OVERVIEW
The JXPath language enables you to invoke Java beans using the Apache Commons
JXPath language. The JXPath language has a similar syntax to XPath, but instead of selecting element or attribute nodes from an XML document, it invokes methods on an object graph of Java beans. If one of the bean attributes returns an XML document (a DOM/JDOM instance), however, the remaining portion of the path is interpreted as an XPath expression and is used to extract an XML node from the document. In other words, the JXPath language provides a hybrid of object graph navigation and XML node selection.
ADDING JXPAT H PACKAGE
To use JXPath in your routes you need to add a dependency on camel -jxpath to your
project as shown in Example 22.1, “ Adding the camel-jxpath dependency” .
Examp le 22.1. Ad d in g t h e camel- jxp at h d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-jxpath </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
380
VARIABLES
Table 22.1, “ JXPath variables” lists the variables that are accessible when using JXPath.
T ab le 22.1. JXPat h variab les
Variable thi s
T ype Value o rg . apache. camel . Exc hang e
The current Exchang e
CHAPT ER 2 2 . JXPAT H
Variable i n o ut
T ype Value o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
EXAMPLE
Example 22.2, “ Routes using JXPath”
shows a route that use JXPath.
Examp le 22.2. Ro u t es u sin g JXPat h
<camelContext>
<route>
<from uri= "activemq:MyQueue" />
<filter>
<jxpath> in/body/name = 'James' </xpath>
<to uri= "mqseries:SomeOtherQueue" />
</filter>
</route>
</camelContext>
381
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 23. MVEL
OVERVIEW
MVEL is a Java-based dynamic language that is similar to OGNL, but is reported to be much faster. The MVEL support is in the camel -mvel module.
SYNT AX
You use the MVEL dot syntax to invoke Java methods, for example: getRequest().getBody().getFamilyName()
Because MVEL is dynamically typed, it is unnecessary to cast the message body instance (of
O bject type) before invoking the g etFami l yName() method. You can also use an abbreviated syntax for invoking bean attributes, for example: request.body.familyName
ADDING T HE MVEL MODULE
To use MVEL in your routes you need to add a dependency on camel -mvel to your project
as shown in Example 23.1, “ Adding the camel-mvel dependency” .
Examp le 23.1. Ad d in g t h e camel- mvel d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-mvel </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
BUILT -IN VARIABLES
382
CHAPT ER 2 3. MVEL
Table 23.1, “ MVEL variables” lists the built-in variables that are accessible when using
MVEL.
T ab le 23.1. MVEL variab les
Name thi s exchang e excepti o n exchang eID faul t req uest respo nse pro perti es pro perty(name) pro perty(name, type)
T ype Descript ion o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Exc hang e
The current Exchang e
T hro wabl e
the Exchang e excep tio n (if any)
Stri ng o rg . apache. camel . Mes sag e
the Exchang e ID
The Fault messag e(if any)
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
Map
O bject
The Exchang e p ro p erties
The value o f the named
Exchang e p ro p erty
Type
The typ ed value o f the named Exchang e p ro p erty
EXAMPLE
Example 23.2, “ Route using MVEL”
shows a route that uses MVEL.
Examp le 23.2. Ro u t e u sin g MVEL
<camelContext>
<route>
<from uri= "seda:foo" />
<filter>
<language langauge= "mvel" > request.headers.foo ==
'bar' </language>
383
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri= "seda:bar" />
</filter>
</route>
</camelContext>
384
CHAPT ER 2 4 . T HE O BJECT - G RAPH NAVIG AT IO N LANG UAG E(O G NL)
CHAPTER 24. THE OBJECT-GRAPH NAVIGATION
LANGUAGE(OGNL)
OVERVIEW
OGNL is an expression language for getting and setting properties of Java objects. You use the same expression for both getting and setting the value of a property. The OGNL support is in the camel -o g nl module.
CAMEL ON EAP DEPLOYMENT
This component is supported by the Camel on EAP (Wildfly Camel) framework, which offers a simplified deployment model on the Red Hat JBoss Enterprise Application Platform (JBoss
EAP) container. For details of this model, see chapter "Apache Camel on Red Hat JBoss
EAP" in "Deploying into a Web Server" .
ADDING T HE OGNL MODULE
To use OGNL in your routes you need to add a dependency on camel -o g nl to your project
as shown in Example 24.1, “ Adding the camel-ognl dependency”
.
Examp le 24 .1. Ad d in g t h e camel- o g n l d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-ognl </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the o g nl () static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.language.ognl.OgnlExpression.ognl;
BUILT -IN VARIABLES
385
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Table 24.1, “ OGNL variables” lists the built-in variables that are accessible when using
OGNL.
T ab le 24 .1. O G NL variab les
Name thi s exchang e excepti o n exchang eID faul t req uest respo nse pro perti es pro perty(name) pro perty(name, type)
T ype Descript ion o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Exc hang e
The current Exchang e
T hro wabl e
the Exchang e excep tio n (if any)
Stri ng o rg . apache. camel . Mes sag e
the Exchang e ID
The Fault messag e(if any)
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
Map
O bject
The Exchang e p ro p erties
The value o f the named
Exchang e p ro p erty
Type
The typ ed value o f the named Exchang e p ro p erty
EXAMPLE
Example 24.2, “ Route using OGNL”
shows a route that uses OGNL.
Examp le 24 .2. Ro u t e u sin g O G NL
<camelContext>
<route>
<from uri= "seda:foo" />
<filter>
<language langauge= "ognl" > request.headers.foo ==
'bar' </language>
386
CHAPT ER 2 4 . T HE O BJECT - G RAPH NAVIG AT IO N LANG UAG E(O G NL)
<to uri= "seda:bar" />
</filter>
</route>
</camelContext>
387
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 25. PHP
OVERVIEW
PHP is a widely-used general-purpose scripting language that is especially suited for Web development. The PHP support is part of the camel -scri pt module.
ADDING T HE SCRIPT MODULE
To use PHP in your routes you need to add a dependency on camel -scri pt to your
project as shown in Example 25.1, “ Adding the camel-script dependency” .
Examp le 25.1. Ad d in g t h e camel- scrip t d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-script </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
388
ST AT IC IMPORT
To use the php() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.camel.script.ScriptBuilder.*;
BUILT -IN AT T RIBUT ES
Table 25.1, “ PHP attributes” lists the built-in attributes that are accessible when using PHP.
T ab le 25.1. PHP at t rib u t es
At t ribut e co ntext
T ype Value o rg . apache. camel . C am el C o ntext
The Camel Co ntext
At t ribut e exchang e req uest respo nse pro perti es
CHAPT ER 2 5. PHP
T ype Value o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
o rg . apache. camel . bui l d er. scri pt. P ro perti esFuncti o n
Functio n with a
reso l ve
metho d to make it easier to use the p ro p erties co mp o nent insid e scrip ts.
The attributes all set at ENG INE_SC O P E.
EXAMPLE
Example 25.2, “ Route using PHP”
shows a route that uses PHP.
Examp le 25.2. Ro u t e u sin g PHP
<camelContext>
<route>
<from uri= "direct:start" />
<choice>
<when>
<language language= "php" > strpos(request.headers.get('user'),
'admin')!== FALSE </language>
<to uri= "seda:adminQueue" />
</when>
<otherwise>
<to uri= "seda:regularQueue" />
</otherwise>
</choice>
</route>
</camelContext>
USING T HE PROPERT IES COMPONENT
To access a property value from the properties component, invoke the reso l ve method on the built-in pro perti es attribute, as follows:
.setHeader("myHeader").php("properties.resolve(PropKey)")
389
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where PropKey is the key of the property you want to resolve, where the key value is of
Stri ng type.
For more details about the properties component, see
Properties
in the Apache Camel
Component Reference Guide.
390
CHAPT ER 2 6 . EXCHANG E PRO PERT Y
CHAPTER 26. EXCHANGE PROPERTY
OVERVIEW
The exchange property language provides a convenient way of accessing exchange
properties. When you supply a key that matches one of the exchange property names, the exchange property language returns the corresponding value.
The exchange property language is part of camel -co re.
XML EXAMPLE
For example, to implement the recipient list pattern when the l i stO fEnd po i nts exchange property contains the recipient list, you could define a route as follows:
<camelContext>
<route>
<from uri="direct:a"/>
<recipientList>
<exchangeProperty>listOfEndpoints</exchangeProperty>
</recipientList>
</route>
</camelContext>
JAVA EXAMPLE
The same recipient list example can be implemented in Java as follows: from("direct:a").recipientList(exchangeProperty("listOfEndpoints"));
391
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 27. PYTHON
OVERVIEW
Python is a remarkably powerful dynamic programming language that is used in a wide variety of application domains. Python is often compared to Tcl, Perl, Ruby, Scheme or
Java. The Python support is part of the camel -scri pt module.
ADDING T HE SCRIPT MODULE
To use Python in your routes you need to add a dependency on camel -scri pt to your
project as shown in Example 27.1, “ Adding the camel-script dependency”
.
Examp le 27.1. Ad d in g t h e camel- scrip t d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-script </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the pytho n() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.camel.script.ScriptBuilder.*;
BUILT -IN AT T RIBUT ES
Table 27.1, “ Python attributes”
lists the built-in attributes that are accessible when using
Python.
T ab le 27.1. Pyt h o n at t rib u t es
392
At t ribut e co ntext exchang e req uest respo nse pro perti es
CHAPT ER 2 7 . PYT HO N
T ype Value o rg . apache. camel . C am el C o ntext
The Camel Co ntext
o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
o rg . apache. camel . bui l d er. scri pt. P ro perti esFuncti o n
Functio n with a
reso l ve
metho d to make it easier to use the p ro p erties co mp o nent insid e scrip ts.
The attributes all set at ENG INE_SC O P E.
EXAMPLE
Example 27.2, “ Route using Python” shows a route that uses Python.
Examp le 27.2. Ro u t e u sin g Pyt h o n
<camelContext>
<route>
<from uri= "direct:start" />
<choice>
<when>
<langauge langauge= "python" > if request.headers.get('user')
= 'admin' </langauge>
<to uri= "seda:adminQueue" />
</when>
<otherwise>
<to uri= "seda:regularQueue" />
</otherwise>
</choice>
</route>
</camelContext>
USING T HE PROPERT IES COMPONENT
To access a property value from the properties component, invoke the reso l ve method on the built-in pro perti es attribute, as follows:
393
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
.setHeader("myHeader").python("properties.resolve(PropKey)")
Where PropKey is the key of the property you want to resolve, where the key value is of
Stri ng type.
For more details about the properties component, see
Properties
in the Apache Camel
Component Reference Guide.
394
CHAPT ER 2 8 . REF
CHAPTER 28. REF
OVERVIEW
The Ref expression language is really just a way to look up a custom Expression from the
Registry . This is particular convenient to use in the XML DSL.
The Ref language is part of camel -co re.
ST AT IC IMPORT
To use the Ref language in your Java application code, include the following import statement in your Java source files: import static org.apache.camel.language.simple.RefLanguage.ref;
XML EXAMPLE
For example, the splitter pattern can reference a custom expression using the Ref language, as follows:
<beans ...>
<bean id="myExpressi o n" class="com.mycompany.MyCustomExpression"/>
...
<camelContext>
<route>
<from uri="seda:a"/>
<split>
<ref>myExpressi o n</ref>
<to uri="mock:b"/>
</split>
</route>
</camelContext>
</beans>
JAVA EXAMPLE
The preceding route can also be implemented in the Java DSL, as follows: from("seda:a")
.split().ref("myExpression")
.to("seda:b");
395
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 29. RUBY
OVERVIEW
Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write. The Ruby support is part of the camel -scri pt module.
ADDING T HE SCRIPT MODULE
To use Ruby in your routes you need to add a dependency on camel -scri pt to your
project as shown in Example 29.1, “ Adding the camel-script dependency” .
Examp le 29 .1. Ad d in g t h e camel- scrip t d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-script </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
ST AT IC IMPORT
To use the ruby() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.camel.script.ScriptBuilder.*;
BUILT -IN AT T RIBUT ES
Table 29.1, “ Ruby attributes”
lists the built-in attributes that are accessible when using Ruby.
T ab le 29 .1. Ru b y at t rib u t es
At t ribut e T ype Value
396
At t ribut e co ntext exchang e req uest respo nse pro perti es
CHAPT ER 2 9 . RUBY
T ype Value o rg . apache. camel . C am el C o ntext
The Camel Co ntext
o rg . apache. camel . Exc hang e
The current Exchang e
o rg . apache. camel . Mes sag e
The IN messag e
o rg . apache. camel . Mes sag e
The O UT messag e
o rg . apache. camel . bui l d er. scri pt. P ro perti esFuncti o n
Functio n with a
reso l ve
metho d to make it easier to use the p ro p erties co mp o nent insid e scrip ts.
The attributes all set at ENG INE_SC O P E.
EXAMPLE
Example 29.2, “ Route using Ruby”
shows a route that uses Ruby.
Examp le 29 .2. Ro u t e u sin g Ru b y
<camelContext>
<route>
<from uri= "direct:start" />
<choice>
<when>
<langauge langauge= "ruby" > $request.headers['user'] ==
'admin' </langauge>
<to uri= "seda:adminQueue" />
</when>
<otherwise>
<to uri= "seda:regularQueue" />
</otherwise>
</choice>
</route>
</camelContext>
USING T HE PROPERT IES COMPONENT
To access a property value from the properties component, invoke the reso l ve method on the built-in pro perti es attribute, as follows:
397
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
.setHeader("myHeader").ruby("properties.resolve(PropKey)")
Where PropKey is the key of the property you want to resolve, where the key value is of
Stri ng type.
For more details about the properties component, see
Properties
in the Apache Camel
Component Reference Guide.
398
CHAPT ER 30 . T HE SIMPLE LANG UAG E
CHAPTER 30. THE SIMPLE LANGUAGE
Ab st ract
The simple language is a language that was developed in Apache Camel specifically for the purpose of accessing and manipulating the various parts of an exchange object. The language is not quite as simple as when it was originally created and it now features a comprehensive set of logical operators and conjunctions.
30.1. JAVA DSL
Simple expressions in Java DSL
In the Java DSL, there are two styles for using the si mpl e() command in a route. You can either pass the si mpl e() command as an argument to a processor, as follows: from("seda:order")
.filter(simple("${in.header.foo}"))
.to("mock:fooOrders");
Or you can call the si mpl e() command as a sub-clause on the processor, for example: from("seda:order")
.filter()
.simple("${in.header.foo}")
.to("mock:fooOrders");
Embedding in a st ring
If you are embedding a simple expression inside a plain text string, you must use the placeholder syntax, ${Expression}. For example, to embed the i n. head er. name expression in a string: simple("Hello ${in.header.name}, how are you?")
Cust omiz ing t he st art and end t okens
From Java, you can customize the start and end tokens ({ and }, by default) by calling the
chang eFuncti o nStartT o ken static method and the chang eFuncti o nEnd T o ken static method on the Si mpl eLang uag e object.
For example, you can change the start and end tokens to [ and ] in Java, as follows:
// Java import org.apache.camel.language.simple.SimpleLanguage;
...
SimpleLanguage.changeFunctionStartToken("[");
399
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
SimpleLanguage.changeFunctionEndToken("]");
No t e
Customizing the start and end tokens affects all Apache Camel applications that share the same camel -co re library on their classpath. For example, in an OSGi server this might affect many applications; whereas in a Web application (WAR file) it would affect only the Web application itself.
30.2. XML DSL
Simple expressions in XML DSL
In the XML DSL, you can use a simple expression by putting the expression inside a si mpl e element. For example, to define a route that performs filtering based on the contents of the
fo o header:
<route id="simpleExample">
<from uri="seda:orders"/>
<filter>
<simple>${in.header.foo}</simple>
<to uri="mock:fooOrders"/>
</filter>
</route>
Alt ernat ive placeholder synt ax
Sometimes—for example, if you have enabled Spring property placeholders or OSGi blueprint property placeholders—you might find that the ${Expression} syntax clashes with another property placeholder syntax. In this case, you can disambiguate the placeholder using the alternative syntax, $si mpl e{Expression}, for the simple expression. For example:
<simple>Hello $simple{in.header.name}, how are you?</simple>
Cust omiz ing t he st art and end t okens
From XML configuration, you can customize the start and end tokens ({ and }, by default) by overriding the Si mpl eLang uag e instance. For example, to change the start and end tokens to [ and ], define a new Si mpl eLang uag e bean in your XML configuration file, as follows:
<bean id="simple" class="org.apache.camel.language.simple.SimpleLanguage">
<constructor-arg name="functionStartToken" value="["/>
<constructor-arg name="functionEndToken" value="]"/>
</bean>
4 00
CHAPT ER 30 . T HE SIMPLE LANG UAG E
No t e
Customizing the start and end tokens affects all Apache Camel applications that share the same camel -co re library on their classpath. For example, in an OSGi server this might affect many applications; whereas in a Web application (WAR file) it would affect only the Web application itself.
Whit espace and aut o-t rim in XML DSL
By default, whitespace preceding and following a simple expression is automatically trimmed in XML DSL. So this expression with surrounding whitespace:
<transform>
<simple>
data=${body}
</simple>
</transform> is automatically trimmed, so that it is equivalent to this expression (no surrounding whitespace):
<transform>
<simple>data=${body}</simple>
</transform>
If you want to include newlines before or after the expression, you can either explicitly add a newline character, as follows:
<transform>
<simple>data=${body}\n</simple>
</transform> or you can switch off auto-trimming, by setting the tri m attribute to fal se, as follows:
<transform trim="false">
<simple>data=${body}
</simple>
</transform>
30.3. INVOKING AN EXT ERNAL SCRIPT
Overview
It is possible to execute Simple scripts that are stored in an external resource, as described here.
4 01
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Synt ax for script resource
Use the following syntax to access a Simple script that is stored as an external resource: resource:Scheme:Location
Where the Scheme: can be either cl asspath: , fi l e: , or http: .
For example, to read the mysi mpl e. txt script from the classpath, simple("resource:classpath:mysimple.txt")
30.4 . EXPRESSIONS
Overview
The simple language provides various elementary expressions that return different parts of a message exchange. For example, the expression, si mpl e("${head er. ti meO fD ay}"), would return the contents of a header called ti meO fD ay from the incoming message.
No t e
Since Apache Camel 2.9, you must always use the placeholder syntax,
${Expression}, to return a variable value. It is never permissible to omit the enclosing tokens (${ and }).
Cont ent s of a single variable
You can use the simple language to define string expressions, based on the variables provided. For example, you can use a variable of the form, i n. head er. HeaderName, to obtain the value of the HeaderName header, as follows: simple("${in.header.foo}")
Variables embedded in a st ring
You can embed simple variables in a string expression—for example: simple("Received a message from ${in.header.user} on
${date:in.header.date:yyyyMMdd}.")
dat e and bean variables
4 02
CHAPT ER 30 . T HE SIMPLE LANG UAG E
As well as providing variables that access all of the different parts of an exchange (see
Table 30.1, “ Variables for the Simple Language”
), the simple language also provides special variables for formatting dates, d ate: command: pattern, and for calling bean methods,
bean: beanRef. For example, you can use the date and the bean variables as follows: simple("Todays date is ${date:now:yyyyMMdd}") simple("The order type is ${bean:orderService?method=getOrderType}")
Specifying t he result t ype
You can specify the result type of an expression explicitly. This is mainly useful for converting the result type to a boolean or numerical type.
In the Java DSL, specify the result type as an extra argument to si mpl e(). For example, to return an integer result, you could evaluate a simple expression as follows:
...
.setHeader("five", simple("5", Integ er. cl ass))
In the XML DSL, specify the result type using the resul tT ype attribute. For example:
<setHeader headerName="five">
<!-- use resultType to indicate that the type should be a java.lang.Integer -->
<simple resul tT ype= "java. l ang . Integ er">5</simple>
</setHeader>
Nest ed expressions
Simple expressions can be nested—for example: simple("${header.${bean:headerChooser?method=whichHeader}}")
Accessing const ant s or enums
You can access a bean's constant or enum fields using the following syntax: type:ClassName.Field
For example, consider the following Java enum type: package org.apache.camel.processor;
...
public enum Customer {
GOLD, SILVER, BRONZE
}
4 03
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can access the C usto mer enum fields, as follows:
from("direct:start")
.choice()
.when().simple("${header.customer} ==
${type:org.apache.camel.processor.Customer.GOLD}")
.to("mock:gold")
.when().simple("${header.customer} ==
${type:org.apache.camel.processor.Customer.SILVER}")
.to("mock:silver")
.otherwise()
.to("mock:other");
OGNL expressions
The Object Graph Navigation Language (OGNL) is a notation for invoking bean methods in a chain-like fashion. If a message body contains a Java bean, you can easily access its bean properties using OGNL notation. For example, if the message body is a Java object with a g etAd d ress() accessor, you can access the Ad d ress object and the Ad d ress object's properties as follows: simple("${body.address}") simple("${body.address.street}") simple("${body.address.zip}") simple("${body.address.city}")
Where the notation, ${bo d y. ad d ress. street}, is shorthand for
${bo d y. g etAd d ress. g etStreet}.
OGNL null-safe operat or
You can use the null-safe operator, ?. , to avoid encountering null-pointer exceptions, in case the body does not have an address. For example: simple("${body?.address?.street}")
If the body is a java. uti l . Map type, you can look up a value in the map with the key, fo o , using the following notation: simple("${body[foo]?.name}")
4 04
OGNL list element access
You can also use square brackets notation, [k], to access the elements of a list. For example: simple("${body.address.lines[0]}") simple("${body.address.lines[1]}") simple("${body.address.lines[2]}")
CHAPT ER 30 . T HE SIMPLE LANG UAG E
The l ast keyword returns the index of the last element of a list. For example, you can access the second last element of a list, as follows: simple("${body.address.lines[last-1]}")
You can use the si ze method to query the size of a list, as follows: simple("${body.address.lines.size}")
OGNL array lengt h access
You can access the length of a Java array through the l eng th method, as follows:
String[] lines = new String[]{"foo", "bar", "cat"}; exchange.getIn().setBody(lines); simple("There are ${body.length} lines")
30.5. PREDICAT ES
Overview
You can construct predicates by testing expressions for equality. For example, the predicate,
si mpl e("${head er. ti meO fD ay} = = ' 14 : 30 ' "), tests whether the ti meO fD ay header in the incoming message is equal to 14 : 30 .
In addition, whenever the resul tT ype is specified as a Boolean the expression is evaluated as a predicate instead of an expression. This allows the predicate syntax to be used for these expressions.
Synt ax
You can also test various parts of an exchange (headers, message body, and so on) using simple predicates. Simple predicates have the following general syntax:
${LHSVariable} Op RHSValue
Where the variable on the left hand side, LHSVariable, is one of the variables shown in
Table 30.1, “ Variables for the Simple Language”
and the value on the right hand side,
RHSValue, is one of the following:
Another variable, ${RHSVariable}.
A string literal, enclosed in single quotes, ' ' .
A numeric constant, enclosed in single quotes, ' ' .
4 05
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The null object, nul l .
The simple language always attempts to convert the RHS value to the type of the LHS value.
No t e
While the simple language will attempt to convert the RHS, depending on the operator the LHS may need to be cast into the appropriate Type before the comparison is made.
Examples
For example, you can perform simple string comparisons and numerical comparisons as follows: simple("${in.header.user} == 'john'") simple("${in.header.number} > '100'") // String literal can be converted to integer
You can test whether the left hand side is a member of a comma-separated list, as follows: simple("${in.header.type} in 'gold,silver'")
You can test whether the left hand side matches a regular expression, as follows: simple("${in.header.number} regex '\d{4}'")
You can test the type of the left hand side using the i s operator, as follows: simple("${in.header.type} is 'java.lang.String'") simple("${in.header.type} is 'String'") // You can abbreviate java.lang. types
You can test whether the left hand side lies in a specified numerical range (where the range is inclusive), as follows: simple("${in.header.number} range '100..199'")
Conjunct ions
You can also combine predicates using the logical conjunctions, & & and | | .
For example, here is an expression using the & & conjunction (logical and):
4 06
CHAPT ER 30 . T HE SIMPLE LANG UAG E
simple("${in.header.title} contains 'Camel' & & ${in.header.type} ==
'gold'")
And here is an expression using the | | conjunction (logical inclusive or): simple("${in.header.title} contains 'Camel' || ${in.header.type} ==
'gold'")
30.6. VARIABLE REFERENCE
T able of variables
Table 30.1, “ Variables for the Simple Language”
shows all of the variables supported by the simple language.
T ab le 30.1. Variab les f o r t h e Simp le Lan g u ag e
Variable camel C o ntext camel Id exchang eId i d bo d y
T ype
O bject
Stri ng
Stri ng
Stri ng
O bject
Descript ion
The Camel co ntext. Supports
OGNL expressions.
The Camel co ntext's ID value.
The exchang e's ID value.
The In messag e ID value.
i n. bo d y o ut. bo d y
O bject
O bject
The In messag e b o d y.
Supports OGNL expressions.
The In messag e b o d y.
Supports OGNL expressions.
bo d yAs(Type)
Type
The Out messag e b o d y.
The In messag e b o d y, co nverted to the sp ecified typ e. All typ es, Type, must b e sp ecified using their fullyq ualified Java name, excep t fo r the typ es:
byte[]
,
Stri ng
,
Integ er
, and
Lo ng
. The co nverted b o d y can b e null.
4 07
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Variable mand ato ryBo d yAs(Type
)
T ype
Type
Descript ion
The In messag e b o d y, co nverted to the sp ecified typ e. All typ es, Type, must b e sp ecified using their fullyq ualified Java name, excep t fo r the typ es:
byte[]
,
Stri ng
,
Integ er
, and
Lo ng
. The co nverted b o d y is exp ected to b e no n-null.
head er.
HeaderName
O bject
The In messag e's
HeaderName head er.
Supports OGNL expressions.
head er[
HeaderName
] O bject head ers.
head ers[
HeaderName
HeaderName
i n. head er.
i n. head er[
HeaderName
HeaderName
i n. head ers.
i n. head ers[
]
]
]
HeaderName
HeaderName
O bject
O bject
O bject
O bject
O bject
O bject o ut. head er.
HeaderName
O bject
The In messag e's
HeaderName head er
(alternative syntax).
The In messag e's
HeaderName head er.
The In messag e's
HeaderName head er
(alternative syntax).
The In messag e's
HeaderName head er.
Supports OGNL expressions.
The In messag e's
HeaderName head er
(alternative syntax).
The In messag e's
HeaderName head er.
Supports OGNL expressions.
The In messag e's
HeaderName head er
(alternative syntax).
The Out messag e's
HeaderName head er.
o ut. head er[
HeaderName
]
O bject
The Out messag e's
HeaderName head er
(alternative syntax).
4 08
Variable o ut. head ers.
HeaderNam e
o ut. head ers[
HeaderNam e
]
T ype
O bject
O bject head erAs(Key,Type)
Type
head ers i n. head ers pro perty.
PropertyName
Map
Map
O bject pro perty[
PropertyName
] O bject sys.
SysPropertyName
sysenv.
SysEnvVar
excepti o n
Stri ng
Stri ng
Stri ng
CHAPT ER 30 . T HE SIMPLE LANG UAG E
Descript ion
The Out messag e's
HeaderName head er.
The Out messag e's
HeaderName head er
(alternative syntax).
The Key head er, co nverted to the sp ecified typ e. All typ es, Type, must b e sp ecified using their fullyq ualified Java name, excep t fo r the typ es:
byte[]
,
Stri ng
,
Integ er
, and
Lo ng
. The co nverted value can b e null.
All o f the In head ers (as a
java. uti l . Map
typ e).
All o f the In head ers (as a
java. uti l . Map
typ e).
The PropertyName p ro p erty o n the exchang e.
The PropertyName p ro p erty o n the exchang e (alternative syntax).
The SysPropertyName Java system p ro p erty.
The SysEnvVar system enviro nment variab le.
Either the excep tio n o b ject fro m
Exchang e. g etExcepti o n()
o r, if this value is null, the caug ht excep tio n fro m the
Exchang e. EXC EP T IO N_
C AUG HT
p ro p erty; o therwise null. Supports OGNL
expressions.
4 09
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Variable excepti o n. messag e
T ype
Stri ng excepti o n. stacktrace d ate:
command
:
pattern
bean: beanID. Method
Stri ng
Stri ng
O bject
Descript ion
If an excep tio n is set o n the exchang e, returns the value o f
Excepti o n. g etMessag e
()
; o therwise, returns
nul l
.
If an excep tio n is set o n the exchang e, returns the value o f
Excepti o n. g etStackT r ace()
; o therwise, returns
nul l
. Not e: The simp le lang uag e first tries to retrieve an excep tio n fro m
Exchang e. g etExcepti o n()
. If that p ro p erty is no t set, it checks fo r a caug ht excep tio n, b y calling
Exchang e. g etP ro perty
(Exchang e. C AUG HT _EXC
EP T IO N)
.
A d ate fo rmatted using a java.text.Simp leDateFo rmat p attern. The fo llo wing co mmand s are sup p o rted :
no w
, fo r the current d ate and time;
head er.
HeaderName, o r
i n. head er.
HeaderName
to use a java.util.Date
o b ject in the HeaderName head er fro m the In messag e;
o ut. head er.
HeaderName
to use a java.util.Date
o b ject in the HeaderName head er fro m the Out messag e;
Invo kes a metho d o n the referenced b ean and returns
the result of the method
invocation. To sp ecify a metho d name, yo u can either use the
beanID. Method
syntax; o r yo u can use the
beanID?
metho d =
methodName
syntax.
4 10
CHAPT ER 30 . T HE SIMPLE LANG UAG E
Variable ref: beanID pro perti es: Key pro perti es: Location:
Key
Stri ng thread Name ro uteId type: Name[. Field]
T ype
O bject
Stri ng
Stri ng
Stri ng
O bject
Descript ion
Lo o ks up the b ean with the
ID, beanID, in the reg istry and returns a reference to the
bean itself. Fo r examp le, if yo u are using the sp litter
EIP, yo u co uld use this variab le to reference the b ean that imp lements the sp litting alg o rithm.
The value o f the Key p ro p erty p laceho ld er (see
The value o f the Key p ro p erty p laceho ld er, where the lo catio n o f the p ro p erties file is g iven b y Location (see
The name o f the current thread .
Returns the ID o f the current ro ute thro ug h which the
Exchang e
is b eing ro uted .
References a typ e o r field b y its Fully-Q ualified -Name
(FQ N). To refer to a field , ap p end
. Field
. Fo r examp le, yo u can refer to the
FILE_NAME
co nstant field fro m the
Exchang e
class as
type: o rg . apache. came l . Exchang e. FILE_NAME
30.7. OPERAT OR REFERENCE
Binary operat ors
The binary operators for simple language predicates are shown in Table 30.2, “ Binary
Operators for the Simple Language” .
T ab le 30.2. Bin ary O p erat o rs f o r t h e Simp le Lan g u ag e
4 11
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
O perat or
= =
>
>=
<
<=
! = co ntai ns no t co ntai ns reg ex no t reg ex i n no t i n i s no t i s rang e no t rang e
Descript ion
Eq uals.
G reater than.
G reater than o r eq uals.
Less than.
Less than o r eq uals.
No t eq ual to .
Test if LHS string co ntains RHS string .
Test if LHS string d o es not co ntain RHS string .
Test if LHS string matches RHS reg ular exp ressio n.
Test if LHS string d o es not match RHS reg ular exp ressio n.
Test if LHS string ap p ears in the RHS co mma-sep arated list.
Test if LHS string d o es not ap p ear in the
RHS co mma-sep arated list.
Test if LHS is an instance o f RHS Java typ e
(using Java
i nstanceo f
o p erato r).
Test if LHS is not an instance o f RHS Java typ e (using Java
i nstanceo f
o p erato r).
Test if LHS numb er lies in the RHS rang e
(where rang e has the fo rmat,
'
min
. . .
max
'
).
Test if LHS numb er d o es not lie in the RHS rang e (where rang e has the fo rmat,
'
min
. . .
max
'
).
Unary operat ors and charact er escapes
The binary operators for simple language predicates are shown in Table 30.3, “ Unary
Operators for the Simple Language” .
4 12
T ab le 30.3. Un ary O p erat o rs f o r t h e Simp le Lan g u ag e
O perat or
+ +
--
\n
\r
\t
\
CHAPT ER 30 . T HE SIMPLE LANG UAG E
Descript ion
Increment a numb er b y 1.
Decrement a numb er b y 1.
The newline character.
The carriag e return character.
The tab character.
(Obsolete) Since Camel versio n 2.11, the b ackslash escap e character is no t sup p o rted .
Combining predicat es
The conjunctions shown in
Table 30.4, “ Conjunctions for Simple Language Predicates”
can be used to combine two or more simple language predicates.
T ab le 30.4 . Co n ju n ct io n s f o r Simp le Lan g u ag e Pred icat es
O perat or
& &
| | and o r
Descript ion
Co mb ine two p red icates with lo g ical and.
Co mb ine two p red icates with lo g ical inclusive
or.
Deprecated. Use
& &
instead .
Deprecated. Use
| |
instead .
4 13
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 31. SPEL
OVERVIEW
The Spring Expression Language (SpEL) is an object graph navigation language provided with Spring 3, which can be used to construct predicates and expressions in a route. A notable feature of SpEL is the ease with which you can access beans from the registry.
SYNT AX
The SpEL expressions must use the placeholder syntax, #{SpelExpression}, so that they can be embedded in a plain text string (in other words, SpEL has expression templating enabled).
SpEL can also look up beans in the registry (typically, the Spring registry), using the
@ BeanID syntax. For example, given a bean with the ID, head erUti l s, and the method,
co unt() (which counts the number of headers on the current message), you could use the
head erUti l s bean in an SpEL predicate, as follows:
#{@ headerUtils.count > 4}
ADDING SPEL PACKAGE
To use SpEL in your routes you need to add a dependency on camel -spri ng to your
project as shown in Example 31.1, “ Adding the camel-spring dependency” .
Examp le 31.1. Ad d in g t h e camel- sp rin g d ep en d en cy
<!-- Maven POM File -->
<properties>
<camel-version> 2.15.1.redhat-621084 </camel-version>
...
</properties>
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-spring </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
4 14
VARIABLES
CHAPT ER 31 . SPEL
Table 31.1, “ SpEL variables” lists the variables that are accessible when using SpEL.
T ab le 31.1. Sp EL variab les
Variable thi s exchang e exchang eId excepti o n faul t req uest respo nse
T ype
Exchang e
Exchang e
Stri ng
T hro wabl e
Messag e
Messag e
Messag e pro perti es pro perty(Name)
Map
O bject pro perty(Name, Type) T ype
Descript ion
The current exchang e is the ro o t o b ject.
The current exchang e.
The current exchang e's ID.
The exchang e excep tio n (if any).
The fault messag e (if any).
The exchang e's In messag e.
The exchang e's Out messag e (if any).
The exchang e p ro p erties.
The exchang e p ro p erty keyed b y Name.
The exchang e p ro p erty keyed b y Name, co nverted to the typ e, Type.
XML EXAMPLE
For example, to select only those messages whose C o untry header has the value USA, you can use the following SpEL expression:
<route>
<from uri="SourceURL"/>
<filter>
<spel>#{request.headers['Country'] == 'USA'}}</spel>
<to uri="TargetURL"/>
</filter>
</route>
JAVA EXAMPLE
You can define the same route in the Java DSL, as follows:
4 15
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
from("SourceURL")
.filter().spel("#{request.headers['Country'] == 'USA'}")
.to("TargetURL");
The following example shows how to embed SpEL expressions within a plain text string: from("SourceURL")
.setBody(spel("Hello #{request.body}! What a beautiful #
{request.headers['dayOrNight']}"))
.to("TargetURL");
4 16
CHAPT ER 32 . T HE XPAT H LANG UAG E
CHAPTER 32. THE XPATH LANGUAGE
Ab st ract
When processing XML messages, the XPath language enables you to select part of a message, by specifying an XPath expression that acts on the message's Document Object
Model (DOM). You can also define XPath predicates to test the contents of an element or an attribute.
32.1. JAVA DSL
Basic expressions
You can use xpath("Expression") to evaluate an XPath expression on the current exchange (where the XPath expression is applied to the body of the current In message). The result of the xpath() expression is an XML node (or node set, if more than one node matches).
For example, to extract the contents of the /perso n/name element from the current In message body and use it to set a header named user, you could define a route like the following: from("queue:foo")
.setHeader("user", xpath("/person/name/text()"))
.to("direct:tie");
Instead of specifying xpath() as an argument to setHead er(), you can use the fluent builder xpath() command—for example: from("queue:foo")
.setHeader("user").xpath("/person/name/text()")
.to("direct:tie");
If you want to convert the result to a specific type, specify the result type as the second argument of xpath(). For example, to specify explicitly that the result type is Stri ng : xpath("/person/name/text()", String.class)
Namespaces
Typically, XML elements belong to a schema, which is identified by a namespace URI. When processing documents like this, it is necessary to associate namespace URIs with prefixes, so that you can identify element names unambiguously in your XPath expressions. Apache
Camel provides the helper class, o rg . apache. camel . bui l d er. xml . Namespaces, which enables you to define associations between namespaces and prefixes.
4 17
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
For example, to associate the prefix, cust, with the namespace,
http: //acme. co m/custo mer/reco rd , and then extract the contents of the element,
/cust: perso n/cust: name, you could define a route like the following: import org.apache.camel.builder.xml.Namespaces;
...
Namespaces ns = new Namespaces("cust",
"http://acme.com/customer/record"); from("queue:foo")
.setHeader("user", xpath("/cust:person/cust:name/text()", ns))
.to("direct:tie");
Where you make the namespace definitions available to the xpath() expression builder by passing the Namespaces object, ns, as an additional argument. If you need to define multiple namespaces, use the Namespace. ad d () method, as follows: import org.apache.camel.builder.xml.Namespaces;
...
Namespaces ns = new Namespaces("cust",
"http://acme.com/customer/record"); ns.add("inv", "http://acme.com/invoice"); ns.add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
If you need to specify the result type and define namespaces, you can use the three-argument form of xpath(), as follows: xpath("/person/name/text()", String.class, ns)
Audit ing namespaces
One of the most frequent problems that can occur when using XPath expressions is that there is a mismatch between the namespaces appearing in the incoming messages and the namespaces used in the XPath expression. To help you troubleshoot this kind of problem, the XPath language supports an option to dump all of the namespaces from all of the incoming messages into the system log.
To enable namespace logging at the INFO log level, enable the l o g Namespaces option in the Java DSL, as follows: xpath("/foo:person/@ id", String.class).logNamespaces()
Alternatively, you could configure your logging system to enable T R AC E level logging on the
o rg . apache. camel . bui l d er. xml . XP athBui l d er logger.
When namespace logging is enabled, you will see log messages like the following for each processed message:
4 18
CHAPT ER 32 . T HE XPAT H LANG UAG E
2012-01-16 13:23:45,878 [stSaxonWithFlag] INFO XPathBuilder -
Namespaces discovered in message: {xmlns:a=[http://apache.org/camel],
DEFAULT=[http://apache.org/default], xmlns:b=[http://apache.org/camelA, http://apache.org/camelB]}
32.2. XML DSL
Basic expressions
To evaluate an XPath expression in the XML DSL, put the XPath expression inside an xpath element. The XPath expression is applied to the body of the current In message and returns an XML node (or node set). Typically, the returned XML node is automatically converted to a string.
For example, to extract the contents of the /perso n/name element from the current In message body and use it to set a header named user, you could define a route like the following:
<beans ...>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="queue:foo"/>
<setHeader headerName="user">
<xpath>/person/name/text()</xpath>
</setHeader>
<to uri="direct:tie"/>
</route>
</camelContext>
</beans>
If you want to convert the result to a specific type, specify the result type by setting the
resul tT ype attribute to a Java type name (where you must specify the fully-qualified type name). For example, to specify explicitly that the result type is java. l ang . Stri ng (you can omit the java. l ang . prefix here):
<xpath resultType="String">/person/name/text()</xpath>
Namespaces
When processing documents whose elements belong to one or more XML schemas, it is typically necessary to associate namespace URIs with prefixes, so that you can identify element names unambiguously in your XPath expressions. It is possible to use the standard
XML mechanism for associating prefixes with namespace URIs. That is, you can set an attribute like this: xml ns: Prefix= "NamespaceURI".
4 19
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
For example, to associate the prefix, cust, with the namespace,
http: //acme. co m/custo mer/reco rd , and then extract the contents of the element,
/cust: perso n/cust: name, you could define a route like the following:
<beans ...>
<camelContext xmlns="http://camel.apache.org/schema/spring"
xml ns: cust= "http: //acme. co m/custo mer/reco rd " >
<route>
<from uri="queue:foo"/>
<setHeader headerName="user">
<xpath>/cust:person/cust:name/text()</xpath>
</setHeader>
<to uri="direct:tie"/>
</route>
</camelContext>
</beans>
Audit ing namespaces
One of the most frequent problems that can occur when using XPath expressions is that there is a mismatch between the namespaces appearing in the incoming messages and the namespaces used in the XPath expression. To help you troubleshoot this kind of problem, the XPath language supports an option to dump all of the namespaces from all of the incoming messages into the system log.
To enable namespace logging at the INFO log level, enable the l o g Namespaces option in the XML DSL, as follows:
<xpath logNamespaces="true" resultType="String">/foo:person/@ id</xpath>
Alternatively, you could configure your logging system to enable T R AC E level logging on the
o rg . apache. camel . bui l d er. xml . XP athBui l d er logger.
When namespace logging is enabled, you will see log messages like the following for each processed message:
2012-01-16 13:23:45,878 [stSaxonWithFlag] INFO XPathBuilder -
Namespaces discovered in message: {xmlns:a=[http://apache.org/camel],
DEFAULT=[http://apache.org/default], xmlns:b=[http://apache.org/camelA, http://apache.org/camelB]}
32.3. XPAT H INJECT ION
Paramet er binding annot at ion
4 20
CHAPT ER 32 . T HE XPAT H LANG UAG E
When using Apache Camel bean integration to invoke a method on a Java bean, you can use the @ XP ath annotation to extract a value from the exchange and bind it to a method parameter.
For example, consider the following route fragment, which invokes the cred i t method on an
Acco untServi ce object: from("queue:payments")
.beanRef("accountService","credit")
...
The cred i t method uses parameter binding annotations to extract relevant data from the message body and inject it into its parameters, as follows: public class AccountService {
...
public void credit(
@ XPath("/transaction/transfer/receiver/text()") String name,
@ XPath("/transaction/transfer/amount/text()") String amount
)
{
...
}
...
}
For more information about bean integration, see Section 2.4, “ Bean Integration”
.
Namespaces
Table 32.1, “ Predefined Namespaces for @XPath”
shows the namespaces that are predefined for XPath. You can use these namespace prefixes in the XP ath expression that appears in the @ XP ath annotation.
T ab le 32.1. Pred ef in ed Namesp aces f o r @ XPat h
Namespace URI http: //www. w3. o rg /20 0 1/XMLSchem a http: //www. w3. o rg /20 0 3/0 5/so apenvel o pe
Prefix xsd so ap
Cust om namespaces
4 21
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can use the @ NamespaceP refi x annotation to define custom XML namespaces.
Invoke the @ NamespaceP refi x annotation to initialize the namespaces argument of the
@ XP ath annotation. The namespaces defined by @ NamespaceP refi x can then be used in the @ XP ath annotation's expression value.
For example, to associate the prefix, ex, with the custom namespace,
http: //fuseso urce. co m/exampl es, invoke the @ XP ath annotation as follows: public class AccountService {
...
public void credit(
@ XPath(
value = "/ex:transaction/ex:transfer/ex:receiver/text()",
namespaces = @ NamespaceP refi x(
prefi x = "ex",
uri = "http: //fuseso urce. co m/exampl es"
)
) String name,
@ XPath(
value = "/ex:transaction/ex:transfer/ex:amount/text()",
namespaces = @ NamespaceP refi x(
prefi x = "ex",
uri = "http: //fuseso urce. co m/exampl es"
)
) String amount,
)
{
...
}
...
}
32.4 . XPAT H BUILDER
Overview
The o rg . apache. camel . bui l d er. xml . XP athBui l d er class enables you to evaluate
XPath expressions independently of an exchange. That is, if you have an XML fragment from any source, you can use XP athBui l d er to evaluate an XPath expression on the XML fragment.
Mat ching expressions
Use the matches() method to check whether one or more XML nodes can be found that match the given XPath expression. The basic syntax for matching an XPath expression using XP athBui l d er is as follows: boolean matches = XPathBuilder
.xpath("Expression")
.matches(CamelContext, "XMLString");
4 22
CHAPT ER 32 . T HE XPAT H LANG UAG E
Where the given expression, Expression, is evaluated against the XML fragment, XMLString, and the result is true, if at least one node is found that matches the expression. For example, the following example returns true, because the XPath expression finds a match in the xyz attribute.
boolean matches = XPathBuilder
.xpath("/foo/bar/@ xyz")
.matches(getContext(), "<foo><bar xyz='cheese'/>
</foo>"));
Evaluat ing expressions
Use the eval uate() method to return the contents of the first node that matches the given
XPath expression. The basic syntax for evaluating an XPath expression using
XP athBui l d er is as follows:
String nodeValue = XPathBuilder
.xpath("Expression")
.evaluate(CamelContext, "XMLString");
You can also specify the result type by passing the required type as the second argument to
eval uate()—for example:
String name = XPathBuilder
.xpath("foo/bar")
.evaluate(context, "<foo><bar>cheese</bar></foo>",
String.class);
Integer number = XPathBuilder
.xpath("foo/bar")
.evaluate(context, "<foo><bar>123</bar></foo>",
Integer.class);
Boolean bool = XPathBuilder
.xpath("foo/bar")
.evaluate(context, "<foo><bar>true</bar></foo>",
Boolean.class);
32.5. ENABLING SAXON
Prerequisit es
A prerequisite for using the Saxon parser is that you add a dependency on the camel -
saxo n artifact (either adding this dependency to your Maven POM, if you use Maven, or adding the camel -saxo n-6 . 2. 1. red hat-0 84 . jar file to your classpath, otherwise).
Using t he Saxon parser in Java DSL
In Java DSL, the simplest way to enable the Saxon parser is to call the saxo n() fluent builder method. For example, you could invoke the Saxon parser as shown in the following example:
4 23
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Java
// create a builder to evaluate the xpath using saxon
XPathBuilder builder = XPathBuilder.xpath("tokenize(/foo/bar, '_')
[2]").saxon();
// evaluate as a String result
String result = builder.evaluate(context, "<foo><bar>abc_def_ghi</bar>
</foo>");
Using t he Saxon parser in XML DSL
In XML DSL, the simplest way to enable the Saxon parser is to set the saxo n attribute to true in the xpath element. For example, you could invoke the Saxon parser as shown in the following example:
<xpath saxon="true" resultType="java.lang.String">current-dateTime()
</xpath>
Programming wit h Saxon
If you want to use the Saxon XML parser in your application code, you can create an instance of the Saxon transformer factory explicitly using the following code:
// Java
import javax.xml.transform.TransformerFactory; import net.sf.saxon.TransformerFactoryImpl;
...
TransformerFactory saxonFactory = new net.sf.saxon.TransformerFactoryImpl();
On the other hand, if you prefer to use the generic JAXP API to create a transformer factory instance, you must first set the javax. xml . transfo rm. T ransfo rmerFacto ry property in the ESBInstall/etc/system. pro perti es file, as follows: javax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactory
Impl
You can then instantiate the Saxon factory using the generic JAXP API, as follows:
// Java
import javax.xml.transform.TransformerFactory;
...
TransformerFactory factory = TransformerFactory.newInstance();
If your application depends on any third-party libraries that use Saxon, it might be necessary to use the second, generic approach.
4 24
CHAPT ER 32 . T HE XPAT H LANG UAG E
No t e
The Saxon library must be installed in the container as the OSGi bundle,
net. sf. saxo n/saxo n9 he (normally installed by default). In versions of Fuse
ESB prior to 7.1, it is not possible to load Saxon using the generic JAXP API.
32.6. EXPRESSIONS
Result t ype
By default, an XPath expression returns a list of one or more XML nodes, of
o rg . w3c. d o m. No d eLi st type. You can use the type converter mechanism to convert the result to a different type, however. In the Java DSL, you can specify the result type in the second argument of the xpath() command. For example, to return the result of an XPath expression as a Stri ng : xpath("/person/name/text()", String.class)
In the XML DSL, you can specify the result type in the resul tT ype attribute, as follows:
<xpath resultType="java.lang.String">/person/name/text()</xpath>
Pat t erns in locat ion pat hs
You can use the following patterns in XPath location paths:
/peo pl e/perso n
The basic location path specifies the nested location of a particular element. That is, the preceding location path would match the person element in the following XML fragment:
<people>
<person>...</person>
</people>
Note that this basic pattern can match multiple nodes—for example, if there is more than one perso n element inside the peo pl e element.
/name/text()
If you just want to access the text inside by the element, append /text() to the location path, otherwise the node includes the element's start and end tags (and these tags would be included when you convert the node to a string).
/perso n/tel epho ne/@ i sD ayT i me
4 25
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To select the value of an attribute, AttributeName, use the syntax @ AttributeName.
For example, the preceding location path returns true when applied to the following
XML fragment:
<person>
<telephone isDayTime="true">1234567890</telephone>
</person>
*
A wildcard that matches all elements in the specified scope. For example,
/peo pl e/perso n/* matches all the child elements of perso n.
@ *
A wildcard that matches all attributes of the matched elements. For example,
/perso n/name/@ * matches all attributes of every matched name element.
//
Match the location path at every nesting level. For example, the //name pattern matches every name element highlighted in the following XML fragment:
<invoice>
<person>
<name .../>
</person>
</invoice>
<person>
<name .../>
</person>
<name .../>
. .
no d e()
Selects the parent of the current context node. Not normally useful in the Apache
Camel XPath language, because the current context node is the document root, which has no parent.
text()
Match any kind of node.
Match a text node.
co mment()
Match a comment node.
4 26
CHAPT ER 32 . T HE XPAT H LANG UAG E pro cessi ng -i nstructi o n()
Match a processing-instruction node.
Predicat e filt ers
You can filter the set of nodes matching a location path by appending a predicate in square brackets, [Predicate]. For example, you can select the N th
node from the list of matches by appending [N] to a location path. The following expression selects the first matching
perso n element:
/people/person[1]
The following expression selects the second-last perso n element:
/people/person[last()-1]
You can test the value of attributes in order to select elements with particular attribute values.
The following expression selects the name elements, whose surname attribute is either
Strachan or Davies:
/person/name[@ surname="Strachan" or @ surname="Davies"]
You can combine predicate expressions using any of the conjunctions and , o r, no t(), and you can compare expressions using the comparators, = , ! = , >, >= , <, <= (in practice, the less-than symbol must be replaced by the & l t; entity). You can also use XPath functions in the predicate filter.
Axes
When you consider the structure of an XML document, the root element contains a sequence of children, and some of those child elements contain further children, and so on. Looked at in this way, where nested elements are linked together by the child-of relationship, the whole
XML document has the structure of a tree. Now, if you choose a particular node in this element tree (call it the context node), you might want to refer to different parts of the tree relative to the chosen node. For example, you might want to refer to the children of the context node, to the parent of the context node, or to all of the nodes that share the same parent as the context node (sibling nodes).
An XPath axis is used to specify the scope of a node match, restricting the search to a particular part of the node tree, relative to the current context node. The axis is attached as a prefix to the node name that you want to match, using the syntax,
AxisType: : MatchingNode. For example, you can use the chi l d : : axis to search the children of the current context node, as follows:
/invoice/items/child::item
The context node of chi l d : : i tem is the i tems element that is selected by the path,
4 27
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
/i nvo i ce/i tems. The chi l d : : axis restricts the search to the children of the context node, i tems, so that chi l d : : i tem matches the children of i tems that are named i tem.
As a matter of fact, the chi l d : : axis is the default axis, so the preceding example can be written equivalently as:
/invoice/items/item
But there several other axes (13 in all), some of which you have already seen in abbreviated form: @ is an abbreviation of attri bute: : , and // is an abbreviation of d escend ant-o r-
sel f: : . The full list of axes is as follows (for details consult the reference below):
ancesto r ancesto r-o r-sel f attri bute chi l d d escend ant d escend ant-o r-sel f fo l l o wi ng fo l l o wi ng -si bl i ng namespace parent preced i ng preced i ng -si bl i ng sel f
Funct ions
XPath provides a small set of standard functions, which can be useful when evaluating predicates. For example, to select the last matching node from a node set, you can use the last() function, which returns the index of the last node in a node set, as follows:
/people/person[last()]
4 28
CHAPT ER 32 . T HE XPAT H LANG UAG E
Where the preceding example selects the last perso n element in a sequence (in document order).
For full details of all the functions that XPath provides, consult the reference below.
Reference
For full details of the XPath grammar, see the XML Path Language, Version 1.0
specification.
32.7. PREDICAT ES
Basic predicat es
You can use xpath in the Java DSL or the XML DSL in a context where a predicate is expected—for example, as the argument to a fi l ter() processor or as the argument to a
when() clause.
For example, the following route filters incoming messages, allowing a message to pass, only if the /perso n/ci ty element contains the value, Lo nd o n: from("direct:tie")
.filter().xpath("/person/city =
'London'").to("file:target/messages/uk");
The following route evaluates the XPath predicate in a when() clause: from("direct:tie")
.choice()
.when(xpath("/person/city =
'London'")).to("file:target/messages/uk")
.otherwise().to("file:target/messages/others");
XPat h predicat e operat ors
The XPath language supports the standard XPath predicate operators, as shown in
Table 32.2, “ Operators for the XPath Language”
.
T ab le 32.2. O p erat o rs f o r t h e XPat h Lan g u ag e
O perat or
=
! =
>
Descript ion
Eq uals.
No t eq ual to .
G reater than.
4 29
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
O perat or
>=
<
<= o r and
Descript ion
G reater than o r eq uals.
Less than.
Less than o r eq uals.
Co mb ine two p red icates with lo g ical and.
Co mb ine two p red icates with lo g ical inclusive
or.
Neg ate p red icate arg ument.
no t()
32.8. USING VARIABLES AND FUNCT IONS
Evaluat ing variables in a rout e
When evaluating XPath expressions inside a route, you can use XPath variables to access the contents of the current exchange, as well as O/S environment variables and Java system properties. The syntax to access a variable value is $VarName or $Prefix: VarName, if the variable is accessed through an XML namespace.
For example, you can access the In message's body as $i n: bo d y and the In message's header value as $i n: HeaderName. O/S environment variables can be accessed as
$env: EnvVar and Java system properties can be accessed as $system: SysVar.
In the following example, the first route extracts the value of the /perso n/ci ty element and inserts it into the ci ty header. The second route filters exchanges using the XPath expression, $i n: ci ty = ' Lo nd o n' , where the $i n: ci ty variable is replaced by the value of the ci ty header.
from("file:src/data?noop=true")
.setHeader("city").xpath("/person/city/text()")
.to("direct:tie"); from("direct:tie")
.filter().xpath("$in:city =
'London'").to("file:target/messages/uk");
Evaluat ing funct ions in a rout e
In addition to the standard XPath functions, the XPath language defines additional
functions. These additional functions (which are listed in Table 32.4, “ XPath Custom
Functions” ) can be used to access the underlying exchange, to evaluate a simple
expression or to look up a property in the Apache Camel property placeholder component.
4 30
CHAPT ER 32 . T HE XPAT H LANG UAG E
For example, the following example uses the i n: head er() function and the i n: bo d y() function to access a head and the body from the underlying exchange: from("direct:start").choice()
.when().xpath("in:header('foo') = 'bar'").to("mock:x")
.when().xpath("in:body() = '<two/>'").to("mock:y")
.otherwise().to("mock:z");
Notice the similarity between theses functions and the corresponding i n: HeaderName or
i n: bo d y variables. The functions have a slightly different syntax however:
i n: head er('HeaderName') instead of i n: HeaderName; and i n: bo d y() instead of
i n: bo d y.
Evaluat ing variables in XPat hBuilder
You can also use variables in expressions that are evaluated using the XP athBui l d er class. In this case, you cannot use variables such as $i n: bo d y or $i n: HeaderName, because there is no exchange object to evaluate against. But you can use variables that are defined inline using the vari abl e(Name, Value) fluent builder method.
For example, the following XPathBuilder construction evaluates the $test variable, which is defined to have the value, Lo nd o n:
String var = XPathBuilder.xpath("$test")
.variable("test", "London")
.evaluate(getContext(), "<name>foo</name>");
Note that variables defined in this way are automatically entered into the global namespace
(for example, the variable, $test, uses no prefix).
32.9. VARIABLE NAMESPACES
T able of namespaces
Table 32.3, “ XPath Variable Namespaces” shows the namespace URIs that are associated
with the various namespace prefixes.
T ab le 32.3. XPat h Variab le Namesp aces
Namespace URI http: //camel . apache.
o rg /schema/spri ng
Prefix
None
Descript ion
Default namesp ace
(asso ciated with variab les that have no namesp ace p refix).
4 31
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Namespace URI http: //camel . apache.
o rg /xml /i n/
Prefix i n http: //camel . apache.
o rg /xml /o ut/ o ut http: //camel . apache.
o rg /xml /functi o ns/ http: //camel . apache.
o rg /xml /vari abl es/en vi ro nment-vari abl es functi o ns env http: //camel . apache.
o rg /xml /vari abl es/sy stem-pro perti es system http: //camel . apache.
o rg /xml /vari abl es/ex chang e-pro perty
Undefined
Descript ion
Used to reference head er o r b o d y o f the current exchang e's In messag e.
Used to reference head er o r b o d y o f the current exchang e's Out messag e.
Used to reference so me custo m functio ns.
Used to reference O /S enviro nment variab les.
Used to reference Java system p ro p erties.
Funct ion i n: bo d y() i n: head er(HeaderName) o ut: bo d y() o ut: head er(HeaderName)
Used to reference exchang e p ro p erties. Yo u must d efine yo ur o wn p refix fo r this namesp ace.
32.10. FUNCT ION REFERENCE
T able of cust om funct ions
Table 32.4, “ XPath Custom Functions” shows the custom functions that you can use in
Apache Camel XPath expressions. These functions can be used in addition to the standard
XPath functions.
T ab le 32.4 . XPat h Cu st o m Fu n ct io n s
Descript ion
Returns the In messag e b o d y.
Returns the In messag e head er with name,
HeaderName.
Returns the Out messag e b o d y.
Returns the Out messag e head er with name,
HeaderName.
4 32
Funct ion functi o n: pro perti es(PropKey) functi o n: si mpl e(SimpleExp)
CHAPT ER 32 . T HE XPAT H LANG UAG E
Descript ion
Lo o ks up a p ro p erty with the key, PropKey
(see
Sectio n 2.7, “ Pro p erty Placeho ld ers”
).
Evaluates the sp ecified simp le exp ressio n,
SimpleExp.
4 33
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 33. XQUERY
OVERVIEW
XQuery was originally devised as a query language for data stored in XML form in a database. The XQuery language enables you to select parts of the current message, when the message is in XML format. XQuery is a superset of the XPath language; hence, any valid
XPath expression is also a valid XQuery expression.
JAVA SYNT AX
You can pass an XQuery expression to xq uery() in several ways. For simple expressions, you can pass the XQuery expressions as a string (java. l ang . Stri ng ). For longer
XQuery expressions, you might prefer to store the expression in a file, which you can then reference by passing a java. i o . Fi l e argument or a java. net. UR L argument to the overloaded xq uery() method. The XQuery expression implicitly acts on the message content and returns a node set as the result. Depending on the context, the return value is interpreted either as a predicate (where an empty node set is interpreted as false) or as an expression.
ADDING T HE SAXON MODULE
To use XQuery in your routes you need to add a dependency on camel -saxo n to your
project as shown in Example 33.1, “ Adding the camel-saxon dependency” .
Examp le 33.1. Ad d in g t h e camel- saxo n d ep en d en cy
<!-- Maven POM File -->
...
<dependencies>
...
<dependency>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-saxon </artifactId>
<version> ${camel-version} </version>
</dependency>
...
</dependencies>
CAMEL ON EAP DEPLOYMENT
The camel -saxo n component is supported by the Camel on EAP (Wildfly Camel) framework, which offers a simplified deployment model on the Red Hat JBoss Enterprise
Application Platform (JBoss EAP) container. For details of this model, see chapter "Apache
Camel on Red Hat JBoss EAP" in "Deploying into a Web Server" .
4 34
ST AT IC IMPORT
CHAPT ER 33. XQ UERY
ST AT IC IMPORT
To use the xq uery() static method in your application code, include the following import statement in your Java source files: import static org.apache.camel.builder.saxon.XQueryBuilder.xquery;
VARIABLES
Table 33.1, “ XQuery variables”
lists the variables that are accessible when using XQuery.
T ab le 33.1. XQ u ery variab les
Variable exchang e i n. bo d y o ut. bo d y i n. head ers. key o ut. head ers. key
key
T ype
Exchang e
O bject
O bject
O bject
O bject
O bject
Descript ion
The current Exchang e
The b o d y o f the IN messag e
The b o d y o f the O UT messag e
The IN messag e head er who se key is key
The O UT messag e head er who se key is key
The Exchang e p ro p erty who se key is key
EXAMPLE
Example 33.2, “ Route using XQuery” shows a route that uses XQuery.
Examp le 33.2. Ro u t e u sin g XQ u ery
<camelContext>
<route>
<from uri= "activemq:MyQueue" />
<filter>
<language langauge= "xquery" > /foo:person[@ name='James'] </language>
4 35
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri= "mqseries:SomeOtherQueue" />
</filter>
</route>
</camelContext>
4 36
PART III. WEB SERVICES AND RO UT ING WIT H CAMEL CXF
PART III. WEB SERVICES AND ROUTING WITH CAMEL CXF
Ab st ract
This guide describes how to use Apache Camel's CXF component to create Web services or wrap existing functionality in Web service facades.
4 37
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 34. DEMONSTRATION CODE FOR CAMEL/CXF
Ab st ract
This chapter explains how to install, build, and run the demonstrations that accompany this guide.
34 .1. DOWNLOADING AND INST ALLING T HE DEMONST RAT IONS
Overview
Most of the examples discussed in this guide are based on working demonstrations, which you can download and try out for yourself. The examples can easily be run by deploying them into a Red Hat JBoss Fuse container, as described here.
Prerequisit es
For building and running the demonstration code, you must have the following prerequisites installed:
Java platform—the demonstrations can run on Java 6 or Java 7.
Apache Maven build tool—to build the demonstration, you require Apache Maven 3.0.4
.
Internet connection—Maven requires an Internet connection in order to download required dependencies from remote repositories while performing a build.
Red Hat JBoss Fuse—the demonstrations are deployed into the JBoss Fuse container.
No t e
For more details of the requirements for installing and working with JBoss Fuse, see "Installation on Apache Karaf" .
Downloading t he demonst rat ion package
The source code for the demonstrations is packaged as a Z ip file, cxf-webi nars-jbo ss-
fuse-6 . 2. 1. zi p, and is available from the following location: https://github.com/FuseByExample/cxf-webinars/archive/jboss-fuse-6.2.1.zip
34 .2. RUNNING T HE DEMONST RAT IONS
4 38
Building t he demonst rat ions
CHAPT ER 34 . DEMO NST RAT IO N CO DE FO R CAMEL/CXF
Use Apache Maven to build the demonstrations. Open a new command prompt, change directory to cxf-webi nars-jbo ss-fuse-6 . 2. 1, and enter the following commands: mvn install
This command builds all of the demonstrations under the cxf-webi nars-jbo ss-fuse-
6 . 2. 1 directory (where the demonstrations are defined to be submodules of the cxf-
webi nars-jbo ss-fuse-6 . 2. 1/po m. xml project). While Maven is building the demonstration code, it downloads whatever dependencies it needs from the Internet and installs them in the local Maven repository.
St art ing and configuring t he Red Hat JBoss Fuse cont ainer
Start and configure the Red Hat JBoss Fuse container as follows:
1. (Optional) If your local Maven repository is in a non-standard location, you might need to edit the JBoss Fuse configuration to specify your custom location. Edit the
InstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file and set the
o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property to the location of your local Maven repository:
#
# Path to the local maven repository which is used to avoid downloading
# artifacts when they already exist locally.
# The value of this property will be extracted from the settings.xml file
# above, or defaulted to:
# System.getProperty( "user.home" ) + "/.m2/repository"
#
#org.ops4j.pax.url.mvn.localRepository= org.ops4j.pax.url.mvn.localRepository=file:E:/Data/.m2/repositor y
2. Launch the JBoss Fuse container. Open a new command prompt, change directory to InstallDir/bi n, and enter the following command:
./fuse
Running t he cust omer-ws-osgi-bundle demonst rat ion
It is now a relatively straightforward task to run each of the demonstrations by installing the relevant OSGi bundles.
For example, to start up the WSDL-first Web service (discussed in Chapter 36, WSDL-First
), enter the following console commands:
JBossFuse:karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-osgi-bundle
4 39
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To see the Web service in action, start up the sample Web service client (discussed in
Chapter 37, Implementing a WS Client ), by entering the following console command:
JBossFuse:karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-client
The bundle creates a thread that invokes the Web service once a second and logs the response. View the log by entering the following console command:
JBossFuse:karaf@ root> log:tail -n 4
You should see log output like the following:
2015-08-20 16:10:16,271 | INFO | #0 - timer://foo | timerToLog
| ? ? | 198 - org.apache.camel.camel-core - 2.15.1.redhat-620133 | The message contains Hi from Camel at 2015-08-20 16:10:16
2015-08-20 16:10:16,367 | INFO | qtp432302853-183 |
CustomerServiceImpl | ?
? | 283 - com.fusesource.byexample.cxf-webinars.customer-ws-osgibundle - 1.0.0.SNAPSHOT | Getting status for customer 1234
2015-08-20 16:10:16,370 | INFO | invoker thread. | ClientInvoker
| ? ? | 284 - com.fusesource.byexample.cxf-webinars.customer-ws-client -
1.0.0.SNAPSHOT | Got back: status = Active, statusMessage = In the park, playing with my frisbee.
2015-08-20 16:10:18,373 | INFO | qtp432302853-182 |
CustomerServiceImpl | ?
? | 283 - com.fusesource.byexample.cxf-webinars.customer-ws-osgibundle - 1.0.0.SNAPSHOT | Getting status for customer 1234
2015-08-20 16:10:18,376 | INFO | invoker thread. | ClientInvoker
| ? ? | 284 - com.fusesource.byexample.cxf-webinars.customer-ws-client -
1.0.0.SNAPSHOT | Got back: status = Active, statusMessage = In the park, playing with my frisbee.
To stop viewing the log, type the interrupt character (usually Ctrl-C).
To stop the client, first discover the client's bundle ID using the o sg i : l i st console command. For example:
JBossFuse:karaf@ root> list | grep customer-ws-client
[ 284] [Active ] [ ] [Started] [ 80] customer-wsclient (1.0.0.SNAPSHOT)
You can then stop the client using the o sg i : sto p console command. For example:
JBossFuse:karaf@ root> stop 284
To shut down the container completely, enter the following console command:
4 4 0
CHAPT ER 34 . DEMO NST RAT IO N CO DE FO R CAMEL/CXF
JBossFuse:karaf@ root> shutdown -f
Running t he ot her demonst rat ions
The remaining demonstrations are all based on the Camel CXF component. You can only run one of these demonstrations at a time, because they all use the same Web service port and would clash, if started at the same time:
custo mer-ws-camel -cxf-po jo custo mer-ws-camel -cxf-payl o ad custo mer-ws-camel -cxf-pro vi d er
The preceding demonstrations all require the Camel CXF component and some of them require the Camel Velocity component as well. Before you run the demonstrations, you must install the requisite features for these Camel components, as follows:
JBossFuse:karaf@ root> features:install camel-cxf
JBossFuse:karaf@ root> features:install camel-velocity
You can test these demonstrations using the provided custo mer-ws-cl i ent client or using the third-party SoapUI utility.
4 4 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 35. JAVA-FIRST SERVICE IMPLEMENTATION
35.1. JAVA-FIRST OVERVIEW
Overview
The Java-first approach is a convenient way to get started with Web services, if you are unfamiliar with WSDL syntax. Using this approach, you can define the Web service interface using an ordinary Java interface and then use the provided Apache CXF utilities to generate the corresponding WSDL contract from the Java interface.
No t e
There is no demonstration code to accompany this example.
Service Endpoint Int erface (SEI)
An SEI is an ordinary Java interface. In order to use the standard JAX-WS frontend, the SEI
must be annotated with the @ WebServi ce annotation.
In the Java-first approach, the SEI is the starting point for implementing the Web service and it plays a central role in the development of the Web service implementation. The SEI is used in the following ways:
Base type of the Web service implementation (server side)—you define the Web service by implementing the SEI.
Proxy type (client side)—on the client side, you use the SEI to invoke operations on the client proxy object.
Basis for generating the WSDL contract—in the Java-first approach, you generate the WSDL contract by converting the SEI to WSDL.
WSDL cont ract
The WSDL contract is a platform-neutral and language-neutral description of the Web service interface. When you want to make the Web service available to third-party clients, you should publish the WSDL contract to some well-known location. The WSDL contract contains all of the metadata required by WS clients.
T he Cust omerService demonst rat ion
Figure 35.1, “ Building a Java-First Web Service” shows an overview of the files required to
implement and build the C usto merServi ce Web service using the Java-first approach.
Fig u re 35.1. Bu ild in g a Java- First Web Service
4 4 2
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
Implement ing and building t he service
To implement and build the Java-first example shown in Figure 35.1, “ Building a Java-First
Web Service” , you would perform the following steps:
1. Implement the SEI, which constitutes the basic definition of the Web service's interface.
2. Annotate the SEI (you can use the annotations to influence the ultimate form of the generated WSDL contract).
3. Implement any other requisite Java classes. In particular, implement the following:
Any data types referenced by the SEI—for example, the C usto mer class.
The implementation of the SEI, C usto merServi ceImpl .
4. Instantiate the Web service endpoint, by adding the appropriate code to a Spring
XML file.
5. Generate the WSDL contract using a Java-to-WSDL converter.
35.2. DEFINE SEI AND RELAT ED CLASSES
Overview
The Service Endpoint Interface (SEI) is the starting point for implementing a Web service in the Java-first approach. The SEI represents the Web service in Java and it is ultimately used as the basis for generating the WSDL contract. This section describes how to create a sample SEI, the C usto merServi ce interface, which enables you to access the details of a customer's account.
T he Cust omerService SEI
4 4 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
T he Cust omerService SEI
A JAX-WS service endpoint interface (SEI) is essentially an ordinary Java interface, augmented by certain annotations (which are discussed in the next section). For example, consider the following C usto merServi ce interface, which defines methods for accessing the C usto mer data type:
// Java package com.fusesource.demo.wsdl.customerservice;
// NOT YET ANNOTATED!
public interface C usto merServi ce {
public com.fusesource.demo.customer.Customer lookupCustomer(
java.lang.String customerId
);
public void updateCustomer(
com.fusesource.demo.customer.Customer cust
);
public void getCustomerStatus(
java.lang.String customerId,
javax.xml.ws.Holder<java.lang.String> status,
javax.xml.ws.Holder<java.lang.String> statusMessage
);
}
After adding the requisite annotations to the C usto merServi ce interface, this interface provides the basis for defining the C usto merServi ce Web service.
javax.xml.ws.Holder<?> t ypes
The g etC usto merStatus method from the C usto merServi ce interface has parameters declared to be of javax. xml . ws. Ho l d er<Stri ng > type. These so-called holder types are needed in order to declare the O UT or INO UT parameters of a WSDL operation.
The syntax of WSDL operations allows you to define any number of OUT or INOUT parameters, which means that the parameters are used to return a value to the caller. This kind of parameter passing is not natively supported by the Java language. Normally, the only way that Java allows you to return a value is by declaring it as the return value of a method. You can work around this language limitation, however, by declaring parameters to be holder types.
For example, consider the definition of the following method, g etStri ng Val ues(), which takes a holder type as its second parameter:
// Java public void getStringValues(
String wrongWay,
javax. xml . ws. Ho l d er<Stri ng > rightWay
4 4 4
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
) {
wrongWay = "Caller will never see this string!";
ri g htWay. val ue = "But the caller *can* see this string.";
}
The caller can access the value of the returned ri g htWay string as ri g htWay. val ue. For example:
// Java
String wrongWay = "This string never changes"; javax.xml.ws.Holder<String> rightWay.value = "This value *can* change."; sampleObject.getStringValues(wrongWay, rightWay);
System.out.println("Unchanged string: " + wrongWay);
System.out.println("Changed string: " + ri g htWay. val ue);
It is, perhaps, slightly unnatural to use Ho l d er<> types in a Java-first example, because this is not a normal Java idiom. But it is interesting to include OUT parameters in the example, so that you can see how a Web service processes this kind of parameter.
Relat ed classes
When you run the Java-to-WSDL compiler on the SEI, it converts not only the SEI, but also the classes referenced as parameters or return values. The parameter types must be convertible to XML, otherwise it would not be possible for WSDL operations to send or to receive those data types. In fact, when you run the Java-to-WSDL compiler, it is typically necessary to convert an entire tree of related classes to XML using the standard JAXB encoding.
Normally, as long as the related classes do not require any exotic language features, the
JAXB encoding should be quite straightforward.
Default const ruct or for relat ed classes
There is one simple rule, however, that you need to keep in mind when implementing related classes: each related class must have a default constructor (that is, a constructor without arguments). If you do not define any constructor for a class, the Java language automatically adds a default constructor. But if you define a class's constructors explicitly, you must ensure that one of them is a default constructor.
T he Cust omer class
For example, the C usto mer class appears as a related class in the definition of the
C usto merServi ce SEI (
the section called “ The CustomerService SEI” ). The C usto mer
class consists of a collection of Stri ng fields and the only special condition it needs to satisfy is that it includes a default constructor:
4 4 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Java package com.fusesource.demo.customer; public class Customer {
protected String firstName;
protected String lastName;
protected String phoneNumber;
protected String id;
// Default constructor, required by JAX-WS
publ i c C usto mer() { }
public Customer(String firstName, String lastName, String phoneNumber,
String id) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String value) {
this.firstName = value;
}
public String getLastName() {
return lastName;
}
public void setLastName(String value) {
this.lastName = value;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String value) {
this.phoneNumber = value;
}
public String getId() {
return id;
}
public void setId(String value) {
this.id = value;
}
}
4 4 6
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
35.3. ANNOT AT E SEI FOR JAX-WS
Overview
To use the JAX-WS frontend, an SEI must be annotated using standardised JAX-WS annotations. The annotations signal to the Web services tooling that the SEI uses JAX-WS and the annotations are also used to customize the mapping from Java to WSDL. Here we only cover the most basic annotations—for complete details of JAX-WS annotations, see
Developing Applications Using JAX-WS from the Apache CXF library.
No t e
It sometimes makes sense also to annotate the service implementation class (the class that implements the SEI)—for example, if you want to associate an implementation class with a specific WSDL servi ceName and po rtName (there can be more than one implementation of a given SEI).
Minimal annot at ion
The minimal annotation required for an SEI using the JAX-WS frontend is to prefix the interface declaration with @ WebServi ce. For example, the C usto merServi ce SEI could be minimally annotated as follows:
// Java package com.fusesource.demo.wsdl.customerservice;
i mpo rt javax. jws. WebServi ce;
@ WebServi ce
public interface CustomerService {
...
}
If you run the Java-to-WSDL utility on this interface, it will generate a complete WSDL contract using the standard default style of code conversion.
@WebService annot at ion
Although it is sufficient to specify the @ WebServi ce annotation without any attributes, it is usually better to specify some attributes to provide a more descriptive WSDL service name and WSDL port name. You will also usually want to specify the XML target namespace. For this, you can specify the following optional attributes of the @ WebServi ce annotation:
name
Specifies the name of the WSDL contract (appearing in the wsd l : d efi ni ti o ns element).
4 4 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide servi ceName
Specifies the name of the WSDL service (a SOAP service is defined by default in the generated contract).
po rtName
Specifies the name of the WSDL port (a SOAP/HTTP port is defined by default in the generated contract).
targ etNamespace
The XML schema namespace that is used, by default, to qualify the elements and types defined in the contract.
@WebParam annot at ion
You can add the @ WebP aram annotation to method arguments in the SEI. The @ WebP aram annotation is optional, but there are a couple of good reasons for adding it:
By default, JAX-WS maps Java arguments to parameters with names like arg 0 , ..., arg N.
Messages are much easier to read, however, when the parameters have meaningful names.
It is a good idea to define parameter elements without a namespace. This makes the XML encoding of requests and responses more compact.
To enable support for WSDL OUT and INOUT parameters.
You can add @ WebP aram annotations with the following attributes:
name
Specifies the mapped name of the parameter.
targ etNamespace
Specifies the namespace of the mapped parameter. Set this to a blank string for a more compact XML encoding.
mo d e
Can have one of the following values:
WebP aram. Mo d e. IN—(default) parameter is passed from client to service (in request).
4 4 8
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
WebP aram. Mo d e. INO UT —parameter is passed from client to service (request) and from the service back to the client (in reply).
WebP aram. Mo d e. O UT —parameter is passed from service back to the client (in reply).
OUT and INOUT paramet ers
In WSDL, OUT and INOUT parameters represent values that can be sent from the service back to the client (where the INOUT parameter is sent in both directions).
In Java syntax, the only value that can ordinarily be returned from a method is the method's return value. In order to support OUT or INOUT parameters in Java (which are effectively like additional return values), you must:
Declare the corresponding Java argument using a
javax. xml . ws. Ho l d er<ParamType> type, where ParamType is the type of the parameter you want to send.
Annotate the Java argument with @ WebP aram, setting either mo d e =
WebP aram. Mo d e. O UT or mo d e = WebP aram. Mo d e. INO UT .
Annot at ed Cust omerService SEI
The following example shows the C usto merServi ce SEI after it has been annotated. Many other annotations are possible, but this level of annotation is usually adequate for a WSDLfirst project.
// Java package com.fusesource.demo.wsdl.customerservice; import javax.jws.WebParam; import javax.jws.WebService;
@ WebServi ce(
targ etNamespace =
"http: //d emo . fuseso urce. co m/wsd l /C usto merServi ce/",
name = "C usto merServi ce",
servi ceName = "C usto merServi ce",
po rtName = "SO AP O verHT T P "
)
public interface CustomerService {
public com.fusesource.demo.customer.Customer lookupCustomer(
@ WebP aram(name = "custo merId ", targ etNamespace = "")
java.lang.String customerId
);
public void updateCustomer(
@ WebP aram(name = "cust", targ etNamespace = "")
4 4 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
com.fusesource.demo.customer.Customer cust
);
public void getCustomerStatus(
@ WebP aram(name = "custo merId ", targ etNamespace = "")
java.lang.String customerId,
@ WebP aram(mo d e = WebP aram. Mo d e. O UT , name = "status", targ etNamespace = "")
javax.xml.ws.Holder<java.lang.String> status,
@ WebP aram(mo d e = WebP aram. Mo d e. O UT , name = "statusMessag e", targ etNamespace = "")
javax.xml.ws.Holder<java.lang.String> statusMessage
);
}
35.4 . INST ANT IAT E T HE WS ENDPOINT
Overview
In Apache CXF, you create a WS endpoint by defining a jaxws: end po i nt element in XML.
The WS endpoint is effectively the runtime representation of the Web service: it opens an IP port to listen for SOAP/HTTP requests, is responsible for marshalling and unmarshalling messages (making use of the generated Java stub code), and routes incoming requests to the relevant methods on the implementor class.
In other words, creating a Web service in Spring XML consists essentially of the following two steps:
1. Create an instance of the implementor class, using the Spring bean element.
2. Create a WS endpoint, using the jaxws: end po i nt element.
T he jaxws:endpoint element
You can instantiate a WS endpoint using the jaxws: end po i nt element in a Spring file, where the jaxws: prefix is associated with the http: //cxf. apache. o rg /jaxws namespace.
No t e
Take care not to confuse the jaxws: end po i nt element with the
cxf: cxfEnd po i nt element, which you meet later in this guide: the
jaxws: end po i nt element is used to integrate a WS endpoint with a Java implementation class; whereas the cxf: cxfEnd po i nt is used to integrate a WS endpoint with a Camel route.
Define JAX-WS endpoint in XML
4 50
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
The following sample Spring file shows how to define a JAX-WS endpoint in XML, using the
jaxws: end po i nt element.
<?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:jaxws="http://cxf.apache.org/jaxws"
xmlns:soap="http://cxf.apache.org/bindings/soap"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:endpoint
xmlns:customer="http://demo.fusesource.com/wsdl/CustomerService/"
id="customerService"
address="/C usto mer"
serviceName="customer:CustomerService"
endpointName="customer:SOAPOverHTTP"
implementor="#custo merServi ceImpl ">
</jaxws:endpoint>
<bean id="custo merServi ceImpl "
class="com.fusesource.customer.ws.CustomerServiceImpl"/>
</beans>
Address for t he Jet t y cont ainer
Apache CXF deploys the WS endpoint into a Jetty servlet container instance and the
ad d ress attribute of jaxws: end po i nt is therefore used to configure the addressing information for the endpoint in the Jetty container.
Apache CXF supports the notion of a default servlet container instance. The way the default servlet container is initialized and configured depends on the particular mode of deployment that you choose. For example the Red Hat JBoss Fuse container and Web containers (such as Tomcat) provide a default servlet container.
There are two different syntaxes you can use for the endpoint address, where the syntax that you use effectively determines whether or not the endpoint is deployed into the default servlet container, as follows:
Address syntax for default servlet container—to use the default servlet container, specify only the servlet context for this endpoint. Do not specify the protocol, host, and IP port in the address. For example, to deploy the endpoint to the /C usto mers servlet context in the default servlet container: address="/Customers"
4 51
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Address syntax for custom servlet container—to instantiate a custom Jetty container for the endpoint, specify a complete HTTP URL, including the host and IP port (the value of the
IP port effectively identifies the target Jetty container). Typically, for a Jetty container, you specify the host as 0 . 0 . 0 . 0 , which is interpreted as a wildcard that matches every IP network interface on the local machine (that is, if deployed on a multi-homed host, Jetty opens a listening port on every network card). For example, to deploy the endpoint to the custom Jetty container listening on IP port, 80 83: address="http://0.0.0.0:8083/Customers"
No t e
If you want to configure a secure endpoint (secured by SSL), you would specify the https: scheme in the address.
Referencing t he service implement at ion
The i mpl emento r attribute of the jaxws: end po i nt element references the implementation of the WS service. The value of this attribute can either be the name of the implementation class or (as in this example) a bean reference in the format, #BeanID, where the # character indicates that the following identifier is the name of a bean in the bean registry.
35.5. JAVA-T O-WSDL MAVEN PLUG-IN
Overview
To generate a WSDL contract from your SEI, you can use either the java2ws command-line utility or the cxf-java2ws-pl ug i n Maven plug-in. The plug-in approach is ideal for
Maven-based projects: after you paste the requisite plug-in configuration into your POM file, the WSDL code generation step is integrated into your build.
Configure t he Java-t o-WSDL Maven plug-in
Configuring the Java-to-WSDL Maven plug-in is relatively easy, because most of the default configuration settings can be left as they are. After copying and pasting the sample pl ug i n element into your project's POM file, there are just a few basic settings that need to be customized, as follows:
CXF version—make sure that the plug-in's dependencies are using the latest version of
Apache CXF.
SEI class name—specify the fully-qualified class name of the SEI in the
co nfi g urati o n/cl assName element.
Location of output—specify the location of the generated WSDL file in the
co nfi g urati o n/o utputFi l e element.
4 52
CHAPT ER 35. JAVA- FIRST SERVICE IMPLEMENT AT IO N
For example, the following POM fragment shows how to configure the cxf-java2ws-
pl ug i n plug-in to generate WSDL from the CustomerService SEI:
<project ...>
...
<properties>
<cxf. versi o n>3. 0 . 4 . red hat-6 210 84 </cxf. versi o n>
</properties>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-java2ws-pl ug i n</artifactId>
<version>${cxf. versi o n}</version>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf. versi o n}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-simple</artifactId>
<version>${cxf. versi o n}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>process-classes</id>
<phase>process-classes</phase>
<configuration>
<className>o rg . fuseso urce. d emo . camel cxf. ws. server. C usto merServi ce</c lassName>
<outputFile>${based i r}/. . /src/mai n/reso urces/wsd l /C usto merServi ce. ws
d l </outputFile>
<genWsdl>true</genWsdl>
<verbose>true</verbose>
</configuration>
<goals>
<goal>java2ws</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
4 53
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Generat ed WSDL
When using the Java-first approach to defining a Web service, there are typically other parts of your application (for example, WS clients) that depend on the generated WSDL file. For this reason, it is generally a good idea to output the generated WSDL file to a common location, which is accessible to other projects in your application, using the o utputFi l e configuration element.
If you do not specify the o utputFi l e configuration element, the generated WSDL is sent to the following location, by default:
BaseDir/target/generated/wsdl/SEIClassName.wsdl
Reference
For full details of how to configure the Java-to-WSDL plug-in, see the Maven Java2WS plugin reference page .
If the SEI is left witho ut anno tatio ns, Ap ache CXF d efaults to using the
simp le fro ntend . This is a no n-stand ard fro ntend , which is not reco mmend ed fo r mo st ap p licatio ns.
4 54
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N
CHAPTER 36. WSDL-FIRST SERVICE IMPLEMENTATION
36.1. WSDL-FIRST OVERVIEW
Overview
If you are familiar with the syntax of WSDL and you want to have ultimate control over the layout and conventions applied to the WSDL contract, you will probably prefer to develop your Web service using the WSDL-first approach. In this approach, you start with the WSDL contract and then use the provided Apache CXF utilities to generate the requisite Java stub files from the WSDL contract.
Demonst rat ion locat ion
The code presented in this chapter is taken from the following demonstration: cxf-webinars-jboss-fuse-6.2.1/custo mer-ws-o sg i -bund l e
For details of how to download and install the demonstration code, see Chapter 34,
Demonstration Code for Camel/CXF
WSDL cont ract
The WSDL contract is a platform-neutral and language-neutral description of the Web service interface. In the WSDL-first approach, the WSDL contract is the starting point for implementing the Web service. You can use it to generate Java stub code, which provides the basis for implementing the Web service on the server side.
Service Endpoint Int erface (SEI)
The most important piece of the generated stub code is the SEI, which is an ordinary Java interface that represents the Web service interface in the Java language.
The SEI is used in the following ways:
Base type of the Web service implementation (server side)—you define the Web service by implementing the SEI.
Proxy type (client side)—on the client side, you use the SEI to invoke operations on the client proxy object.
T he Cust omerService demonst rat ion
Figure 36.1, “ Building a WSDL-First Web Service” shows an overview of the files required to
implement and build the C usto merServi ce Web service using the WSDL-first approach.
4 55
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Fig u re 36 .1. Bu ild in g a WSDL- First Web Service
4 56
Implement ing and building t he service
To implement and build the WSDL-first example shown in Figure 36.1, “ Building a WSDL-
First Web Service” , starting from scratch, you would perform the following steps:
1. Create the WSDL contract.
2. Generate the Java stub code from the WSDL contract using a WSDL-to-Java converter, ws2java. This gives you the SEI, C usto merServi ce, and its related classes, such as C usto mer.
3. Write the implementation of the SEI, C usto merServi ceImpl .
4. Instantiate the Web service endpoint, by adding the appropriate code to a Spring
XML file.
36.2. CUST OMERSERVICE WSDL CONT RACT
Sample WSDL cont ract
The WSDL contract used in this demonstration is the C usto merServi ce WSDL contract, which is available in the following location: cxf-webinars-jboss-fuse-6.2.1/src/main/resources/wsdl
Because the WSDL contract is a fairly verbose format, it is not shown in here in full. The main point you need to be aware of is that the C usto merSeri vi ce WSDL contract exposes the following operations:
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N l o o kupC usto mer
Given a customer ID, the operation returns the corresponding C usto mer data object.
upd ateC usto mer
Stores the given C usto mer data object against the given customer ID.
g etC usto merStatus
Returns the status of the customer with the given customer ID.
Part s of t he WSDL cont ract
A WSDL contract has the following main parts:
the section called “ Port type” .
the section called “ WSDL binding”
.
the section called “ WSDL port”
.
Port t ype
The port type is defined in the WSDL contract by the wsd l : po rtT ype element. It is analogous to an interface and it defines the operations that can be invoked on the Web service.
For example, the following WSDL fragment shows the wsd l : po rtT ype definition from the
C usto merServi ce WSDL contract:
<wsdl:definitions name= "CustomerService"
targetNamespace= "http://demo.fusesource.com/wsdl/CustomerService/"
...>
...
<wsdl:portType name= "CustomerService" >
<wsdl:operation name= "lookupCustomer" >
<wsdl:input message= "tns:lookupCustomer" ></wsdl:input>
<wsdl:output message= "tns:lookupCustomerResponse" ></wsdl:output>
</wsdl:operation>
<wsdl:operation name= "updateCustomer" >
<wsdl:input message= "tns:updateCustomer" ></wsdl:input>
<wsdl:output message= "tns:updateCustomerResponse" ></wsdl:output>
</wsdl:operation>
<wsdl:operation name= "getCustomerStatus" >
4 57
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<wsdl:input message= "tns:getCustomerStatus" ></wsdl:input>
<wsdl:output message= "tns:getCustomerStatusResponse" ></wsdl:output>
</wsdl:operation>
</wsdl:portType>
...
</wsdl:definitions>
WSDL binding
A WSDL binding describes how to encode all of the operations and data types associated with a particular port type. A binding is specific to a particular protocol—for example, SOAP or JMS.
WSDL port
A WSDL port specifies the transport protocol and contains addressing data that enables clients to locate and connect to a remote server endpoint.
For example, the C usto merServi ce WSDL contract defines the following WSDL port:
<wsdl:definitions ...>
...
<wsdl:service name="CustomerService">
<wsdl:port name="SOAPOverHTTP" binding="tns:CustomerServiceSOAP">
<soap:address location="http://0.0.0.0:8183/CustomerService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
The address specified by the so ap: ad d ress element's l o cati o n attribute in the original
WSDL contract is typically overridden at run time, however.
T he get Cust omerSt at us operat ion
Because a WSDL contract is fairly verbose, it can be a bit difficult to see what the parameters of an operation are. Typically, for each operation, you can find data types in the XML schema section that represent the operation request and the operation response. For example, the g etC usto merStatus operation has its request parameters (IN parameters) encoded by the g etC usto merStatus element and its response parameters (OUT parameters) encoded by the g etC usto merStatusR espo nse element, as follows:
<wsdl:definitions name="CustomerService"
targetNamespace="http://demo.fusesource.com/wsdl/CustomerService/"
...>
<wsdl:types>
4 58
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N
<xsd:schema ...>
...
<xsd:element name="g etC usto merStatus">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="customerId" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="g etC usto merStatusR espo nse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="status" type="xsd:string"/>
<xsd:element name="statusMessage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
...
</wsdl:definitions>
References
For more details about the format of WSDL contracts and how to create your own WSDL contracts, see Writing WSDL Contracts and the Eclipse JAX-WS Tools Component .
36.3. WSDL-T O-JAVA MAVEN PLUG-IN
Overview
In contrast to the Java-first approach, which starts with a Java interface and then generates the WSDL contract, the WSDL-first approach needs to generate Java stub code from the
WSDL contract.
To generate Java stub code from the WSDL contract, you can use either the ws2java command-line utility or the cxf-co d eg en-pl ug i n Maven plug-in. The plug-in approach is ideal for Maven-based projects: after you paste the requisite plug-in configuration into your
POM file, the WSDL-to-Java code generation step is integrated into your build.
Configure t he WSDL-t o-Java Maven plug-in
Configuring the WSDL-to-Java Maven plug-in is relatively easy, because most of the default configuration settings can be left as they are. After copying and pasting the sample pl ug i n element into your project's POM file, there are just a few basic settings that need to be customized, as follows:
4 59
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CXF version—make sure that the plug-in's dependencies are using the latest version of
Apache CXF.
WSDL file location—specify the WSDL file location in the
co nfi g urati o n/wsd l O pti o ns/wsd l O pti o n/wsd l element.
Location of output—specify the root directory of the generated Java source files in the
co nfi g urati o n/so urceR o o t element.
For example, the following POM fragment shows how to configure the cxf-co d eg en-
pl ug i n plug-in to generate Java stub code from the C usto merServi ce. wsd l WSDL file:
<project ...>
...
<parent>
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>cxf-webinars</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-co d eg en-pl ug i n</artifactId>
<version>${cxf-version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<!-- Maven auto-compiles any source files under target/generated-sources/ -->
<sourceRoot>${based i r}/targ et/g enerated -
so urces/jaxws</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${based i r}/. . /src/mai n/reso urces/wsd l /C usto merServi ce. wsd l </wsd l>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
4 60
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N
</plugins>
</build>
</project>
Generat ed Java source code
With the sample configuration shown here, the generated Java source code is written under the targ et/g enerated -so urces/jaxws directory. Note that the Web service implementation is dependent on this generated stub code—for example, the service implementation class must implement the generated C usto merServi ce SEI.
Adding t he generat ed source t o an IDE
If you are using an IDE such as Eclipse or Intellij's IDEA, you need to make sure that the IDE is aware of the generated Java code. For example, in Eclipse it is necessary to add the
targ et/g enerated -so urces/jaxws directory to the project as a source code directory.
Compiling t he generat ed code
You must ensure that the generated Java code is compiled and added to the deployment package. By convention, Maven automatically compiles any source files that it finds under the following directory:
BaseDir/target/generated-sources/
Hence, if you configure the output directory as shown in the preceding POM fragment, the generated code is automatically compiled by Maven.
Reference
For full details of how to configure the Java-to-WSDL plug-in, see the Maven cxf-codegenplugin reference page.
36.4 . INST ANT IAT E T HE WS ENDPOINT
Overview
In Apache CXF, you create a WS endpoint by defining a jaxws: end po i nt element in XML.
The WS endpoint is effectively the runtime representation of the Web service: it opens an IP port to listen for SOAP/HTTP requests, is responsible for marshalling and unmarshalling messages (making use of the generated Java stub code), and routes incoming requests to the relevant methods on the implementor class.
In other words, creating a Web service in Spring XML consists essentially of the following two steps:
4 61
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
1. Create an instance of the implementor class, using the Spring bean element.
2. Create a WS endpoint, using the jaxws: end po i nt element.
Define JAX-WS endpoint in XML
The following sample Spring file shows how to define a JAX-WS endpoint in XML, using the
jaxws: end po i nt element.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:soap="http://cxf.apache.org/bindings/soap"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:endpoint
xmlns:customer="http://demo.fusesource.com/wsdl/CustomerService/"
id="customerService"
address="/C usto mer"
serviceName="customer:CustomerService"
endpointName="customer:SOAPOverHTTP"
implementor="#customerServiceImpl">
</jaxws:endpoint>
<bean id="customerServiceImpl"
class="com.fusesource.customer.ws.CustomerServiceImpl"/>
</beans>
Address for t he Jet t y cont ainer
In the preceding example, the ad d ress attribute of the jaxws: end po i nt element specifies the servlet context for this endpoint, relative to the Jetty container in which it is deployed.
Referencing t he service implement at ion
The i mpl emento r attribute of the jaxws: end po i nt element is used to reference the implementation of the WS service. The value of this attribute can either be the name of the implementation class or (as in this example) a bean reference in the format, #BeanID, where the # character indicates that the following identifier is the name of a bean in the bean registry.
4 62
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N
36.5. DEPLOY T O AN OSGI CONT AINER
Overview
One of the options for deploying the Web service is to package it as an OSGi bundle and deploy it into an OSGi container such as Red Hat JBoss Fuse. Some of the advantages of an OSGi deployment include:
Bundles are a relatively lightweight deployment option (because dependencies can be shared between deployed bundles).
OSGi provides sophisticated dependency management, ensuring that only versionconsistent dependencies are added to the bundle's classpath.
Using t he Maven bundle plug-in
The Maven bundle plug-in is used to package your project as an OSGi bundle, in preparation for deployment into the OSGi container. There are two essential modifications to make to your project's po m. xml file:
1. Change the packaging type to bund l e (by editing the value of the
pro ject/packag i ng element in the POM).
2. Add the Maven bundle plug-in to your POM file and configure it as appropriate.
Configuring the Maven bundle plug-in is quite a technical task (although the default settings are often adequate). For full details of how to customize the plug-in configuration, consult
Deploying into the OSGi Container and Managing OSGi Dependencies.
Sample bundle plug-in configurat ion
The following POM fragment shows a sample configuration of the Maven bundle plug-in, which is appropriate for the current example.
<?xml version="1.0"?>
<project ...>
...
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>customer-ws-osgi-bundle</artifactId>
<name>customer-ws-osgi-bundle</name>
<url>http://www.fusesource.com</url>
<packag i ng >bund l e</packag i ng >
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
4 63
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<artifactId>maven-bund l e-pl ug i n</artifactId>
<version>${version.maven-bundle-plugin}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>
!com.fusesource.customer.ws,
!com.fusesource.demo.customer,
!com.fusesource.demo.wsdl.customerservice
</Export-Package>
<Import-Package>
*
</Import-Package>
<DynamicImport-Package>
org.apache.cxf.*,
org.springframework.beans.*
</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
Dynamic import s
The Java packages from Apache CXF and the Spring API are imported using dynamic imports (specified using the D ynami cImpo rt-P ackag e element). This is a pragmatic way of dealing with the fact that Spring XML files are not terribly well integrated with the Maven bundle plug-in. At build time, the Maven bundle plug-in is not able to figure out which Java classes are required by the Spring XML code. By listing wildcarded package names in the
D ynami cImpo rt-P ackag e element, however, you allow the OSGi container to figure out which Java classes are needed by the Spring XML code at run time.
No t e
In general, using D ynami cImpo rt-P ackag e headers is not recommended in
OSGi, because it short-circuits OSGi version checking. Normally, what should happen is that the Maven bundle plug-in lists the Java packages used at build time, along with their versions, in the Impo rt-P ackag e header. At deploy time, the OSGi container then checks that the available Java packages are compatible with the build-time versions listed in the Impo rt-P ackag e header. With dynamic imports, this version checking cannot be performed.
Build and deploy t he service bundle
After you have configured the POM file, you can build the Maven project and install it in your local repository by entering the following command: mvn install
4 64
CHAPT ER 36 . WSDL- FIRST SERVICE IMPLEMENT AT IO N
To deploy the service bundle, enter the following command at the command console: karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-osgi-bundle
No t e
If your local Maven repository is stored in a non-standard location, you might need to customize the value of the o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property in the EsbInstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file, before you can use the mvn: scheme to access Maven artifacts.
Red Hat JBoss Fuse default servlet cont ainer
Red Hat JBoss Fuse has a default Jetty container which, by default, listens for HTTP requests on port 8181. Moreover, WS endpoints in this container are implicitly deployed under the servlet context cxf/. Hence, any WS endpoint whose ad d ress attribute is configured in the jaxws: end po i nt element as /EndpointContext will have the following effective address: http://Hostname:8181/cxf/EndpointContext
You can optionally customize the default servlet container by editing settings in the following file:
InstallDir/etc/org.ops4j.pax.web.cfg
Full details of the properties you can set in this file are given in the Ops4j Pax Web configuration reference .
Check t hat t he service is running
A simple way of checking that the service is running is to point your browser at the following
URL: http://localhost:8181/cxf/Customer?wsdl
This query should return a copy of the WS endpoint's WSDL contract.
4 65
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 37. IMPLEMENTING A WS CLIENT
37.1. WS CLIENT OVERVIEW
Overview
The key object in a WS client is the WS client proxy object, which enables you to access the remote Web service by invoking methods on the SEI. The proxy object itself can easily be instantiated using the jaxws: cl i ent element in Spring XML.
Demonst rat ion locat ion
The code presented in this chapter is taken from the following demonstration: cxf-webinars-jboss-fuse-6.2.1/custo mer-ws-cl i ent
For details of how to download and install the demonstration code, see Chapter 34,
Demonstration Code for Camel/CXF
WSDL cont ract
The WSDL contract is a platform-neutral and language-neutral description of the Web service interface. It contains all of the metadata that a client needs to find a Web service and invoke its operations. You can generate Java stub code from the WSDL contract, which provides an API that makes it easy to invoke the remote WSDL operations.
Service Endpoint Int erface (SEI)
The most important piece of the generated stub code is the SEI, which is an ordinary Java interface that represents the Web service interface in the Java language.
WS client proxy
The WS client proxy is an object that converts Java method invocations to remote procedure calls, sending and receiving messages to a remote instance of the Web service across the network. The methods of the proxy are exposed through the SEI.
No t e
The proxy type is generated dynamically by Apache CXF at run time. That is, their is no class in the stub code that corresponds to the implementation of the proxy (the only relevant entity is the SEI, which defines the proxy's interface).
T he Cust omerService client
4 66
CHAPT ER 37 . IMPLEMENT ING A WS CLIENT
To take a specific example, consider the custo mer-ws-cl i ent demonstration, which is available from the following location: cxf-webinars-jboss-fuse-6.2.1/customer-ws-client
Figure 37.1, “ Building a WS Client”
shows an overview of the files required to implement and build the WS client.
Fig u re 37.1. Bu ild in g a WS Clien t
Implement ing and building t he WS client
To implement and build the sample WS client shown in Figure 37.1, “ Building a WS Client”
, starting from scratch, you would perform the following steps:
1. Obtain a copy of the WSDL contract.
2. Generate the Java stub code from the WSDL contract using a WSDL-to-Java converter, ws2java. This gives you the SEI, C usto merServi ce, and its related classes, such as C usto mer.
3. Implement the main client class, C l i entInvo ker, which invokes the Web service operations. In this class define a bean property of type, C usto merServi ce, so that the client class can receive a reference to the WS client proxy by property injection.
4. In a Spring XML file, instantiate the WS client proxy and inject it into the main client class, C l i entInvo ker.
37.2. WSDL-T O-JAVA MAVEN PLUG-IN
Overview
4 67
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To generate Java stub code from the WSDL contract, you can use either the ws2java command-line utility or the cxf-co d eg en-pl ug i n Maven plug-in. When using Maven, the plug-in approach is ideal: after you paste the requisite plug-in configuration into your POM file, the WSDL-to-Java code generation step is integrated into your build.
Configure t he WSDL-t o-Java Maven plug-in
Configuring the WSDL-to-Java Maven plug-in is relatively easy, because most of the default configuration settings can be left as they are. After copying and pasting the sample pl ug i n element into your project's POM file, there are just a few basic settings that need to be customized, as follows:
CXF version—make sure that the plug-in's dependencies are using the latest version of
Apache CXF.
WSDL file location—specify the WSDL file location in the
co nfi g urati o n/wsd l O pti o ns/wsd l O pti o n/wsd l element.
Location of output—specify the root directory of the generated Java source files in the
co nfi g urati o n/so urceR o o t element.
For example, the following POM fragment shows how to configure the cxf-co d eg en-
pl ug i n plug-in to generate Java stub code from the C usto merServi ce. wsd l WSDL file:
<project ...>
...
<parent>
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>cxf-webinars</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-co d eg en-pl ug i n</artifactId>
<version>${cxf-version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${based i r}/targ et/g enerated -
so urces/jaxws</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${based i r}/. . /src/mai n/reso urces/wsd l /C usto merServi ce. wsd l </wsd l>
4 68
CHAPT ER 37 . IMPLEMENT ING A WS CLIENT
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Generat ed Java source code
With the sample configuration shown here, the generated Java source code is written under the targ et/g enerated -so urces/jaxws directory. Note that the client implementation is dependent on this generated stub code—for example, the client invokes the proxy using the generated C usto merServi ce SEI.
Add generat ed source t o IDE
If you are using an IDE such as Eclipse or Intellij's IDEA, you need to make sure that the IDE is aware of the generated Java code. For example, in Eclipse it is necessary to add the
targ et/g enerated -so urces/jaxws directory to the project as a source code directory.
Compiling t he generat ed code
You must ensure that the generated Java code is compiled and added to the deployment package. By convention, Maven automatically compiles any source files that it finds under the following directory:
BaseDir/target/generated-sources/
Hence, if you configure the output directory as shown in the preceding POM fragment, the generated code is automatically compiled by Maven.
Reference
For full details of how to configure the Java-to-WSDL plug-in, see the Maven cxf-codegenplugin reference page.
37.3. INST ANT IAT E T HE WS CLIENT PROXY
Overview
4 69
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The WS client proxy is the most important kind of object in a WS client, because it provides a simple way of invoking operations on a remote Web service. The proxy enables you to access a Web service by invoking methods locally on a Java interface. The methods invoked on the proxy object are then translated into remote procedure calls on the Web service.
You can instantiate a WS client proxy straightforwardly using the jaxws: cl i ent element.
Define t he WS client in XML
The following Spring XML fragment shows how to instantiate a client proxy bean using the
jaxws: cl i ent element.
<?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:jaxws="http://cxf.apache.org/jaxws"
xmlns:soap="http://cxf.apache.org/bindings/soap"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:client
id="custo merServi ceP ro xy"
address="http://localhost:8181/cxf/Customer" serviceClass="com.fusesource.demo.wsdl.customerservice.CustomerService"
/>
<bean id="customerServiceClient"
class="com.fusesource.customer.client.ClientInvoker"
init-method="init" destroy-method="destroy">
<property name="customerService" ref="custo merServi ceP ro xy"/>
</bean>
</beans>
T he jaxws:client element
The jaxws: cl i ent element creates a client proxy dynamically (that is, there is no dedicated class that represents a proxy implementation in the Java stub code). The following attributes are used to define the proxy:
i d
4 70
The ID that you specify here is entered in the bean registry and can be used to reference the proxy instance from other beans.
ad d ress
CHAPT ER 37 . IMPLEMENT ING A WS CLIENT
The full address of the remote Web service that this proxy connects to.
servi ceC l ass
The fully-qualified class name of the Web service's SEI (you invoke methods on the proxy through the SEI).
Inject ing wit h t he proxy reference
To access the proxy instance, simply inject the proxy into one or more other beans defined in
XML. Given that the proxy ID has the value, custo merServi ceP ro xy, you can inject it into a bean property using the Spring pro perty element, as follows:
<bean ...>
<property name="customerService" ref="custo merServi ceP ro xy"/>
</bean>
The bean class that is being injected must have a corresponding setC usto merServi ce setter method—for example:
// Java
...
public class ClientInvoker implements Runnable {
...
public void setCustomerService(CustomerService customerService) {
this.customerService = customerService;
}
}
37.4 . INVOKE WS OPERAT IONS
Proxy int erface is SEI int erface
The proxy implements the SEI. Hence, to make remote procedure calls on the Web service, simply invoke the SEI methods on the proxy instance.
Invoking t he lookupCust omer operat ion
For example, the C usto merServi ce SEI exposes the l o o kupC usto mer method, which takes a customer ID as its argument and returns a C usto mer data object. Using the proxy instance, custo merServi ce, you can invoke the l o o kupC usto mer operation as follows:
// Java com.fusesource.demo.customer.Customer response
= customerService.lookupCustomer("1234");
4 71
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
log.info("Got back " + response.getFirstName() + " "
+ response.getLastName()
+ ", ph:" + response.getPhoneNumber() );
T he Client Invoker class
In the cxf-webi nars-jbo ss-fuse-6 . 2. 1/custo mer-ws-cl i ent project, there is a
C l i entInvo ker class (located in
src/mai n/java/co m/fuseso urce/custo mer/cl i ent), which defines a continuous loop that invokes the l o o kupC usto mer operation.
When you are experimenting with the demonstration code in the latter chapters of this guide, you might need to modify the C l i entInvo ker class, possibly adding operation invocations.
37.5. DEPLOY T O AN OSGI CONT AINER
Overview
One of the options for deploying the WS client is to package it as an OSGi bundle and deploy it into an OSGi container such as Red Hat JBoss Fuse. Some of the advantages of an OSGi deployment include:
Bundles are a relatively lightweight deployment option (because dependencies can be shared between deployed bundles).
OSGi provides sophisticated dependency management, ensuring that only versionconsistent dependencies are added to the bundle's classpath.
Using t he Maven bundle plug-in
The Maven bundle plug-in is used to package your project as an OSGi bundle, in preparation for deployment into the OSGi container. There are two essential modifications to make to your project's po m. xml file:
1. Change the packaging type to bund l e (by editing the value of the
pro ject/packag i ng element in the POM).
2. Add the Maven bundle plug-in to your POM file and configure it as appropriate.
Configuring the Maven bundle plug-in is quite a technical task (although the default settings are often adequate). For full details of how to customize the plug-in configuration, consult
Deploying into the OSGi Container and Managing OSGi Dependencies.
Sample bundle plug-in configurat ion
4 72
CHAPT ER 37 . IMPLEMENT ING A WS CLIENT
The following POM fragment shows a sample configuration of the Maven bundle plug-in, which is appropriate for the current example.
<?xml version= "1.0" ?>
<project ...>
...
<groupId> com.fusesource.byexample.cxf-webinars </groupId>
<artifactId> customer-ws-client </artifactId>
<name> customer-ws-client </name>
<packaging>
bund l e
</packaging>
...
<build>
<plugins>
...
<plugin>
<groupId> org.apache.felix
</groupId>
<artifactId>
maven-bund l e-pl ug i n
</artifactId>
<extensions> true </extensions>
<configuration>
<instructions>
<Export-Package>
!com.fusesource.customer.client,
!com.fusesource.demo.customer,
!com.fusesource.demo.wsdl.customerservice
</Export-Package>
<Import-Package>
*
</Import-Package>
<DynamicImport-Package>
org.apache.cxf.*,
org.springframework.beans.*
</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
Dynamic import s
The Java packages from Apache CXF and the Spring API are imported using dynamic imports (specified using the D ynami cImpo rt-P ackag e element). This is a pragmatic way of dealing with the fact that Spring XML files are not terribly well integrated with the Maven bundle plug-in. At build time, the Maven bundle plug-in is not able to figure out which Java classes are required by the Spring XML code. By listing wildcarded package names in the
D ynami cImpo rt-P ackag e element, however, you allow the OSGi container to figure out which Java classes are needed by the Spring XML code at run time.
4 73
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
In general, using D ynami cImpo rt-P ackag e headers is not recommended in
OSGi, because it short-circuits OSGi version checking. Normally, what should happen is that the Maven bundle plug-in lists the Java packages used at build time, along with their versions, in the Impo rt-P ackag e header. At deploy time, the OSGi container then checks that the available Java packages are compatible with the build time versions listed in the Impo rt-P ackag e header. With dynamic imports, this version checking cannot be performed.
Build and deploy t he client bundle
After you have configured the POM file, you can build the Maven project and install it in your local repository by entering the following command: mvn install
To deploy the client bundle, enter the following command at the containers command console: karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-client
No t e
If your local Maven repository is stored in a non-standard location, you might need to customize the value of the o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property in the EsbInstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file, before you can use the mvn: scheme to access Maven artifacts.
Check t hat t he client is running
Assuming that you have already deployed the corresponding Web service into the OSGi container, you can verify that the client is successfully invoking WSDL operations by checking the log, as follows: karaf@ root> log:display -n 10
The client invokes an operation on the Web service once every second.
4 74
CHAPT ER 38 . PO JO - BASED RO UT E
CHAPTER 38. POJO-BASED ROUTE
38.1. PROCESSING MESSAGES IN POJO FORMAT
Overview
By default, the Camel CXF component marshals incoming Web service requests into the
POJO data form, where the In message body is encoded as a list of Java objects (one for each operation parameter). The POJO data format has advantages and disadvantages, as follows:
The big advantage of the POJO data format is that the operation parameters are encoded using the JAXB standard, which makes them easy to manipulate in Java.
The downside of the POJO data format, on the other hand, is that it requires that the
WSDL metadata is converted to Java in advance (as defined by the JAX-WS and JAXB mappings) and compiled into your application. This means that a POJO-based route is not very dynamic.
Demonst rat ion locat ion
The code presented in this chapter is taken from the following demonstration: cxf-webinars-jboss-fuse-6.2.1/custo mer-ws-camel -cxf-po jo
For details of how to download and install the demonstration code, see Chapter 34,
Demonstration Code for Camel/CXF
Camel CXF component
The Camel CXF component is an Apache CXF component that integrates Web services with routes. You can use it either to instantiate consumer endpoints (at the start of a route), which behave like Web service instances, or to instantiate producer endpoints (at any other points in the route), which behave like WS clients.
No t e
Camel CXF endpoints—which are instantiated using the cxf: cxfEnd po i nt XML element and are implemented by the Apache Camel project—are not to be confused with the Apache CXF JAX-WS endpoints—which are instantiated using the
jaxws: end po i nt XML element and are implemented by the Apache CXF project.
POJO dat a format
POJO data format is the default data format used by the Camel CXF component and it has the following characteristics:
4 75
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
JAX-WS and JAXB stub code (as generated from the WSDL contract) must be provided.
The SOAP body is marshalled into a list of Java objects.
One Java object for each part or parameter of the corresponding WSDL operation.
The type of the message body is
o rg . apache. cxf. messag e. Messag eC o ntentsLi st.
The SOAP headers are converted into headers in the exchange's In message.
Implement ing and building a POJO rout e
To implement and build the demonstration POJO-based route, starting from scratch, you would perform the following steps:
1. Obtain a copy of the WSDL contract that is to be integrated into the route.
2. Generate the Java stub code from the WSDL contract using a WSDL-to-Java converter. This gives you the SEI, C usto merServi ce, and its related classes, such as C usto mer.
3. Instantiate the Camel CXF endpoint in Spring, using the cxf: cxfEnd po i nt element.
4. Implement the route in XML, where you can use the content-based router to sort requests by operation name.
5. Implement the operation processor beans, which are responsible for processing each operation. When implementing these beans, the message contents must be accessed in POJO data format.
Sample POJO rout e
Figure 38.1, “ Sample POJO Route” shows an outline of the route that is used to process the
operations of the C usto merServi ce Web service using the POJO data format. After sorting the request messages by operation name, an operation-specific processor bean reads the incoming request parameters and then generates a response in the POJO data format.
Fig u re 38.1. Samp le PO JO Ro u t e
4 76
CHAPT ER 38 . PO JO - BASED RO UT E
38.2. WSDL-T O-JAVA MAVEN PLUG-IN
Overview
To generate Java stub code from the WSDL contract, you can use either the ws2java command-line utility or the cxf-co d eg en-pl ug i n Maven plug-in. When using Maven, the plug-in approach is ideal: after you paste the requisite plug-in configuration into your POM file, the WSDL-to-Java code generation step is integrated into your build.
Configure t he WSDL-t o-Java Maven plug-in
Configuring the WSDL-to-Java Maven plug-in is relatively easy, because most of the default configuration settings can be left as they are. After copying and pasting the sample pl ug i n element into your project's POM file, there are just a few basic settings that need to be customized, as follows:
CXF version—make sure that the plug-in's dependencies are using the latest version of
Apache CXF.
WSDL file location—specify the WSDL file location in the
co nfi g urati o n/wsd l O pti o ns/wsd l O pti o n/wsd l element.
Location of output—specify the root directory of the generated Java source files in the
co nfi g urati o n/so urceR o o t element.
For example, the following POM fragment shows how to configure the cxf-co d eg en-
pl ug i n plug-in to generate Java stub code from the C usto merServi ce. wsd l WSDL file:
<project ...>
...
<parent>
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>cxf-webinars</artifactId>
4 77
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<version>1.0-SNAPSHOT</version>
</parent>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-co d eg en-pl ug i n</artifactId>
<version>${cxf-version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${based i r}/targ et/g enerated -
so urces/jaxws</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${based i r}/. . /src/mai n/reso urces/wsd l /C usto merServi ce. wsd l </wsd l>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Generat ed Java source code
With the sample configuration shown here, the generated Java source code is written under the targ et/g enerated -so urces/jaxws directory. Note that the route is dependent on this generated stub code—for example, when processing the POJO parameters, the parameter processor uses the C usto mer data type from the stub code.
Add generat ed code t o IDE
If you are using an IDE such as Eclipse or Intellij's IDEA, you need to make sure that the IDE is aware of the generated Java code. For example, in Eclipse it is necessary to add the
targ et/g enerated -so urces/jaxws directory to the project as a source code directory.
Compiling t he generat ed code
4 78
CHAPT ER 38 . PO JO - BASED RO UT E
You must ensure that the generated Java code is compiled and added to the deployment package. By convention, Maven automatically compiles any source files that it finds under the following directory:
BaseDir/target/generated-sources/
Hence, if you configure the output directory as shown in the preceding POM fragment, the generated code is automatically compiled by Maven.
Reference
For full details of how to configure the Java-to-WSDL plug-in, see the Maven cxf-codegenplugin reference page.
38.3. INST ANT IAT E T HE WS ENDPOINT
Overview
In Apache Camel, the Camel CXF component is the key to integrating routes with Web services. You can use the Camel CXF component to create a CXF endpoint, which can be used in either of the following ways:
Consumer—(at the start of a route) represents a Web service instance, which integrates with the route. The type of payload injected into the route depends on the value of the endpoint's d ataFo rmat option.
Producer—(at other points in the route) represents a WS client proxy, which converts the current exchange object into an operation invocation on a remote Web service. The format of the current exchange must match the endpoint's d ataFo rmat setting.
In the current demonstration, we are interested in creating a Camel CXF consumer endpoint, with the d ataFo rmat option set to POJO.
Maven dependency
The Camel CXF component requires you to add a dependency on the camel -cxf component in your Maven POM. For example, the po m. xml file from the custo mer-ws-
camel -cxf-po jo demonstration project includes the following dependency:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-cxf</artifactId>
<version>${camel-version}</version>
</dependency>
T he cxf:bean: URI synt ax
4 79
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The cxf: bean: URI is used to bind an Apache CXF endpoint to a route and has the following general syntax: cxf:bean:CxfEndpointID[?Options]
Where CxfEndpointID is the ID of a bean created using the cxf: cxfEnd po i nt element, which configures the details of the WS endpoint. You can append options to this URI (where the options are described in detail in chapter "CXF" in "Apache Camel Component
Reference" ). If you do not specify any additional options, the endpoint uses the POJO data
format by default.
For example, to start a route with a Apache CXF endpoint that is configured by the bean with
ID, custo mer-ws, define the route as follows:
<route>
<from uri="cxf:bean:custo mer-ws"/>
...
</route>
No t e
There is an alternative URI syntax, cxf: //WsAddress[?Options], which enables you to specify all of the WS endpoint details in the URI (so there is no need to reference a bean instance). This typically results in a long and cumbersome URI, but is useful in some cases.
T he cxf:cxfEndpoint element
The cxf: cxfEnd po i nt element is used to define a WS endpoint that binds either to the start (consumer endpoint) or the end (producer endpoint) of a route. For example, to define the custo mer-ws WS endpoint referenced in the preceding route, you would define a
cxf: cxfEnd po i nt element as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...
xmlns:cxf="http://camel.apache.org/schema/cxf" ...>
...
<cxf:cxfEndpoint id="custo mer-ws"
ad d ress= "/C usto mer"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService" serviceClass="com.fusesource.demo.wsdl.customerservice.CustomerService"
xmlns:c="http://demo.fusesource.com/wsdl/CustomerService/"/>
...
</beans>
4 80
CHAPT ER 38 . PO JO - BASED RO UT E
Imp o rt an t
Remember that the cxf: cxfEnd po i nt element and the jaxws: end po i nt element use different XML schemas (although the syntax looks superficially similar).
These elements bind a WS endpoint in different ways: the cxf: cxfEnd po i nt element instantiates and binds a WS endpoint to an Apache Camel route, whereas the jaxws: end po i nt element instantiates and binds a WS endpoint to a Java class using the JAX-WS mapping.
Address for t he Jet t y cont ainer
Apache CXF deploys the WS endpoint into a Jetty servlet container instance and the
ad d ress attribute of cxf: cxfEnd po i nt is therefore used to configure the addressing information for the endpoint in the Jetty container.
Apache CXF supports the notion of a default servlet container instance. The way the default servlet container is initialized and configured depends on the particular mode of deployment that you choose. For example the Red Hat JBoss Fuse container and Web containers (such as Tomcat) provide a default servlet container.
There are two different syntaxes you can use for the endpoint address, where the syntax that you use effectively determines whether or not the endpoint is deployed into the default servlet container, as follows:
Address syntax for default servlet container—to use the default servlet container, specify only the servlet context for this endpoint. Do not specify the protocol, host, and IP port in the address. For example, to deploy the endpoint to the /C usto mer servlet context in the default servlet container: address="/Customer"
Address syntax for custom servlet container—to instantiate a custom Jetty container for this endpoint, specify a complete HTTP URL, including the host and IP port (the value of the
IP port effectively identifies the target Jetty container). Typically, for a Jetty container, you specify the host as 0 . 0 . 0 . 0 , which is interpreted as a wildcard that matches every IP network interface on the local machine (that is, if deployed on a multi-homed host, Jetty opens a listening port on every network card). For example, to deploy the endpoint to the custom Jetty container listening on IP port, 80 83: address="http://0.0.0.0:8083/Customer"
No t e
If you want to configure a secure endpoint (secured by SSL), you would specify the https: scheme in the address.
Referencing t he SEI
4 81
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The servi ceC l ass attribute of the cxf: cxfEnd po i nt element references the SEI of the
Web service, which in this case is the C usto merServi ce interface.
38.4 . SORT MESSAGES BY OPERAT ION NAME
T he operat ionName header
When the WS endpoint parses an incoming operation invocation in POJO mode, it automatically sets the o perati o nName header to the name of the invoked operation. You can then use this header to sort messages by operation name.
Sort ing by operat ion name
For example, the custo mer-ws-camel -cxf-po jo demonstration defines the following route, which uses the content-based router pattern to sort incoming messages, based on the operation name. The when predicates check the value of the o perati o nName header using simple language expressions, sorting messages into invocations on the upd ateC usto mer operation, the l o o kupC usto mer operation, or the g etC usto merStatus operation.
<beans ...>
...
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="cxf:bean:customer-ws"/>
<choice>
<when>
<simple>${in.header.operationName} ==
'updateCustomer'</simple>
<to uri="updateCustomer"/>
</when>
<when>
<simple>${in.header.operationName} ==
'lookupCustomer'</simple>
<to uri="lookupCustomer"/>
</when>
<when>
<simple>${in.header.operationName} ==
'getCustomerStatus'</simple>
<to uri="getCustomerStatus"/>
</when>
</choice>
</route>
</camelContext>
<bean id="updateCustomer" class="com.fusesource.customerwscamelcxfpojo.UpdateCustomerProcessor"/>
<bean id="getCustomerStatus" class="com.fusesource.customerwscamelcxfpojo.GetCustomerStatusProcessor
4 82
CHAPT ER 38 . PO JO - BASED RO UT E
"/>
<bean id="lookupCustomer" class="com.fusesource.customerwscamelcxfpojo.LookupCustomerProcessor"/>
</beans>
Beans as endpoint s
Note how the preceding route uses a convenient shortcut to divert each branch of the
cho i ce DSL to a different processor bean. The DSL for sending exchanges to producer endpoints (for example, <to uri = "Destination"/>) is integrated with the bean registry: if the Destination does not resolve to an endpoint or a component, the Destination is used as a bean ID to look up the bean registry. In this example, the exchange is routed to processor beans (which implement the o rg . apache. camel . P ro cesso r interface).
38.5. PROCESS OPERAT ION PARAMET ERS
Overview
The most important characteristic of using Camel CXF in POJO mode is that the exchange's message body contains a list of Java objects, representing the parameters of the WSDL operation. The types of the Java objects are defined by the standard JAXB mapping and the implementations of these parameter types are provided by the Java stub code.
Cont ent s of request message body
In POJO mode, the body of the request message is an
o rg . apache. cxf. messag e. Messag eC o ntentsLi st object. You can also obtain the message body as an O bject[] array (where type conversion is automatic).
When the body is obtained as an O bject[] array, the array contains the list of all the operation's IN, INOUT, and OUT parameters in exactly the same order as defined in the WSDL
contract (and in the same order as the corresponding operation signature of the SEI). The parameter mode affects the content as follows:
IN
INO UT
Contains a parameter value from the client.
Contains a Ho l d er object containing a parameter value from the client.
O UT
Contains an empty Ho l d er object, which is a placeholder for the response.
4 83
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
Unlike OUT parameters, there is no placeholder in the request's O bject[] array to represent a return value.
Cont ent s of response message body
In POJO mode, the body of the response message can be either an
o rg . apache. cxf. messag e. Messag eC o ntentsLi st object or an O bject[] array.
When setting the response body as an O bject[] array, the array should contain only the operation's INOUT and OUT parameters in the same order as defined in the WSDL contract, omitting the IN parameters. The parameter mode affects the content as follows:
INO UT
Contains a Ho l d er object, which you must set to a response value. The Ho l d er object used here must be exactly the Holder object for the corresponding parameter that
was extracted from the request Object[] array. Creating and inserting a new Ho l d er object into the O bject[] array does not work.
O UT
Contains a Ho l d er object, which you must initialize with a response value. The
Ho l d er object used here must be exactly the Holder object for the corresponding
parameter that was extracted from the request Object[] array. Creating and inserting a new Ho l d er object into the O bject[] array does not work.
No t e
If you defined the Web service interface using the Java-first approach, note that the return value (if any) must be set as the first element in the response's O bject[] array. The return type is set as a plain object: it does not use a Ho l d er object.
Example: get Cust omerSt at us operat ion
For example, the g etC usto merStatus operation takes three parameters: IN, OUT, and OUT, respectively. The corresponding method signature in the SEI is, as follows:
// Java public void getCustomerStatus(
@ WebParam(name = "customerId", targetNamespace = "")
java.lang.String customerId,
@ WebParam(mode = WebParam.Mode.OUT, name = "status", targetNamespace = "")
javax.xml.ws.Holder<java.lang.String> status,
4 84
CHAPT ER 38 . PO JO - BASED RO UT E
@ WebParam(mode = WebParam.Mode.OUT, name = "statusMessage", targetNamespace = "")
javax.xml.ws.Holder<java.lang.String> statusMessage
);
Example: request and response bodies
For the g etC usto merStatus operation, the bodies of the request message and the response message have the following contents:
Request message—as an O bject[] array type, the contents are: { Stri ng
custo merId , Ho l d er<Stri ng > status, Ho l d er<Stri ng > statusMessag e }.
Response message—as an O bject[] array type, the contents are: {Ho l d er<Stri ng >
status, Ho l d er<Stri ng > statusMessag e }
Example: processing get Cust omerSt at us
The G etC usto merStatusP ro cesso r class is responsible for processing incoming
g etC usto merStatus invocations. The following sample implementation for POJO mode shows how to read the request parameters from the In message body and then set the response parameters in the Out message body.
// Java package com.fusesource.customerwscamelcxfpojo; import javax.xml.ws.Holder; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GetCustomerStatusProcessor implements Processor {
public static final Logger log =
LoggerFactory.getLogger(GetCustomerStatusProcessor.class);
public void process(Exchange exchng) throws Exception {
Object[] args = exchng.getIn().getBody(Object[].class);
String id = (String) args[0];
Holder<String> status = (Holder<String>) args[1];
Holder<String> statusMsg = (Holder<String>) args[2];
log.debug("Getting status for customer '" + id + "'");
// This is where you'd actually do the work! Setting
// the holder values to constants for the sake of brevity.
//
status.value = "Offline";
statusMsg.value = "Going to sleep now!";
4 85
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
exchng.getOut().setBody(new Object[] {status , statusMsg});
}
}
38.6. DEPLOY T O OSGI
Overview
One of the options for deploying the POJO-based route is to package it as an OSGi bundle and deploy it into an OSGi container such as Red Hat JBoss Fuse. Some of the advantages of an OSGi deployment include:
Bundles are a relatively lightweight deployment option (because dependencies can be shared between deployed bundles).
OSGi provides sophisticated dependency management, ensuring that only versionconsistent dependencies are added to the bundle's classpath.
Using t he Maven bundle plug-in
The Maven bundle plug-in is used to package your project as an OSGi bundle, in preparation for deployment into the OSGi container. There are two essential modifications to make to your project's po m. xml file:
1. Change the packaging type to bund l e (by editing the value of the
pro ject/packag i ng element in the POM).
2. Add the Maven bundle plug-in to your POM file and configure it as appropriate.
Configuring the Maven bundle plug-in is quite a technical task (although the default settings are often adequate). For full details of how to customize the plug-in configuration, consult
Deploying into the OSGi Container and Managing OSGi Dependencies.
Sample bundle plug-in configurat ion
The following POM fragment shows a sample configuration of the Maven bundle plug-in, which is appropriate for the current example.
<?xml version="1.0"?>
<project ...>
...
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>customer-ws-camel-cxf-pojo</artifactId>
<name>customer-ws-camel-cxf-pojo</name>
4 86
CHAPT ER 38 . PO JO - BASED RO UT E
<packag i ng >bund l e</packag i ng >
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
*
</Import-Package>
<DynamicImport-Package>
org.apache.cxf.*,
org.springframework.beans.*
</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
Dynamic import s
The Java packages from Apache CXF and the Spring API are imported using dynamic imports (specified using the D ynami cImpo rt-P ackag e element). This is a pragmatic way of dealing with the fact that Spring XML files are not terribly well integrated with the Maven bundle plug-in. At build time, the Maven bundle plug-in is not able to figure out which Java classes are required by the Spring XML code. By listing wildcarded package names in the
D ynami cImpo rt-P ackag e element, however, you allow the OSGi container to figure out which Java classes are needed by the Spring XML code at run time.
No t e
In general, using D ynami cImpo rt-P ackag e headers is not recommended in
OSGi, because it short-circuits OSGi version checking. Normally, what should happen is that the Maven bundle plug-in lists the Java packages used at build time, along with their versions, in the Impo rt-P ackag e header. At deploy time, the OSGi container then checks that the available Java packages are compatible with the build time versions listed in the Impo rt-P ackag e header. With dynamic imports, this version checking cannot be performed.
Build and deploy t he POJO rout e bundle
After you have configured the POM file, you can build the Maven project and install it in your local repository by entering the following command: mvn install
4 87
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To deploy the route bundle, enter the following command at the JBoss Fuse console: karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-camel-cxf-pojo
No t e
If your local Maven repository is stored in a non-standard location, you might need to customize the value of the o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property in the EsbInstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file, before you can use the mvn: scheme to access Maven artifacts.
4 88
CHAPT ER 39 . PAYLO AD- BASED RO UT E
CHAPTER 39. PAYLOAD-BASED ROUTE
39.1. PROCESSING MESSAGES IN PAYLOAD FORMAT
Overview
Select the PAYLOAD format, if you want to access the SOAP message body in XML format, encoded as a DOM object (that is, of o rg . w3c. d o m. No d e type). One of the advantages of the PAYLOAD format is that no JAX-WS and JAXB stub code is required, which allows your application to be dynamic, potentially handling many different WSDL interfaces.
Having a message body in XML format enables you to parse the request using XML languages such as XPath and to generate responses using templating languages, such as
Velocity.
No t e
The DOM format is not the optimal type to use for large XML message bodies. For
large messages, consider using the techniques described in Chapter 40, Provider-
Demonst rat ion locat ion
The code presented in this chapter is taken from the following demonstration: cxf-webinars-jboss-fuse-6.2.1/custo mer-ws-camel -cxf-payl o ad
For details of how to download and install the demonstration code, see Chapter 34,
Demonstration Code for Camel/CXF
Camel CXF component
The Camel CXF component is an Apache CXF component that integrates Web services with routes. You can use it either to instantiate consumer endpoints (at the start of a route), which behave like Web service instances, or to instantiate producer endpoints (at any other points in the route), which behave like WS clients.
No t e
Came CXF endpoints—which are instantiated using the cxf: cxfEnd po i nt XML element and are implemented by the Apache Camel project—are not to be confused with the Apache CXF JAX-WS endpoints—which are instantiated using the
jaxws: end po i nt XML element and are implemented by the Apache CXF project.
PAYLOAD dat a format
4 89
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The PAYLOAD data format is selected by setting the d ataFo rmat= P AY LO AD option on a
Camel CXF endpoint URI and it has the following characteristics:
Enables you to access the message body as a DOM object (XML payload).
No JAX-WS or JAXB stub code required.
The SOAP body is marshalled as follows:
The message body is effectively an XML payload of o rg . w3c. d o m. No d e type
(wrapped in a C xfP ayl o ad object).
The type of the message body is
o rg . apache. camel . co mpo nent. cxf. C xfP ayl o ad .
The SOAP headers are converted into headers in the exchange's In message, of
o rg . apache. cxf. bi nd i ng . so ap. So apHead er type.
Implement ing and building a PAYLOAD rout e
To implement and build the demonstration PAYLOAD-based route, starting from scratch, you would perform the following steps:
1. Instantiate the Camel CXF endpoint in Spring, using the cxf: cxfEnd po i nt element.
2. Implement the route in XML, where you can use the content-based router to sort requests by operation name.
3. For each operation, define a processor bean to process the request.
4. Define velocity templates for generating the reponse messages.
Sample PAYLOAD rout e
Figure 39.1, “ Sample PAYLOAD Route”
shows an outline of the route that is used to process the operations of the C usto merServi ce Web service using the PAYLOAD data format. After sorting the request messages by operation name, an operation-specific processor bean reads the incoming request parameters. Finally, the response messages are generated using
Velocity templates.
Fig u re 39 .1. Samp le PAYLO AD Ro u t e
4 90
CHAPT ER 39 . PAYLO AD- BASED RO UT E
39.2. INST ANT IAT E T HE WS ENDPOINT
Overview
In Apache Camel, the CXF component is the key to integrating routes with Web services. You can use the CXF component to create two different kinds of endpoint:
Consumer endpoint—(at the start of a route) represents a Web service instance, which integrates with the route. The type of payload injected into the route depends on the value of the endpoint's d ataFo rmat option.
Producer endpoint—represents a special kind of WS client proxy, which converts the current exchange object into an operation invocation on a remote Web service. The format of the current exchange must match the endpoint's d ataFo rmat setting.
T he cxf:bean: URI synt ax
The cxf: bean: URI is used to bind an Apache CXF endpoint to a route and has the following general syntax: cxf:bean:CxfEndpointID[?Options]
Where CxfEndpointID is the ID of a bean created using the cxf: cxfEnd po i nt element, which configures the details of the WS endpoint. You can append options to this URI (where the options are described in detail in
CXF
in the Apache Camel Component Reference Guide ).
To enable payload mode, you must set the URI option, d ataFo rmat= P AY LO AD .
For example, to start a route with an endpoint in PAYLOAD mode, where the endpoint is configured by the custo mer-ws bean, define the route as follows:
<route>
<from uri="cxf:bean:customer-ws?d ataFo rmat= P AY LO AD "/>
...
</route>
4 91
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
T he cxf:cxfEndpoint element
The cxf: cxfEnd po i nt element is used to define a WS endpoint that binds either to the start (consumer endpoint) or the end (producer endpoint) of a route. For example, to define the custo mer-ws WS endpoint in PAYLOAD mode, you define a cxf: cxfEnd po i nt element as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint id="customer-ws"
ad d ress= "/C usto mer"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsd l UR L= "wsd l /C usto merServi ce. wsd l "
xmlns:c="http://demo.fusesource.com/wsdl/CustomerService/"/>
...
</beans>
No t e
In the case of PAYLOAD mode, you do not need to reference the SEI and you must specify the WSDL location instead. In fact, in PAYLOAD mode, you do not require any Java stub code at all.
4 92
Address for t he Jet t y cont ainer
Apache CXF deploys the WS endpoint into a Jetty servlet container instance and the
ad d ress attribute of cxf: cxfEnd po i nt is therefore used to configure the addressing information for the endpoint in the Jetty container.
Apache CXF supports the notion of a default servlet container instance. The way the default servlet container is initialized and configured depends on the particular mode of deployment that you choose. For example the OSGi container and Web containers (such as Tomcat) provide a default servlet container.
There are two different syntaxes you can use for the endpoint address, where the syntax that you use effectively determines whether or not the endpoint is deployed into the default servlet container, as follows:
Address syntax for default servlet container—to use the default servlet container, specify only the servlet context for this endpoint. Do not specify the protocol, host, and IP port in the address. For example, to deploy the endpoint to the /C usto mer servlet context in the default servlet container: address="/Customer"
Address syntax for custom servlet container—to instantiate a custom Jetty container for this endpoint, specify a complete HTTP URL, including the host and IP port (the value of the
CHAPT ER 39 . PAYLO AD- BASED RO UT E
IP port effectively identifies the target Jetty container). Typically, for a Jetty container, you specify the host as 0 . 0 . 0 . 0 , which is interpreted as a wildcard that matches every IP network interface on the local machine (that is, if deployed on a multi-homed host, Jetty opens a listening port on every network card). For example, to deploy the endpoint to the custom Jetty container listening on IP port, 80 83: address="http://0.0.0.0:8083/Customer"
No t e
If you want to configure a secure endpoint (secured by SSL), you would specify the https: scheme in the address.
Specifying t he WSDL locat ion
The wsd l UR L attribute of the cxf: cxfEnd po i nt element is used to specify the location of the WSDL contract for this endpoint. The WSDL contract is used exclusively as the source of metadata for this endpoint: there is need to specify an SEI in PAYLOAD mode.
39.3. SORT MESSAGES BY OPERAT ION NAME
T he operat ionName header
When the WS endpoint parses an incoming operation invocation in PAYLOAD mode, it automatically sets the o perati o nName header to the name of the invoked operation. You can then use this header to sort messages by operation name.
Sort ing by operat ion name
For example, the custo mer-ws-camel -cxf-payl o ad demonstration defines the following route, which uses the content-based router pattern to sort incoming messages, based on the operation name. The when predicates check the value of the o perati o nName header using simple language expressions, sorting messages into invocations on the upd ateC usto mer operation, the l o o kupC usto mer operation, or the g etC usto merStatus operation.
<beans ...>
...
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="cxf:bean:customer-ws?dataFormat=PAYLOAD"/>
<choice>
<when>
<simple>${in.header.operationName} ==
'updateCustomer'</simple>
...
</when>
<when>
<simple>${in.header.operationName} ==
4 93
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
'lookupCustomer'</simple>
...
</when>
<when>
<simple>${in.header.operationName} ==
'getCustomerStatus'</simple>
...
</when>
</choice>
</route>
</camelContext>
</beans>
39.4 . SOAP/HT T P-T O-JMS BRIDGE USE CASE
Overview
In this section, we consider a SOAP/HTTP-to-JMS bridge use case: that is, you want to create a route that transforms a synchronous operation invocation (over SOAP/HTTP) into an asynchronous message delivery (by pushing the message onto a JMS queue). In this way, it becomes possible to process the incoming operation invocations at a later time, by pulling messages off the JMS queue.
Of course, an alternative solution would be to modify the WSDL contract directly to declare the operation as OneWay, thus making the operation asynchronous. Unfortunately, it is often impractical to modify existing WSDL contracts in the real world, because this can have an impact on third-party applications.
Figure 39.2, “ SOAP/HTTP-to-JMS Bridge” shows the general outline of a bridge that can
transform synchronous SOAP/HTTP invocations into asynchronous JMS message deliveries.
Fig u re 39 .2. SO AP/HT T P- t o - JMS Brid g e
4 94
T ransforming RPC operat ions t o One Way
CHAPT ER 39 . PAYLO AD- BASED RO UT E
As shown in Figure 39.2, “ SOAP/HTTP-to-JMS Bridge”
, the route for transforming synchronous SOAP/HTTP to asynchronous JMS works as follows:
1. The WS client invokes a synchronous operation on the Camel CXF endpoint at the start of the route. The Camel CXF endpoint then creates an initial InOut exchange at the start of the route, where the body of the exchange message contains a payload in
XML format.
2. The i nO nl y DSL command pushes a copy of the XML payload onto a JMS queue, so that it can be processed offline at some later time.
3. The transfo rm DSL command constructs an immediate response to send back to the client, where the response has the form of an XML string.
4. The Camel CXF component supports implicit type conversion of the XML string to payload format.
5. The response is sent back to the WS client, thus completing the synchronous operation invocation.
Evidently, this transformation can only work, if the original operation invocation has no return value. Otherwise, it would be impossible to generate a response message before the request has been processed.
Creat ing a broker inst ance
You can use Apache ActiveMQ as the JMS implementation. A convenient approach to use in this demonstration is to embed the Apache ActiveMQ broker in the bridge bundle. Simply define an amq : bro ker element in the Spring XML file, as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:amq="http://activemq.apache.org/schema/core"
...>
<amq:broker brokerName="CxfPayloadDemo" persistent="false">
<amq:transportConnectors>
<amq:transportConnector name="openwire" uri="tcp://localhost:51616"/>
<amq:transportConnector name="vm" uri="vm:local"/>
</amq:transportConnectors>
</amq:broker>
...
</beans>
4 95
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
This broker instance is created with the persi stent attribute set to fal se, so that the messages are stored only in memory.
Configuring t he JMS component
Because the broker is co-located with the bridge route (in the same JVM), the most efficient way to connect to the broker is to use the VM (Virtual Machine) transport. Configure the
Apache ActiveMQ component as follows, to connect to the co-located broker using the VM protocol:
<beans ...>
...
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="vm:local"/>
</bean>
...
</beans>
No t e
By defining the bean with an i d value of acti vemq , you are implicitly overriding the component associated with the endpoint URI prefix, acti vemq : . In other words, your custom Acti veMQ C o mpo nent instance is used instead of the default
Acti veMQ C o mpo nent instance from the camel -acti vemq JAR file.
Sample SOAP/HT T P-t o-JMS rout e
For example, you could define a route that implements the SOAP/HTTP-to-JMS bridge specifically for the upd ateC usto mer operation from the C usto merServi ce SEI, as follows:
<when>
<simple>${in.header.operationName} == 'updateCustomer'</simple>
<log message="Placing update customer message onto queue."/>
<inOnly uri="activemq:queue:CustomerUpdates?jmsMessageType=Text"/>
<transform>
<constant>
<![CDATA[
<ns2:updateCustomerResponse xmlns:ns2="http://demo.fusesource.com/wsdl/CustomerService/"/>
]]>
</constant>
</transform>
</when>
4 96
Sending t o t he JMS endpoint in inOnly mode
CHAPT ER 39 . PAYLO AD- BASED RO UT E
Note how the message payload is sent to the JMS queue using the i nO nl y DSL command instead of the to DSL command. When you send a message using the to DSL command, the default behavior is to use the same invocation mode as the current exchange. But the current exchange has an InOut MEP, which means that the to DSL command would wait forever for a response message from JMS.
The invocation mode we want to use when sending the payload to the JMS queue is InOnly
(asynchronous), and we can force this mode by inserting the i nO nl y DSL command into the route.
No t e
By specifying the option, jmsMessag eT ype= T ext, Camel CXF implicitly converts the message payload to an XML string before pushing it onto the JMS queue.
Ret urning a lit eral response value
The transfo rm DSL command uses an expression to set the body of the exchange's Out message and this message is then used as the response to the client. Your first impulse when defining a response in XML format might be to use a DOM API, but in this example, the response is specified as a string literal. This approach has the advantage of being both efficient and very easy to program.
The final step of processing, which consists of converting the XML string to a DOM object, is performed by Apache Camel's implicit type conversion mechanism.
39.5. GENERAT ING RESPONSES USING T EMPLAT ES
Overview
One of the simplest and quickest approaches to generating a response message is to use a
velocity template. Figure 39.3, “ Response Generated by Velocity” shows the outline of a
general template-based route. At the start of the route is a Camel CXF endpoint in PAYLOAD mode, which is the appropriate mode to use for processing the message as an XML document. After doing the work required to process the message and stashing some intermediate results in message headers, the route generates the response message using a
Velocity template.
Fig u re 39 .3. Resp o n se G en erat ed b y Velo cit y
4 97
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Sample t emplat e-based rout e
For example, you could define a template-based route specifically for the
g etC usto emrStatus operation, as follows:
...
<when>
<simple>${in.header.operationName} ==
'getCustomerStatus'</simple>
<convertBodyTo type="org.w3c.dom.Node"/>
<setHeader headerName="customerId">
<xpath>/cus:getCustomerStatus/customerId/text()</xpath>
</setHeader>
<to uri="getCustomerStatus"/>
<to uri="velocity:getCustomerStatusResponse.vm"/>
</when>
</choice>
</route>
</camelContext
...
<bean id="getCustomerStatus" class="com.fusesource.customerwscamelcxfpayload.GetCustomerStatus"/>
Rout e processing st eps
Given the preceding route definition, any message whose operation name matches
g etC usto merStatus would be processed as follows:
1. To facilitate processing the payload body, the first step uses co nvertBo d yT o to convert the body type from o rg . apache. camel . co mpo nent. cxf. C xfP ayl o ad
(the default payload type) to o rg . w3c. d o m. No d e.
2. The route then applies an XPath expression to the message in order to extract the customer ID value and then stashes it in the custo merId header.
3. The next step sends the message to the g etC usto merStatus bean, which does whatever processing is required to get the customer status for the specified customer
ID. The results from this step are stashed in message headers.
4. Finally, a response is generated using a velocity template.
No t e
A common pattern when implementing Apache Camel routes is to use message headers as a temporary stash to hold intermediate results (you could also use exchange properties in the same way).
4 98
Convert ing XPat h result t o a st ring
CHAPT ER 39 . PAYLO AD- BASED RO UT E
Because the default return type of XPath is a node list, you must explicitly convert the result to a string, if you want to obtain the string contents of an element. There are two alternative ways of obtaining the string value of an element:
Specify the result type explicitly using the resul tT ype attribute, as follows:
<xpath
resul tT ype= "java. l ang . Stri ng ">/cus:getCustomerStatus/customerId</xp ath>
Modify the expression so that it returns a text() node, which automatically converts to string:
<xpath>/cus:getCustomerStatus/customerId/text()</xpath>
get Cust omerSt at us processor bean
The g etC usto merStatus processor bean is an instance of the G etC usto merStatus processor class, which is defined as follows:
// Java package com.fusesource.customerwscamelcxfpayload; import org.apache.camel.Exchange; import org.apache.camel.Processor; public class GetCustomerStatus implements Processor
{
public void process(Exchange exchng) throws Exception {
String id = exchng.getIn().getHeader("customerId",
String.class);
// Maybe do some kind of lookup here!
//
exchng.getIn().setHeader("status", "Away");
exchng.getIn().setHeader("statusMessage", "Going to sleep.");
}
}
The implementation shown here is just a placeholder. In a realistic application you would perform some sort of checks or database lookup to obtain the customer status. In the demonstration code, however, the status and statusMessag e are simply set to constant values and stashed in message headers.
In the preceding code, we make the modifications directly to the In message. When the exchange's Out message is nul l , the next processor in the route gets a copy of the current
In message instead
4 99
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
An exceptional case occurs when the message exchange pattern is inOnly, in which case the Out message value is always copied into the In message, even if it is nul l .
get Cust omerSt at usResponse.vm Velocit y t emplat e
You can generate a response message very simply using a Velocity template. The Velocity template consists of a message in plain text, where specific pieces of data can be inserted using expressions—for example, the expression ${head er. HeaderName} substitutes the value of a named header.
The Velocity template for generating the g etC usto merStatus reponse is located in the
custo mer-ws-camel -cxf-payl o ad /src/mai n/reso urces directory and it contains the following template script:
500
<ns2:getCustomerStatusResponse xmlns:ns2="http://demo.fusesource.com/wsdl/CustomerService/">
<status>${headers.status}</status>
<statusMessage>${headers.statusMessage}</statusMessage>
</ns2:getCustomerStatusResponse>
39.6. T YPECONVERT ER FOR CXFPAYLOAD
Overview
Apache Camel supports a type converter mechanism, which is used to perform implicit and explicit type conversions of message bodies and message headers. The payload demonstration requires a customer type converter that can convert Stri ng objects to
C XFP ayl o ad objects. This type converter automatically gets invoked at the end of the
Camel route, when the generated response message (which is a Stri ng type) gets converted into a C XFP ayl o ad object.
St ring t o CXFPayload t ype convert er
The Stri ng to C XFP ayl o ad type converter is implemented in the
Ad d i ti o nal C xfP ayl o ad C o nverters class, as follows:
// Java package com.fusesource.customerwscamelcxfpayload; import java.io.ByteArrayInputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory;
CHAPT ER 39 . PAYLO AD- BASED RO UT E
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.camel.Converter; import org.apache.camel.component.cxf.CxfPayload; import org.apache.cxf.binding.soap.SoapHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node;
@ Converter public class AdditionalCxfPayloadConverters {
...
@ Converter
public static CxfPayload<SoapHeader> to C xfP ayl o ad (String xml) {
// System.out.println("To CxfPayload " + xml);
List<Element> elements = new ArrayList<Element>();
try {
Document doc = b.newDocumentBuilder().parse(new
ByteArrayInputStream(xml.getBytes()));
elements.add(doc.getDocumentElement());
} catch (Exception ex) {
log.warn("Exception while converting String payload to
CxfPayload; resulting payload will be empty.");
}
// The CxfPayload is changed to use Source object under layer, the elements API only work if we already setup the list before creating the CxfPayload
CxfPayload<SoapHeader> ret = new CxfPayload<SoapHeader>(null, elements);
return ret;
}
...
}
Reference
For full details of the type converter mechanism in Apache Camel, see Section 43.3, “ Built-In
Type Converters” and Chapter 45, Type Converters .
39.7. DEPLOY T O OSGI
Overview
One of the options for deploying the payload-based route is to package it as an OSGi bundle and deploy it into an OSGi container such as Red Hat JBoss Fuse. Some of the advantages of an OSGi deployment include:
Bundles are a relatively lightweight deployment option (because dependencies can be shared between deployed bundles).
501
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
OSGi provides sophisticated dependency management, ensuring that only versionconsistent dependencies are added to the bundle's classpath.
Using t he Maven bundle plug-in
The Maven bundle plug-in is used to package your project as an OSGi bundle, in preparation for deployment into the OSGi container. There are two essential modifications to make to your project's po m. xml file:
1. Change the packaging type to bund l e (by editing the value of the
pro ject/packag i ng element in the POM).
2. Add the Maven bundle plug-in to your POM file and configure it as appropriate.
Configuring the Maven bundle plug-in is quite a technical task (although the default settings are often adequate). For full details of how to customize the plug-in configuration, consult
Deploying into the OSGi Container and Managing OSGi Dependencies.
Sample bundle plug-in configurat ion
The following POM fragment shows a sample configuration of the Maven bundle plug-in, which is appropriate for the current example.
<?xml version="1.0"?>
<project ...>
...
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>customer-ws-camel-cxf-payload</artifactId>
<name>customer-ws-camel-cxf-payload</name>
<packag i ng >bund l e</packag i ng >
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
org.apache.camel.component.velocity,
javax.jws,
javax.wsdl,
javax.xml.bind,
javax.xml.bind.annotation,
javax.xml.namespace,
javax.xml.ws,
org.w3c.dom,
*
</Import-Package>
502
<DynamicImport-Package>
org.apache.cxf.*,
org.springframework.beans.*
</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
CHAPT ER 39 . PAYLO AD- BASED RO UT E
Dynamic import s
The Java packages from Apache CXF and the Spring API are imported using dynamic imports (specified using the D ynami cImpo rt-P ackag e element). This is a pragmatic way of dealing with the fact that Spring XML files are not terribly well integrated with the Maven bundle plug-in. At build time, the Maven bundle plug-in is not able to figure out which Java classes are required by the Spring XML code. By listing wildcarded package names in the
D ynami cImpo rt-P ackag e element, however, you allow the OSGi container to figure out which Java classes are needed by the Spring XML code at run time.
No t e
In general, using D ynami cImpo rt-P ackag e headers is not recommended in
OSGi, because it short-circuits OSGi version checking. Normally, what should happen is that the Maven bundle plug-in lists the Java packages used at build time, along with their versions, in the Impo rt-P ackag e header. At deploy time, the OSGi container then checks that the available Java packages are compatible with the build time versions listed in the Impo rt-P ackag e header. With dynamic imports, this version checking cannot be performed.
Build and deploy t he client bundle
After you have configured the POM file, you can build the Maven project and install it in your local repository by entering the following command: mvn install
Install the camel -vel o ci ty feature, which is needed for this example: karaf@ root> features:install camel-velocity
To deploy the route bundle, enter the following command at the console: karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-camel-cxf-payload
503
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
No t e
If your local Maven repository is stored in a non-standard location, you might need to customize the value of the o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property in the InstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file, before you can use the mvn: scheme to access Maven artifacts.
504
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
CHAPTER 40. PROVIDER-BASED ROUTE
4 0.1. PROVIDER-BASED JAX-WS ENDPOINT
Overview
Use the provider-based approach, if you need to process very large Web services messages.
The provider-based approach is a variant of the PAYLOAD data format that enables you to encode the message body as an XML streaming type, such as SAXSo urce. Since the
XMLstreaming types are more efficient than DOM objects, the provider-based approach is ideal for large XML messages.
Demonst rat ion locat ion
The code presented in this chapter is taken from the following demonstration: cxf-webinars-jboss-fuse-6.2.1/custo mer-ws-camel -cxf-pro vi d er
For details of how to download and install the demonstration code, see Chapter 34,
Demonstration Code for Camel/CXF
Camel CXF component
The Camel CXF component is an Apache CXF component that integrates Web services with routes. You can use it either to instantiate consumer endpoints (at the start of a route), which behave like Web service instances, or to instantiate producer endpoints (at any other points in the route), which behave like WS clients.
No t e
Came CXF endpoints—which are instantiated using the cxf: cxfEnd po i nt XML element and are implemented by the Apache Camel project—are not to be confused with the Apache CXF JAX-WS endpoints—which are instantiated using the
jaxws: end po i nt XML element and are implemented by the Apache CXF project.
Provider-based approach and t he PAYLOAD dat a format
The provider-based approach is a variant of the PAYLOAD data format, which is enabled as follows:
Define a custom javax. xml . ws. P ro vi d er<StreamType> class, where the
StreamType type is an XML streaming type, such as SAXSo urce.
The PAYLOAD data format is selected by an annotation on the custom P ro vi d er<?> class (see
the section called “ The SAXSourceService provider class” ).
505
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The custom P ro vi d er<?> class is referenced by setting the servi ceC l ass attribute of the cxf: cxfEnd po i nt element in XML configuration.
The provider-based approach has the following characteristics:
Enables you to access the message body as a streamed XML type—for example,
javax. xml . transfo rm. sax. SAXSo urce.
No JAX-WS or JAXB stub code required.
The SOAP body is marshalled into a stream-based SAXSo urce type.
The SOAP headers are converted into headers in the exchange's In message, of
o rg . apache. cxf. bi nd i ng . so ap. So apHead er type.
Implement ing and building a provider-based rout e
To implement and build the demonstration provider-based route, starting from scratch, you would perform the following steps:
1. Define a custom javax. xml . ws. P ro vi d er<StreamType> class (the current demonstration uses SAXSo urce as the StreamType type).
2. Instantiate the Camel CXF endpoint in Spring, using the cxf: cxfEnd po i nt element and reference the custom provider class (using the servi ceC l ass attribute).
3. Implement the route in XML, where you can use the content-based router to sort requests by operation name.
4. For each operation, define a processor bean to process the request.
5. Define velocity templates for generating the reponse messages.
6. Define a custom type converter, to support converting a Stri ng message body to a
SAXSo urce message body.
Sample provider-based rout e
Figure 40.1, “ Sample Provider-Based Route” shows an outline of the route that is used to
process the operations of the C usto merServi ce Web service using the provider-based approach. After sorting the request messages by operation name, an operation-specific processor bean reads the incoming request parameters. Finally, the response messages are generated using Velocity templates.
Fig u re 4 0.1. Samp le Pro vid er- Based Ro u t e
506
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
4 0.2. CREAT E A PROVIDER<?> IMPLEMENT AT ION CLASS
Overview
The fundamental prerequisite for using provider mode is to define a custom P ro vi d er<> class that implements the i nvo ke() method. In fact, the sole purpose of this class is to provide runtime type information for Apache CXF: the invoke() method never gets called!
By implementing the provider class in the way shown here, you are merely indicating to the
Apache CXF runtime that the WS endpoint should operate in in PAYLOAD mode and the type of the message PAYLOAD should be SAXSo urce.
T he SAXSourceService provider class
The definition of the provider class is relatively short and the complete definition of the customer provider class, SAXSo urceServi ce, is as follows:
// Java package com.fusesource.customerwscamelcxfprovider; import javax.xml.transform.sax.SAXSource; import javax.xml.ws.Provider; import javax.xml.ws.Service.Mode; import javax.xml.ws.ServiceMode; import javax.xml.ws.WebServiceProvider;
@ WebServiceProvider()
@ ServiceMode(Mode.PAYLOAD) public class SAXSourceService implements Provider<SAXSource>
{
public SAXSource invoke(SAXSource t) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
507
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The customer provider class, SAXSo urceServi ce, must be annotated by the
@ WebServi ceP ro vi d er annotation to mark it as a provider class and can be optionally annotated by the @ Servi ceMo d e annotation to select PAYLOAD mode.
4 0.3. INST ANT IAT E T HE WS ENDPOINT
Overview
In Apache Camel, the CXF component is the key to integrating routes with Web services. You can use the CXF component to create two different kinds of endpoint:
Consumer endpoint—(at the start of a route) represents a Web service instance, which integrates with the route. The type of payload injected into the route depends on the value of the endpoint's d ataFo rmat option.
Producer endpoint—represents a special kind of WS client proxy, which converts the current exchange object into an operation invocation on a remote Web service. The format of the current exchange must match the endpoint's d ataFo rmat setting.
T he cxf:bean: URI synt ax
The cxf: bean: URI is used to bind an Apache CXF endpoint to a route and has the following general syntax: cxf:bean:CxfEndpointID[?Options]
Where CxfEndpointID is the ID of a bean created using the cxf: cxfEnd po i nt element, which configures the details of the WS endpoint. You can append options to this URI (where the options are described in detail in
CXF
in the Apache Camel Component Reference Guide ).
Provider mode is essentially a variant of PAYLOAD mode: you could specify this mode on the URI (by setting d ataFo rmat= P AY LO AD ), but this is not necessary, because PAYLOAD mode is already selected by the @ Servi ceMo d e annotation on the custom P ro vi d er class.
For example, to start a route with an endpoint in provider mode, where the endpoint is configured by the custo mer-ws bean, define the route as follows:
<route>
<from uri="cxf:bean:customer-ws"/>
...
</route>
T he cxf:cxfEndpoint element
508
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
The cxf: cxfEnd po i nt element is used to define a WS endpoint that binds either to the start (consumer endpoint) or the end (producer endpoint) of a route. For example, to define the custo mer-ws WS endpoint in provider mode, you define a cxf: cxfEnd po i nt element as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint id="customer-ws"
ad d ress= "/C usto mer"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsd l UR L= "wsd l /C usto merServi ce. wsd l "
servi ceC l ass= "co m. fuseso urce. custo merwscamel cxfpro vi d er. SAXSo urceSer vi ce"
xmlns:c="http://demo.fusesource.com/wsdl/CustomerService/"/>
...
</beans>
Specifying t he WSDL locat ion
The wsd l UR L attribute of the cxf: cxfEnd po i nt element is used to specify the location of the WSDL contract for this endpoint. The WSDL contract is used as the source of metadata for this endpoint.
Specifying t he service class
A key difference between provider mode and ordinary PAYLOAD mode is that the
servi ceC l ass attribute must be set to the provider class, SAXSo urceServi ce.
4 0.4 . SORT MESSAGES BY OPERAT ION NAME
T he operat ionName header
When the WS endpoint parses an incoming operation invocation in PROVIDER mode, it automatically sets the o perati o nName header to the name of the invoked operation. You can then use this header to sort messages by operation name.
Sort ing by operat ion name
For example, the custo mer-ws-camel -cxf-pro vi d er demonstration defines the following route, which uses the content-based router pattern to sort incoming messages, based on the operation name. The when predicates check the value of the o perati o nName header using simple language expressions, sorting messages into invocations on the
upd ateC usto mer operation, the l o o kupC usto mer operation, or the
g etC usto merStatus operation.
509
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<beans ...>
...
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="cxf:bean:customer-ws"/>
<choice>
<when>
<simple>${in.header.operationName} ==
'updateCustomer'</simple>
...
</when>
<when>
<simple>${in.header.operationName} ==
'lookupCustomer'</simple>
...
</when>
<when>
<simple>${in.header.operationName} ==
'getCustomerStatus'</simple>
...
</when>
</choice>
</route>
</camelContext>
...
</beans>
4 0.5. SOAP/HT T P-T O-JMS BRIDGE USE CASE
Overview
In this section, we consider a SOAP/HTTP-to-JMS bridge use case: that is, you want to create a route that transforms a synchronous operation invocation (over SOAP/HTTP) into an asynchronous message delivery (by pushing the message onto a JMS queue). In this way, it becomes possible to process the incoming operation invocations at a later time, by pulling messages off the JMS queue.
Figure 40.2, “ SOAP/HTTP-to-JMS Bridge”
shows the general outline of a bridge that can transform synchronous SOAP/HTTP invocations into asynchronous JMS message deliveries.
Fig u re 4 0.2. SO AP/HT T P- t o - JMS Brid g e
510
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
T ransforming RPC operat ions t o One Way
As shown in Figure 40.2, “ SOAP/HTTP-to-JMS Bridge” , the route for transforming
synchronous SOAP/HTTP to asynchronous JMS works as follows:
1. The WS client invokes a synchronous operation on the Camel CXF endpoint at the start of the route. The Camel CXF endpoint then creates an initial InOut exchange at the start of the route, where the body of the exchange message contains a payload in
XML format.
2. The i nO nl y DSL command pushes a copy of the XML payload onto a JMS queue, so that it can be processed offline at some later time.
3. The transfo rm DSL command constructs an immediate response to send back to the client, where the response has the form of an XML string.
4. The route explicitly converts the XML string to the
javax. xml . transfo rm. sax. SAXSo urce type.
5. The response is sent back to the WS client, thus completing the synchronous operation invocation.
Evidently, this transformation can only work, if the original operation invocation has no return value. Otherwise, it would be impossible to generate a response message before the request has been processed.
Creat ing a broker inst ance
You can use Apache ActiveMQ as the JMS implementation. A convenient approach to use in this demonstration is to embed the Apache ActiveMQ broker in the bridge bundle. Simply define an amq : bro ker element in the Spring XML file, as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
...
511
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
xmlns:amq="http://activemq.apache.org/schema/core"
...>
<amq:broker brokerName="CxfPayloadDemo" persistent="false">
<amq:transportConnectors>
<amq:transportConnector name="openwire" uri="tcp://localhost:51616"/>
<amq:transportConnector name="vm" uri="vm:local"/>
</amq:transportConnectors>
</amq:broker>
...
</beans>
No t e
This broker instance is created with the persi stent attribute set to fal se, so that the messages are stored only in memory.
Configuring t he JMS component
Because the broker is co-located with the bridge route (in the same JVM), the most efficient way to connect to the broker is to use the VM (Virtual Machine) transport. Configure the
Apache ActiveMQ component as follows, to connect to the co-located broker using the VM protocol:
512
<beans ...>
...
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="vm:local"/>
</bean>
...
</beans>
No t e
By defining the bean with an i d value of acti vemq , you are implicitly overriding the component associated with the endpoint URI prefix, acti vemq : . In other words, your custom Acti veMQ C o mpo nent instance is used instead of the default
Acti veMQ C o mpo nent instance from the camel -acti vemq JAR file.
Sample SOAP/HT T P-t o-JMS rout e
For example, you could define a route that implements the SOAP/HTTP-to-JMS bridge specifically for the upd ateC usto mer operation from the C usto merServi ce SEI, as follows:
<when>
<simple>${in.header.operationName} == 'updateCustomer'</simple>
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
<log message="Placing update customer message onto queue."/>
<inOnly uri="activemq:queue:CustomerUpdates?jmsMessageType=Text"/>
<transform>
<constant>
<![CDATA[
<ns2:updateCustomerResponse xmlns:ns2="http://demo.fusesource.com/wsdl/CustomerService/"/>
]]>
</constant>
</transform>
<convertBodyTo type="javax.xml.transform.sax.SAXSource"/>
</when>
Sending t o t he JMS endpoint in inOnly mode
Note how the message payload is sent to the JMS queue using the i nO nl y DSL command instead of the to DSL command. When you send a message using the to DSL command, the default behavior is to use the same invocation mode as the current exchange. But the current exchange has an InOut MEP, which means that the to DSL command would wait forever for a response message from JMS.
The invocation mode we want to use when sending the payload to the JMS queue is InOnly
(asynchronous), and we can force this mode by inserting the i nO nl y DSL command into the route.
No t e
By specifying the option, jmsMessag eT ype= T ext, Camel CXF implicitly converts the message payload to an XML string before pushing it onto the JMS queue.
Ret urning a lit eral response value
The transfo rm DSL command uses an expression to set the body of the exchange's Out message and this message is then used as the response to the client. Your first impulse when defining a response in XML format might be to use a DOM API, but in this example, the response is specified as a string literal. This approach has the advantage of being both efficient and very easy to program.
T ype conversion of t he response message
In this example, the reply message (like the request message) is required to be of type,
javax. xml . transfo rm. sax. SAXSo urce. In the last step of the route, therefore, you must convert the message body from Stri ng type to javax. xml . transfo rm. sax. SAXSo urce type, by invoking the co nvertBo d yT o DSL command.
The implementation of the String to SAXSo urce conversion is provided by a custom type converter, as described in
Section 40.7, “ TypeConverter for SAXSource”
.
4 0.6. GENERAT ING RESPONSES USING T EMPLAT ES
513
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Overview
One of the simples and quickest approaches to generating a response message is to use a
velocity template. Figure 40.3, “ Response Generated by Velocity”
shows the outline of a general template-based route. At the start of the route is a Camel CXF endpoint in provider mode, which is the appropriate mode to use for processing the message as an XML document. After doing the work required to process the message and stashing some intermediate results in message headers, the route generates the response message using a
Velocity template.
Fig u re 4 0.3. Resp o n se G en erat ed b y Velo cit y
514
Sample t emplat e-based rout e
For example, you could define a template-based route specifically for the
g etC usto emrStatus operation, as follows:
...
<when>
<simple>${in.header.operationName} ==
'getCustomerStatus'</simple>
<setHeader headerName="customerId">
<xpath resultType="java.lang.String">/cus:getCustomerStatus/customerId</xpath
>
</setHeader>
<to uri="getCustomerStatus"/>
<to uri="velocity:getCustomerStatusResponse.vm"/>
<convertBodyTo type="javax.xml.transform.sax.SAXSource"/>
</when>
</choice>
</route>
</camelContext
...
<bean id="getCustomerStatus" class="com.fusesource.customerwscamelcxfpayload.GetCustomerStatus"/>
Rout e processing st eps
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
Given the preceding route definition, any message whose operation name matches
g etC usto merStatus would be processed as follows:
1. The route applies an XPath expression to the message in order to extract the customer ID value and then stashes it in the custo merId header.
2. The next step sends the message to the g etC usto merStatus bean, which does whatever processing is required to get the customer status for the specified customer
ID. The results from this step are stashed in message headers.
3. A response is generated using a Velocity template.
4. Finally, the XML string generated by the Velocity template must be explicitly converted to the javax. xml . transfo rm. sax. SAXSo urce type using co nvertBo d yT o
(which implicitly relies on a type converter).
No t e
A common pattern when implementing Apache Camel routes is to use message headers as a temporary stash to hold intermediate results (you could also use exchange properties in the same way).
XPat h expressions and SAXSource
XPath expressions can be applied directly to SAXSource objects. The XPath implementation has a pluggable architecture that supports a variety of XML parsers and when XPath encounters a SAXSource object, it automatically loads the plug-in required to support
SAXSource parsing.
get Cust omerSt at us processor bean
The g etC usto merStatus processor bean is an instance of the G etC usto merStatus processor class, which is defined as follows:
// Java package com.fusesource.customerwscamelcxfpayload; import org.apache.camel.Exchange; import org.apache.camel.Processor; public class GetCustomerStatus implements Processor
{
public void process(Exchange exchng) throws Exception {
String id = exchng.getIn().getHeader("customerId",
String.class);
// Maybe do some kind of lookup here!
//
515
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
exchng.getIn().setHeader("status", "Away");
exchng.getIn().setHeader("statusMessage", "Going to sleep.");
}
}
The implementation shown here is just a placeholder. In a realistic application you would perform some sort of checks or database lookup to obtain the customer status. In the demonstration code, however, the status and statusMessag e are simply set to constant values and stashed in message headers.
get Cust omerSt at usResponse.vm Velocit y t emplat e
You can generate a response message very simply using a Velocity template. The Velocity template consists of a message in plain text, where specific pieces of data can be inserted using expressions—for example, the expression ${head er. HeaderName} substitutes the value of a named header.
The Velocity template for generating the g etC usto merStatus reponse is located in the
custo mer-ws-camel -cxf-pro vi d er/src/mai n/reso urces directory and it contains the following template script:
<ns2:getCustomerStatusResponse xmlns:ns2="http://demo.fusesource.com/wsdl/CustomerService/">
<status>${headers.status}</status>
<statusMessage>${headers.statusMessage}</statusMessage>
</ns2:getCustomerStatusResponse>
4 0.7. T YPECONVERT ER FOR SAXSOURCE
Overview
Apache Camel supports a type converter mechanism, which is used to perform implicit and explicit type conversions of message bodies and message headers. The type converter mechanism is extensible and it so happens that the provider demonstration requires a custom type converter that can convert Stri ng objects to SAXSo urce objects.
St ring t o SAXSource t ype convert er
The Stri ng to SAXSo urce type converter is implemented in the Ad d i ti o nal C o nverters class, as follows:
// Java package com.fusesource.customerwscamelcxfprovider; import java.io.ByteArrayInputStream; import javax.xml.transform.sax.SAXSource; import org.apache.camel.Converter; import org.xml.sax.InputSource;
516
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
@ Converter public class AdditionalConverters {
@ Converter
public static SAXSource toSAXSource(String xml) {
return new SAXSource(new InputSource(new
ByteArrayInputStream(xml.getBytes())));
}
}
Reference
For full details of the type converter mechanism in Apache Camel, see Section 43.3, “ Built-In
Type Converters” and Chapter 45, Type Converters .
4 0.8. DEPLOY T O OSGI
Overview
One of the options for deploying the provider-based route is to package it as an OSGi bundle and deploy it into an OSGi container such as Red Hat JBoss Fuse. Some of the advantages of an OSGi deployment include:
Bundles are a relatively lightweight deployment option (because dependencies can be shared between deployed bundles).
OSGi provides sophisticated dependency management, ensuring that only versionconsistent dependencies are added to the bundle's classpath.
Using t he Maven bundle plug-in
The Maven bundle plug-in is used to package your project as an OSGi bundle, in preparation for deployment into the OSGi container. There are two essential modifications to make to your project's po m. xml file:
1. Change the packaging type to bund l e (by editing the value of the
pro ject/packag i ng element in the POM).
2. Add the Maven bundle plug-in to your POM file and configure it as appropriate.
Configuring the Maven bundle plug-in is quite a technical task (although the default settings are often adequate). For full details of how to customize the plug-in configuration, consult
Deploying into the OSGi Container and Managing OSGi Dependencies.
Sample bundle plug-in configurat ion
517
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The following POM fragment shows a sample configuration of the Maven bundle plug-in, which is appropriate for the current example.
<?xml version="1.0"?>
<project ...>
...
<groupId>com.fusesource.byexample.cxf-webinars</groupId>
<artifactId>customer-ws-camel-cxf-provider</artifactId>
<name>customer-ws-camel-cxf-provider</name>
<packag i ng >bund l e</packag i ng >
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
org.apache.camel.component.velocity,
javax.jws,
javax.wsdl,
javax.xml.bind,
javax.xml.bind.annotation,
javax.xml.namespace,
javax.xml.ws,
org.w3c.dom,
*
</Import-Package>
<DynamicImport-Package>
org.apache.cxf.*,
org.springframework.beans.*
</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
...
</plugins>
</build>
</project>
Dynamic import s
The Java packages from Apache CXF and the Spring API are imported using dynamic imports (specified using the D ynami cImpo rt-P ackag e element). This is a pragmatic way of dealing with the fact that Spring XML files are not terribly well integrated with the Maven bundle plug-in. At build time, the Maven bundle plug-in is not able to figure out which Java classes are required by the Spring XML code. By listing wildcarded package names in the
D ynami cImpo rt-P ackag e element, however, you allow the OSGi container to figure out which Java classes are needed by the Spring XML code at run time.
518
CHAPT ER 4 0 . PRO VIDER- BASED RO UT E
No t e
In general, using D ynami cImpo rt-P ackag e headers is not recommended in
OSGi, because it short-circuits OSGi version checking. Normally, what should happen is that the Maven bundle plug-in lists the Java packages used at build time, along with their versions, in the Impo rt-P ackag e header. At deploy time, the OSGi container then checks that the available Java packages are compatible with the build time versions listed in the Impo rt-P ackag e header. With dynamic imports, this version checking cannot be performed.
Build and deploy t he client bundle
After you have configured the POM file, you can build the Maven project and install it in your local repository by entering the following command: mvn install
To deploy the route bundle, enter the following command at the container console: karaf@ root> install -s mvn:com.fusesource.byexample.cxfwebinars/customer-ws-camel-cxf-provider
No t e
If your local Maven repository is stored in a non-standard location, you might need to customize the value of the o rg . o ps4 j. pax. url . mvn. l o cal R epo si to ry property in the EsbInstallDir/etc/o rg . o ps4 j. pax. url . mvn. cfg file, before you can use the mvn: scheme to access Maven artifacts.
519
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 41. PROXYING A WEB SERVICE
Ab st ract
A common use case for the Camel CXF component is to use a route as a proxy for a Web service. That is, in order to perform additional processing of WS request and response messages, you interpose a route between the WS client and the original Web service.
4 1.1. PROXYING WIT H HT T P
Overview
The simplest way to proxy a SOAP/HTTP Web service is to treat the request and reply messages as HTTP packets. This type of proxying can be used where there is no requirement to read or modify the messages passing through the route. For example, you could use this kind of proxying to apply various patterns of flow control on the WS messges.
Figure 41.1, “ Proxy Route with Message in HTTP Format”
shows an overview of how to proxy a Web service using an Apache Camel route, where the route treats the messages as HTTP packets. The key feature of this route is that both the consumer endpoint (at the start of the route) and the producer endpoint (at the end of the route) must be compatible with the HTTP packet format.
Fig u re 4 1.1. Pro xy Ro u t e wit h Messag e in HT T P Fo rmat
520
Alt ernat ives for t he consumer endpoint
The following Apache Camel endpoints can be used as consumer endpoints for HTTP format messages:
Jetty endpoint—is a lightweight Web server. You can use Jetty to handle messages for any
HTTP-based protocol, including the commonly-used Web service SOAP/HTTP protocol.
Camel CXF endpoint in MESSAGE mode—when a Camel CXF endpoint is used in MESSAGE mode, the body of the exchange message is the raw message received from the transport layer (which is HTTP). In other words, the Camel CXF endpoint in MESSAGE mode is equivalent to a Jetty endpoint in the case of HTTP-based protocols.
Consumer endpoint for HT T P
A Jetty endpoint has the general form, jetty: HttpAddress. To configure the Jetty
CHAPT ER 4 1 . PRO XYING A WEB SERVICE
endpoint to be a proxy for a Web service, use a HttpAddress value that is almost identical to the HTTP address the client connects to, except that Jetty's version of HttpAddress uses the special hostname, 0 . 0 . 0 . 0 (which matches all of the network interfaces on the current machine).
<route>
<from uri="jetty: http: //0 . 0 . 0 . 0 : 9 0 9 3/C usto mers?
matchO nUri P refi x= true"/>
...
</route>
mat chOnUriPrefix opt ion
Normally, a Jetty consumer endpoint accepts only an exact match on the context path. For example, a request that is sent to the address http: //l o cal ho st: 9 0 9 3/C usto mers would be accepted, but a request sent to http: //l o cal ho st: 9 0 9 3/C usto mers/Fo o would be rejected. By setting matchO nUri P refi x to true, however, you enable a kind of wildcarding on the context path, so that any context path prefixed by /C usto mers is accepted.
Alt ernat ives for t he producer endpoint
The following Apache Camel endpoints can be used as producer endpoints for HTTP format messages:
Jetty HTTP client endpoint—(recommended) the Jetty library implements a HTTP client. In particular, the Jetty HTTP client features support for HttpC l i ent thread pools, which means that the Jetty implementation scales particularly well.
HTTP endpoint—the HTTP endpoint implements a HTTP client based on the HttpC l i ent
3.x API.
HTTP4 endpoint—the HTTP endpoint implements a HTTP client based on the HttpC l i ent
4.x API.
Producer endpoint for HT T P
To configure a Jetty HTTP endpoint to send HTTP requests to a remote SOAP/HTTP Web service, set the uri attribute of the to element at the end of the route to be the address of the remote Web service, as follows:
<route>
...
<to uri="jetty: http: //l o cal ho st: 80 83/C usto mers?
bri d g eEnd po i nt= true& amp;thro wExcepti o nO nFai l ure= fal se"/>
</route>
bridgeEndpoint opt ion
521
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The HTTP component supports a bri d g eEnd po i nt option, which you can enable on a
HTTP producer endpoint to configure the endpoint appropriately for operating in a HTTP-to-
HTTP bridge (as is the case in this demonstration). In particular, when
bri d g eEnd po i nt= true, the HTTP endpoint ignores the value of the
Exchang e. HT T P _UR I header, using the HTTP address from the endpoint URI instead.
t hrowExcept ionOnFailure opt ion
Setting thro wExcepti o nO nFai l ure to fal se ensures that any HTTP exceptions are relayed back to the original WS client, instead of being thrown within the route.
Handling message headers
When defining a HTTP bridge application, the C amel Http* headers set by the consumer endpoint at the start of the route can affect the behavior of the producer endpoint. For this reason, in a bridge application it is advisable to remove the C amel Http* headers before the message reaches the producer endpoint, as follows:
<route>
<from uri="jetty:http:..."/>
...
<remo veHead ers pattern= "C amel Http*"/>
<to uri="jetty:http:..."/>
</route>
Out going HT T P headers
By default, any headers in the exchange that are not prefixed by C amel will be converted into HTTP headers and sent out over the wire by the HTTP producer endpoint. This could have adverse consequences on the behavior of your application, so it is important to be aware
of any headers that are set in the exchange object and to remove them, if necessary.
For more details about dealing with headers, see Section 41.4, “ Handling HTTP Headers”
.
4 1.2. PROXYING WIT H POJO FORMAT
Overview
If you want to access the content of the Web services messages that pass throught the route, you might prefer to process the messages in POJO format: that is, where the body of the exchange consists of a list of Java objects representing the WS operation parameters. The key advantate of using POJO format is that you can easily process the contents of a message, by accessing the operation parameters as Java objects.
522
Figure 41.2, “ Proxy Route with Message in POJO Format”
shows an overview of how to proxy a Web service using an Apache Camel route, where the route processes the messages in POJO format. The key feature of this route is that both the consumer endpoint (at the start of the route) and the producer endpoint (at the end of the route) must be compatible with the
POJO data format.
CHAPT ER 4 1 . PRO XYING A WEB SERVICE
Fig u re 4 1.2. Pro xy Ro u t e wit h Messag e in PO JO Fo rmat
Consumer endpoint for CXF/POJO
To parse incoming messages into POJO data format, the consumer endpoint at the start of the route must be a Camel CXF endpoint that is configured to use POJO mode. Use the
cxf: bean: BeanID URI format to reference the Camel CXF endpoint as follows (where the
d ataFo rmat option defaults to POJO):
<route>
<from uri="cxf: bean: custo merServi ceP ro xy"/>
...
</route>
The bean with the ID, custo merServi ceP ro xy, is a Camel CXF/POJO endpoint, which is defined as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint
id="custo merServi ceP ro xy"
xmlns:c="http://demo.fusesource.org/wsdl/camelcxf"
address="/C usto mers"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsdlURL="wsdl/CustomerService.wsdl" serviceClass="o rg . fuseso urce. d emo . wsd l . camel cxf. C usto merServi ce"
/>
...
</beans>
Producer endpoint for CXF/POJO
To convert the exchange body from POJO data format to a SOAP/HTTP message, the producer endpoint at the end of the route must be a Camel CXF endpoint configured to use
POJO mode. Use the cxf: bean: BeanID URI format to reference the Camel CXF endpoint as follows (where the d ataFo rmat option defaults to POJO):
523
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<route>
...
<to uri="cxf: bean: custo merServi ceR eal "/>
</route>
The bean with the ID, custo merServi ceR eal , is a Camel CXF/POJO endpoint, which is defined as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint
id="custo merServi ceR eal "
xmlns:c="http://demo.fusesource.org/wsdl/camelcxf"
address="http: //l o cal ho st: 80 83/C usto mers"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsdlURL="wsdl/CustomerService.wsdl" serviceClass="o rg . fuseso urce. d emo . wsd l . camel cxf. C usto merServi ce"
/>
...
</beans>
4 1.3. PROXYING WIT H PAYLOAD FORMAT
Overview
If you want to access the content of the Web services messages that pass throught the route, you might prefer to process the messages in the normal PAYLOAD format: that is, where the body of the exchange is accessible as an XML document (essentially, an
o rg . w3c. d o m. No d e object). The key advantate of using PAYLOAD format is that you can easily process the contents of a message, by accessing the message body as an XML document.
Figure 41.3, “ Proxy Route with Message in PAYLOAD Format” shows an overview of how to
proxy a Web service using an Apache Camel route, where the route processes the messages in PAYLOAD format. The key feature of this route is that both the consumer endpoint (at the start of the route) and the producer endpoint (at the end of the route) must be compatible with the PAYLOAD data format.
Fig u re 4 1.3. Pro xy Ro u t e wit h Messag e in PAYLO AD Fo rmat
524
Consumer endpoint for CXF/PAYLOAD
CHAPT ER 4 1 . PRO XYING A WEB SERVICE
To parse incoming messages into PAYLOAD data format, the consumer endpoint at the start of the route must be a Camel CXF endpoint that is configured to use PAYLOAD mode. Use the
cxf: bean: BeanID URI format to reference the Camel CXF endpoint as follows, where you
must set the d ataFo rmat option to PAYLOAD:
<route>
<from uri="cxf: bean: custo merServi ceP ro xy?d ataFo rmat= P AY LO AD "/>
...
</route>
The bean with the ID, custo merServi ceP ro xy, is a Camel CXF/PAYLOAD endpoint, which is defined as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint
id="custo merServi ceP ro xy"
xmlns:c="http://demo.fusesource.org/wsdl/camelcxf"
address="/C usto mers"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsdlURL="wsdl/CustomerService.wsdl"
/>
...
</beans>
Producer endpoint for CXF/PAYLOAD
To convert the exchange body from PAYLOAD data format to a SOAP/HTTP message, the producer endpoint at the end of the route must be a Camel CXF endpoint configured to use
PAYLOAD mode. Use the cxf: bean: BeanID URI format to reference the Camel CXF endpoint as follows, where you must set the d ataFo rmat option to PAYLOAD:
<route>
...
<to uri="cxf: bean: custo merServi ceR eal ?d ataFo rmat= P AY LO AD "/>
</route>
The bean with the ID, custo merServi ceR eal , is a Camel CXF/PAYLOAD endpoint, which is defined as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
<cxf:cxfEndpoint
id="custo merServi ceR eal "
525
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
xmlns:c="http://demo.fusesource.org/wsdl/camelcxf"
address="http: //l o cal ho st: 80 83/C usto mers"
endpointName="c:SOAPOverHTTP"
serviceName="c:CustomerService"
wsdlURL="wsdl/CustomerService.wsdl"
/>
...
</beans>
Out going HT T P headers
By default, any headers in the exchange that are not prefixed by C amel will be converted into HTTP headers and sent out over the wire by the Camel CXF producer endpoint. This could have adverse consequences on the behavior of your application, so it is important to be
aware of any headers that are set in the exchange object and to remove them, if necessary.
For more details about dealing with headers, see Section 41.4, “ Handling HTTP Headers”
.
4 1.4 . HANDLING HT T P HEADERS
Overview
When building bridge applications using HTTP or HTTP-based components, it is important to be aware of how the HTTP-based endpoints process headers. In many cases, internal headers (prefixed by Camel) or other headers can cause unwanted side-effects on your application. It is often necessary to remove or filter out certain headings or classes of headings in your route, in order to ensure that your application behaves as expected.
HT T P-based component s
The behavior described in this section affects not just the Camel HTTP component (camel -
http), but also a number of other HTTP-based components, including: camel-http camel-http4 camel-jetty camel-restlet camel-cxf camel-cxfrs
526
HT T P headers in Camel CXF
The Camel CXF component copies HTTP headers into message headers for all of the supported data formats:
P O JO
P AY LO AD
CHAPT ER 4 1 . PRO XYING A WEB SERVICE
MESSAG E
HT T P consumer endpoint
When a HTTP consumer endpoint receives an incoming message, it creates an In message with the following headers:
C amel Http* h ead ers
Several headers with the C amel Http prefix are created, which record the status of the incoming message. For details of these internal headers, see HTTP .
HT T P h ead ers
All of the HTTP headers from the original incoming message are mapped to headers on the exchange's In message.
URL o p t io n s (Jetty only)
The URL options from the original HTTP request URL are mapped to headers on the exchange's In message. For example, given the client request with the URL,
http: //myserver/myserver?o rd eri d = 123, a Jetty consumer endpoint creates the o rd eri d header with value 123.
HT T P producer endpoint
When a HTTP producer endpoint receives an exchange and converts it to the target message format, it handles the In message headers as follows:
C amel Http*
Headers prefixed by C amel Http are used to control the behaviour of the HTTP producer endpoint. Any headers of this kind are consumed by the HTTP producer endpoint and the endpoint behaves as directed.
No t e
However, C amel Http message headers are ignored by Camel CXF producer endpoints (but not by Camel CXF-RS producer endpoints).
C amel *
All other headers prefixed by C amel are presumed to be meant for internal use and are not mapped to HTTP headers in the target message (in other words, these headers are ignored).
*
527
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
All other headers are converted to HTTP headers in the target message, with the exception of the following headers, which are blocked (based on a case-insensitive match): content-length content-type cache-control connection date pragma trailer transfer-encoding upgrade via warning
Implicat ions for HT T P bridge applicat ions
When defining a HTTP bridge application (that is, a route starting with a HTTP consumer endpoint and ending with a HTTP producer endpoint), the C amel Http* headers set by the consumer endpoint at the start of the route can affect the behavior of the producer endpoint.
For this reason, in a bridge application it is advisable to remove the C amel Http* headers, as follows: from("http://0.0.0.0/context/path")
. remo veHead ers("C amel Http*)
...
.to("http://remoteHost/context/path");
Set t ing a cust om header filt er
If you want to customize the way that a HTTP producer endpoint processes headers, you can define your own customer header filter by defining the head erFi l terStrateg y option on the endpoint URI. For example, to configure a producer endpoint with the
myHead erFi l terStrateg y filter, you could use a URI like the following: http://remoteHost/context/path?
headerFilterStrategy=#myHeaderFilterStrategy
Where myHead erFi l terStrateg y is the bean ID of your custom filter instance.
528
CHAPT ER 4 2 . FILT ERING SO AP MESSAG E HEADERS
CHAPTER 42. FILTERING SOAP MESSAGE HEADERS
Ab st ract
The Camel CXF component supports a flexible header filtering mechanism, which enables you to process SOAP headers, applying different filters according to the header's XML namespace.
4 2.1. BASIC CONFIGURAT ION
Overview
When more than one CXF endpoint appears in a route, you need to decide whether or not to allow headers to propagate between the endpoints. By default, the headers are relayed back and forth between the endpoints, but in many cases it might be necessary to filter the headers or to block them altogether. You can control header propagation by applying filters to producer endpoints.
CxfHeaderFilt erSt rat egy
Header filtering is controlled by the C xfHead erFi l terStrateg y class. Basic configuration of the C xfHead erFi l terStrateg y class involves setting one or more of the following options:
the section called “ relayHeaders option”
.
the section called “ relayAllMessageHeaders option” .
relayHeaders opt ion
The semantics of the rel ayHead ers option can be summarized as follows:
O ut - of- band headers
Filter
rel ayHead ers= true
,
d ataFo rmat= P AY LO AD rel ayHead ers= true
,
d ataFo rmat= P O JO rel ayHead ers= fal se
In-band headers
In- band headers
Filter
Relay all
Block
Filter
Block
529
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
An in-band header is a header that is explicitly defined as part of the WSDL binding contract for an endpoint.
Out -of-band headers
An out-of-band header is a header that is serialized over the wire, but is not explicitly part of the WSDL binding contract. In particular, the SOAP binding permits out-of-band headers, because the SOAP specification does not require headers to be defined in the WSDL contract.
Payload format
The CXF endpoint's payload format affects the filter behavior as follows:
P O JO
(Default) Only out-of-band headers are available for filtering, because the in-band headers have already been processed and removed from the list by CXF. The inband headers are incorporated into the Messag eC o ntentLi st in POJO mode. If you require access to headers in POJO mode, you have the option of implementing a custom CXF interceptor or JAX-WS handler.
P AY LO AD
In this mode, both in-band and out-of-band headers are available for filtering.
MESSAG E
Not applicable. (In this mode, the message remains in a raw format and the headers are not processed at all.)
Default filt er
The default filter is of type, So apMessag eHead erFi l ter, which removes only the SOAP headers that the SOAP specification expects an intermediate Web service to consume. For more details, see
the section called “ SoapMessageHeaderFilter”
.
Overriding t he default filt er
You can override the default C xfHead erFi l terStrateg y instance by defining a new
C xfHead erFi l terStrateg y bean and associating it with a CXF endpoint.
Sample relayHeaders configurat ion
The following example shows how you can use the rel ayHead ers option to create a
C xfHead erFi l terStrateg y bean that blocks all message headers. The CXF endpoints in the route use the head erFi l terStrateg y option to install the filter strategy in the endpoint,
530
CHAPT ER 4 2 . FILT ERING SO AP MESSAG E HEADERS
where the head erFi l terStrateg y setting has the syntax,
head erFi l terStrateg y= #BeanID.
<beans ...>
...
<bean id="d ro pAl l Messag eHead ersStrateg y" class="org.apache.camel.component.cxf.common.header.CxfHeaderFilterStr
ategy">
<!-- Set relayHeaders to false to drop all SOAP headers -->
<property name="relayHeaders" value="false"/>
</bean>
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="cxf:bean:routerNoRelayEndpoint?
headerFilterStrategy=#d ro pAl l Messag eHead ersStrateg y"/>
<to uri="cxf:bean:serviceNoRelayEndpoint?
headerFilterStrategy=#d ro pAl l Messag eHead ersStrateg y"/>
</route>
</camelContext>
...
</beans>
relayAllMessageHeaders opt ion
The rel ayAl l Messag eHead ers option is used to propagate all SOAP headers, without applying any filtering (any installed filters would be bypassed). In order to enable this feature, you must set both rel ayHead ers and rel ayAl l Messag eHead ers to true.
Sample relayAllMessageHeaders configurat ion
The following example shows how to configure CXF endpoints to propagate all SOAP message headers. The pro pag ateAl l Messag es filter strategy sets both rel ayHead ers and rel ayAl l Messag eHead ers to true.
<beans ...>
...
<bean id="pro pag ateAl l Messag es" class="org.apache.camel.component.cxf.common.header.CxfHeaderFilterStr
ategy">
<!-- Set both properties to true to propagate *all* SOAP headers -->
<property name="relayHeaders" value="true"/>
<property name="relayAllMessageHeaders" value="true"/>
</bean>
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="cxf:bean:routerNoRelayEndpoint?
531
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
headerFilterStrategy=#pro pag ateAl l Messag es"/>
<to uri="cxf:bean:serviceNoRelayEndpoint?
headerFilterStrategy=#pro pag ateAl l Messag es"/>
</route>
</camelContext>
...
</beans>
4 2.2. HEADER FILT ERING
Overview
You can optionally install multiple headers in a C xfHead erFi l terStrateg y instance. The filtering mechanism then uses the header's XML namespace to lookup a particular filter, which it then applies to the header.
Filt er map
shows an overview of the filter map that is contained within a
C xfHead erFi l terStrateg y instance. For each filter that you install in
C xfHead erFi l terStrateg y, corresponding entries are made in the filter map, where one or more XML schema namespaces are associated with each filter.
Fig u re 4 2.1. Filt er Map
532
Filt er behavior
CHAPT ER 4 2 . FILT ERING SO AP MESSAG E HEADERS
When a header is filtered, the filter mechanism peeks at the header to discover the header's
XML namespace. The filter then looks up the XML namespace in the filter map to find the corresponding filter implementation. This filter is then applied to the header.
PAYLOAD mode
In PAYLOAD mode, both in-band and out-of-band messages pass through the installed filters.
POJO mode
In POJO mode, only out-of-band messages pass through the installed filters. In-band messages bypass the filters and are propagated by default.
4 2.3. IMPLEMENT ING A CUST OM FILT ER
Overview
You can implement your own customer message header filters by implementing the
Messag eHead erFi l ter Java interface. You must associate a filter with one or more XML schema namespaces (representing the header's namespace) and it is possible to differentiate between request message headers and response message headers.
MessageHeaderFilt er int erface
The Messag eHead erFi l ter interface is defined in the
o rg . apache. camel . co mpo nent. cxf. co mmo n. head er package, as follows:
// Java package org.apache.camel.component.cxf.common.header; import java.util.List; import org.apache.camel.spi.HeaderFilterStrategy.Direction; import org.apache.cxf.headers.Header; public interface MessageHeaderFilter {
List<String> getActivationNamespaces();
void filter(Direction direction, List<Header> headers);
}
Implement ing t he filt er() met hod
The Messag eHead erFi l ter. fi l ter() method is reponsible for applying header filtering.
Filtering is applied both before and after an operation is invoked on an endpoint. Hence, there are two directions to which filtering is applied, as follows:
533
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
D i recti o n. O UT
When the d i recti o n parameter equals D i recti o n. O UT , the filter is being applied to a request either leaving a consumer endpoint or entering a producer endpoint
(that is, it applies to a WS request message propagating through a route).
D i recti o n. IN
When the d i recti o n parameter equals D i recti o n. IN, the filter is being applied to a response either leaving a producer endpoint or entering a consumer endpoint
(that is, it applies to a WS response message being sent back).
Filtering can be applied by removing elements from the list of headers, head ers. Any headers left in the list are propagated.
Binding filt ers t o XML namespaces
It is possible to register multiple header filters against a given CXF endpoint. The CXF endpoint selects the appropriate filter to use based on the XML namespace of the WSDL binding protocol (for example, the namespace for the SOAP 1.1 binding or for the SOAP 1.2
binding). If a header's namespace is unknown, the header is propagated by default.
To bind a filter to one or more namespaces, implement the g etActi vati o nNamespaces() method, which returns the list of bound XML namespaces.
Ident ifying t he namespace t o bind t o
Example 42.1, “ Sample Binding Namespaces” illustrates how to identify the namespaces to
which you can bind a filter. This example shows the WSDL file for a Bank server that exposes SOAP endpoints.
Examp le 4 2.1. Samp le Bin d in g Namesp aces
<wsdl:definitions targetNamespace="http://cxf.apache.org/schemas/cxf/idl/bank"
xmlns:tns="http://cxf.apache.org/schemas/cxf/idl/bank"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:so ap="http: //schemas. xml so ap. o rg /wsd l /so ap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
...
<wsdl:binding name="BankSOAPBinding" type="tns:Bank">
<so ap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getAccount">
...
</wsdl:operation>
534
CHAPT ER 4 2 . FILT ERING SO AP MESSAG E HEADERS
...
</wsdl:binding>
...
</wsdl>
From the so ap: bi nd i ng tag, you can infer that namespace associated with the SOAP binding is http: //schemas. xml so ap. o rg /wsd l /so ap/.
Implement ing a cust om filt er
If you want to implement your own custom filter, define a class that inherits from the
Messag eHead erFi l ter interface and implement its methods as described in this section.
For example,
Example 42.2, “ Sample Header Filter Implementation” shows an example of a
custom filter, C usto mHead erFi l ter, that binds to the namespace,
http: //cxf. apache. o rg /bi nd i ng s/custo m, and relays all of the headers that pass through it.
Examp le 4 2.2. Samp le Head er Filt er Imp lemen t at io n
// Java package org.apache.camel.component.cxf.soap.headers; import java.util.Arrays; import java.util.List; import org.apache.camel.component.cxf.common.header.MessageHeaderFilter; import org.apache.camel.spi.HeaderFilterStrategy.Direction; import org.apache.cxf.headers.Header; public class CustomHeaderFilter implements MessageHeaderFilter {
public static final String ACTIVATION_NAMESPACE =
"http://cxf.apache.org/bindings/custom";
public static final List<String> ACTIVATION_NAMESPACES =
Arrays.asList(ACTIVATION_NAMESPACE);
public List<String> getActivationNamespaces() {
return ACTIVATION_NAMESPACES;
}
public void filter(Direction direction, List<Header> headers) {
}
}
4 2.4 . INST ALLING FILT ERS
Overview
535
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
To install message header filters, set the messag eHead erFi l ters property of the
C xfHead erFi l terStrateg y object. When you initialize this property with a list of message header filters, the header filter strategy combines the specified filters to make a filter map.
The messag eHead erFi l ters property is of type, Li st<Messag eHead erFi l ter>.
Inst alling filt ers in XML
The following example shows how to create a C xfHead erFi l terStrateg y instance, specifying a customized list of header filters in the messag eHead erFi l ters property.
There are two header filters in this example: So apMessag eHead erFi l ter and
C usto mHead erFi l ter.
<bean id="customMessageFilterStrategy" class="org.apache.camel.component.cxf.common.header.CxfHeaderFilterStr
ategy">
<property name="messageHeaderFilters">
<list>
<!-- SoapMessageHeaderFilter is the built in filter. It can be removed by omitting it. -->
<bean class="org.apache.camel.component.cxf.common.header.SoapMessageHeaderF
ilter"/>
<!-- Add custom filter here -->
<bean class="org.apache.camel.component.cxf.soap.headers.CustomHeaderFilter"
/>
</list>
</property>
<!-- The 'relayHeaders' property is 'true' by default -->
</bean>
SoapMessageHeaderFilt er
The first header filter in the preceding example is the So apMessag eHead erFi l ter filter, which is the default header filter. This filter is designed to filter standard SOAP headers and is bound to the following XML namespaces: http://schemas.xmlsoap.org/soap/ http://schemas.xmlsoap.org/wsdl/soap/ http://schemas.xmlsoap.org/wsdl/soap12/
This filter peeks at the header element, in order to decide whether or not to block a particular header. If the so ap: acto r attribute (SOAP 1.1) or the so ap: ro l e attribute (SOAP 1.2) is present and has the value next, the header is removed from the message. Otherwise, the header is propagated.
Namespace clashes
536
CHAPT ER 4 2 . FILT ERING SO AP MESSAG E HEADERS
Normally, each namespace should be bound to just a single header filter. If a namespace is bound to more than one header filter, this normally causes an error. It is possible, however, to override this policy by setting the al l o wFi l terNamespaceC l ash property to true in the C xfHead erFi l terStrateg y instance. When this policy is set to true, the nearest to last filter is selected, in the event of a namespace clash.
537
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
PART IV. PROGRAMMING EIP COMPONENTS
Ab st ract
This guide describes how to use the Apache Camel API.
538
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S
CHAPTER 43. UNDERSTANDING MESSAGE FORMATS
Ab st ract
Before you can begin programming with Apache Camel, you should have a clear understanding of how messages and message exchanges are modelled. Because Apache
Camel can process many message formats, the basic message type is designed to have an abstract format. Apache Camel provides the APIs needed to access and transform the data formats that underly message bodies and message headers.
4 3.1. EXCHANGES
Overview
An exchange object is a wrapper that encapsulates a received message and stores its associated metadata (including the exchange properties). In addition, if the current message is dispatched to a producer endpoint, the exchange provides a temporary slot to hold the reply (the Out message).
An important feature of exchanges in Apache Camel is that they support lazy creation of messages. This can provide a significant optimization in the case of routes that do not require explicit access to messages.
Fig u re 4 3.1. Exch an g e O b ject Passin g t h ro u g h a Ro u t e
Figure 43.1, “ Exchange Object Passing through a Route” shows an exchange object
passing through a route. In the context of a route, an exchange object gets passed as the argument of the P ro cesso r. pro cess() method. This means that the exchange object is directly accessible to the source endpoint, the target endpoint, and all of the processors in between.
T he Exchange int erface
The o rg . apache. camel . Exchang e interface defines methods to access In and Out messages, as shown in
Example 43.1, “ Exchange Methods”
.
Examp le 4 3.1. Exch an g e Met h o d s
539
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Access the In message
Message getIn(); void setIn(Message in);
// Access the Out message (if any)
Message getOut(); void setOut(Message out); boolean hasOut();
// Access the exchange ID
String getExchangeId(); void setExchangeId(String id);
For a complete description of the methods in the Exchang e interface, see Section 52.1, “ The
Laz y creat ion of messages
Apache Camel supports lazy creation of In, Out, and Fault messages. This means that message instances are not created until you try to access them (for example, by calling
g etIn() or g etO ut()). The lazy message creation semantics are implemented by the
o rg . apache. camel . i mpl . D efaul tExchang e class.
If you call one of the no-argument accessors (g etIn() or g etO ut()), or if you call an accessor with the boolean argument equal to true (that is, g etIn(true) or
g etO ut(true)), the default method implementation creates a new message instance, if one does not already exist.
If you call an accessor with the boolean argument equal to fal se (that is, g etIn(fal se) or g etO ut(fal se)), the default method implementation returns the current message value.
Laz y creat ion of exchange IDs
Apache Camel supports lazy creation of exchange IDs. You can call g etExchang eId () on any exchange to obtain a unique ID for that exchange instance, but the ID is generated only when you actually call the method. The D efaul tExchang e. g etExchang eId () implementation of this method delegates ID generation to the UUID generator that is registered with the C amel C o ntext.
For details of how to register UUID generators with the C amel C o ntext, see Section 43.4,
4 3.2. MESSAGES
Overview
54 0
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S
Message objects represent messages using the following abstract model:
Message body
Message headers
Message attachments
The message body and the message headers can be of arbitrary type (they are declared as type O bject) and the message attachments are declared to be of type
javax. acti vati o n. D ataHand l er , which can contain arbitrary MIME types. If you need to obtain a concrete representation of the message contents, you can convert the body and headers to another type using the type converter mechanism and, possibly, using the marshalling and unmarshalling mechanism.
One important feature of Apache Camel messages is that they support lazy creation of message bodies and headers. In some cases, this means that a message can pass through a route without needing to be parsed at all.
T he Message int erface
The o rg . apache. camel . Messag e interface defines methods to access the message
body, message headers and message attachments, as shown in Example 43.2, “ Message
Examp le 4 3.2. Messag e In t erf ace
// Access the message body
Object getBody();
<T> T getBody(Class<T> type); void setBody(Object body);
<T> void setBody(Object body, Class<T> type);
// Access message headers
Object getHeader(String name);
<T> T getHeader(String name, Class<T> type); void setHeader(String name, Object value);
Object removeHeader(String name);
Map<String, Object> getHeaders(); void setHeaders(Map<String, Object> headers);
// Access message attachments
javax.activation.DataHandler getAttachment(String id); java.util.Map<String, javax.activation.DataHandler> getAttachments(); java.util.Set<String> getAttachmentNames(); void addAttachment(String id, javax.activation.DataHandler content)
54 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Access the message ID
String getMessageId(); void setMessageId(String messageId);
For a complete description of the methods in the Messag e interface, see Section 53.1, “ The
Laz y creat ion of bodies, headers, and at t achment s
Apache Camel supports lazy creation of bodies, headers, and attachments. This means that the objects that represent a message body, a message header, or a message attachment are not created until they are needed.
For example, consider the following route that accesses the fo o message header from the In message: from("SourceURL")
.filter(header("foo")
.isEqualTo("bar"))
.to("TargetURL");
In this route, if we assume that the component referenced by SourceURL supports lazy creation, the In message headers are not actually parsed until the head er("fo o ") call is executed. At that point, the underlying message implementation parses the headers and populates the header map. The message body is not parsed until you reach the end of the route, at the to ("TargetURL") call. At that point, the body is converted into the format required for writing it to the target endpoint, TargetURL.
By waiting until the last possible moment before populating the bodies, headers, and attachments, you can ensure that unnecessary type conversions are avoided. In some cases, you can completely avoid parsing. For example, if a route contains no explicit references to message headers, a message could traverse the route without ever parsing the headers.
Whether or not lazy creation is implemented in practice depends on the underlying component implementation. In general, lazy creation is valuable for those cases where creating a message body, a message header, or a message attachment is expensive. For
details about implementing a message type that supports lazy creation, see Section 53.2,
“ Implementing the Message Interface” .
Laz y creat ion of message IDs
Apache Camel supports lazy creation of message IDs. That is, a message ID is generated only when you actually call the g etMessag eId () method. The
D efaul tExchang e. g etExchang eId () implementation of this method delegates ID generation to the UUID generator that is registered with the C amel C o ntext.
54 2
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S
Some endpoint implementations would call the g etMessag eId () method implicitly, if the endpoint implements a protocol that requires a unique message ID. In particular, JMS messages normally include a header containing unique message ID, so the JMS component automatically calls g etMessag eId () to obtain the message ID (this is controlled by the
messag eId Enabl ed option on the JMS endpoint).
For details of how to register UUID generators with the C amel C o ntext, see Section 43.4,
Init ial message format
The initial format of an In message is determined by the source endpoint, and the initial format of an Out message is determined by the target endpoint. If lazy creation is supported by the underlying component, the message remains unparsed until it is accessed explicitly by the application. Most Apache Camel components create the message body in a relatively raw form—for example, representing it using types such as byte[], ByteBuffer,
InputStream, or O utputStream. This ensures that the overhead required for creating the initial message is minimal. Where more elaborate message formats are required components usually rely on type converters or marshalling processors.
T ype convert ers
It does not matter what the initial format of the message is, because you can easily convert a
message from one format to another using the built-in type converters (see Section 43.3,
“ Built-In Type Converters” ). There are various methods in the Apache Camel API that expose
type conversion functionality. For example, the co nvertBo d yT o (C l ass type) method can be inserted into a route to convert the body of an In message, as follows: from("SourceURL").convertBodyTo(String.class).to("TargetURL");
Where the body of the In message is converted to a java. l ang . Stri ng . The following example shows how to append a string to the end of the In message body: from("SourceURL").setBody(bodyAs(String.class).append("My Special
Signature")).to("TargetURL");
Where the message body is converted to a string format before appending a string to the end. It is not necessary to convert the message body explicitly in this example. You can also use: from("SourceURL").setBody(body().append("My Special
Signature")).to("TargetURL");
Where the append () method automatically converts the message body to a string before appending its argument.
T ype conversion met hods in Message
54 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The o rg . apache. camel . Messag e interface exposes some methods that perform type conversion explicitly:
g etBo d y(C l ass<T > type)—Returns the message body as type, T .
g etHead er(Stri ng name, C l ass<T > type)—Returns the named header value as type, T .
For the complete list of supported conversion types, see Section 43.3, “ Built-In Type
Convert ing t o XML
In addition to supporting conversion between simple types (such as byte[], ByteBuffer,
Stri ng , and so on), the built-in type converter also supports conversion to XML formats. For example, you can convert a message body to the o rg . w3c. d o m. D o cument type. This conversion is more expensive than the simple conversions, because it involves parsing the entire message and then creating a tree of nodes to represent the XML document structure.
You can convert to the following XML document types:
o rg . w3c. d o m. D o cument javax. xml . transfo rm. sax. SAXSo urce
XML type conversions have narrower applicability than the simpler conversions. Because not every message body conforms to an XML structure, you have to remember that this type conversion might fail. On the other hand, there are many scenarios where a router deals exclusively with XML message types.
Marshalling and unmarshalling
Marshalling involves converting a high-level format to a low-level format, and unmarshalling involves converting a low-level format to a high-level format. The following two processors are used to perform marshalling or unmarshalling in a route:
marshal () unmarshal ()
For example, to read a serialized Java object from a file and unmarshal it into a Java object, you could use the route definition shown in
Example 43.3, “ Unmarshalling a Java Object” .
Examp le 4 3.3. Un marsh allin g a Java O b ject
from("file://tmp/appfiles/serialized")
.unmarshal()
.serialization()
54 4
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S
.<FurtherProcessing>
.to("TargetURL");
Final message format
When an In message reaches the end of a route, the target endpoint must be able to convert the message body into a format that can be written to the physical endpoint. The same rule applies to Out messages that arrive back at the source endpoint. This conversion is usually performed implicitly, using the Apache Camel type converter. Typically, this involves converting from a low-level format to another low-level format, such as converting from a
byte[] array to an InputStream type.
4 3.3. BUILT -IN T YPE CONVERT ERS
Overview
This section describes the conversions supported by the master type converter. These conversions are built into the Apache Camel core.
Usually, the type converter is called through convenience functions, such as
Messag e. g etBo d y(C l ass<T > type) or Messag e. g etHead er(Stri ng name,
C l ass<T > type). It is also possible to invoke the master type converter directly. For example, if you have an exchange object, exchang e, you could convert a given value to a
Stri ng as shown in
Example 43.4, “ Converting a Value to a String” .
Examp le 4 3.4 . Co n vert in g a Valu e t o a St rin g
org.apache.camel.TypeConverter tc = exchange.getContext().getTypeConverter();
String str_value = tc.convertTo(String.class, value);
Basic t ype convert ers
Apache Camel provides built-in type converters that perform conversions to and from the following basic types:
java. i o . Fi l e
Stri ng
byte[] and java. ni o . ByteBuffer
java. i o . InputStream and java. i o . O utputStream
54 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
java. i o . R ead er and java. i o . Wri ter
java. i o . Buffered R ead er and java. i o . Buffered Wri ter
java. i o . Stri ng R ead er
However, not all of these types are inter-convertible. The built-in converter is mainly focused on providing conversions from the Fi l e and Stri ng types. The Fi l e type can be converted to any of the preceding types, except R ead er, Wri ter, and Stri ng R ead er. The
Stri ng type can be converted to Fi l e, byte[], ByteBuffer, InputStream, or
Stri ng R ead er. The conversion from Stri ng to Fi l e works by interpreting the string as a file name. The trio of Stri ng , byte[], and ByteBuffer are completely inter-convertible.
No t e
You can explicitly specify which character encoding to use for conversion from
byte[] to Stri ng and from Stri ng to byte[] by setting the
Exchang e. C HAR SET _NAME exchange property in the current exchange. For example, to perform conversions using the UTF-8 character encoding, call
exchang e. setP ro perty("Exchang e. C HAR SET _NAME", "UT F-8"). The supported character sets are described in the java. ni o . charset. C harset class.
Collect ion t ype convert ers
Apache Camel provides built-in type converters that perform conversions to and from the following collection types:
O bject[] java. uti l . Set java. uti l . Li st
All permutations of conversions between the preceding collection types are supported.
Map t ype convert ers
Apache Camel provides built-in type converters that perform conversions to and from the following map types:
java. uti l . Map java. uti l . HashMap java. uti l . Hashtabl e
54 6
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S java. uti l . P ro perti es
The preceding map types can also be converted into a set, of java. uti l . Set type, where the set elements are of the MapEntry<K,V> type.
DOM t ype convert ers
You can perform type conversions to the following Document Object Model (DOM) types:
o rg . w3c. d o m. D o cument—convertible from byte[], Stri ng , java. i o . Fi l e, and
java. i o . InputStream.
o rg . w3c. d o m. No d e
javax. xml . transfo rm. d o m. D O MSo urce—convertible from Stri ng .
javax. xml . transfo rm. So urce—convertible from byte[] and Stri ng .
All permutations of conversions between the preceding DOM types are supported.
SAX t ype convert ers
You can also perform conversions to the javax. xml . transfo rm. sax. SAXSo urce type, which supports the SAX event-driven XML parser (see the SAX Web site for details). You can convert to SAXSo urce from the following types:
Stri ng
InputStream
So urce
StreamSo urce
D O MSo urce
enum t ype convert er
Camel provides a type converter for performing Stri ng to enum type conversions, where the string value is converted to the matching enum constant from the specified enumeration class (the matching is case-insensitive). This type converter is rarely needed for converting message bodies, but it is frequently used internally by Apache Camel to select particular options.
For example, when setting the logging level option, the following value, INFO , is converted into an enum constant:
54 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<to uri="log:foo?level=INFO"/>
Because the enum type converter is case-insensitive, any of the following alternatives would also work:
<to uri="log:foo?level=info"/>
<to uri="log:foo?level=INfo"/>
<to uri="log:foo?level=InFo"/>
Cust om t ype convert ers
Apache Camel also enables you to implement your own custom type converters. For details
on how to implement a custom type converter, see Chapter 45, Type Converters .
4 3.4 . BUILT -IN UUID GENERAT ORS
Overview
Apache Camel enables you to register a UUID generator in the C amel C o ntext. This UUID generator is then used whenever Apache Camel needs to generate a unique ID—in particular, the registered UUID generator is called to generate the IDs returned by the
Exchang e. g etExchang eId () and the Messag e. g etMessag eId () methods.
For example, you might prefer to replace the default UUID generator, if part of your application does not support IDs with a length of 36 characters (like Websphere MQ). Also, it can be convenient to generate IDs using a simple counter (see the
Si mpl eUui d G enerato r) for testing purposes.
Provided UUID generat ors
You can configure Apache Camel to use one of the following UUID generators, which are provided in the core:
o rg . apache. camel . i mpl . Acti veMQ Uui d G enerato r—(Default) generates the same style of ID as is used by Apache ActiveMQ. This implementation might not be suitable for all applications, because it uses some JDK APIs that are forbidden in the context of cloud computing (such as the Google App Engine).
o rg . apache. camel . i mpl . Si mpl eUui d G enerato r—implements a simple counter
ID, starting at 1. The underlying implementation uses the
java. uti l . co ncurrent. ato mi c. Ato mi cLo ng type, so that it is thread-safe.
o rg . apache. camel . i mpl . JavaUui d G enerato r—implements an ID based on the
java. uti l . UUID type. Because java. uti l . UUID is synchronized, this might affect performance on some highly concurrent systems.
54 8
Cust om UUID generat or
CHAPT ER 4 3. UNDERST ANDING MESSAG E FO RMAT S
To implement a custom UUID generator, implement the
o rg . apache. camel . spi . Uui d G enerato r interface, which is shown in
“ UuidGenerator Interface” . The g enerateUui d () must be implemented to return a unique
ID string.
Examp le 4 3.5. Uu id G en erat o r In t erf ace
// Java package org.apache.camel.spi;
/**
* Generator to generate UUID strings.
*/ public interface UuidGenerator {
String generateUuid();
}
Specifying t he UUID generat or using Java
To replace the default UUID generator using Java, call the setUui d G enerato r() method on the current C amel C o ntext object. For example, you can register a
Si mpl eUui d G enerato r instance with the current C amel C o ntext, as follows:
// Java getContext().setUuidGenerator(new org.apache.camel.impl.SimpleUuidGenerator());
No t e
The setUui d G enerato r() method should be called during startup, before any routes are activated.
Specifying t he UUID generat or using Spring
To replace the default UUID generator using Spring, all you need to do is to create an instance of a UUID generator using the Spring bean element. When a camel C o ntext instance is created, it automatically looks up the Spring registry, searching for a bean that implements o rg . apache. camel . spi . Uui d G enerato r. For example, you can register a
Si mpl eUui d G enerato r instance with the C amel C o ntext as follows:
<beans ...>
<bean id="simpleUuidGenerator"
class="org.apache.camel.impl.SimpleUuidGenerator" />
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
54 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
...
</camelContext>
...
</beans>
If there is no active metho d the returned value will b e
nul l
.
550
CHAPT ER 4 4 . IMPLEMENT ING A PRO CESSO R
CHAPTER 44. IMPLEMENTING A PROCESSOR
Ab st ract
Apache Camel allows you to implement a custom processor. You can then insert the custom processor into a route to perform operations on exchange objects as they pass through the route.
4 4 .1. PROCESSING MODEL
Pipelining model
The pipelining model describes the way in which processors are arranged in Section 5.4,
“ Pipes and Filters” . Pipelining is the most common way to process a sequence of endpoints
(a producer endpoint is just a special type of processor). When the processors are arranged
in this way, the exchange's In and Out messages are processed as shown in Figure 44.1,
Fig u re 4 4 .1. Pip elin in g Mo d el
The processors in the pipeline look like services, where the In message is analogous to a request, and the Out message is analogous to a reply. In fact, in a realistic pipeline, the nodes in the pipeline are often implemented by Web service endpoints, such as the CXF component.
For example,
Example 44.1, “ Java DSL Pipeline”
shows a Java DSL pipeline constructed from a sequence of two processors, P ro cesso rA, P ro cesso rB, and a producer endpoint,
TargetURI.
Examp le 4 4 .1. Java DSL Pip elin e
from(SourceURI).pipeline(ProcessorA, ProcessorB, TargetURI);
4 4 .2. IMPLEMENT ING A SIMPLE PROCESSOR
Overview
551
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
This section describes how to implement a simple processor that executes message processing logic before delegating the exchange to the next processor in the route.
Processor int erface
Simple processors are created by implementing the o rg . apache. camel . P ro cesso r
interface. As shown in Example 44.2, “ Processor Interface”
, the interface defines a single method, pro cess(), which processes an exchange object.
Examp le 4 4 .2. Pro cesso r In t erf ace
package org.apache.camel; public interface Processor { void process(Exchange exchange) throws Exception;
}
Implement ing t he Processor int erface
To create a simple processor you must implement the P ro cesso r interface and provide the logic for the pro cess() method.
Example 44.3, “ Simple Processor Implementation”
shows the outline of a simple processor implementation.
Examp le 4 4 .3. Simp le Pro cesso r Imp lemen t at io n
import org.apache.camel.Processor; public class MyProcessor implements Processor { public MyProcessor() { } public void process(Exchange exchange) throws Exception
{
// Insert code that gets executed *before* delegating
// to the next processor in the chain.
...
}
}
552
All of the code in the pro cess() method gets executed before the exchange object is delegated to the next processor in the chain.
For examples of how to access the message body and header values inside a simple processor, see
Section 44.3, “ Accessing Message Content”
.
Insert ing t he simple processor int o a rout e
CHAPT ER 4 4 . IMPLEMENT ING A PRO CESSO R
Use the pro cess() DSL command to insert a simple processor into a route. Create an instance of your custom processor and then pass this instance as an argument to the
pro cess() method, as follows: org.apache.camel.Processor myProc = new MyProcessor(); from("SourceURL").process(myProc).to("TargetURL");
4 4 .3. ACCESSING MESSAGE CONT ENT
Accessing message headers
Message headers typically contain the most useful message content from the perspective of a router, because headers are often intended to be processed in a router service. To access header data, you must first get the message from the exchange object (for example, using
Exchang e. g etIn()), and then use the Messag e interface to retrieve the individual headers (for example, using Messag e. g etHead er()).
Example 44.4, “ Accessing an Authorization Header” shows an example of a custom
processor that accesses the value of a header named Autho ri zati o n. This example uses the Exchang eHel per. g etMand ato ryHead er() method, which eliminates the need to test for a null header value.
Examp le 4 4 .4 . Accessin g an Au t h o riz at io n Head er
import org.apache.camel.*; import org.apache.camel.util.ExchangeHelper; public class MyProcessor implements Processor { public void process(Exchange exchange) {
String auth = ExchangeHelper.g etMand ato ryHead er(
exchange,
"Authorization" ,
String.class
);
// process the authorization string...
// ...
}
}
For full details of the Messag e interface, see
.
Accessing t he message body
You can also access the message body. For example, to append a string to the end of the In
message, you can use the processor shown in Example 44.5, “ Accessing the Message
553
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Examp le 4 4 .5. Accessin g t h e Messag e Bo d y
import org.apache.camel.*; import org.apache.camel.util.ExchangeHelper; public class MyProcessor implements Processor { public void process(Exchange exchange) {
Message in = exchange.getIn();
in.setBody(in.getBody(String.class) + " World!" );
}
}
Accessing message at t achment s
You can access a message's attachments using either the Messag e. g etAttachment()
method or the Messag e. g etAttachments() method. See Example 43.2, “ Message
4 4 .4 . T HE EXCHANGEHELPER CLASS
Overview
The o rg . apache. camel . uti l . Exchang eHel per class is a Apache Camel utility class that provides methods that are useful when implementing a processor.
Resolve an endpoint
The static reso l veEnd po i nt() method is one of the most useful methods in the
Exchang eHel per class. You use it inside a processor to create new End po i nt instances on the fly.
Examp le 4 4 .6 . T h e reso l veEnd po i nt() Met h o d
public final class ExchangeHelper {
...
@ SuppressWarnings({"unchecked" })
public static Endpoint
resolveEndpoint(Exchange exchange, Object value)
throws NoSuchEndpointException { ... }
...
}
554
The first argument to reso l veEnd po i nt() is an exchange instance, and the second argument is usually an endpoint URI string.
Example 44.7, “ Creating a File Endpoint”
shows how to create a new file endpoint from an exchange instance exchang e
CHAPT ER 4 4 . IMPLEMENT ING A PRO CESSO R
Examp le 4 4 .7. Creat in g a File En d p o in t
Endpoint file_endp = ExchangeHelper.resolveEndpoint(exchange,
"file://tmp/messages/in.xml");
Wrapping t he exchange accessors
The Exchang eHel per class provides several static methods of the form
g etMand ato ryBeanProperty(), which wrap the corresponding g etBeanProperty() methods on the Exchang e class. The difference between them is that the original
g etBeanProperty() accessors return nul l , if the corresponding property is unavailable, and the g etMand ato ryBeanProperty() wrapper methods throw a Java exception. The following wrapper methods are implemented in the Exchang eHel per class: public final class ExchangeHelper {
...
public static <T> T getMandatoryProperty(Exchange exchange,
String propertyName, Class<T> type)
throws NoSuchPropertyException { ... }
public static <T> T getMandatoryHeader(Exchange exchange, String propertyName, Class<T> type)
throws NoSuchHeaderException { ... }
public static Object getMandatoryInBody(Exchange exchange)
throws InvalidPayloadException { ... }
public static <T> T getMandatoryInBody(Exchange exchange, Class<T> type)
throws InvalidPayloadException { ... }
public static Object getMandatoryOutBody(Exchange exchange)
throws InvalidPayloadException { ... }
public static <T> T getMandatoryOutBody(Exchange exchange,
Class<T> type)
throws InvalidPayloadException { ... }
...
}
T est ing t he exchange pat t ern
Several different exchange patterns are compatible with holding an In message. Several different exchange patterns are also compatible with holding an Out message. To provide a quick way of checking whether or not an exchange object is capable of holding an In message or an Out message, the Exchang eHel per class provides the following methods: public final class ExchangeHelper {
...
public static boolean isInCapable(Exchange exchange) { ... }
555
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
public static boolean isOutCapable(Exchange exchange) { ... }
...
}
Get t he In message's MIME cont ent t ype
If you want to find out the MIME content type of the exchange's In message, you can access it by calling the Exchang eHel per. g etC o ntentT ype(exchang e) method. To implement this, the Exchang eHel per object looks up the value of the In message's C o ntent-T ype header—this method relies on the underlying component to populate the header value).
556
CHAPT ER 4 5. T YPE CO NVERT ERS
CHAPTER 45. TYPE CONVERTERS
Ab st ract
Apache Camel has a built-in type conversion mechanism, which is used to convert message bodies and message headers to different types. This chapter explains how to extend the type conversion mechanism by adding your own custom converter methods.
4 5.1. T YPE CONVERT ER ARCHIT ECT URE
Overview
This section describes the overall architecture of the type converter mechanism, which you must understand, if you want to write custom type converters. If you only need to use the
built-in type converters, see Chapter 43, Understanding Message Formats
.
T ype convert er int erface
Example 45.1, “ TypeConverter Interface”
shows the definition of the
o rg . apache. camel . T ypeC o nverter interface, which all type converters must implement.
Examp le 4 5.1. T yp eCo n vert er In t erf ace
package org.apache.camel; public interface TypeConverter {
<T> T convertTo(Class<T> type, Object value);
}
Mast er t ype convert er
The Apache Camel type converter mechanism follows a master/slave pattern. There are many
slave type converters, which are each capable of performing a limited number of type conversions, and a single master type converter, which aggregates the type conversions performed by the slaves. The master type converter acts as a front-end for the slave type converters. When you request the master to perform a type conversion, it selects the appropriate slave and delegates the conversion task to that slave.
For users of the type conversion mechanism, the master type converter is the most important because it provides the entry point for accessing the conversion mechanism. During start up, Apache Camel automatically associates a master type converter instance with the
C amel C o ntext object. To obtain a reference to the master type converter, you call the
C amel C o ntext. g etT ypeC o nverter() method. For example, if you have an exchange object, exchang e, you can obtain a reference to the master type converter as shown in
Example 45.2, “ Getting a Master Type Converter”
.
557
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Examp le 4 5.2. G et t in g a Mast er T yp e Co n vert er
org.apache.camel.TypeConverter tc = exchange.getContext().getTypeConverter();
T ype convert er loader
The master type converter uses a type converter loader to populate the registry of slave type converters. A type converter loader is any class that implements the
T ypeC o nverterLo ad er interface. Apache Camel currently uses only one kind of type converter loader—the annotation type converter loader (of
Anno tati o nT ypeC o nverterLo ad er type).
T ype conversion process
Figure 45.1, “ Type Conversion Process”
gives an overview of the type conversion process, showing the steps involved in converting a given data value, val ue, to a specified type,
to T ype.
Fig u re 4 5.1. T yp e Co n versio n Pro cess
558
CHAPT ER 4 5. T YPE CO NVERT ERS
The type conversion mechanism proceeds as follows:
1. The C amel C o ntext object holds a reference to the master T ypeC o nverter instance. The first step in the conversion process is to retrieve the master type converter by calling C amel C o ntext. g etT ypeC o nverter().
2. Type conversion is initiated by calling the co nvertT o () method on the master type converter. This method instructs the type converter to convert the data object, val ue, from its original type to the type specified by the to T ype argument.
3. Because the master type converter is a front end for many different slave type converters, it looks up the appropriate slave type converter by checking a registry of type mappings The registry of type converters is keyed by a type mapping pair
(toType, fromType). If a suitable type converter is found in the registry, the master type converter calls the slave's co nvertT o () method and returns the result.
4. If a suitable type converter cannot be found in the registry, the master type converter loads a new type converter, using the type converter loader.
5. The type converter loader searches the available JAR libraries on the classpath to find a suitable type converter. Currently, the loader strategy that is used is implemented by the annotation type converter loader, which attempts to load a class
6. If the type converter loader is successful, a new slave type converter is loaded and entered into the type converter registry. This type converter is then used to convert the
val ue argument to the to T ype type.
7. If the data is successfully converted, the converted data value is returned. If the conversion does not succeed, nul l is returned.
4 5.2. IMPLEMENT ING T YPE CONVERT ER USING ANNOT AT IONS
Overview
The type conversion mechanism can easily be customized by adding a new slave type converter. This section describes how to implement a slave type converter and how to integrate it with Apache Camel, so that it is automatically loaded by the annotation type converter loader.
How t o implement a t ype convert er
To implement a custom type converter, perform the following steps:
1.
Implement an annotated converter class
.
2.
559
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
3.
.
Implement an annot at ed convert er class
You can implement a custom type converter class using the @ C o nverter annotation. You must annotate the class itself and each of the stati c methods intended to perform type conversion. Each converter method takes an argument that defines the from type, optionally takes a second Exchang e argument, and has a non-void return value that defines the to type. The type converter loader uses Java reflection to find the annotated methods and
integrate them into the type converter mechanism. Example 45.3, “ Example of an Annotated
Converter Class” shows an example of an annotated converter class that defines a converter
method for converting from java. i o . Fi l e to java. i o . InputStream and another converter method (with an Exchang e argument) for converting from byte[] to Stri ng .
Examp le 4 5.3. Examp le o f an An n o t at ed Co n vert er Class
package com.YourDomain.YourPackageName; import org.apache.camel.C o nverter; import java.io.*;
@ C o nverter
public class IOConverter { private IOConverter() {
}
@ C o nverter public
stati c
InputStream toInputStream(File file) throws
FileNotFoundException { return new BufferedInputStream( new FileInputStream(file));
}
@ C o nverter public
stati c
String toString( byte [] data, Exchang e exchange) { if (exchange != null ) {
String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class); if (charsetName != null ) { try { return new String(data, charsetName);
} catch (UnsupportedEncodingException e) {
LOG.warn( "Can't convert the byte to String with the charset " + charsetName, e);
}
}
} return new String(data);
}
}
560
CHAPT ER 4 5. T YPE CO NVERT ERS
The to InputStream() method is responsible for performing the conversion from the Fi l e type to the InputStream type and the to Stri ng () method is responsible for performing the conversion from the byte[] type to the Stri ng type.
No t e
The method name is unimportant, and can be anything you choose. What is important are the argument type, the return type, and the presence of the
@ C o nverter annotation.
Creat e a T ypeConvert er file
To enable the discovery mechanism (which is implemented by the annotation type converter
loader) for your custom converter, create a T ypeC o nverter file at the following location:
META-INF/services/org/apache/camel/TypeConverter
The T ypeC o nverter file must contain a comma-separated list of package names identifying the packages that contain type converter classes. For example, if you want the type converter loader to search the co m. YourDomain. YourPackageName package for annotated converter classes, the T ypeC o nverter file would have the following contents: com.YourDomain.YourPackageName
Package t he t ype convert er
The type converter is packaged as a JAR file containing the compiled classes of your custom type converters and the MET A-INF directory. Put this JAR file on your classpath to make it available to your Apache Camel application.
Fallback convert er met hod
In addition to defining regular converter methods using the @ C o nverter annotation, you can optionally define a fallback converter method using the @ Fal l backC o nverter annotation. The fallback converter method will only be tried, if the master type converter fails to find a regular converter method in the type registry.
The essential difference between a regular converter method and a fallback converter method is that whereas a regular converter is defined to perform conversion between a specific pair of types (for example, from byte[] to Stri ng ), a fallback converter can potentially perform conversion between any pair of types. It is up to the code in the body of the fallback converter method to figure out which conversions it is able to perform. At run time, if a conversion cannot be performed by a regular converter, the master type converter iterates through every available fallback converter until it finds one that can perform the conversion.
The method signature of a fallback converter can have either of the following forms:
561
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// 1. Non-generic form of signature
@ FallbackConverter public static Object MethodName(
Class type,
Exchange exchange,
Object value,
TypeConverterRegistry registry
)
// 2. Templating form of signature
@ FallbackConverter public static <T> T MethodName(
Class<T> type,
Exchange exchange,
Object value,
TypeConverterRegistry registry
)
Where MethodName is an arbitrary method name for the fallback converter.
For example, the following code extract (taken from the implementation of the File component) shows a fallback converter that can convert the body of a G eneri cFi l e object, exploiting the type converters already available in the type converter registry: package org.apache.camel.component.file; import org.apache.camel.C o nverter; import org.apache.camel.Fal l backC o nverter; import org.apache.camel.Exchange; import org.apache.camel.TypeConverter; import org.apache.camel.spi.TypeConverterRegistry;
@ C o nverter
public final class GenericFileConverter {
private GenericFileConverter() {
// Helper Class
}
@ Fal l backC o nverter
public static <T> T convertTo(Class<T> type, Exchange exchange,
Object value, TypeConverterRegistry registry) {
// use a fallback type converter so we can convert the embedded body if the value is GenericFile
if (GenericFile.class.isAssignableFrom(value.getClass())) {
GenericFile file = (GenericFile) value;
Class from = file.getBody().getClass();
TypeConverter tc = registry.lookup(type, from);
if (tc != null) {
Object body = file.getBody();
return tc.convertTo(type, exchange, body);
}
}
562
CHAPT ER 4 5. T YPE CO NVERT ERS
return null;
}
...
}
4 5.3. IMPLEMENT ING A T YPE CONVERT ER DIRECT LY
Overview
Generally, the recommended way to implement a type converter is to use an annotated class,
as described in the previous section, Section 45.2, “ Implementing Type Converter Using
Annotations” . But if you want to have complete control over the registration of your type
converter, you can implement a custom slave type converter and add it directly to the type converter registry, as described here.
Implement t he T ypeConvert er int erface
To implement your own type converter class, define a class that implements the
T ypeC o nverter interface. For example, the following MyO rd erT ypeC o nverter class converts an integer value to a MyO rd er object, where the integer value is used to initialize the order ID in the MyO rd er object.
import org.apache.camel.TypeConverter
private class MyOrderTypeConverter implements TypeConverter {
public <T> T convertTo(Class<T> type, Object value) {
// converter from value to the MyOrder bean
MyOrder order = new MyOrder();
order.setId(Integer.parseInt(value.toString()));
return (T) order;
}
public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
// this method with the Exchange parameter will be preferd by
Camel to invoke
// this allows you to fetch information from the exchange during convertions
// such as an encoding parameter or the likes
return convertTo(type, value);
}
public <T> T mandatoryConvertTo(Class<T> type, Object value) {
return convertTo(type, value);
}
public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange,
Object value) {
return convertTo(type, value);
}
}
563
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Add t he t ype convert er t o t he regist ry
You can add the custom type converter directly to the type converter registry using code like the following:
// Add the custom type converter to the type converter registry context.getTypeConverterRegistry().addTypeConverter(MyOrder.class,
String.class, new MyOrderTypeConverter());
Where co ntext is the current o rg . apache. camel . C amel C o ntext instance. The
ad d T ypeC o nverter() method registers the MyO rd erT ypeC o nverter class against the specific type conversion, from Stri ng . cl ass to MyO rd er. cl ass.
564
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
CHAPTER 46. PRODUCER AND CONSUMER TEMPLATES
Ab st ract
The producer and consumer templates in Apache Camel are modelled after a feature of the
Spring container API, whereby access to a resource is provided through a simplified, easyto-use API known as a template. In the case of Apache Camel, the producer template and consumer template provide simplified interfaces for sending messages to and receiving messages from producer endpoints and consumer endpoints.
4 6.1. USING T HE PRODUCER T EMPLAT E
4 6.1.1. Int roduct ion t o t he Producer T emplat e
Ove rvie w
The producer template supports a variety of different approaches to invoking producer endpoints. There are methods that support different formats for the request message (as an
Exchang e object, as a message body, as a message body with a single header setting, and so on) and there are methods to support both the synchronous and the asynchronous style of invocation. Overall, producer template methods can be grouped into the following categories:
the section called “ Synchronous invocation” .
the section called “ Synchronous invocation with a processor”
.
the section called “ Asynchronous invocation”
.
the section called “ Asynchronous invocation with a callback”
.
Synchro no us invo cat io n
The methods for invoking endpoints synchronously have names of the form send Suffix() and req uestSuffix(). For example, the methods for invoking an endpoint using either the default message exchange pattern (MEP) or an explicitly specified MEP are named send (),
send Bo d y(), and send Bo d yAnd Head er() (where these methods respectively send an
Exchang e object, a message body, or a message body and header value). If you want to force the MEP to be InOut (request/reply semantics), you can call the req uest(),
req uestBo d y(), and req uestBo d yAnd Head er() methods instead.
The following example shows how to create a P ro d ucerT empl ate instance and use it to send a message body to the acti vemq : MyQ ueue endpoint. The example also shows how to send a message body and header value using send Bo d yAnd Head er().
import org.apache.camel.ProducerTemplate
import org.apache.camel.impl.DefaultProducerTemplate
565
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
...
ProducerTemplate template = context.createProducerTemplate();
// Send to a specific queue template.sendBody("activemq:MyQueue", "<hello>world!</hello>");
// Send with a body and header template.sendBodyAndHeader(
"activemq:MyQueue",
"<hello>world!</hello>",
"CustomerRating", "Gold" );
Synchro no us invo cat io n wit h a pro ce sso r
A special case of synchronous invocation is where you provide the send () method with a
P ro cesso r argument instead of an Exchang e argument. In this case, the producer template implicitly asks the specified endpoint to create an Exchang e instance (typically, but not always having the InOnly MEP by default). This default exchange is then passed to the processor, which initializes the contents of the exchange object.
The following example shows how to send an exchange initialized by the MyP ro cesso r processor to the acti vemq : MyQ ueue endpoint.
import org.apache.camel.ProducerTemplate
import org.apache.camel.impl.DefaultProducerTemplate
...
ProducerTemplate template = context.createProducerTemplate();
// Send to a specific queue, using a processor to initialize template.send("activemq:MyQueue", new MyProcessor());
The MyP ro cesso r class is implemented as shown in the following example. In addition to setting the In message body (as shown here), you could also initialize message heades and exchange properties.
import org.apache.camel.Processor; import org.apache.camel.Exchange;
...
public class MyProcessor implements Processor {
public MyProcessor() { }
public void process(Exchange ex) {
ex.getIn().setBody("<hello>world!</hello>");
}
}
Asynchro no us invo cat io n
The methods for invoking endpoints asynchronously have names of the form
asyncSend Suffix() and asyncR eq uestSuffix(). For example, the methods for invoking an endpoint using either the default message exchange pattern (MEP) or an
566
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
explicitly specified MEP are named asyncSend () and asyncSend Bo d y() (where these methods respectively send an Exchang e object or a message body). If you want to force the
MEP to be InOut (request/reply semantics), you can call the asyncR eq uestBo d y(),
asyncR eq uestBo d yAnd Head er(), and asyncR eq uestBo d yAnd Head ers() methods instead.
The following example shows how to send an exchange asynchronously to the
d i rect: start endpoint. The asyncSend () method returns a
java. uti l . co ncurrent. Future object, which is used to retrieve the invocation result at a later time.
import java.util.concurrent.Future; import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange;
...
Exchange exchange = new DefaultExchange(context); exchange.getIn().setBody("Hello");
Future<Exchange> future = template.asyncSend("direct:start", exchange);
// You can do other things, whilst waiting for the invocation to complete
...
// Now, retrieve the resulting exchange from the Future
Exchange result = future.get();
The producer template also provides methods to send a message body asynchronously (for example, using asyncSend Bo d y() or asyncR eq uestBo d y()). In this case, you can use one of the following helper methods to extract the returned message body from the Future object:
<T> T extractFutureBody(Future future, Class<T> type);
<T> T extractFutureBody(Future future, long timeout, TimeUnit unit,
Class<T> type) throws TimeoutException;
The first version of the extractFutureBo d y() method blocks until the invocation completes and the reply message is available. The second version of the
extractFutureBo d y() method allows you to specify a timeout. Both methods have a type argument, type, which casts the returned message body to the specified type using a built-in type converter.
The following example shows how to use the asyncR eq uestBo d y() method to send a message body to the d i rect: start endpoint. The blocking extractFutureBo d y() method is then used to retrieve the reply message body from the Future object.
Future<Object> future = template.asyncRequestBody("direct:start",
"Hello");
// You can do other things, whilst waiting for the invocation to
567
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
complete
...
// Now, retrieve the reply message body as a String type
String result = template.extractFutureBody(future, String.class);
Asynchro no us invo cat io n wit h a callback
In the preceding asynchronous examples, the request message is dispatched in a subthread, while the reply is retrieved and processed by the main thread. The producer template also gives you the option, however, of processing replies in the sub-thread, using one of the
asyncC al l back(), asyncC al l backSend Bo d y(), or
asyncC al l backR eq uestBo d y() methods. In this case, you supply a callback object (of
o rg . apache. camel . i mpl . Synchro ni zati o nAd apter type), which automatically gets invoked in the sub-thread as soon as a reply message arrives.
The Synchro ni zati o n callback interface is defined as follows: package org.apache.camel.spi; import org.apache.camel.Exchange; public interface Synchro ni zati o n {
void onComplete(Exchange exchange);
void onFailure(Exchange exchange);
}
Where the o nC o mpl ete() method is called on receipt of a normal reply and the
o nFai l ure() method is called on receipt of a fault message reply. Only one of these methods gets called back, so you must override both of them to ensure that all types of reply are processed.
The following example shows how to send an exchange to the d i rect: start endpoint, where the reply message is processed in the sub-thread by the Synchro ni zati o nAd apter callback object.
import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.SynchronizationAdapter;
...
Exchange exchange = context.getEndpoint("direct:start").createExchange(); exchange.getIn().setBody("Hello");
Future<Exchange> future = template.asyncCallback("direct:start", exchange, new SynchronizationAdapter() {
@ Override
568
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
public void onComplete(Exchange exchange) {
assertEquals("Hello World", exchange.getIn().getBody());
}
});
Where the Synchro ni zati o nAd apter class is a default implementation of the
Synchro ni zati o n interface, which you can override to provide your own definitions of the
o nC o mpl ete() and o nFai l ure() callback methods.
You still have the option of accessing the reply from the main thread, because the
asyncC al l back() method also returns a Future object—for example:
// Retrieve the reply from the main thread, specifying a timeout
Exchange reply = future.get(10, TimeUnit.SECONDS);
4 6.1.2. Synchronous Send
Ove rvie w
The synchronous send methods are a collection of methods that you can use to invoke a producer endpoint, where the current thread blocks until the method invocation is complete and the reply (if any) has been received. These methods are compatible with any kind of message exchange protocol.
Se nd an e xchange
The basic send () method is a general-purpose method that sends the contents of an
Exchang e object to an endpoint, using the message exchange pattern (MEP) of the exchange. The return value is the exchange that you get after it has been processed by the producer endpoint (possibly containing an Out message, depending on the MEP).
There are three varieties of send () method for sending an exchange that let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object.
Exchange send(Exchange exchange);
Exchange send(String endpointUri, Exchange exchange);
Exchange send(Endpoint endpoint, Exchange exchange);
Se nd an e xchange po pulat e d by a pro ce sso r
A simple variation of the general send () method is to use a processor to populate a default
exchange, instead of supplying the exchange object explicitly (see the section called
“ Synchronous invocation with a processor” for details).
569
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The send () methods for sending an exchange populated by a processor let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object. In addition, you can optionally specify the exchange's MEP by supplying the pattern argument, instead of accepting the default.
Exchange send(Processor processor);
Exchange send(String endpointUri, Processor processor);
Exchange send(Endpoint endpoint, Processor processor);
Exchange send(
String endpointUri,
ExchangePattern pattern,
Processor processor
);
Exchange send(
Endpoint endpoint,
ExchangePattern pattern,
Processor processor
);
Se nd a m e ssage bo dy
If you are only concerned with the contents of the message body that you want to send, you can use the send Bo d y() methods to provide the message body as an argument and let the producer template take care of inserting the body into a default exchange object.
The send Bo d y() methods let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object. In addition, you can optionally specify the exchange's MEP by supplying the pattern argument, instead of accepting the default. The methods without a pattern argument return vo i d (even though the invocation might give rise to a reply in some cases); and the methods with a pattern argument return either the body of the Out message (if there is one) or the body of the In message (otherwise).
void sendBody(Object body); void sendBody(String endpointUri, Object body); void sendBody(Endpoint endpoint, Object body);
Object sendBody(
String endpointUri,
ExchangePattern pattern,
Object body
);
Object sendBody(
Endpoint endpoint,
ExchangePattern pattern,
Object body
);
Se nd a m e ssage bo dy and he ade r(s)
For testing purposes, it is often interesting to try out the effect of a single header setting and the send Bo d yAnd Head er() methods are useful for this kind of header testing. You supply the message body and header setting as arguments to send Bo d yAnd Head er() and let the
570
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
producer template take care of inserting the body and header setting into a default exchange object.
The send Bo d yAnd Head er() methods let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object. In addition, you can optionally specify the exchange's MEP by supplying the pattern argument, instead of accepting the default. The methods without a pattern argument return
vo i d (even though the invocation might give rise to a reply in some cases); and the methods with a pattern argument return either the body of the Out message (if there is one) or the body of the In message (otherwise).
void sendBodyAndHeader(
Object body,
String header,
Object headerValue
); void sendBodyAndHeader(
String endpointUri,
Object body,
String header,
Object headerValue
); void sendBodyAndHeader(
Endpoint endpoint,
Object body,
String header,
Object headerValue
);
Object sendBodyAndHeader(
String endpointUri,
ExchangePattern pattern,
Object body,
String header,
Object headerValue
);
Object sendBodyAndHeader(
Endpoint endpoint,
ExchangePattern pattern,
Object body,
String header,
Object headerValue
);
The send Bo d yAnd Head ers() methods are similar to the send Bo d yAnd Head er() methods, except that instead of supplying just a single header setting, these methods allow you to specify a complete hash map of header settings.
void sendBodyAndHeaders(
Object body,
Map<String, Object> headers
); void sendBodyAndHeaders(
String endpointUri,
Object body,
571
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Map<String, Object> headers
); void sendBodyAndHeaders(
Endpoint endpoint,
Object body,
Map<String, Object> headers
);
Object sendBodyAndHeaders(
String endpointUri,
ExchangePattern pattern,
Object body,
Map<String, Object> headers
);
Object sendBodyAndHeaders(
Endpoint endpoint,
ExchangePattern pattern,
Object body,
Map<String, Object> headers
);
572
Se nd a m e ssage bo dy and e xchange pro pe rt y
You can try out the effect of setting a single exchange property using the
send Bo d yAnd P ro perty() methods. You supply the message body and property setting as arguments to send Bo d yAnd P ro perty() and let the producer template take care of inserting the body and exchange property into a default exchange object.
The send Bo d yAnd P ro perty() methods let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object. In addition, you can optionally specify the exchange's MEP by supplying the pattern argument, instead of accepting the default. The methods without a pattern argument return
vo i d (even though the invocation might give rise to a reply in some cases); and the methods with a pattern argument return either the body of the Out message (if there is one) or the body of the In message (otherwise).
void sendBodyAndProperty(
Object body,
String property,
Object propertyValue
); void sendBodyAndProperty(
String endpointUri,
Object body,
String property,
Object propertyValue
); void sendBodyAndProperty(
Endpoint endpoint,
Object body,
String property,
Object propertyValue
);
Object sendBodyAndProperty(
String endpoint,
ExchangePattern pattern,
Object body,
String property,
Object propertyValue
);
Object sendBodyAndProperty(
Endpoint endpoint,
ExchangePattern pattern,
Object body,
String property,
Object propertyValue
);
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
4 6.1.3. Synchronous Request wit h InOut Pat t ern
Ove rvie w
The synchronous request methods are similar to the synchronous send methods, except that the request methods force the message exchange pattern to be InOut (conforming to request/reply semantics). Hence, it is generally convenient to use a synchronous request method, if you expect to receive a reply from the producer endpoint.
Re que st an e xchange po pulat e d by a pro ce sso r
The basic req uest() method is a general-purpose method that uses a processor to populate a default exchange and forces the message exchange pattern to be InOut (so that the invocation obeys request/reply semantics). The return value is the exchange that you get after it has been processed by the producer endpoint, where the Out message contains the reply message.
The req uest() methods for sending an exchange populated by a processor let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Exchange request(String endpointUri, Processor processor);
Exchange request(Endpoint endpoint, Processor processor);
Re que st a m e ssage bo dy
If you are only concerned with the contents of the message body in the request and in the reply, you can use the req uestBo d y() methods to provide the request message body as an argument and let the producer template take care of inserting the body into a default exchange object.
The req uestBo d y() methods let you specify the target endpoint in one of the following ways: as the default endpoint, as an endpoint URI, or as an End po i nt object. The return value is the body of the reply message (Out message body), which can either be returned as plain O bject or converted to a specific type, T , using the built-in type converters (see
Section 43.3, “ Built-In Type Converters”
).
573
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Object requestBody(Object body);
<T> T requestBody(Object body, Class<T> type);
Object requestBody(
String endpointUri,
Object body
);
<T> T requestBody(
String endpointUri,
Object body,
Class<T> type
);
Object requestBody(
Endpoint endpoint,
Object body
);
<T> T requestBody(
Endpoint endpoint,
Object body,
Class<T> type
);
Re que st a m e ssage bo dy and he ade r(s)
You can try out the effect of setting a single header value using the
req uestBo d yAnd Head er() methods. You supply the message body and header setting as arguments to req uestBo d yAnd Head er() and let the producer template take care of inserting the body and exchange property into a default exchange object.
574
The req uestBo d yAnd Head er() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object. The return value is the body of the reply message (Out message body), which can either be returned as plain O bject or
converted to a specific type, T , using the built-in type converters (see Section 43.3, “ Built-In
Object requestBodyAndHeader(
String endpointUri,
Object body,
String header,
Object headerValue
);
<T> T requestBodyAndHeader(
String endpointUri,
Object body,
String header,
Object headerValue,
Class<T> type
);
Object requestBodyAndHeader(
Endpoint endpoint,
Object body,
String header,
Object headerValue
);
<T> T requestBodyAndHeader(
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
Endpoint endpoint,
Object body,
String header,
Object headerValue,
Class<T> type
);
The req uestBo d yAnd Head ers() methods are similar to the req uestBo d yAnd Head er() methods, except that instead of supplying just a single header setting, these methods allow you to specify a complete hash map of header settings.
Object requestBodyAndHeaders(
String endpointUri,
Object body,
Map<String, Object> headers
);
<T> T requestBodyAndHeaders(
String endpointUri,
Object body,
Map<String, Object> headers,
Class<T> type
);
Object requestBodyAndHeaders(
Endpoint endpoint,
Object body,
Map<String, Object> headers
);
<T> T requestBodyAndHeaders(
Endpoint endpoint,
Object body,
Map<String, Object> headers,
Class<T> type
);
4 6.1.4 . Asynchronous Send
Ove rvie w
The producer template provides a variety of methods for invoking a producer endpoint asynchronously, so that the main thread does not block while waiting for the invocation to complete and the reply message can be retrieved at a later time. The asynchronous send methods described in this section are compatible with any kind of message exchange protocol.
Se nd an e xchange
The basic asyncSend () method takes an Exchang e argument and invokes an endpoint asynchronously, using the message exchange pattern (MEP) of the specified exchange. The return value is a java. uti l . co ncurrent. Future object, which is a ticket you can use to collect the reply message at a later time—for details of how to obtain the return value from the
Future object, see the section called “ Asynchronous invocation”
.
575
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The following asyncSend () methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Exchange> asyncSend(String endpointUri, Exchange exchange);
Future<Exchange> asyncSend(Endpoint endpoint, Exchange exchange);
Se nd an e xchange po pulat e d by a pro ce sso r
A simple variation of the general asyncSend () method is to use a processor to populate a default exchange, instead of supplying the exchange object explicitly.
The following asyncSend () methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Exchange> asyncSend(String endpointUri, Processor processor);
Future<Exchange> asyncSend(Endpoint endpoint, Processor processor);
Se nd a m e ssage bo dy
If you are only concerned with the contents of the message body that you want to send, you can use the asyncSend Bo d y() methods to send a message body asynchronously and let the producer template take care of inserting the body into a default exchange object.
The asyncSend Bo d y() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Object> asyncSendBody(String endpointUri, Object body);
Future<Object> asyncSendBody(Endpoint endpoint, Object body);
4 6.1.5. Asynchronous Request wit h InOut Pat t ern
Ove rvie w
The asynchronous request methods are similar to the asynchronous send methods, except that the request methods force the message exchange pattern to be InOut (conforming to request/reply semantics). Hence, it is generally convenient to use an asynchronous request method, if you expect to receive a reply from the producer endpoint.
Re que st a m e ssage bo dy
If you are only concerned with the contents of the message body in the request and in the reply, you can use the req uestBo d y() methods to provide the request message body as an argument and let the producer template take care of inserting the body into a default exchange object.
The asyncR eq uestBo d y() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object. The return value that is
576
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
retrievable from the Future object is the body of the reply message (Out message body), which can be returned either as a plain O bject or converted to a specific type, T , using a
built-in type converter (see the section called “ Asynchronous invocation”
).
Future<Object> asyncRequestBody(
String endpointUri,
Object body
);
<T> Future<T> asyncRequestBody(
String endpointUri,
Object body,
Class<T> type
);
Future<Object> asyncRequestBody(
Endpoint endpoint,
Object body
);
<T> Future<T> asyncRequestBody(
Endpoint endpoint,
Object body,
Class<T> type
);
Re que st a m e ssage bo dy and he ade r(s)
You can try out the effect of setting a single header value using the
asyncR eq uestBo d yAnd Head er() methods. You supply the message body and header setting as arguments to asyncR eq uestBo d yAnd Head er() and let the producer template take care of inserting the body and exchange property into a default exchange object.
The asyncR eq uestBo d yAnd Head er() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object. The return value that is retrievable from the Future object is the body of the reply message (Out message body), which can be returned either as a plain O bject or converted to a specific type, T , using a
built-in type converter (see the section called “ Asynchronous invocation”
).
Future<Object> asyncRequestBodyAndHeader(
String endpointUri,
Object body,
String header,
Object headerValue
);
<T> Future<T> asyncRequestBodyAndHeader(
String endpointUri,
Object body,
String header,
Object headerValue,
Class<T> type
);
Future<Object> asyncRequestBodyAndHeader(
Endpoint endpoint,
Object body,
String header,
Object headerValue
577
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
);
<T> Future<T> asyncRequestBodyAndHeader(
Endpoint endpoint,
Object body,
String header,
Object headerValue,
Class<T> type
);
The asyncR eq uestBo d yAnd Head ers() methods are similar to the
asyncR eq uestBo d yAnd Head er() methods, except that instead of supplying just a single header setting, these methods allow you to specify a complete hash map of header settings.
Future<Object> asyncRequestBodyAndHeaders(
String endpointUri,
Object body,
Map<String, Object> headers
);
<T> Future<T> asyncRequestBodyAndHeaders(
String endpointUri,
Object body,
Map<String, Object> headers,
Class<T> type
);
Future<Object> asyncRequestBodyAndHeaders(
Endpoint endpoint,
Object body,
Map<String, Object> headers
);
<T> Future<T> asyncRequestBodyAndHeaders(
Endpoint endpoint,
Object body,
Map<String, Object> headers,
Class<T> type
);
4 6.1.6. Asynchronous Send wit h Callback
Ove rvie w
The producer template also provides the option of processing the reply message in the same sub-thread that is used to invoke the producer endpoint. In this case, you provide a callback object, which automatically gets invoked in the sub-thread as soon as the reply message is received. In other words, the asynchronous send with callback methods enable you to initiate an invocation in your main thread and then have all of the associated processing— invocation of the producer endpoint, waiting for a reply and processing the reply—occur asynchronously in a sub-thread.
Se nd an e xchange
578
The basic asyncC al l back() method takes an Exchang e argument and invokes an endpoint asynchronously, using the message exchange pattern (MEP) of the specified
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
exchange. This method is similar to the asyncSend () method for exchanges, except that it takes an additional o rg . apache. camel . spi . Synchro ni zati o n argument, which is a callback interface with two methods: o nC o mpl ete() and o nFai l ure(). For details of how
The following asyncC al l back() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Exchange> asyncCallback(
String endpointUri,
Exchange exchange,
Synchronization onCompletion
);
Future<Exchange> asyncCallback(
Endpoint endpoint,
Exchange exchange,
Synchronization onCompletion
);
Se nd an e xchange po pulat e d by a pro ce sso r
The asyncC al l back() method for processors calls a processor to populate a default exchange and forces the message exchange pattern to be InOut (so that the invocation obeys request/reply semantics).
The following asyncC al l back() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Exchange> asyncCallback(
String endpointUri,
Processor processor,
Synchronization onCompletion
);
Future<Exchange> asyncCallback(
Endpoint endpoint,
Processor processor,
Synchronization onCompletion
);
Se nd a m e ssage bo dy
If you are only concerned with the contents of the message body that you want to send, you can use the asyncC al l backSend Bo d y() methods to send a message body asynchronously and let the producer template take care of inserting the body into a default exchange object.
The asyncC al l backSend Bo d y() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Object> asyncCallbackSendBody(
579
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
String endpointUri,
Object body,
Synchronization onCompletion
);
Future<Object> asyncCallbackSendBody(
Endpoint endpoint,
Object body,
Synchronization onCompletion
);
Re que st a m e ssage bo dy
If you are only concerned with the contents of the message body in the request and in the reply, you can use the asyncC al l backR eq uestBo d y() methods to provide the request message body as an argument and let the producer template take care of inserting the body into a default exchange object.
The asyncC al l backR eq uestBo d y() methods let you specify the target endpoint in one of the following ways: as an endpoint URI, or as an End po i nt object.
Future<Object> asyncCallbackRequestBody(
String endpointUri,
Object body,
Synchronization onCompletion
);
Future<Object> asyncCallbackRequestBody(
Endpoint endpoint,
Object body,
Synchronization onCompletion
);
4 6.2. USING T HE CONSUMER T EMPLAT E
Overview
The consumer template provides methods for polling a consumer endpoint in order to receive incoming messages. You can choose to receive the incoming message either in the form of an exchange object or in the form of a message body (where the message body can be cast to a particular type using a built-in type converter).
Example of polling exchanges
You can use a consumer template to poll a consumer endpoint for exchanges using one of the following polling methods: blocking recei ve(); recei ve() with a timeout; or
recei veNo Wai t(), which returns immediately. Because a consumer endpoint represents a service, it is also essential to start the service thread by calling start() before you attempt to poll for exchanges.
580
The following example shows how to poll an exchange from the sed a: fo o consumer endpoint using the blocking recei ve() method:
CHAPT ER 4 6 . PRO DUCER AND CO NSUMER T EMPLAT ES
import org.apache.camel.ProducerTemplate; import org.apache.camel.ConsumerTemplate; import org.apache.camel.Exchange;
...
ProducerTemplate template = context.createProducerTemplate();
ConsumerTemplate consumer = context.createConsumerTemplate();
// Start the consumer service consumer.start();
...
template.sendBody("seda:foo", "Hello");
Exchange out = consumer.recei ve("seda:foo");
...
// Stop the consumer service consumer.sto p();
Where the consumer template instance, co nsumer, is instantiated using the
C amel C o ntext. createC o nsumerT empl ate() method and the consumer service thread is started by calling C o nsumerT empl ate. start().
Example of polling message bodies
You can also poll a consumer endpoint for incoming message bodies using one of the following methods: blocking recei veBo d y(); recei veBo d y() with a timeout; or
recei veBo d yNo Wai t(), which returns immediately. As in the previous example, it is also essential to start the service thread by calling start() before you attempt to poll for exchanges.
The following example shows how to poll an incoming message body from the sed a: fo o consumer endpoint using the blocking recei veBo d y() method: import org.apache.camel.ProducerTemplate; import org.apache.camel.ConsumerTemplate;
...
ProducerTemplate template = context.createProducerTemplate();
ConsumerTemplate consumer = context.createConsumerTemplate();
// Start the consumer service consumer.start();
...
template.sendBody("seda:foo", "Hello");
Object body = consumer.recei veBo d y("seda:foo");
...
// Stop the consumer service consumer.sto p();
Met hods for polling exchanges
581
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
There are three basic methods for polling exchanges from a consumer endpoint: recei ve() without a timeout blocks indefinitely; recei ve() with a timeout blocks for the specified period of milliseconds; and recei veNo Wai t() is non-blocking. You can specify the consumer endpoint either as an endpoint URI or as an End po i nt instance.
Exchange receive(String endpointUri);
Exchange receive(String endpointUri, long timeout);
Exchange receiveNoWait(String endpointUri);
Exchange receive(Endpoint endpoint);
Exchange receive(Endpoint endpoint, long timeout);
Exchange receiveNoWait(Endpoint endpoint);
Met hods for polling message bodies
There are three basic methods for polling message bodies from a consumer endpoint:
recei veBo d y() without a timeout blocks indefinitely; recei veBo d y() with a timeout blocks for the specified period of milliseconds; and recei veBo d yNo Wai t() is nonblocking. You can specify the consumer endpoint either as an endpoint URI or as an
End po i nt instance. Moreover, by calling the templating forms of these methods, you can convert the returned body to a particular type, T , using a built-in type converter.
Object receiveBody(String endpointUri);
Object receiveBody(String endpointUri, long timeout);
Object receiveBodyNoWait(String endpointUri);
Object receiveBody(Endpoint endpoint);
Object receiveBody(Endpoint endpoint, long timeout);
Object receiveBodyNoWait(Endpoint endpoint);
<T> T receiveBody(String endpointUri, Class<T> type);
<T> T receiveBody(String endpointUri, long timeout, Class<T> type);
<T> T receiveBodyNoWait(String endpointUri, Class<T> type);
<T> T receiveBody(Endpoint endpoint, Class<T> type);
<T> T receiveBody(Endpoint endpoint, long timeout, Class<T> type);
<T> T receiveBodyNoWait(Endpoint endpoint, Class<T> type);
582
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
CHAPTER 47. IMPLEMENTING A COMPONENT
Ab st ract
This chapter provides a general overview of the approaches can be used to implement a
Apache Camel component.
4 7.1. COMPONENT ARCHIT ECT URE
4 7.1.1. Fact ory Pat t erns for a Component
Ove rvie w
A Apache Camel component consists of a set of classes that are related to each other through a factory pattern. The primary entry point to a component is the C o mpo nent object itself (an instance of o rg . apache. camel . C o mpo nent type). You can use the C o mpo nent object as a factory to create End po i nt objects, which in turn act as factories for creating
C o nsumer, P ro d ucer, and Exchang e objects. These relationships are summarized in
Figure 47.1, “ Component Factory Patterns”
Fig u re 4 7.1. Co mp o n en t Fact o ry Pat t ern s
Co m po ne nt
A component implementation is an endpoint factory. The main task of a component implementor is to implement the C o mpo nent. createEnd po i nt() method, which is responsible for creating new endpoints on demand.
Each kind of component must be associated with a component prefix that appears in an endpoint URI. For example, the file component is usually associated with the file prefix, which can be used in an endpoint URI like file://tmp/messages/input. When you install a new component in Apache Camel, you must define the association between a particular component prefix and the name of the class that implements the component.
583
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Endpo int
Each endpoint instance encapsulates a particular endpoint URI. Every time Apache Camel encounters a new endpoint URI, it creates a new endpoint instance. An endpoint object is also a factory for creating consumer endpoints and producer endpoints.
Endpoints must implement the o rg . apache. camel . End po i nt interface. The End po i nt interface defines the following factory methods:
createC o nsumer() and createP o l l i ng C o nsumer()—Creates a consumer endpoint, which represents the source endpoint at the beginning of a route.
createP ro d ucer()—Creates a producer endpoint, which represents the target endpoint at the end of a route.
createExchang e()—Creates an exchange object, which encapsulates the messages passed up and down the route.
Co nsum e r
Consumer endpoints consume requests. They always appear at the start of a route and they encapsulate the code responsible for receiving incoming requests and dispatching outgoing replies. From a service-oriented prospective a consumer represents a service.
Consumers must implement the o rg . apache. camel . C o nsumer interface. There are a number of different patterns you can follow when implementing a consumer. These patterns
are described in Section 47.1.3, “ Consumer Patterns and Threading”
.
Pro duce r
Producer endpoints produce requests. They always appears at the end of a route and they encapsulate the code responsible for dispatching outgoing requests and receiving incoming replies. From a service-oriented prospective a producer represents a service consumer.
Producers must implement the o rg . apache. camel . P ro d ucer interface. You can optionally implement the producer to support an asynchronous style of processing. See
Section 47.1.4, “ Asynchronous Processing”
for details.
Exchange
Exchange objects encapsulate a related set of messages. For example, one kind of message exchange is a synchronous invocation, which consists of a request message and its related reply.
Exchanges must implement the o rg . apache. camel . Exchang e interface. The default implementation, D efaul tExchang e, is sufficient for many component implementations.
However, if you want to associated extra data with the exchanges or have the exchanges preform additional processing, it can be useful to customize the exchange implementation.
584
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
Me ssage
There are two different message slots in an Exchang e object:
In message—holds the current message.
Out message—temporarily holds a reply message.
All of the message types are represented by the same Java object,
o rg . apache. camel . Messag e. It is not always necessary to customize the message implementation—the default implementation, D efaul tMessag e, is usually adequate.
4 7.1.2. Using a Component in a Rout e
Ove rvie w
A Apache Camel route is essentially a pipeline of processors, of
o rg . apache. camel . P ro cesso r type. Messages are encapsulated in an exchange object, E, which gets passed from node to node by invoking the pro cess() method. The
architecture of the processor pipeline is illustrated in Figure 47.2, “ Consumer and Producer
Fig u re 4 7.2. Co n su mer an d Pro d u cer In st an ces in a Ro u t e
So urce e ndpo int
At the start of the route, you have the source endpoint, which is represented by an
o rg . apache. camel . C o nsumer object. The source endpoint is responsible for accepting incoming request messages and dispatching replies. When constructing the route, Apache
Camel creates the appropriate C o nsumer type based on the component prefix from the
endpoint URI, as described in Section 47.1.1, “ Factory Patterns for a Component” .
Pro ce sso rs
Each intermediate node in the pipeline is represented by a processor object (implementing the o rg . apache. camel . P ro cesso r interface). You can insert either standard processors
(for example, fi l ter, thro ttl er, or d el ayer) or insert your own custom processor implementations.
T arge t e ndpo int
585
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
At the end of the route is the target endpoint, which is represented by an
o rg . apache. camel . P ro d ucer object. Because it comes at the end of a processor pipeline, the producer is also a processor object (implementing the
o rg . apache. camel . P ro cesso r interface). The target endpoint is responsible for sending outgoing request messages and receiving incoming replies. When constructing the route, Apache Camel creates the appropriate P ro d ucer type based on the component prefix from the endpoint URI.
4 7.1.3. Consumer Pat t erns and T hreading
Ove rvie w
The pattern used to implement the consumer determines the threading model used in processing the incoming exchanges. Consumers can be implemented using one of the following patterns:
Event-driven pattern —The consumer is driven by an external thread.
Scheduled poll pattern —The consumer is driven by a dedicated thread pool.
—The threading model is left undefined.
Eve nt -drive n pat t e rn
In the event-driven pattern, the processing of an incoming request is initiated when another part of the application (typically a third-party library) calls a method implemented by the consumer. A good example of an event-driven consumer is the Apache Camel JMX component, where events are initiated by the JMX library. The JMX library calls the
hand l eNo ti fi cati o n() method to initiate request processing—see
“ JMXConsumer Implementation” for details.
Figure 47.3, “ Event-Driven Consumer”
shows an outline of the event-driven consumer pattern. In this example, it is assumed that processing is triggered by a call to the notify() method.
Fig u re 4 7.3. Even t - Driven Co n su mer
586
The event-driven consumer processes incoming requests as follows:
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
1. The consumer must implement a method to receive the incoming event (in
Figure 47.3, “ Event-Driven Consumer”
this is represented by the notify() method).
The thread that calls notify() is normally a separate part of the application, so the consumer's threading policy is externally driven.
For example, in the case of the JMX consumer implementation, the consumer implements the No ti fi cati o nLi stener. hand l eNo ti fi cati o n() method to receive notifications from JMX. The threads that drive the consumer processing are created within the JMX layer.
2. In the body of the notify() method, the consumer first converts the incoming event into an exchange object, E, and then calls pro cess() on the next processor in the route, passing the exchange object as its argument.
Sche dule d po ll pat t e rn
In the scheduled poll pattern, the consumer retrieves incoming requests by checking at regular time intervals whether or not a request has arrived. Checking for requests is scheduled automatically by a built-in timer class, the scheduled executor service, which is a standard pattern provided by the java.util.concurrent library. The scheduled executor service executes a particular task at timed intervals and it also manages a pool of threads, which are used to run the task instances.
Figure 47.4, “ Scheduled Poll Consumer” shows an outline of the scheduled poll consumer
pattern.
Fig u re 4 7.4 . Sch ed u led Po ll Co n su mer
The scheduled poll consumer processes incoming requests as follows:
1. The scheduled executor service has a pool of threads at its disposal, that can be used to initiate consumer processing. After each scheduled time interval has elapsed, the scheduled executor service attempts to grab a free thread from its pool (there are five threads in the pool by default). If a free thread is available, it uses that thread to call the po l l () method on the consumer.
587
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
2. The consumer's po l l () method is intended to trigger processing of an incoming request. In the body of the po l l () method, the consumer attempts to retrieve an incoming message. If no request is available, the po l l () method returns immediately.
3. If a request message is available, the consumer inserts it into an exchange object and then calls pro cess() on the next processor in the route, passing the exchange object as its argument.
Po lling pat t e rn
In the polling pattern, processing of an incoming request is initiated when a third-party calls one of the consumer's polling methods:
recei ve() recei veNo Wai t() recei ve(l o ng ti meo ut)
It is up to the component implementation to define the precise mechanism for initiating calls on the polling methods. This mechanism is not specified by the polling pattern.
Figure 47.5, “ Polling Consumer”
shows an outline of the polling consumer pattern.
Fig u re 4 7.5. Po llin g Co n su mer
588
The polling consumer processes incoming requests as follows:
1. Processing of an incoming request is initiated whenever one of the consumer's polling methods is called. The mechanism for calling these polling methods is implementation defined.
2. In the body of the recei ve() method, the consumer attempts to retrieve an incoming
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
request message. If no message is currently available, the behavior depends on which receive method was called.
recei veNo Wai t() returns immediately
recei ve(l o ng ti meo ut) waits for the specified timeout interval
before returning
recei ve() waits until a message is received
3. If a request message is available, the consumer inserts it into an exchange object and then calls pro cess() on the next processor in the route, passing the exchange object as its argument.
4 7.1.4 . Asynchronous Processing
Ove rvie w
Producer endpoints normally follow a synchronous pattern when processing an exchange.
When the preceding processor in a pipeline calls pro cess() on a producer, the
pro cess() method blocks until a reply is received. In this case, the processor's thread remains blocked until the producer has completed the cycle of sending the request and receiving the reply.
Sometimes, however, you might prefer to decouple the preceding processor from the producer, so that the processor's thread is released immediately and the pro cess() call does not block. In this case, you should implement the producer using an asynchronous pattern, which gives the preceding processor the option of invoking a non-blocking version of the pro cess() method.
To give you an overview of the different implementation options, this section describes both the synchronous and the asynchronous patterns for implementing a producer endpoint.
Synchro no us pro duce r
Figure 47.6, “ Synchronous Producer”
shows an outline of a synchronous producer, where the preceding processor blocks until the producer has finished processing the exchange.
Fig u re 4 7.6 . Syn ch ro n o u s Pro d u cer
589
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The synchronous producer processes an exchange as follows:
1. The preceding processor in the pipeline calls the synchronous pro cess() method on the producer to initiate synchronous processing. The synchronous pro cess() method takes a single exchange argument.
2. In the body of the pro cess() method, the producer sends the request (In message) to the endpoint.
3. If required by the exchange pattern, the producer waits for the reply (Out message) to arrive from the endpoint. This step can cause the pro cess() method to block indefinitely. However, if the exchange pattern does not mandate a reply, the
pro cess() method can return immediately after sending the request.
4. When the pro cess() method returns, the exchange object contains the reply from the synchronous call (an Out message message).
Asynchro no us pro duce r
Figure 47.7, “ Asynchronous Producer” shows an outline of an asynchronous producer,
where the producer processes the exchange in a sub-thread, and the preceding processor is not blocked for any significant length of time.
Fig u re 4 7.7. Asyn ch ro n o u s Pro d u cer
590
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
The asynchronous producer processes an exchange as follows:
1. Before the processor can call the asynchronous pro cess() method, it must create an asynchronous callback object, which is responsible for processing the exchange on the return portion of the route. For the asynchronous callback, the processor must implement a class that inherits from the AsyncC al l back interface.
2. The processor calls the asynchronous pro cess() method on the producer to initiate asynchronous processing. The asynchronous pro cess() method takes two arguments: an exchange object a synchronous callback object
3. In the body of the pro cess() method, the producer creates a R unnabl e object that encapsulates the processing code. The producer then delegates the execution of this
R unnabl e object to a sub-thread.
4. The asynchronous pro cess() method returns, thereby freeing up the processor's thread. The exchange processing continues in a separate sub-thread.
5. The R unnabl e object sends the In message to the endpoint.
6. If required by the exchange pattern, the R unnabl e object waits for the reply (Out or
Fault message) to arrive from the endpoint. The R unnabl e object remains blocked until the reply is received.
591
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
7. After the reply arrives, the R unnabl e object inserts the reply (Out message) into the exchange object and then calls d o ne() on the asynchronous callback object. The asynchronous callback is then responsible for processing the reply message
(executed in the sub-thread).
4 7.2. HOW T O IMPLEMENT A COMPONENT
Overview
This section gives a brief overview of the steps required to implement a custom Apache
Camel component.
Which int erfaces do you need t o implement ?
When implementing a component, it is usually necessary to implement the following Java interfaces:
o rg . apache. camel . C o mpo nent o rg . apache. camel . End po i nt o rg . apache. camel . C o nsumer o rg . apache. camel . P ro d ucer
In addition, it can also be necessary to implement the following Java interfaces:
o rg . apache. camel . Exchang e o rg . apache. camel . Messag e
Implement at ion st eps
You typically implement a custom component as follows:
1. Implement the Component interface—A component object acts as an endpoint factory.
You extend the D efaul tC o mpo nent class and implement the createEnd po i nt() method.
See Chapter 48, Component Interface
.
2. Implement the Endpoint interface—An endpoint represents a resource identified by a specific URI. The approach taken when implementing an endpoint depends on whether the consumers follow an event-driven pattern, a scheduled poll pattern, or a
polling pattern.
592
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
For an event-driven pattern, implement the endpoint by extending the
D efaul tEnd po i nt class and implementing the following methods:
createP ro d ucer() createC o nsumer()
For a scheduled poll pattern, implement the endpoint by extending the
Sched ul ed P o l l End po i nt class and implementing the following methods:
createP ro d ucer() createC o nsumer()
For a polling pattern, implement the endpoint by extending the
D efaul tP o l l i ng End po i nt class and implementing the following methods:
createP ro d ucer() createP o l l C o nsumer()
See Chapter 49, Endpoint Interface
.
3. Implement the Consumer interface—There are several different approaches you can take to implementing a consumer, depending on which pattern you need to implement (event-driven, scheduled poll, or polling). The consumer implementation is also crucially important for determining the threading model used for processing a message exchange.
See Section 50.2, “ Implementing the Consumer Interface”
.
4. Implement the Producer interface—To implement a producer, you extend the
D efaul tP ro d ucer class and implement the pro cess() method.
See Chapter 51, Producer Interface
.
5. Optionally implement the Exchange or the Message interface—The default implementations of Exchang e and Messag e can be used directly, but occasionally, you might find it necessary to customize these types.
See Chapter 52, Exchange Interface
and Chapter 53, Message Interface
.
Inst alling and configuring t he component
You can install a custom component in one of the following ways:
Add the component directly to the CamelContext—The C amel C o ntext. ad d C o mpo nent()
593
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Add the component directly to the CamelContext—The C amel C o ntext. ad d C o mpo nent() method adds a component programatically.
Add the component using Spring configuration—The standard Spring bean element creates a component instance. The bean's i d attribute implicitly defines the component prefix. For
details, see Section 47.3.2, “ Configuring a Component” .
Configure Apache Camel to auto-discover the component—Auto-discovery, ensures that
Apache Camel automatically loads the component on demand. For details, see
Section 47.3.1, “ Setting Up Auto-Discovery” .
4 7.3. AUT O-DISCOVERY AND CONFIGURAT ION
4 7.3.1. Set t ing Up Aut o-Discovery
Ove rvie w
Auto-discovery is a mechanism that enables you to dynamically add components to your
Apache Camel application. The component URI prefix is used as a key to load components on demand. For example, if Apache Camel encounters the endpoint URI, activemq://MyQName, and the ActiveMQ endpoint is not yet loaded, Apache Camel searches for the component identified by the activemq prefix and dynamically loads the component.
Availabilit y o f co m po ne nt classe s
Before configuring auto-discovery, you must ensure that your custom component classes are accessible from your current classpath. Typically, you bundle the custom component classes into a JAR file, and add the JAR file to your classpath.
Co nfiguring aut o -disco ve ry
To enable auto-discovery of your component, create a Java properties file named after the component prefix, component-prefix, and store that file in the following location:
/META-INF/services/org/apache/camel/component/component-prefix
The component-prefix properties file must contain the following property setting: class=component-class-name
Where component-class-name is the fully-qualified name of your custom component class.
You can also define additional system property settings in this file.
Exam ple
594
For example, you can enable auto-discovery for the Apache Camel FTP component by creating the following Java properties file:
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
/META-INF/services/org/apache/camel/component/ftp
Which contains the following Java property setting: class=org.apache.camel.component.file.remote.RemoteFileComponent
No t e
The Java properties file for the FTP component is already defined in the JAR file,
camel -ftp-Version. jar.
4 7.3.2. Configuring a Component
Ove rvie w
You can add a component by configuring it in the Apache Camel Spring configuration file,
MET A-INF/spri ng /camel -co ntext. xml . To find the component, the component's URI prefix is matched against the ID attribute of a bean element in the Spring configuration. If the component prefix matches a bean element ID, Apache Camel instantiates the referenced class and injects the properties specified in the Spring configuration.
No t e
This mechanism has priority over auto-discovery. If the CamelContext finds a
Spring bean with the requisite ID, it will not attempt to find the component using auto-discovery.
De fine be an pro pe rt ie s o n yo ur co m po ne nt class
If there are any properties that you want to inject into your component class, define them as bean properties. For example: public class CustomComponent extends
DefaultComponent<CustomExchange> {
...
PropType getProperty() { ... }
void setProperty(PropType v) { ... }
}
The g etProperty() method and the setProperty() method access the value of property.
Co nfigure t he co m po ne nt in Spring
To configure a component in Spring, edit the configuration file, MET A-
INF/spri ng /camel -co ntext. xml , as shown in
595
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Examp le 4 7.1. Co n f ig u rin g a Co mp o n en t in Sp rin g
<?xml version= "1.0" encoding= "UTF-8" ?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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" >
<package>
RouteBuilderPackage
</package>
</camelContext>
<bean id= "component-prefix" class= "component-class-name" >
<property name= "property" value= "propertyValue" />
</bean>
</beans>
596
The bean element with ID component-prefix configures the component-class-name component.
You can inject properties into the component instance using pro perty elements. For example, the pro perty element in the preceding example would inject the value,
propertyValue, into the property property by calling setProperty() on the component.
Exam ple s
Example 47.2, “ JMS Component Spring Configuration”
shows an example of how to configure the Apache Camel's JMS component by defining a bean element with ID equal to
jms. These settings are added to the Spring configuration file, camel -co ntext. xml .
Examp le 4 7.2. JMS Co mp o n en t Sp rin g Co n f ig u rat io n
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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">
<package>org.apache.camel.example.spring</package>
</camelContext>
<bean id="jms"
CHAPT ER 4 7 . IMPLEMENT ING A CO MPO NENT
class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL"
value="vm://localhost?
broker.persistent=false& amp;broker.useJmx=false"/>
</bean>
</property>
</bean>
</beans>
The C amel C o ntext automatically instantiates any R o uteBui l d er classes that it finds in the specified Java package, org.apache.camel.example.spring.
The bean element with ID, jms, configures the JMS component. The bean ID corresponds to the component's URI prefix. For example, if a route specifies an endpoint with the URI, jms://MyQName, Apache Camel automatically loads the JMS component using the settings from the jms bean element.
JMS is just a wrapper for a messaging service. You must specify the concrete implementation of the messaging system by setting the co nnecti o nFacto ry property on the JmsC o mpo nent class.
In this example, the concrete implementation of the JMS messaging service is Apache
ActiveMQ. The bro kerUR L property initializes a connection to an ActiveMQ broker instance, where the message broker is embedded in the local Java virtual machine
(JVM). If a broker is not already present in the JVM, ActiveMQ will instantiate it with the options bro ker. persi stent= fal se (the broker does not persist messages) and bro ker. useJmx= fal se (the broker does not open a JMX port).
The timeo ut interval is typ ically sp ecified in milliseco nd s.
597
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 48. COMPONENT INTERFACE
Ab st ract
This chapter describes how to implement the C o mpo nent interface.
4 8.1. T HE COMPONENT INT ERFACE
Overview
To implement a Apache Camel component, you must implement the
o rg . apache. camel . C o mpo nent interface. An instance of C o mpo nent type provides the entry point into a custom component. That is, all of the other objects in a component are
ultimately accessible through the C o mpo nent instance. Figure 48.1, “ Component
Inheritance Hierarchy” shows the relevant Java interfaces and classes that make up the
C o mpo nent inheritance hierarchy.
Fig u re 4 8.1. Co mp o n en t In h erit an ce Hierarch y
598
T he Component int erface
Example 48.1, “ Component Interface”
shows the definition of the
o rg . apache. camel . C o mpo nent interface.
Examp le 4 8.1. Co mp o n en t In t erf ace
package org.apache.camel; public interface Component {
CamelContext getCamelContext(); void setCamelContext(CamelContext context);
Endpoint createEndpoint(String uri) throws Exception;
}
Component met hods
The C o mpo nent interface defines the following methods:
CHAPT ER 4 8 . CO MPO NENT INT ERFACE
g etC amel C o ntext() and setC amel C o ntext()—References the C amel C o ntext to which this C o mpo nent belongs. The setC amel C o ntext() method is automatically called when you add the component to a C amel C o ntext.
createEnd po i nt()—The factory method that gets called to create End po i nt instances for this component. The uri parameter is the endpoint URI, which contains the details required to create the endpoint.
4 8.2. IMPLEMENT ING T HE COMPONENT INT ERFACE
T he Default Component class
You implement a new component by extending the
o rg . apache. camel . i mpl . D efaul tC o mpo nent class, which provides some standard functionality and default implementations for some of the methods. In particular, the
D efaul tC o mpo nent class provides support for URI parsing and for creating a scheduled
executor (which is used for the scheduled poll pattern).
URI parsing
The createEnd po i nt(Stri ng uri ) method defined in the base C o mpo nent interface takes a complete, unparsed endpoint URI as its sole argument. The D efaul tC o mpo nent class, on the other hand, defines a three-argument version of the createEnd po i nt() method with the following signature: protected abstract Endpoint createEndpoint(
String uri,
String remaining,
Map parameters
) throws Exception;
uri is the original, unparsed URI; remai ni ng is the part of the URI that remains after stripping off the component prefix at the start and cutting off the query options at the end; and parameters contains the parsed query options. It is this version of the
createEnd po i nt() method that you must override when inheriting from
D efaul tC o mpo nent. This has the advantage that the endpoint URI is already parsed for you.
The following sample endpoint URI for the fi l e component shows how URI parsing works in practice: file:///tmp/messages/foo?delete=true& moveNamePostfix=.old
599
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
For this URI, the following arguments are passed to the three-argument version of
createEnd po i nt():
Argument uri remai ni ng parameters
Sample Value
file:///tmp /messag es/fo o ?d elete=true&mo veNamePo stfix=.o ld
/tmp/messag es/fo o
Two entries are set in
java. uti l . Map
: p arameter
d el ete
is b o o lean
true
p arameter
mo veNameP o stfi x
has the string value,
. o l d
.
Paramet er inject ion
By default, the parameters extracted from the URI query options are injected on the endpoint's bean properties. The D efaul tC o mpo nent class automatically injects the parameters for you.
For example, if you want to define a custom endpoint that supports two URI query options:
d el ete and mo veNameP o stfi x. All you must do is define the corresponding bean methods (getters and setters) in the endpoint class: public class FileEndpoint extends ScheduledPollEndpoint {
...
public boolean isDelete() { return delete;
} public void setDelete( boolean delete) { this .delete = delete;
}
...
public String getMoveNamePostfix() { return moveNamePostfix;
} public void setMoveNamePostfix(String moveNamePostfix) { this .moveNamePostfix = moveNamePostfix;
}
}
Disabling endpoint paramet er inject ion
600
CHAPT ER 4 8 . CO MPO NENT INT ERFACE
If there are no parameters defined on your End po i nt class, you can optimize the process of endpoint creation by disabling endpoint parameter injection. To disable parameter injection on endpoints, override the useIntro specti o nO nEnd po i nt() method and implement it to return fal se, as follows: protected boolean useIntrospectionOnEndpoint() {
return false;
}
No t e
The useIntro specti o nO nEnd po i nt() method does not affect the parameter injection that might be performed on a C o nsumer class. Parameter injection at that level is controlled by the End po i nt. co nfi g ureP ro perti es() method (see
Section 49.2, “ Implementing the Endpoint Interface” ).
Scheduled execut or service
The scheduled executor is used in the scheduled poll pattern, where it is responsible for driving the periodic polling of a consumer endpoint (a scheduled executor is effectively a thread pool implementation).
To instantiate a scheduled executor service, use the Executo rServi ceStrateg y object that is returned by the C amel C o ntext. g etExecuto rServi ceStrateg y() method. For details of the Apache Camel threading model, see
Section 2.8, “ Threading Model” .
No t e
Prior to Apache Camel 2.3, the D efaul tC o mpo nent class provided a
g etExecuto rServi ce() method for creating thread pool instances. Since 2.3, however, the creation of thread pools is now managed centrally by the
Executo rServi ceStrateg y object.
Validat ing t he URI
If you want to validate the URI before creating an endpoint instance, you can override the
val i d ateUR I() method from the D efaul tC o mpo nent class, which has the following signature:
pro tected vo i d val i d ateUR I(Stri ng uri ,
Stri ng path,
Map parameters)
thro ws R eso l veEnd po i ntFai l ed Excepti o n;
If the supplied URI does not have the required format, the implementation of
val i d ateUR I() should throw the
o rg . apache. camel . R eso l veEnd po i ntFai l ed Excepti o n exception.
Creat ing an endpoint
601
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Example 48.2, “ Implementation of createEnd po i nt()”
outlines how to implement the
D efaul tC o mpo nent. createEnd po i nt() method, which is responsible for creating endpoint instances on demand.
Examp le 4 8.2. Imp lemen t at io n o f createEnd po i nt()
public class CustomComponent extends DefaultComponent {
...
protected Endpoint createEndpoint(String uri, String remaining,
Map parameters) throws Exception {
CustomEndpoint result = new CustomEndpoint(uri, this);
// ...
return result;
}
}
602
The CustomComponent is the name of your custom component class, which is defined by extending the D efaul tC o mpo nent class.
When extending D efaul tC o mpo nent, you must implement the createEnd po i nt() method with three arguments (see
the section called “ URI parsing” ).
Create an instance of your custom endpoint type, CustomEndpoint, by calling its constructor. At a minimum, this constructor takes a copy of the original URI string,
uri , and a reference to this component instance, thi s.
Example
Example 48.3, “ FileComponent Implementation”
shows a sample implementation of a
Fi l eC o mpo nent class.
Examp le 4 8.3. FileCo mp o n en t Imp lemen t at io n
package org.apache.camel.component.file; import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.impl.DefaultComponent;
CHAPT ER 4 8 . CO MPO NENT INT ERFACE
import java.io.File; import java.util.Map; public class FileComponent extends DefaultComponent {
public static final String HEADER_FILE_NAME =
"org.apache.camel.file.name";
public FileComponent() {
}
public FileComponent(CamelContext context) {
super(context);
}
protected Endpoint createEndpoint(String uri, String remaining,
Map parameters) throws Exception {
File file = new File(remaining);
FileEndpoint result = new FileEndpoint(file, uri, this);
return result;
}
}
Always define a no-argument constructor for the component class in order to facilitate automatic instantiation of the class.
A constructor that takes the parent C amel C o ntext instance as an argument is convenient when creating a component instance by programming.
The implementation of the Fi l eC o mpo nent. createEnd po i nt() method follows
the pattern described in Example 48.2, “ Implementation of createEnd po i nt()” .
The implementation creates a Fi l eEnd po i nt object.
Synchroniz at ionRout eAware Int erface
Synchro ni zati o nR o uteAware interface allows you to have callbacks before and after the exchange has been routed.
o nBefo reR o ute: Invoked before the exchange has been routed by the given route.
However, this callback may not get invoked, if you add the
Synchro ni zati o nR o uteAware implementation to the Uni tO fWo rk, after starting the route.
o nAfterR o ute: Invoked after the exchange has been routed by the given route.
603
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
o nAfterR o ute: Invoked after the exchange has been routed by the given route.
However, if the exchange is being routed through multiple routes, it would generate call backs for each route.
This invocation occurs before these callbacks:
The consumer of the route writes any response back to the caller (if in InO ut mode)
The Uni tO fWo rk is done by calling either
Synchro ni zati o n. o nC o mpl ete(o rg . apache. camel . Exchang e) or
Synchro ni zati o n. o nFai l ure(o rg . apache. camel . Exchang e)
604
CHAPT ER 4 9 . ENDPO INT INT ERFACE
CHAPTER 49. ENDPOINT INTERFACE
Ab st ract
This chapter describes how to implement the End po i nt interface, which is an essential step in the implementation of a Apache Camel component.
4 9.1. T HE ENDPOINT INT ERFACE
Overview
An instance of o rg . apache. camel . End po i nt type encapsulates an endpoint URI, and it also serves as a factory for C o nsumer, P ro d ucer, and Exchang e objects. There are three different approaches to implementing an endpoint:
Event-driven scheduled poll polling
These endpoint implementation patterns complement the corresponding patterns for
implementing a consumer—see Section 50.2, “ Implementing the Consumer Interface” .
Figure 49.1, “ Endpoint Inheritance Hierarchy” shows the relevant Java interfaces and
classes that make up the End po i nt inheritance hierarchy.
Fig u re 4 9 .1. En d p o in t In h erit an ce Hierarch y
605
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
606
T he Endpoint int erface
Example 49.1, “ Endpoint Interface” shows the definition of the
o rg . apache. camel . End po i nt interface.
Examp le 4 9 .1. En d p o in t In t erf ace
package org.apache.camel; public interface Endpoint { boolean isSingleton();
String getEndpointUri();
String getEndpointKey();
CamelContext getCamelContext(); void setCamelContext(CamelContext context); void configureProperties(Map options); boolean isLenientProperties();
CHAPT ER 4 9 . ENDPO INT INT ERFACE
Exchange createExchange();
Exchange createExchange(ExchangePattern pattern);
Exchange createExchange(Exchange exchange);
Producer createProducer() throws Exception;
Consumer createConsumer(Processor processor) throws Exception;
PollingConsumer createPollingConsumer() throws Exception;
}
Endpoint met hods
The End po i nt interface defines the following methods:
i sSi ng l eto n()—Returns true, if you want to ensure that each URI maps to a single endpoint within a CamelContext. When this property is true, multiple references to the identical URI within your routes always refer to a single endpoint instance. When this property is fal se, on the other hand, multiple references to the same URI within your routes refer to distinct endpoint instances. Each time you refer to the URI in a route, a new endpoint instance is created.
g etEnd po i ntUri ()—Returns the endpoint URI of this endpoint.
g etEnd po i ntKey()—Used by o rg . apache. camel . spi . Li fecycl eStrateg y when registering the endpoint.
g etC amel C o ntext()—return a reference to the C amel C o ntext instance to which this endpoint belongs.
setC amel C o ntext()—Sets the C amel C o ntext instance to which this endpoint belongs.
co nfi g ureP ro perti es()—Stores a copy of the parameter map that is used to inject parameters when creating a new C o nsumer instance.
i sLeni entP ro perti es()—Returns true to indicate that the URI is allowed to contain unknown parameters (that is, parameters that cannot be injected on the End po i nt or the
C o nsumer class). Normally, this method should be implemented to return fal se.
createExchang e()—An overloaded method with the following variants:
Exchang e createExchang e()—Creates a new exchange instance with a default exchange pattern setting.
Exchang e createExchang e(Exchang eP attern pattern)—Creates a new exchange instance with the specified exchange pattern.
607
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Exchang e createExchang e(Exchang e exchang e)—Converts the given
exchang e argument to the type of exchange needed for this endpoint. If the given exchange is not already of the correct type, this method copies it into a new instance of the correct type. A default implementation of this method is provided in the
D efaul tEnd po i nt class.
createP ro d ucer()—Factory method used to create new P ro d ucer instances.
createC o nsumer()—Factory method to create new event-driven consumer instances.
The pro cesso r argument is a reference to the first processor in the route.
createP o l l i ng C o nsumer()—Factory method to create new polling consumer instances.
Endpoint singlet ons
In order to avoid unnecessary overhead, it is a good idea to create a single endpoint instance for all endpoints that have the same URI (within a CamelContext). You can enforce this condition by implementing i sSi ng l eto n() to return true.
No t e
In this context, same URI means that two URIs are the same when compared using string equality. In principle, it is possible to have two URIs that are equivalent, though represented by different strings. In that case, the URIs would not be treated as the same.
4 9.2. IMPLEMENT ING T HE ENDPOINT INT ERFACE
Alt ernat ive ways of implement ing an endpoint
The following alternative endpoint implementation patterns are supported:
Event-driven endpoint implementation
Scheduled poll endpoint implementation
Polling endpoint implementation
Event -driven endpoint implement at ion
If your custom endpoint conforms to the event-driven pattern (see Section 47.1.3, “ Consumer
Patterns and Threading” ), it is implemented by extending the abstract class,
o rg . apache. camel . i mpl . D efaul tEnd po i nt, as shown in
“ Implementing DefaultEndpoint” .
608
CHAPT ER 4 9 . ENDPO INT INT ERFACE
Examp le 4 9 .2. Imp lemen t in g Def au lt En d p o in t
import java.util.Map; import java.util.concurrent.BlockingQueue; import org.apache.camel.Component; import org.apache.camel.Consumer; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.impl.DefaultExchange; public class CustomEndpoint extends DefaultEndpoint {
public CustomEndpoint(String endpointUri, Component component)
{
super(endpointUri, component);
// Do any other initialization...
}
public Producer createProducer() throws Exception {
return new CustomProducer(this);
}
public Consumer createConsumer(Processor processor) throws
Exception {
return new CustomConsumer(this, processor);
}
public boolean isSingleton() {
return true;
}
// Implement the following methods, only if you need to set exchange properties.
//
public Exchange createExchange() {
return this.createExchange(getExchangePattern());
}
public Exchange createExchange(ExchangePattern pattern) {
Exchange result = new DefaultExchange(getCamelContext(), pattern);
// Set exchange properties
...
return result;
}
}
609
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Implement an event-driven custom endpoint, CustomEndpoint, by extending the
D efaul tEnd po i nt class.
You must have at least one constructor that takes the endpoint URI, end po i ntUri , and the parent component reference, co mpo nent, as arguments.
Implement the createP ro d ucer() factory method to create producer endpoints.
Implement the createC o nsumer() factory method to create event-driven consumer instances.
Imp o rt an t
Do not override the createP o l l i ng C o nsumer() method.
In general, it is not necessary to override the createExchang e() methods. The implementations inherited from D efaul tEnd po i nt create a D efaul tExchang e object by default, which can be used in any Apache Camel component. If you need to initialize some exchange properties in the D efaul tExchang e object, however, it is appropriate to override the createExchang e() methods here in order to add the exchange property settings.
The D efaul tEnd po i nt class provides default implementations of the following methods, which you might find useful when writing your custom endpoint code:
g etEnd po i ntUri ()—Returns the endpoint URI.
g etC amel C o ntext()—Returns a reference to the C amel C o ntext.
g etC o mpo nent()—Returns a reference to the parent component.
createP o l l i ng C o nsumer()—Creates a polling consumer. The created polling consumer's functionality is based on the event-driven consumer. If you override the event-driven consumer method, createC o nsumer(), you get a polling consumer implementation for free.
610
CHAPT ER 4 9 . ENDPO INT INT ERFACE
createExchang e(Exchang e e)—Converts the given exchange object, e, to the type required for this endpoint. This method creates a new endpoint using the overridden
createExchang e() endpoints. This ensures that the method also works for custom exchange types.
Scheduled poll endpoint implement at ion
If your custom endpoint conforms to the scheduled poll pattern (see Section 47.1.3,
“ Consumer Patterns and Threading” ) it is implemented by inheriting from the abstract class,
o rg . apache. camel . i mpl . Sched ul ed P o l l End po i nt, as shown in
“ ScheduledPollEndpoint Implementation” .
Examp le 4 9 .3. Sch ed u led Po llEn d p o in t Imp lemen t at io n
import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.impl.ScheduledPollEndpoint; public class CustomEndpoint extends ScheduledPollEndpoint {
protected CustomEndpoint(String endpointUri, CustomComponent component) {
super(endpointUri, component);
// Do any other initialization...
}
public Producer createProducer() throws Exception {
Producer result = new CustomProducer(this);
return result;
}
public Consumer createConsumer(Processor processor) throws
Exception {
Consumer result = new CustomConsumer(this, processor);
configureConsumer(result);
return result;
}
public boolean isSingleton() {
return true;
}
// Implement the following methods, only if you need to set exchange properties.
//
public Exchange createExchange() {
return this.createExchange(getExchangePattern());
}
611
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
public Exchange createExchange(ExchangePattern pattern) {
Exchange result = new DefaultExchange(getCamelContext(), pattern);
// Set exchange properties
...
return result;
}
}
612
Implement a scheduled poll custom endpoint, CustomEndpoint, by extending the
Sched ul ed P o l l End po i nt class.
You must to have at least one constructor that takes the endpoint URI,
end po i ntUri , and the parent component reference, co mpo nent, as arguments.
Implement the createP ro d ucer() factory method to create a producer endpoint.
Implement the createC o nsumer() factory method to create a scheduled poll consumer instance.
Imp o rt an t
Do not override the createP o l l i ng C o nsumer() method.
The co nfi g ureC o nsumer() method, defined in the Sched ul ed P o l l End po i nt base class, is responsible for injecting consumer query options into the consumer.
See the section called “ Consumer parameter injection” .
In general, it is not necessary to override the createExchang e() methods. The implementations inherited from D efaul tEnd po i nt create a D efaul tExchang e object by default, which can be used in any Apache Camel component. If you need to initialize some exchange properties in the D efaul tExchang e object, however, it is
CHAPT ER 4 9 . ENDPO INT INT ERFACE
appropriate to override the createExchang e() methods here in order to add the exchange property settings.
Polling endpoint implement at ion
If your custom endpoint conforms to the polling consumer pattern (see Section 47.1.3,
“ Consumer Patterns and Threading” ), it is implemented by inheriting from the abstract class,
o rg . apache. camel . i mpl . D efaul tP o l l i ng End po i nt, as shown in
“ DefaultPollingEndpoint Implementation” .
Examp le 4 9 .4 . Def au lt Po llin g En d p o in t Imp lemen t at io n
import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.impl.DefaultPollingEndpoint; public class CustomEndpoint extends DefaultPollingEndpoint {
...
public PollingConsumer createPollingConsumer() throws Exception
{
PollingConsumer result = new CustomConsumer( this );
configureConsumer(result); return result;
}
// Do NOT implement createConsumer(). It is already implemented in DefaultPollingEndpoint.
...
}
Because this CustomEndpoint class is a polling endpoint, you must implement the
createP o l l i ng C o nsumer() method instead of the createC o nsumer() method. The consumer instance returned from createP o l l i ng C o nsumer() must inherit from the
Apart from the implementation of the createP o l l i ng C o nsumer() method, the steps for implementing a D efaul tP o l l i ng End po i nt are similar to the steps for implementing a
Sched ul ed P o l l End po i nt. See
Example 49.3, “ ScheduledPollEndpoint Implementation”
for details.
Implement ing t he BrowsableEndpoint int erface
If you want to expose the list of exchange instances that are pending in the current endpoint, you can implement the o rg . apache. camel . spi . Bro wsabl eEnd po i nt interface, as shown in
Example 49.5, “ BrowsableEndpoint Interface”
. It makes sense to implement this
613
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
interface if the endpoint performs some sort of buffering of incoming events. For example, the
Apache Camel SEDA endpoint implements the Bro wsabl eEnd po i nt interface—see
Example 49.6, “ SedaEndpoint Implementation” .
Examp le 4 9 .5. Bro wsab leEn d p o in t In t erf ace
package org.apache.camel.spi; import java.util.List; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; public interface BrowsableEndpoint extends Endpoint {
List<Exchange> getExchanges();
}
614
Example
Example 49.6, “ SedaEndpoint Implementation” shows a sample implementation of
Sed aEnd po i nt. The SEDA endpoint is an example of an event-driven endpoint. Incoming events are stored in a FIFO queue (an instance of
java. uti l . co ncurrent. Bl o cki ng Q ueue) and a SEDA consumer starts up a thread to read and process the events. The events themselves are represented by
o rg . apache. camel . Exchang e objects.
Examp le 4 9 .6 . Sed aEn d p o in t Imp lemen t at io n
package org.apache.camel.component.seda; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import org.apache.camel.Component; import org.apache.camel.Consumer; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.spi.BrowsableEndpoint; public class SedaEndpoint extends DefaultEndpoint implements
BrowsableEndpoint {
private BlockingQueue<Exchange> queue;
public SedaEndpoint(String endpointUri, Component component,
BlockingQueue<Exchange> queue) {
CHAPT ER 4 9 . ENDPO INT INT ERFACE
super(endpointUri, component);
this.queue = queue;
}
public SedaEndpoint(String uri, SedaComponent component, Map parameters) {
this(uri, component, component.createQueue(uri, parameters));
}
public Producer createProducer() throws Exception {
return new CollectionProducer(this, getQueue());
}
public Consumer createConsumer(Processor processor) throws
Exception {
return new SedaConsumer(this, processor);
}
public BlockingQueue<Exchange> getQueue() {
return queue;
}
public boolean isSingleton() {
return true;
}
public List<Exchange> getExchanges() {
return new ArrayList<Exchange>(getQueue());
}
}
The Sed aEnd po i nt class follows the pattern for implementing an event-driven endpoint by extending the D efaul tEnd po i nt class. The Sed aEnd po i nt class also implements the Bro wsabl eEnd po i nt interface, which provides access to the list of exchange objects in the queue.
Following the usual pattern for an event-driven consumer, Sed aEnd po i nt defines a constructor that takes an endpoint argument, end po i ntUri , and a component reference argument, co mpo nent.
Another constructor is provided, which delegates queue creation to the parent component instance.
615
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The createP ro d ucer() factory method creates an instance of
C o l l ecti o nP ro d ucer, which is a producer implementation that adds events to the queue.
The createC o nsumer() factory method creates an instance of Sed aC o nsumer, which is responsible for pulling events off the queue and processing them.
The g etQ ueue() method returns a reference to the queue.
The i sSi ng l eto n() method returns true, indicating that a single endpoint instance should be created for each unique URI string.
The g etExchang es() method implements the corresponding abstract method from
Bro wsabl eEnd po i nt.
616
CHAPT ER 50 . CO NSUMER INT ERFACE
CHAPTER 50. CONSUMER INTERFACE
Ab st ract
This chapter describes how to implement the C o nsumer interface, which is an essential step in the implementation of a Apache Camel component.
50.1. T HE CONSUMER INT ERFACE
Overview
An instance of o rg . apache. camel . C o nsumer type represents a source endpoint in a
route. There are several different ways of implementing a consumer (see Section 47.1.3,
“ Consumer Patterns and Threading” ), and this degree of flexibility is reflected in the
inheritance hierarchy ( see Figure 50.1, “ Consumer Inheritance Hierarchy” ), which includes
several different base classes for implementing a consumer.
Fig u re 50.1. Co n su mer In h erit an ce Hierarch y
Consumer paramet er inject ion
For example, consider the following endpoint URI for a component identified by the custo m prefix:
617
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
custom:destination?consumer.myConsumerParam
Apache Camel provides support for automatically injecting query options of the form
co nsumer. *. For the co nsumer. myC o nsumerP aram parameter, you need to define corresponding setter and getter methods on the C o nsumer implementation class as follows: public class CustomConsumer extends ScheduledPollConsumer {
...
String getMyConsumerParam() { ... } void setMyConsumerParam(String s) { ... }
...
}
Where the getter and setter methods follow the usual Java bean conventions (including capitalizing the first letter of the property name).
In addition to defining the bean methods in your Consumer implementation, you must also remember to call the co nfi g ureC o nsumer() method in the implementation of
example of a createC o nsumer() method implementation, taken from the Fi l eEnd po i nt class in the file component:
Examp le 50.1. FileEn d p o in t creat eCo n su mer( ) Imp lemen t at io n
...
public class FileEndpoint extends ScheduledPollEndpoint {
...
public Consumer createConsumer(Processor processor) throws
Exception {
Consumer result = new FileConsumer( this , processor);
configureConsumer(result); return result;
}
...
}
618
At run time, consumer parameter injection works as follows:
1. When the endpoint is created, the default implementation of
D efaul tC o mpo nent. createEnd po i nt(Stri ng uri ) parses the URI to extract the consumer parameters, and stores them in the endpoint instance by calling
Sched ul ed P o l l End po i nt. co nfi g ureP ro perti es().
2. When createC o nsumer() is called, the method implementation calls
co nfi g ureC o nsumer() to inject the consumer parameters (see
“ FileEndpoint createConsumer() Implementation” ).
CHAPT ER 50 . CO NSUMER INT ERFACE
3. The co nfi g ureC o nsumer() method uses Java reflection to call the setter methods whose names match the relevant options after the co nsumer. prefix has been stripped off.
Scheduled poll paramet ers
A consumer that follows the scheduled poll pattern automatically supports the consumer
parameters shown in Table 50.1, “ Scheduled Poll Parameters”
(which can appear as query options in the endpoint URI).
T ab le 50.1. Sch ed u led Po ll Paramet ers
Name i ni ti al D el ay d el ay useFi xed D el ay
Default
10 0 0
50 0
Descript ion
Delay, in milliseco nd s, b efo re the first p o ll.
Dep end s o n the value o f the
useFi xed D el ay
flag
(time unit is milliseco nd s).
fal se
If
fal se
, the
d el ay
p arameter is interp reted as the p o lling p erio d . Po lls will o ccur at
i ni ti al D el ay
,
i ni ti al D el ay+ d el ay
,
i ni ti al D el ay+ 2*d el ay
, and so o n.
If
true
, the
d el ay
p arameter is interp reted as the time elap sed b etween the p revio us executio n and the next executio n. Po lls will o ccur at
i ni ti al D el ay
,
i ni ti al D el ay+ [ProcessingTime]+ d el ay
, and so o n. Where ProcessingTime is the time taken to p ro cess an exchang e o b ject in the current thread .
Convert ing bet ween event -driven and polling consumers
Apache Camel provides two special consumer implementations which can be used to convert back and forth between an event-driven consumer and a polling consumer. The following conversion classes are provided:
o rg . apache. camel . i mpl . EventD ri venP o l l i ng C o nsumer—Converts an eventdriven consumer into a polling consumer instance.
o rg . apache. camel . i mpl . D efaul tSched ul ed P o l l C o nsumer—Converts a polling consumer into an event-driven consumer instance.
In practice, these classes are used to simplify the task of implementing an End po i nt type.
The End po i nt interface defines the following two methods for creating a consumer instance: package org.apache.camel;
619
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
620
public interface Endpoint {
...
Consumer createConsumer(Processor processor) throws Exception;
PollingConsumer createPollingConsumer() throws Exception;
}
createC o nsumer() returns an event-driven consumer and createP o l l i ng C o nsumer() returns a polling consumer. You would only implement one these methods. For example, if you are following the event-driven pattern for your consumer, you would implement the
createC o nsumer() method provide a method implementation for
createP o l l i ng C o nsumer() that simply raises an exception. With the help of the conversion classes, however, Apache Camel is able to provide a more useful default implementation.
For example, if you want to implement your consumer according to the event-driven pattern, you implement the endpoint by extending D efaul tEnd po i nt and implementing the
createC o nsumer() method. The implementation of createP o l l i ng C o nsumer() is inherited from D efaul tEnd po i nt, where it is defined as follows: public PollingConsumer<E> createPollingConsumer() throws Exception {
return new EventDrivenPollingConsumer<E>(this);
}
The EventD ri venP o l l i ng C o nsumer constructor takes a reference to the event-driven consumer, thi s, effectively wrapping it and converting it into a polling consumer. To implement the conversion, the EventD ri venP o l l i ng C o nsumer instance buffers incoming events and makes them available on demand through the recei ve(), the recei ve(l o ng
ti meo ut), and the recei veNo Wai t() methods.
Analogously, if you are implementing your consumer according to the polling pattern, you implement the endpoint by extending D efaul tP o l l i ng End po i nt and implementing the
createP o l l i ng C o nsumer() method. In this case, the implementation of the
createC o nsumer() method is inherited from D efaul tP o l l i ng End po i nt, and the default implementation returns a D efaul tSched ul ed P o l l C o nsumer instance (which converts the polling consumer into an event-driven consumer).
Shut downPrepared int erface
Consumer classes can optionally implement the
o rg . apache. camel . spi . Shutd o wnP repared interface, which enables your custom consumer endpoint to receive shutdown notifications.
Example 50.2, “ ShutdownPrepared Interface” shows the definition of the
Shutd o wnP repared interface.
Examp le 50.2. Sh u t d o wn Prep ared In t erf ace
package org.apache.camel.spi;
CHAPT ER 50 . CO NSUMER INT ERFACE
public interface ShutdownPrepared {
void prepareShutdown(boolean forced);
}
The Shutd o wnP repared interface defines the following methods:
prepareShutd o wn
Receives notifications to shut down the consumer endpoint in one or two phases, as follows:
1. Graceful shutdown—where the fo rced argument has the value fal se.
Attempt to clean up resources gracefully. For example, by stopping threads gracefully.
2. Forced shutdown—where the fo rced argument has the value true. This means that the shutdown has timed out, so you must clean up resources more aggressively. This is the last chance to clean up resources before the process exits.
Shut downAware int erface
Consumer classes can optionally implement the
o rg . apache. camel . spi . Shutd o wnAware interface, which interacts with the graceful shutdown mechanism, enabling a consumer to ask for extra time to shut down. This is typically needed for components such as SEDA, which can have pending exchanges stored in an internal queue. Normally, you would want to process all of the exchanges in the queue before shutting down the SEDA consumer.
Example 50.3, “ ShutdownAware Interface”
shows the definition of the Shutd o wnAware interface.
Examp le 50.3. Sh u t d o wn Aware In t erf ace
// Java package org.apache.camel.spi; import org.apache.camel.ShutdownRunningTask; public interface ShutdownAware extends ShutdownPrepared {
621
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
boolean deferShutdown(ShutdownRunningTask shutdownRunningTask);
int getPendingExchangesSize();
}
The Shutd o wnAware interface defines the following methods:
d eferShutd o wn
Return true from this method, if you want to delay shutdown of the consumer. The
shutd o wnR unni ng T ask argument is an enum which can take either of the following values:
Shutd o wnR unni ng T ask. C o mpl eteC urrentT askO nl y—finish processing the exchanges that are currently being processed by the consumer's thread pool, but do not attempt to process any more exchanges than that.
Shutd o wnR unni ng T ask. C o mpl eteAl l T asks—process all of the pending exchanges. For example, in the case of the SEDA component, the consumer would process all of the exchanges from its incoming queue.
g etP end i ng Exchang esSi ze
Indicates how many exchanges remain to be processed by the consumer. A zero value indicates that processing is finished and the consumer can be shut down.
For an example of how to define the Shutd o wnAware methods, see Example 50.7, “ Custom
50.2. IMPLEMENT ING T HE CONSUMER INT ERFACE
Alt ernat ive ways of implement ing a consumer
You can implement a consumer in one of the following ways:
Event-driven consumer implementation
Scheduled poll consumer implementation
Polling consumer implementation
Custom threading implementation
Event -driven consumer implement at ion
622
CHAPT ER 50 . CO NSUMER INT ERFACE
In an event-driven consumer, processing is driven explicitly by external events. The events are received through an event-listener interface, where the listener interface is specific to the particular event source.
Example 50.4, “ JMXConsumer Implementation” shows the implementation of the
JMXC o nsumer class, which is taken from the Apache Camel JMX component implementation.
The JMXC o nsumer class is an example of an event-driven consumer, which is implemented by inheriting from the o rg . apache. camel . i mpl . D efaul tC o nsumer class. In the case of the JMXC o nsumer example, events are represented by calls on the
No ti fi cati o nLi stener. hand l eNo ti fi cati o n() method, which is a standard way of receiving JMX events. In order to receive these JMX events, it is necessary to implement the
No ti fi cati o nLi stener interface and override the hand l eNo ti fi cati o n() method,
as shown in Example 50.4, “ JMXConsumer Implementation”
.
Examp le 50.4 . JMXCo n su mer Imp lemen t at io n
package org.apache.camel.component.jmx; import javax.management.Notification; import javax.management.NotificationListener; import org.apache.camel.Processor; import org.apache.camel.impl.DefaultConsumer; public class JMXConsumer extends DefaultConsumer implements
NotificationListener {
JMXEndpoint jmxEndpoint;
public JMXConsumer(JMXEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.jmxEndpoint = endpoint;
}
public void handleNotification(Notification notification,
Object handback) {
try {
getProcessor().process(jmxEndpoint.createExchange(notification));
} catch (Throwable e) {
handleException(e);
}
}
}
623
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The JMXC o nsumer pattern follows the usual pattern for event-driven consumers by extending the D efaul tC o nsumer class. Additionally, because this consumer is designed to receive events from JMX (which are represented by JMX notifications), it is necessary to implement the No ti fi cati o nLi stener interface.
You must implement at least one constructor that takes a reference to the parent endpoint, end po i nt, and a reference to the next processor in the chain,
pro cesso r, as arguments.
The hand l eNo ti fi cati o n() method (which is defined in
No ti fi cati o nLi stener) is automatically invoked by JMX whenever a JMX notification arrives. The body of this method should contain the code that performs the consumer's event processing. Because the hand l eNo ti fi cati o n() call originates from the JMX layer, the consumer's threading model is implicitly controlled by the JMX layer, not by the JMXC o nsumer class.
No t e
The hand l eNo ti fi cati o n() method is specific to the JMX example.
When implementing your own event-driven consumer, you must identify an analogous event listener method to implement in your custom consumer.
This line of code combines two steps. First, the JMX notification object is converted into an exchange object, which is the generic representation of an event in Apache
Camel. Then the newly created exchange object is passed to the next processor in the route (invoked synchronously).
The hand l eExcepti o n() method is implemented by the D efaul tC o nsumer base class. By default, it handles exceptions using the
o rg . apache. camel . i mpl . Lo g g i ng Excepti o nHand l er class.
Scheduled poll consumer implement at ion
In a scheduled poll consumer, polling events are automatically generated by a timer class,
java. uti l . co ncurrent. Sched ul ed Executo rServi ce. To receive the generated polling events, you must implement the Sched ul ed P o l l C o nsumer. po l l () method (see
Section 47.1.3, “ Consumer Patterns and Threading” ).
624
CHAPT ER 50 . CO NSUMER INT ERFACE
Example 50.5, “ ScheduledPollConsumer Implementation” shows how to implement a
consumer that follows the scheduled poll pattern, which is implemented by extending the
Sched ul ed P o l l C o nsumer class.
Examp le 50.5. Sch ed u led Po llCo n su mer Imp lemen t at io n
import java.util.concurrent.ScheduledExecutorService; import org.apache.camel.Consumer; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.PollingConsumer; import org.apache.camel.Processor; import org.apache.camel.impl.ScheduledPollConsumer; public class CustomConsumer extends ScheduledPollConsumer {
private final CustomEndpoint endpoint;
public CustomConsumer(CustomEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.endpoint = endpoint;
}
protected void poll() throws Exception {
Exchange exchange = /* Receive exchange object ... */;
// Example of a synchronous processor.
getProcessor().process(exchange);
}
@ Override
protected void doStart() throws Exception {
// Pre-Start:
// Place code here to execute just before start of processing.
super.doStart();
// Post-Start:
// Place code here to execute just after start of processing.
}
@ Override
protected void doStop() throws Exception {
// Pre-Stop:
// Place code here to execute just before processing stops.
super.doStop();
// Post-Stop:
// Place code here to execute just after processing stops.
}
}
625
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
626
Implement a scheduled poll consumer class, CustomConsumer, by extending the
o rg . apache. camel . i mpl . Sched ul ed P o l l C o nsumer class.
You must implement at least one constructor that takes a reference to the parent endpoint, end po i nt, and a reference to the next processor in the chain,
pro cesso r, as arguments.
Override the po l l () method to receive the scheduled polling events. This is where you should put the code that retrieves and processes incoming events (represented by exchange objects).
In this example, the event is processed synchronously. If you want to process events asynchronously, you should use a reference to an asynchronous processor instead, by calling g etAsyncP ro cesso r(). For details of how to process events asynchronously, see
Section 47.1.4, “ Asynchronous Processing” .
(Optional) If you want some lines of code to execute as the consumer is starting up, override the d o Start() method as shown.
(Optional) If you want some lines of code to execute as the consumer is stopping, override the d o Sto p() method as shown.
Polling consumer implement at ion
Example 50.6, “ PollingConsumerSupport Implementation”
outlines how to implement a consumer that follows the polling pattern, which is implemented by extending the
P o l l i ng C o nsumerSuppo rt class.
Examp le 50.6 . Po llin g Co n su merSu p p o rt Imp lemen t at io n
import org.apache.camel.Exchange;
CHAPT ER 50 . CO NSUMER INT ERFACE
import org.apache.camel.RuntimeCamelException; import org.apache.camel.impl.PollingConsumerSupport; public class CustomConsumer extends PollingConsumerSupport {
private final CustomEndpoint endpoint;
public CustomConsumer(CustomEndpoint endpoint) {
super(endpoint);
this.endpoint = endpoint;
}
public Exchange receiveNoWait() {
Exchange exchange = /* Obtain an exchange object. */;
// Further processing ...
return exchange;
}
public Exchange receive() {
// Blocking poll ...
}
public Exchange receive(long timeout) {
// Poll with timeout ...
}
protected void doStart() throws Exception {
// Code to execute whilst starting up.
}
protected void doStop() throws Exception {
// Code to execute whilst shutting down.
}
}
Implement your polling consumer class, CustomConsumer, by extending the
o rg . apache. camel . i mpl . P o l l i ng C o nsumerSuppo rt class.
You must implement at least one constructor that takes a reference to the parent endpoint, end po i nt, as an argument. A polling consumer does not need a reference to a processor instance.
The recei veNo Wai t() method should implement a non-blocking algorithm for retrieving an event (exchange object). If no event is available, it should return nul l .
627
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
retrieving an event (exchange object). If no event is available, it should return nul l .
The recei ve() method should implement a blocking algorithm for retrieving an event. This method can block indefinitely, if events remain unavailable.
The recei ve(l o ng ti meo ut) method implements an algorithm that can block for as long as the specified timeout (typically specified in units of milliseconds).
If you want to insert code that executes while a consumer is starting up or shutting down, implement the d o Start() method and the d o Sto p() method, respectively.
Cust om t hreading implement at ion
If the standard consumer patterns are not suitable for your consumer implementation, you can implement the C o nsumer interface directly and write the threading code yourself. When writing the threading code, however, it is important that you comply with the standard
Apache Camel threading model, as described in
Section 2.8, “ Threading Model” .
For example, the SEDA component from camel -co re implements its own consumer
threading, which is consistent with the Apache Camel threading model. Example 50.7,
“ Custom Threading Implementation” shows an outline of how the Sed aC o nsumer class
implements its threading.
Examp le 50.7. Cu st o m T h read in g Imp lemen t at io n
package org.apache.camel.component.seda; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import org.apache.camel.Consumer; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.ShutdownRunningTask; import org.apache.camel.impl.LoggingExceptionHandler; import org.apache.camel.impl.ServiceSupport; import org.apache.camel.util.ServiceHelper;
...
import org.apache.commons.logging.Log;
628
CHAPT ER 50 . CO NSUMER INT ERFACE
import org.apache.commons.logging.LogFactory;
/**
* A Consumer for the SEDA component.
*
* @ version $Revision: 922485 $
*/ public class SedaConsumer extends ServiceSupport implements
Consumer, Runnable, ShutdownAware {
private static final transient Log LOG =
LogFactory.getLog(SedaConsumer.class);
private SedaEndpoint endpoint;
private Processor processor;
private ExecutorService executor;
...
public SedaConsumer(SedaEndpoint endpoint, Processor processor)
{
this.endpoint = endpoint;
this.processor = processor;
}
...
public void run() {
BlockingQueue<Exchange> queue = endpoint.getQueue();
// Poll the queue and process exchanges
...
}
...
protected void doStart() throws Exception {
int poolSize = endpoint.getConcurrentConsumers();
executor = endpoint.getCamelContext().getExecutorServiceStrategy()
.newFixedThreadPool(this, endpoint.getEndpointUri(), poolSize);
for (int i = 0; i < poolSize; i++) {
executor.execute(this);
}
endpoint.onStarted(this);
}
protected void doStop() throws Exception {
endpoint.onStopped(this);
// must shutdown executor on stop to avoid overhead of having them running endpoint.getCamelContext().getExecutorServiceStrategy().shutdownNow
(executor);
executor = null;
if (multicast != null) {
ServiceHelper.stopServices(multicast);
}
629
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
}
...
//----------
// Implementation of ShutdownAware interface
public boolean deferShutdown(ShutdownRunningTask shutdownRunningTask) {
// deny stopping on shutdown as we want seda consumers to run in case some other queues
// depend on this consumer to run, so it can complete its exchanges
return true;
}
public int getPendingExchangesSize() {
// number of pending messages on the queue
return endpoint.getQueue().size();
}
}
630
The Sed aC o nsumer class is implemented by extending the
o rg . apache. camel . i mpl . Servi ceSuppo rt class and implementing the
C o nsumer, R unnabl e, and Shutd o wnAware interfaces.
Implement the R unnabl e. run() method to define what the consumer does while it is running in a thread. In this case, the consumer runs in a loop, polling the queue for new exchanges and then processing the exchanges in the latter part of the queue.
The d o Start() method is inherited from Servi ceSuppo rt. You override this method in order to define what the consumer does when it starts up.
Instead of creating threads directly, you should create a thread pool using the
Executo rServi ceStrateg y object that is registered with the C amel C o ntext. This is important, because it enables Apache Camel to implement centralized management of threads and support such features as graceful shutdown.
For details, see
Section 2.8, “ Threading Model” .
CHAPT ER 50 . CO NSUMER INT ERFACE
Kick off the threads by calling the Executo rServi ce. execute() method
po o l Si ze times.
The d o Sto p() method is inherited from Servi ceSuppo rt. You override this method in order to define what the consumer does when it shuts down.
Shut down the thread pool, which is represented by the executo r instance.
631
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 51. PRODUCER INTERFACE
Ab st ract
This chapter describes how to implement the P ro d ucer interface, which is an essential step in the implementation of a Apache Camel component.
51.1. T HE PRODUCER INT ERFACE
Overview
An instance of o rg . apache. camel . P ro d ucer type represents a target endpoint in a route. The role of the producer is to send requests (In messages) to a specific physical endpoint and to receive the corresponding response (Out or Fault message). A P ro d ucer object is essentially a special kind of P ro cesso r that appears at the end of a processor chain (equivalent to a route).
Figure 51.1, “ Producer Inheritance Hierarchy”
shows the inheritance hierarchy for producers.
Fig u re 51.1. Pro d u cer In h erit an ce Hierarch y
632
T he Producer int erface
Example 51.1, “ Producer Interface”
shows the definition of the
o rg . apache. camel . P ro d ucer interface.
Examp le 51.1. Pro d u cer In t erf ace
package org.apache.camel; public interface Producer extends Processor, Service, IsSingleton {
Endpoint<E> getEndpoint();
Exchange createExchange();
CHAPT ER 51 . PRO DUCER INT ERFACE
Exchange createExchange(ExchangePattern pattern);
Exchange createExchange(E exchange);
}
Producer met hods
The P ro d ucer interface defines the following methods:
pro cess() (inherited from Processor)—The most important method. A producer is essentially a special type of processor that sends a request to an endpoint, instead of forwarding the exchange object to another processor. By overriding the pro cess() method, you define how the producer sends and receives messages to and from the relevant endpoint.
g etEnd po i nt()—Returns a reference to the parent endpoint instance.
createExchang e()—These overloaded methods are analogous to the corresponding methods defined in the End po i nt interface. Normally, these methods delegate to the corresponding methods defined on the parent End po i nt instance (this is what the
D efaul tEnd po i nt class does by default). Occasionally, you might need to override these methods.
Asynchronous processing
Processing an exchange object in a producer—which usually involves sending a message to a remote destination and waiting for a reply—can potentially block for a significant length of time. If you want to avoid blocking the current thread, you can opt to implement the producer as an asynchronous processor. The asynchronous processing pattern decouples the preceding processor from the producer, so that the pro cess() method returns without
delay. See Section 47.1.4, “ Asynchronous Processing” .
When implementing a producer, you can support the asynchronous processing model by implementing the o rg . apache. camel . AsyncP ro cesso r interface. On its own, this is not enough to ensure that the asynchronous processing model will be used: it is also necessary for the preceding processor in the chain to call the asynchronous version of the pro cess()
method. The definition of the AsyncP ro cesso r interface is shown in Example 51.2,
Examp le 51.2. Asyn cPro cesso r In t erf ace
package org.apache.camel; public interface AsyncProcessor extends Processor {
boolean process(Exchange exchange, AsyncCallback callback);
}
633
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The asynchronous version of the pro cess() method takes an extra argument, cal l back, of o rg . apache. camel . AsyncC al l back type. The corresponding AsyncC al l back interface is defined as shown in
Example 51.3, “ AsyncCallback Interface”
.
Examp le 51.3. Asyn cCallb ack In t erf ace
package org.apache.camel; public interface AsyncCallback {
void done(boolean doneSynchronously);
}
The caller of AsyncP ro cesso r. pro cess() must provide an implementation of
AsyncC al l back to receive the notification that processing has finished. The
AsyncC al l back. d o ne() method takes a boolean argument that indicates whether the processing was performed synchronously or not. Normally, the flag would be fal se, to indicate asynchronous processing. In some cases, however, it can make sense for the producer not to process asynchronously (in spite of being asked to do so). For example, if the producer knows that the processing of the exchange will complete rapidly, it could optimise the processing by doing it synchronously. In this case, the d o neSynchro no usl y flag should be set to true.
ExchangeHelper class
When implementing a producer, you might find it helpful to call some of the methods in the
o rg . apache. camel . uti l . Exchang eHel per utility class. For full details of the
Exchang eHel per class, see
Section 44.4, “ The ExchangeHelper Class” .
51.2. IMPLEMENT ING T HE PRODUCER INT ERFACE
Alt ernat ive ways of implement ing a producer
You can implement a producer in one of the following ways:
How to implement a synchronous producer
.
How to implement an asynchronous producer .
How t o implement a synchronous producer
Example 51.4, “ DefaultProducer Implementation”
outlines how to implement a synchronous producer. In this case, call to P ro d ucer. pro cess() blocks until a reply is received.
Examp le 51.4 . Def au lt Pro d u cer Imp lemen t at io n
634
CHAPT ER 51 . PRO DUCER INT ERFACE
import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Producer; import org.apache.camel.impl.DefaultProducer; public class CustomProducer extends DefaultProducer {
public CustomProducer(Endpoint endpoint) {
super(endpoint);
// Perform other initialization tasks...
}
public void process(Exchange exchange) throws Exception {
// Process exchange synchronously.
// ...
}
}
Implement a custom synchronous producer class, CustomProducer, by extending the
o rg . apache. camel . i mpl . D efaul tP ro d ucer class.
Implement a constructor that takes a reference to the parent endpoint.
The pro cess() method implementation represents the core of the producer code.
The implementation of the pro cess() method is entirely dependent on the type of component that you are implementing. In outline, the pro cess() method is normally implemented as follows:
If the exchange contains an In message, and if this is consistent with the specified exchange pattern, then send the In message to the designated endpoint.
If the exchange pattern anticipates the receipt of an Out message, then wait until the Out message has been received. This typically causes the pro cess() method to block for a significant length of time.
When a reply is received, call exchang e. setO ut() to attach the reply to the exchange object. If the reply contains a fault message, set the fault flag on the Out message using Messag e. setFaul t(true).
How t o implement an asynchronous producer
635
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Example 51.5, “ CollectionProducer Implementation”
outlines how to implement an asynchronous producer. In this case, you must implement both a synchronous pro cess() method and an asynchronous pro cess() method (which takes an additional
AsyncC al l back argument).
Examp le 51.5. Co llect io n Pro d u cer Imp lemen t at io n
import org.apache.camel.AsyncCallback; import org.apache.camel.AsyncProcessor; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Producer; import org.apache.camel.impl.DefaultProducer; public class CustomProducer extends DefaultProducer implements
AsyncProcessor {
public CustomProducer(Endpoint endpoint) {
super(endpoint);
// ...
}
public void process(Exchange exchange) throws Exception {
// Process exchange synchronously.
// ...
}
public boolean process(Exchange exchange, AsyncCallback callback) {
// Process exchange asynchronously.
CustomProducerTask task = new CustomProducerTask(exchange, callback);
// Process 'task' in a separate thread...
// ...
return false;
}
}
public class CustomProducerTask implements Runnable {
private Exchange exchange;
private AsyncCallback callback;
public CustomProducerTask(Exchange exchange, AsyncCallback callback) {
this.exchange = exchange;
this.callback = callback;
}
public void run() {
// Process exchange.
636
CHAPT ER 51 . PRO DUCER INT ERFACE
// ...
callback.done(false);
}
}
Implement a custom asynchronous producer class, CustomProducer, by extending the o rg . apache. camel . i mpl . D efaul tP ro d ucer class, and implementing the
AsyncP ro cesso r interface.
Implement a constructor that takes a reference to the parent endpoint.
Implement the synchronous pro cess() method.
Implement the asynchronous pro cess() method. You can implement the asynchronous method in several ways. The approach shown here is to create a
java. l ang . R unnabl e instance, task, that represents the code that runs in a subthread. You then use the Java threading API to run the task in a sub-thread (for example, by creating a new thread or by allocating the task to an existing thread pool).
Normally, you return fal se from the asynchronous pro cess() method, to indicate that the exchange was processed asynchronously.
The CustomProducerT ask class encapsulates the processing code that runs in a sub-thread. This class must store a copy of the Exchang e object, exchang e, and the AsyncC al l back object, cal l back, as private member variables.
The run() method contains the code that sends the In message to the producer endpoint and waits to receive the reply, if any. After receiving the reply (Out message or Fault message) and inserting it into the exchange object, you must call
cal l back. d o ne() to notify the caller that processing is complete.
637
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 52. EXCHANGE INTERFACE
Ab st ract
This chapter describes the Exchang e interface. Since the refactoring of the camel-core module performed in Apache Camel 2.0, there is no longer any necessity to define custom exchange types. The D efaul tExchang e implementation can now be used in all cases.
52.1. T HE EXCHANGE INT ERFACE
Overview
An instance of o rg . apache. camel . Exchang e type encapsulates the current message passing through a route, with additional metadata encoded as exchange properties.
Figure 52.1, “ Exchange Inheritance Hierarchy”
shows the inheritance hierarchy for the exchange type. The default implementation, D efaul tExchang e, is always used.
Fig u re 52.1. Exch an g e In h erit an ce Hierarch y
638
T he Exchange int erface
Example 52.1, “ Exchange Interface”
shows the definition of the
o rg . apache. camel . Exchang e interface.
Examp le 52.1. Exch an g e In t erf ace
package org.apache.camel; import java.util.Map; import org.apache.camel.spi.Synchronization; import org.apache.camel.spi.UnitOfWork; public interface Exchange {
CHAPT ER 52 . EXCHANG E INT ERFACE
// Exchange property names (string constants)
// (Not shown here)
...
ExchangePattern getPattern(); void setPattern(ExchangePattern pattern);
Object getProperty(String name);
Object getProperty(String name, Object defaultValue);
<T> T getProperty(String name, Class<T> type);
<T> T getProperty(String name, Object defaultValue, Class<T> type); void setProperty(String name, Object value);
Object removeProperty(String name);
Map<String, Object> getProperties(); boolean hasProperties();
Message getIn();
<T> T getIn(Class<T> type); void setIn(Message in);
Message getOut();
<T> T getOut(Class<T> type); void setOut(Message out); boolean hasOut();
Throwable getException();
<T> T getException(Class<T> type); void setException(Throwable e); boolean isFailed(); boolean isTransacted(); boolean isRollbackOnly();
CamelContext getContext();
Exchange copy();
Endpoint getFromEndpoint(); void setFromEndpoint(Endpoint fromEndpoint);
String getFromRouteId(); void setFromRouteId(String fromRouteId);
UnitOfWork getUnitOfWork(); void setUnitOfWork(UnitOfWork unitOfWork);
String getExchangeId(); void setExchangeId(String id); void addOnCompletion(Synchronization onCompletion); void handoverCompletions(Exchange target);
}
639
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Exchange met hods
The Exchang e interface defines the following methods:
g etP attern(), setP attern()—The exchange pattern can be one of the values enumerated in o rg . apache. camel . Exchang eP attern. The following exchange pattern values are supported:
InO nl y
R o bustInO nl y
InO ut
InO pti o nal O ut
O utO nl y
R o bustO utO nl y
O utIn
O utO pti o nal In
setP ro perty(), g etP ro perty(), g etP ro perti es(), remo veP ro perty(),
hasP ro perti es()—Use the property setter and getter methods to associate named properties with the exchange instance. The properties consist of miscellaneous metadata that you might need for your component implementation.
setIn(), g etIn()—Setter and getter methods for the In message.
The g etIn() implementation provided by the D efaul tExchang e class implements lazy creation semantics: if the In message is null when g etIn() is called, the
D efaul tExchang e class creates a default In message.
setO ut(), g etO ut(), hasO ut()—Setter and getter methods for the Out message.
The g etO ut() method implicitly supports lazy creation of an Out message. That is, if the current Out message is nul l , a new message instance is automatically created.
setExcepti o n(), g etExcepti o n()—Getter and setter methods for an exception object (of T hro wabl e type).
i sFai l ed ()—Returns true, if the exchange failed either due to an exception or due to a fault.
64 0
CHAPT ER 52 . EXCHANG E INT ERFACE
i sT ransacted ()—Returns true, if the exchange is transacted.
i sR o l l back()—Returns true, if the exchange is marked for rollback.
g etC o ntext()—Returns a reference to the associated C amel C o ntext instance.
co py()—Creates a new, identical (apart from the exchange ID) copy of the current custom exchange object. The body and headers of the In message, the Out message (if any), and the Fault message (if any) are also copied by this operation.
setFro mEnd po i nt(), g etFro mEnd po i nt()—Getter and setter methods for the consumer endpoint that orginated this message (which is typically the endpoint appearing in the fro m() DSL command at the start of a route).
setFro mR o uteId (), g etFro mR o uteId ()—Getters and setters for the route ID that originated this exchange. The g etFro mR o uteId () method should only be called internally.
setUni tO fWo rk(), g etUni tO fWo rk()—Getter and setter methods for the
o rg . apache. camel . spi . Uni tO fWo rk bean property. This property is only required for exchanges that can participate in a transaction.
setExchang eId (), g etExchang eId ()—Getter and setter methods for the exchange
ID. Whether or not a custom component uses and exchange ID is an implementation detail.
ad d O nC o mpl eti o n()—Adds an o rg . apache. camel . spi . Synchro ni zati o n callback object, which gets called when processing of the exchange has completed.
hand o verC o mpl eti o ns()—Hands over all of the OnCompletion callback objects to the specified exchange object.
64 1
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 53. MESSAGE INTERFACE
Ab st ract
This chapter describes how to implement the Messag e interface, which is an optional step in the implementation of a Apache Camel component.
53.1. T HE MESSAGE INT ERFACE
Overview
An instance of o rg . apache. camel . Messag e type can represent any kind of message (In
or Out). Figure 53.1, “ Message Inheritance Hierarchy”
shows the inheritance hierarchy for the message type. You do not always need to implement a custom message type for a component. In many cases, the default implementation, D efaul tMessag e, is adequate.
Fig u re 53.1. Messag e In h erit an ce Hierarch y
64 2
T he Message int erface
Example 53.1, “ Message Interface” shows the definition of the
o rg . apache. camel . Messag e interface.
Examp le 53.1. Messag e In t erf ace
package org.apache.camel; import java.util.Map; import java.util.Set; import javax.activation.DataHandler; public interface Message {
String getMessageId();
void setMessageId(String messageId);
CHAPT ER 53. MESSAG E INT ERFACE
Exchange getExchange();
boolean isFault();
void setFault(boolean fault);
Object getHeader(String name);
Object getHeader(String name, Object defaultValue);
<T> T getHeader(String name, Class<T> type);
<T> T getHeader(String name, Object defaultValue, Class<T> type);
Map<String, Object> getHeaders();
void setHeader(String name, Object value);
void setHeaders(Map<String, Object> headers);
Object removeHeader(String name);
boolean removeHeaders(String pattern);
boolean hasHeaders();
Object getBody();
Object getMandatoryBody() throws InvalidPayloadException;
<T> T getBody(Class<T> type);
<T> T getMandatoryBody(Class<T> type) throws
InvalidPayloadException;
void setBody(Object body);
<T> void setBody(Object body, Class<T> type);
DataHandler getAttachment(String id);
Map<String, DataHandler> getAttachments();
Set<String> getAttachmentNames();
void removeAttachment(String id);
void addAttachment(String id, DataHandler content);
void setAttachments(Map<String, DataHandler> attachments);
boolean hasAttachments();
Message copy();
void copyFrom(Message message);
String createExchangeId();
}
Message met hods
The Messag e interface defines the following methods:
setMessag eId (), g etMessag eId ()—Getter and setter methods for the message ID.
Whether or not you need to use a message ID in your custom component is an implementation detail.
g etExchang e()—Returns a reference to the parent exchange object.
64 3
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
i sFaul t(), setFaul t()—Getter and setter methods for the fault flag, which indicates whether or not this message is a fault message.
g etHead er(), g etHead ers(), setHead er(), setHead ers(), remo veHead er(),
hasHead ers()—Getter and setter methods for the message headers. In general, these message headers can be used either to store actual header data, or to store miscellaneous metadata.
g etBo d y(), g etMand ato ryBo d y(), setBo d y()—Getter and setter methods for the message body. The getMandatoryBody() accessor guarantees that the returned body is non-null, otherwise the Inval i d P ayl o ad Excepti o n exception is thrown.
g etAttachment(), g etAttachments(), g etAttachmentNames(),
remo veAttachment(), ad d Attachment(), setAttachments(),
hasAttachments()—Methods to get, set, add, and remove attachments.
co py()—Creates a new, identical (including the message ID) copy of the current custom message object.
co pyFro m()—Copies the complete contents (including the message ID) of the specified generic message object, messag e, into the current message instance. Because this method must be able to copy from any message type, it copies the generic message properties, but not the custom properties.
createExchang eId ()—Returns the unique ID for this exchange, if the message implementation is capable of providing an ID; otherwise, return nul l .
53.2. IMPLEMENT ING T HE MESSAGE INT ERFACE
How t o implement a cust om message
Example 53.2, “ Custom Message Implementation”
outlines how to implement a message by extending the D efaul tMessag e class.
Examp le 53.2. Cu st o m Messag e Imp lemen t at io n
import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultMessage; public class CustomMessage extends DefaultMessage {
public CustomMessage() {
// Create message with default properties...
}
@ Override
public String toString() {
// Return a stringified message...
64 4
CHAPT ER 53. MESSAG E INT ERFACE
}
@ Override
public CustomMessage newInstance() {
return new CustomMessage( ... );
}
@ Override
protected Object createBody() {
// Return message body (lazy creation).
}
@ Override
protected void populateInitialHeaders(Map<String, Object> map)
{
// Initialize headers from underlying message (lazy creation).
}
@ Override
protected void populateInitialAttachments(Map<String,
DataHandler> map) {
// Initialize attachments from underlying message (lazy creation).
}
}
Implements a custom message class, CustomMessage, by extending the
o rg . apache. camel . i mpl . D efaul tMessag e class.
Typically, you need a default constructor that creates a message with default properties.
Override the to Stri ng () method to customize message stringification.
The newInstance() method is called from inside the Messag eSuppo rt. co py() method. Customization of the newInstance() method should focus on copying all of the custom properties of the current message instance into the new message instance. The Messag eSuppo rt. co py() method copies the generic message properties by calling co pyFro m().
64 5
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The createBo d y() method works in conjunction with the
Messag eSuppo rt. g etBo d y() method to implement lazy access to the message body. By default, the message body is nul l . It is only when the application code tries to access the body (by calling g etBo d y()), that the body should be created.
The Messag eSuppo rt. g etBo d y() automatically calls createBo d y(), when the message body is accessed for the first time.
The po pul ateIni ti al Head ers() method works in conjunction with the header getter and setter methods to implement lazy access to the message headers. This method parses the message to extract any message headers and inserts them into the hash map, map. The po pul ateIni ti al Head ers() method is automatically called when a user attempts to access a header (or headers) for the first time (by calling g etHead er(), g etHead ers(), setHead er(), or setHead ers()).
The po pul ateIni ti al Attachments() method works in conjunction with the attachment getter and setter methods to implement lazy access to the attachments.
This method extracts the message attachments and inserts them into the hash map,
map. The po pul ateIni ti al Attachments() method is automatically called when a user attempts to access an attachment (or attachments) for the first time by calling
g etAttachment(), g etAttachments(), g etAttachmentNames(), or
ad d Attachment().
64 6
PART V. T HE API CO MPO NENT FRAMEWO RK
PART V. THE API COMPONENT FRAMEWORK
Ab st ract
How to create a Camel component that wraps any Java API, using the API Component
Framework.
64 7
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 54. INTRODUCTION TO THE API COMPONENT
FRAMEWORK
Ab st ract
The API component framework helps you with the challenge of implementing complex Camel components based on a large Java API.
54 .1. WHAT IS T HE API COMPONENT FRAMEWORK?
Mot ivat ion
For components with a small number of options, the standard approach to implementing components (
Chapter 47, Implementing a Component ) is quite effective. Where it starts to
become problematic, however, is when you need to implement a component with a large number of options. This problem becomes dramatic when it comes to enterprise-level components, which can require you to wrap an API consisting of hundreds of operations.
Such components require a large effort to create and maintain.
The API component framework was developed precisely to deal with the challenge of implementing such components.
T urning APIs int o component s
Experience of implementing Camel components based on Java APIs has shown that a lot of the work is routine and mechanical. It consists of taking a particular Java method, mapping it to a particular URI syntax, and enabling the user to set the method parameters through URI options. This type of work is an obvious candidate for automation and code generation.
Generic URI format
The first step in automating the implementation of a Java API is to design a standard way of mapping an API method to a URI. For this we need to define a generic URI format, which can be used to wrap any Java API. Hence, the API component framework defines the following syntax for endpoint URIs:
scheme://endpoint-prefix/endpoint?Option1=Value1& ...& OptionN=ValueN
Where scheme is the default URI scheme defined by the component; endpoint-prefix is a short API name, which maps to one of the classes or interfaces from the wrapped Java API;
endpoint maps to a method name; and the URI options map to method argument names.
URI format for a single API class
In the case where an API consists of just a single Java class, the endpoint-prefix part of the URI becomes redundant, and you can specify the URI in the following, shorter format:
64 8
CHAPT ER 54 . INT RO DUCT IO N T O T HE API CO MPO NENT FRAMEWO RK
scheme://endpoint?Option1=Value1& ...& OptionN=ValueN
No t e
To enable this URI format, it is also necessary for the component implementor to leave the api Name element blank in the configuration of the API component Maven plug-in.
Reflect ion and met adat a
In order to map Java method invocations to a URI syntax, it is obvious that some form of reflection mechanism is needed. But the standard Java reflection API suffers from a notable limitation: it does not preserve method argument names. This is a problem, because we need the method argument names in order to generate meaningful URI option names. The solution is to provide metadata in alternative format: either as Javadoc or in method signature files.
Javadoc
Javadoc is an ideal form of metadata for the API component framework, because it preserves the complete method signature, including method argument names. It is also easy to generate (particularly, using maven-javad o c-pl ug i n) and, in many cases, is already provided in a third-party library.
Met hod signat ure files
If Javadoc is unavailable or unsuitable for some reason, the API component framework also supports an alternative source of metadata: the method signature files. A signature file is a simple text file which consists of a list of Java method signatures. It is relatively easy to create these files manually by copying and pasting from Java code (and lightly editing the resulting files).
What does t he framework consist of?
From the perspective of a component developer, the API component framework consists of a number of different elements, as follows:
A Maven arch et yp e
The camel -archetype-api -co mpo nent Maven archetype is used to generate skeleton code for the component implementation.
A Maven p lu g - in
The camel -api -co mpo nent-maven-pl ug i n Maven plug-in is responsible for generating the code that implements the mapping between the Java API and the endpoint URI syntax.
64 9
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Sp ecializ ed b ase classes
To support the programming model of the API component framework, the Apache
Camel core provides a specialized API in the
o rg . apache. camel . uti l . co mpo nent package. Amongst other things, this API provides specialized base classes for the component, endpoint, consumer, and producer classes.
54 .2. HOW T O USE T HE FRAMEWORK
Overview
The procedure for implementing a component using the API framework involve a mixture of automated code generation, implementing Java code, and customizing the build, by editing
Maven POM files. FIG gives an overview of this development process.
Fig u re 54 .1. Usin g t h e API Co mp o n en t Framewo rk
650
Java API
The starting point for your API component is always a Java API. Generally speaking, in the context of Camel, this usually means a Java client API, which connects to a remote server endpoint. The first question is, where does the Java API come from? Here are a few possibilities:
CHAPT ER 54 . INT RO DUCT IO N T O T HE API CO MPO NENT FRAMEWO RK
Implement the Java API yourself (though this typically would involve a lot of work and is generally not the preferred approach).
Use a third-party Java API. For example, the Apache Camel Box component is based on the third-party Box Java SDK library.
Generate the Java API from a language-neutral interface. For example, the Apache Camel
LinkedIn component obtains its Java API by converting a WADL description of its REST services to Java (using the Apache CXF wad l 2java tool).
Javadoc met adat a
You have the option of providing metadata for the Java API in the form of Javadoc (which is needed for generating code in the API component framework). If you use a third-party Java
API from a Maven repository, you will usually find that the Javadoc is already provided in the
Maven artifact. But even in the cases where Javadoc is not provided, you can easily generate it, using the maven-javad o c-pl ug i n Maven plug-in.
No t e
Currently, there is a limitation in the processing of Javadoc metadata, such that generic nesting is not supported. For example, java. uti l . Li st<Stri ng > is supported, but java. uti l . Li st<java. uti l . Li st<Stri ng >> is not. The workaround is to specify the nested generic type as
java. uti l . Li st<java. uti l . Li st> in a signature file.
Signat ure file met adat a
If for some reason it is not convenient to provide Java API metadata in the form of Javadoc, you have the option of providing metadata in the form of signature files. The signature files consist of a list of method signatures (one method signature per line). These files can be created manually and are needed only at build time.
Note the following points about signature files:
You must create one signature file for each proxy class (Java API class).
The method signatures must not include a rai ses clause. All exceptions raised at runtime are wrapped in a R unti meC amel Excepti o n and returned from the endpoint.
Class names that specify the type of an argument must be fully-qualified class names
(except for the java. l ang . * types). There is no mechanism for importing package names.
Currently, there is a limitation in the signature parser, such that generic nesting is not supported. For example, java. uti l . Li st<Stri ng > is supported, whereas
java. uti l . Li st<java. uti l . Li st<Stri ng >> is not. The workaround is to specify the nested generic type as java. uti l . Li st<java. uti l . Li st>.
651
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The following shows a simple example of the contents of a signature file: public String sayHi(); public String greetMe(String name); public String greetUs(String name1, String name2);
Generat e st art ing code wit h t he Maven archet ype
The easiest way to get started developing an API component is to generate an initial Maven project using the camel -archetype-api -co mpo nent Maven archetype. For details of how to run the archetype, see
Section 55.1, “ Generate Code with the Maven Archetype”
.
After you run the Maven archetype, you will find two sub-projects under the generated
ProjectName directory:
ProjectName-api
This project contains the Java API, which forms the basis of the API component.
When you build this project, it packages up the Java API in a Maven bundle and generates the requisite Javadoc as well. If the Java API and Javadoc are already provided by a third-party, however, you do not need this sub-project.
ProjectName-co mpo nent
This project contains the skeleton code for the API component.
Edit component classes
You can edit the skeleton code in ProjectName-co mpo nent to develop your own component implementation. The following generated classes make up the core of the skeleton implementation:
ComponentNameComponent
ComponentNameEndpoint
ComponentNameConsumer
ComponentNameProducer
ComponentNameConfiguration
Cust omiz e POM files
You also need to edit the Maven POM files to customize the build, and to configure the
camel -api -co mpo nent-maven-pl ug i n Maven plug-in.
Configure t he camel-api-component -maven-plugin
652
CHAPT ER 54 . INT RO DUCT IO N T O T HE API CO MPO NENT FRAMEWO RK
The most important aspect of configuring the POM files is the configuration of the camel -
api -co mpo nent-maven-pl ug i n Maven plug-in. This plug-in is responsible for generating the mapping between API methods and endpoint URIs, and by editing the plug-in configuration, you can customize the mapping.
For example, the following camel -api -co mpo nent-maven-pl ug i n plug-in configuration shows a minimal configuration for an API class called Exampl eJavad o cHel l o :
<configuration>
<apis>
<api>
<apiName>hello-javadoc</apiName>
<proxyClass>org.jboss.fuse.example.api.ExampleJavadocHello</proxyClass
>
<fromJavadoc/>
</api>
</apis>
</configuration>
In this example, the hel l o -javad o c API name is mapped to the Exampl eJavad o cHel l o class, which means you can invoke methods from this class using URIs of the form,
scheme: //hel l o -javad o c/endpoint. The presence of the fro mJavad o c element indicates that the Exampl eJavad o cHel l o class gets its metadata from Javadoc.
OSGi bundle configurat ion
The sample POM for the component sub-project, ProjectName-co mpo nent/po m. xml , is configured to package the component as an OSGi bundle. The component POM includes a sample configuration of the maven-bund l e-pl ug i n. You should customize the configuration of the maven-bund l e-pl ug i n plug-in, to ensure that Maven generates a properly configured OSGi bundle for your component.
Build t he component
When you build the component with Maven, the camel -api -co mpo nent-maven-pl ug i n plug-in automatically generates the API mapping classes (which define the mapping between the Java API and the endpoint URI syntax), placing them into the
targ et/g enerated -cl asses project subdirectory. When you are dealing with a large and complex Java API, this generated code actually constitutes the bulk of the component source code.
When the Maven build completes, the compiled code and resources are packaged up as an
OSGi bundle and stored in your local Maven repository as a Maven artifact.
653
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
CHAPTER 55. GETTING STARTED WITH THE FRAMEWORK
Ab st ract
This chapter explains the basic principles of implementing a Camel component using the API component framework, based on code generated using the camel -archetype-api -
co mpo nent Maven archetype.
55.1. GENERAT E CODE WIT H T HE MAVEN ARCHET YPE
Maven archet ypes
A Maven archetype is analogous to a code wizard: given a few simple parameters, it generates a complete, working Maven project, populated with sample code. You can then use this project as a template, customizing the implementation to create your own application.
T he API component Maven archet ype
The API component framework provides a Maven archetype, camel -archetype-api -
co mpo nent, that can generate starting point code for your own API component implementation. This is the recommended approach to start creating your own API component.
Prerequisit es
The only prerequisites for running the camel -archetype-api -co mpo nent archetype are that Apache Maven is installed and the Maven setti ng s. xml file is configured to use the standard JBoss Fuse repositories. For more details, see appendix "Red Hat JBoss A-MQ
Maven Repositories" in "Installation on Apache Karaf" .
Invoke t he Maven archet ype
To create an Exampl e component, which uses the exampl e URI scheme, invoke the
camel -archetype-api -co mpo nent archetype to generate a new Maven project, as follows: mvn archetype:generate \
-DarchetypeGroupId=org.apache.camel.archetypes \
-DarchetypeArtifactId=camel-archetype-api-component \
-DarchetypeVersion=2.15.1.redhat-621084 \
-DgroupId=org.jboss.fuse.example \
-DartifactId=camel-api-example \
-Dname=Example \
-Dscheme=example \
-Dversion=1.0-SNAPSHOT \
-DinteractiveMode=false
654
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
No t e
The backslash character, \, at the end of each line represents line continuation, which works only on Linux and UNIX platforms. On Windows platforms, remove the backslash and put the arguments all on a single line.
Opt ions
Options are provided to the archetype generation command using the syntax, -
D Name= Value. Most of the options should be set as shown in the preceding mvn
archetype: g enerate command, but a few of the options can be modified, to customize the generated project. The following table shows the options that you can use to customize the generated API component project:
Name g ro upId arti factId name scheme archetypeVersi o n
Descript ion
(Generic Maven option) Sp ecifies the g ro up ID o f the g enerated Maven p ro ject. By d efault, this value also d efines the Java p ackag e name fo r the g enerated classes. Hence, it is a g o o d id ea to cho o se this value to match the Java p ackag e name that yo u want.
(Generic Maven option) Sp ecifies the artifact ID o f the g enerated Maven p ro ject.
The name o f the API co mp o nent. This value is used fo r g enerating class names in the g enerated co d e (hence, it is reco mmend ed that the name sho uld start with a cap ital letter).
The d efault scheme to use in URIs fo r this co mp o nent. Yo u sho uld make sure that this scheme d o es no t co nflict with the scheme o f any existing Camel co mp o nents.
(Generic Maven option) Id eally, this sho uld b e the Ap ache Camel versio n used b y the co ntainer where yo u p lan to d ep lo y the co mp o nent. If necessary, ho wever, yo u can also mo d ify the versio ns o f Maven d ep end encies after yo u have g enerated the p ro ject.
St ruct ure of t he generat ed project
655
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Assuming that the code generation step completes successfully, you should see a new directory, camel -api -exampl e, which contains the new Maven project. If you look inside the camel -api -exampl e directory, you will see that it has the following general structure: camel-api-example/
pom.xml
camel-api-example-api/
camel-api-example-component/
At the top level of the project is an aggregate POM, po m. xml , which is configured to build two sub-projects, as follows:
camel- ap i- examp le- ap i
The API sub-project (named as ArtifactId-api ) holds the Java API which you are about to turn into a component. If you are basing the API component on a Java
API that you wrote yourself, you can put the Java API code directly into this project.
The API sub-project can be used for one or more of the following purposes:
To package up the Java API code (if it is not already available as a Maven package).
To generate Javadoc for the Java API (providing the needed metadata for the API component framework).
To generate the Java API code from an API description (for example, from a WADL description of a REST API).
In some cases, however, you might not need to perform any of these tasks. For example, if the API component is based on a third-party API, which already provides the Java API and Javadoc in a Maven package. In such cases, you can delete the
API sub-project.
camel- ap i- examp le- co mp o n en t
The component sub-project (named as ArtifactId-co mpo nent) holds the implementation of the new API component. This includes the component implementation classes and the configuration of the camel -api -co mpo nent-
maven plug-in (which generates the API mapping classes from the Java API).
55.2. GENERAT ED API SUB-PROJECT
Overview
656
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
Assuming that you generated a new Maven project as described in Section 55.1, “ Generate
Code with the Maven Archetype” , you can now find a Maven sub-project for packaging the
Java API under the camel -api -exampl e/camel -api -exampl e-api project directory. In this section, we take a closer look at the generated example code and describe how it works.
Sample Java API
The generated example code includes a sample Java API, on which the example API component is based. The sample Java API is relatively simple, consisting of just two Hello
World classes: Exampl eJavad o cHel l o and Exampl eFi l eHel l o .
ExampleJavadocHello class
Example 55.1, “ ExampleJavadocHello class”
shows the Exampl eJavad o cHel l o class from the sample Java API. As the name of the class suggests, this particular class is used to show how you can supply mapping metadata from Javadoc.
Examp le 55.1. Examp leJavad o cHello class
// Java
package org.jboss.fuse.example.api;
/**
* Sample API used by Example Component whose method signatures are read from Javadoc.
*/
public class ExampleJavadocHello { public String sayHi() { return "Hello!" ;
} public String greetMe(String name) { return "Hello " + name;
} public String greetUs(String name1, String name2) { return "Hello " + name1 + ", " + name2;
}
}
ExampleFileHello class
Example 55.2, “ ExampleFileHello class” shows the Exampl eFi l eHel l o class from the
sample Java API. As the name of the class suggests, this particular class is used to show how you can supply mapping metadata from a signature file.
Examp le 55.2. Examp leFileHello class
657
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
// Java
package org.jboss.fuse.example.api;
/**
* Sample API used by Example Component whose method signatures are read from File.
*/
public class ExampleFileHello { public String sayHi() { return "Hello!" ;
} public String greetMe(String name) { return "Hello " + name;
} public String greetUs(String name1, String name2) { return "Hello " + name1 + ", " + name2;
}
}
Generat ing t he Javadoc met adat a for ExampleJavadocHello
Because the metadata for Exampl eJavad o cHel l o is provided as Javadoc, it is necessary to generate Javadoc for the sample Java API and install it into the camel -api -exampl e-
api Maven artifact. The API POM file, camel -api -exampl e-api /po m. xml , configures the maven-javad o c-pl ug i n to perform this step automatically during the Maven build.
55.3. GENERAT ED COMPONENT SUB-PROJECT
Overview
The Maven sub-project for building the new component is located under the camel -api -
exampl e/camel -api -exampl e-co mpo nent project directory. In this section, we take a closer look at the generated example code and describe how it works.
Providing t he Java API in t he component POM
The Java API must be provided as a dependency in the component POM. For example, the sample Java API is defined as a dependency in the component POM file, camel -api -
exampl e-co mpo nent/po m. xml , as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
658
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
...
<dependencies>
...
<dependency>
<groupId>org.jboss.fuse.example</groupId>
<artifactId>camel-api-example-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
...
</dependencies>
...
</project>
Providing t he Javadoc met adat a in t he component POM
If you are using Javadoc metadata for all or part of the Java API, you must provide the
Javadoc as a dependency in the component POM. There are two things to note about this dependency:
The Maven coordinates for the Javadoc are almost the same as for the Java API, except that you must also specify a cl assi fi er element, as follows:
<classifier>javadoc</classifier>
You must declare the Javadoc to have pro vi d ed scope, as follows:
<scope>provided</scope>
For example, in the component POM, the Javadoc dependency is defined as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
...
<dependencies>
...
<!-- Component API javadoc in provided scope to read API signatures -->
<dependency>
<groupId>org.jboss.fuse.example</groupId>
<artifactId>camel-api-example-api</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>javadoc</classifier>
<scope>provided</scope>
</dependency>
...
</dependencies>
...
</project>
659
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Defining t he file met adat a for Example File Hello
The metadata for Exampl eFi l eHel l o is provided in a signature file. In general, this file must be created manually, but it has quite a simple format, which consists of a list of method signatures (one on each line). The example code provides the signature file, fi l e-si g -
api . txt, in the directory, camel -api -exampl e-co mpo nent/si g natures, which has the following contents: public String sayHi(); public String greetMe(String name); public String greetUs(String name1, String name2);
Configuring t he API mapping
One of the key features of the API component framework is that it automatically generates the code to perform API mapping. That is, generating stub code that maps endpoint URIs to method invocations on the Java API. The basic inputs to the API mapping are: the Java API, the Javadoc metadata, and/or the signature file metadata.
660
The component that performs the API mapping is the camel -api -co mpo nent-maven-
pl ug i n Maven plug-in, which is configured in the component POM. The following extract from the component POM shows how the camel -api -co mpo nent-maven-pl ug i n plugin is configured:
<?xml version= "1.0" encoding= "UTF-8" ?>
<project xmlns= "http://maven.apache.org/POM/4.0.0"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" >
...
<build>
<defaultGoal> install </defaultGoal>
<plugins>
...
<!-- generate Component source and test source -->
<plugin>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-api-component-maven-plugin </artifactId>
<executions>
<execution>
<id> generate-test-component-classes </id>
<goals>
<goal> fromApis </goal>
</goals>
<configuration>
<apis>
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
<api>
<apiName> hello-file </apiName>
<proxyClass> org.jboss.fuse.example.api.ExampleFileHello
</proxyClass>
<fromSignatureFile> signatures/file-sigapi.txt
</fromSignatureFile>
</api>
<api>
<apiName> hello-javadoc </apiName>
<proxyClass> org.jboss.fuse.example.api.ExampleJavadocHello
</proxyClass
>
<fromJavadoc/>
</api>
</apis>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>
The plug-in is configured by the co nfi g urati o n element, which contains a single api s child element to configure the classes of the Java API. Each API class is configured by an
api element, as follows:
api Name
The API name is a short name for the API class and is used as the endpoint-
prefix part of an endpoint URI.
No t e
If the API consists of just a single Java class, you can leave the api Name element empty, so that the endpoint-prefix becomes redundant, and
pro xyC l ass
The proxy class element specifies the fully-qualified name of the API class.
fro mJavad o c
661
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
If the API class is accompanied by Javadoc metadata, you must indicate this by including the fro mJavad o c element and the Javadoc itself must also be specified in
the Maven file, as a pro vi d ed dependency (see the section called “ Providing the
Javadoc metadata in the component POM” ).
fro mSi g natureFi l e
If the API class is accompanied by signature file metadata, you must indicate this by including the fro mSi g natureFi l e element, where the content of this element specifies the location of the signature file.
No t e
The signature files do not get included in the final package built by Maven, because these files are needed only at build time, not at run time.
Generat ed component implement at ion
The API component consists of the following core classes (which must be implemented for every Camel component), under the camel -api -exampl e-co mpo nent/src/mai n/java directory:
662
Exampl eC o mpo nent
Represents the component itself. This class acts as a factory for endpoint instances
(for example, instances of Exampl eEnd po i nt).
Exampl eEnd po i nt
Represents an endpoint URI. This class acts as a factory for consumer endpoints (for example, Exampl eC o nsumer) and as a factory for producer endpoints (for example,
Exampl eP ro d ucer).
Exampl eC o nsumer
Represents a concrete instance of a consumer endpoint, which is capable of consuming messages from the location specified in the endpoint URI.
Exampl eP ro d ucer
Represents a concrete instance of a producer endpoint, which is capable of sending messages to the location specified in the endpoint URI.
Exampl eC o nfi g urati o n
Can be used to define endpoint URI options. The URI options defined by this configuration class are not tied to any specific API class. That is, you can combine these URI options with any of the API classes or methods. This can be useful, for example, if you need to declare username and password credentials in order to
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
connect to the remote service. The primary purpose of the
Exampl eC o nfi g urati o n class is to provide values for parameters required to instantiate API classes, or classes that implement API interfaces. For example, these could be constructor parameters, or parameter values for a factory method or class.
To implement a URI option, option, in this class, all that you need to do is implement the pair of accessor methods, g etOption and setOption. The component framework automatically parses the endpoint URI and injects the option values at run time.
ExampleComponent class
The generated Exampl eC o mpo nent class is defined as follows:
// Java
package org.jboss.fuse.example; import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.util.component.AbstractApiComponent; import org.jboss.fuse.example.internal.ExampleApiCollection; import org.jboss.fuse.example.internal.ExampleApiName;
/**
* Represents the component that manages {@link ExampleEndpoint}.
*/
@ UriEndpoint(scheme = "example" , consumerClass =
ExampleConsumer.class, consumerPrefix = "consumer" ) public class ExampleComponent extends
AbstractApiComponent<ExampleApiName, ExampleConfiguration,
ExampleApiCollection> { public ExampleComponent() { super (ExampleEndpoint.class, ExampleApiName.class,
ExampleApiCollection.getCollection());
} public ExampleComponent(CamelContext context) { super (context, ExampleEndpoint.class, ExampleApiName.class,
ExampleApiCollection.getCollection());
}
@ Override protected ExampleApiName getApiName(String apiNameStr) throws
IllegalArgumentException { return ExampleApiName.fromValue(apiNameStr);
}
@ Override protected Endpoint createEndpoint(String uri, String methodName,
ExampleApiName apiName,
ExampleConfiguration
663
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
endpointConfiguration) { return new ExampleEndpoint(uri, this , apiName, methodName, endpointConfiguration);
}
}
The important method in this class is createEnd po i nt, which creates new endpoint instances. Typically, you do not need to change any of the default code in the component class. If there are any other objects with the same life cycle as this component, however, you might want to make those objects available from the component class (for example, by adding a methods to create those objects or by injecting those objects into the component).
ExampleEndpoint class
The generated Exampl eEnd po i nt class is defined as follows:
// Java
package org.jboss.fuse.example; import java.util.Map; import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.util.component.AbstractApiEndpoint; import org.apache.camel.util.component.ApiMethod; import org.apache.camel.util.component.ApiMethodPropertiesHelper; import org.jboss.fuse.example.api.ExampleFileHello; import org.jboss.fuse.example.api.ExampleJavadocHello; import org.jboss.fuse.example.internal.ExampleApiCollection; import org.jboss.fuse.example.internal.ExampleApiName; import org.jboss.fuse.example.internal.ExampleConstants; import org.jboss.fuse.example.internal.ExamplePropertiesHelper;
/**
* Represents a Example endpoint.
*/
@ UriEndpoint(scheme = "example" , consumerClass =
ExampleConsumer.class, consumerPrefix = "consumer" ) public class ExampleEndpoint extends
AbstractApiEndpoint<ExampleApiName, ExampleConfiguration> {
// TODO create and manage API proxy
private Object apiProxy; public ExampleEndpoint(String uri, ExampleComponent component,
ExampleApiName apiName, String methodName,
ExampleConfiguration endpointConfiguration) { super (uri, component, apiName, methodName,
ExampleApiCollection.getCollection().getHelper(apiName), endpointConfiguration);
664
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
} public Producer createProducer() throws Exception { return new ExampleProducer( this );
} public Consumer createConsumer(Processor processor) throws
Exception {
// make sure inBody is not set for consumers
if (inBody != null ) { throw new IllegalArgumentException( "Option inBody is not supported for consumer endpoint" );
} final ExampleConsumer consumer = new ExampleConsumer( this , processor);
// also set consumer.* properties
configureConsumer(consumer); return consumer;
}
@ Override protected ApiMethodPropertiesHelper<ExampleConfiguration> getPropertiesHelper() { return ExamplePropertiesHelper.getHelper();
} protected String getThreadProfileName() { return ExampleConstants.THREAD_PROFILE_NAME;
}
@ Override protected void afterConfigureProperties() {
// TODO create API proxy, set connection properties, etc.
switch (apiName) { case HELLO_FILE:
apiProxy = new ExampleFileHello(); break ; case HELLO_JAVADOC:
apiProxy = new ExampleJavadocHello(); break ; default : throw new IllegalArgumentException( "Invalid API name
" + apiName);
}
}
@ Override public Object getApiProxy(ApiMethod method, Map<String, Object> args) { return apiProxy;
}
}
In the context of the API component framework, one of the key steps performed by the endpoint class is to create an API proxy. The API proxy is an instance from the target Java
665
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
API, whose methods are invoked by the endpoint. Because a Java API typically consists of many classes, it is necessary to pick the appropriate API class, based on the end po i nt-
prefi x appearing in the URI (recall that a URI has the general form,
scheme: //endpoint-prefix/endpoint).
ExampleConsumer class
The generated Exampl eC o nsumer class is defined as follows:
// Java package org.jboss.fuse.example; import org.apache.camel.Processor; import org.apache.camel.util.component.AbstractApiConsumer; import org.jboss.fuse.example.internal.ExampleApiName;
/**
* The Example consumer.
*/ public class ExampleConsumer extends
AbstractApiConsumer<ExampleApiName, ExampleConfiguration> {
public ExampleConsumer(ExampleEndpoint endpoint, Processor processor) {
super(endpoint, processor);
}
}
ExampleProducer class
The generated Exampl eP ro d ucer class is defined as follows:
// Java
package org.jboss.fuse.example; import org.apache.camel.util.component.AbstractApiProducer; import org.jboss.fuse.example.internal.ExampleApiName; import org.jboss.fuse.example.internal.ExamplePropertiesHelper;
/**
* The Example producer.
*/
public class ExampleProducer extends
AbstractApiProducer<ExampleApiName, ExampleConfiguration> { public ExampleProducer(ExampleEndpoint endpoint) { super (endpoint, ExamplePropertiesHelper.getHelper());
}
}
666
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
ExampleConfigurat ion class
The generated Exampl eC o nfi g urati o n class is defined as follows:
// Java
package org.jboss.fuse.example; import org.apache.camel.spi.UriParams;
/**
* Component configuration for Example component.
*/
@ UriParams public class ExampleConfiguration {
// TODO add component configuration properties
}
To add a URI option, option, to this class, define a field of the appropriate type, and implement a corresponding pair of accessor methods, g etOption and setOption. The component framework automatically parses the endpoint URI and injects the option values at run time.
No t e
This class is used to define general URI options, which can be combined with any
API method. To define URI options tied to a specific API method, configure extra
options in the API component Maven plug-in. See
Section 56.7, “ Extra Options”
for details.
URI format
Recall the general format of an API component URI:
scheme://endpoint-prefix/endpoint?Option1=Value1& ...& OptionN=ValueN
In general, a URI maps to a specific method invocation on the Java API. For example, suppose you want to invoke the API method, Exampl eJavad o cHel l o . g reetMe("Jane
D o e"), the URI would be constructed, as follows:
[ scheme]
The API component scheme, as specified when you generated the code with the
Maven archetype. In this case, the scheme is exampl e.
[ endpoint-prefix]
667
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The API name, which maps to the API class defined by the camel -api -
co mpo nent-maven-pl ug i n Maven plug-in configuration. For the
Exampl eJavad o cHel l o class, the relevant configuration is:
<configuration>
<apis>
<api>
<apiName>hel l o -javad o c</apiName>
<proxyClass>org.jboss.fuse.example.api.ExampleJavadocHello</prox yClass>
<fromJavadoc/>
</api>
...
</apis>
</configuration>
Which shows that the required endpoint-prefix is hel l o -javad o c.
[ endpoint]
The endpoint maps to the method name, which is g reetMe.
[ Option1= Value1]
The URI options specify method parameters. The g reetMe(Stri ng name) method takes the single parameter, name, which can be specified as name= Jane%20 D o e. If you want to define default values for options, you can do this by overriding the
i nterceptP ro perti es method (see
Section 55.4, “ Programming Model” ).
Putting together the pieces of the URI, we see that we can invoke
Exampl eJavad o cHel l o . g reetMe("Jane D o e") with the following URI: example://hello-javadoc/greetMe?name=Jane%20Doe
Default component inst ance
In order to map the exampl e URI scheme to the default component instance, the Maven archetype creates the following file under the camel -api -exampl e-co mpo nent subproject: src/main/resources/META-
INF/services/org/apache/camel/component/example
This resource file is what enables the Camel core to identify the component associated with the exampl e URI scheme. Whenever you use an exampl e: // URI in a route, Camel searches the classpath to look for the corresponding exampl e resource file. The exampl e file has the following contents: class=org.jboss.fuse.example.ExampleComponent
668
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
This enables the Camel core to create a default instance of the Exampl eC o mpo nent component. The only time you would need to edit this file is if you refactor the name of the component class.
55.4 . PROGRAMMING MODEL
Overview
In the context of the API component framework, the main component implementation classes are derived from base classes in the o rg . apache. camel . uti l . co mpo nent package.
These base classes define some methods which you can (optionally) override when you are implementing your component. In this section, we provide a brief description of those methods and how you might use them in your own component implementation.
Component met hods t o implement
In addition to the generated method implementations (which you usually do not need to modify), you can optionally override some of the following methods in the C o mpo nent class:
d o Start()
(Optional) A callback to create resources for the component during a cold start. An alternative approach is to adopt the strategy of lazy initialization (creating resources only when they are needed). In fact, lazy initialization is often the best strategy, so the
d o Start method is often not needed.
d o Sto p()
(Optional) A callback to invoke code while the component is stopping. Stopping a component means that all of its resources are shut down, internal state is deleted, caches are cleared, and so on.
No t e
Camel guarantees that d o Sto p is always called when the current
C amel C o ntext shuts down, even if the corresponding d o Start was never called.
d o Shutd o wn
(Optional) A callback to invoke code while the C amel C o ntext is shutting down.
Whereas a stopped component can be restarted (with the semantics of a cold start), a component that gets shut down is completely finished. Hence, this callback represents the last chance to free up any resources belonging to the component.
What else t o implement in t he Component class?
669
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
The C o mpo nent class is the natural place to hold references to objects that have the same
(or similar) life cycle to the component object itself. For example, if a component uses OAuth security, it would be natural to hold references to the required OAuth objects in the
C o mpo nent class and to define methods in the C o mpo nent class for creating the OAuth objects.
Endpoint met hods t o implement
You can modify some of the generated methods and, optionally, override some inherited methods in the End po i nt class, as follows:
afterC o nfi g ureP ro perti es()
The main thing you need to do in this method is to create the appropriate type of proxy class (API class), to match the API name. The API name (which has already been extracted from the endpoint URI) is available either through the inherited
api Name field or through the g etApi Name accessor. Typically, you would do a switch on the api Name field to create the corresponding proxy class. For example:
// Java
private Object apiProxy;
...
@ Override protected void afterConfigureProperties() {
// TODO create API proxy, set connection properties, etc.
switch (apiName) { case HELLO_FILE:
apiProxy = new ExampleFileHello(); break ; case HELLO_JAVADOC:
apiProxy = new ExampleJavadocHello(); break ; default : throw new IllegalArgumentException( "Invalid API name " + apiName);
}
}
g etApi P ro xy(Api Metho d metho d , Map<Stri ng , O bject> arg s)
Override this method to return the proxy instance that you created in
afterC o nfi g ureP ro perti es. For example:
@ Override public Object getApiProxy(ApiMethod method, Map<String, Object> args) {
return apiProxy;
}
In special cases, you might want to make the choice of proxy dependent on the API method and arguments. The g etApi P ro xy gives you the flexibility to take this approach, if required.
670
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK d o Start()
(Optional) A callback to create resources during a cold start. Has the same semantics as C o mpo nent. d o Start().
d o Sto p()
(Optional) A callback to invoke code while the component is stopping. Has the same semantics as C o mpo nent. d o Sto p().
d o Shutd o wn
(Optional) A callback to invoke code while the component is shutting down. Has the same semantics as C o mpo nent. d o Shutd o wn().
i nterceptP ro pertyNames(Set<Stri ng > pro pertyNames)
(Optional) The API component framework uses the endpoint URI and supplied option values to determine which method to invoke (ambiguity could be due to overloading and aliases). If the component internally adds options or method parameters, however, the framework might need help in order to determine the right method to invoke. In this case, you must override the i nterceptP ro pertyNames method and add the extra (hidden or implicit) options to the pro pertyNames set. When the complete list of method parameters are provided in the pro pertyNames set, the framework will be able to identify the right method to invoke.
No t e
You can override this method at the level of the End po i nt, P ro d ucer or
C o nsumer class. The basic rule is, if an option affects both producer endpoints and consumer endpoints, override the method in the End po i nt class.
i nterceptP ro perti es(Map<Stri ng ,O bject> pro perti es)
(Optional) By overriding this method, you can modify or set the actual values of the options, before the API method is invoked. For example, you could use this method to set default values for some options, if necessary. In practice, it is often necessary to override both the i nterceptP ro pertyNames method and the i nterceptP ro perty method.
No t e
You can override this method at the level of the End po i nt, P ro d ucer or
C o nsumer class. The basic rule is, if an option affects both producer endpoints and consumer endpoints, override the method in the End po i nt class.
Consumer met hods t o implement
671
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
You can optionally override some inherited methods in the C o nsumer class, as follows:
i nterceptP ro pertyNames(Set<Stri ng > pro pertyNames)
(Optional) The semantics of this method are similar to
End po i nt. i nterceptP ro pertyNames i nterceptP ro perti es(Map<Stri ng ,O bject> pro perti es)
(Optional) The semantics of this method are similar to
End po i nt. i nterceptP ro perti es d o Invo keMetho d (Map<Stri ng , O bject> arg s)
(Optional) Overriding this method enables you to intercept the invocation of the Java
API method. The most common reason for overriding this method is to customize the error handling around the method invocation. For example, a typical approach to overriding d o Invo keMetho d is shown in the following code fragment:
// Java
@ Override protected Object doInvokeMethod(Map<String, Object> args) { try { return super .doInvokeMethod(args);
} catch (RuntimeCamelException e) {
// TODO - Insert custom error handling here!
...
}
}
You should invoke d o Invo keMetho d on the super-class, at some point in this implementation, to ensure that the Java API method gets invoked.
i nterceptR esul t(O bject metho d R esul t, Exchang e resul tExchang e)
(Optional) Do some additional processing on the result of the API method invocation.
For example, you could add custom headers to the Camel exchange object,
resul tExchang e, at this point.
O bject spl i tR esul t(O bject resul t)
(Optional) By default, if the result of the method API invocation is a
java. uti l . C o l l ecti o n object or a Java array, the API component framework splits the result into multiple exchange objects (so that a single invocation result is converted into multiple messages).
If you want to change the default behaviour, you can override the spl i tR esul t method in the consumer endpoint. The resul t argument contains the result of the
API message invocation. If you want to split the result, you should return an array type.
672
CHAPT ER 55. G ET T ING ST ART ED WIT H T HE FRAMEWO RK
No t e
You can also switch off the default splitting behaviour by setting
co nsumer. spl i tR esul t= fal se on the endpoint URI.
Producer met hods t o implement
You can optionally override some inherited methods in the P ro d ucer class, as follows:
i nterceptP ro pertyNames(Set<Stri ng > pro pertyNames)
(Optional) The semantics of this method are similar to
End po i nt. i nterceptP ro pertyNames i nterceptP ro perti es(Map<Stri ng ,O bject> pro perti es)
(Optional) The semantics of this method are similar to
End po i nt. i nterceptP ro perti es d o Invo keMetho d (Map<Stri ng , O bject> arg s)
(Optional) The semantics of this method are similar to
C o nsumer. d o Invo keMetho d .
i nterceptR esul t(O bject metho d R esul t, Exchang e resul tExchang e)
(Optional) The semantics of this method are similar to
C o nsumer. i nterceptR esul t.
No t e
The P ro d ucer. spl i tR esul t() method is never called, so it is not possible to split an API method result in the same way as you can for a consumer endpoint. To get a similar effect for a producer endpoint, you can use Camel's spl i t() DSL command (one of the standard enterprise integration patterns) to split
C o l l ecti o n or array results.
Consumer polling and t hreading model
The default threading model for consumer endpoints in the API component framework is
scheduled poll consumer. This implies that the API method in a consumer endpoint is invoked
55.5. SAMPLE COMPONENT IMPLEMENT AT IONS
673
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Overview
Several of the components distributed with Apache Camel have been implemented with the aid of the API component framework. If you want to learn more about the techniques for implementing Camel components using the framework, it is a good idea to study the source code of these component implementations.
Box.com
The Camel Box component shows how to model and invoke the third party Box.com Java
SDK using the API component framework. It also demonstrates how the framework can be adapted to customize consumer polling, in order to support Box.com's long polling API.
The Camel LinkedIn component demonstrates how to wrap a REST API provided in the form of WADL and XML schemas. The implementation of this component exploits the Apache CXF
wad l 2java Maven plug-in to generate a Java API, which can then be wrapped using the
API component framework.
This approach can be easily replicated to create a Camel component for any SaaS product or platform.
GoogleDrive
The Camel GoogleDrive component demonstrates how the API component framework can handle even Method Object style Google APIs. In this case, URI options are mapped to a method object, which is then invoked by overriding the d o Invo ke method in the consumer and the producer.
Olingo2
The Camel Olingo2 component demonstrates how a callback-based Asynchronous API can be wrapped using the API component framework. This example shows how asynchronous processing can be pushed into underlying resources, like HTTP NIO connections, to make
Camel endpoints more resource efficient.
674
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
CHAPTER 56. CONFIGURING THE API COMPONENT
MAVEN PLUG-IN
Ab st ract
This chapter provides a reference for all of the configuration options available on the API component Maven plug-in.
56.1. OVERVIEW OF T HE PLUG-IN CONFIGURAT ION
Overview
The main purpose of the API component Maven plug-in, camel -api -co mpo nent-maven-
pl ug i n, is to generate the API mapping classes, which implement the mapping between endpoint URIs and API method invocations. By editing the configuration of the API component Maven plug-in, you can customize various aspects of the API mapping.
Locat ion of t he generat ed code
The API mapping classes generated by the API component Maven plug-in are placed in the following location, by default:
ProjectName-component/target/generated-sources/camel-component
Prerequisit es
The main inputs to the API component Maven plug-in are the Java API classes and the
Javadoc metadata. These are made available to the plug-in by declaring them as regular
Maven dependencies (where the Javadoc Maven dependencies should be declared with
pro vi d ed scope).
Set t ing up t he plug-in
The recommended way to set up the API component Maven plug-in is to generate starting point code using the API component archetype. This generates the default plug-in configuration in the ProjectName-co mpo nent/po m. xml file, which you can then customize for your project. The main aspects of the plug-in set-up are, as follows:
1. Maven dependencies must be declared for the requisite Java API and for the
Javadoc metadata.
2. The plug-in's base configuration is declared in the pl ug i nManag ement scope
(which also defines the version of the plug-in to use).
3. The plug-in instance itself is declared and configured.
675
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
4. The bui l d -hel per-maven plug-in is configured to pick up the generated sources from the targ et/g enerated -so urces/camel -co mpo nent directory and include them in the Maven build.
Example base configurat ion
The following POM file extract shows the base configuration of the API component Maven plug-in, as defined in the Maven pl ug i nManag ement scope when the code has been generated using the API component archetype:
<?xml version= "1.0" encoding= "UTF-8" ?>
<project ...>
...
<build>
...
<pluginManagement>
<plugins>
<plugin>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-api-component-maven-plugin </artifactId>
<version> 2.15.1.redhat-621084 </version>
<configuration>
<scheme> ${schemeName} </scheme>
<componentName> ${componentName} </componentName>
<componentPackage> ${componentPackage} </componentPackage>
<outPackage> ${outPackage} </outPackage>
</configuration>
</plugin>
</plugins>
</pluginManagement>
...
</build>
...
</project
The configuration specified in the pl ug i nManag ement scope provides default settings for the plug-in. It does not actually create an instance of a plug-in, but its default settings will be used by any API component plug-in instance.
Base configurat ion
In addition to specifying the plug-in version (in the versi o n element), the preceding base configuration specifies the following configuration properties:
scheme
The URI scheme for this API component.
co mpo nentName
676
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
The name of this API component (which is also used as a prefix for generated class names).
co mpo nentP ackag e
Specifies the Java package containing the classes generated by the API component
Maven archetype. This package is also exported by the default maven-bund l e-
pl ug i n configuration. Hence, if you want a class to be publicly visible, you should place it in this Java package.
o utP ackag e
Specifies the Java package where the generated API mapping classes are placed
(when they are generated by the API component Maven plug-in). By default, this has the value of the co mpo nentName property, with the addition of the . i nternal suffix. This package is declared as private by the default maven-bund l e-pl ug i n configuration. Hence, if you want a class to be private, you should place it in this
Java package.
Example inst ance configurat ion
The following POM file extract shows a sample instance of the API component Maven plugin, which is configured to generate an API mapping during the Maven build:
<?xml version= "1.0" encoding= "UTF-8" ?>
<project xmlns= "http://maven.apache.org/POM/4.0.0"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" >
...
<build>
<defaultGoal> install </defaultGoal>
<plugins>
...
<!-- generate Component source and test source -->
<plugin>
<groupId> org.apache.camel
</groupId>
<artifactId> camel-api-component-maven-plugin </artifactId>
<executions>
<execution>
<id> generate-test-component-classes </id>
<goals>
<goal> fromApis </goal>
</goals>
<configuration>
<apis>
<api>
<apiName> hello-file </apiName>
<proxyClass> org.jboss.fuse.example.api.ExampleFileHello
</proxyClass>
<fromSignatureFile> signatures/file-sig-
677
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
api.txt
</fromSignatureFile>
</api>
<api>
<apiName> hello-javadoc </apiName>
<proxyClass> org.jboss.fuse.example.api.ExampleJavadocHello
</proxyClass
>
<fromJavadoc/>
</api>
</apis>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>
Basic mapping configurat ion
The plug-in is configured by the co nfi g urati o n element, which contains a single api s child element to configure the classes of the Java API. Each API class is configured by an
api element, as follows:
api Name
The API name is a short name for the API class and is used as the endpoint-
prefix part of an endpoint URI.
No t e
If the API consists of just a single Java class, you can leave the api Name element empty, so that the endpoint-prefix becomes redundant, and
pro xyC l ass
This element specifies the fully-qualified name of the API class.
fro mJavad o c
If the API class is accompanied by Javadoc metadata, you must indicate this by including the fro mJavad o c element and the Javadoc itself must also be specified in the Maven file, as a pro vi d ed dependency.
fro mSi g natureFi l e
678
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
If the API class is accompanied by signature file metadata, you must indicate this by including the fro mSi g natureFi l e element, where the content of this element specifies the location of the signature file.
No t e
The signature files do not get included in the final package built by Maven, because these files are needed only at build time, not at run time.
Cust omiz ing t he API mapping
The following aspects of the API mapping can be customized by configuring the plug-in:
Method aliases—you can define additional names (aliases) for an API method using the
al i ases configuration element. For details, see
Section 56.3, “ Method Aliases” .
Nullable options—you can use the nul l abl eO pti o ns configuration element to declare method arguments that default to nul l . For details, see
Section 56.4, “ Nullable Options”
.
Argument name substitution—due to the way the API mapping is implemented, the arguments from all of the methods in a particular API class belong to the same namespace. If two arguments with the same name are declared to be of different type, this leads to a clash. To avoid such name clashes, you can use the substi tuti o ns configuration element to rename method arguments (as they would appear in a URI). For
details, see Section 56.5, “ Argument Name Substitution” .
Excluding arguments—when it comes to mapping Java arguments to URI options, you might sometimes want to exclude certain arguments from the mapping. You can filter out unwanted arguments by specifying either the excl ud eC o nfi g Names element or the
excl ud eC o nfi g T ypes element. For details, see
Section 56.6, “ Excluded Arguments” .
Extra options—sometimes you might want to define extra options, which are not part of the
Java API. You can do this using the extraO pti o ns configuration element.
Configuring Javadoc met adat a
It is possible to filter the Javadoc metadata to ignore or explicitly include certain content. For
details of how to do this, see Section 56.2, “ Javadoc Options” .
Configuring signat ure file met adat a
In cases where no Javadoc is available, you can resort to signature files to supply the needed mapping metadata. The fro mSi g natureFi l e is used to specify the location of the corresponding signature file. It has no special options.
56.2. JAVADOC OPT IONS
679
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Overview
If the metadata for your Java API is provided by Javadoc, it is generally sufficient to specify the fro mJavad o c element with no options. But in cases where you do not want to include the entire Java API in your API mapping, you can filter the Javadoc metadata to customize the content. In other words, because the API component Maven plug-in generates the API mapping by iterating over the Javadoc metadata, it is possible to customize the scope of the generated API mapping by filtering out unwanted parts of the Javadoc metadata.
Synt ax
The fro mJavad o c element can be configured with optional child elements, as follows:
<fromJavadoc>
<excludePackages>PackageNamePattern</excludePackages>
<excludeClasses>ClassNamePattern</excludeClasses>
<excludeMethods>MethodNamePattern</excludeMethods>
<includeMethods>MethodNamePattern</includeMethods>
<includeStaticMethods>[true|false]<includeStaticMethods>
</fromJavadoc>
Scope
As shown in the following extract, the fro mJavad o c element can optionally appear as a child of the api s element and/or as a child of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<fro mJavad o c>. . . </fro mJavad o c>
</api>
<fro mJavad o c>. . . </fro mJavad o c>
...
</apis>
</configuration>
680
You can define the fro mJavad o c element at the following scopes:
As a child of an api element—the fro mJavad o c options apply only to the API class specified by the api element.
As a child of the apis element—the fro mJavad o c options apply to all API classes by default, but can be overridden at the api level.
Opt ions
The following options can be defined as child elements of fro mJavad o c:
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN excl ud eP ackag es
Specifies a regular expression (java. uti l . reg ex syntax) for excluding Java packages from the API mapping model. All package names that match the regular expression are excluded; and all classes derived from the excluded classes are also
ignored. Default value is javax?\. l ang . *.
excl ud eC l asses
Specifies a regular expression (java. uti l . reg ex syntax) for excluding API base classes from the API mapping. All class names that match the regular expression are excluded; and all classes derived from the excluded classes are also ignored.
excl ud eMetho d s
Specifies a regular expression (java. uti l . reg ex syntax) for excluding methods from the API mapping model.
i ncl ud eMetho d s
Specifies a regular expression (java. uti l . reg ex syntax) for including methods from the API mapping model.
i ncl ud eStati cMetho d s
If true, static methods will also be included in the API mapping model. Default is
fal se.
56.3. MET HOD ALIASES
Overview
Often it can be useful to define additional names (aliases) for a given method, in addition to the standard method name that appears in the Java API. A particularly common case is where you allow a property name (such as wi d g et) to be used as an alias for an accessor method (such as g etWi d g et or setWi d g et).
Synt ax
The al i ases element can be defined with one or more al i as child elements, as follows:
<aliases>
<alias>
<methodPattern>MethodPattern</methodPattern>
<methodAlias>Alias</methodAlias>
</alias>
...
</aliases>
681
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Where MethodPattern is a regular expression (java. uti l . reg ex syntax) for matching method names from the Java API, and the pattern typically includes capturing groups. The
Alias is the replacement expression (for use in a URI), which can use the text from the preceding capturing groups (for example, specified as $1, $2, or $3 for the text from the first, second, or third capturing group).
Scope
As shown in the following extract, the al i ases element can optionally appear as a child of the api s element and/or as a child of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<al i ases>. . . </al i ases>
</api>
<al i ases>. . . </al i ases>
...
</apis>
</configuration>
You can define the al i ases element at the following scopes:
As a child of an api element—the al i ases mappings apply only to the API class specified by the api element.
As a child of the apis element—the al i ases mappings apply to all API classes by default, but can be overridden at the api level.
Example
The following example shows how to generate aliases for the common get/set bean method pattern:
<aliases>
<alias>
<methodPattern> [gs]et(.+) </methodPattern>
<methodAlias> $1 </methodAlias>
</alias>
</aliases>
With the preceding alias definition, you could use wi d g et as an alias for either of the methods g etWi d g et or setWi d g et. Note the use of a capturing group, (. + ), to capture the latter part of the method name (for example, Wi d g et).
56.4 . NULLABLE OPT IONS
682
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
Overview
In some cases, it can make sense to let method arguments default to nul l . But this is not allowed by default. If you want to allow some of your method arguments from the Java API to take nul l values, you must declare this explicitly using the nul l abl eO pti o ns element.
Synt ax
The nul l abl eO pti o ns element can be defined with one or more nul l abl eO pti o n child elements, as follows:
<nullableOptions>
<nullableOption>ArgumentName</nullableOption>
...
</nullableOptions>
Where ArgumentName is the name of a method argument from the Java API.
Scope
As shown in the following extract, the nul l abl eO pti o ns element can optionally appear as a child of the api s element and/or as a child of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<nul l abl eO pti o ns>. . . </nul l abl eO pti o ns>
</api>
...
<nul l abl eO pti o ns>. . . </nul l abl eO pti o ns>
</apis>
</configuration>
You can define the nul l abl eO pti o ns element at the following scopes:
As a child of an api element—the nul l abl eO pti o ns mappings apply only to the API class specified by the api element.
As a child of the apis element—the nul l abl eO pti o ns mappings apply to all API classes by default, but can be overridden at the api level.
Example
The following example shows the nullable options declared for the C o mpani esR eso urce proxy class from the Apache Camel LinkedIn component:
683
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
<nullableOptions>
<nullableOption> companySizes </nullableOption>
<nullableOption> count </nullableOption>
<nullableOption> email_domain </nullableOption>
<nullableOption> end_timestamp </nullableOption>
<nullableOption> event_type </nullableOption>
<nullableOption> geos </nullableOption>
<nullableOption> industries </nullableOption>
<nullableOption> is_company_admin </nullableOption>
<nullableOption> jobFunc </nullableOption>
<nullableOption> secure_urls </nullableOption>
<nullableOption> seniorities </nullableOption>
<nullableOption> start </nullableOption>
<nullableOption> start_timestamp </nullableOption>
<nullableOption> statistics_update_key </nullableOption>
<nullableOption> time_granularity </nullableOption>
</nullableOptions>
56.5. ARGUMENT NAME SUBST IT UT ION
Overview
The API component framework requires that URI option names are unique within each proxy
class (Java API class). This is not always the case for method argument names, however. For example, consider the following Java methods in an API class: public void doSomething( int id, String name); public void doSomethingElse( int id, String name);
When you build your Maven project, the camel -api -co mpo nent-maven-pl ug i n generates the configuration class, ProxyClassEnd po i ntC o nfi g urati o n, which contains getter and setter methods for all of the arguments in the ProxyClass class. For example, given the preceding methods, the plug-in would generate the following getter and setter methods in the configuration class: public int getId(); public void setId(int id); public String getName(); public void setName(String name);
But what happens, if the i d argument appears multiple times as different types, as in the following example: public void doSomething(i nt id, String name); public void doSomethingElse(i nt id, String name); public String lookupByID(Stri ng id);
684
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
In this case, the code generation would fail, because you cannot define a g etId method that returns i nt and a g etId method that returns Stri ng in the same scope. The solution to this problem is to use argument name substitution to customize the mapping of argument names to URI option names.
Synt ax
The substi tuti o ns element can be defined with one or more substi tuti o n child elements, as follows:
<substitutions>
<substitution>
<method>MethodPattern</method>
<argName>ArgumentNamePattern</argName>
<argType>TypeNamePattern</argType>
<replacement>SubstituteArgName</replacement>
<replaceWithType>[true|false]</replaceWithType>
</substitution>
...
</substitutions>
Where the arg T ype element and the repl aceWi thT ype element are optional and can be omitted.
Scope
As shown in the following extract, the substi tuti o ns element can optionally appear as a child of the api s element and/or as a child of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<substi tuti o ns>. . . </substi tuti o ns>
</api>
<substi tuti o ns>. . . </substi tuti o ns>
...
</apis>
</configuration>
You can define the substi tuti o ns element at the following scopes:
As a child of an api element—the substi tuti o ns apply only to the API class specified by the api element.
As a child of the apis element—the substi tuti o ns apply to all API classes by default, but can be overridden at the api level.
Child element s
685
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
Each substi tuti o n element can be defined with the following child elements:
metho d
Specifies a regular expression (java. uti l . reg ex syntax) to match a method name from the Java API.
arg Name
Specifies a regular expression (java. uti l . reg ex syntax) to match an argument name from the matched method, where the pattern typically includes capturing groups.
arg T ype
(Optional) Specifies a regular expression (java. uti l . reg ex syntax) to match the type of the argument. If you set the repl aceWi thT ype option to true, you would typically use capturing groups in this regular expression.
repl acement
Given a particular match of the metho d pattern, arg Name pattern, and (optionally)
arg T ype pattern, the repl acement element defines the substitute argument name
(for use in a URI). The replacement text can be constructed using strings captured from the arg Name regular expression pattern (using the syntax, $1, $2, $3 to insert the first, second, or third capturing group, respectively). Alternatively, the replacement text can be constructed using strings captured from the arg T ype regular expression pattern, if you set the repl aceWi thT ype option to true.
repl aceWi thT ype
When true, specifies that the replacement text is constructed using strings captured from the arg T ype regular expression. Defaults to fal se.
Example
The following substitution example modifies every argument of java. l ang . Stri ng type, by adding the suffix, P aram to the argument name:
<substitutions>
<substitution>
<method> ^.+$ </method>
<argName> ^.+$ </argName>
<argType> java.lang.String
</argType>
<replacement> $1Param </replacement>
<replaceWithType> false </replaceWithType>
</substitution>
</substitutions>
686
For example, given the following method signature:
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
public String greetUs(String name1, String name2);
The arguments of this method would be specified through the options, name1P aram and
name2P aram, in the endpoint URI.
56.6. EXCLUDED ARGUMENT S
Overview
Sometimes, you might need to exclude certain arguments, when it comes to mapping Java arguments to URI options. You can filter out unwanted arguments by specifying either the
excl ud eC o nfi g Names element or the excl ud eC o nfi g T ypes element in the camel -
api -co mpo nent-maven-pl ug i n plug-in configuration.
Synt ax
The excl ud eC o nfi g Names element and the excl ud eC o nfi g T ypes element are specified as follows:
<excludeConfigNames>ArgumentNamePattern</excludeConfigNames>
<excludeConfigTypes>TypeNamePattern</excludeConfigTypes>
Where ArgumentNamePattern and TypeNamePattern are regular expressions that match the argument name and the argument type, respectively.
Scope
As shown in the following extract, the excl ud eC o nfi g Names element and the
excl ud eC o nfi g T ypes element can optionally appear as children of the api s element and/or as children of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<excl ud eC o nfi g Names>. . . </excl ud eC o nfi g Names>
<excl ud eC o nfi g T ypes>. . . </excl ud eC o nfi g T ypes>
</api>
<excl ud eC o nfi g Names>. . . </excl ud eC o nfi g Names>
<excl ud eC o nfi g T ypes>. . . </excl ud eC o nfi g T ypes>
...
</apis>
</configuration>
You can define the excl ud eC o nfi g Names element and the excl ud eC o nfi g T ypes element at the following scopes:
687
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
As a child of an api element—the exclusions apply only to the API class specified by the
api element.
As a child of the apis element—the exclusions apply to all API classes by default, but can be overridden at the api level.
Element s
The following elements can be used to exclude arguments from the API mapping (so that they are unavailable as URI options):
excl ud eC o nfi g Names
Specifies a regular expression (java. uti l . reg ex syntax) for excluding arguments, based on matching the argument name.
excl ud eC o nfi g T ypes
Specifies a regular expression (java. uti l . reg ex syntax) for excluding arguments, based on matching the argument type.
56.7. EXT RA OPT IONS
Overview
The extraO pti o ns options are usually used to either compute or hide complex API parameters by providing simpler options instead. For example, the API method might take a
POJO option, that could be provided more easily as parts of the POJO in the URI. The component could do this by adding the parts as extra options, and creating the POJO parameter internally. To complete the implementation of these extra options, you also need to override the i nterceptP ro perti es method in the End po i ntC o nsumer and/or
End po i ntP ro d ucer classes (see
Section 55.4, “ Programming Model”
).
Synt ax
The extraO pti o ns element can be defined with one or more extraO pti o n child elements, as follows:
<extraOptions>
<extraOption>
<type>TypeName</type>
<name>OptionName</name>
</extraOption>
</extraOptions>
Where TypeName is the fully-qualified type name of the extra option and OptionName is the name of the extra URI option.
688
CHAPT ER 56 . CO NFIG URING T HE API CO MPO NENT MAVEN PLUG - IN
Scope
As shown in the following extract, the extraO pti o ns element can optionally appear as a child of the api s element and/or as a child of api elements:
<configuration>
<apis>
<api>
<apiName>...</apiName>
...
<extraO pti o ns>. . . </extraO pti o ns>
</api>
<extraO pti o ns>. . . </extraO pti o ns>
...
</apis>
</configuration>
You can define the extraO pti o ns element at the following scopes:
As a child of an api element—the extraO pti o ns apply only to the API class specified by the api element.
As a child of the apis element—the extraO pti o ns apply to all API classes by default, but can be overridden at the api level.
Child element s
Each extraO pti o ns element can be defined with the following child elements:
type
Specifies the fully-qualified type name of the extra option.
name
Specifies the option name, as it would appear in an endpoint URI.
Example
The following example defines an extra URI option, custo mO pti o n, which is of
java. uti l . l i st<Stri ng > type:
<extraOptions>
<extraOption>
<type> java.util.List& lt;String& gt; </type>
<name> customOption </name>
</extraOption>
</extraOptions>
689
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
INDEX
Symbols
@ Co n vert er, Imp lemen t an an n o t at ed co n vert er class
A
Asyn cCallb ack,
Asyn ch ro n o u s p ro cessin g
asyn ch ro n o u s p ro d u cer imp lemen t in g ,
Ho w t o imp lemen t an asyn ch ro n o u s p ro d u cer
Asyn cPro cesso r,
Asyn ch ro n o u s p ro cessin g
au t o - d isco very co n f ig u rat io n ,
Co n f ig u rin g au t o - d isco very
C
Co mp o n en t
creat eEn d p o in t ( ) , URI p arsin g
d ef in it io n , T h e Co mp o n en t in t erf ace
met h o d s, Co mp o n en t met h o d s
co mp o n en t p ref ix,
Co mp o n en t co mp o n en t s, Co mp o n en t
b ean p ro p ert ies,
Def in e b ean p ro p ert ies o n yo u r co mp o n en t class
co n f ig u rin g , In st allin g an d co n f ig u rin g t h e co mp o n en t
imp lemen t at io n st ep s, Imp lemen t at io n st ep s
in st allin g , In st allin g an d co n f ig u rin g t h e co mp o n en t
in t erf aces t o imp lemen t ,
Wh ich in t erf aces d o yo u n eed t o imp lemen t ?
p aramet er in ject io n ,
Sp rin g co n f ig u rat io n ,
Co n f ig u re t h e co mp o n en t in Sp rin g
Co n su mer,
690
even t - d riven ,
p o llin g ,
sch ed u led , Sch ed u led p o ll p at t ern
t h read in g ,
D
Def au lt Co mp o n en t
creat eEn d p o in t ( ) , URI p arsin g
creat ePo llin g Co n su mer( ) ,
Even t - d riven en d p o in t imp lemen t at io n
g et CamelCo n ext ( ) ,
Even t - d riven en d p o in t imp lemen t at io n
g et Co mp o n en t ( ) ,
E
En d p o in t ,
creat eCo n su mer( ) ,
En d p o in t met h o d s creat eExch an g e( ) , En d p o in t met h o d s
creat ePo llin g Co n su mer( ) ,
creat ePro d u cer( ) ,
g et CamelCo n t ext ( ) ,
En d p o in t met h o d s g et En d p o in t URI( ) , En d p o in t met h o d s
in t erf ace d ef in it io n ,
T h e En d p o in t in t erf ace
isLen ien t Pro p ert ies( ) ,
isSin g let o n ( ) ,
En d p o in t met h o d s set CamelCo n t ext ( ) , En d p o in t met h o d s
INDEX
691
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
692 en d p o in t even t - d riven ,
Even t - d riven en d p o in t imp lemen t at io n
sch ed u led , Sch ed u led p o ll en d p o in t imp lemen t at io n
en d p o in t s, En d p o in t
Exch an g e,
,
T h e Exch an g e in t erf ace
co p y( ) , Exch an g e met h o d s
g et Exch an g eId ( ) ,
g et In ( ) ,
g et Pro p ert ies( ) ,
Exch an g e met h o d s g et Pro p ert y( ) , Exch an g e met h o d s
g et Un it O f Wo rk( ) ,
set Pro p ert y( ) ,
Exch an g e met h o d s set Un it O f Wo rk( ) , Exch an g e met h o d s
exch an g e in cap ab le,
T est in g t h e exch an g e p at t ern
o u t cap ab le,
T est in g t h e exch an g e p at t ern
exch an g e p ro p ert ies
accessin g , Wrap p in g t h e exch an g e accesso rs
Exch an g eHelp er, T h e Exch an g eHelp er Class
g et Co n t en t T yp e( ) , G et t h e In messag e' s MIME co n t en t t yp e
g et Man d at o ryHead er( ) , Accessin g messag e h ead ers
exch an g e accesso rs
isIn Cap ab le( ) ,
T est in g t h e exch an g e p at t ern
isO u t Cap ab le( ) ,
T est in g t h e exch an g e p at t ern
reso lveEn d p o in t ( ) , Reso lve an en d p o in t
I
in messag e
MIME t yp e, G et t h e In messag e' s MIME co n t en t t yp e
M
Messag e,
g et Head er( ) , Accessin g messag e h ead ers
messag e h ead ers
accessin g , Accessin g messag e h ead ers
P
p ip elin e,
Pro cesso r,
imp lemen t in g ,
Imp lemen t in g t h e Pro cesso r in t erf ace
creat eExch an g e( ) , Pro d u cer met h o d s g et En d p o in t ( ) , Pro d u cer met h o d s
INDEX
693
Red Hat JBoss Fuse 6 .2 .1 Apache Camel Development G uide
p ro cess( ) , Pro d u cer met h o d s
p ro d u cers
asyn ch ro n o u s, Asyn ch ro n o u s p ro d u cer
syn ch ro n o u s, Syn ch ro n o u s p ro d u cer
S
Sch ed u led Po llEn d p o in t ,
Sch ed u led p o ll en d p o in t imp lemen t at io n
simp le p ro cesso r imp lemen t in g ,
Imp lemen t in g t h e Pro cesso r in t erf ace
syn ch ro n o u s p ro d u cer imp lemen t in g ,
Ho w t o imp lemen t a syn ch ro n o u s p ro d u cer
T
t yp e co n versio n ru n t ime p ro cess,
T yp e co n versio n p ro cess
t yp e co n vert er
an n o t at in g t h e imp lemen t at io n , Imp lemen t an an n o t at ed co n vert er class
d isco very f ile,
Creat e a T yp eCo n vert er f ile
imp lemen t at io n st ep s, Ho w t o imp lemen t a t yp e co n vert er
mat er, Mast er t yp e co n vert er
p ackag in g , Packag e t h e t yp e co n vert er
slave,
T yp eCo n vert er,
T yp e co n vert er in t erf ace
T yp eCo n vert erLo ad er, T yp e co n vert er lo ad er
U
u seIn t ro sp ect io n O n En d p o in t ( ) ,
Disab lin g en d p o in t p aramet er in ject io n
694
W
wire t ap p at t ern ,
wire t ap p at t ern , Syst em Man ag emen t
INDEX
695
Download
Advertisement
Key features
Connect different systems and applications
Define routing rules for messages
Transform data between different formats
Supports Java and XML DSLs
Provides a wide range of Enterprise Integration Patterns (EIP)
Offers a flexible and extensible architecture
Frequently asked questions
Apache Camel is an open-source integration framework that enables you to connect different systems and applications together.
You can define routing rules in Camel using either a Java DSL or an XML DSL.
Key features of Camel include the ability to connect different systems and applications, define routing rules for messages, and transform data between different formats. Camel also provides a wide range of Enterprise Integration Patterns (EIP) and offers a flexible and extensible architecture.