The onException
clause is a powerful mechanism for trapping exceptions
that occur in one or more routes: it is type-specific, enabling you to define distinct
actions to handle different exception types; it allows you to define actions using
essentially the same (actually, slightly extended) syntax as a route, giving you
considerable flexibility in the way you handle exceptions; and it is based on a trapping
model, which enables a single onException
clause to deal with exceptions
occurring at any node in any route.
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
Fuse Mediation Router runtime implicitly encloses each route node in a try block. This is why the
onException
clause is able to trap exceptions at any point in the route.
But this wrapping is done for you automatically; it is not visible in the route
definitions.
In the following Java DSL example, the onException
clause applies to all
of the routes defined in the RouteBuilder
class. If a
ValidationException
exception occurs while processing either of the routes
(from("seda:inputA")
or from("seda:inputB")
), the
onException
clause traps the exception and redirects the current exchange
to the validationFailed
JMS queue (which serves as a deadletter
queue).
// Java public class MyRouteBuilder extends RouteBuilder { public void configure() { onException(ValidationException.class) .to("activemq:validationFailed"); from("seda:inputA") .to("validation:foo/bar.xsd", "activemq:someQueue"); from("seda:inputB").to("direct:foo") .to("rnc:mySchema.rnc", "activemq:anotherQueue"); } }
The preceding example can also be expressed in the XML DSL, using the
onException
element to define the exception clause, as follows:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <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> </beans>
You can define multiple onException
clauses to trap exceptions in a
RouteBuilder
scope. This enables you to take different actions in response
to different exceptions. For example, the following series of onException
clauses defined in the Java DSL define different deadletter destinations for
ValidationException
, ValidationException
, and
Exception
:
onException(ValidationException.class).to("activemq:validationFailed"); onException(java.io.IOException.class).to("activemq:ioExceptions"); onException(Exception.class).to("activemq:exceptions");
You can define the same series of onException
clauses in the XML DSL as
follows:
<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>
You can also group multiple exceptions together to be trapped by the same
onException
clause. In the Java DSL, you can group multiple exceptions as
follows:
onException(ValidationException.class, BuesinessException.class) .to("activemq:validationFailed");
In the XML DSL, you can group multiple exceptions together by defining more than one
exception
element inside the onException
element, as
follows:
<onException> <exception>com.mycompany.ValidationException</exception> <exception>com.mycompany.BuesinessException</exception> <to uri="activemq:validationFailed"/> </onException>
When trapping multiple exceptions, the order of the onException
clauses
is significant. Fuse Mediation Router initially attempts to match the thrown exception against the
first clause. If the first clause fails to match, the next
onException
clause is tried, and so on until a match is found. Each
matching attempt is governed by the following algorithm:
If the thrown exception is a chained exception (that is, where an exception has been caught and rethrown as a different exception), the most nested exception type serves initially as the basis for matching. This exception is tested as follows:
If the exception-to-test has exactly the type specified in the
onException
clause (tested usinginstanceof
), a match is triggered.If the exception-to-test is a sub-type of the type specified in the
onException
clause, a match is triggered.
If the most nested exception fails to yield a match, the next exception in the chain (the wrapping exception) is tested instead. The testing continues up the chain until either a match is triggered or the chain is exhausted.
The basic examples of onException
usage have so far all exploited the
deadletter channel pattern. That is, when an
onException
clause traps an exception, the current exchange is routed to a
special destination (the 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.
For more details about the deadletter channel pattern, see Dead Letter Channel.
By the time an exception is raised in the middle of a route, the message in the exchange could have been modified considerably (and might not even by readable by a human). Often, it is easier for an administrator to decide what corrective actions to take, if the messages visible in the deadletter queue are the original messages, as received at the start of the route.
In the Java DSL, you can replace the message in the exchange by the original message,
using the useOriginalMessage()
DSL command, as follows:
onException(ValidationException.class) .useOriginalMessage() .to("activemq:validationFailed");
In the XML DSL, you can retrieve the original message by setting the
useOriginalMessage
attribute on the onException
element, as
follows:
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
Instead of interrupting the processing of a message and giving up as soon as an exception is raised, Fuse Mediation Router gives you the option of attempting to redeliver the message at the point where the exception occurred. In networked systems, where timeouts can occur and temporary faults arise, it is often possible for failed messages to be processed successfully, if they are redelivered shortly after the original exception was raised.
The Fuse Mediation Router redelivery supports various strategies for redelivering messages after an exception occurs. Some of the most important options for configuring redelivery are as follows:
maximumRedeliveries()
Specifies the maximum number of times redelivery can be attempted (default is
0
). A negative value means redelivery is always attempted (equivalent to an infinite value).retryWhile()
Specifies a predicate (of
Predicate
type), which determines whether Fuse Mediation Router ought to continue redelivering. If the predicate evaluates totrue
on the current exchange, redelivery is attempted; otherwise, redelivery is stopped and no further redelivery attempts are made.This option takes precedence over the
maximumRedeliveries()
option.
In the Java DSL, redelivery policy options are specified using DSL commands in the
onException
clause. For example, you can specify a maximum of six
redeliveries, after which the exchange is sent to the validationFailed
deadletter queue, as follows:
onException(ValidationException.class) .maximumRedeliveries(6) .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN) .to("activemq:validationFailed");
In the XML DSL, redelivery policy options are specified by setting attributes on the
redeliveryPolicy
element. For example, the preceding route can be expressed
in XML DSL as follows:
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <redeliveryPolicy maximumRedeliveries="6"/> <to uri="activemq:validationFailed"/> </onException>
The latter part of the route—after the redelivery options are set—is not processed until after the last redelivery attempt has failed. For detailed descriptions of all the redelivery options, see Dead Letter Channel.
Alternatively, you can specify redelivery policy options in a
redeliveryPolicyProfile
instance. You can then reference the
redeliveryPolicyProfile
instance using the onException
element's redeliverPolicyRef
attribute. For example, the preceding route can
be expressed as follows:
<redeliveryPolicyProfile id="redelivPolicy" maximumRedeliveries="6" retryAttemptedLogLevel="WARN"/> <onException useOriginalMessage="true" redeliveryPolicyRef="redelivPolicy"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
![]() | Tip |
---|---|
The approach using |
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.
For example, in the following Java DSL fragment,the first onException
clause triggers, only if the thrown exception matches MyUserException
and the
user
header is non-null in the current exchange:
// Java // Here we define onException() to catch MyUserException when // there is a header[user] on the exchange that is not null onException(MyUserException.class) .onWhen(header("user").isNotNull()) .maximumRedeliveries(2) .to(ERROR_USER_QUEUE); // Here we define onException to catch MyUserException as a kind // of fallback when the above did not match. // Noitce: The order how we have defined these onException is // important as Camel will resolve in the same order as they // have been defined onException(MyUserException.class) .maximumRedeliveries(2) .to(ERROR_QUEUE);
The preceding onException
clauses can be expressed in the XML DSL as
follows:
<redeliveryPolicyProfile id="twoRedeliveries" maximumRedeliveries="2"/> <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>
By default, when an exception is raised 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. When an onException
clause is
triggered, the behavior is essentially the same, except that the onException
clause performs some processing before the thrown exception is propagated back.
But this default behavior is not the only way to handle an
exception. The onException
provides various options to modify the exception
handling behavior, as follows:
Suppressing exception rethrow—you have the option of suppressing the rethrown exception after the
onException
clause has completed. In other words, in this case the exception does not propagate back to the consumer endpoint at the start of the route.Continuing processing—you have the option of resuming normal processing of the exchange from the point where the exception originally occurred. Implicitly, this approach also suppresses the rethrown exception.
Sending a response—in the special case where the consumer endpoint at the start of the route expects a reply (that is, having an InOut MEP), you might prefer to construct a custom fault reply message, rather than propagating the exception back to the consumer endpoint.
To prevent the current exception from being rethrown and propagated back to the
consumer endpoint, you can set the handled()
option to true
in
the Java DSL, as follows:
onException(ValidationException.class) .handled(true) .to("activemq:validationFailed");
In the Java DSL, the argument to the handled()
option can be of boolean
type, of Predicate
type, or of Expression
type (where any
non-boolean expression is interpreted as true
, if it evaluates to a non-null
value).
The same route can be configured to suppress the rethrown exception in the XML DSL,
using the handled
element, as follows:
<onException> <exception>com.mycompany.ValidationException</exception> <handled> <constant>true</constant> </handled> <to uri="activemq:validationFailed"/> </onException>
To continue processing the current message from the point in the route where the
exception was originally thrown, you can set the continued
option to
true
in the Java DSL, as follows:
onException(ValidationException.class) .continued(true);
In the Java DSL, the argument to the continued()
option can be of boolean
type, of Predicate
type, or of Expression
type (where any
non-boolean expression is interpreted as true
, if it evaluates to a non-null
value).
The same route can be configured in the XML DSL, using the continued
element, as follows:
<onException> <exception>com.mycompany.ValidationException</exception> <continued> <constant>true</constant> </continued> </onException>
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.
For example, the following Java DSL fragment shows how to send a reply message
containing the text string, Sorry
, whenever the
MyFunctionalException
exception occurs:
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body as Sorry. onException(MyFunctionalException.class) .handled(true) .transform().constant("Sorry");
If you are sending a fault response to the client, you will often want to incorporate
the text of the exception message in the response. You can access the text of the current
exception message using the exceptionMessage()
builder method. For example,
you can send a reply containing just the text of the exception message whenever the
MyFunctionalException
exception occurs, as follows:
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return the exception message onException(MyFunctionalException.class) .handled(true) .transform(exceptionMessage());
The exception message text is also accessible from the Simple language, through the
exception.message
variable. For example, you could embed the current
exception text in a reply message, as follows:
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return a nice message // using the simple language where we want insert the exception message onException(MyFunctionalException.class) .handled(true) .transform().simple("Error reported: ${exception.message} - cannot process this message.");
The preceding onException
clause can be expressed in XML DSL as
follows:
<onException> <exception>com.mycompany.MyFunctionalException</exception> <handled> <constant>true</constant> </handled> <transform> <simple>Error reported: ${exception.message} - cannot process this message.</simple> </transform> </onException>
An exception that gets thrown while handling an existing exception (in other words,
one that gets thrown in the middle of processing an onException
clause) is
handled in a special way. Such an exception is handled by the special fallback exception
handler, which handles the exception as follows:
All existing exception handlers are ignored and processing fails immediately.
The new exception is logged.
The new exception is set on the exchange object.
The simple strategy avoids complex failure scenarios which could otherwise end up with
an onException
clause getting locked into an infinite loop.
The onException
clauses can be effective in either of the following
scopes:
RouteBuilder scope—
onException
clauses defined as standalone statements inside aRouteBuilder.configure()
method affect all of the routes defined in thatRouteBuilder
instance. On the other hand, theseonException
clauses have no effect whatsoever on routes defined inside any otherRouteBuilder
instance. TheonException
clauses must appear before the route definitions.All of the examples up to this point are defined using the
RouteBuilder
scope.Route scope—
onException
clauses can also be embedded directly within a route. These onException clauses affect only the route in which they are defined.
You can embed an onException
clause anywhere inside a route definition,
but you must terminate the embedded onException
clause using the
end()
DSL command.
For example, you can define an embedded onException
clause in the Java
DSL, as follows:
// Java from("direct:start") .onException(OrderFailedException.class) .maximumRedeliveries(1) .handled(true) .beanRef("orderService", "orderFailed") .to("mock:error") .end() .beanRef("orderService", "handleOrder") .to("mock:result");
You can define an embedded onException
clause in the XML DSL, as
follows:
<route errorHandlerRef="deadLetter"> <from uri="direct:start"/> <onException> <exception>com.mycompany.OrderFailedException</exception> <redeliveryPolicy maximumRedeliveries="1"/> <handled> <constant>true</constant> </handled> <bean ref="orderService" method="orderFailed"/> <to uri="mock:error"/> </onException> <bean ref="orderService" method="handleOrder"/> <to uri="mock:result"/> </route>