Apache Camel route: onCompletion not reached when exception occurs? - java

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.

Related

Apache Camel. How to manage with a Custom Processor the error raised at runtime

I'm trying to convert my route made with Java DSL in a route made with XML.
The following is my original route that works. And what it does is simple.
Get as input an array of integer and at runtime throws some errors.
At the end of the route I need to read all the error raised by myself and not with a long stacktrace or other long messages in console.
other lines of code...
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
onException(Exception.class)
.handled(true)
.process(new ErrorProcessor());
from("direct:start_route")
.split(body())
.processRef("payloadProcessor")
.to("direct:a")
.end()
.to("direct:d");
from("direct:a")
.beanRef("stupidBean", "stupidMethod");
from("direct:d")
.beanRef("errorProcessor", "check");
}
});
other lines of code...
The following is my xml route that doesn't work.
...
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:call.playWithPayload" />
<onException>
<exception>java.lang.Exception</exception>
<process ref="errorProcessor" />
</onException>
<split onPrepareRef="payloadProcessor">
<simple>${body}</simple>
<to uri="direct:call.a" />
</split>
<to uri="direct:call.d" />
</route>
<!--SUB ROUTE-->
<route>
<from uri="direct:call.a" />
<bean ref="stupidBean" method="stupidMethod" />
</route>
<route>
<from uri="direct:call.d" />
<bean ref="errorProcessor" method="check" />
</route>
</camelContext>
...
What I need is that the direct:call.d is called after the split.
Thanks to this I can read all the errors added into a List<Exception> that is stored into the header.
I think that the problem is in the onException management.
When I try to add the handled to reproduce the my Java DSL
<onException>
...
<handled>
<constant>
true
</constant>
</handled>
...
I got this error:
Invalid content was found starting with element 'handled'.
One of '{"http://camel.apache.org/schema/spring":description, "http://camel.apache.org/schema/spring":exception}' is expected.
Found solution.
My problem was an incorrect format of my xml route.
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<process ref="errorProcessor" />
</onException>
now it works.

How to prevent renaming of file in Camel when Exception is thrown in Process?

I'm having a bit of an issue with preventing the original file from being renamed when I throw/catch an Exception in a Processor.
I have a route like this:
<route customId="true" id="localRoute">
<from uri="{{ftp.pull.LOCAL.server}}" />
<process ref="Processor" />
<to uri="{{ftp.push.LOCAL.route}}" />
</route>
The from URI includes the option of: &move=${file:name.noext}.${file:name.ext}.old
And during my Process, I stop the Exchange from being routed to the end under certain conditions and also throw an Exception that I catch using:
<onException>
<exception>com.myException.ThrownException</exception>
<handled><constant>true</constant></handled>
</onException>
Is there any way to prevent the renaming of the original file I'm pulling from if I throw and catch that exception?
(I throw and catch that Exception in order to prevent a file from entering a Idempotent File Repository for other routes. Many routes use this Processor.)
File will be renamed unless you remove the <handled> or mark that as false.
If you handle the exception it won't propagate and your file component thinks that the Camel exchange processed successfully and it will rename the file.
So I think I may have found an answer.
So what I have found out is that by doing a global <onException> block that catches my custom Exception, it will prevent logging a stack trace and entering in a file name into the File Repo for routes that use a Idempotent File Repository. But that global block will not prevent the routes that rename files from renaming the files.
In order to catch the Exception and prevent the logging of the stack trace on routes that rename files, I needed to use the doTry/doCatch blocks. I have to do something like this:
<route customId="true" id="localRoute">
<from uri="{{ftp.pull.LOCAL.server}}" />
<doTry>
<process ref="Processor" />
<doCatch><exception>com.myException.ThrownException</exception></doCatch>
</doTry>
<to uri="{{ftp.push.LOCAL.route}}" />
</route>
So my solution ends up looking like this:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>com.myException.ThrownException</exception>
<handled><constant>true</constant></handled>
</onException>
<route customId="true" id="localRoute">
<from uri="{{ftp.pull.LOCAL.server}}" />
<doTry>
<process ref="Processor" />
<doCatch><exception>com.myException.ThrownException</exception></doCatch>
</doTry>
<to uri="{{ftp.push.LOCAL.route}}" />
</route>
<route customId="true" id="otherRoute">
<from uri="{{ftp.pull.OTHER.server}}" />
<setHeader headerName="otherFileRepoKey">
<simple>${file:name}-${file:modified}</simple>
</setHeader>
<idempotentConsumer messageIdRepositoryRef="otherFileStore">
<header>otherFileRepoKey</header>
<process ref="Processor" />
<to uri="{{ftp.push.OTHER.route}}"/>
</idempotentConsumer>
</route>
</camelContext>

Camel get response from Route

I am able to called Web Service, now what ever response came from web service, i need to fetch same response in java code so that i am able to further processing.
<camelContext xmlns="http://camel.apache.org/schema/spring" trace="false">
<route id="my_Sample_Camel_Route_with_CXF">
<from uri="file:src/data?noop=true"/>
<log loggingLevel="INFO" message=">>> ${body}"/>
<to uri="cxf://http://www.webservicex.net/stockquote.asmx?wsdlURL=src/main/resources/META-INF/stockquote.wsdl&serviceName={http://www.webserviceX.NET/}StockQuote&portName={http://www.webserviceX.NET/}StockQuoteSoap&dataFormat=MESSAGE"/>
<log loggingLevel="INFO" message=">>> ${body}"/>
</route>
Instead of logging using

Proper use of Camel Aggregator "to" URIs

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

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.

Categories

Resources