Moved the location of the documentation up a level

This commit is contained in:
Ben Hale
2008-02-28 17:15:39 +00:00
parent a5abac62d5
commit e79ed2cc58
14 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="adapters">
<title>Channel Adapters</title>
<section id="adapters-intro">
<title>Introduction</title>
<para>
Channel Adapters are the components responsible for interacting with external systems or other components that
are external to the messaging system. As the name implies, the interaction consists of adapting the external
system or component to send-to and/or receive-from a <interfacename>MessageChannel</interfacename>. Within
Spring Integration, there is a distinction between <emphasis>source adapters</emphasis> and <emphasis>target
adapters</emphasis>. In the 1.0 Milestone 2 release, Spring Integration includes source and target adapters
for JMS, Files, Streams, and Spring ApplicationEvents as well as a target adapter for sending e-mail.
</para>
</section>
<section id="adapters-jms">
<title>JMS Adapters</title>
<para>
Spring Integration provides two adapters for accepting JMS messages:
<classname>JmsPollingSourceAdapter</classname> and <classname>JmsMessageDrivenSourceAdapter</classname>.
The former uses Spring's <classname>JmsTemplate</classname> to receive based on a polling period. The latter
configures and delegates to an instance of Spring's <classname>DefaultMessageListenerContainer</classname>.
</para>
<para>
The <classname>JmsPollingSourceAdapter</classname> requires a reference to either a single
<classname>JmsTemplate</classname> instance or both <interfacename>ConnectionFactory</interfacename> and
<interfacename>Destination</interfacename> (a 'destinationName' can be provided in place of the 'destination'
reference). The <classname>JmsPollingSourceAdapter</classname> also requires a 'channel' property that should be
a reference to a <interfacename>MessageChannel</interfacename> instance. The adapter accepts additional
properties such as: period, initialDelay, maxMessagesPerTask, and sendTimeout. The following example defines a
JMS source adapter that polls every 5 seconds and then sends to the "exampleChannel":
<programlisting><![CDATA[<bean class="org.springframework.integration.adapter.jms.JmsPollingSourceAdapter">
<constructor-arg ref="jmsTemplate"/>
<property name="channel" ref="exampleChannel"/>
<property name="period" value="5000"/>
</bean>]]></programlisting>
</para>
<para>
In most cases, Spring Integration's message-driven JMS adapter is more appropriate since it delegates to a
<interfacename>MessageListener</interfacename> container and supports dynamically adjusting concurrent
consumers. The <classname>JmsMessageDrivenSourceAdapter</classname> requires references to a
<interfacename>MessageChannel</interfacename>, a <interfacename>ConnectionFactory</interfacename>, and a
<interfacename>Destination</interfacename> (or 'destinationName'). The following example defines a JMS
message-driven source adapter that receives from the JMS queue called "exampleQueue" and then sends to
the Spring Integration channel named "exampleChannel":
<programlisting><![CDATA[<bean class="org.springframework.integration.adapter.jms.JmsMessageDrivenSourceAdapter">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="exampleQueue"/>
<property name="channel" ref="exampleChannel"/>
</bean>]]></programlisting>
</para>
<para>
For both source adapter types, Spring's <interfacename>MessageConverter</interfacename> strategy is used to
convert the JMS message into a plain Java object, and then Spring Integration's
<interfacename>MessageMapper</interfacename> strategy is used to convert from the plain object to a
<interfacename>Message</interfacename>.
</para>
<para>
The <classname>JmsTargetAdapter</classname> is a <interfacename>MessageHandler</interfacename> implementation
that is capable of mapping Spring Integration <interfacename>Messages</interfacename> to JMS messages and then
sending to a JMS destination. It requires either a 'jmsTemplate' reference or both 'connectionFactory' and
'destination' references (again, the 'destinationName' may be provided in place of the 'destination). In
<xref linkend="namespace-adapters"/>, you will see how to configure a JMS target adapter with Spring
Integration's namespace support.
</para>
</section>
<section id="adapters-file">
<title>File Adapters</title>
<para>
The <classname>FileSourceAdapter</classname> extends the generic <classname>PollingSourceAdapter</classname>
(just as the polling JMS adapter does). It requires the following constructor arguments:
<programlisting>public FileSourceAdapter(File directory, MessageChannel channel, int period)</programlisting>
Optional properties include 'initialDelay' and 'maxMessagesPerTask'.
</para>
<para>
The <classname>FileTargetAdapter</classname> constructor only requires the 'directory' argument. The target
adapter also accepts an implementation of the <interfacename>FileNameGenerator</interfacename> strategy that
defines the following method: <programlisting>String generateFileName(Message message)</programlisting>
</para>
<para>
As with the JMS adapters, the most convenient way to configure File adapters is with the namespace support. For
examples, see <xref linkend="namespace-adapters"/>.
</para>
</section>
<section id="adapters-email">
<title>Mail Adapters</title>
<para>
Spring Integration currently provides support for <emphasis>outbound</emphasis> email only with the
<classname>MailTargetAdapter</classname>. This adapter delegates to a configured instance of Spring's
<interfacename>JavaMailSender</interfacename>, and its various mapping strategies use Spring's
<interfacename>MailMessage</interfacename> abstraction. By default text-based mails are created when
the handled message has a String-based payload. If the message payload is a byte array, then that will
be mapped to an attachment.
</para>
<para>
The adapter also delegates to a <interfacename>MailHeaderGenerator</interfacename> for providing the
mail's properties, such as the recipients (TO, CC, and BCC), the from/reply-to, and the subject.
<programlisting><![CDATA[public interface MailHeaderGenerator {
void populateMailMessageHeader(MailMessage mailMessage, Message<?> message);
}]]></programlisting>
A static implementation is available out-of-the-box, but typically most of the properties would need to be
dynamically generated based on the message itself. The following is an example of a configured
mail adapter.
<programlisting><![CDATA[<bean id="mailTargetAdapter" class="org.springframework.integration.adapter.mail.MailTargetAdapter">
<property name="mailSender" ref="javaMailSender"/>
<property name="headerGenerator" ref="dynamicMailMessageHeaderGenerator"/>
</bean>]]></programlisting>
</para>
</section>
<section id="adapters-stream">
<title>Stream Adapters</title>
<para>
Spring Integration also provides adapters for streams. Both <classname>ByteStreamSourceAdapter</classname> and
<classname>CharacterStreamSourceAdapter</classname> extend the <classname>PolllingSourceAdapter</classname> so
that the polling period can be configured, and the Message Bus can automatically detect and schedule them. Both
require an <classname>InputStream</classname> as the single constructor argument. The
<classname>ByteStreamSourceAdapter</classname> also accepts the 'bytesPerMessage' property to determine how many
bytes it will attempt to read into each <interfacename>Message</interfacename>.
</para>
<para>
For target streams, there are also two implementations: <classname>ByteStreamTargetAdapter</classname> and
<classname>CharacterStreamTargetAdapter</classname>. Each defines a constructor that requires an
<classname>OutputStream</classname>, and each provides a second constructor that adds the optional
'bufferSize' property. Since both of these ultimately implement the <interfacename>MessageHandler</interfacename>
interface, they can be referenced from an endpoint configuration as will be described in more detail in
<xref linkend="namespace-endpoint"/>.
</para>
</section>
<section id="adapters-applicationevents">
<title>ApplicationEvent Adapters</title>
<para>
Spring <classname>ApplicationEvents</classname> can also be integrated as either a source or target for Spring
Integration message channels. To receive the events and send to a channel, simply define an instance of Spring
Integration's <classname>ApplicationEventSourceAdapter</classname> (as with all source adapters, if a
<classname>MessageBus</classname> is defined, it will automatically detect the event source adapter). The
<classname>ApplicationEventSourceAdapter</classname> implements Spring's
<interfacename>ApplicationListener</interfacename> interface. By default it will pass all received events as
Spring Integration Messages. To limit based on the type of event, configure the list of event types that you
want to receive with the 'eventTypes' property.
</para>
<para>
To send Spring <classname>ApplicationEvents</classname>, register an instance of the
<classname>ApplicationEventTargetAdapter</classname> class as the handler of an endpoint (such configuration
will be described in detail in <xref linkend="namespace-endpoint"/>). This adapter implements Spring's
<interfacename>ApplicationEventPublisherAware</interfacename> interface and thus acts as a bridge between
Spring Integration <classname>Messages</classname> and <classname>ApplicationEvents</classname>.
</para>
</section>
</chapter>

