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 either or both 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.
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. |
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 ) { ... } }