Proper use of Camel Aggregator "to" URIs - java

I have a route where I want Camel to visit the following beans:
First, loggingBean
Second, an aggregator that waits for a certain number of messages to aggregate on it
Once the Aggregator's completionSize is reached (3), keep going to #4
Third, processorBean
And fourth/last, finalizerBean
Here is my route:
<route id="my-camel-route">
<from uri="direct:starter" />
<to uri="bean:loggingBean?method=shutdown" />
<aggregate strategyRef="myAggregationStrategy" completionSize="3">
<correlationExpression>
<simple>${header.id} == 1</simple>
</correlationExpression>
<to uri="bean:processorBean?method=process" />
</aggregate>
<to uri="bean:finalizerBean?method=shutdown" />
</route>
My question: do I need to place finalizerBean inside the <aggregate> element like so:
<aggregate strategyRef="myAggregationStrategy" completionSize="3">
<correlationExpression>
<simple>${header.id} == 1</simple>
</correlationExpression>
<to uri="bean:processorBean?method=process" />
<to uri="bean:finalizerBean?method=shutdown" />
</aggregate>
Basically, I'm wondering if the way I currently have things will prompt Camel to send the message to the aggregator, and then also send it on to finalizerBean (essentially, bypassing the aggregator). In my case, I want it to aggregate until completionSize is 3, and then send the aggregated exchange on to the processorBean and then finally finalizerBean.
Or have I configured this correctly? What's the difference between finalizerBean being inside the <aggregate> element vs being outside it?

The second example is correct.
<aggregate strategyRef="myAggregationStrategy" completionSize="3">
<correlationExpression>
<simple>${header.id} == 1</simple>
</correlationExpression>
<to uri="bean:processorBean?method=process" />
<to uri="bean:finalizerBean?method=shutdown" />
</aggregate>
If finalizerBean is "outside" the <aggregate>, it will get executed for every message that comes from direct:starter - which isn't what you want ;)

Related

How do I use Camel File Component Consumer using multiple threads

How do I use Camel File Component Consumer using multiple threads ?
meaning I have this code :
<route id="incomingFile">
<from
uri="file://{{incomingFileBaseFolder}}?filter=#fileFilter&recursive=true&readLock=changed&move=${file:parent}/.backup/${date:now:yyyy}/backup_${exchangeId}_${file:onlyname.noext}.${file:name.ext}&sortBy=file:modified&delay={{incomingFileDelay}}" />
<transacted />
<threads poolSize="10">
<convertBodyTo type="java.lang.String" />
<setHeader headerName="{{incoming_file_backup_date_header_name}}">
<simple>$simple{date:now:yyyy}
</simple>
</setHeader>
<bean ref="saveFile" method="duplicateCeck" />
<to uri="direct:validateFileDirect" />
<to uri="direct:inputFileContentHandle" />
</threads>
</route>
but it does not seems to work on more than one file at a time.
How do I make it happens ?
Remove <transacted/> as it does not support asynchronous routing. Also transactions only works with component/resources that support JTA transactions natively, which typically is only JMS and JDBC.

Apache Camel route: onCompletion not reached when exception occurs?

I have a Camel route that looks something like the one below. If all records parse successfully, then I get an email from the onCompletion step. If one record gets an exception then the rest of the records will process, which is fine, but the onCompletion step does not fire.
What I'd like is for the onCompletion step to run even if there are errors and to be able to send a message saying "route completed with errors". How can I do this?
<route id="route1">
<from uri="file://C:/TEMP/load?noop=true&idempotentRepository=#sysoutStore&sorter=#externalDataFilesSorter"/>
<choice>
<when>
<simple>${file:name} regex '*file.*.(txt)'</simple>
<to uri="direct:RouteFile" />
</when>
</choice>
</route>
<route id="testRouteDirect">
<from uri="direct:RouteFile" />
<onException>
<exception>java.lang.IllegalArgumentException</exception>
<redeliveryPolicy maximumRedeliveries="1" />
<handled>
<constant>true</constant>
</handled>
<to uri="log:java.lang.IllegalArgumentException"></to>
</onException>
<onException>
<exception>java.text.ParseException</exception>
<redeliveryPolicy maximumRedeliveries="1" />
<handled>
<constant>true</constant>
</handled>
<to uri="log:java.text.ParseException"></to>
</onException>
<split parallelProcessing="false" strategyRef="exchangePropertiesAggregatorStrategy" >
<tokenize token="\r\n"/>
<to uri="log:Record"></to>
</split>
<onCompletion>
<to uri="log:completion"></to>
<to uri="smtp://mail.com?contentType=text/html&to=done#test.com&from=route#test.com&subject=we're done" />
</onCompletion>
</route>
The best part of your route is, you have onException inside your route with handled=true. So move your onCompletion to the parent route(route1), It should work !
There are a bunch of tickets related to oncompletion on the camel site: Camel Jira URL. I upgraded to a newer version of camel & I don't get this issue any more.

How to set headers from bean integration method call in a camel route

Help needed on setting headers based on the method call from bean integration.
within my application I am using a custom POJO and there are before I actually send the message over the wire I want to do set the headers on the exchange, but don't want to do it within my bean and rather do it where my spring DSL is written for the route.
I know that usually the value returned from method is sent as body for the message to the next the endpoint but i want to send values returned as header.
I have attached a sample of my route needs to be, and want:
<route id="someRoute">
<from ref="InboundAsyncEndpoint" />
<to uri="bean:validatorBean?method=validateMessageInternals(MyCostomMessagePojo obj)" />
<choice>
<when>
<simple>
${body.getMetaData().getFinalDestinationName()} == 'AMQEndpoint'
</simple>
<to uri="bean:payloadAndHeaderExtractor?extractHeader(MyCostomMessagePojo obj)" />
<to uri="bean:payloadAndHeaderExtractor?extractPayload(MyCostomMessagePojo obj)" /> <!-- i want the headers being set on the exchange from the map that is returnd from the previous bean and method -->
<to uri="activemq:someQueue"
</when>
<otherwise>
...
...
</otherwise>
</choice>
You can do that in the following way:
<setHeader headerName="YOUR_HEADER">
<simple>bean:payloadAndHeaderExtractor?extractHeader(MyCostomMessagePojo obj)</simple>
</setHeader>
Hope this helps.
R.

