PK
ZsAoa, mimetypeapplication/epub+zipPK
ZsA META-INF/PK
ZsA OEBPS/PK
ZsA
OEBPS/images/PK
ZsA OEBPS/imagesdb/PK
ZsAh h META-INF/container.xml
The aspect oriented programming (AOP) feature in Apache Camel enables you to apply before and after processing to a specified portion of a route. As a matter of fact, AOP does not provide anything that you could not do with the regular route syntax. The advantage of the AOP syntax, however, is that it enables you to specify before and after processing at a single point in the route. In some cases, this gives a more readable syntax. The typical use case for AOP is the application of a symmetrical pair of operations before and after a route fragment is processed. For example, typical pairs of operations that you might want to apply using AOP are: encrypt and decrypt; begin transaction and commit transaction; allocate resources and deallocate resources; and so on.
In Java DSL, the route fragment to which you apply before and after processing is
bracketed between aop() and end(). For example, the following
route performs AOP processing around the route fragment that calls the bean methods:
from("jms:queue:inbox")
.aop().around("log:before", "log:after")
.to("bean:order?method=validate")
.to("bean:order?method=handle")
.end()
.to("jms:queue:order");Where the around() subclause specifies an endpoint,
log:before, where the exchange is routed before processing
the route fragment and an endpoint, log:after, where the exchange is routed
after processing the route fragment.
Starting an AOP block with aop().around() is probably the most common use
case, but the AOP block supports other subclauses, as follows:
around()—specifies before and
after endpoints.
begin()—specifies before endpoint
only.
after()—specifies after endpoint
only.
aroundFinally()—specifies a before endpoint,
and an after endpoint that is always called, even when an exception
occurs in the enclosed route fragment.
afterFinally()—specifies an after endpoint
that is always called, even when an exception occurs in the enclosed route
fragment.
In the XML DSL, the route fragment to which you apply before and
after processing is enclosed in the aop element. For
example, the following Spring XML route performs AOP processing around the route fragment
that calls the bean methods:
<route>
<from uri="jms:queue:inbox"/>
<aop beforeUri="log:before" afterUri="log:after">
<to uri="bean:order?method=validate"/>
<to uri="bean:order?method=handle"/>
</aop>
<to uri="jms:queue:order"/>
</route>Where the beforeUri attribute specifies the endpoint where the exchange is
routed before processing the route fragment, and the
afterUri attribute specifies the endpoint where the exchange is routed
after processing the route fragment.
The aop element supports the following optional attributes:
beforeUri
afterUri
afterFinallyUri
The various use cases described for the Java DSL can be obtained in Spring XML using the
appropriate combinations of these attributes. For example, the aroundFinally()
Java DSL subclause is equivalent to the combination of beforeUri and
afterFinallyUri in Spring XML.
Bean integration provides a general purpose mechanism for processing messages using arbitrary Java objects. By inserting a bean reference into a route, you can call an arbitrary method on a Java object, which can then access and modify the incoming exchange. The mechanism that maps an exchange's contents to the parameters and return values of a bean method is known as parameter binding. Parameter binding can use any combination of the following approaches in order to initialize a method's parameters:
Conventional method signatures — If the method signature conforms to certain conventions, the parameter binding can use Java reflection to determine what parameters to pass.
Annotations and dependency injection — For a more flexible binding mechanism, employ Java annotations to specify what to inject into the method's arguments. This dependency injection mechanism relies on Spring 2.5 component scanning. Normally, if you are deploying your Fuse Mediation Router application into a Spring container, the dependency injection mechanism will work automatically.
Explicitly specified parameters — You can specify parameters explicitly (either as constants or using the Simple language), at the point where the bean is invoked.
To process exchange objects using a Java bean (which is a plain old Java object or
POJO), use the bean() processor, which binds the inbound exchange to a method
on the Java object. For example, to process inbound exchanges using the class,
MyBeanProcessor, define a route like the following:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody")
.to("file:data/outbound");Where the bean() processor creates an instance of
MyBeanProcessor type and invokes the processBody() method to
process inbound exchanges. This approach is adequate if you only want to access the
MyBeanProcessor instance from a single route. However, if you want to access
the same MyBeanProcessor instance from multiple routes, use the variant of
bean() that takes the Object type as its first argument. For
example:
MyBeanProcessor myBean = new MyBeanProcessor();
from("file:data/inbound")
.bean(myBean, "processBody")
.to("file:data/outbound");
from("activemq:inboundData")
.bean(myBean, "processBody")
.to("activemq:outboundData");If a bean defines overloaded methods, you can choose which of the overloaded methods to
invoke by specifying the method name along with its parameter types. For example, if the
MyBeanBrocessor class has two overloaded methods,
processBody(String) and processBody(String,String), you can
invoke the latter overloaded method as follows:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody(String,String)")
.to("file:data/outbound");Alternatively, if you want to identify a method by the number of parameters it takes,
rather than specifying the type of each parameter explicitly, you can use the wildcard
character, *. For example, to invoke a method named processBody
that takes two parameters, irrespective of the exact type of the parameters, invoke the
bean() processor as follows:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody(*,*)")
.to("file:data/outbound");When specifying the method, you can use either a simple unqualified type name—for
example, processBody(Exchange)—or a fully qualified type name—for example,
processBody(org.apache.camel.Exchange).
![]() | Note |
|---|---|
In the current implementation, the specified type name must be an exact match of the parameter type. Type inheritance is not taken into account. |
You can specify parameter values explicitly, when you call the bean method. The following simple type values can be passed:
Boolean: true or false.
Numeric: 123, 7, and so on.
String: 'In single quotes' or "In double quotes".
Null object: null.
The following example shows how you can mix explicit parameter values with type specifiers in the same method invocation:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBody(String, 'Sample string value', true, 7)")
.to("file:data/outbound");In the preceding example, the value of the first parameter would presumably be determined by a parameter binding annotation (see Basic annotations).
In addition to the simple type values, you can also specify parameter values using the
Simple language (The Simple Language in Routing Expression and Predicate Languages). This means that the
full power of the Simple language is available when specifying
parameter values. For example, to pass the message body and the value of the
title header to a bean method:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBodyAndHeader(${body},${header.title})")
.to("file:data/outbound");You can also pass the entire header hash map as a parameter. For example, in the
following example, the second method parameter must be declared to be of type
java.util.Map:
from("file:data/inbound")
.bean(MyBeanProcessor.class, "processBodyAndAllHeaders(${body},${header})")
.to("file:data/outbound");To bind exchanges to a bean method, you can define a method signature that conforms to certain conventions. In particular, there are two basic conventions for method signatures:
If you want to implement a bean method that accesses or modifies the incoming message
body, you must define a method signature that takes a single String argument
and returns a String value. For example:
// Java
package com.acme;
public class MyBeanProcessor {
public String processBody(String body) {
// Do whatever you like to 'body'...
return newBody;
}
}For greater flexibility, you can implement a bean method that accesses the incoming
exchange. This enables you to access or modify all headers, bodies, and exchange properties.
For processing exchanges, the method signature takes a single
org.apache.camel.Exchange parameter and returns void. For
example:
// Java
package com.acme;
public class MyBeanProcessor {
public void processExchange(Exchange exchange) {
// Do whatever you like to 'exchange'...
exchange.getIn().setBody("Here is a new message body!");
}
}Instead of creating a bean instance in Java, you can create an instance using Spring
XML. In fact, this is the only feasible approach if you are defining your routes in XML. To
define a bean in XML, use the standard Spring bean element. The following
example shows how to create an instance of MyBeanProcessor:
<beans ...>
...
<bean id="myBeanId" class="com.acme.MyBeanProcessor"/>
</beans>It is also possible to pass data to the bean's constructor arguments using Spring
syntax. For full details of how to use the Spring bean element, see The IoC
Container from the Spring reference guide.
When you create an object instance using the bean element, you can
reference it later using the bean's ID (the value of the bean element's
id attribute). For example, given the bean element with ID equal
to myBeanId, you can reference the bean in a Java DSL route using the
beanRef() processor, as follows:
from("file:data/inbound").beanRef("myBeanId", "processBody").to("file:data/outbound");Where the beanRef() processor invokes the
MyBeanProcessor.processBody() method on the specified bean instance. You can
also invoke the bean from within a Spring XML route, using the Camel schema's
bean element. For example:
<camelContext id="CamelContextID" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:data/inbound"/>
<bean ref="myBeanId" method="processBody"/>
<to uri="file:data/outbound"/>
</route>
</camelContext>The basic parameter bindings described in Basic method signatures might not always be convenient to use. For example, if you have a legacy Java class that performs some data manipulation, you might want to extract data from an inbound exchange and map it to the arguments of an existing method signature. For this kind of parameter binding, Fuse Mediation Router provides the following kinds of Java annotation:
Table 2.2 shows the annotations from
the org.apache.camel Java package that you can use to inject message data into
the arguments of a bean method.
Table 2.2. Basic Bean Annotations
| Annotation | Meaning | Parameter? |
|---|---|---|
@Attachments | Binds to a list of attachments. | |
@Body | Binds to an inbound message body. | |
@Header | Binds to an inbound message header. | String name of the header. |
@Headers | Binds to a java.util.Map of the inbound message headers. | |
@OutHeaders | Binds to a java.util.Map of the outbound message headers. | |
@Property | Binds to a named exchange property. | String name of the property. |
@Properties | Binds to a java.util.Map of the exchange properties. |
For example, the following class shows you how to use basic annotations to inject
message data into the processExchange() method arguments.
// Java
import org.apache.camel.*;
public class MyBeanProcessor {
public void processExchange(
@Header(name="user") String user,
@Body String body,
Exchange exchange
) {
// Do whatever you like to 'exchange'...
exchange.getIn().setBody(body + "UserName = " + user);
}
}Notice how you are able to mix the annotations with the default conventions. As well as
injecting the annotated arguments, the parameter binding also automatically injects the
exchange object into the org.apache.camel.Exchange argument.
The expression language annotations provide a powerful mechanism for injecting message
data into a bean method's arguments. Using these annotations, you can invoke an arbitrary
script, written in the scripting language of your choice, to extract data from an inbound
exchange and inject the data into a method argument. Table 2.3 shows the annotations from the
org.apache.camel.language package (and sub-packages, for the non-core
annotations) that you can use to inject message data into the arguments of a bean
method.
Table 2.3. Expression Language Annotations
| Annotation | Description |
|---|---|
@Bean | Injects a Bean expression. |
@Constant | Injects a Constant expression |
@EL | Injects an EL expression. |
@Groovy | Injects a Groovy expression. |
@Header | Injects a Header expression. |
@JavaScript | Injects a JavaScript expression. |
@OGNL | Injects an OGNL expression. |
@PHP | Injects a PHP expression. |
@Python | Injects a Python expression. |
@Ruby | Injects a Ruby expression. |
@Simple | Injects a Simple expression. |
@XPath | Injects an XPath expression. |
@XQuery | Injects an XQuery expression. |
For example, the following class shows you how to use the @XPath annotation
to extract a username and a password from the body of an incoming message in XML
format:
// Java
import org.apache.camel.language.*;
public class MyBeanProcessor {
public void checkCredentials(
@XPath("/credentials/username/text()") String user,
@XPath("/credentials/password/text()") String pass
) {
// Check the user/pass credentials...
...
}
}The @Bean annotation is a special case, because it enables you to inject
the result of invoking a registered bean. For example, to inject a correlation ID into a
method argument, you can use the @Bean annotation to invoke an ID generator
class, as follows:
// Java
import org.apache.camel.language.*;
public class MyBeanProcessor {
public void processCorrelatedMsg(
@Bean("myCorrIdGenerator") String corrId,
@Body String body
) {
// Check the user/pass credentials...
...
}
}Where the string, myCorrIdGenerator, is the bean ID of the ID generator
instance. The ID generator class can be instantiated using the spring bean
element, as follows:
<beans ...>
...
<bean id="myCorrIdGenerator" class="com.acme.MyIdGenerator"/>
</beans>Where the MySimpleIdGenerator class could be defined as follows:
// Java
package com.acme;
public class MyIdGenerator {
private UserManager userManager;
public String generate(
@Header(name = "user") String user,
@Body String payload
) throws Exception {
User user = userManager.lookupUser(user);
String userId = user.getPrimaryId();
String id = userId + generateHashCodeForPayload(payload);
return id;
}
}Notice that you can also use annotations in the referenced bean class,
MyIdGenerator. The only restriction on the generate() method
signature is that it must return the correct type to inject into the argument annotated by
@Bean. Because the @Bean annotation does not let you specify a
method name, the injection mechanism simply invokes the first method in the referenced bean
that has the matching return type.
![]() | Note |
|---|---|
Some of the language annotations are available in the core component
( |
Parameter binding annotations can be inherited from an interface or from a superclass.
For example, if you define a Java interface with a Header annotation and a
Body annotation, as follows:
// Java
import org.apache.camel.*;
public interface MyBeanProcessorIntf {
void processExchange(
@Header(name="user") String user,
@Body String body,
Exchange exchange
);
}The overloaded methods defined in the implementation class,
MyBeanProcessor, now inherit the annotations defined in the base interface, as
follows:
// Java
import org.apache.camel.*;
public class MyBeanProcessor implements MyBeanProcessorIntf {
public void processExchange(
String user, // Inherits Header annotation
String body, // Inherits Body annotation
Exchange exchange
) {
...
}
}The class that implements a Java interface is often protected,
private or in package-only scope. If you try to invoke a method
on an implementation class that is restricted in this way, the bean binding falls back to
invoking the corresponding interface method, which is publicly accessible.
For example, consider the following public BeanIntf interface:
// Java
public interface BeanIntf {
void processBodyAndHeader(String body, String title);
}Where the BeanIntf interface is implemented by the following protected
BeanIntfImpl class:
// Java
protected class BeanIntfImpl implements BeanIntf {
void processBodyAndHeader(String body, String title) {
...
}
}The following bean invocation would fall back to invoking the public
BeanIntf.processBodyAndHeader method:
from("file:data/inbound")
.bean(BeanIntfImpl.class, "processBodyAndHeader(${body}, ${header.title})")
.to("file:data/outbound");Bean integration has the capability to invoke static methods
without creating an instance of the associated class. For example,
consider the following Java class that defines the static method,
changeSomething():
// Java
...
public final class MyStaticClass {
private MyStaticClass() {
}
public static String changeSomething(String s) {
if ("Hello World".equals(s)) {
return "Bye World";
}
return null;
}
public void doSomething() {
// noop
}
}You can use bean integration to invoke the static changeSomething method,
as follows:
from("direct:a")
.bean(MyStaticClass.class, "changeSomething")
.to("mock:a");Note that, although this syntax looks identical to the invocation of an ordinary
function, bean integration exploits Java reflection to identify the method as static and
proceeds to invoke the method without instantiating
MyStaticClass.
In the special case where a route is deployed into the Fuse ESB container, it is possible
to invoke an OSGi service directly using bean integration. For example, assuming that one of
the bundles in the OSGi container has exported the service,
org.fusesource.example.HelloWorldOsgiService, you could invoke the
sayHello method using the following bean integration code:
from("file:data/inbound")
.bean(org.fusesource.example.HelloWorldOsgiService.class, "sayHello")
.to("file:data/outbound");You could also invoke the OSGi service from within a Spring or blueprint XML file, using the bean component, as follows:
<to uri="bean:org.fusesource.example.HelloWorldOsgiService?method=sayHello"/>
The way this works is that Fuse Mediation Router sets up a chain of registries when it is deployed in the OSGi container. First of all, it looks up the specified class name in the OSGi service registry; if this lookup fails, it then falls back to the local Spring DM or blueprint registry.
The errorHandler() clause provides similar features to the
onException clause, except that this mechanism is not
able to discriminate between different exception types. The errorHandler()
clause is the original exception handling mechanism provided by Fuse Mediation Router and was available
before the onException clause was implemented.
The errorHandler() clause is defined in a RouteBuilder class
and applies to all of the routes in that RouteBuilder class. It is triggered
whenever an exception of any kind occurs in one of the applicable
routes. For example, to define an error handler that routes all failed exchanges to the
ActiveMQ deadLetter queue, you can define a RouteBuilder as
follows:
public class MyRouteBuilder extends RouteBuilder {
public void configure() {
errorHandler(deadLetterChannel("activemq:deadLetter"));
// The preceding error handler applies
// to all of the following routes:
from("activemq:orderQueue")
.to("pop3://fulfillment@acme.com");
from("file:src/data?noop=true")
.to("file:target/messages");
// ...
}
}Redirection to the dead letter channel will not occur, however, until all attempts at redelivery have been exhausted.
In the XML DSL, you define an error handler within a camelContext scope
using the errorHandler element. For example, to define an error handler that
routes all failed exchanges to the ActiveMQ deadLetter queue, you can define
an errorHandler element 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">
<errorHandler type="DeadLetterChannel"
deadLetterUri="activemq:deadLetter"/>
<route>
<from uri="activemq:orderQueue"/>
<to uri="pop3://fulfillment@acme.com"/>
</route>
<route>
<from uri="file:src/data?noop=true"/>
<to uri="file:target/messages"/>
</route>
</camelContext>
</beans>Table 2.1 provides an overview of the different types of error handler you can define.
Table 2.1. Error Handler Types
| Java DSL Builder | XML DSL Type Attribute | Description |
|---|---|---|
defaultErrorHandler() | DefaultErrorHandler | Propagates exceptions back to the caller and supports the redelivery policy, but it does not support a dead letter queue. |
deadLetterChannel() | DeadLetterChannel | Supports the same features as the default error handler and, in addition, supports a dead letter queue. |
loggingErrorChannel() | LoggingErrorChannel | Logs the exception text whenever an exception occurs. |
noErrorHandler() | NoErrorHandler | Dummy handler implementation that can be used to disable the error handler. |
TransactionErrorHandler | An error handler for transacted routes. A default transaction error handler instance is automatically used for a route that is marked as transacted. |
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 using instanceof), 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 to
true 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 a RouteBuilder.configure() method
affect all of the routes defined in that RouteBuilder instance. On the
other hand, these onException clauses have no effect
whatsoever on routes defined inside any other RouteBuilder
instance. The onException 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>The Camel CXF component provides an integration with Apache CXF, enabling you to send and receive SOAP messages from Apache Camel endpoints. You can easily define Apache Camel endpoints in XML, which can then be referenced in a route using the endpoint's bean ID. For more details, see CXF in Apache Camel Documentation.
It is possible to configure a CXF endpoint so that, when a Java exception is thrown on
the server side, the stack trace for the exception is marshalled into a fault message and
returned to the client. To enable this feaure, set the dataFormat to
PAYLOAD and set the faultStackTraceEnabled property to
true in the cxfEndpoint element, as follows:
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
wsdlURL="ship.wsdl"
endpointName="s:TestSoapEndpoint"
serviceName="s:TestService"
xmlns:s="http://test">
<cxf:properties>
<!-- enable sending the stack trace back to client; the default value is false-->
<entry key="faultStackTraceEnabled" value="true" />
<entry key="dataFormat" value="PAYLOAD" />
</cxf:properties>
</cxf:cxfEndpoint>For security reasons, the stack trace does not include the causing exception (that is,
the part of a stack trace that follows Caused by). If you want to include the
causing exception in the stack trace, set the exceptionMessageCauseEnabled
property to true in the cxfEndpoint element, as follows:
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
wsdlURL="ship.wsdl"
endpointName="s:TestSoapEndpoint"
serviceName="s:TestService"
xmlns:s="http://test">
<cxf:properties>
<!-- enable to show the cause exception message and the default value is false -->
<entry key="exceptionMessageCauseEnabled" value="true" />
<!-- enable to send the stack trace back to client, the default value is false-->
<entry key="faultStackTraceEnabled" value="true" />
<entry key="dataFormat" value="PAYLOAD" />
</cxf:properties>
</cxf:cxfEndpoint>![]() | Warning |
|---|---|
You should only enable the |
To handle exceptions within a route, you can use a combination of the
doTry, doCatch, and doFinally clauses, which
handle exceptions in a similar way to Java's try, catch, and
finally blocks.
In general, the doCatch() clause in a route definition behaves in an
analogous way to the catch() statement in Java code. In particular, the
following features are supported by the doCatch() clause:
Multiple doCatch clauses—you can have multiple
doCatch clauses within a single doTry block. The
doCatch clauses are tested in the order they appear, just like Java
catch() statements. Apache Camel executes the first doCatch
clause that matches the thrown exception.
![]() | Note |
|---|---|
This algorithm is different from the exception matching algorithm used by the
|
Rethrowing exceptions—you can rethrow the current
exception from within a doCatch clause using the handled
sub-clause (see Rethrowing exceptions in doCatch).
There are some special features of the doCatch() clause, however, that
have no analogue in the Java catch() statement. The following features are
specific to doCatch():
Catching multiple exceptions—the doCatch
clause allows you to specify a list of exceptions to catch, in contrast to the Java
catch() statement, which catches only one exception (see Example).
Conditional catching—you can catch an exception
conditionally, by appending an onWhen sub-clause to the
doCatch clause (see Conditional exception catching using onWhen).
The following example shows how to write a doTry block in the Java DSL,
where the doCatch() clause will be executed, if either the
IOException exception or the IllegalStateException exception
are raised, and the doFinally() clause is always
executed, irrespective of whether an exception is raised or not.
from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class, IllegalStateException.class)
.to("mock:catch")
.doFinally()
.to("mock:finally")
.end();Or equivalently, in Spring XML:
<route>
<from uri="direct:start"/>
<!-- here the try starts. its a try .. catch .. finally just as regular java code -->
<doTry>
<process ref="processorFail"/>
<to uri="mock:result"/>
<doCatch>
<!-- catch multiple exceptions -->
<exception>java.io.IOException</exception>
<exception>java.lang.IllegalStateException</exception>
<to uri="mock:catch"/>
</doCatch>
<doFinally>
<to uri="mock:finally"/>
</doFinally>
</doTry>
</route>It is possible to rethrow an exception in a doCatch() clause by calling
the handled() sub-clause with its argument set to false, as
follows:
from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class)
// mark this as NOT handled, eg the caller will also get the exception
.handled(false)
.to("mock:io")
.doCatch(Exception.class)
// and catch all other exceptions
.to("mock:error")
.end();In the preceding example, if the IOException is caught by
doCatch(), the current exchange is sent to the mock:io
endpoint, and then the IOException is rethrown. This gives the consumer
endpoint at the start of the route (in the from() command) an opportunity to
handle the exception as well.
The following example shows how to define the same route in Spring XML:
<route>
<from uri="direct:start"/>
<doTry>
<process ref="processorFail"/>
<to uri="mock:result"/>
<doCatch>
<exception>java.io.IOException</exception>
<!-- mark this as NOT handled, eg the caller will also get the exception -->
<handled>
<constant>false</constant>
</handled>
<to uri="mock:io"/>
</doCatch>
<doCatch>
<!-- and catch all other exceptions they are handled by default (ie handled = true) -->
<exception>java.lang.Exception</exception>
<to uri="mock:error"/>
</doCatch>
</doTry>
</route>A special feature of the Apache Camel
doCatch() clause is that you can conditionalize the catching of exceptions
based on an expression that is evaluated at run time. In other words, if you catch an
exception using a clause of the form,
doCatch(,
an exception will only be caught, if the predicate expression,
ExceptionList).doWhen(Expression)Expression, evaluates to true at run
time.
For example, the following doTry block will catch the exceptions,
IOException and IllegalStateException, only if the exception
message contains the word, Severe:
from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class, IllegalStateException.class)
.onWhen(exceptionMessage().contains("Severe"))
.to("mock:catch")
.doCatch(CamelExchangeException.class)
.to("mock:catchCamel")
.doFinally()
.to("mock:finally")
.end();Or equivalently, in Spring XML:
<route>
<from uri="direct:start"/>
<doTry>
<process ref="processorFail"/>
<to uri="mock:result"/>
<doCatch>
<exception>java.io.IOException</exception>
<exception>java.lang.IllegalStateException</exception>
<onWhen>
<simple>${exception.message} contains 'Severe'</simple>
</onWhen>
<to uri="mock:catch"/>
</doCatch>
<doCatch>
<exception>org.apache.camel.CamelExchangeException</exception>
<to uri="mock:catchCamel"/>
</doCatch>
<doFinally>
<to uri="mock:finally"/>
</doFinally>
</doTry>
</route>