View File

@@ -0,0 +1,359 @@
<?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. Of course, it is also possible to always stick with a single approach. The main point is that these
are <emphasis>options</emphasis> for configuration motivated by the need to support a user community with a wide
range of preferences. That said, there has also been a concerted effort to provide consistent naming so that, for
example, the 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>&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><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, use the 'channel' element:
<programlisting>&lt;channel id="exampleChannel"/&gt;</programlisting>
</para>
<para>
You can also specify the channel's capacity: <programlisting>&lt;channel id="exampleChannel" capacity="100"/&gt;</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>&lt;channel id="exampleChannel" publish-subscribe="true"/&gt;</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>
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;. 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 'endpoint' element with the 'input-channel' and 'handler-ref'
attributes:
<programlisting>&lt;endpoint input-channel="exampleChannel" handler-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 "handler-method" attribute.
<programlisting>&lt;endpoint input-channel="exampleChannel" handler-ref="somePojo" handler-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 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>&lt;endpoint input-channel="exampleChannel"
handler-ref="somePojo"
handler-method="someMethod"
default-output-channel="replyChannel"/&gt;</programlisting>
</para>
<para>
Endpoint's also support <interfacename>MessageSelectors</interfacename> as described in
<xref linkend="api-messageselector"/>. To configure selectors with namespace support, simply add one or more
&lt;selector&gt; sub-elements to the endpoint definition:
<programlisting><![CDATA[<endpoint id="endpoint" input-channel="channel" handler-ref="handler">
]]><emphasis><![CDATA[<selector ref="exampleSelector"/>]]></emphasis><![CDATA[
</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[<endpoint input-channel="exampleChannel" handler-ref="exampleHandler"/>
]]><emphasis><![CDATA[<schedule period="3000"/>]]></emphasis><![CDATA[
</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[<endpoint input-channel="exampleChannel" handler-ref="exampleHandler"/>
]]><emphasis><![CDATA[<concurrency core="5" max="25" queue-capacity="20" keep-alive="120"/>]]></emphasis><![CDATA[
</endpoint>]]></programlisting>
Recall the default concurrency policy values as listed in <xref linkend="api-messagebus-concurrencypolicy"/>.
</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>&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><![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 source and target adapters (Spring Integration 1.0 M1 includes
namespace support for JMS and Files):
<programlisting><![CDATA[
<jms-source connection-factory="connectionFactory" destination="inputQueue" channel="inputChannel1"/
<jms-target connection-factory="connectionFactory" destination="outputQueue" channel="outputChannel1"/>
<file-source directory="/tmp/input" channel="inputChannel2" poll-period="10000"/>
<file-target directory="/tmp/output" channel="outputChannel2"/>
]]></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>&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>@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 processFoo(<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>
Finally, 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>
</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&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 map to either a property or attribute name.
<programlisting>@Router(property="customerType")
public String route(String customerType)
@Router(attribute="orderStatus")
public List&lt;String&gt; 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&lt;LineItem&gt; 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>

View File

@@ -0,0 +1,444 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="api">
<title>The Core API</title>
<section id="api-message">
<title>Message</title>
<para>
The Spring Integration <interfacename>Message</interfacename> is a generic container for data. Any object can
be provided as the payload, and each <interfacename>Message</interfacename> also includes a header containing
user-extensible properties as key-value pairs. Here is the definition of the
<interfacename>Message</interfacename> interface:
<programlisting>public interface Message&lt;T&gt; {
Object getId();
MessageHeader getHeader();
T getPayload();
boolean isExpired();
}</programlisting>
And the header provides the following properties:
<table id="api-message-headerproperties">
<title>Properties of the MessageHeader</title>
<tgroup cols="2">
<colspec align="left" />
<thead>
<row>
<entry align="center">Property Name</entry>
<entry align="center">Property Type</entry>
</row>
</thead>
<tbody>
<row>
<entry>timestamp</entry>
<entry>java.util.Date</entry>
</row>
<row>
<entry>expiration</entry>
<entry>java.util.Date</entry>
</row>
<row>
<entry>correlationId</entry>
<entry>java.lang.Object</entry>
</row>
<row>
<entry>returnAddress</entry>
<entry>java.lang.Object (can be a String or MessageChannel)</entry>
</row>
<row>
<entry>sequenceNumber</entry>
<entry>int</entry>
</row>
<row>
<entry>sequenceSize</entry>
<entry>int</entry>
</row>
<row>
<entry>priority</entry>
<entry>int</entry>
</row>
<row>
<entry>properties</entry>
<entry>java.util.Properties</entry>
</row>
<row>
<entry>attributes</entry>
<entry>Map&lt;String,Object&gt;</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
The base implementation of the <interfacename>Message</interfacename> interface is
<classname>GenericMessage&lt;T&gt;</classname>, and it provides three constructors:
<programlisting>new GenericMessage&lt;T&gt;(Object id, T payload);
new GenericMessage&lt;T&gt;(T payload);
new GenericMessage&lt;T&gt;(T payload, MessageHeader headerToCopy)</programlisting>
When no id is provided, a random unique id will be generated. The constructor that accepts a
<classname>MessageHeader</classname> will copy properties, attributes, and any 'returnAddress' from the
provided header. There are also two convenient subclasses available currently:
<classname>StringMessage</classname> and <classname>ErrorMessage</classname>. The latter accepts any
<classname>Throwable</classname> object as its payload.
</para>
<para>
The <interfacename>Message</interfacename> is obviously a very important part of the API. By encapsulating the
data in a generic wrapper, the messaging system can pass it around without any knowledge of the data's type. As
the system evolves to support new types, or when the types themselves are modified and/or extended, the messaging
system will not be affected by such changes. On the other hand, when some component in the messaging system
<emphasis>does</emphasis> require access to information about the <interfacename>Message</interfacename>, such
metadata can typically be stored to and retrieved from the metadata in the header (the 'properties' and
'attributes').
</para>
</section>
<section id="api-messagechannel">
<title>MessageChannel</title>
<para>
While the <interfacename>Message</interfacename> plays the crucial role of encapsulating data, it is the
<interfacename>MessageChannel</interfacename> that decouples message producers from message consumers.
Spring Integration's <interfacename>MessageChannel</interfacename> interface is defined as follows.
<programlisting><![CDATA[public interface MessageChannel {
String getName();
void setName(String name);
DispatcherPolicy getDispatcherPolicy();
boolean send(Message message);
boolean send(Message message, long timeout);
Message receive();
Message receive(long timeout);
List<Message<?>> clear();
List<Message<?>> purge(MessageSelector selector);
}]]></programlisting>
When sending a message, the return value will be <emphasis>true</emphasis> if the message is sent successfully.
If the send call times out or is interrupted, then it will return <emphasis>false</emphasis>. Likewise when
receiving a message, the return value will be <emphasis>null</emphasis> in the case of a timeout or interrupt.
The <classname>SimpleChannel</classname> implementation wraps a queue. It provides a no-argument constructor as
well as a constructor that accepts the queue capacity:
<programlisting>public SimpleChannel(int capacity)</programlisting>
Specifying a capacity of 0 will create a "direct-handoff" channel where a sender will block until the channel's
<methodname>receive()</methodname> method is called. Otherwise a channel that has not reached its capacity limit
will store messages in its internal queue, and the <methodname>send()</methodname> method will return immediately
even if no receiver is ready to handle the message.
</para>
<para>
Whereas the <classname>SimpleChannel</classname> enforces first-in/first-out (FIFO) ordering, the
<classname>PriorityChannel</classname> is an alternative implementation that allows for messages to be ordered
within the channel based upon a priority. By default the priority is determined by the
'<literal>priority</literal>' property within each message's header. However, for custom priority determination
logic, a comparator of type <classname>Comparator&lt;Message&lt;?&gt;&gt;</classname> can be provided to the
<classname>PriorityChannel</classname>'s constructor.
</para>
</section>
<section id="api-channelinterceptor">
<title>ChannelInterceptor</title>
<para>
One of the advantages of a messaging architecture is the ability to provide common behavior and capture
meaningful information about the messages passing through the system in a non-invasive way. Since the
<interfacename>Messages</interfacename> are being sent to and received from
<interfacename>MessageChannels</interfacename>, those channels provide an opportunity for intercepting
the send and receive operations. The <interfacename>ChannelInterceptor</interfacename> strategy interface
provides methods for each of those operations:
<programlisting><![CDATA[public interface ChannelInterceptor {
boolean preSend(Message<?> message, MessageChannel channel);
void postSend(Message<?> message, MessageChannel channel, boolean sent);
boolean preReceive(MessageChannel channel);
void postReceive(Message<?> message, MessageChannel channel);
}]]></programlisting>
After implementing the interface, registering the interceptor with a channel is just a matter of calling:
<programlisting>channel.addInterceptor(someChannelInterceptor);</programlisting>
The methods that return a <literal>boolean</literal> value can return '<literal>false</literal>' to prevent the
send or receive operation from proceeding (send would return 'false' and receive would return 'null').
</para>
<para>
Because it is rarely necessary to implement all of the interceptor methods, a
<classname>ChannelInterceptorAdapter</classname> class is also available for sub-classing. It provides no-op
methods (the <literal>void</literal> methods are empty, and the <literal>boolean</literal> methods return
<literal>true</literal>). Therefore, it is often easiest to extend that class and just implement the method(s)
that you need as in the following example.
<programlisting><![CDATA[public class CountingChannelInterceptor extends ChannelInterceptorAdapter {
private final AtomicInteger sendCount = new AtomicInteger();
@Override
public boolean preSend(Message<?> message, MessageChannel channel) {
sendCount.incrementAndGet();
return true;
}
}]]></programlisting>
</para>
</section>
<section id="api-messagehandler">
<title>MessageHandler</title>
<para>
So far we have seen that generic message objects are sent-to and received-from simple channel objects. Here is
Spring Integration's callback interface for handling the <interfacename>Messages</interfacename>:
<programlisting>public interface MessageHandler {
Message&lt;?&gt; handle(Message&lt;?&gt; message);
}</programlisting>
The handler plays an important role, since it is typically responsible for translating between the generic
<interfacename>Message</interfacename> objects and the domain objects or primitive values expected by business
components that consume the message payload. That said, developers will rarely need to implement this interface
directly. While that option will always be available, we will soon discuss the higher-level configuration options
including both annotation-driven techniques and XML-based configuration with convenient namespace support.
</para>
</section>
<section id="api-messagebus">
<title>MessageBus</title>
<para>
There is a rather obvious gap in what we have reviewed thus far. The
<interfacename>MessageChannel</interfacename> provides a <methodname>receive()</methodname> method that returns
a <interfacename>Message</interfacename>, and the <interfacename>MessageHandler</interfacename> provides a
<methodname>handle()</methodname> method that accepts a <interfacename>Message</interfacename>, but how do the
messages get passed from the channel to the handler? As mentioned earlier, the <classname>MessageBus</classname>
provides a runtime form of inversion of control, and so the short answer is: you don't need to worry about it.
Nevertheless since this is a reference guide, we will explore this in a bit of detail.
</para>
<para>
The <interfacename>MessageBus</interfacename> is an example of a mediator. It performs a number of roles - mostly
by delegating to other strategies. One of its fundamental responsibilities is to manage registration of the
<interfacename>MessageChannels</interfacename> and <interfacename>MessageHandlers</interfacename>. It provides
the following methods:
<programlisting>public void registerChannel(String name, MessageChannel channel)
public void registerHandler(String name, MessageHandler handler, Subscription subscription)
public void registerHandler(String name, MessageHandler handler, Subscription subscription,
ConcurrencyPolicy concurrencyPolicy)</programlisting>
As those method signatures reveal, the message bus is handling several of the concerns here so that the channel
and handler objects can be as simple as possible. These responsibilities include the creation and lifecycle
management of message dispatchers, the activation of handler subscriptions, and the configuration of thread
pools. The bus coordinates all of that behavior based upon the metadata provided via these registration methods,
and typically developers will not even use this API directly since the metadata can be provided in XML and/or
annotations. We will briefly take a look at each of those metadata objects.
</para>
<para>
The bus creates and manages dispatchers that pull messages from a channel in order to push those messages to
handlers subscribed to that channel. Each channel has a <classname>DispatcherPolicy</classname> that contains
metadata for configuring those dispatchers:
<table id="api-messagebus-dispatcherpolicy">
<title>Properties of the DispatcherPolicy</title>
<tgroup cols="3">
<colspec align="left"/>
<thead>
<row>
<entry align="center">Property Name</entry>
<entry align="center">Default Value</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>publishSubscribe</entry>
<entry>false</entry>
<entry>whether the dispatcher should attempt to publish to all of its handlers (rather than just one)</entry>
</row>
<row>
<entry>maxMessagesPerTask</entry>
<entry>1</entry>
<entry>maximum number of messages to retrieve per poll</entry>
</row>
<row>
<entry>receiveTimeout</entry>
<entry>1000 (milliseconds)</entry>
<entry>how long to block on the receive call (0 for no blocking, -1 for indefinite block)</entry>
</row>
<row>
<entry>rejectionLimit</entry>
<entry>5</entry>
<entry>maximum number of attempts to invoke handlers (e.g. no threads available)</entry>
</row>
<row>
<entry>retryInterval</entry>
<entry>1000 (milliseconds)</entry>
<entry>amount of time to wait between successive attempts to invoke handlers</entry>
</row>
<row>
<entry>shouldFailOnRejectionLimit</entry>
<entry>true</entry>
<entry>whether to throw a <classname>MessageDeliveryException</classname> if the 'rejectionLimit' is
reached - if this is set to 'false', then such undeliverable messages would be dropped silently</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
The bus registers handlers with a channel's dispatcher based upon the <classname>Subscription</classname>
metadata provided to the <methodname>registerHandler()</methodname> method.
<table id="api-messagebus-subscription">
<title>Properties of the Subscription</title>
<tgroup cols="2">
<colspec align="left" />
<thead>
<row>
<entry align="center">Property Name</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>channel</entry>
<entry>the channel instance to subscribe to (an object reference)</entry>
</row>
<row>
<entry>channelName</entry>
<entry>the name of the channel to subscribe to - only used as a fallback if 'channel' is null</entry>
</row>
<row>
<entry>schedule</entry>
<entry>the scheduling metadata (see below)</entry>
</row>
</tbody>
</tgroup>
</table>
The scheduling metadata is provided as an implementation of the <interfacename>Schedule</interfacename>
interface. This is an abstraction designed to allow extensibility of schedulers for messaging tasks. Currently,
there is a single implementation called <classname>PollingSchedule</classname> that provides the following
properties:
<table id="api-messagebus-pollingschedule">
<title>Properties of the PollingSchedule</title>
<tgroup cols="3">
<colspec align="left"/>
<thead>
<row>
<entry align="center">Property Name</entry>
<entry align="center">Default Value</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>period</entry>
<entry>N/A</entry>
<entry>the delay interval between each poll</entry>
</row>
<row>
<entry>initialDelay</entry>
<entry>0</entry>
<entry>the delay prior to the first poll</entry>
</row>
<row>
<entry>timeUnit</entry>
<entry>TimeUnit.MILLISECONDS</entry>
<entry>time unit for 'period' and 'initialDelay'</entry>
</row>
<row>
<entry>fixedRate</entry>
<entry>false</entry>
<entry>'false' indicates fixed-delay (no backlog)</entry>
</row>
</tbody>
</tgroup>
</table>
The <classname>PollingSchedule</classname> constructor requires the 'period' value.
</para>
<para>
The <classname>ConcurrencyPolicy</classname> is an optional parameter to provide when registering a handler.
When the <interfacename>MessageBus</interfacename> registers a handler, it will use these properties to configure
that handler's thread pool. These parameters are configurable on a per-handler basis since handlers may have
different performance characteristics and may have different expectations with regard to the volume of
throughput. The following table lists the available properties and their default values:
<table id="api-messagebus-concurrencypolicy">
<title>Properties of the ConcurrencyPolicy</title>
<tgroup cols="3">
<colspec align="left"/>
<thead>
<row>
<entry align="center">Property Name</entry>
<entry align="center">Default Value</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>coreSize</entry>
<entry>1</entry>
<entry>the core size of the thread pool</entry>
</row>
<row>
<entry>maxSize</entry>
<entry>10</entry>
<entry>the maximum size the thread pool can reach when under demand</entry>
</row>
<row>
<entry>queueCapacity</entry>
<entry>0</entry>
<entry>capacity of the queue which defers an increase of the pool size</entry>
</row>
<row>
<entry>keepAliveSeconds</entry>
<entry>60</entry>
<entry>how long added threads (beyond core size) should remain idle before being removed from the pool</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section id="api-messageendpoint">
<title>MessageEndpoint</title>
<para>
When <interfacename>MessageHandlers</interfacename> are registered with the <classname>MessageBus</classname>,
the bus assigns the handler to a dispatcher based on the provided schedule as described above. Internally, the
bus is creating and registering an instance that implements the <interfacename>MessageEndpoint</interfacename>
interface. This is where other handler metadata enters the picture (e.g. the concurrency settings). Basically,
you can consider the endpoint to be a composite handler built from a simple implementation of the
<interfacename>MessageHandler</interfacename> along with its metadata. In fact, the
<interfacename>MessageEndpoint</interfacename> does extend the <interfacename>MessageHandler</interfacename>
interface.
<programlisting>public interface MessageEndpoint extends MessageHandler {
String getName();
Subscription getSubscription();
ConcurrencyPolicy getConcurrencyPolicy();
}</programlisting>
</para>
<para>
When using the API, it's simpler to register handlers with metadata and leave the message endpoint as an internal
responsibility of the bus. However, it is possible to create endpoints directly. Spring Integration provides a
single implementation: <classname>DefaultMessageEndpoint</classname>.
</para>
</section>
<section id="api-messageselector">
<title>MessageSelector</title>
<para>
As described above, when a <interfacename>MessageHandler</interfacename> is registered with the message bus, it
is hosted by an endpoint and thereby subscribed to a channel. Often it is necessary to provide additional
<emphasis>dynamic</emphasis> logic to determine what messages the handler should receive. The
<interfacename>MessageSelector</interfacename> strategy interface fulfills that role.
<programlisting><![CDATA[public interface MessageSelector {
boolean accept(Message<?> message);
}]]></programlisting>
A <interfacename>MessageEndpoint</interfacename> can be configured with zero or more selectors, and will only
receive messages that are accepted by each selector. Even though the interface is simple to implement, a couple
common selector implementations are provided. For example, the <classname>PayloadTypeSelector</classname>
provides similar functionality to Datatype Channels (as described in <xref linkend="namespace-channel"/>)
except that in this case the type-matching can be done by the endpoint rather than the channel.
<programlisting><![CDATA[PayloadTypeSelector selector = new PayloadTypeSelector(String.class, Integer.class);
assertTrue(selector.accept(new StringMessage("example")));
assertTrue(selector.accept(new GenericMessage<Integer>(123)));
assertFalse(selector.accept(new GenericMessage<SomeObject>(someObject)));
]]></programlisting>
Another simple but useful <interfacename>MessageSelector</interfacename> provided out-of-the-box is the
<classname>UnexpiredMessageSelector</classname>. As the name suggests, it only accepts messages that have
not yet expired.
</para>
<para>
Essentially, using a selector provides <emphasis>reactive</emphasis> routing whereas the Datatype Channel
and Message Router provide <emphasis>proactive</emphasis> routing. However, selectors accommodate additional
uses. For example, the <interfacename>MessageChannel</interfacename>'s 'purge' method accepts a selector:
<programlisting>channel.purge(someSelector);</programlisting>
There is even a <classname>ChannelPurger</classname> utility class whose purge operation is a good candidate for
Spring's JMX support:
<programlisting>ChannelPurger purger = new ChannelPurger(channel, new ExampleMessageSelector());
purger.purge();</programlisting>
</para>
<para>
Implementations of <interfacename>MessageSelector</interfacename> might provide opportunities for reuse on
channels in addition to endpoints. For that reason, Spring Integration provides a simple selector-wrapping
<interfacename>ChannelInterceptor</interfacename> that accepts one or more selectors in its constructor.
<programlisting>MessageSelectingInterceptor interceptor = new MessageSelectingInterceptor(selector1, selector2);
channel.addInterceptor(interceptor);</programlisting>
</para>
</section>
</chapter>

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="overview">
<title>Spring Integration Overview</title>
<section id="overview-background">
<title>Background</title>
<para>
One of the key themes of the Spring Framework is <emphasis>inversion of control</emphasis>. In its broadest
sense, this means that the framework handles responsibilities on behalf of the components that are managed within
its context. The components themselves are simplified since they are relieved of those responsibilities. For
example, <emphasis>dependency injection</emphasis> relieves the components of the responsibility of locating or
creating their dependencies. Likewise, <emphasis>aspect-oriented programming</emphasis> relieves business
components of generic cross-cutting concerns by modularizing them into reusable aspects. In each case, the end
result is a system that is easier to test, understand, maintain, and extend.
</para>
<para>
Furthermore, the Spring framework and portfolio provide a comprehensive programming model for building
enterprise applications. Developers benefit from the consistency of this model and especially the fact that it is
based upon well-established best practices such as programming to interfaces and favoring composition over
inheritance. Spring's simplified abstractions and powerful support libraries boost developer productivity while
simultaneously increasing the level of testability and portability.
</para>
<para>
Spring Integration is a new member of the Spring portfolio motivated by these same goals and principles. It
extends the Spring programming model into the messaging domain and builds upon Spring's existing enterprise
integration support to provide an even higher level of abstraction. It supports message-driven architectures
where inversion of control applies to runtime concerns, such as <emphasis>when</emphasis> certain business logic
should execute and <emphasis>where</emphasis> the response should be sent. It supports routing and transformation
of messages so that different transports and different data formats can be integrated without impacting
testability. In other words, the messaging and integration concerns are handled by the framework, so business
components are further isolated from the infrastructure and developers are relieved of complex integration
responsibilities.
</para>
<para>
As an extension of the Spring programming model, Spring Integration provides a wide variety of configuration
options including annotations, XML with namespace support, XML with generic "bean" elements, and of course direct
usage of the underlying API. That API is based upon well-defined strategy interfaces and non-invasive, delegating
adapters. Spring Integration's design is inspired by the recognition of a strong affinity between common patterns
within Spring and the well-known <ulink url="http://www.eaipatterns.com">Enterprise Integration Patterns</ulink>
as described in the book of the same name by Gregor Hohpe and Bobby Woolf (Addison Wesley, 2003). Developers who
have read that book should be immediately comfortable with the Spring Integration concepts and terminology.
</para>
</section>
<section id="overview-goalsandprinciples">
<title>Goals and Principles</title>
<para>Spring Integration is motivated by the following goals:
<itemizedlist>
<listitem>
Provide a simple model for implementing complex enterprise integration solutions.
</listitem>
<listitem>
Facilitate asynchronous, message-driven behavior within a Spring-based application.
</listitem>
<listitem>
Promote intuitive, incremental adoption for existing Spring users.
</listitem>
</itemizedlist>
</para>
<para>Spring Integration is guided by the following principles:
<itemizedlist>
<listitem>
Components should be <emphasis>loosely coupled</emphasis> for modularity and testability.
</listitem>
<listitem>
The framework should enforce <emphasis>separation of concerns</emphasis> between business logic and
integration logic.
</listitem>
<listitem>
Extension points should be abstract in nature but within well-defined boundaries to promote
<emphasis>reuse</emphasis> and <emphasis>portability</emphasis>.
</listitem>
</itemizedlist>
</para>
</section>
<section id="overview-components">
<title>Main Components</title>
<para>
From the <emphasis>vertical</emphasis> perspective, a layered architecture facilitates separation of concerns,
and interface-based contracts between layers promote loose coupling. Spring-based applications are typically
designed this way, and the Spring framework and portfolio provide a strong foundation for following this best
practice for the full-stack of an enterprise application. Message-driven architectures add a
<emphasis>horizontal</emphasis> perspective, yet these same goals are still relevant. Just as "layered
architecture" is an extremely generic and abstract paradigm, messaging systems typically follow the similarly
abstract "pipes-and-filters" model. The "filters" represent any component that is capable of producing and/or
consuming messages, and the "pipes" transport the messages between filters so that the components themselves
remain loosely-coupled. It is important to note that these two high-level paradigms are not mutually exclusive.
The underlying messaging infrastructure that supports the "pipes" should still be encapsulated in a layer whose
contracts are defined as interfaces. Likewise, the "filters" themselves would typically be managed within a layer
that is logically above the application's service layer, interacting with those services through interfaces much
in the same way that a web-tier would.
</para>
<section id="overview-components-message">
<title>Message</title>
<para>
In Spring Integration, a Message is a generic wrapper for any Java object combined with metadata used by the
framework while handling that object. It consists of a payload and header and has a unique identifier. The
payload can be of any type and the header holds commonly required information such as timestamp, expiration,
and return address. Developers can also store any arbitrary key-value properties or attributes in the header.
</para>
</section>
<section id="overview-components-channel">
<title>Message Channel</title>
<para>
A Message Channel represents the "pipe" of a pipes-and-filters architecture. Producers send Messages to
a MessageChannel, and consumers receive Messages from a MessageChannel. The send and receive methods both come
in two forms: one that blocks indefinitely and one that accepts a timeout (for an immediate return, specify a
timeout value of 0). There are two main types of channels: <emphasis>Point-to-Point</emphasis> channels where
typically a single consumer will receive the Message and <emphasis>Publish-Subscribe</emphasis> channels where
all subscribers should receive the Message.
</para>
</section>
<section id="overview-components-endpoint">
<title>Message Endpoint</title>
<para>
A Message Endpoint represents the "filter" of a pipes-and-filters architecture. The endpoint's primary role is
to connect application code to the messaging framework and to do so in a non-invasive manner. In other words,
the application code should have no awareness of the messaging framework. This is similar to the role of a
Controller in the MVC paradigm. Just as a Controller handles HTTP requests, the endpoint handles Messages. Just
as Controllers are mapped to URL patterns, endpoints are mapped to Message Channels. The goal is the same in
both cases: isolate application code from the infrastructure. In Spring Integration, the Message Endpoint
"hosts" and delegates to a <interfacename>MessageHandler</interfacename> strategy interface as described in
<xref linkend="api-messagehandler"/>.
</para>
</section>
<section id="overview-component-router">
<title>Message Router</title>
<para>
A Message Router is a particular type of <interfacename>MessageHandler</interfacename> that is capable of
receiving a Message and then deciding what channel or channels should receive the Message next. Typically the
decision is based upon the Message's content and/or metadata. A Message Router is often used as a dynamic
alternative to configuring the input and output channels for an endpoint.
</para>
</section>
<section id="overview-component-channeladapter">
<title>Channel Adapter</title>
<para>
A Channel Adapter is used to connect components to a Message Channel when those components are not themselves
Message Endpoints. These adapters provide a mechanism for connecting to external systems, such as JMS queues
or a File system. Channel Adapters may be configured for input and/or output. An input (source) adapter will
receive (or poll for) data, convert that data to a Message, and then send that Message to its Message Channel.
An output (target) adapter is simply another type of <interfacename>MessageHandler</interfacename>, but when it
receives a Message, it will convert it to the target's expected type and then "send" it (publish to a JMS
queue, write to a File, etc.).
</para>
</section>
<section id="overview-component-bus">
<title>Message Bus</title>
<para>
The Message Bus acts as a registry for Message Channels and Message Endpoints. It also encapsulates the
complexity of message retrieval and dispatching. Essentially, the Message Bus forms a logical extension of the
Spring application context into the messaging domain. For example, it will automatically detect Message Channel
and Message Endpoint components from within the application context. It handles the scheduling of pollers, the
creation of thread pools, and the lifecycle management of all messaging components that can be initialized,
started, and stopped. The Message Bus is the primary example of inversion of control within Spring Integration.
</para>
</section>
</section>
</chapter>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="resources">
<title>Additional Resources</title>
<section id="resources-home">
<title>Spring Integration Home</title>
<para>
The definitive source of information about Spring Integration is the
<ulink url="http://www.springframework.org/spring-integration">Spring Integration Home</ulink> at
<ulink url="http://www.springframework.org">http://www.springframework.org</ulink>. That site serves as a hub of
information and is the best place to find up-to-date announcements about the project as well as links to
articles, blogs, and new sample applications.
</para>
</section>
</chapter>

View File

@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="samples">
<title>Spring Integration Samples</title>
<section id="samples-cafe">
<title>The Cafe Sample</title>
<para>
In this section, we will review a sample application that is included in the Spring Integration Milestone 1
release. This sample is inspired by one of the samples featured in Gregor Hohpe's
<ulink url="http://www.eaipatterns.com/ramblings.html">Ramblings</ulink>.
</para>
<para>
The domain is that of a Cafe, and the basic flow is depicted in the following diagram:
</para>
<para>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/cafe-demo.png" format="PNG"/>
</imageobject>
</mediaobject>
</para>
<para>
The <classname>DrinkOrder</classname> object may contain multiple <classname>Drinks</classname>. Once the order
is placed, a <emphasis>Splitter</emphasis> will break the composite order message into a single message per
drink. Each of these is then processed by a <emphasis>Router</emphasis> that determines whether the drink is hot
or cold (checking the <classname>Drink</classname> object's 'isIced' property). Finally the
<classname>Barista</classname> prepares each drink, but hot and cold drink preparation are handled by two
distinct methods: 'prepareHotDrink' and 'prepareColdDrink'.
</para>
<para>
Here is the XML configuration:
<programlisting><![CDATA[<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<message-bus/>
<annotation-driven/>
<context:component-scan base-package="org.springframework.integration.samples.cafe"/>
<channel id="orders"/>
<channel id="drinks"/>
<channel id="coldDrinks"/>
<channel id="hotDrinks"/>
<endpoint input-channel="coldDrinks" handler-ref="barista" handler-method="prepareColdDrink"/>
<endpoint input-channel="hotDrinks" handler-ref="barista" handler-method="prepareHotDrink"/>
<beans:bean id="cafe" class="org.springframework.integration.samples.cafe.Cafe">
<beans:property name="orderChannel" ref="orders"/>
</beans:bean>
</beans:beans>]]></programlisting>
Notice that the Message Bus is defined. It will automatically detect and register all channels and endpoints.
The 'annotation-driven' element will enable the detection of the splitter and router - both of which carry
the <interfacename>@MessageEndpoint</interfacename> annotation. That annotation extends Spring's
"stereotype" annotations (by relying on the @Component meta-annotation), and so all classes carrying the
endpoint annotation are capable of being detected by the component-scanner.
<programlisting><![CDATA[@MessageEndpoint(input="orders")
public class OrderSplitter {
@Splitter(channel="drinks")
public List<Drink> split(DrinkOrder order) {
return order.getDrinks();
}
}]]></programlisting>
<programlisting><![CDATA[@MessageEndpoint(input="drinks")
public class DrinkRouter {
@Router
public String resolveDrinkChannel(Drink drink) {
return (drink.isIced()) ? "coldDrinks" : "hotDrinks";
}
}]]></programlisting>
</para>
<para>
Now turning back to the XML, you see that there are two &lt;endpoint&gt; elements. Each of these is delegating
to the same <classname>Barista</classname> instance but different methods. The 'barista' could have been
defined in the XML, but instead the <interfacename>@Component</interfacename> annotation is applied:
<programlisting><![CDATA[@Component
public class Barista {
private long hotDrinkDelay = 1000;
private long coldDrinkDelay = 700;
private AtomicInteger hotDrinkCounter = new AtomicInteger();
private AtomicInteger coldDrinkCounter = new AtomicInteger();
public void setHotDrinkDelay(long hotDrinkDelay) {
this.hotDrinkDelay = hotDrinkDelay;
}
public void setColdDrinkDelay(long coldDrinkDelay) {
this.coldDrinkDelay = coldDrinkDelay;
}
public void prepareHotDrink(Drink drink) {
try {
Thread.sleep(this.hotDrinkDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("prepared hot drink #" + hotDrinkCounter.incrementAndGet() + ": " + drink);
}
public void prepareColdDrink(Drink drink) {
try {
Thread.sleep(this.coldDrinkDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("prepared cold drink #" + coldDrinkCounter.incrementAndGet() + ": " + drink);
}
}]]></programlisting>
</para>
<para>
As you can see from the code excerpt above, the barista methods have different delays. This simulates work being
completed at different rates. When the <classname>CafeDemo</classname> 'main' method runs, it will loop 100
times sending a single hot drink and a single cold drink each time.
<programlisting><![CDATA[public static void main(String[] args) {
AbstractApplicationContext context = null;
if(args.length > 0) {
context = new FileSystemXmlApplicationContext(args);
}
else {
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
}
context.start();
Cafe cafe = (Cafe) context.getBean("cafe");
DrinkOrder order = new DrinkOrder();
Drink hotDoubleLatte = new Drink(DrinkType.LATTE, 2, false);
Drink icedTripleMocha = new Drink(DrinkType.MOCHA, 3, true);
order.addDrink(hotDoubleLatte);
order.addDrink(icedTripleMocha);
for (int i = 0; i < 100; i++) {
cafe.placeOrder(order);
}
}]]></programlisting>
</para>
<para>
To run this demo, go to the "samples" directory within the root of the Spring Integration distribution. On
Unix/Mac you can run 'cafeDemo.sh', and on Windows you can run 'cafeDemo.bat'. Each of these will by default
create a Spring <interfacename>ApplicationContext</interfacename> from the 'cafeDemo.xml' file that is
in the "spring-integration-samples" JAR and hence on the classpath (it is the same as the XML above). However, a
copy of that file is also available within the "samples" directory, so that you can provide the file name as a
command line argument to either 'cafeDemo.sh' or 'cafeDemo.bat'. This will allow you to experiment with the
configuration and immediately run the demo with your changes. It is probably a good idea to first copy the
original file so that you can make as many changes as you want and still refer back to the original to compare.
</para>
<para>
When you run cafeDemo, you will see that all 100 cold drinks are prepared in roughly the same amount of time as
only 70 of the hot drinks. This is to be expected based on their respective delays of 700 and 1000 milliseconds.
However, by configuring the endpoint concurrency, you can dramatically change the results. For example, on my
machine, the following single modification causes all 100 hot drinks to be prepared before the 4th cold drink is
ready:
<programlisting><![CDATA[<endpoint input-channel="coldDrinks" handler-ref="barista" handler-method="prepareColdDrink"/>
<endpoint input-channel="hotDrinks" handler-ref="barista" handler-method="prepareHotDrink">
]]><emphasis><![CDATA[<concurrency core="25" max="50"/>]]></emphasis><![CDATA[
</endpoint>]]></programlisting>
</para>
<para>
In addition to experimenting with the 'concurrency' settings, you can also try adding the 'schedule' sub-element
as described in <xref linkend="namespace-endpoint"/>. Additionally, you can experiment with the channel's
configuration, such as adding a 'dispatcher-policy' as described in <xref linkend="namespace-channel"/>. If you
want to explore the sample in more detail, the source JAR is available in the "dist" directory:
'spring-integration-samples-sources-1.0.0.m1.jar'.
</para>
</section>
</chapter>

View File

@@ -0,0 +1,46 @@
<?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" [
<!ENTITY overview SYSTEM "overview.xml">
<!ENTITY core-api SYSTEM "core-api.xml">
<!ENTITY adapters SYSTEM "adapters.xml">
<!ENTITY configuration SYSTEM "configuration.xml">
<!ENTITY samples SYSTEM "samples.xml">
<!ENTITY resources SYSTEM "resources.xml">
]>
<book>
<bookinfo>
<title>Spring Integration Reference Manual</title>
<productname>Spring Integration</productname>
<releaseinfo>1.0.0.m2 (Milestone 2)</releaseinfo>
<mediaobject>
<imageobject role="fo">
<imagedata align="center" fileref="images/logo.png" format="PNG"/>
</imageobject>
<imageobject role="html">
<imagedata align="right" fileref="images/logo.png" format="PNG"/>
</imageobject>
</mediaobject>
<authorgroup>
<author>
<firstname>Mark</firstname>
<surname>Fisher</surname>
</author>
</authorgroup>
<legalnotice>Copyright &copy; SpringSource Inc., 2008</legalnotice>
</bookinfo>
<toc></toc>
&overview;
&core-api;
&adapters;
&configuration;
&samples;
&resources;
</book>