Você está na página 1de 13

Part 2 Core Camel

n part 1, we guided you through what we consider introductory topics in

Camel. They were topics you absolutely needed to know to use Camel. In this next part, well cover in depth the core features of Camel. oull need many of these features when using Camel in real!world applications. In chapter " well take a look at the data in the messages being routed by Camel. In particular, well look at how you can transform this data to other formats using Camel. Camel has great support for integrating beans into your routing applications. In chapter # well look at the many ways beans can be used in Camel applications. In complex enterprise systems, lots of things can go wrong. This is why Camel features an extensive set of error!handling abilities. In chapter $ well discuss these in detail. In chapter % well take a look at another important topic in application development& testing. 'ell look at the testing facilities shipped with Camel. ou can use these features for testing your own Camel applications or applications based on other stacks. Components are the main extension mechanism in Camel. (s such, they include functionality to connect to many different transports, ()Is, and other extensions to Camels core. Chapter * covers the most heavily used components that ship with Camel. The last chapter of this part revisits the important topic of enterprise integration patterns +,I)s- in Camel. .ack in chapter /, we covered some of the simpler ,I)s0 in chapter 1, well look at several of the more complex ,I)s. Licensed to Ian White <ian@biancashouse.com> Licensed to Ian White <ian@biancashouse.com>
61

Transforming data with Camel


In the previous chapter, we covered routing, which is the single most important feature any integration kit must provide. In this chapter, well take a look at the second most important feature& data or message transformation. 2ust as in the real world, where people speak different languages, the IT world speaks different protocols. 3oftware engineers regularly need to act as mediators between various protocols when IT systems must be integrated. To address this, the data models used by the protocols must be transformed from one form to another, adapting to whatever protocol the receiver understands. 4ediation and data transformation is a key feature in any integration kit, including Camel.

This chapter covers


Transforming data using EIPs and Java Transforming XML data Transforming using well-known data formats Writing your own data formats for transformations Understanding the amel ty!e-"onverter me"hanism Licensed to Ian White <ian@biancashouse.com>

62 CHAPTER 3 Transforming data with Camel

