Files
spring-integration/spring-integration-reference/src/channel.xml

355 lines
21 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="channel">
<title>Message Channels</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.
</para>
<section id="channel-interfaces">
<title>The MessageChannel Interface</title>
<para>
Spring Integration's top-level <interfacename>MessageChannel</interfacename> interface is defined as follows.
<programlisting language="java"><![CDATA[public interface MessageChannel {
String getName();
boolean send(Message message);
boolean send(Message message, long timeout);
}]]></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>.
</para>
<section id="channel-interfaces-pollablechannel">
<title>PollableChannel</title>
<para>
Since Message Channels may or may not buffer Messages (as discussed in the overview), there are two
sub-interfaces defining the buffering (pollable) and non-buffering (subscribable) channel behavior. Here is the
definition of <interfacename>PollableChannel</interfacename>.
<programlisting language="java">public interface PollableChannel extends MessageChannel {
Message&lt;?&gt; receive();
Message&lt;?&gt; receive(long timeout);
List&lt;Message&lt;?&gt;&gt; clear();
List&lt;Message&lt;?&gt;&gt; purge(MessageSelector selector);
}</programlisting>
Similar to the send methods, when receiving a message, the return value will be <emphasis>null</emphasis> in the
case of a timeout or interrupt.
</para>
</section>
<section id="channel-interfaces-subscribablechannel">
<title>SubscribableChannel</title>
<para>
The <interfacename>SubscribableChannel</interfacename> base interface is implemented by channels that send
Messages directly to their subscribed consumers. Therefore, they do not provide receive methods for polling, but
instead define methods for handling those subscribers:
<programlisting language="java">public interface SubscribableChannel extends MessageChannel {
boolean subscribe(MessageConsumer consumer);
boolean unsubscribe(MessageConsumer consumer);
}</programlisting>
</para>
</section>
</section>
<section id="channel-implementations">
<title>Message Channel Implementations</title>
<para>
Spring Integration provides several different Message Channel implementations. Each is briefly described in the
sections below.
</para>
<section id="channel-implementations-publishsubscribechannel">
<title>PublishSubscribeChannel</title>
<para>
The <classname>PublishSubscribeChannel</classname> implementation broadcasts any Message
sent to it to all of its subscribed consumers. This is most often used for sending
<emphasis>Event Messages</emphasis> whose primary role is notification as opposed to
<emphasis>Document Messages</emphasis> which are generally intended to be processed by
a single consumer. Note that the <classname>PublishSubscribeChannel</classname> is
intended for sending only. Since it broadcasts to its subscribers directly when its
<methodname>send(Message)</methodname> method is invoked, consumers cannot poll for
Messages (it does not implement <interfacename>PollableChannel</interfacename> and
therefore has no <methodname>receive()</methodname> method). Instead, any subscriber
must be a <interfacename>MessageConsumer</interfacename> itself, and the subscriber's
<methodname>send(Message)</methodname> method will be invoked in turn.
</para>
</section>
<section id="channel-implementations-queuechannel">
<title>QueueChannel</title>
<para>
The <classname>QueueChannel</classname> implementation wraps a queue. Unlike, the
<classname>PublishSubscribeChannel</classname>, the <classname>QueueChannel</classname> has point-to-point
semantics. In other words, even if the channel has multiple consumers, only one of them should receive any
Message sent to that channel. It provides a default no-argument constructor (providing an essentially unbounded
capacity of <code>Integer.MAX_VALUE</code>) as well as a constructor that accepts the queue capacity:
<programlisting language="java">public QueueChannel(int capacity)</programlisting>
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. If the queue has reached capacity, then the sender will block until room is available. Likewise, a
receive call will return immediately if a message is available on the queue, but if the queue is empty, then
a receive call may block until either a message is available or the timeout elapses. In either case, it is
possible to force an immediate return regardless of the queue's state by passing a timeout value of 0.
Note however, that calling the no-arg versions of <methodname>send()</methodname> and
<methodname>receive()</methodname> will block indefinitely.
</para>
</section>
<section id="channel-implementations-prioritychannel">
<title>PriorityChannel</title>
<para>
Whereas the <classname>QueueChannel</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>' header within each message. 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="channel-implementations-rendezvouschannel">
<title>RendezvousChannel</title>
<para>
The <classname>RendezvousChannel</classname> enables a "direct-handoff" scenario where a sender will block
until another party invokes the channel's <methodname>receive()</methodname> method or vice-versa. Internally,
this implementation is quite similar to the <classname>QueueChannel</classname> except that it uses a
<classname>SynchronousQueue</classname> (a zero-capacity implementation of
<interfacename>BlockingQueue</interfacename>). This works well in situations where the sender and receiver are
operating in different threads but simply dropping the message in a queue asynchronously is too dangerous. For
example, the sender's thread could roll back a transaction if the send operation times out, whereas with a
<classname>QueueChannel</classname>, the message would have been stored to the internal queue and potentially
never received.
</para>
<para>
The <classname>RendezvousChannel</classname> is also useful for implementing request-reply
operations. The sender can create a temporary, anonymous instance of <classname>RendezvousChannel</classname>
which it then sets as the 'replyChannel' header when building a Message. After sending that Message, the sender
can immediately call receive (optionally providing a timeout value) in order to block while waiting for a reply
Message.
</para>
</section>
<section id="channel-implementations-directchannel">
<title>DirectChannel</title>
<para>
The <classname>DirectChannel</classname> has point-to-point semantics, but otherwise is more similar to the
<classname>PublishSubscribeChannel</classname> than any of the queue-based channel implementations described
above. It implements the <interfacename>SubscribableChannel</interfacename> interface instead of the
<interfacename>PollableChannel</interfacename> interface, so it dispatches Messages directly to a subscriber.
As a point-to-point channel, however, it differs from the <classname>PublishSubscribeChannel</classname> in
that it will only send each Message to a <emphasis>single</emphasis> subscribed
<classname>MessageConsumer</classname>. Its primary purpose is to enable a single thread to perform the
operations on "both sides" of the channel. For example, if a consumer is subscribed to a
<classname>DirectChannel</classname>, then sending a Message to that channel will trigger invocation of that
consumer's <methodname>onMessage(Message)</methodname> method <emphasis>directly in the sender's
thread</emphasis>. The key motivation for providing a channel implementation with this behavior is to support
transactions that must span across the channel while still benefiting from the abstraction and loose coupling
that the channel provides. If the send call is invoked within the scope of a transaction, then the outcome of
the consumer's invocation (e.g. updating a database record) can play a role in determining the ultimate result
of that transaction (commit or rollback).
<note>
Since the <classname>DirectChannel</classname> is the simplest option and does not add any additional
overhead that would be required for scheduling and managing the threads of a poller, it is the default
channel type within Spring Integration. The general idea is to define the channels for an application and
then to consider which of those needs to provide buffering to throttle input, and to modify those to be
queue-based <interfacename>PollableChannels</interfacename>. Likewise, if a channel needs to broadcast
messages, it should not be a <classname>DirectChannel</classname> but rather a
<classname>PublishSubscribeChannel</classname>. Below you will see how these can be configured.
</note>
</para>
</section>
<section id="channel-implementations-threadlocalchannel">
<title>ThreadLocalChannel</title>
<para>
The final channel implementation type is <classname>ThreadLocalChannel</classname>. This channel also delegates
to a queue internally, but the queue is bound to the current thread. That way the thread that sends to the
channel will later be able to receive those same Messages, but no other thread would be able to access them.
While probably the least common type of channel, this is useful for situations where
<classname>DirectChannels</classname> are being used to enforce a single thread of operation but any reply
Messages should be sent to a "terminal" channel. If that terminal channel is a
<classname>ThreadLocalChannel</classname>, the original sending thread can collect its replies from it.
</para>
</section>
</section>
<section id="channel-interceptors">
<title>Channel Interceptors</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 language="java"><![CDATA[public interface ChannelInterceptor {
Message<?> preSend(Message<?> message, MessageChannel channel);
void postSend(Message<?> message, MessageChannel channel, boolean sent);
boolean preReceive(MessageChannel channel);
Message<?> postReceive(Message<?> message, MessageChannel channel);
}]]></programlisting>
After implementing the interface, registering the interceptor with a channel is just a matter of calling:
<programlisting language="java">channel.addInterceptor(someChannelInterceptor);</programlisting>
The methods that return a Message instance can be used for transforming the Message or can return 'null'
to prevent further processing (of course, any of the methods can throw an Exception). Also, the
<methodname>preReceive</methodname> method can return '<literal>false</literal>' to prevent the receive
operation from proceeding.
</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> method is empty, the <classname>Message</classname> returning methods
return the Message parameter as-is, and the <literal>boolean</literal> method returns <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 language="java"><![CDATA[public class CountingChannelInterceptor extends ChannelInterceptorAdapter {
private final AtomicInteger sendCount = new AtomicInteger();
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
sendCount.incrementAndGet();
return message;
}
}]]></programlisting>
</para>
</section>
<section id="channel-template">
<title>MessageChannelTemplate</title>
<para>
As you will see when the endpoints and their various configuration options are introduced, Spring Integration
provides a foundation for messaging components that enables non-invasive invocation of your application code
<emphasis>from the messaging system</emphasis>. However, sometimes it is necessary to invoke the messaging system
<emphasis>from your application code</emphasis>. For convenience when implementing such use-cases, Spring
Integration provides a <classname>MessageChannelTemplate</classname> that supports a variety of operations across
the Message Channels, including request/reply scenarios. For example, it is possible to send a request
and wait for a reply.
<programlisting language="java">MessageChannelTemplate template = new MessageChannelTemplate();
Message reply = template.sendAndReceive(new StringMessage("test"), someChannel);</programlisting>
In that example, a temporary anonymous channel would be created internally by the template. The
'sendTimeout' and 'receiveTimeout' properties may also be set on the template, and other exchange
types are also supported.
<programlisting language="java"><![CDATA[public boolean send(final Message<?> message, final MessageChannel channel) { ... }
public Message<?> sendAndReceive(final Message<?> request, final MessageChannel channel) { .. }
public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programlisting>
</para>
</section>
<section id="channel-configuration">
<title>Configuring Message Channels</title>
<para>
To create a Message Channel instance, you can use the 'channel' element:
<programlisting language="xml">&lt;channel id="exampleChannel"/&gt;</programlisting>
</para>
<para>
The default channel type is <emphasis>Point to Point</emphasis>. To create a
<emphasis>Publish Subscribe</emphasis> channel, use the "publish-subscribe-channel" element:
<programlisting language="xml">&lt;publish-subscribe-channel id="exampleChannel"/&gt;</programlisting>
</para>
<para>
To create a <ulink url="http://www.eaipatterns.com/DatatypeChannel.html">Datatype Channel</ulink> that only
accepts messages containing a certain payload type, provide the fully-qualified class name in the
channel element's <literal>datatype</literal> attribute:
<programlisting language="xml"><![CDATA[<channel id="numberChannel" datatype="java.lang.Number"/>]]></programlisting>
Note that the type check passes for any type that is <emphasis>assignable</emphasis> to the channel's
datatype. In other words, the "numberChannel" above would accept messages whose payload is
<classname>java.lang.Integer</classname> or <classname>java.lang.Double</classname>. Multiple types can be
provided as a comma-delimited list:
<programlisting language="xml"><![CDATA[<channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
</para>
<para>
When using the "channel" element without any sub-elements, it will create a <classname>DirectChannel</classname>
instance (a <interfacename>SubscribableChannel</interfacename>).
</para>
<para>
However, you can also provide a variety of "queue" sub-elements to create the channel types (as described in
<xref linkend="channel-implementations"/>). Examples of each are shown below.
</para>
<section id="channel-configuration-directchannel">
<title>DirectChannel Configuration</title>
<para>
As mentioned above, <classname>DirectChannel</classname> is the default type.
<programlisting language="xml"><![CDATA[<channel id="exampleChannel"/>]]></programlisting>
</para>
</section>
<section id="channel-configuration-queuechannel">
<title>QueueChannel Configuration</title>
<para>
To create a <classname>QueueChannel</classname>, use the "queue" sub-element.
You must specify the channel's capacity:
<programlisting language="xml">&lt;channel id="exampleChannel"&gt;
&lt;queue capacity="25"/&gt;
&lt;/channel&gt;</programlisting>
</para>
</section>
<section id="channel-configuration-pubsubchannel">
<title>PublishSubscribeChannel Configuration</title>
<para>
To create a <classname>PublishSubscribeChannel</classname>, use the "publish-subscribe-channel" element.
When using this element, you can also specify the "task-executor" used for publishing
Messages (if none is specified it simply publishes in the sender's thread):
<programlisting language="xml">&lt;publish-subscribe-channel id="exampleChannel" task-executor="someTaskExecutor"/&gt;</programlisting>
</para>
</section>
<section id="channel-configuration-prioritychannel">
<title>PriorityChannel Configuration</title>
<para>
To create a <classname>PriorityChannel</classname>, use the "priority-queue" sub-element:
<programlisting language="xml"><![CDATA[<channel id="exampleChannel">
<priority-queue capacity="20"/>
</channel>]]></programlisting>
By default, the channel will consult the <classname>MessagePriority</classname> header of the
message. However, a custom <interfacename>Comparator</interfacename> reference may be
provided instead. Also, note that the <classname>PriorityChannel</classname> (like the other types)
does support the "datatype" attribute. As with the QueueChannel, it also supports a "capacity" attribute.
The following example demonstrates all of these:
<programlisting language="xml"><![CDATA[<channel id="exampleChannel" datatype="example.Widget">
<priority-queue comparator="widgetComparator"
capacity="10"/>
</channel>
]]></programlisting>
</para>
</section>
<section id="channel-configuration-rendezvouschannel">
<title>RendezvousChannel Configuration</title>
<para>
The <classname>RendezvousChannel</classname> does not provide any additional configuration options.
<programlisting language="xml"><![CDATA[<channel id="exampleChannel"/>
<rendezvous-queue/>
</channel>
]]></programlisting>
</para>
</section>
<section id="channel-configuration-threadlocalchannel">
<title>ThreadLocalChannel Configuration</title>
<para>
The <classname>ThreadLocalChannel</classname> does not provide any additional configuration options.
<programlisting language="xml"><![CDATA[<thread-local-channel id="exampleChannel"/>]]></programlisting>
</para>
</section>
<para>
Message channels may also have interceptors as described in <xref linkend="channel-interceptors"/>. One or
more &lt;interceptor&gt; elements can be added as sub-elements of &lt;channel&gt; (or the more specific element
types). Provide the "ref" attribute to reference any Spring-managed object that implements the
<interfacename>ChannelInterceptor</interfacename> interface:
<programlisting language="xml"><![CDATA[<channel id="exampleChannel">
]]><emphasis><![CDATA[<interceptor ref="trafficMonitoringInterceptor"/>]]></emphasis><![CDATA[
</channel>]]></programlisting>
In general, it is a good idea to define the interceptor implementations in a separate location since they
usually provide common behavior that can be reused across multiple channels.
</para>
</section>
</chapter>