I'm currently working on an application which uses Camel heavily. I'll briefly explain what I'm trying to achieve:
An OnException Processor (handled=true) catches an Exception and when that happens I want to stop the Exchange from being processed by the RoutePolicy(s) of the route. Example with pseudo-code:
<route id="route1" routePolicyRef="policy1, policy2, policy3">
... an exception *e1* is thrown...
</route>
<onException>
<handled>true</handled>
... handle exception *e1*
<bean ref="customExceptionProcessor"/>
</onException>
So in essence, once an Exception is handled I want the Exchange to stop propagating to all the attached Policy(s).
Wondering if there's a simple way to achieve this which I may have missed when reading from the documentation.
That's it really. Cheers.
Related
I'm trying to implement the following JMS message flow using camel routes:
there is a topic published on external message broker. My program is listening for messages on this topic. Each incoming message triggers specific route to be executed - ONE TIME ONLY (some kind of ad-hoc, disposable route). This route is supposed to move messages between queues within my internal message broker based on some selector (get all messages from queue A matching given selector and move them to queue B). I'm only starting with camel and so far I figured out just the first part - listening for messages on topic:
<bean id="somebroker" class="org.apache.camel.component.jms.JmsComponent"
p:connectionFactory-ref="rmAdvisoriesConnectionFactory"/>
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<endpoint id="jms" uri="somebroker:topic:sometopic"/>
<route id="routeAdvisories">
<from ref="jms"/>
<to>???</to>
</route>
</camelContext>
Can you suggest a destination for these advisory messages? I need to read some of their JMS properties and use these values to construct JMS selector that will be used for the "move messages" operation. But I have no idea how to declare and trigger this ad-hoc route. It would be ideal if I could define it within the same camelContext using only Spring DSL. Or alternatively, I could route advisories to some java method, which would be creating and executing this ad-hoc routes. But if this isn't possible, I'll be grateful for any suggestion.
Thanks.
As far as I understand, it will be useful to use the 'selector' option, in your JMS consumer route, for example:
from("activemq:queue:test?selector=key='value1'").to("mock:a");
from("activemq:queue:test?selector=key='value2'").to("mock:b");
Maybe, another option is to implement some routes based on 'Content Based Router Pattern" through "choice" option. You can find more info here: http://camel.apache.org/content-based-router.html
I hope it helps.
I couldn't get it working the way I intended, so I had to abandon my original approach. Instead of using camel routes to move messages between queues (now I'm not sure camel routes are even intended to be used this way) I ended up using ManagedRegionBroker - the way JMX operation "moveMatchingMessagesTo" is implemented - to move messages matching given selector.
I have a route that calls an external rest service. I have configured my error handler as shown below.
errorHandler(deadLetterChannel("jms:dlc").maximumRedeliveries(3));
What i want to do:
If connection to external api fails, i want to retry 3 times and then send to deadLetterChannel
If api call is fine, i want to check the status code, log the response and then send the message to deadLetterChannel.
For that i set throwExceptionOnFailure to false.
In my route i have a bean as the last endpoint. This bean receives the response from the external end point and checks for the status.
void process(Exchange exchange){
//check http status code
//if not success
exchange.setProperty(Exchange.ROUTE_STOP,true);
//sendToDeadLetterQueue;
}
My problem is that redelivery happens even when i am able to connect to API. I expect the redelivery to happen on error. But i am handling the response and also setting the exchange to stop.
Can i stop the redelivery from my bean?
You can use onException as follows:
<onException>
<exception>SomeException</exception>
<handled><constant>true</constant></handled>
<process ref="failureResponse"/>
</onException
Use onException tag handled as true
Java DSL
onException(ExceptionClass).handled(true)
.to(deadLetterChannel);
Spring DSL :
<onException>
<exception>SomeException</exception>
<handled><constant>true</constant></handled>
<to uri=deadLetterChannel/>
</onException>
For More Clarification click here
Have the following camel route.
#Override
public void configure() throws Exception {
onException(java.lang.Exception.class).useOriginalMessage()
.beanRef("discoveryService", "updateConnection")
.redeliveryPolicyRef("redeliverMessagePolicy");
from(ENDPOINT_URI).to(queueName);
}
with Redelivery policy defined as following in xml-
<redeliveryPolicyProfile id="redeliverMessagePolicy"
retryAttemptedLogLevel="WARN" maximumRedeliveries="8"
redeliveryDelay="${redeliveryDelay}" />
However when an exception is thrown the redelivery attempts are made before the OnException block is executed(Some configuration properties get updated in the onException block. Have a debug point in DiscoveryService inside Onexception, it gets called after the redelivery attempts are made). Thus the current message gets lost without being redelivered. Not sure why this happens.
Using activemq-camel version 5.8.0
Thnks
Yes this is intended, the onException block is only executed when the exchange is exhausted (eg after all redelivery attempts have failed).
Read more about how error handling in Camel works in the docs
http://camel.apache.org/error-handling-in-camel.html
And if you have a copy of the Camel in Action book it has an entire chapter devoted to cover all about error handling (most complete documentation there is)
If you want to do some custom logic before each redelivery, then use the onRedelivery processor: http://camel.apache.org/exception-clause.html
We have an XML file which consists of multiple customer elements in it and we want to save customer information to the DB using transactions. From my understanding, transactions needs to be run in a single thread to roll back the whole transaction in case of errors.
Here's my XML:
<root>
<customers>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
</customers
</root>
Here is my route:
<route id="routeA">
<from uri="direct-vm:sample" />
<transacted />
<splitter parallelProcessing = "true" stopOnException="true"
strategyRef="combine" />
<xpath>/root/customers/customer</xpath>
<bean ref="customerService" method="saveCustomer" />
<onException>java.lang.Exception</onException>
<handled><constant>true</constant></handled>
<rollback markRollbackOnly="true" />
</route>
The method saveCustomer() runs a lot of business logic before saving customers to the Database and if for some reason an exception is thrown for 1 or 2 customers, I see multiple rollback messages, and it seems like this is happening for each thread. Do transactions in camel routes with parallel processing work? Is there any other way to save customers in parallel to the DB in a single DB transaction?
No you cannot do parallel work in the same transaction. The work must occur on the same thread.
You can use shareUnitOfWork() in combination with your parallelProcessing.
As the Camel Documentation of the Splitter EIP mentions it will rollback the entire unit of work not only the sub units:
When the Splitter is done, it checks the state of the shared unit of work and checks if any errors occurred. And if an error occurred it will set the exception on the Exchange and mark it for rollback. The error handler will yet again kick in, as the Exchange has been marked as rollback and it had an exception as well. No redelivery attempts is performed (as it was marked for rollback) and the Exchange will be moved into the dead letter queue.
see Sharing Unit of work chapter in the link.
I am currently using doTry/doCatch blocks in my routes due to which I cannot use a global onException block.
However, I want to perform some business logic if camel route ever breaks (because of bad code or unexpected/untested scenarios). This will hopefully never happen, but I still want to handle for worse case.
I cannot have a java.lang.Exception in global onException block, also, I don't want to put an addition catch on every route.
Is there a specific method Camel calls before throwing uncaught exceptions and breaking route.
I see following log for Uncaught Exceptions:
2015-04-20 15:11:35,279 [Camel (fulfillmentOrderProcessor) thread #5 - seda://FulfillmentSedaQueue] WARN o.a.c.component.aws.sqs.SqsConsumer [, ID-ip-10-180-252-213-54360-1429566855015-0-144]: Exchange failed, so rolling back message status: Exchange[Message: {... }]
java.lang.IllegalArgumentException: Unable to parse string argument null
I looked at UnitOfWork.afterprocess. But this will not help as exchange will have exception even if I have handled it in camel route.
by default, Camel will propagate the exception back to the caller, so you can catch the exception in whatever client code invokes the seda://FulfillmentSedaQueue route...
otherwise, the options on the server side (as you mentioned) are to use a global onException clause or route specific doTry/doCatch statements