Apache Camel provides several different mechanisms, which let you handle exceptions at different levels of granularity: you can handle exceptions within a route using doTry
, doCatch
, and doFinally
; or you can specify what action to take for each exception type and apply this rule to all routes in a RouteBuilder
using onException
option.
Trapping exceptions using onException
The onException
clause is a mechanism for trapping, rather than catching exceptions. That is, once you define an onException clause, it traps exceptions that occur at any point in a route. This contrasts with the Java try/catch mechanism, where an exception is caught, only if a particular code fragment is explicitly enclosed in a try block.
What really happens when you define an onException clause is that the Apache Camel runtime implicitly encloses each route node in a try block. In the following example, the onException clause applies to all of the routes defined in the RouteBuilder
class. If an exception occurs while processing either of the routes (from("seda:inputA")
or from("seda:inputB")
), then the onException clause will automatically trap the exception and then it will redirect the current exchange to the validationFailed
JMS queue
<camelContext xmlns="http://camel.apache.org/schema/spring"> <onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <route> <from uri="seda:inputA"/> <to uri="validation:foo/bar.xsd"/> <to uri="activemq:someQueue"/> </route> <route> <from uri="seda:inputB"/> <to uri="rnc:mySchema.rnc"/> <to uri="activemq:anotherQueue"/> </route> </camelContext>
Trapping multiple exceptions
We can define multiple exception clauses to trap more than exceptions in a RouteBuilder scope. This provides more flexibility and enables us to take different actions in response to different types of exceptions. For example, the following series of exception clauses defined in the DSL defines different destinations for ValidationException
, IOException
, and Exception
:
<onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <onException> <exception>java.io.IOException</exception> <to uri="activemq:ioExceptions"/> </onException> <onException> <exception>java.lang.Exception</exception> <to uri="activemq:exceptions"/> </onException>
Additionally, we can group multiple exceptions together by defining more than one exception element inside the onException
statement:
<onException> <exception>com.mycompany.ValidationException</exception> <exception>com.mycompany.BuesinessException</exception> <to uri="validationFailed"/> </onException>
Deadletter Channel
The deadletter channel serves as a holding area for failed messages that have not been processed. An administrator can inspect the messages at a later time and decide what action needs to be taken.
Conditional trapping
Exception trapping with onException
can be made conditional by specifying the onWhen option. If you specify the onWhen option in an onException clause, a match is triggered only when the thrown exception matches the clause and the onWhen
predicate evaluates to true
on the current exchange.
<onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <onWhen> <simple>${header.user} != null</simple> </onWhen> <to uri="activemq:error_user_queue"/> </onException> <onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <to uri="activemq:error_queue"/> </onException>
Handling exceptions
By default, when an exception is raised or found in the middle of a route, processing of the current exchange is interrupted and the thrown exception is propagated back to the consumer endpoint at the start of the route.
However, when an onException clause is triggered, the behavior is similar with the exception that the onException
clause will perform some additional processing before the thrown exception is propagated back.
Sending a response
When the consumer endpoint that starts a route expects a reply, you might prefer to construct a custom fault reply message, instead of simply letting the thrown exception propagate back to the consumer. There are two essential steps you need to follow in this case: suppress the rethrown exception using the handled
option; and populate the exchange’s Out message slot with a custom fault message.
<onException> <exception>com.mycompany.MyFunctionalException</exception> <handled> <constant>true</constant> </handled> <transform> <simple>Error reported: ${exception.message} - cannot process this message.</simple> </transform> </onException>