Camel - Exception handling in 'sub routes' - apache-camel

Camel explicitly handles two 'scopes' of error handling:
Global
per Route
The issue I'm having is exceptions thrown in a 'sub route'. For instance, I've got this route:
from("direct:sendToWebservice").
.processRef("massageBeforeSending").
.to("http://webservice.com").
.processRef("massageResponse");
Then I've got two other routes that need to send messages to the webservice:
from(direct:fromSystemA").
.errorHandler(deadLetterChannel("direct:TellSystemA")).
.to("direct:sendToWebservice");
from(direct:fromSystemB").
.errorHandler(deadLetterChannel("direct:TellSystemB")).
.to("direct:sendToWebservice");
What I would like to happen is, if the webservice route throws an exception, it's propagated up to the caller, and either system A or system B would be notified. I don't see a way to achieve this.
I feel like this would be a common use case - has anyone bumped up against it before?
Thanks again for your time,
Roy

Got the answer from a colleague: The subroute needs to have it's error handling disabled:
from("direct:sendToWebservice").
.errorHandler(noErrorHandler()) // disables error handling for this route
.processRef("massageBeforeSending").
.to("http://webservice.com").
.processRef("massageResponse");
This forces Camel to propagate the error to the calling route.

Related

Camel -Specific exception processing handling based on message

I have a queue system of which camel is just a small part of. In this queue system, for some queues, the broker returns FAIL when the queue is full. To handle this, I look at JMS Exception I get and from the message I can see whether or not the reason is that the queue is full.
What I want to achieve in Camel is that for the specific case of full queue, I want to retry deliveries, whereas for any other JMS Exception (or any other exception) I want to send it to DLQ.
I'm assuming that I have to use the onException(JMSException.class) and provide a custom processor which will look at the exception message, but after that, I'm not sure what to do. I tried raising a specific custom exception from the processor in that case (e.g. QueueFailedException) and have another onException(QueueFailedException) in the setup, but then I get the following error:
"(...) ERROR org.apache.camel.processor.FatalFallbackErrorHandler - Exception occurred while trying to handle previously thrown exception on exchangeId: (...) using: [Channel[DelegateSync[org.eumetsat.gems.bridge.JmsMessageReplicator$JMSExceptionHandler#67a93d5f]]]. The previous and the new exception will be logged in the following."
As far as I understood, you need to tell Camel it must consider an exception handled and also it should retry sending the message according to some logic check. The .handled() also accepts a Predicate or an Expression besides Boolean.
Don't the following sections help you achieve that?
Marking exceptions as handled
Using fine grained retry using retrywhile predicate.
I'm considering you're able to identify if the exception was really caused by a queue flooding through a caught JMSException.

Is there a way to abort the processing of a Routing Slip without throwing an Exception?

I have a routing slip that has a handful of steps. I want to be able to include steps that perform some validation that can short-circuit the routing slip if needed.
I have:
from("direct:Start")
.setHeader(header("RoutingSlip", config::getRoutingSlip)
.doTry()
.routingSlip(header("RoutingSlip"))
.doEndTry()
.doCatch(ValidationException.class)
.log("Validation failed!")
.end()
Is this the best way to do this?
The fact that there is a stacktrace being logged makes me question if this is the right way to do this.
Thanks.
Could you try to replace your raising exception code by setting
the property Exchange.ROUTE_STOP to true
ie.
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);

I have issues with the design of onException handled(true) in apache camel

onException(NullPointerException.class)
.handled(true)
.to("google-pubsub:some_topic");
In here, my design is such that for any unforseen error,I want to put the problem message to gcp pubsub error topic.
But I am saying "handled" as true.Hence for any error in publishing to pubsub error topic (say network error etc),the error will be silently ignored !! This is no good for me.Now I lost the message because since it was handled,the message has been acknowledged automatically and the message will not be redelivered by gcp pubsub !!
Please let me know my alternatives
I was just going through the Camel in Action Book.Below is the text
"Camel doesn’t allow further error
handling while already handling an error(onException handled is true) . In other words, when
Camel detects that another
exception was thrown during error handling, it prevents any further action
from taking place. This is done by the
org.apache.camel.processor.FataFallbackErrorHandler, which catches the
new exception, logs a warning, sets this as the exception on the Exchange, and
stops any further routing."
This mean if an exception is thrown while handling an exception,the exception will not be propagated,but a warning will be logged and sets an exception on the exchange.
Since an error has been marked in the exchange ,the pub sub message in the exchange will not be marked as Acked,and will be redelivered by pubsub.
Let me test it my self.
Request-reply pattern
If the caller knew operation has failed, they can retry so you will not mark the exception as handled
One-way pattern and can afford to lose message
Just like java catch block that catches but doesn't re-throw. May be you log a message. Since you can afford to lose the message, it is fine.
One-way pattern, you prefer moving the message somewhere on best of efforts
Exactly like your example. You prefer moving somewhere but if the error hapens on the onException route, you are ok to lose the message
One-way pattern and you cannot afford to lose message
In case of recoverable error, you want to retry a few times but after that you want to move it somewhere else. In the case of irrecoverable error, you want to move it somewhere else straight away. In both cases, if you don't move it, you will endup with infinite loop and your route will be busy repeatedly consuming the same message while others are ignored.
Since you cannot afford to lose the message if the error happens on the onException route, you cannot mark it as handled and at the same time, you cannot let it go back and start a infinite loop
So your option here is Dead Letter Channel error handler
DeadLetterChannel
When the DeadLetterChannel moves a message to the dead letter endpoint, any new Exception thrown is by default handled by the dead letter channel as well. This ensures that the DeadLetterChannel will always succeed.
Reference
https://camel.apache.org/components/latest/eips/dead-letter-channel.html
Note:
As you can see in the image, DeadLetterChannel is another error handler
Move the exception handling logic to new route and use queue/direct component to handle it. In case of failure you can write your failed message to an error/dlq and come up with a strategy to reprocess them.
Main Route:
onException(Exception.class)
.handled(true)
.to("queue:myapp.exception.handler");
from(direct:mainRoute")
.process("processTransaction")
.to("sql:***");
Exception Handler Route:
onException(Exception.class)
.handled(true)
.maximumRedeliveries(2)
.redeliveryDelay(30000)
.to("queue:myapp.exception.handler.failed") ;
from("queue:myapp.exception.handler")
.to("google-pubsub:some_topic");

Trying to stop Camel exchange processing based on filter

My question is very similar to this one but the solution there does not work for me here - I am trying to use the filter EIP to discard selected exchanges. My routes look like (edited down for clarity):
from("{{fromSource}}")
.convertBodyTo(RequestInterface.class)
.enrich(INVOKE_BACKEND_URI, combiner)
.to("{{toDestination}}");
from(INVOKE_BACKEND_URI)
.to(backendUri)
.filter().method(DiscardResponse.class).log(LoggingLevel.INFO, "Discarding undesired response").stop().end()
.convertBodyTo(BodyInterface.class);
When the filter does NOT select the message, all is well - the log() is not displayed and the message goes to the convertBodyTo() and then back to the main route.
However, when the filter DOES select the message, the log() text is displayed but the exchange still continues on to the convertBodyTo() where it throws an exception because it's a message that shouldn't be there. The stop() appears to either not be executed or has no affect.
Can anyone suggest a solution to this?
It is possible from within a Processor to do this in order to stop the exchange:
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
Since I'm not used to writing my routes using Java DSL I don't know if that option is available directly on the exchange within the route, but it probably is.
I guess one way could be:
from(INVOKE_BACKEND_URI)
.to(backendUri)
.filter().method(DiscardResponse.class).log(LoggingLevel.INFO, "Discarding undesired response")
.choice()
.when(simple("${property.Exchange.FILTER_MATCHED}=true")
.stop()
.end()
.convertBodyTo(BodyInterface.class);
Take a look at the bottom of the doc here:
http://camel.apache.org/message-filter.html

how to handle exception or fault in multiple routes

I have some problems when handle the exception between multiple routes.
As a java developer's perspective, I want to extract some common logic to a common route so that other routes can call the common route directly without containing the common logic everywhere.(like the route-version function call) But when it comes to the error handling, I found it's a little tricky.
For instance:
//main logic 1
from("direct:route1")
.doTry()
.to("direct:common")
.doCatch(Exception.class)
.log("Error in route1")
.end()
//main logic 2
from("direct:route2")
.doTry()
.to("direct:common")
.doCatch(Exception.class)
.log("Error in route2")
.end()
//common logic
from("direct:common")
.to("mock:commonlogic")
The problem is when some exception thrown from the "mock:commonlogic" endpoint, the exception won't be caught by doTry...doCatch blocks defined both in route1 and route2. It seems like the exception just can be handled in the common route scope. But what I want is the common route just 'throws out' the exception and the 'caller' routes handle it all by themselves. Is there any way to do that?
Thanks
You need to disable error handling in the common route.Then any exceptions thrown from the common route, is not handled by any error handler, and propagated back to the caller route, which has the try .. catch block.
from("direct:common")
.errorHandler(noErrorHandler())
.to("mock:commonlogic")
You might want to use the exception clause.
http://camel.apache.org/exception-clause.html
Like this (in the route builder's configure method)
// A common error handler for all exceptions. You could also write onException statements for explicit exception types to handle different errors different ways.
onException(Exception.class).to("log:something");
from("direct:route1")...;
from("direct:route2")...;
It should do the trick for you.
The onException will be global for the current route builder.

Resources