Execute task after reaching threshold for maximumRedeliveries in Camel - apache-camel

I have implemented a retry functionality in my code using camel retry. It will retry a maximum of five times.
onException(Exception.class)
.maximumRedeliveries(5)
.retryAttemptedLogLevel(LoggingLevel.WARN)
.backOffMultiplier(5)
.maximumRedeliveryDelay(5)
.useExponentialBackOff();
Now I want to call a custom method if the threshold has been reached instead of throwing an exception. How can I achieve this?

This is exact use-case for Dead Letter Channel EIP pattern. I would suggest converting your onException block to errorHandler and use deadLetterChannel builder.
errorHandler(
deadLetterChannel("direct:redeliveryExhausted")
.maximumRedeliveries(5)
.retryAttemptedLogLevel(LoggingLevel.WARN)
.backOffMultiplier(5)
.maximumRedeliveryDelay(5)
.useExponentialBackOff()
);
from("direct:redeliveryExhausted")
.log("I am going to handle non-deliverable message")
.log("Because of this exception: ${exception}")
.to("log:failed");

You can set the handled-flagto true and then call your custom bean.
onException(Exception.class)
.maximumRedeliveries(5)
.retryAttemptedLogLevel(LoggingLevel.WARN)
.backOffMultiplier(5)
.maximumRedeliveryDelay(5)
.useExponentialBackOff()
.handled(true)
.to("bean:myCustomBean");

Related

Having access to entire soap:body when service call returns soap:fault

I'm building a route that sends a SOAP request to a webservice. For achieving that, I wrote this code.
.doTry()
.inOut(getEndpointDocumentWS())
.log("Response WHEN OKAY: ${body}")
.process(Document_WS_REPLY_PROCESSOR)
.endDoTry()
.doCatch(Exception.class)
.log(LoggingLevel.INFO, "SOAP REPLY WITH FAULTMESSAGE")
.log("Response ON ERROR FAULT: ${body}")
.process(Document_WS_REPLY_ERROR_PROCESSOR)
.end();
Everything goes as planned when the service response is "okay". Otherwise, when the service response is a soap:Fault, I'm not having access to all of the response (I am using soapUI to mock the soap:Fault response).
I can access a tiny fraction of the soap:fault by getting the EXCEPTION_CAUGHT property.
The instruction
.log("Response ON ERROR FAULT: ${body}")
Has no data at all.
What can I do differently to have access to all the instead of only the faultstring?
Exception exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT,
Exception.class);
According to this answer, Camel's CXF component not catching onException(Exception.class):
Camel's onException only triggeres if there is an exception. A SOAP
Fault is represented as a Message with the fault flag = true.
What you can do is to set handleFault=true on CamelContext, then it
will turn SOAP fault messages into an exception that the onException
can react upon.
Assuming you have not configured handleFault=true, then it's odd that your exception handler is running at all. It could be that some other exception, not the Fault you're looking for, is occurring which causes the exception handler to run.
If you have already configured handleFault=true, I don't have any advice except inspect the data objects in a debugger to see if you can find out what's going on. (If you don't know, to do this you can add a Processor to your exception handler and insert a break point inside the Processor. Break points don't work in route definition because route definitions are only run on initialization.)

Camel - Stop processing a route on exception

I have a camel route which processes hl7 messages. After the processing succeeds I would like to pass it to another endpoint b to do further processing.
But if any exception happens in processHL7, I have a catch block which does some processing. I want to stop processing when I go into the doCatch and encounter end, but this is not happening. Whatever happens the flow is going to endpoint b. How do i stop when I go into the doCatch block?
from("direct:a")
.doTry()
.to("bean:processHL7?method=process")
.doCatch(HL7Exception.class)
.to("direct:ErrorACK")
.transform(ack())
.end()
.transform(ack())
.to("direct:b");
This should work.
from("direct:a")
.doTry()
.to("bean:processHL7?method=process")
.doCatch(HL7Exception.class)
.to("direct:ErrorACK")
.transform(ack())
.stop()
.end()
.transform(ack())
.to("direct:b");
You can use stop() to stop the route from further processing.

