The service activator pattern, shown in Figure 9.9, describes the scenario where a service's operations are invoked in response to an incoming request message. The service activator identifies which operation to call and extracts the data to use as the operation's parameters. Finally, the service activator invokes an operation using the data extracted from the message. The operation invocation can be either oneway (request only) or two-way (request/reply).
In many respects, a service activator resembles a conventional remote procedure call (RPC), where operation invocations are encoded as messages. The main difference is that a service activator needs to be more flexible. An RPC framework standardizes the request and reply message encodings (for example, Web service operations are encoded as SOAP messages), whereas a service activator typically needs to improvise the mapping between the messaging system and the service's operations.
The main mechanism that Fuse Mediation Router provides to support the service activator pattern is
bean integration. Bean integration
provides a general framework for mapping incoming messages to method invocations on Java
objects. For example, the Java fluent DSL provides the processors bean()
and
beanRef()
that you can insert into a route to invoke methods on a registered
Java bean. The detailed mapping of message data to Java method parameters is determined by
the bean binding, which can be implemented by adding annotations to
the bean class.
For example, consider the following route which calls the Java method,
BankBean.getUserAccBalance()
, to service requests incoming on a JMS/ActiveMQ
queue:
from("activemq:BalanceQueries") .setProperty("userid", xpath("/Account/BalanceQuery/UserID").stringResult()) .beanRef("bankBean", "getUserAccBalance") .to("velocity:file:src/scripts/acc_balance.vm") .to("activemq:BalanceResults");
The messages pulled from the ActiveMQ endpoint, activemq:BalanceQueries
,
have a simple XML format that provides the user ID of a bank account. For example:
<?xml version='1.0' encoding='UTF-8'?> <Account> <BalanceQuery> <UserID>James.Strachan</UserID> </BalanceQuery> </Account>
The first processor in the route, setProperty()
, extracts the user ID from
the In message and stores it in the userid
exchange
property. This is preferable to storing it in a header, because the In
headers are not available after invoking the bean.
The service activation step is performed by the beanRef()
processor, which
binds the incoming message to the getUserAccBalance()
method on the Java object
identified by the bankBean
bean ID. The following code shows a sample
implementation of the BankBean
class:
package tutorial; import org.apache.camel.language.XPath; public class BankBean { public int getUserAccBalance(@XPath("/Account/BalanceQuery/UserID") String user) { if (user.equals("James.Strachan")) { return 1200; } else { return 0; } } }
Where the binding of message data to method parameter is enabled by the
@XPath
annotation, which injects the content of the UserID
XML
element into the user
method parameter. On completion of the call, the return
value is inserted into the body of the Out message which is then copied
into the In message for the next step in the route. In order for the
bean to be accessible to the beanRef()
processor, you must instantiate an
instance in Spring XML. For example, you can add the following lines to the
META-INF/spring/camel-context.xml
configuration file to instantiate
the bean:
<?xml version="1.0" encoding="UTF-8"?> <beans ... > ... <bean id="bankBean" class="tutorial.BankBean"/> </beans>
Where the bean ID, bankBean
, identifes this bean instance in the
registry.
The output of the bean invocation is injected into a Velocity template, to produce a
properly formatted result message. The Velocity endpoint,
velocity:file:src/scripts/acc_balance.vm
, specifies the location of a
velocity script with the following contents:
<?xml version='1.0' encoding='UTF-8'?> <Account> <BalanceResult> <UserID>${exchange.getProperty("userid")}</UserID> <Balance>${body}</Balance> </BalanceResult> </Account>
The exchange instance is available as the Velocity variable, exchange
,
which enables you to retrieve the userid
exchange property, using
${exchange.getProperty("userid")}
. The body of the current
In message, ${body}
, contains the result of the
getUserAccBalance()
method invocation.