Apache Camel: How to catch exception from setProperty- Exchange? - apache-camel

In following method; getHumanResourcePort throws exception
.setProperty("humanResourcesService")
.exchange( d-> HumanResourcesUtil.getHumanResourcePort("INT04"))
There is global onException on whole route. But it is asking to catch this.
any idea of fiing same

You can do something similar:
onException(Exception.class) // global exception
.handled(true)
//somelogic
.end();
onException(SomeException.class) // HumanResourcesUtil.getHumanResourcePort exception
.handled(true)
//some other logic as well as you can throw another custom exception
.end();
please refer to the docs

Related

Exception from recipientList EIP of camel not caught at route level

Camel Version 2.22.0
Runtime: SpringBoot : 2.0.2.RELEASE
JDK version: 1.8.0_121
EIP: recipientList.
Problem: Exception raised from parallel process of recipientList is not caught at route level onException clause.
Below is the DSL
#Override
public void configure() throws Exception {
restConfiguration().clientRequestValidation(true)
//.contextPath("/pss/v1.0/")
.port("8080").host("0.0.0.0")
.enableCORS(true)
.apiContextPath("/api-doc")
.apiProperty("api.title", "Test REST API")
.apiProperty("api.version", "v1")
.apiContextRouteId("doc-api")
.component("servlet")
.bindingMode(RestBindingMode.json);
rest("/api/").clientRequestValidation(true)
.id("api-route")
.consumes("application/json")
.get("/bean/{name}")
.bindingMode(RestBindingMode.json)
.to("direct:remoteService");
from("direct:remoteService")
.onException(Exception.class).handled(true)
.log("Exception Caught : ${exception.message}")
.end()
.recipientList(constant("direct:route1, direct:route2"), ",").parallelProcessing().aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.end()
.log("The final Exchange data : ${exception.message}");
from("direct:route1")
.setHeader( Exchange.CONTENT_ENCODING, simple("gzip"))
.setBody(simple("RESPONSE - [ { \"id\" : \"bf383eotal length is 16250]]"))
.log("${body}");
from("direct:route2")
.log("${body}")
.process(e-> {
List<String> myList = new ArrayList();
myList.add("A");
myList.add("b");
myList.add("C");
e.getIn().setBody(myList);
})
.split(body())
.parallelProcessing(true)
.aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.log("${body}")
.choice()
.when(simple("${body} == 'b'"))
.throwException(new Exception("jsdhfjkASDf"));
}
Try make onException as global like this:
onException(Exception.class).handled(true)
.log("Exception Caught : ${exception.message}")
.end();
from("direct:remoteService")
.recipientList(constant("direct:route1, direct:route2"), ",").parallelProcessing().aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.end()
.log("The final Exchange data : ${exception.message}")
;
UPD: So you need to disable error handlers in recipient routes. Try like this (can't insert normally code sample)
That's a classical mistake: (exactly like the split EIP) each recipient will process a copy of the original Exchange. Any failure on these copies will not affect (raise an exception on) the route processing the master Exchange, as every single exchange runs in a completely separate unit of work.
If you enable the "shareUnitOfWork" option (on the recipientList), exceptions should be propagated.

Camel 2.18.0 pollEnrich connection exceptions are not caught with OnException block

Im trying to download a file with dynamic filenames using pollEnrich in a loop , when there is a conenction exception at pollEnrich it was not handled in onException block even io cannot us docatch after the pollenrich statment.
i also tried using throwExceptionOnConnectFailed=true in endpoint uri. Not no use.
do this have any workaround?
onException(Exception.class)
.log( "${exception.stacktrace}")
.end();
from("direct:DownloadFiles")
.loop(exchangeProperty("FileCount"))
.pollEnrich().simple("sftp://testeruser:password#localhost:24?
move=Processed&antInclude=*${property.soNumber}*.*").timeout(30000)
.to("TARGET SFTP endpoint")
.end();
By default Camel ignores connection problems
} catch (Exception e) {
loggedIn = false;
// login failed should we thrown exception
if (getEndpoint().getConfiguration().isThrowExceptionOnConnectFailed()) {
throw e;
}
}
Therefore you have to enable the option throwExceptionOnConnectFailed on the SFTP consumer. In your case this would be
.pollEnrich()
.simple("sftp://testeruser:password#localhost:24?move=Processed&throwExceptionOnConnectFailed=true&antInclude=*${property.soNumber}*.*")
.timeout(30000)
I know you write in your question that you tried that option without success, but in my test it is this option that decides (according to the Camel code above) if the ConnectException is reaching the error handler or is ignored.

Camel error handler processor exception not caught

Hei,
I try to setup a camel route like this:
onException(UnrecoverableException.class)
.process(new UnrecoverableExceptionProcessor());
onException(MappingException.class)
.process(new MappingExceptionProcessor())
.to(undeliverableChannel);
In case a MappingException is thrown the MappingExceptionProcessor is called.
This processor itself can throw an exception (for example UnrecoverableException). I am not able to catch this exception throw by the processor. What am I missing? Thanks!

apache camel idempotent consumer key removal

I have a setup like below.
The issue I have is that the key from the Idempotent Repository is not being removed when an exception is thrown (on line stated below) when using seda component within OnException. When I change to use direct within the OnException the key is removed from the cache. On both trials the email is also being sent correctly.
My queries are:
why is the key not being removed from the repository cache when using seda within the OnException?
is there an issue with using seda within the OnException?
Here is the routes:
MyRouteClass1
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.to("seda:notifySupportOnFailure")
.end();
from("direct:findWorkItems")
.bean(someService, "findWorkItems")
.split(body())
.throttle(1).timePeriodMillis(5000L)
.to("direct:handleWorkItem")
.choice().when(header("resultId").isGreaterThan(0))
.bean(someService, "updateWorkItemToHandled")
.end();
from("direct:handleWorkItem")
.idempotentConsumer(simple("${body.workItemId}"), duplicatesRepo)
.bean(someService, "handleWorkItem") // e.g. exception would be thrown within here
.setHeader("resultId", body())
.end();
MyRouteClass2
from("seda:notifySupportOnFailure")
.setHeader("from", simple("sender#mail.com"))
.setHeader("to", simple("recipient#mail.com"))
.setBody(simple("Failure:\n ${exception.message} \n\ndetail: \n${body}"))
.to("smtp://localhost")
.log("Failure email now sent.")
.end();
I have also attempted another workaround. I have modified the onException clause as per below. This does allow the key to be removed from the Repository when the exception is thrown. I am wondering if this is a correct approach ?
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.multicast().to("seda:notifySupportOnFailure").end()
.end();
A work around would be to put the idempotentConsumer call in your parent route 'findWorkItems' and setting the completionEager flag on it to true.
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.to("seda:notifySupportOnFailure")
.end();
from("direct:findWorkItems")
.idempotentConsumer(simple("${body.workItemId}"), duplicatesRepo).completionEager(true)
.bean(someService, "findWorkItems")
.split(body())
.throttle(1).timePeriodMillis(5000L)
.to("direct:handleWorkItem")
.choice().when(header("resultId").isGreaterThan(0))
.bean(someService, "updateWorkItemToHandled")
.end();
from("direct:handleWorkItem")
.bean(someService, "handleWorkItem") // e.g. exception would be thrown within here
.setHeader("resultId", body())
.end();

Camel message redelivery not behaving as expected

I have a route in Camel that I want to retry when an exception occurs, but I want to set a property so that the route can do something slightly differently the second time to try to stop the error happening again on the retry. Here's a route that illustrates the idea I'm trying at the moment.
from("direct:onExceptionTest")
.onException(Exception.class)
.maximumRedeliveries(1)
.log("Retrying")
.setProperty("retrying", constant(true))
.end()
.log("Start")
.choice()
.when(property("retrying").isNull())
.log("Throwing")
.throwException(new Exception("Hello world"))
.end()
.end()
.log("Done")
Obviously this isn't the real route; the whole choice body just simulates my component erroring in certain cases. I'm expecting to see the following messages logged:
Start
Throwing
Retrying
Start
Done
But what I actually see is:
Start
Throwing
Retrying
Failed delivery for (MessageId: ... on ExchangeId: ...). Exhausted after delivery attempt: 2 caught: java.lang.Exception: Hello world. Processed by failure processor: FatalFallbackErrorHandler[Pipeline[[Channel[Log(onExceptionTest)[Retrying]], Channel[setProperty(retrying, true)]]]]
I've tried adding handled(true) to the exception handler, but all this does is suppress the error message. I don't see the second Start or Done log message.
Why doesn't my route behave as I expect, and what do I need to do to get it to behave the way I want?
Update
#ProgrammerDan points out that the problem is that redelivery isn't intended for what I'm trying to achieve, which would explain why my route doesn't work! So I need to do the work in my handler, but my route calls a web service and has a few other steps and I don't want to duplicate all this in the handler. I've come up with this, which works as expected but it involves the route calling itself again from the start. Is this a bad idea? Will I get myself into knots with this approach?
from("direct:onExceptionTest")
.onException(Exception.class)
.onWhen(property("retrying").isNull()) // don't retry forever
.log("Retrying")
.setProperty("retrying", constant(true))
.handled(true)
.to("direct:onExceptionTest") // is recursion bad?
.end()
.log("Start")
.choice()
.when(property("retrying").isNull())
.log("Throwing")
.throwException(new Exception("Hello world"))
.end()
.end()
.log("Done")
Use onRedelivery with a Processor to set the property:
String KEY = "retrying";
from("direct:onExceptionTest")
.onException(RuntimeException.class)
.onRedelivery(new Processor() { // Sets a processor that should be processed before a redelivery attempt.
#Override
public void process(final Exchange exchange) throws Exception {
LOG.info("Retrying");
exchange.setProperty(KEY, true);
}
})
.maximumRedeliveries(1)
.handled(true)
.end()
.log("Start")
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
LOG.info("No problem");
}
})
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
if (exchange.getProperty(KEY) == null) {
LOG.info("Throwing");
throw new RuntimeException("Hello World");
}
else {
LOG.info("No throwing");
}
}
})
.log("Done");
This prints
[ main] route1 INFO Start
[ main] OnExceptionHandler INFO No problem
[ main] OnExceptionHandler INFO Throwing
[ main] OnExceptionHandler INFO Retrying
[ main] OnExceptionHandler INFO No throwing
[ main] route1 INFO Done
As #ProgrammerDan noted, only the processor that failed is re-executed but not the first processor that passed without any problems.
Edit:
If all the processing has to be re-done then you may use a sub-route with doTry and doCatch as follows:
from("direct:onExceptionTest")
.doTry()
.to("direct:subroute")
.doCatch(RuntimeException.class)
.setProperty(KEY, constant(true))
.to("direct:subroute")
.end()
.log("Done");
from("direct:subroute")
.log("Start")
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
LOG.info("No problem");
}
})
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
if (exchange.getProperty(KEY) == null) {
LOG.info("Throwing");
throw new RuntimeException("Hello World");
}
else {
LOG.info("No throwing");
}
}
});
From the Camel Docs:
When using doTry .. doCatch .. doFinally then the regular Camel Error Handler does not apply. That means any onException or the likes does not trigger. The reason is that doTry .. doCatch .. doFinally is in fact its own error handler and that it aims to mimic and work like how try/catch/finally works in Java.
Couple of points to consider about Camel's redelivery mechanism. First, check out the docs on the topic which might challenge your assumptions about how Camel handles redelivery. The point I've linked to is that Camel attempts redelivery at point of failure, it does not start over from the beginning of the route (as you appear to assume). If I'm understanding the docs correctly (I haven't tried this pattern in a while) you are basically telling it to retry throwing an exception several times, which I doubt is what you want to test.
Second, I'd recommend just doing the alternate handling directly in the onException() processor chain, as demonstrated a little further down in the same docs. Basically, you could specify how you want the message handled via a custom processor, and use both handled(true) and stop() to indicate that no further processing is necessary.
To sum it up, redelivery is generally meant to handle typical endpoint delivery failures, like intermittent connectivity drops, receiving server momentary unavailability, etc. where it makes the most sense to just "try again" and have a reasonable expectation of success. If you need more complex logic to handle retries, use a custom processor or series of processors within your onException() processor chain.

Resources