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 Apache Camel
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.
Apache Camel 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
onExceptionclause (tested usinginstanceof), a match is triggered.If the exception-to-test is a sub-type of the type specified in the
onExceptionclause, 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, Apache Camel 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 Apache Camel 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
Predicatetype), which determines whether Apache Camel ought to continue redelivering. If the predicate evaluates totrueon 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>![]() | Note |
|---|---|
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
onExceptionclause 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—
onExceptionclauses defined as standalone statements inside aRouteBuilder.configure()method affect all of the routes defined in thatRouteBuilderinstance. On the other hand, theseonExceptionclauses have no effect whatsoever on routes defined inside any otherRouteBuilderinstance. TheonExceptionclauses must appear before the route definitions.All of the examples up to this point are defined using the
RouteBuilderscope.Route scope—
onExceptionclauses 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>





![[Note]](imagesdb/note.gif)