In this chapter, youll learn all about how Camel can help you with your data transformation challenges. 'ell start with a brief overview of data transformation in Camel and then look at how you can transform data into any custom format you may have. Then well look at some Camel components that are speciali5ed for transforming 647 data and other well!known data formats. 'ell end the chapter by looking into Camels type!converter mechanism, which supports implicitly and explicitly type coercing. (fter reading this chapter, youll know how to tackle any data transformation youre faced with and which Camel solution to leverage.

3.1 Data transformation overview


Camel provides many techni8ues for data transformation, and well cover them shortly. .ut first well start with an overview of data transformation in Camel. Data transformation is a broad term that covers two types of transformation& Data format transformation 9The data format of the message body is transformed from one form to another. :or example, a C3; record is formatted as 647. Data type transformation 9The data type of the message body is transformed from one type to another. :or example a java.lang.String is transformed into a javax.jms.TextMessage. :igure ".1 illustrates the principle of transforming a message body from one form into another. This transformation can involve any combination of format and type transformations. In most cases, the data transformation youll face with Camel is format transformation, where you have to mediate between two protocols. Camel has a builtin type!converter mechanism that can automatically convert between types, which greatly reduces the need for end users to deal with type transformations. Camel has many data!transformation features. 'ell introduce them in the following section, and then look at them one by one. (fter reading this chapter, youll have a solid understanding of how to use Camel to transform your data.

3.1.1 Data transformation with Camel In Camel, data transformation typically takes places in the six ways listed in table ".1.
Message Message body Message Message body Transform Figure 3.1 Camel

offers many features for transforming data from one form to another.

Licensed to Ian White <ian@biancashouse.com>


Transforming data using EIPs and Java 63

In this chapter, well cover the first five of the data transformation methods listed in table ".1. 'ell leave the last one for chapter 11.

3.2 Transforming data using EIPs and Java


<ata mapping is the process of mapping between two distinct data models, and its a key factor in data integration. There are many existing standards for data models, governed by various organi5ations or committees. (s such, youll often find yourself needing to map from a companys custom data model to a standard data model. Camel provides great freedom in data mapping because it allows you to use 2ava code9you arent limited to using a particular data mapping tool that at first might seem elegant but that turns out to make things impossible. In this section, well look at how you can map data using a Processor, which is a Camel ()I. Camel can also use beans for mapping, which is a good practice, because it allows your mapping logic to be independent of the Camel ()I.

3.2.1 sing the !essage Translator EIP The 4essage Translator ,I) is illustrated in figure "./. This pattern covers translating a message from one format to another. Its the e8uivalent of the (dapter pattern from the =ang of :our book.
Table 3.1 Six ays data transformation ty!i"ally ta#es !la"e in Camel Transformation $es"ri!tion #ata transformation in

routes $ou "an e%!li"itly enfor"e transformation in the route using the Message Translator or the ontent Enri"her EIPs& This gives you the !ower to do data ma!!ing using regular Java "ode& We'll "over this in se"tion (&)& #ata transformation using "om!onents amel !rovides a range of "om!onents for transformation* su"h as the X+LT "om!onent for XML transformation& We'll dive into this in se"tion (&(& #ata transformation using data formats #ata formats are amel transformers that "ome in !airs to transform data ,a"k and forth ,etween well-known formats& #ata transformation using tem!lates amel !rovides a range of "om!onents for transforming using tem!lates* su"h as -!a"he .elo"ity& We'll look at this in se"tion (&/& #ata ty!e transformation using amel's ty!e"onverter me"hanism amel has an ela,orate ty!e-"onverter me"hanism that a"tivates on demand& This is "onvenient when you need to "onvert from "ommon ty!es su"h as java.lang.Integer to java.lang.String or even from java.io.File to java.lang.String& Ty!e "onverters are "overed in se"tion (&0& Message transformation in "om!onent ada!ters amel's many "om!onents ada!t to various "ommonly used !roto"ols and* as su"h* need to ,e a,le to transform messages as they travel to and from those !roto"ols& 1ften these "om!onents use a "om,ination of "ustom data transformations and ty!e "onverters& This ha!!ens seamlessly* and only "om!onent writers need to worry a,out it& We'll "over writing "ustom "om!onents in "ha!ter 22&

Licensed to Ian White <ian@biancashouse.com>


64 CHAPTER 3 Transforming data with Camel
%&TE The =ang of :our book is Design Patterns: Elements of Reusable Object

Oriented !oftware by ,rich =amma, >ichard ?elm, >alph 2ohnson, and 2ohn ;lissides. 3ee the @<esign )atternsA 'ikipedia article for more information& http&BBen.wikipedia.orgBwikiB<esignC)atternsC+book-. Camel provides three ways of using this pattern& Dsing a Processor Dsing beans Dsing <transform> 'ell look at them each in turn.
TRA%SF&R'(%) *S(%) A PR&CESS&R

The Camel Processor is an interface defined in org.apache.camel.Processor with a single method&


public void process(Exchange exchange) throws Exception;

The Processor is a low!level ()I where you work directly on the Camel Exchange instance. It gives you full access to all Camels moving parts from the CamelContext, which you can obtain Exchange using the getCamelContext method. 7ets look at an example. (t >ider (uto )arts youve been asked to generate daily reports of newly received orders to be outputted to a C3; file. The company uses a custom format for order entries, but to make things easy, they already have an ?TT) service that returns a list of orders for whatever date you input. The challenge you face is mapping the returned data from the ?TT) service to a C3; format and writing the report to a file. .ecause you want to get started on a prototype 8uickly, you decide to use the Camel Processor.
import org.apache.camel.Exchange; import org.apache.camel.Processor; public class OrderToCsvProcessor implements Processor { public void process(Exchange exchange) throws Exception { String custom = exchange.getIn() .getBody(String.class);

+isting 3.1 *sing a Processor to translate from a "ustom format to CS, format
Message translator

Incoming message Translated message

Figure 3.- (n the 'essage Translator E(P. an in"oming message goes through a translator and "omes out as a translated message.

Gets custom payload

B
Licensed to Ian White <ian@biancashouse.com>
Transforming data using EIPs and Java 65
String id = custom.substring(0, 9); String customerId = custom.substring(10, 19); String date = custom.substring(20, 29); String items = custom.substring(30); String[] itemIds = items.split("@"); StringBuilder csv = new StringBuilder(); csv.append(id.trim()); csv.append(",").append(date.trim()); csv.append(",").append(customerId.trim()); for (String item : itemIds) { csv.append(",").append(item.trim()); } exchange.getIn().setBody(csv.toString()); } }

:irst you grab the custom format payload from the exchange B. Its a String type, so you pass String in as the parameter to have the payload returned as a String. Then you extract data from the custom format to the local variables C. The custom format could be anything, but in this example its a fixed!length custom format. Then you map the C3; format by building a string with comma!separated values D. :inally, you

replace the custom payload with your new C3; payload E. ou can use the OrderToCsvProcessor from listing ".1 in a Camel route as follows&
from("quartz://report?cron=0+0+6+*+*+?") .to("http://riders.com/orders/cmd=received&date=yesterday") .process(new OrderToCsvProcessor()) .to("file://riders/orders?fileName=report-${header.Date}.csv");

The preceding route uses Euart5 to schedule a Fob to run once a day at % a.m. It then invokes the ?TT) service to retrieve the orders received yesterday, which are returned in the custom format. Gext, it uses OrderToCSVProcessor to map from the custom format to C3; format before writing the result to a file. The e8uivalent route in 3pring 647 is as follows&
<bean id="csvProcessor" class="camelinaction.OrderToCsvProcessor"/> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="quartz://report?cron=0+0+6+*+*+?"/> <to uri="http://riders.com/orders/cmd=received&amp;date=yesterday"/> <process ref="csvProcessor"/> <to uri="file://riders/orders?fileName=report-${header.Date}.csv"/> </route> </camelContext>

ou can try this example yourself9weve provided a little unit test with the books source code. =o to the chapter"Btransform directory, and run these 4aven goals&
mvn test -Dtest=OrderToCsvProcessorTest mvn test -Dtest=SpringOrderToCsvProcessorTest

(fter the test runs, a report file is written in the targetBordersBreceived directory.

Extracts data to local variables Maps to

CSV format

Replaces payload with CSV payload

E
Licensed to Ian White <ian@biancashouse.com>
66 CHAPTER 3 Transforming data with Camel

Dsing a processor has one disadvantage& youre re8uired to use the Camel ()I. In the next section, well look at how to avoid this by using a bean.
TRA%SF&R'(%) *S(%) /EA%S

Dsing beans is a great practice because it allows you to use any 2ava code and library you wish. Camel imposes no restrictions whatsoever. Camel can invoke any bean you choose, so you can use existing beans without having to rewrite or recompile them. 7ets try using a bean instead of a Processor.
public class OrderToCsvBean { public static String map(String custom) { String id = custom.substring(0, 9); String customerId = custom.substring(10, 19); String date = custom.substring(20, 29); String items = custom.substring(30); String[] itemIds = items.split("@"); StringBuilder csv = new StringBuilder(); csv.append(id.trim()); csv.append(",").append(date.trim()); csv.append(",").append(customerId.trim()); for (String item : itemIds) { csv.append(",").append(item.trim()); } return csv.toString(); } }

+isting 3.- *sing a bean to translate from a "ustom format to CS, format

*sing the getIn and getOut methods on ex"hanges


The amel Exchange defines two methods for retrieving messages3 getIn and getOut& The getIn method returns the in"oming message* and the getOut method a""esses the out,ound message& There are two s"enarios where the amel end user will have to de"ide among using these methods3 - read-only s"enario* su"h as when you're logging the in"oming message - write s"enario* su"h as when you're transforming the message In the se"ond s"enario* you'd assume getOut should ,e used& That's "orre"t a""ording to theory* ,ut in !ra"ti"e there's a "ommon !itfall when using getOut3 the in"oming message headers and atta"hments will ,e lost& This is often not what you want* so you must "o!y the headers and atta"hments from the in"oming message to the outgoing message* whi"h "an ,e tedious& The alternative is to set the "hanges dire"tly on the in"oming message using getIn* and not to use getOut at all& This is the !ra"ti"e we use in this ,ook&

Extracts data to local variables Returns CSV payload

C
Licensed to Ian White <ian@biancashouse.com>
Transforming data using EIPs and Java 67

The first noticeable difference between listings ".1 and "./ is that listing "./ doesnt use any Camel imports. This means your bean is totally independent of the Camel ()I. The next difference is that you can name the method signature in listing "./9in this case its a static method named map. The method signature defines the contract, which means that the first parameter, (String custom), is the message body youre going to use for translation. The method returns a String, which means the translated data will be a String type. (t runtime, Camel binds to this method signature. 'e wont go into any more details here0 well cover much more about using beans in chapter #. The actual mapping B is the same as with the processor. (t the end, you return the mapping output C.

ou can use OrderToCsvBean in a Camel route as shown here&


from("quartz://report?cron=0+0+6+*+*+?") .to("http://riders.com/orders/cmd=received&date=yesterday") .bean(new OrderToCsvBean()) .to("file://riders/orders?fileName=report-${header.Date}.csv");

The e8uivalent route in 3pring 647 is as follows&


<bean id="csvBean" class="camelinaction.OrderToCsvBean"/> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="quartz://report?cron=0+0+6+*+*+?"/> <to uri="http://riders.com/orders/cmd=received&amp;date=yesterday"/> <bean ref="csvBean"/> <to uri="file://riders/orders?fileName=report-${header.Date}.csv"/> </route> </camelContext>

ou can try this example from the chapter"Btransform directory by using the following 4aven goals&
mvn test -Dtest=OrderToCsvBeanTest mvn test -Dtest=SpringOrderToCsvBeanTest

It will generate a test report file in the targetBordersBreceived directory. (nother advantage of using beans over processors for mappings is that unit testing is much easier. :or example, listing "./ doesnt re8uire the use of Camel at all, as opposed to listing ".1 where you need to create and pass in an Exchange instance. 'ell leave the beans for now, because theyre covered extensively in the next chapter. .ut you should keep in mind that beans are very useful for doing message transformation.
TRA%SF&R'(%) *S(%) THE TRA%SF&R'01 'ETH&$ FR&' THE 2A,A $S+

Transform() is a method in the 2ava <37 that can be used in Camel routes to transform messages. .y allowing the use of expressions, transform() permits great flexibility,

and using expressions directly within the <37 can sometimes save time. 7ets look at a little example. Licensed to Ian White <ian@biancashouse.com>
68 CHAPTER 3 Transforming data with Camel

3uppose you need to prepare some text for ?T47 formatting by replacing all line breaks with a <br/> tag. This can be done with a built!in Camel expression that searches and replaces using regular expressions&
from("direct:start") .transform(body().regexReplaceAll("\n", "<br/>")) .to("mock:result");

'hat this route does is use the transform() method to tell Camel that the message should be transformed using an expression. Camel provides what is know as the .uilder pattern to build expressions from individual expressions. This is done by chaining together method calls, which is the essence of the .uilder pattern. %&TE :or more information on the .uilder pattern, see the 'ikipedia article& http&BBen.wikipedia.orgBwikiB.uilderCpattern. In this example, you combine body() and regexReplaceAll(). The expression should be read as follows& take the body and perform a regular expression that replaces all new lines +\n- with <br/> tags. Gow youve combined two methods that conform to a compound Camel expression. ou can run this example from chapter"Btransform directly by using the following 4aven goal&
mvn test -Dtest=TransformTest

Camel also allows you to use custom expressions. This is useful when you need to be in full control and have 2ava code at your fingertips. :or example, the previous example could have been implemented as follows&
from("direct:start") .transform(new Expression() { public <T> T evaluate(Exchange exchange, Class<T> type) { String body = exchange.getIn().getBody(String.class); body = body.replaceAll("\n", "<br/>"); body = "<body>" + body + "</body>"; return (T) body; } }) .to("mock:result");

(s you can see, this code uses an inlined Camel Expression that allows you to use 2ava

code in its evaluate method. This follows the same principle as the Camel Processor you saw before.

The $ire"t "om!onent


The e%am!le here uses the #ire"t "om!onent 4htt!355"amel&a!a"he&org5dire"t6 as the in!ut sour"e for the route 4from("direct:start")6& The #ire"t "om!onent !rovides dire"t invo"ation ,etween a !rodu"er and a "onsumer& It only allows "onne"tivity from within amel* so e%ternal systems "an't send messages dire"tly to it& This "om!onent is used within amel to do things su"h as link routes together or for testing&

Licensed to Ian White <ian@biancashouse.com>


Transforming data using EIPs and Java 69

Gow lets see how you can transform data using 3pring 647.
TRA%SF&R'(%) *S(%) 3TRA%SF&R'4 FR&' SPR(%) 5'+

Dsing Htransform> from 3pring 647 is a bit different than from 2ava <37 because the 647 <37 isnt as powerful. In 3pring 647, the .uilder pattern expressions arent available because with 647 you dont have a real programming language underneath. 'hat you can do instead is invoke a method on a bean or use scripting languages. 7ets see how this works. The following route uses a method call on a bean as the expression&
<bean id="htmlBean" class="camelinaction.HtmlBean"/> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <transform> <method bean="htmlBean" method="toHtml"/> </transform> <to uri="mock:result"/> </route> </camelContext>

:irst, you declare a regular spring bean to be used to transform the message B. Then, in the route, you use <transform> with a <method> call expression to invoke the bean C. The implementation of the htmlBean is very straightforward&
public public body = body = return } } class HtmlBean { static String toHtml(String body) { body.replaceAll("\n", "<br/>"); "<body>" + body + "</body>"; body;

ou can also use scripting languages as expressions in Camel. :or example, you can use =roovy, 4;,7, 2ava3cript, or Camels own scripting language, called 3imple +explained in some detail in appendix (-. 'e wont go in detail on how to use the other scripting languages at this point, but the 3imple language can be used to build strings using placeholders. It pretty much speaks for itself9Im sure youll understand what the following transformation does&
<transform> <simple>Hello ${body} how are you?</simple> </transform>

ou can try the 3pring transformation examples provided in the books source code by running the following 4aven goals from the chapter"Btransform directory&
mvn test -Dtest= SpringTransformMethodTest mvn test -Dtest= SpringTransformScriptTest

Theyre located in the chapter"Btransform directory and are named SpringTransformMethodTest and SpringTransformScriptTest.
Does the transformation

B
Invo es to!tml method on bean

C
Licensed to Ian White <ian@biancashouse.com>
70 CHAPTER 3 Transforming data with Camel

'ere done covering the 4essage Translator ,I), so lets look at the related Content ,nricher ,I).

3.2.2 sing the Content Enricher EIP The Content ,nricher ,I) is illustrated in figure ".". This pattern documents the scenario where a message is enriched with data obtained from another resource. To help understand this pattern, well turn back to >ider (uto )arts. It turns out that the data mapping you did in listing ".1 wasnt sufficient. Irders are also piled up on an :T) server, and your Fob is to somehow merge this information into the existing report. :igure ".# illustrates the scenario.
Basic message Enriched message

Enricher
Resource
Figure 3.3 (n the Content Enri"her E(P. an existing message has data added to it from another sour"e. HTTP server FTP server
Content enricher Quartz scheduler Report (CSV)

File server
Orders (CSV) Transform

Camel

BC D E F G
Figure 3.6 An o7er7ie of the route that generates the orders re!ort. no ith the "ontent enri"her !ulling in data from an FTP ser7er

Licensed to Ian White <ian@biancashouse.com>


Transforming data using EIPs and Java 71

In figure ".#, a scheduled consumer using Euart5 starts the route every day at % a.m. B. It then pulls data from an ?TT) server, which returns orders in a custom formatC, which is then transformed into C3; format D. (t this point, you have to perform the additional content enrichment step E with the data obtained from the
:T) server F. (fter this, the final report is written to the file server

G.

.efore we dig into the code and see how to implement this, we need to take a step back and look at how the Content ,nricher ,I) is implemented in Camel. Camel provides two operations in the <37 for implementing the pattern& pollEnrich9This operation merges data retrieved from another source using a consumer. enrich9This operation merges data retrieved from another source using a producer. Camel uses the org.apache.camel.processor.AggregationStrategy interface to merge the result from the source with the original message, as follows&
Exchange aggregate(Exchange oldExchange, Exchange newExchange);

This aggregate method is a callback that you must implement. The method has two parameters& the first, named oldExchange, contains the original exchange0 the second, newExchange, is the enriched source. our task is to enrich the message using

2ava code and return the merged result. This may sound a bit confusing, so lets see it in action. To solve the problem at >ider (uto )arts, you need to use pollEnrich because its capable of polling a file from an :T) server.
E%R(CH(%) *S(%) P&++E%R(CH

7isting "." shows how you can use pollEnrich to retrieve the additional orders from the remote :T) server and aggregate this data with the existing message using Camels AggregationStrategy.
from("quartz://report?cron=0+0+6+*+*+?") .to("http://riders.com/orders/cmd=received") .process(new OrderToCSVProcessor()) .pollEnrich("ftp://riders.com/orders/?username=rider&password=secret",

+isting 3.3 *sing pollEnrich to merge additional data

ith an existing message

The differen"e bet een pollEnrich and enrich


The differen"e ,etween pollEnrich and enrich is that the former uses a "onsumer and the latter a !rodu"er to retrieve data from the sour"e& 7nowing the differen"e is im!ortant3 the file "om!onent "an ,e used with ,oth* ,ut using enrich will write the message "ontent as a file8 using pollEnrich will read the file as the sour"e* whi"h is most likely the s"enario you'll ,e fa"ing when enri"hing with files& The 9TTP "om!onent only works with enrich8 it allows you to invoke an e%ternal 9TTP servi"e and use its re!ly as the sour"e&

Licensed to Ian White <ian@biancashouse.com>


72 CHAPTER 3 Transforming data with Camel
new AggregationStrategy() { public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { if (newExchange == null) { return oldExchange; } String http = oldExchange.getIn() .getBody(String.class); String ftp = newExchange.getIn() .getBody(String.class); String body = http + "\n" + ftp; oldExchange.getIn().setBody(body); return oldExchange; } }) .to("file://riders/orders");

The route is triggered by Euart5 to run at % a.m. every day. ou invoke the ?TT) service to retrieve the orders and transform them to C3; format using a processor. (t this point, you need to enrich the existing data with the orders from the remote :T) server. This is done by using pollEnrich B, which consumes the remote file.

To merge the data, you use AggregationStrategy C. :irst, you check whether any data was consumed or not. If newExchange is null, there is no remote file to consume, and you Fust return the existing data. If there is a remote file, you merge the data by concatenating the existing data with the new data and setting it back on the oldExchange. Then, you return the merged data by returning the oldExchange. To write the C3; report file, you use the file component D. PollEnrich uses a polling consumer to retrieve messages, and it offers three timeout modes& pollEnrich(timeout = -1)9)olls the message and waits until a message arrives. This mode will block until a message exists. pollEnrich(timeout = 0)9Immediately polls the message if any exists0 otherwise null is returned. It will never wait for messages to arrive, so this mode will never block. This is the default mode. pollEnrich(timeout > 0)9)olls the message, and if no message exists, it will wait for one, waiting at most until the timeout triggers. This mode will potentially block. Its a best practice to either use timeout = 0 or to assign a timeout value when using pollEnrich to avoid waiting indefinitely if no message arrives.
"ses pollEnrich

B C

to read #$% file

Mer&es data usin& '&&re&ationStrate&y (rites output to file

D
Enrich and pollEnrich "an8t a""ess information in the "urrent ex"hange :either enrich nor pollEnrich "an leverage any information from the "urrent e%"hange& This means* for e%am!le* that you "an't store a filename header on the e%"hange for pollEnrich to use to sele"t a !arti"ular file& This may "hange in the future if the amel team "an find a solution&

Licensed to Ian White <ian@biancashouse.com>


Transforming XML 73

Gow lets take a 8uick look at how to use enrich with 3pring 6470 its a bit different than when using the 2ava <37.
E%R(CH(%) *S(%) E%R(CH

Enrich is used when you need to enrich the current message with data from another

source using re8uest!response messaging. ( prime example would be to enrich the current message with the reply from a web service call. .ut well look at another example, using 3pring 647 to enrich the current message using the TC) transport&
<bean id="quoteStrategy" class="camelinaction.QuoteStrategy"/> <route> <from uri="activemq:queue:quotes"/> <enrich url="mina:tcp://riders.com:9876?textline=true&sync=true" strategyRef="quoteStrategy"/> <to uri="log:quotes"/> </route>

?ere you use the Camel mina component for TC) transport, configured to use re8uest!response messaging by using sync=true option. To merge the original message with data from the remote server, <enrich> must refer to an AggregationStrategy. This is done using the strategyRef attribute. (s you can see in the example, the
quoteStrategy being referred to is a bean id B, which contains the actual implementation of the AggregationStrategy, where the merging takes place.

ouve seen a lot about how to transform data in Camel, using 2ava code for the actual transformations. Gow lets take a peek into the 647 world and look at the 637T component, which is used for transforming 647 messages using 637T stylesheets.

3.3 Transforming "!#


Camel provides two ways to perform 647 transformations& "!#T component9:or transforming an 647 payload into another format using 637T stylesheets "$# marshaling9:or marshaling and unmarshaling obFects to and from 647 .oth of these will be covered in following sections.

3.3.1 Transforming "!# with "$#T 637 Transformations +637T- is a declarative 647!based language used to transform 647 documents into other documents. :or example, 637T can be used to transform 647 into ?T47 for web pages or to transform an 647 document into another 647 document with a different structure. 637T is powerful and versatile, but its also a complex language that takes time and effort to fully understand and master. Think twice before deciding to pick up and use 637T. Camel provides 637T as a component in camel!spring.Far because it leverages 3prings resource loading. This means greater flexibility in loading stylesheets because 3pring enables them to be loaded from various locations, such as the classpath, file paths, and over ?TT).

)ean implementin& '&&re&ationStrate&y

Licensed to Ian White <ian@biancashouse.com>


74 CHAPTER 3 Transforming data with Camel Dsing the 637T component is straightforward because its Fust another Camel component.

The following route shows an example of how you could use it0 this route is also illustrated in figure ".$.
from("file://rider/inbox") .to("xslt://camelinaction/transform.xsl") .to("activemq:queue:transformed")

The file consumer picks up new files and routes them to the 637T component, which transforms the payload using the stylesheet. (fter the transformation, the message is routed to a 243 producer, which sends the message to the 243 8ueue. Gotice in the preceding code how the D>7 for the 637T component is defined& xslt://camelinaction/ transform.xsl. The part after the scheme is the D>I location of the stylesheet to use. Camel will look in the classpath by default. (s mentioned before, the Camel 637T component leverages 3pring to load the stylesheet. ou can prefix the resource name with any of the three prefixes listed in table "./. 7ets leave the 637T world now and take a look at how you can do 647!to!obFect marshaling with Camel.
Table 3.- Prefixes su!!orted by the 5S+T "om!onent for loading stylesheets Prefix Exam!le $es"ri!tion

<none> xslt://camelinaction/ transform.xsl


If no !refi% is !rovided* amel loads the resour"e from the "lass!ath

classpath: xslt://classpath:com/ mycompany/transform.xml


Loads the resour"e from the "lass!ath

file: xslt://file:/rider/config/ transform.xml


Loads the resour"e from the filesystem

http: xslt://http://rider.com/ styles/transform.xsl


Loads the resour"e from an U;L XSLT component
Style sheet
File message
D

Transformed message

File consumer JMS producer

BCD
Figure 3.9 A Camel route using an 5S+T "om!onent to transform an 5'+ do"ument before it8s sent to a 2'S :ueue

Licensed to Ian White <ian@biancashouse.com>


Transforming XML 75

3.3.2 Transforming "!# with o%&ect marshaling (ny software engineer who has worked with 647 knows that its a challenge to use the low!level 647 ()I that 2ava offers. Instead, people often prefer to work with regular 2ava obFects and use marshaling to transform between 2ava obFects and 647 representations. In Camel, this marshaling process is provided in ready!to!use components known as data formats. 'ell cover data formats in full detail in section ".#, but well take a 8uick look at the 63tream and 2(6. data formats here as we cover 647 transformations using marshaling.
TRA%SF&R'(%) *S(%) 5STREA'

63tream is a simple library for seriali5ing obFects to 647 and back again. To use it, you need camel!xstream.Far on the classpath and the 63tream library itself. 3uppose you need to send messages in 647 format to a shared 243 8ueue, which is then used to integrate two systems. 7ets look at how this can be done.
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <dataFormats> <xstream id="myXstream"/> </dataFormats> <route> <from uri="direct:foo"/>

<marshal ref="myXstream"/> <to uri="activemq:queue:foo"/> </route> </camelContext>

'hen using the 647 <37, you can declare the data formats used at the top B of the <camelContext>. .y doing this, you can share the data formats in multiple routes. In the first route, where you send messages to a 243 8ueue, you use marshal which refers to the id from B, so Camel knows that the 63tream data format is being used. ou can also use the 63tream data format directly in the route, which can shorten the syntax a bit, like this&
<route> <from uri="direct:foo"/> <marshal><xstream/></marshal> <to uri="activemq:queue:foo"/> </route>

C,

The same route is a bit shorter to write in the 2ava <37, because you can do it with one line per route&
from("direct:foo").marshal().xstream().to("uri:activemq:queue:foo");

es, using 63tream is that simple. (nd the reverse operation, unmarshaling from 647 to an obFect, is Fust as simple&
+isting 3.6 *sing 5Stream to transform a message into 5'+ Specifies *Stream

data format $ransforms to *M+

C
Licensed to Ian White <ian@biancashouse.com>
76 CHAPTER 3 Transforming data with Camel
<route> <from uri="activemq:queue:foo"/> <unmarshal ref="myXstream"/> <to uri="direct:handleFoo"/> </route>

ouve now seen how easy it is to use 63tream with Camel. 7ets take a look at using
2(6. with Camel.
TRA%SF&R'(%) *S(%) 2A5/

2(6. +2ava (rchitecture for 647 .inding- is a standard specification for 647 binding, and its provided out of the box in the 2ava runtime. 7ike 63tream, it allows you to seriali5e obFects to 647 and back again. Its not as simple, but it does offer more bells and whistles for controlling the 647 output. (nd because its distributed in 2ava, you dont need any special 2(> files on the classpath. Dnlike 63tream, 2(6. re8uires that you do a bit of work to declare the binding between 2ava obFects and the 647 form. This is often done using annotations. 3uppose

you define a model bean to represent an order, as shown in listing ".$, and you want to transform this into 647 before sending it to a 243 8ueue. Then you want to transform it back to the order bean again when consuming from the 243 8ueue. This can be done as shown in listings ".$ and ".%.
package com.acme.order; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class PurchaseOrder { @XmlAttribute private String name; @XmlAttribute private double price; @XmlAttribute private double amount; }

7isting ".$ shows how to use 2(6. annotations to decorate your model obFect +omitting the usual getters and setters-. :irst you define @XmlRootElement B as a class!level

annotation to indicate that this class is an 647 element. Then you define the @XmlAccessorType to let 2(6. access fields directly. To expose the fields of this model obFect as 647 attributes, you mark them with the @XmlAttribute annotation. Dsing 2(6., you should be able to marshal a model obFect into an 647 representation like this&
<purchaseOrder name="Camel in Action" price="4995" amount="1"/>

+isting 3.9 Annotating a bean

ith 2A5/ so it "an be transformed to and from 5'+

%urchase,rder class is -'*) annotated

Licensed to Ian White <ian@biancashouse.com>


Transforming with data formats 77

7isting ".% shows how you can use 2(6. in routes to transform the PurchaseOrder obFect to 647 before its sent to a 243 8ueue, and then back again from 647 to the PurchaseOrder obFect when consuming from the same 243 8ueue.
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <dataFormats> <jaxb id="jaxb" contextPath="camelinaction"/> </dataFormats> <route> <from uri="direct:order"/> <marshal ref="jaxb"/> <to uri="activemq:queue:order"/> </route> <route> <from uri="activemq:queue:order"/> <unmarshal ref="jaxb"/> <to uri="direct:doSomething"/> </route> </camelContext>

:irst you need to declare the 2(6. data format B. Gote that a contextPath attribute is also defined on the 2(6. data format9this is a package name that instructs 2(6. to look in this package for classes that are 2(6.!annotated. The first route then marshals to 647 C and the second route unmarshals to transform the 647 back into the PurchaseOrder obFect D. ou can try this example by running the following 4aven goal from the chapter"B order directory&
mvn test -Dtest=PurchaseOrderJaxbTest

%&TE To tell 2(6. which classes are 2(6.!annotated, you need to drop a special jaxb.index file into the context path. Its a plain text file in which each

line lists the class name. In the preceding example, the file contains a single line with the text PurchaseOrder. Thats the basis of using 647 obFect marshaling with 63tream and 2(6.. .oth of them are implemented in Camel via data formats that are capable of transforming back and forth between various well!known formats.

3.' Transforming with data formats


In Camel, data formats are pluggable transformers that can transform messages from one form to another and vice versa. ,ach data format is represented in Camel as an interface in org.apace.camel.spi.DataFormat containing two methods& marshal9:or marshaling a message into another form, such as marshaling 2ava obFects to 647, C3;, ,<I, ?7*, or other well!known data models unmarshal9:or performing the reverse operation, which turns data from wellknown formats back into a message

Você também pode gostar