401 lines
24 KiB
XML
401 lines
24 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<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><![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>></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><emphasis><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 (<beans:bean ... />). 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, use the 'channel' element:
|
|
<programlisting><channel id="exampleChannel"/></programlisting>
|
|
</para>
|
|
<para>
|
|
You can also specify the channel's capacity: <programlisting><channel id="exampleChannel" capacity="100"/></programlisting>
|
|
</para>
|
|
<para>
|
|
The default channel type is <emphasis>Point to Point</emphasis>. To create a
|
|
<emphasis>Publish Subscribe</emphasis> channel, provide a value of <emphasis>true</emphasis> for the
|
|
'publish-subscribe' attribute of the channel element:
|
|
<programlisting><channel id="exampleChannel" publish-subscribe="true"/></programlisting>
|
|
</para>
|
|
<para>
|
|
When the <classname>MessageBus</classname> detects and registers channels, it will establish a dispatcher for
|
|
each channel. The default dispatcher settings were previously displayed in
|
|
<xref linkend="api-messagebus-dispatcherpolicy"/>. To customize these settings for a particular channel, add
|
|
the 'dispatcher-policy' sub-element and provide one or more of the attributes shown below:
|
|
<programlisting><![CDATA[<channel id="exampleChannel" publish-subscribe="true">
|
|
<dispatcher-policy max-messages-per-task="25"
|
|
receive-timeout="10"
|
|
rejection-limit="3"
|
|
retry-interval="500"
|
|
should-fail-on-rejection-limit="false"/>
|
|
</channel>]]></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><![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><![CDATA[<channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
|
|
</para>
|
|
<para>
|
|
To create a <classname>PriorityChannel</classname>, use the "priority-channel" element:
|
|
<programlisting><![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, the <classname>PriorityChannel</classname> does support the "datatype"
|
|
attribute. The following example demonstrates both:
|
|
<programlisting><![CDATA[<priority-channel id="exampleChannel"
|
|
datatype="example.Widget" comparator-ref="widgetComparator"/>
|
|
]]></programlisting>
|
|
</para>
|
|
<para>
|
|
Message channels may also have interceptors as described in <xref linkend="api-channelinterceptor"/>. One or
|
|
more <interceptor> elements can be added as sub-elements of <channel>. Provide the "ref" attribute
|
|
to reference any Spring-managed object that implements the <interfacename>ChannelInterceptor</interfacename>
|
|
interface:
|
|
<programlisting><![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>
|
|
To create a Message Endpoint instance, use the 'handler-endpoint' element with the 'input-channel' and 'handler'
|
|
attributes:
|
|
<programlisting><endpoint input-channel="exampleChannel" handler="exampleHandler"/></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><endpoint input-channel="exampleChannel" handler="somePojo" method="someMethod"/></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 for a value in 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>. If the message header does not contain a
|
|
'returnAddress' property at all, then it will fallback to its own 'defaultOutputChannelName' property. If
|
|
neither is available, then a <classname>MessageHandlingException</classname> will be thrown. To configure the
|
|
default output channel when using the XML namespace, provide the 'default-output-channel' attribute:
|
|
<programlisting><endpoint input-channel="exampleChannel"
|
|
handler="somePojo"
|
|
method="someMethod"
|
|
default-output-channel="replyChannel"/></programlisting>
|
|
</para>
|
|
<para>
|
|
Endpoints also support <interfacename>MessageSelectors</interfacename> as described in
|
|
<xref linkend="api-messageselector"/>. To configure selectors with namespace support, simply add one or more
|
|
<selector> sub-elements to the endpoint definition:
|
|
<programlisting><![CDATA[<handler-endpoint id="endpoint" input-channel="channel" handler="handler">
|
|
]]><emphasis><![CDATA[<selector ref="exampleSelector"/>]]></emphasis><![CDATA[
|
|
</handler-endpoint>]]></programlisting>
|
|
</para>
|
|
<para>
|
|
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 channel and push them to the endpoints. To configure the polling period for an individual endpoint's
|
|
schedule, provide a 'schedule' sub-element with the 'period' in milliseconds:
|
|
<programlisting><![CDATA[<handler-endpoint input-channel="exampleChannel" handler="exampleHandler"/>
|
|
]]><emphasis><![CDATA[<schedule period="3000"/>]]></emphasis><![CDATA[
|
|
</handler-endpoint>]]></programlisting>
|
|
</para>
|
|
<note>
|
|
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.
|
|
</note>
|
|
<para>
|
|
One of the most important configuration options for endpoints is the concurrency policy. Each endpoint is
|
|
capable of managing a thread pool for its handler, and the values you provide for that pool's core and max
|
|
size can make a substantial difference in how the handler performs under load. These settings are available
|
|
per-endpoint since the performance characteristics of an endpoint's handler 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' sub-element and one or more of the properties shown below:
|
|
<programlisting><![CDATA[<handler-endpoint input-channel="exampleChannel" handler="exampleHandler"/>
|
|
]]><emphasis><![CDATA[<concurrency core="5" max="25" queue-capacity="20" keep-alive="120"/>]]></emphasis><![CDATA[
|
|
</handler-endpoint>]]></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 will be invoked in the caller's thread.
|
|
</para>
|
|
<tip>
|
|
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.
|
|
</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><message-bus/></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><![CDATA[<message-bus error-channel="errorChannel"/>
|
|
|
|
<channel id="errorChannel" publish-subscribe="true" capacity="500"/>]]></programlisting>
|
|
When exceptions occur in an 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>PayloadTypeRouter</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.
|
|
</para>
|
|
<para>
|
|
The 'message-bus' element accepts two more optional attributes. First is the size of the dispatcher thread
|
|
pool. The dispatcher threads are responsible for polling channels and then passing the messages to handlers.
|
|
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. Finally, the Message Bus is
|
|
capable of automatically creating channel instances (with default settings) if an endpoint registers a
|
|
subscription by providing the name of a channel that the bus does not recognize.
|
|
<programlisting><![CDATA[<message-bus dispatcher-pool-size="25" auto-create-channels="true"/>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="namespace-adapters">
|
|
<title>Configuring Channel Adapters</title>
|
|
<para>
|
|
The most convenient way to configure Channel Adapters is by using the namespace support. The following examples
|
|
demonstrate the namespace-based configuration of several source and target adapters:
|
|
<programlisting><![CDATA[
|
|
<jms-source connection-factory="connFactory" destination="inQueue" channel="in1"/>
|
|
|
|
<!-- using the default "connectionFactory" reference -->
|
|
<jms-target destination="outQueue" channel="out1"/>
|
|
|
|
<file-source directory="/tmp/in" channel="in2" poll-period="10000"/>
|
|
|
|
<file-target directory="/tmp/out" channel="out2"/>
|
|
|
|
<rmi-source id="rmiSource" channel="in3"/>
|
|
|
|
<rmi-target id="rmiTarget" local-channel="out3"
|
|
remote-channel="someRemoteChannel" host="somehost"/>
|
|
|
|
<httpinvoker-source name="/some/path" channel="in4"/>
|
|
|
|
<httpinvoker-target id="httpTarget" channel="out4" url="http://somehost/test"/>
|
|
|
|
<mail-target id="mailTarget" channel="out5"
|
|
host="somehost" username="someuser" password="somepassword"/>
|
|
|
|
<ws-target id="wsTarget" uri="http://example.org" channel="out6"/>
|
|
|
|
<ftp-source id="ftpAdapter" channel="in5"
|
|
period="60000" host="example.org"
|
|
username="someuser" password="somepassword"
|
|
local-working-directory="/some/path"
|
|
remote-working-directory="/some/path"/>
|
|
]]></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><annotation-driven/></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>@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>@MessageEndpoint(input="fooChannel")
|
|
public class FooService {
|
|
|
|
@Handler
|
|
public void bar(<emphasis>Foo foo</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 message header's 'replyChannelName' property will be used if available, and the endpoint's default output is
|
|
the fallback. To configure the default output for an annotation-driven endpoint, provide the 'defaultOutput'
|
|
attribute on the <interfacename>@MessageEndpoint</interfacename>.
|
|
<programlisting>@MessageEndpoint(input="exampleChannel", defaultOutput="replyChannel")</programlisting>
|
|
</para>
|
|
<para>
|
|
Just as the 'schedule' sub-element and its 'period' attribute can be provided for a namespace-based
|
|
endpoint, the 'pollPeriod' attribute can be provided on the <interfacename>@MessageEndpoint</interfacename>.
|
|
<programlisting>@MessageEndpoint(input="exampleChannel", pollPeriod=3000)</programlisting>
|
|
Likewise, <interfacename>@Concurrency</interfacename> provides an annotation-based equivalent of the
|
|
<concurrency/> element:
|
|
<programlisting>@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>@Router
|
|
public MessageChannel route(Message message) {...}
|
|
|
|
@Router
|
|
public List<MessageChannel> route(Message message) {...}
|
|
|
|
@Router
|
|
public String route(Foo payload) {...}
|
|
|
|
@Router
|
|
public List<String> 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 map to either a property or attribute name.
|
|
<programlisting>@Router(property="customerType")
|
|
public String route(String customerType)
|
|
|
|
@Router(attribute="orderStatus")
|
|
public List<String> route(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. The <interfacename>@Splitter</interfacename>
|
|
annotation expects a 'channel' attribute that specifies the channel name to which those messages should be sent.
|
|
<programlisting>@Splitter(channel="exampleChannel")
|
|
List<LineItem> extractItems(Order order) {
|
|
return order.getItems()
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
The <interfacename>@Publisher</interfacename> annotation is a convenience 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><![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><![CDATA[@Subscriber(channel="fooChannel")
|
|
public void log(String foo) {
|
|
System.out.println(foo);
|
|
}]]></programlisting>
|
|
</para>
|
|
</section>
|
|
</chapter> |