Camel errorHandler / deadLetterChannel REST response

I have a Camel rest endpoint (Jetty) which validates and processes incoming requests. Besides specific Exception handlers (onException) it uses a DLQ error handler (errorHandler(deadLetterChannel...)) which is setup to retry 3 times - if unsuccessful the message is moved to the DLQ.
My question is, how do I still return a user friendly error message back to the client if an unexpected Exception occurs rather than the full Exception body? Is there some config I'm missing on the errorHandler?
I've tried to find some examples on the camel unit tests (DeadLetterChannelHandledExampleTest) and camel in action 2 (Chapter 11) but none seemed to have specific examples for this scenario.
Code is:
.from(ROUTE_URI)
.errorHandler(deadLetterChannel("{{activemq.webhook.dlq.queue}}")
.onPrepareFailure(new FailureProcessor())
.maximumRedeliveries(3)
.redeliveryDelay(1000))
.bean(ParcelProcessor.class, "process");
Thank you for your help!
Use a 2nd route as the DLQ, eg direct:dead and then send the message first to the real DLQ, and then do the message transformation afterwards to return a friendly response.
errorHandler(deadLetterChannel("direct:dead")
from("direct:dead")
.to("{{activemq.webhook.dlq.queue}}")
.transform(constant("Sorry something was wrong"));

Handle exception from load balancer only once (when failover is exhausted)

My code consumes jms queue and through lb redirects to external http client.
I need to log original message for every failed delivery to local directory.
Problem is that onException is caught by each failover.
Is there any way how to achieve this?
Pseudo code:
onException(Exception.class).useOriginalMessage()
.setHeader(...)
.to("file...")
.setHeader(...)
.to("file...")
from("activemq...")
.process(...)
.loadBalance().failover(...)
.to("lb-route1")
.to("lb-route2")
.end()
.process()
.to("file...")
from("lb-route1")
.recipientList("dynamic url")
.end()
from("lb-route2")
.recipientList("dynamic url")
.end()
I have not tested this logic with multiple to statements, but when I have my list of endpoints in an array this logic functions just fine. I will attempt to deliver to the first endpoint, if I cannot I will attempt to deliver this to the second endpoint. If an exception occurs at the second endpoint it will propagate back to the route's error handler. If you need it to round robin instead, change the last false on the failover statement to a true.
String[] endpointList = {"direct:end1", "direct:end2"};
from("direct:start")
.loadbalance().failover(endpointList.length-1, false, false)
.to(endpointList);

onCompletion in Camel 2.14

I'd like to wrap the result of a processed message into some reply-object to answer a webservice. This is my test-route:
this.from("cxf:someEndpoint")
.process(new SomeProcessorThatMightThrowAnException())
.process(new SomeOtherProcessorThatMightThrowAnException())
.log("normal end of route");
Nevermind if there was an exception or not, the result should be wrapped in some object, that is given back to the caller of my ws.
In camel 2.13.x I did this by adding an other processor to the end of the route and to do the same in 'onException'.
Now I tried to simplify this (technical thing and handle it outside of the 'functional route') in camel 2.14 (2.14 because of 'modeBeforeConsumer'), and added this to my routebuilder:
onCompletion()
.modeBeforeConsumer()
.process(new ConvertToWsReplyProcessor());
This ConvertToWsReplyProcessor should handle an Exception, but I found no way to see, if there was an Exception, because exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class) is allways null.
Questions:
1) Is there a way to find out if there was an excetion in onCompletion()?
2) The only way I found to prevent camel from dumping a stacktrace is to use onException(Ex...).handled(true), are there others?
3) How are these onXY processed? Do they get a copy of the exchange? And is onCompletion called last?
OnCompletionProcessor just remove some exchange properties before processing the exchange, that could explain why you cannot fine the exception here.
As camel use onException to handle the exception, I'm afraid you have to do it that way.

Resources