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
Related
I am trying to run a camel transacted() route (a standalone java process) with JPATransactionManager is the spring PlatformTransactionManager (as I want camel route to run in a single DB transaction) but I am not able to suppress redelivery from MQ Broker in case a transactional method fails even though I have used handled(true) in onException clause along with my custom redelivery policy (which is executed successfully). I only want MQ to redeliver when there is a service crash.
Tried below but it doesn't work:
Setting setTransacted(false) in JMSComponent config so as to prevent camel jms to run is transacted_session jms mode but it does not work
doTry and doCatch the exception from transactional block
camel redeliveries followed by handled(true).
onException(Exception.class)
.log("ERROR OCCURRED")
.redeliveryPolicyRef("myRedeliveryPolicy")
.handled(true)
.to(getPostExceptionRoute());
#Bean
#Autowired
public RedeliveryPolicy myRedeliveryPolicy() {
RedeliveryPolicy myRedeliveryPolicy= new RedeliveryPolicy();
myRedeliveryPolicy.setMaximumRedeliveries(2);
myRedeliveryPolicy.setMaximumRedeliveryDelay(2000);
return myRedeliveryPolicy;
}
#Bean
#Autowired
public JmsComponent jms(IJMSConnectionFactory cf) throws JMSException {
JmsComponent jmsComponent = new JmsComponent();
jmsComponent.setConfiguration(jmsConfig(cf));
jmsComponent.setTransacted(false);
return jmsComponent;
}
from("jms:queue:TestQueue?acknowledgementModeName=CLIENT_ACKNOWLEDGE")
.unmarshal().json(JsonLibrary.Jackson, TestObject.class)
.transacted()
.processRef("myPersistInDBProcessor")
I expect camel to try redeliveries as per redelivery policy (working) but MQ should not redeliver.
I expect my camel route to run in a single db transaction.
I expect MQ broker to redeliver only when my java service crashes in middle of processing, so that I do not lose the message.
I expect camel to try redeliveries as per redelivery policy (working) but MQ should not redeliver
When MQ must never do a redelivery (because you handle errors in Camel), you should remove acknowledgementModeName=CLIENT_ACKNOWLEDGE or explicitly set AUTO_ACKNOWLEDGE (default value).
As long as a message is not acknowledged, it is from the broker perspective not delivered. AUTO_ACKNOWLEDGE immediately acknowledges the message after consumption what makes sense if you never want to get redeliveries.
CLIENT_ACKNOWLEDGE on the other hand only acknowledges a message under certain conditions, see this post for some more info about this.
EDIT due to comment with new information
If you want MQ redeliveries, but "override" them with Camel in most cases, you have to consume messages transacted.
Use local JMS broker transactions by configuring your JMS component like this
jmsComponent.setLazyCreateTransactionManager(false);
jmsComponent.setTransacted(true);
For this type of transaction you don't need a Spring TransactionManager at all. So I guess the JPATransactionManager is ignored by JMS and your JMS consumptions should be transactional.
Now, when your Camel error handler "swallows" an Exception by using handled(true), there must be no MQ redelivery. But MQ does a redelivery when an Exception is propagated back to the broker.
I expect my camel route to run in a single db transaction
I did not find anything in your question about not working database transactions. There seems to be just one processor that does database stuff. If this is not working correctly, please describe the problem in your question or a separate question.
According to the Apache Karaf Transaction Guide should doTry and doCatch work as expected. What is probably the problem in your case is the Exception triggering the error scenario. Only checked exceptions (no RuntimeException or it's descendant) don't mark ongoing transaction for rolling back.
I have camel route which suspends the route using control bus on exception.
onException(Exception.class).maximumRedeliveries(1).onRedeliveryRef("controlBusProcessor");
from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2000&trigger.repeatCount=0")
.routeId("myRoute")
.process(simpleProcessor)
.to("stream:out")
The route initially calls a simple processor which raises an Exception, the onException has a redeliveryRef which calls a control bus processor which suspends the route, however it still calls simple processor to redeliver the message.
If the route is suspended, why is simple processor is still called, should it not wait until the route resumes?
No the route will complete its in-flight messages before being suspended. In your custom redelivery processor, you can mark the exchange to stop continue routing by setting the property on the exchange with Exchange.STOP=true.
See how the StopProcessor does that: https://github.com/apache/camel/blob/master/camel-core/src/main/java/org/apache/camel/processor/StopProcessor.java
I suspect the redelivery starts at the point of failure which is at .process(simpleProcessor). But if you only want to suspend the route why not just add .onException(Exception.class).to("controlBusProcessor")
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
I have an amqp-backed channel <int-amqp:channel> to temporarily persist messages coming from a JDBC inbound adapter. when no exception is thrown, the message is ack'd and removed from the rabbit queue. when an exception occurs, the message is returned to the queue and is reprocessed continuosly. There are some circumstances where I'd like the request to go back to the queue, but in most cases I just want to log the error and acknowledge the request (remove from the rabbit queue).
I've implemented an errorHandler to deal with thrown exceptions and allow for logging and "successful" completion, however even after handling, the original request is redelivered to the rabbit queue (unacknowledged)
in the inbound-rabbit-adapter, there is a property for error-channel and handling the message on the errorChannel allows me to achieve the desired behavior described above. the only error property on the amqp channel is errorhandler.
any suggestions on a configuration that would allow me to meet my requirements?
thanks
Throw an AmqpRejectAndDontRequeueException. See 3.9 Exception Handling.
The default error handler does that for message conversion exceptions (which are likely irrecoverable).
In fact you can use that error handler by injecting a custom FatalExceptionStrategy.
Given a basic MessageListener implementation which consumes messages from a RabbitMQ queue, how can I send the message to different dead-letter-queues based on the type of exceptions that could be thrown while processing it?
The queue were the messages are originally published has the x-dead-letter-exchange and x-dead-letter-routing-key set on it, but this is not enough in my case.
In case it matters, my application is using Spring 4 and Spring Amqp.
As far as I understand RabbitMQ documentation and Spring AMQP, it is not possible to send a message to different DLQs based on conditions from inside the code. The reason I say this is that my understanding from debugging the code is that when a message has to be send to a DLQ, the code doesn't specify the exchange or the routing key and RabbitMQ uses the ones defined when the message was published.
So, the only solution I found is to implement something like this:
try {
try_to_do_useful_stuff(message);
} catch (BusinessException e) {
resend_the_message_to_business_dlq(message);
}
This way, if a business exception is thrown, then the message is manually send to the business DLQ. Of course, some details get lost, like originating queue, but this is not an issue if they're not used.
When a non-business exception is thrown then the standard path is followed: retry (if configured) and then route to the defined DLQ.