In my project I want to apply separate OnCompletion and OnException processor on each newly created route.
Suppose I have to create 3 routes. For each route I am preparing a separate RouteBuilder class and doing configuration like below -
onException(Throwable.class).handled(true).process(new ExceptionHandlingProcessor(RouteId)).maximumRedeliveries(2)
.redeliveryDelay(1000).retriesExhaustedLogLevel(LoggingLevel.ERROR)
.retryAttemptedLogLevel(LoggingLevel.WARN);
onCompletion().process(new OnCompletionProcessor(RouteId)) ;
from("sftp:Configuration").routeId("test")
.choice()
.when(body().isEqualTo(null))
.process(new AgeCalculatingProcessor("test"))
.otherwise()
.to("file:configuration").log("Downloaded file ${file:name} complete.")
;
My question is ....are the OnException and OnCompletion working on the route that is being created on the same Route Builder class (as I am creating only one route in each RouteBuilder class) or these will be applied to context level and will work on all the routes?
Actually I want to apply Onexception and OnCompletion on Route level, but I am getting exception (like - try moving OnException to the top of route), if I apply the OnException on each endPoint, like below -
from(sftp:conf).OnException(Throwable.class).restExceptionconf
.to(file:conf).OnException(Throwable.class).restExceptionConf
RouteBuilder level onException: If you define onException handler like this
onException(...).log("This is RouteBuilder level exception handling.");
configure() {
from(...).to(...);
}
it will handler exceptions from all routes within the same RouteBuilder.
Route level onException: If you define onException handler like this
configure() {
from(...)
.onException(...).log("This is Route level exception handling.");
.to(...);
}
it will become a route level onException handler and it will be used only for exceptions on that single route.
Route level onException definitions will override RouteBuilder level definitions (e.g. if both define onException(MyException.class) then only the one defined directly in the route will be called if MyException is raised on that route).
onCompletion will behave the same way as onException.
About the "try moving OnException to the top of route" exception you are getting: you are supposed to only define onException at the beginning of the route like this:
from(sftp:conf).OnException(Throwable.class).restExceptionconf
.to(file:conf);
Further reading about onException here and about onCompletion here.
Related
How do I stop a camel route when there is an exception. My dynamic route is consuming jms and sending to reactive endpoint.
camelContext.addRoutes(New GenerateRoute());
Route generator class is as below:
public class GenerateRoute extends RouteBuilder {
#Override
public void configure() {
from("jms:queue:myQueue")
.toF("reactive-streams:myStream").setId("myRoute");
}}
Try this
from("jms:queue:myQueue")
.routeId("myRoute")
.doTry()
.toF("reactive-streams:myStream")
.doCatch(Exception.class)
.process(exchange -> exchange.getFromEndpoint().stop())
.end();
You can use handled and continued. in the onException clause. "Continued allows you to both handle and continue routing in the original route as if the exception did not occur." if continued is false the routing won't go back to the original route.
DSL:
<onException>
<exception>java.lang.IllegalArgumentException</exception>
<continued><constant>false</constant></continued>
</onException>
Java:
onException(IllegalArgumentException.class).continued(fasle);
Refer to: https://camel.apache.org/manual/latest/exception-clause.html#
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.
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();
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.
I created a camel route without INOUT exchange pattern and the route looks like
direct:start > bean:myBean?method=handle
I'm sending payload using ProducerTemplate's send method
Exchange response = producerTemplate.send(endpointUri, exchange);
I set the exception on exchange in the bean's handle method, but its not retained in the response.
Is there something I'm missing.
You should throw an exception from the bean if you want to signal an exception.
I found where camel's hiding the exception. Since I marked the exchange as handled and marked for rollback, camel is setting the exception to null and moved it to properties.
I was able to retrieve it using
result.getProperty(Exchange.EXCEPTION_CAUGHT)