Files
spring-integration/spring-integration-reference/src/configuration.xml
2008-07-09 12:29:06 +00:00

597 lines
35 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="config">
<title>Configuration</title>
<section id="config-intro">
<title>Introduction</title>
<para>
Spring Integration offers a number of configuration options. Which option you choose depends upon your particular
needs and at what level you prefer to work. As with the Spring framework in general, it is also possible to mix
and match the various techniques according to the particular problem at hand. For example, you may choose the
XSD-based namespace for the majority of configuration combined with a handful of objects that are configured with
annotations. As much as possible, the two provide consistent naming. XML elements defined by the XSD schema will
match the names of annotations, and the attributes of those XML elements will match the names of annotation
properties. Direct usage of the API is yet another option and is described in detail in <xref linkend="api"/>.
We expect that most users will choose one of the higher-level options, such as the namespace-based or
annotation-driven configuration.
</para>
</section>
<section id="namespace">
<title>Namespace Support</title>
<para>
Spring Integration components can be configured with XML elements that map directly to the terminology and
concepts of enterprise integration. In many cases, the element names match those of the
<ulink url="http://www.eaipatterns.com">Enterprise Integration Patterns</ulink>.
</para>
<para>
To enable Spring Integration's namespace support within your Spring configuration files, add the following
namespace reference and schema mapping in your top-level 'beans' element:
<programlisting language="xml"><![CDATA[<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
]]><emphasis>xmlns:integration="http://www.springframework.org/schema/integration"</emphasis><![CDATA[
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
]]><emphasis>http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd"</emphasis>&gt;</programlisting>
</para>
<para>
You can choose any name after "xmlns:"; <emphasis>integration</emphasis> is used here for clarity, but you might
prefer a shorter abbreviation. Of course if you are using an XML-editor or IDE support, then the availability of
auto-completion may convince you to keep the longer name for clarity. Alternatively, you can create configuration
files that use the Spring Integration schema as the primary namespace:
<programlisting language="xml"><emphasis>&lt;beans:beans xmlns="http://www.springframework.org/schema/integration"</emphasis><![CDATA[
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
]]><emphasis>xmlns:beans="http://www.springframework.org/schema/beans"</emphasis><![CDATA[
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">]]></programlisting>
</para>
<para>
When using this alternative, no prefix is necessary for the Spring Integration elements. On the other hand, if
you want to define a generic Spring "bean" within the same configuration file, then a prefix would be required
for the bean element (&lt;beans:bean ... /&gt;). Since it is generally a good idea to modularize the
configuration files themselves based on responsibility and/or architectural layer, you may find it appropriate to
use the latter approach in the integration-focused configuration files, since generic beans are seldom necessary
within those same files. For purposes of this documentation, we will assume the "integration" namespace is
primary.
</para>
<section id="namespace-channel">
<title>Configuring Message Channels</title>
<para>
To create a Message Channel instance, you can use the generic 'channel' element:
<programlisting language="xml">&lt;channel id="exampleChannel"/&gt;</programlisting>
</para>
<para>
The default channel type is <emphasis>Point to Point</emphasis>. To create a
<emphasis>Publish Subscribe</emphasis> channel, use the "publish-subscribe-channel" element:
<programlisting language="xml">&lt;publish-subscribe-channel id="exampleChannel"/&gt;</programlisting>
</para>
<para>
To create a <ulink url="http://www.eaipatterns.com/DatatypeChannel.html">Datatype Channel</ulink> that only
accepts messages containing a certain payload type, provide the fully-qualified class name in the
channel element's <literal>datatype</literal> attribute:
<programlisting language="xml"><![CDATA[<channel id="numberChannel" datatype="java.lang.Number"/>]]></programlisting>
Note that the type check passes for any type that is <emphasis>assignable</emphasis> to the channel's
datatype. In other words, the "numberChannel" above would accept messages whose payload is
<classname>java.lang.Integer</classname> or <classname>java.lang.Double</classname>. Multiple types can be
provided as a comma-delimited list:
<programlisting language="xml"><![CDATA[<channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
</para>
<para>
When using the "channel" element, the creation of the channel instances will be deferred to the <classname>ChannelFactory</classname>
defined on the <classname>MessageBus</classname> (see below).
</para>
<para>
It is also possible to use more specific elements for the various channel types (as described in
<xref linkend="api-messagechannel"/>). Depending on the channel, these may provide additional configuration
options. Examples of each are shown below.
</para>
<section id="namespace-channel-queuechannel">
<title>The &lt;queue-channel/&gt; element</title>
<para>
To create a <classname>QueueChannel</classname>, use the "queue-channel" element.
By using this element, you can also specify the channel's capacity:
<programlisting language="xml">&lt;queue-channel id="exampleChannel" capacity="25"/&gt;</programlisting>
</para>
</section>
<section id="namespace-channel-pubsubchannel">
<title>The &lt;publish-subscribe-channel/&gt; element</title>
<para>
To create a <classname>PublishSubscribeChannel</classname>, use the "publish-subscribe-channel" element.
By using this element, you can also specify the "task-executor" used for publishing
Messages (if none is specified it simply publishes in the sender's thread):
<programlisting language="xml">&lt;publish-subscribe-channel id="exampleChannel" task-executor="someTaskExecutor"/&gt;</programlisting>
</para>
</section>
<section id="namespace-channel-prioritychannel">
<title>The &lt;priority-channel/&gt; element</title>
<para>
To create a <classname>PriorityChannel</classname>, use the "priority-channel" element:
<programlisting language="xml"><![CDATA[<priority-channel id="exampleChannel"/>]]></programlisting>
By default, the channel will consult the <classname>MessagePriority</classname> value in the
message's header. However, a custom <interfacename>Comparator</interfacename> reference may be
provided instead. Also, note that the <classname>PriorityChannel</classname> (like the other types)
does support the "datatype" attribute. As with the "queue-channel", it also supports a "capacity" attribute.
The following example demonstrates all of these:
<programlisting language="xml"><![CDATA[<priority-channel id="exampleChannel"
datatype="example.Widget"
comparator="widgetComparator"
capacity="10"/>
]]></programlisting>
</para>
</section>
<section id="namespace-channel-rendezvouschannel">
<title>The &lt;rendezvous-channel/&gt; element</title>
<para>
The <classname>RendezvousChannel</classname> does not provide any additional configuration options.
<programlisting language="xml"><![CDATA[<rendezvous-channel id="exampleChannel"/>]]></programlisting>
</para>
</section>
<section id="namespace-channel-directchannel">
<title>The &lt;direct-channel/&gt; element</title>
<para>
The <classname>DirectChannel</classname> does not provide any additional configuration options.
<programlisting language="xml"><![CDATA[<direct-channel id="exampleChannel"/>]]></programlisting>
</para>
</section>
<section id="namespace-channel-threadlocalchannel">
<title>The &lt;thread-local-channel/&gt; element</title>
<para>
The <classname>ThreadLocalChannel</classname> does not provide any additional configuration options.
<programlisting language="xml"><![CDATA[<thread-local-channel id="exampleChannel"/>]]></programlisting>
</para>
</section>
<para>
Message channels may also have interceptors as described in <xref linkend="api-channelinterceptor"/>. One or
more &lt;interceptor&gt; elements can be added as sub-elements of &lt;channel&gt; (or the more specific element
types). Provide the "ref" attribute to reference any Spring-managed object that implements the
<interfacename>ChannelInterceptor</interfacename> interface:
<programlisting language="xml"><![CDATA[<channel id="exampleChannel">
]]><emphasis><![CDATA[<interceptor ref="trafficMonitoringInterceptor"/>]]></emphasis><![CDATA[
</channel>]]></programlisting>
In general, it is a good idea to define the interceptor implementations in a separate location since they
usually provide common behavior that can be reused across multiple channels.
</para>
</section>
<section id="namespace-endpoint">
<title>Configuring Message Endpoints</title>
<para>
Each of the endpoint types (channel-adapter, service-activator, etc) has its own element in the namespace.
</para>
<section id="namespace-endpoint-inboundchanneladapter">
<title>The inbound &lt;channel-adapter/&gt; element with a MessageSource</title>
<para>
A "channel-adapter" element can connect any implementation of the <interfacename>MessageSource</interfacename>
interface to a <interfacename>MessageChannel</interfacename>. When the <interfacename>MessageBus</interfacename>
registers the endpoint, it will activate the subscription by assigning the endpoint to the input channel's dispatcher.
The dispatcher is capable of handling multiple endpoint subscriptions for its channel and delegates to a scheduler
for managing the tasks that pull messages from the source and push them to the channel. To configure the polling
period for an individual channel-adapter's schedule, provide a 'schedule' sub-element with the 'period' in
milliseconds:
<programlisting language="xml"><![CDATA[<channel-adapter source="exampleSource" channel="exampleChannel">
<schedule period="5000"/>
</channel-adapter>]]></programlisting>
</para>
</section>
<section id="namespace-endpoint-outboundchanneladapter">
<title>The outbound &lt;channel-adapter/&gt; with a MessageTarget</title>
<para>
A "channel-adapter" element can also connect a <interfacename>MessageChannel</interfacename> to any implementation
of the <interfacename>MessageTarget</interfacename> interface.
<programlisting language="xml"><![CDATA[<channel-adapter channel="exampleChannel" target="exampleTarget"/>]]></programlisting>
Again, it is possible to provide a schedule:
<programlisting language="xml"><![CDATA[<channel-adapter channel="exampleChannel" target="exampleTarget">
]]><emphasis><![CDATA[<schedule period="3000"/>]]></emphasis><![CDATA[
</channel-adapter>]]></programlisting>
</para>
<note>
<para>
Individual endpoint schedules only apply for "Point-to-Point" channels, since in that case only a single
subscriber needs to receive the message. On the other hand, when a Spring Integration channel is configured as
a "Publish-Subscribe" channel, then the dispatcher will drive all endpoint notifications according to its own
default schedule, and any 'schedule' element configured for those endpoints will be ignored.
</para>
</note>
</section>
<section id="namespace-service-activator">
<title>The &lt;service-activator/&gt; element</title>
<para>
To create a Service Activator, use the 'service-activator' element with the 'input-channel' and
'ref' attributes:
<programlisting language="xml">&lt;service-activator input-channel="exampleChannel" ref="exampleHandler"/&gt;</programlisting>
</para>
<para>
The configuration above assumes that "exampleHandler" is an actual implementation of the
<interfacename>MessageHandler</interfacename> interface as described in <xref linkend="api-messagehandler"/>.
To delegate to an arbitrary method of any object, simply add the "method" attribute.
<programlisting language="xml">&lt;service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/&gt;</programlisting>
</para>
<para>
In either case (<interfacename>MessageHandler</interfacename> or arbitrary object/method), when the handling
method returns a non-null value, the endpoint will attempt to send the reply message to an appropriate reply
channel. To determine the reply channel, it will first check if an "output-channel" was provided in the
endpoint configuration:
<programlisting language="xml">&lt;service-activator input-channel="exampleChannel" output-channel="replyChannel"
ref="somePojo" method="someMethod"/&gt;</programlisting>
If no "output-channel" is available, it will next check the message header's '<literal>returnAddress</literal>'
property. If that value is available, it will then check its type. If it is a <classname>MessageChannel</classname>,
the reply message will be sent to that channel. If it is a <classname>String</classname>, then the endpoint will
attempt to resolve the channel by performing a lookup in the <interfacename>ChannelRegistry</interfacename>.
</para>
<para>
To reverse the order so that the 'returnAddress' is given priority over the endpoint's "output-channel", then
provide the "return-address-overrides" attribute with a value of 'true':
<programlisting language="xml">&lt;service-activator input-channel="exampleChannel" output-channel="replyChannel"
ref="somePojo" method="someMethod" return-address-overrides="true"/&gt;</programlisting>
If neither is available, then a <classname>MessageHandlingException</classname> will be thrown.
</para>
</section>
<para>
Message Endpoints also support <interfacename>MessageSelectors</interfacename> as described in
<xref linkend="api-messageselector"/>. To configure a selector with namespace support, simply add the
"selector" attribute to the endpoint definition and reference an implementation of the
<interfacename>MessageSelector</interfacename> interface.
<programlisting language="xml"><![CDATA[<service-activator id="endpoint" input-channel="channel" ref="handler"
selector="exampleSelector"/>]]></programlisting>
</para>
<para>
Another important configuration option for message endpoints is the inclusion of
<classname>EndpointInterceptors</classname>. The interface is defined as follows:
<programlisting language="java"><![CDATA[public interface EndpointInterceptor {
boolean preSend(Message<?> message);
boolean aroundSend(Message<?> message, MessageTarget endpoint);
void postSend(Message<?> message, boolean result);
}]]></programlisting>
There is also an EndpointInterceptorAdapter that provides no-op methods for convenience
when subclassing. Within an endpoint configuration, interceptors can be added within
the &lt;interceptors&gt; sub-element. It accepts either "ref" elements or inner "beans":
<programlisting language="xml"><![CDATA[<service-activator id="exampleEndpoint"
input-channel="requestChannel"
ref="someObject"
method="someMethod"
output-channel="replyChannel">
<schedule period="1000"/>
<interceptors>
<ref bean="someInterceptor"/>
<beans:bean class="example.AnotherInterceptor"/>
</interceptors>
</service-activator>]]></programlisting>
</para>
<para>
Spring Integration provides a <classname>TransactionInterceptor</classname> and namespace
support with the &lt;transaction-interceptor&gt; element. The attributes for this element
should be familiar to anyone who has experience with Spring's Transaction management:
<programlisting language="xml"><![CDATA[<service-activator id="exampleEndpoint"
input-channel="requestChannel"
ref="someObject"
method="someMethod"
output-channel="replyChannel">
<schedule period="1000"/>
<interceptors>
<transaction-interceptor transaction-manager="txManager"
propagation="REQUIRES_NEW"
isolation="REPEATABLE_READ"
timeout="10000"
read-only="false"/>
</interceptors>
</service-activator>]]></programlisting>
</para>
<para>
Spring Integration also provides a ConcurrencyInterceptor. By applying this, an endpoint becomes capable of
managing a thread pool, and the concurrency settings you provide for that pool's core size, max size,
and queue capacity can make a substantial difference in how the endpoint performs under load.
These settings are available per-endpoint since the performance characteristics of an endpoint's handler or
target is one of the major factors to consider (the other major factor being the expected volume on the
channel to which the endpoint subscribes). To enable concurrency for an endpoint that is configured with the
XML namespace support, provide the 'concurrency-interceptor' element within the 'interceptors' sub-element
and then provide one or more of the properties shown below:
<programlisting language="xml"><![CDATA[<service-activator input-channel="exampleChannel" ref="exampleHandler">
<interceptors>
]]><emphasis><![CDATA[<concurrency-interceptor core="5" max="25" queue-capacity="20" keep-alive="120"/>]]></emphasis><![CDATA[
</interceptors>
</service-activator>]]></programlisting>
Recall the default concurrency policy values as listed in <xref linkend="api-messagebus-concurrencypolicy"/>.
If no concurrency settings are provided (i.e. a <emphasis>null</emphasis>
<classname>ConcurrencyPolicy</classname>), the endpoint's handler or target will be invoked in the caller's thread.
Note that the "caller" is usually the dispatcher except in the case of a <classname>DirectChannel</classname>
(see <xref linkend="api-messagechannel-directchannel"/> for more detail).
</para>
<para>
Another option for the concurrency-interceptor is to provide the "task-executor" attribute
with a reference to any implementation of Spring's <interfacename>TaskExecutor</interfacename>
interface.
</para>
<tip>
<para>
For the concurrency settings, the default queue capacity of 0 triggers the creation of a
<classname>SynchronousQueue</classname>. In many cases, this is preferable since the direct handoff eliminates
the chance of a message handling task being "stuck" in the queue (thread pool executors will favor adding to the
queue rather than increasing the pool size). Specifically, whenever a dispatcher for a Point-to-Point channel
has more than one subscribed endpoint, a task that is rejected due to an exhausted thread pool can be handled
immediately by another endpoint whose pool has one or more threads available. On the other hand, when a
particular channel/endpoint may be expecting bursts of activity, setting a queue capacity value might be the
best way to accommodate the volume.
</para>
</tip>
</section>
<section id="namespace-messagebus">
<title>Configuring the Message Bus</title>
<para>
As described in <xref linkend="api-messagebus"/>, the <classname>MessageBus</classname> plays a central role.
Nevertheless, its configuration is quite simple since it is primarily concerned with managing internal details
based on the configuration of channels and endpoints. The bus is aware of its host application context, and
therefore is also capable of auto-detecting the channels and endpoints. Typically, the
<classname>MessageBus</classname> can be configured with a single empty element:
<programlisting language="xml">&lt;message-bus/&gt;</programlisting>
</para>
<para>
The Message Bus provides default error handling for its components in the form of a configurable error channel,
and the 'message-bus' element accepts a reference with its 'error-channel' attribute:
<programlisting language="xml"><![CDATA[<message-bus error-channel="errorChannel"/>
<channel id="errorChannel" capacity="500"/>]]></programlisting>
When exceptions occur in a concurrent endpoint's execution of its <interfacename>MessageHandler</interfacename>
callback, those exceptions will be wrapped in <classname>ErrorMessages</classname> and sent to the Message Bus'
'errorChannel' by default. To enable global error handling, simply register a handler on that channel. For
example, you can configure Spring Integration's <classname>RootCauseErrorMessageRouter</classname> as the handler of
an endpoint that is subscribed to the 'errorChannel'. That router can then spread the error messages across
multiple channels based on <classname>Exception</classname> type. However, since most of the errors will already
have been wrapped in <classname>MessageDeliveryException</classname> or <classname>MessageHandlingException</classname>,
the <classname>RootCauseErrorMessageRouter</classname> is typically a better option.
</para>
<para>
The 'message-bus' element accepts several more optional attributes. First, you can control whether the
<classname>MessageBus</classname> will be started automatically (the default) or will require explicit startup
by invoking its <methodname>start()</methodname> method (<classname>MessageBus</classname> implements
Spring's <interfacename>Lifecycle</interfacename> interface):
<programlisting language="xml"><![CDATA[<message-bus auto-startup="false"/>]]></programlisting>
</para>
<para>
Another configurable property is the size of the dispatcher thread pool. The dispatcher threads are responsible
for polling channels and then passing the messages to handlers.
<programlisting language="xml"><![CDATA[<message-bus dispatcher-pool-size="25"/>]]></programlisting>
When the endpoints are concurrency-enabled as described in the previous section, the invocation of the handling
methods will happen within the handler thread pool and not the dispatcher pool. However, when no concurrency
policy is provided to an endpoint, then it will be invoked in the dispatcher's thread (with the exception of
<classname>DirectChannels</classname>).
</para>
<para>
Also, the Message Bus is capable of automatically creating channel instances if an endpoint registers a
subscription by providing the name of a channel that the bus does not recognize.
<programlisting language="xml"><![CDATA[<message-bus auto-create-channels="true"/>]]></programlisting>
</para>
<para>
Finally, the type of channel that gets created automatically by the bus can be customized by using the
"channel-factory" attribute on the "message-bus" definition as in the following example:
<programlisting language="xml"><![CDATA[<message-bus channel-factory="channelFactoryBean"/>
<beans:bean id="channelFactoryBean"
class="org.springframework.integration.channel.factory.PriorityChannelFactory"/>]]></programlisting>
With this definition, all the channels created automatically will be <classname>PriorityChannel</classname> instances.
Without the "channel-factory" element, the Message Bus will assume a default <classname>QueueChannelFactory</classname>.
</para>
</section>
<section id="namespace-adapters">
<title>Configuring Adapters</title>
<para>
The most convenient way to configure Source and Target adapters is by using the namespace support. The
following examples demonstrate the namespace-based configuration of several source, target, gateway,
and handler adapters:
<programlisting language="xml"><![CDATA[<jms-source id="jmsSource" connection-factory="connFactory" destination="inQueue"/>
<!-- using the default "connectionFactory" reference -->
<jms-target id="jmsTarget" destination="outQueue"/>
<file-source id="fileSource" directory="/tmp/in"/>
<file-target id="fileTarget" directory="/tmp/out"/>
<rmi-gateway id="rmiSource" request-channel="rmiSourceInput"/>
<rmi-handler id="rmiTarget"
local-channel="rmiTargetOutput"
remote-channel="someRemoteChannel"
host="somehost"/>
<httpinvoker-gateway id="httpSource" name="/some/path" request-channel="httpInvokerInput"/>
<httpinvoker-handler id="httpTarget" channel="httpInvokerOutput" url="http://somehost/test"/>
<mail-target id="mailTarget" host="somehost" username="someuser" password="somepassword"/>
<ws-handler id="wsTarget" uri="http://example.org" channel="wsOutput"/>
<ftp-source id="ftpSource"
host="example.org"
username="someuser"
password="somepassword"
local-working-directory="/some/path"
remote-working-directory="/some/path"/>
]]></programlisting>
</para>
<para>
In the examples above, notice that simple implementations of the <interfacename>MessageSource</interfacename>
and <interfacename>MessageTarget</interfacename> interfaces do not accept any 'channel' references. To
connect such sources and targets to a channel, register them within a 'channel-adapter'. For example, here
is a File source with an endpoint whose polling will be scheduled to execute every 30 seconds by the
<classname>MessageBus</classname>.
<programlisting language="xml"><![CDATA[<channel-adapter source="fileSource" channel="exampleChannel">
<schedule period="30000"/>
</channel-adapter>
<file-source id="fileSource" directory="/tmp/in"/>
]]></programlisting>
Likewise, here is an example of a JMS target that is registered within a 'channel-adapter' and whose Messages
will be received from the "exampleChannel" that is polled every 500 milliseconds.
<programlisting language="xml"><![CDATA[<channel-adapter channel="exampleChannel" target="jmsTarget">
<schedule period="500"/>
</channel-adapter>
<jms-target id="jmsTarget" destination="targetDestination"/>
]]></programlisting>
</para>
</section>
<section id="namespace-annotationdriven">
<title>Enabling Annotation-Driven Configuration</title>
<para>
The next section will describe Spring Integration's support for annotation-driven configuration. To enable
those features, add this single element to the XML-based configuration:
<programlisting language="xml">&lt;annotation-driven/&gt;</programlisting>
</para>
</section>
</section>
<section id="annotations">
<title>Annotations</title>
<para>
In addition to the XML namespace support for configuring Message Endpoints, it is also possible to use
annotations. The class-level <interfacename>@MessageEndpoint</interfacename> annotation indicates that the
annotated class is capable of being registered as an endpoint, and the method-level
<interfacename>@Handler</interfacename> annotation indicates that the annotated method is capable of handling
a message.
<programlisting language="java">@MessageEndpoint(input="fooChannel")
public class FooService {
@Handler
public void processMessage(Message message) {
...
}
}</programlisting>
</para>
<para>
In most cases, the annotated handler method should not require the <classname>Message</classname> type as its
parameter. Instead, the method parameter type can match the message's payload type.
<programlisting language="java">@MessageEndpoint(input="fooChannel")
public class FooService {
@Handler
public void bar(<emphasis>Foo foo</emphasis>) {
...
}
}</programlisting>
</para>
<para>
When the method parameter should be mapped from a value in the <classname>MessageHeader</classname>, another
option is to use the <interfacename>@HeaderAttribute</interfacename> and/or
<interfacename>@HeaderProperty</interfacename> parameter annotations.
<programlisting language="java">@MessageEndpoint(input="fooChannel")
public class FooService {
@Handler
public void bar(<emphasis>@HeaderAttribute("fooAttrib") Foo foo</emphasis>) {
...
}
}</programlisting>
<programlisting language="java">@MessageEndpoint(input="fooChannel")
public class FooService {
@Handler
public void bar(<emphasis>@HeaderProperty("foo") String input</emphasis>) {
...
}
}</programlisting>
</para>
<para>
As described in the previous section, when the handler method returns a non-null value, the endpoint will
attempt to send a reply. This is consistent across both configuration options (namespace and annotations) in that
the the endpoint's output channel will be used if available, and the message header's 'returnAddress' value will be
the fallback. To configure the output channel for an annotation-driven endpoint, provide the 'output'
attribute on the <interfacename>@MessageEndpoint</interfacename>.
<programlisting language="java">@MessageEndpoint(input="exampleChannel", output="replyChannel")</programlisting>
</para>
<para>
Just as the 'schedule' sub-element and its 'period' attribute can be provided for a namespace-based
endpoint, the <interfacename>@Polled</interfacename> annotation can be provided with the
<interfacename>@MessageEndpoint</interfacename> annotation.
<programlisting language="java">@MessageEndpoint(input="exampleChannel")
@Polled(period=3000)
public class FooService {
...
}</programlisting>
Likewise, <interfacename>@Concurrency</interfacename> provides an annotation-based equivalent of the
&lt;concurrency/&gt; element:
<programlisting language="java">@MessageEndpoint(input="fooChannel")
@Concurrency(coreSize=5, maxSize=20)
public class FooService {
@Handler
public void bar(Foo foo) {
...
}
}</programlisting>
</para>
<para>
Two additional annotations are supported, and both act as a special form of handler method:
<interfacename>@Router</interfacename> and <interfacename>@Splitter</interfacename>. As with the
<interfacename>@Handler</interfacename> annotation, methods annotated with either of these two annotations can
either accept the <classname>Message</classname> itself or the message payload type as the parameter.
When using the <interfacename>@Router</interfacename> annotation, the annotated method can return either the
<interfacename>MessageChannel</interfacename> or <classname>String</classname> type. In the case of the latter,
the endpoint will resolve the channel name as it does for the default output. Additionally, the method can return
either a single value or a collection. When a collection is returned, the reply message will be sent to multiple
channels. To summarize, the following method signatures are all valid.
<programlisting language="java">@Router
public MessageChannel route(Message message) {...}
@Router
public List&lt;MessageChannel&gt; route(Message message) {...}
@Router
public String route(Foo payload) {...}
@Router
public List&lt;String&gt; route(Foo payload) {...}</programlisting>
</para>
<para>
In addition to payload-based routing, a common requirement is to route based on metadata available within the
message header as either a property or attribute. Rather than requiring use of the
<interfacename>Message</interfacename> type as the method parameter, the <interfacename>@Router</interfacename>
annotation may also use the same parameter annotations that were introduced above.
<programlisting language="java">@Router
public String route(@HeaderProperty("customerType") String customerType)
@Router
public List&lt;String&gt; route(@HeaderAttribute("orderStatus") OrderStatus status)</programlisting>
</para>
<para>
The <interfacename>@Splitter</interfacename> annotation is also applicable to methods that expect either the
<interfacename>Message</interfacename> type or the message payload type, and the return values of the method
should be a collection of any type. If the returned values are not actual <interfacename>Message</interfacename>
objects, then each of them will be sent as the payload of a message. Those messages will be sent to the output
channel as designated for the endpoint on which the <interfacename>@Splitter</interfacename> is defined.
<programlisting language="java">@Splitter
List&lt;LineItem&gt; extractItems(Order order) {
return order.getItems()
}</programlisting>
</para>
<para>
The <interfacename>@Publisher</interfacename> annotation is convenient for sending messages with AOP
<emphasis>after-returning advice</emphasis>. For example, each time the following method is invoked, its return
value will be sent to the "fooChannel":
<programlisting language="java"><![CDATA[@Publisher(channel="fooChannel")
public String foo() {
return "bar";
}]]></programlisting>
</para>
<para>
Similarly, the <interfacename>@Subscriber</interfacename> annotation triggers the retrieval of messages from a
channel, and the payload of each message will then be sent as input to an arbitrary method. This is one of the
simplest ways to configure asynchronous, event-driven behavior:
<programlisting language="java"><![CDATA[@Subscriber(channel="fooChannel")
public void log(String foo) {
System.out.println(foo);
}]]></programlisting>
</para>
</section>
</chapter>