...
Here if the processing of seda:inputA or seda:inputB cause a ValidationException to be thrown (such as due to the XSD validation of the Validation component or the Relax NG Compact syntax validation of the Jing component), then the message will be sent to activemq:validationFailed queue.
How does Camel select which clause should handle a given thrown Exception
Camel uses DefaultExceptionPolicyStrategy
to determine a strategy how an exception being thrown should be handled by which onException
clause. The strategy is:
- the order in which the
onException
is configured takes precedence. Camel will test from first...last defined. - Camel will start from the bottom (nested caused by) and recursive up in the exception hierarchy to find the first matching
onException
clause instanceof
test is used for testing the given exception with theonException
clause defined exception list. An exactinstanceof
match will always be used, otherwise theonException
clause that has an exception that is the closets super of the thrown exception is selected (recurring up the exception hierarchy)
This is best illustrated with an exception:
Code Block | ||||
---|---|---|---|---|
| ||||
onException(IOException.class).maximumRedeliveries(3);
onException(OrderFailedException.class).maximumRedeliveries(2);
|
In the sample above we have defined two exceptions in which IOException is first, so Camel will pickup this exception if there is a match. IOException that is more general is selected then.
So if an exception is thrown with this hierarchy:
Code Block | |||
---|---|---|---|
+ RuntimeCamelException (wrapper exception by Camel)
+ OrderFailedException | |||
Warning | |||
| |||
It's important to notice that the exception clauses are added to the error handler. So if you have not or only have one error handler defined then all exception clauses is configure to the same error handler. Even though you have exception clauses defined in different routes. So in this situation there will always be 3 exception clauses for both routes Code Block | | java | java | public void configure() throws Exception { // general error handler using [Dead Letter Channel] errorHandler(deadLetterChannel("seda:error").maximumRedeliveries(2)); // excption clauses can also be defined within routes. MUST use .end() to indicate end of exception clause and where the regular routing continues
Then Camel will try testing the exception in this order: FileNotFoundException, IOException, OrderFailedException and RuntimeCamelException.
As we have defined a onException(
...
IOException.class) Camel will select this as it's the closest match.
If we add a third onException clause with the FileNotFoundException
Code Block | ||||
---|---|---|---|---|
| ||||
onException(IOException.class).maximumRedeliveries(3);
onException(OrderFailedException.class).maximumRedeliveries(2);
onException(FileNotFoundException.class).handled(true).to("log:nofile");
|
Then with the previous example Camel will now use the last onException(FileNotFoundException.class)
as its an exact match. Since this is an exact match it will override the general IOException that was used before to handle the same exception thrown.
Now a new situation if this exception was thrown instead:
Code Block |
---|
+ RuntimeCamelException (wrapper exception by Camel)
+ OrderFailedException
+ OrderNotFoundException
|
Then the onException(OrderFailedException.class)
will be selected - no surprise here.
And this last sample demonstrates the instanceof
test aspect in which Camel will select an exception if it's an instance of the defined exception in the onException
clause. Illustrated as:
Code Block |
---|
+ RuntimeCamelException (wrapper exception by Camel)
+ SocketException
|
Since SocketException is an instanceof IOException, Camel will select the onException(IOException.class)
clause.
Configuring ReliveryPolicy (redeliver options)
The default error handler used in Camel is the Dead Letter Channel which supports attempting to redeliver the message exchange a number of times before sending it to a dead letter endpoint.
See Dead Letter Channel for further information about redeliver.
Sometimes you want to configure the redelivery policy on a per exception type basis. By default in the first examples, if a ValidationException occurs then the message will not be redelivered; however if some other exception occurs (such as a JDBC deadlock or remote method invocation) the route will be retried.
However if you want to customize any methods on the RedeliveryPolicy object, you can do this via the fluent API:
Code Block |
---|
onException(MyException.class).
maximumRedeliveries(2);
|
And the spring DSL:
Code Block | ||||
---|---|---|---|---|
| ||||
<onException>
<exception>com.mycompany.MyException</exception>
<redeliveryPolicy maximumRedeliveries="2"/>
</onException>
|
Reusing ReliveryPolicy
Available as of Camel 1.5.1 or later
You can reference a RedeliveryPolicy
so you can reuse existing configurations and use standard spring bean style configuration that supports property placeholders.
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="myRedeliveryPolicy" class="org.apache.camel.processor.RedeliveryPolicy">
<property name="maximumRedeliveries" value="${myprop.max}"/>
</bean>
<onException>
<!-- you can define multiple exceptions just adding more exception elements as show below -->
<exception>com.mycompany.MyFirstException</exception> .maximumRedeliveries(0).end()
.to("bean:handleOrder");
from("direct:shipOrder")
.onException(MyShipException.class).maximumRedeliveries(3).end()
.onException(Exception.class).handled(true).maximumRedeliveries(0).to("bean:fallbackHandler").end()
.to("bean:shipOrder");
In the example above we always have 3 exception clauses defined for the same error handler, as we only have one global error handler. So if we are processing an order in the first route, and a MyOrderException is thrown it is caught as expected. However what would be unexpected is that any other exceptions will be triggered to be handled by onException(Exception.class) from the 2nd route. To fix this you need to add a nested errorHandler as well. Code Block | | java | java | |
from("direct:order")
.errorHandler(deadLetterChannel("seda:orderError").maximumRedeliveries(2))
.onException(MyOrderException.class).maximumRedeliveries(0).end()
.to("bean:handleOrder");
| ||||
Code Block | java | java | public void configure() throws Exception { // general error handler using [Dead Letter Channel] errorHandler(deadLetterChannel("seda:error").maximumRedeliveries(2)); // excption clauses onException(MyOrderException.class).maximumRedeliveries(0); onException(MyShipException.class).maximumRedeliveries(3);
Exception Policy Strategy
As of Camel 1.4 you can configure the ExceptionPolicyStrategy as this example shows. Notice that we use Exception Clause to handle known exceptions being thrown.
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/CustomExceptionPolicyStrategyTest.java} |
Using our own strategy MyPolicy we can change the default behavior of Camel with our own code to resolve which ExceptionType from above should be handling the given thrown exception.
</onException>
|
Marking exceptions as being handled
Available as of Camel 1.5
Using onException to handle known exceptions is a very powerful feature in Camel. However prior to Camel 1.5 you could not mark the exception as being handled, so the caller would still receive the caused exception as a response. In Camel 1.5 you can now change this behavior with the new handle DSL. The handle is a Predicate that is overloaded to accept three types of parameters:
- Boolean
- Predicate
- Expression that will be evaluates as a Predicate using this rule set: If the expressions returns a Boolean its used directly. For any other response its regarded as
true
if the response isnot null
.
For instance to mark all ValidationException
as being handled we can do this:
Code Block |
---|
onException(ValidationException).handled(true);
|
Example using handled
In this route below we want to do special handling of all OrderFailedException as we want to return a customized response to the caller. First we setup our routing as:
unmigratedunmigrated-inline-wiki-markup |
---|
{snippet:id=e2e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/CustomExceptionPolicyStrategyTestDeadLetterChannelHandledExampleTest.java} |
How does Camel select which clause should handle a given thrown Exception
Camel uses DefaultExceptionPolicyStrategy
to determine a strategy how an exception being thrown should be handled by which onException
clause. The strategy is:
- the order in which the
onException
is configured takes precedence. Camel will test from first...last defined. - Camel will start from the bottom (nested caused by) and recursive up in the exception hierarchy to find the first matching
onException
clause instanceof
test is used for testing the given exception with theonException
clause defined exception list. An exactinstanceof
match will always be used, otherwise theonException
clause that has an exception that is the closets super of the thrown exception is selected (recurring up the exception hierarchy)
This is best illustrated with an exception:
...
onException(IOException.class).maximumRedeliveries(3);
onException(OrderFailedException.class).maximumRedeliveries(2);
In the sample above we have defined two exceptions in which IOException is first, so Camel will pickup this exception if there is a match. IOException that is more general is selected then.
So if an exception is thrown with this hierarchy:
Code Block |
---|
+ RuntimeCamelException (wrapper exception by Camel)
+ OrderFailedException
+ IOException
+ FileNotFoundException
|
Then Camel will try testing the exception in this order: FileNotFoundException, IOException, OrderFailedException and RuntimeCamelException.
As we have defined a onException(IOException.class) Camel will select this as it's the closest match.
If we add a third onException clause with the FileNotFoundException
...
onException(IOException.class).maximumRedeliveries(3);
onException(OrderFailedException.class).maximumRedeliveries(2);
onException(FileNotFoundException.class).handled(true).to("log:nofile");
Then with the previous example Camel will now use the last onException(FileNotFoundException.class)
as its an exact match. Since this is an exact match it will override the general IOException that was used before to handle the same exception thrown.
Now a new situation if this exception was thrown instead:
Code Block |
---|
+ RuntimeCamelException (wrapper exception by Camel)
+ OrderFailedException
+ OrderNotFoundException
|
Then the onException(OrderFailedException.class)
will be selected - no surprise here.
And this last sample demonstrates the instanceof
test aspect in which Camel will select an exception if it's an instance of the defined exception in the onException
clause. Illustrated as:
Code Block |
---|
+ RuntimeCamelException (wrapper exception by Camel)
+ SocketException
|
Since SocketException is an instanceof IOException, Camel will select the onException(IOException.class)
clause.
Catching multiple exceptions
Available as of Camel 1.5
In Camel 1.5 the exception clauses has been renamed to onException and it also supports multiple exception classes:
Code Block |
---|
onException(MyBusinessException.class, MyOtherBusinessException.class).
to("activemq:businessFailed");
|
But the most important new feature is support for marking exceptions as being handled.
Marking exceptions as being handled
Available as of Camel 1.5
Using onException to handle known exceptions is a very powerful feature in Camel. However prior to Camel 1.5 you could not mark the exception as being handled, so the caller would still receive the caused exception as a response. In Camel 1.5 you can now change this behavior with the new handle DSL. The handle is a Predicate that is overloaded to accept three types of parameters:
- Boolean
- Predicate
- Expression that will be evaluates as a Predicate using this rule set: If the expressions returns a Boolean its used directly. For any other response its regarded as
true
if the response isnot null
.
For instance to mark all ValidationException
as being handled we can do this:
Code Block |
---|
onException(ValidationException).handled(true);
|
Example using handled
In this route below we want to do special handling of all OrderFailedException as we want to return a customized response to the caller. First we setup our routing as:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/DeadLetterChannelHandledExampleTest.java} |
Then we have our service beans that is just plain POJO demonstrating how you can use Bean Integration in Camel to avoid being tied to the Camel API:
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/DeadLetterChannelHandledExampleTest.java} |
And finally the exception that is being thrown is just a regular exception:
Wiki Markup |
---|
{snippet:id=e3|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/DeadLetterChannelHandledExampleTest.java} |
So what happens?
If we sent an order that is being processed OK then the caller will receive an Exchange as reply containing Order OK
as the payload and orderid=123
in a header.
If the order could not be processed and thus an OrderFailedException was thrown the caller will not receive this exception (as opposed to in Camel 1.4, where the caller received the OrderFailedException) but our customized response that we have fabricated in the orderFailed
method in our OrderService
. So the caller receives an Exchange with the payload Order ERROR
and a orderid=failed
in a header.
Using handled with Spring DSL
The same route as above in Spring DSL:
Wiki Markup |
---|
{snippet:id=e1|lang=xml|url=activemq/camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/onexception/deadLetterChannelHandledExampleTest.xml} |
Handling and sending a fixed response back to the client
In the route above we handled the exception but routed it to a different endpoint. What if you need to alter the response and send a fixed response back to the original caller (the client). No secret here just do as you do in normal Camel routing, use transform to set the response, as shown in the sample below:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
We modify the sample slightly to return the original caused exception message instead of the fixed text Sorry:
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
And we can use the Simple language to set a readable error message with the caused excepetion message:
Wiki Markup |
---|
{snippet:id=e3|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
Overloading the RedeliveryPolicy
The default error handler used in Camel is the Dead Letter Channel which supports attempting to redeliver the message exchange a number of times before sending it to a dead letter endpoint. Sometimes you want to overload the redelivery policy on a per exception type basis. By default in the above examples, if a ValidationException occurs then the message will not be redelivered; however if some other exception occurs (such as a JDBC deadlock or remote method invocation) the route will be retried.
However if you want to customize any methods on the RedeliveryPolicy object, you can do this via the fluent API...
Code Block |
---|
onException(MyException.class).
maximumRedeliveries(2);
|
You can mix and match these approaches; specifying a custom processor to be used after all the redeliveries fail together with customizing any aspect of the RedeliveryPolicy
And the spring DSL:
...
<onException>
<exception>com.mycompany.MyException</exception>
<redeliveryPolicy maximumRedeliveries="2"/>
</onException>
Defining multiple exceptions
You can define multiple exceptions in the clause as:
Code Block |
---|
onException(MyException.class, MyOtherException.class).
maximumRedeliveries(2);
|
And in Spring DSL you just add another exception element:
...
<onException>
<exception>com.mycompany.MyException</exception>
<exception>com.mycompany.MyOtherException</exception>
<redeliveryPolicy maximumRedeliveries="2"/>
</onException>
Reusing ReliveryPolicy
Available as of Camel 1.5.1 or later
You can reference a RedeliveryPolicy
so you can reuse existing configurations and use standard spring bean style configuration that supports property placeholders.
...
<bean id="myRedeliveryPolicy" class="org.apache.camel.processor.RedeliveryPolicy">
<property name="maximumRedeliveries" value="${myprop.max}"/>
</bean>
<onException>
<!-- you can define multiple exceptions just adding more exception elements as show below -->
<exception>com.mycompany.MyFirstException</exception>
<exception>com.mycompany.MySecondException</exception>
<!-- here we reference our redelivy policy defined above -->
<redeliveryPolicy ref="myRedeliveryPolicy"/>
</onException>
Then we have our service beans that is just plain POJO demonstrating how you can use Bean Integration in Camel to avoid being tied to the Camel API:
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/DeadLetterChannelHandledExampleTest.java} |
And finally the exception that is being thrown is just a regular exception:
Wiki Markup |
---|
{snippet:id=e3|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/DeadLetterChannelHandledExampleTest.java} |
So what happens?
If we sent an order that is being processed OK then the caller will receive an Exchange as reply containing Order OK
as the payload and orderid=123
in a header.
If the order could not be processed and thus an OrderFailedException was thrown the caller will not receive this exception (as opposed to in Camel 1.4, where the caller received the OrderFailedException) but our customized response that we have fabricated in the orderFailed
method in our OrderService
. So the caller receives an Exchange with the payload Order ERROR
and a orderid=failed
in a header.
Using handled with Spring DSL
The same route as above in Spring DSL:
Wiki Markup |
---|
{snippet:id=e1|lang=xml|url=activemq/camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/onexception/deadLetterChannelHandledExampleTest.xml} |
Handling and sending a fixed response back to the client
In the route above we handled the exception but routed it to a different endpoint. What if you need to alter the response and send a fixed response back to the original caller (the client). No secret here just do as you do in normal Camel routing, use transform to set the response, as shown in the sample below:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
We modify the sample slightly to return the original caused exception message instead of the fixed text Sorry:
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
And we can use the Simple language to set a readable error message with the caused excepetion message:
Wiki Markup |
---|
{snippet:id=e3|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionHandleAndTransformTest.java} |
Catching multiple exceptions
Available as of Camel 1.5
In Camel 1.5 the exception clauses has been renamed to onException and it also supports multiple exception classes:
Code Block |
---|
onException(MyBusinessException.class, MyOtherBusinessException.class)
.maximumRedeliveries(2)
.to("activemq:businessFailed");
|
And in Spring DSL you just add another exception element:
Code Block | ||||
---|---|---|---|---|
| ||||
<onException>
<exception>com.mycompany.MyBusinessException</exception>
<exception>com.mycompany.MyOtherBusinessException</exception>
<redeliveryPolicy maximumRedeliveries="2"/>
<to uri="activemq:businessFailed"/>
</onException>
|
Advanced Usage of Exception Clause
Using per route exception clauses
Using fine grained selection using onWhen predicate
Available as of Camel 1.5.1 or later
You can attach an Expression to the exception clause to have fine grained control when a clause should be selected or not. As it's an Expression you can use any kind of code to perform the test. Here is a sample:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategyUsingWhenTest.java} |
In the sample above we have two onException's defined. The first has an onWhen expression attached to only trigger if the message has a header with the key user that is not null. If so this clause is selected and is handling the thrown exception. The 2nd clause is a for coarse gained selection to select the same exception being thrown but when the expression is evaluated to false. Notice: this is not required, if the 2nd clause is omitted, then the default error handler will kick in.
Using fine grained retry using retryUntil predicate
Available as of Camel 2.0
When you need fine grained control for determining if an exchange should be retried or not you can use the retryUntil predicate. Camel will redeliver until the predicate returns false.
This is demonstrated in the sample below:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionRetryUntilTest.java} |
Where the bean myRetryHandler is computing if we should retry or not:
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionRetryUntilTest.java} |
Using custom ExceptionPolicyStrategy
- Available in Camel 1.4*
The default ExceptionPolicyStrategy in Camel should be sufficient in nearly all use-cases (see section How does Camel select which clause should handle a given thrown Exception). However if you need to use your own this can be configued as the sample below illustrates
Using fine grained selection using onWhen predicate
Available as of Camel 1.5.1 or later
You can attach an Expression to the exception clause to have fine grained control when a clause should be selected or not. As it's an Expression you can use any kind of code to perform the test. Here is a sample:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategyUsingWhenTest.java} |
In the sample above we have two onException's defined. The first has an onWhen expression attached to only trigger if the message has a header with the key user that is not null. If so this clause is selected and is handling the thrown exception. The 2nd clause is a for coarse gained selection to select the same exception being thrown but when the expression is evaluated to false. Notice: this is not required, if the 2nd clause is omitted, then the default error handler will kick in.
Using fine grained retry using retryUntil predicate
Available as of Camel 2.0
...
- :
Wiki Markup {snippet:id=e1|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/
...
exceptionpolicy/
...
CustomExceptionPolicyStrategyTest.java}
Where the bean myRetryHandler is computing if we should retry or not:Using our own strategy MyPolicy we can change the default behavior of Camel with our own code to resolve which ExceptionType from above should be handling the given thrown exception.
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=activemq/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexceptionexceptionpolicy/OnExceptionRetryUntilTestCustomExceptionPolicyStrategyTest.java} |
See also
The Error Handler for the general error handling documentation
The Dead Letter Channel for further details.
The Transactional Client for transactional behavior