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

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

Related

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

Apache Camel - recipientList - need of end

After using recipientList in a camel route, within a choice.when, I would like to route this further to another destination with to("xy").
Syntax-highlighting in a java IDE is showing me that this is not possible.
If I put an end after recipientList, all appears to fit again.
Is that required? I could not find any examples in the docs/net showing s/t similar...
.choice()
.when(aPredicate)
.setHeader(Exchange.FILE_NAME).simple("st")
.recipientList(getAValueBuilder())
.end()
.to("ftp:me#ftpserv//usr/dest")
.when(anotherPredicate)
.to(nirv)
.setHeader(Exchange.FILE_NAME).simple("nt")
.to("ftp:me#ftpserv//usr/anotherdest")
.end()
Generarilly I am not sure, while using choice, when to use end. And to make it more difficult, there is an endChoice...
I tryed to use the formatting, to show the way I think it should be use above.
Thanks for feedback.

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.

Camel - Exception handling in 'sub routes'

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.

Resources