Camel AggregationStrategy behaving unexpectedly

I have a situation where I want to pass data into an Aggregator, but I don't want the aggregator to do anything until it has received messages from 3 distinct routes:
<route id="route-1">
<from uri="direct:fizz" />
<to uri="bean:bean1?method=process" />
<setHeader headerName="id">
<constant>1</constant>
</setHeader>
<to uri="direct:aggregator" />
</route>
<route id="route-2">
<from uri="direct:buzz" />
<to uri="bean:bean2?method=process" />
<setHeader headerName="id">
<constant>2</constant>
</setHeader>
<to uri="direct:aggregator" />
</route>
<route id="route-3">
<from uri="direct:foo" />
<to uri="bean:bean3?method=process" />
<setHeader headerName="id">
<constant>3</constant>
</setHeader>
<to uri="direct:aggregator" />
</route>
<route id="aggregator-route">
<from uri="direct:aggregator" />
<aggregate strategyRef="myAggregationStrategy" completionSize="1">
<correlationExpression>
<simple>header.id</simple>
</correlationExpression>
<to uri="bean:lastBean?method=process" />
</aggregate>
</route>
The way this is configured, when the aggregator's completionSize is set to 1 or 2, the aggregated Exchange is routed on to my lastBean. However, if I set completionSize to 3, for some reason, lastBean#process never gets invoked.
I'm sure that I'm using header.id and the aggregator incorrectly here. In the correlationExpression, I just need to make sure that we have 1 Message from each of the 3 routes.
So my question: what do I need to do to make my aggregator "wait" until it has received 1 message from route-1, 1 message from route-2 and 1 message from route-3?
If you are correlating messages from three routes, there needs to be a way for them all to have a matching header.id value by the time they reach the aggregating route.
In your example, each route sets a different id value so there would be no match. If you set the id value to "1" in each route, I think it would start to work as expected.

Apache Camel conditional routing

I have a service which has two operations.
RegisterUser
UpdateUser
I have a camel rout:
<camel:route id="myRoute">
<camel:from uri="cxf:bean:myListenerEndpoint?dataFormat=POJO&synchronous=true" />
<camel:bean ref="processor" method="processMessage"/>
<camel:to uri="xslt:file:resources/service/2.0.0/UserRegistration.xsl"/>
<camel:to uri="cxf:bean:myTargetEndpoint"/>
</camel:route>
In my processor bean, when I specify:
RegisterUser registerUser = exchange.getIn().getBody(RegisterUser.class);
I get the register user object. Everything works fine.
The problem is that I want camel to route my request conditionally, for e.g:
If the service operation is RegisterUser I want to route the message to my specific bean and if the service operation is UpdateUser I want to route the message to the other bean.
I have tried to use camel xPath, but it not seems to be working.
<camel:route id="myRoute">
<camel:from uri="cxf:bean:myListenerEndpoint?dataFormat=POJO&synchronous=true" />
<camel:choice>
<camel:when>
<camel:xpath>
//RegisterUser
</camel:xpath>
<camel:bean ref="processor" method="processMessage"/>
<camel:to uri="xslt:file:resources/service/2.0.0/UserRegistration.xsl"/>
</camel:when>
</camel:choice>
<camel:to uri="cxf:bean:myTargetEndpoint"/>
</camel:route>
I was searching how to set up camel to route to the different targets but did not find anything. Maybe somebody knows where might be the problem?
The information of the operation required will be in the header of the message.
The header you are looking for is called 'operationName'
So here is an example :
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="example">
<from uri="cxf:bean:myListenerEndpoint?dataFormat=POJO&synchronous=true" />
<log message="The expected operation is :: ${headers.operationName}" />
<choice>
<when>
<simple>${headers.operationName} == 'RegisterUser'</simple>
<bean ref="processor" method="processMessage"/>
<to uri="xslt:file:resources/service/2.0.0/UserRegistration.xsl"/>
</when>
<when>
<simple>${headers.operationName} == 'UpdateUser'</simple>
<!-- Do the update user logic here -->
<bean ref="processor" method="updateUser" />
</when>
</choice>
<to uri="cxf:bean:myTargetEndpoint"/>
</route>
</camelContext>
(Note the example is using apache aries blueprint - but it will be identical for spring, other than the namespace)
try using camel-simple expressions instead of xpath for this...
<when><simple>${body} is 'com.RegisterUser'</simple><to uri="..."/></when>
Spring XML route
In my case I use inbound Jetty EP.
I check parametr in request.
Invole URL http://localhost:8080/srv?alg=1
<choice id="_choice1">
<when id="_when1">
<simple>${in.header.alg} == '1'</simple>
<log id="_log10" message="LOG ALG 1"/>
</when>
...
<otherwise id="_otherwise1">
<setFaultBody id="_setFaultBody1">
<constant>Return message about ERROR</constant>
</setFaultBody>
</otherwise>
</choice>
final CamelContext context = exchange.getContext();
if (isAlive) {
context.startRoute("table-reader-route");
log.info("starting dailycase route= " + response);
} else {
context.stopRoute("table-reader-route");
log.info("stoping dailycase route= " + response);
}

Categories

Resources