This is a significant update to the build system, including the changes listed below. README.md has been updated with instructions on the most important day-to-day commands. - Eliminate buildSrc submodule In favor of using the new bundlor and docbook-reference plugins. The net effect is a large reduction in number of lines of build code. Common docbook resources, stylesheets, etc are stored directly in the docbook plugin. This means that --recursive is no longer required when cloning and there will never be a need to use `git submodule` commands. README files have been updated to reflect. Use of the new bundlor plugin also means the removal of template.mf files from the source tree in favor of an inline approach. See build.gradle for details. Bundlor 'import templates' are built up programmatically and kept physically close to gradle dependency declarations, leading to more convenience when changing these values and hopefully fewer errors / version inconsistencies over time. Certain tests depended on the presence of template.mf files, all of which have recently been removed from the source tree in favor of the new bundlor plugin which allows for inlining bundlor configuration within the Gradle build script. These tests now create temp files using the java.io.File API instead. - Upgrade to Gradle 1.0-milestone-6 The m6 release is significantly faster when resolving dependencies and has a number of valuable new features over the earlier m3 version. Review the release notes for Gradle 1.0-milestone-6 online for full details. - Switch to repo.springsource.org repository Previously the project build declared as many repositories as necessary to resolve all project dependencies. Now depending on a single 'virtual repository' defined within the SpringSource Artifactory instance at http://repo.springsource.org. Currently, the virtual repository in use is 'libs-milestone', which allows for the resolution of all "milestone-or-better" versions of all S2 and third-party dependencies. Should snapshot dependencies become required, this value may be changed from 'libs-milestone' to 'libs-snapshot'. To build only against GA releases, change the value to 'libs-release'. - New build plan(s) Spring Integration build plans have been updated to use the Artifactory Bamboo plugin and publish to repo.springsource.org. Build plans have names like 2.1.x to reflect the version under development, not necessarily the name of the branch, as this may change over time and across major releases. - Improve release process As mentioned above, Spring Integration will now use the Artifactory Bamboo plugin to publish releases and also use Artifactory's support for pushing builds directly into Maven Central via oss.sonatype.org. Generate poms that contain all necessary fields for onboarding at Maven central (scm, developers, organization, licenses, etc). Generate -source and -javadoc poms to comply with Maven Central onboarding rules (and for general good practice anyway). Generation of PGP signatures, sha1 and md5 checksums are all handled automatically by Artifactory. These are also requirements for automated entry into Maven Central. - Remove source-level pom generation Automatic generation of Maven poms suitable for use in building Spring Integration is no longer supported. Generation and publication of poms for the purpose of dependency management remains supported. Sonar support has to date depended on these poms, but will be switched over to use the Gradle Sonar plugin shortly. - Eliminate docs subproject Move docs/src to the root of the project and eliminate docs as a formal subproject. This simplifies the build in a number of ways, including removing the need for distinguishing between 'subprojects' and 'javaprojects' as well as allowing users to build both 'api' and 'reference' docs without qualifying with a ':docs' prefix. Also rename the src/info directory to src/dist to better reflect that these files are packaged with the distribution. For example, the readme.txt there is really the distribution readme, distinct from the README.md at the root of the project which is for building from source, etc.
808 lines
51 KiB
XML
808 lines
51 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="channel"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<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 {
|
||
|
||
boolean send(Message message);
|
||
|
||
boolean send(Message message, long timeout);
|
||
}]]></programlisting>
|
||
</para>
|
||
<para>
|
||
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<?> receive();
|
||
|
||
Message<?> receive(long timeout);
|
||
|
||
}</programlisting>
|
||
</para>
|
||
<para>
|
||
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 <interfacename>MessageHandler</interfacename>s. Therefore, they do not
|
||
provide receive methods for polling, but instead define methods for managing those subscribers:
|
||
<programlisting language="java">public interface SubscribableChannel extends MessageChannel {
|
||
|
||
boolean subscribe(MessageHandler handler);
|
||
|
||
boolean unsubscribe(MessageHandler handler);
|
||
|
||
}</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 handlers. 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 handler. 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>MessageHandler</interfacename> itself, and the subscriber's
|
||
<methodname>handleMessage(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>
|
||
</para>
|
||
<para>
|
||
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. Or, if using
|
||
the send call that accepts a timeout, it will block until either room is available or the timeout period
|
||
elapses, whichever occurs first. 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 calls to 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<Message<?>></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 not appropriate.
|
||
In other words, with a <classname>RendezvousChannel</classname> at least the sender knows that some receiver
|
||
has accepted the message, whereas with a <classname>QueueChannel</classname>, the message would have been
|
||
stored to the internal queue and potentially never received.
|
||
</para>
|
||
<tip>
|
||
<para>
|
||
Keep in mind that all of these queue-based channels are storing messages in-memory only by default.
|
||
When persistence is required, you can either provide a 'message-store' attribute within the 'queue'
|
||
element to reference a persistent MessageStore implementation, or you can replace the local channel
|
||
with one that is backed by a persistent broker, such as a JMS-backed channel or Channel Adapter.
|
||
The latter option allows you to take advantage of any JMS provider's
|
||
implementation for message persistence, and it will be discussed in <xref linkend="jms"/>. However, when
|
||
buffering in a queue is not necessary, the simplest approach is to rely upon the
|
||
<classname>DirectChannel</classname> discussed next.
|
||
</para>
|
||
</tip>
|
||
<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. This is very similar to the implementation used internally by many of Spring Integration's
|
||
request-reply components.
|
||
</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>MessageHandler</classname>.
|
||
</para>
|
||
<para>
|
||
In addition to being the simplest point-to-point channel option, one of its most important features is that
|
||
it enables a single thread to perform the operations on "both sides" of the channel. For example, if a handler
|
||
is subscribed to a <classname>DirectChannel</classname>, then sending a Message to that channel will trigger
|
||
invocation of that handler's <methodname>handleMessage(Message)</methodname> method <emphasis>directly in the
|
||
sender's thread</emphasis>, before the send() method invocation can return.
|
||
</para>
|
||
<para>
|
||
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 handler's
|
||
invocation (e.g. updating a database record) will 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 need to provide buffering or to throttle input, and then 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 each of these can be configured.
|
||
</note>
|
||
</para>
|
||
<para>
|
||
The <classname>DirectChannel</classname> internally delegates to a Message Dispatcher to invoke its
|
||
subscribed Message Handlers, and that dispatcher can have a load-balancing strategy. The load-balancer
|
||
determines how invocations will be ordered in the case that there are multiple handlers subscribed to the
|
||
same channel. When using the namespace support described below, the default strategy is
|
||
"round-robin" which essentially load-balances across the handlers in rotation.
|
||
<note>
|
||
The "round-robin" strategy is currently the only implementation available out-of-the-box in Spring
|
||
Integration. Other strategy implementations may be added in future versions.
|
||
</note>
|
||
</para>
|
||
<para>
|
||
The load-balancer also works in combination with a boolean <emphasis>failover</emphasis> property.
|
||
If the "failover" value is true (the default), then the dispatcher will fall back to any subsequent
|
||
handlers as necessary when preceding handlers throw Exceptions. The order is determined by an optional
|
||
order value defined on the handlers themselves or, if no such value exists, the order in which the
|
||
handlers are subscribed.
|
||
</para>
|
||
<para>
|
||
If a certain situation requires that the dispatcher always try to invoke the first handler, then
|
||
fallback in the same fixed order sequence every time an error occurs, no load-balancing strategy should
|
||
be provided. In other words, the dispatcher still supports the failover boolean property even when no
|
||
load-balancing is enabled. Without load-balancing, however, the invocation of handlers will always begin
|
||
with the first according to their order. For example, this approach works well when there is a clear
|
||
definition of primary, secondary, tertiary, and so on. When using the namespace support, the "order"
|
||
attribute on any endpoint will determine that order.
|
||
</para>
|
||
<note>
|
||
Keep in mind that load-balancing and failover only apply when a channel has more than one
|
||
subscribed Message Handler. When using the namespace support, this means that more than one
|
||
endpoint shares the same channel reference in the "input-channel" attribute.
|
||
</note>
|
||
</section>
|
||
<section id="executor-channel">
|
||
<title>ExecutorChannel</title>
|
||
<para>
|
||
The <classname>ExecutorChannel</classname> is a point-to-point channel that supports
|
||
the same dispatcher configuration as <classname>DirectChannel</classname> (load-balancing strategy
|
||
and the failover boolean property). The key difference between these two dispatching channel types
|
||
is that the <classname>ExecutorChannel</classname> delegates to an instance of
|
||
<interfacename>TaskExecutor</interfacename> to perform the dispatch. This means that the send method
|
||
typically will not block, but it also means that the handler invocation may not occur in the sender's
|
||
thread. It therefore <emphasis>does not support transactions spanning the sender and receiving
|
||
handler</emphasis>.
|
||
<tip>
|
||
Note that there are occasions where the sender may block. For example, when using a
|
||
TaskExecutor with a rejection-policy that throttles back on the client (such as the
|
||
<code>ThreadPoolExecutor.CallerRunsPolicy</code>), the sender's thread will execute
|
||
the method directly anytime the thread pool is at its maximum capacity and the
|
||
executor's work queue is full. Since that situation would only occur in a non-predictable
|
||
way, that obviously cannot be relied upon for transactions.
|
||
</tip>
|
||
</para>
|
||
</section>
|
||
<section id="channel-implementations-threadlocalchannel">
|
||
<title>Scoped Channel</title>
|
||
<para>
|
||
Spring Integration 1.0 provided a <classname>ThreadLocalChannel</classname> implementation, but that has been removed as of 2.0. Now, there is a more general way for handling the same requirement by simply adding a "scope" attribute to a channel. The value of the attribute can be any name of a Scope that is available within the context. For example, in a web environment, certain Scopes are available, and any custom Scope implementations can be registered with the context. Here's an example of a ThreadLocal-based scope being applied to a channel, including the registration of the Scope itself.</para>
|
||
<programlisting language="xml"><![CDATA[ <int:channel id="threadScopedChannel" scope="thread">
|
||
<int:queue />
|
||
</int:channel>
|
||
|
||
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
|
||
<property name="scopes">
|
||
<map>
|
||
<entry key="thread" value="org.springframework.context.support.SimpleThreadScope" />
|
||
</map>
|
||
</property>
|
||
</bean>
|
||
]]></programlisting>
|
||
<para>
|
||
The channel above also delegates to a queue internally, but the channel is bound
|
||
to the current thread, so the contents of the queue are as well. 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 thread-scoped channels are rarely needed, they can be
|
||
useful in 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 thread-scoped, the original sending thread can collect its replies from it.
|
||
</para>
|
||
<para>
|
||
Now, since any channel can be scoped, you can define your own scopes in addition to Thread Local.
|
||
</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 a RuntimeException). Also, the
|
||
<methodname>preReceive</methodname> method can return '<literal>false</literal>' to prevent the receive
|
||
operation from proceeding.
|
||
<note>
|
||
Keep in mind that <methodname>receive()</methodname> calls are only relevant for
|
||
<interfacename>PollableChannels</interfacename>. In fact the
|
||
<interfacename>SubscribableChannel</interfacename> interface does not even define a
|
||
<methodname>receive()</methodname> method. The reason for this is that when a Message is sent to a
|
||
<interfacename>SubscribableChannel</interfacename> it will be sent directly to one or more subscribers
|
||
depending on the type of channel (e.g. a PublishSubscribeChannel sends to all of its subscribers). Therefore,
|
||
the <methodname>preReceive(..)</methodname> and <methodname>postReceive(..)</methodname> interceptor methods
|
||
are only invoked when the interceptor is applied to a <interfacename>PollableChannel</interfacename>.
|
||
</note>
|
||
Spring Integration also provides an implementation of the
|
||
<ulink url="http://eaipatterns.com/WireTap.html">Wire Tap</ulink> pattern.
|
||
It is a simple interceptor that sends the Message to another channel without otherwise altering the
|
||
existing flow. It can be very useful for debugging and monitoring. An example is shown in
|
||
<xref linkend="channel-wiretap"/>.
|
||
</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 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>
|
||
<tip>
|
||
The order of invocation for the interceptor methods depends on the type of channel. As described above,
|
||
the queue-based channels are the only ones where the receive method is intercepted in the first place.
|
||
Additionally, the relationship between send and receive interception depends on the timing of separate
|
||
sender and receiver threads. For example, if a receiver is already blocked while waiting for a message
|
||
the order could be: preSend, preReceive, postReceive, postSend. However, if a receiver polls after the
|
||
sender has placed a message on the channel and already returned, the order would be: preSend, postSend,
|
||
(some-time-elapses) preReceive, postReceive. The time that elapses in such a case depends on a number
|
||
of factors and is therefore generally unpredictable (in fact, the receive may never happen!).
|
||
Obviously, the type of queue also plays a role (e.g. rendezvous vs. priority). The bottom line is that
|
||
you cannot rely on the order beyond the fact that preSend will precede postSend and preReceive will
|
||
precede postReceive.
|
||
</tip>
|
||
</para>
|
||
</section>
|
||
|
||
<section id="channel-template">
|
||
<title>MessagingTemplate</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>MessagingTemplate</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">MessagingTemplate template = new MessagingTemplate();
|
||
|
||
Message reply = template.sendAndReceive(someChannel, new GenericMessage("test"));</programlisting>
|
||
</para>
|
||
<para>
|
||
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 MessageChannel channel, final Message<?> message) { ... }
|
||
|
||
public Message<?> sendAndReceive(final MessageChannel channel, final Message<?> request) { .. }
|
||
|
||
public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programlisting>
|
||
</para>
|
||
<note>
|
||
<para>
|
||
A less invasive approach that allows you to invoke simple interfaces with payload and/or header
|
||
values instead of Message instances is described in <xref linkend="gateway-proxy"/>.
|
||
</para>
|
||
</note>
|
||
</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"><int:channel id="exampleChannel"/></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"><int:publish-subscribe-channel id="exampleChannel"/></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 alternatively provide a variety of <queue/> sub-elements to create any of
|
||
the pollable 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[<int:channel id="directChannel"/>]]></programlisting>
|
||
</para>
|
||
<para>
|
||
A default channel will have a <emphasis>round-robin</emphasis> load-balancer and will also have
|
||
failover enabled (See the discussion in <xref linkend="channel-implementations-directchannel"/>
|
||
for more detail). To disable one or both of these, add a <dispatcher/> sub-element and
|
||
configure the attributes:
|
||
<programlisting language="xml"><![CDATA[<int:channel id="failFastChannel">
|
||
<int:dispatcher failover="false"/>
|
||
</channel>
|
||
|
||
<int:channel id="channelWithFixedOrderSequenceFailover">
|
||
<int:dispatcher load-balancer="none"/>
|
||
</int:channel>
|
||
]]></programlisting>
|
||
</para>
|
||
</section>
|
||
|
||
<section id="channel-datatype-channel">
|
||
<title>Datatype Channel Configuration</title>
|
||
<para>
|
||
There are times when a consumer can only process a particular type of payload and you need to therefore ensure the payload type of input Messages.
|
||
Of course the first thing that comes to mind is Message Filter. However all that Message Filter will do is filter out Messages that are not compliant with
|
||
the requirements of the consumer. Another way would be to use a Content Based Router and route Messages with non-compliant data-types to specific
|
||
Transformers to enforce transformation/conversion to the required data-type. This of course would work, but a simpler way of accomplishing the
|
||
same thing is to apply the <ulink url="http://www.eaipatterns.com/DatatypeChannel.html">Datatype Channel</ulink> pattern.
|
||
You can use separate Datatype Channels for each specific payload data-type.
|
||
</para>
|
||
<para>
|
||
To create a Datatype Channel 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[<int:channel id="numberChannel" datatype="java.lang.Number"/>]]></programlisting>
|
||
</para>
|
||
<para>
|
||
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[<int:channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
|
||
</para>
|
||
<para>
|
||
So the 'numberChannel' above will only accept Messages with a data-type of <classname>java.lang.Number</classname>. But what happens
|
||
if the payload of the Message is not of the required type? It depends on whether you have defined a bean named "integrationConversionService"
|
||
that is an instance of Spring's <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert-ConversionService-API">Conversion Service</ulink>.
|
||
If not, then an Exception would be thrown immediately, but if you do have an "integrationConversionService" bean defined, it will be used
|
||
in an attempt to convert the Message's payload to the acceptable type.
|
||
</para>
|
||
<para>
|
||
You can even register custom converters. For example, let's say you are sending a Message with a String payload to the 'numberChannel' we configured above.
|
||
<programlisting language="java"><![CDATA[MessageChannel inChannel = context.getBean("numberChannel", MessageChannel.class);
|
||
inChannel.send(new GenericMessage<String>("5"));]]></programlisting>
|
||
</para>
|
||
<para>
|
||
Typically this would be a perfectly legal operation, however since we are using Datatype Channel the result of such operation would generate an exception:
|
||
|
||
<programlisting language="java"><![CDATA[Exception in thread "main" org.springframework.integration.MessageDeliveryException:
|
||
Channel 'numberChannel'
|
||
expected one of the following datataypes [class java.lang.Number],
|
||
but received [class java.lang.String]
|
||
…]]></programlisting>
|
||
</para>
|
||
<para>
|
||
And rightfully so since we are requiring the payload type to be a Number while sending a String. So we need something to convert String to a Number.
|
||
All we need to do is implement a Converter.
|
||
<programlisting language="java"><![CDATA[public static class StringToIntegerConverter implements Converter<String, Integer> {
|
||
public Integer convert(String source) {
|
||
return Integer.parseInt(source);
|
||
}
|
||
}]]></programlisting>
|
||
</para>
|
||
<para>
|
||
Then, register it as a Converter with the Integration Conversion Service:
|
||
<programlisting language="java"><![CDATA[<int:converter ref="strToInt"/>
|
||
|
||
<bean id="strToInt" class="org.springframework.integration.util.Demo.StringToIntegerConverter"/>]]></programlisting>
|
||
</para>
|
||
<para>
|
||
When the 'converter' element is parsed, it will create the "integrationConversionService" bean on-demand if one is not already defined.
|
||
With that Converter in place, the send operation would now be successful since the Datatype Channel will use that Converter to convert the String
|
||
payload to an Integer.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="channel-configuration-queuechannel">
|
||
<title>QueueChannel Configuration</title>
|
||
<para>
|
||
To create a <classname>QueueChannel</classname>, use the <queue/> sub-element.
|
||
You may specify the channel's capacity:
|
||
<programlisting language="xml"><int:channel id="queueChannel">
|
||
<queue capacity="25"/>
|
||
</int:channel></programlisting>
|
||
<note>
|
||
If you do not provide a value for the 'capacity' attribute on this <queue/> sub-element,
|
||
the resulting queue will be unbounded. To avoid issues such as OutOfMemoryErrors, it is highly
|
||
recommended to set an explicit value for a bounded queue.
|
||
</note>
|
||
</para>
|
||
|
||
<para><emphasis>Persistent QueueChannel Configuration</emphasis></para>
|
||
|
||
<para>
|
||
Since a <classname>QueueChannel</classname> provides the capability to buffer Messages, but does so in-memory only
|
||
by default, it also introduces a possibility that Messages could be lost in the event of a system failure. To
|
||
mitigate this risk, a <classname>QueueChannel</classname> may be backed by a persistent implementation of the
|
||
<classname>MessageGroupStore</classname> strategy interface. For more details on <classname>MessageGroupStore</classname>
|
||
and <classname>MessageStore</classname> see <xref linkend="message-store" />.
|
||
</para>
|
||
<para>
|
||
When a <classname>QueueChannel</classname> receives a Message, it will add it to the Message Store, and when a Message
|
||
is polled from a <classname>QueueChannel</classname>, it is removed from the Message Store.
|
||
</para>
|
||
<para>
|
||
By default any <classname>QueueChannel</classname> only stores its Messages in an in-memory Queue
|
||
and can therefore lead to the lost message scenario mentioned above. However Spring Integration
|
||
provides a <classname>JdbcMessageStore</classname> to allow a <classname>QueueChannel</classname> to be backed by an RDBMS.
|
||
</para>
|
||
<para>
|
||
You can configure a Message Store for any <classname>QueueChannel</classname> by adding the
|
||
<code>message-store</code> attribute as shown in the next example.
|
||
|
||
<programlisting language="xml"><![CDATA[<int:channel id="dbBackedChannel">
|
||
<int:queue message-store="messageStore">
|
||
<int:channel id="myChannel">
|
||
|
||
<int-jdbc:message-store id="messageStore" data-source="someDataSource"/>]]></programlisting>
|
||
|
||
The above example also shows that <classname>JdbcMessageStore</classname> can be configured with the namespace support
|
||
provided by the Spring Integration JDBC module. All you need to do is inject any <classname>javax.sql.DataSource</classname>
|
||
instance. The Spring Integration JDBC module also provides schema DDL for most popular databases. These schemas are located in
|
||
the <emphasis>org.springframework.integration.jdbc</emphasis> package of that module (spring-integration-jdbc).
|
||
|
||
<important>
|
||
One important feature is that with any transactional persistent store (e.g., JdbcMessageStore), as long as the poller has a transaction configured,
|
||
a Message removed from the store will only be permanently removed if the transaction completes
|
||
successfully, otherwise the transaction will roll back and the Message will not be lost.
|
||
</important>
|
||
Many other implementations of the Message Store will be available as the growing number of Spring projects
|
||
related to "NoSQL" data stores provide the underlying support. Of course, you can always provide your own implementation
|
||
of the MessageGroupStore interface if you cannot find one that meets your particular needs.
|
||
</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 <code>task-executor</code> used for publishing
|
||
Messages (if none is specified it simply publishes in the sender's thread):
|
||
<programlisting language="xml"><int:publish-subscribe-channel id="pubsubChannel" task-executor="someExecutor"/></programlisting>
|
||
If you are providing a <emphasis>Resequencer</emphasis> or <emphasis>Aggregator</emphasis> downstream
|
||
from a <classname>PublishSubscribeChannel</classname>, then you can set the 'apply-sequence' property
|
||
on the channel to <code>true</code>. That will indicate that the channel should set the sequence-size
|
||
and sequence-number Message headers as well as the correlation id prior to passing the Messages along.
|
||
For example, if there are 5 subscribers, the sequence-size would be set to 5, and the Messages would
|
||
have sequence-number header values ranging from 1 to 5.
|
||
<programlisting language="xml"><int:publish-subscribe-channel id="pubsubChannel" apply-sequence="true"/></programlisting>
|
||
<note>
|
||
The <code>apply-sequence</code> value is <code>false</code> by default so that a Publish Subscribe Channel
|
||
can send the exact same Message instances to multiple outbound channels. Since Spring Integration
|
||
enforces immutability of the payload and header references, the channel creates new Message
|
||
instances with the same payload reference but different header values when the flag is set to
|
||
<code>true</code>.
|
||
</note>
|
||
</para>
|
||
</section>
|
||
<section id="channel-configuration-executorchannel">
|
||
<title>ExecutorChannel</title>
|
||
<para>
|
||
To create an <classname>ExecutorChannel</classname>, add the <dispatcher> sub-element along
|
||
with a <code>task-executor</code> attribute. Its value can reference any <interfacename>TaskExecutor</interfacename>
|
||
within the context. For example, this enables configuration of a thread-pool for dispatching messages
|
||
to subscribed handlers. As mentioned above, this does break the "single-threaded" execution context
|
||
between sender and receiver so that any active transaction context will not be shared by the invocation
|
||
of the handler (i.e. the handler may throw an Exception, but the send invocation has already returned
|
||
successfully).
|
||
<programlisting language="xml"><![CDATA[<int:channel id="executorChannel">
|
||
<int:dispatcher task-executor="someExecutor"/>
|
||
</int:channel>]]></programlisting>
|
||
</para>
|
||
<note>
|
||
The <code>load-balancer</code> and <code>failover</code> options are also both available on the <dispatcher/> sub-element
|
||
as described above in <xref linkend="channel-configuration-directchannel"/>. The same defaults
|
||
apply as well. So, the channel will have a round-robin load-balancing strategy with failover
|
||
enabled unless explicit configuration is provided for one or both of those attributes.
|
||
<programlisting language="xml"><![CDATA[<int:channel id="executorChannelWithoutFailover">
|
||
<int:dispatcher task-executor="someExecutor" failover="false"/>
|
||
</int:channel>]]></programlisting>
|
||
</note>
|
||
</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[<int:channel id="priorityChannel">
|
||
<int:priority-queue capacity="20"/>
|
||
</int: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 <code>datatype</code> attribute. As with the QueueChannel, it also supports a <code>capacity</code> attribute.
|
||
The following example demonstrates all of these:
|
||
<programlisting language="xml"><![CDATA[<int:channel id="priorityChannel" datatype="example.Widget">
|
||
<int:priority-queue comparator="widgetComparator"
|
||
capacity="10"/>
|
||
</int:channel>
|
||
]]></programlisting>
|
||
</para>
|
||
</section>
|
||
<section id="channel-configuration-rendezvouschannel">
|
||
<title>RendezvousChannel Configuration</title>
|
||
<para>
|
||
A <classname>RendezvousChannel</classname> is created when the queue sub-element is
|
||
a <rendezvous-queue>. It does not provide any additional configuration options to
|
||
those described above, and its queue does not accept any capacity value since it is a
|
||
0-capacity direct handoff queue.
|
||
<programlisting language="xml"><![CDATA[<int:channel id="rendezvousChannel"/>
|
||
<int:rendezvous-queue/>
|
||
</int:channel>
|
||
]]></programlisting>
|
||
</para>
|
||
</section>
|
||
<section id="channel-configuration-threadlocalchannel">
|
||
<title>Scoped Channel Configuration</title>
|
||
<para>
|
||
Any channel can be configured with a "scope" attribute.
|
||
<programlisting language="xml"><![CDATA[<int:channel id="threadLocalChannel" scope="thread"/>]]></programlisting>
|
||
</para>
|
||
</section>
|
||
|
||
<section id="channel-configuration-interceptors">
|
||
<title>Channel Interceptor Configuration</title>
|
||
<para>
|
||
Message channels may also have interceptors as described in <xref linkend="channel-interceptors"/>. The
|
||
<interceptors/> sub-element can be added within <channel/> (or the more specific element
|
||
types). Provide the <code>ref</code> attribute to reference any Spring-managed object that implements the
|
||
<interfacename>ChannelInterceptor</interfacename> interface:
|
||
<programlisting language="xml"><![CDATA[<int:channel id="exampleChannel">
|
||
]]><emphasis><![CDATA[<int:interceptors>
|
||
<ref bean="trafficMonitoringInterceptor"/>
|
||
</int:interceptors>]]></emphasis><![CDATA[
|
||
</int: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="global-channel-configuration-interceptors">
|
||
<title>Global Channel Interceptor Configuration</title>
|
||
|
||
<titleabbrev id="global-channel-interceptor">Global Channel Interceptor</titleabbrev>
|
||
|
||
<para>
|
||
Channel Interceptors provide a clean and concise way of applying cross-cutting behavior per individual channel.
|
||
If the same behavior should be applied on multiple channels, configuring the same set of interceptors for
|
||
each channel <emphasis>would not be</emphasis> the most efficient way. To avoid repeated configuration while
|
||
also enabling interceptors to apply to multiple channels, Spring Integration provides
|
||
<emphasis>Global Interceptors</emphasis>.
|
||
|
||
Look at the example below:
|
||
<programlisting language="xml"><![CDATA[<int:channel-interceptor pattern="input*, bar*, foo" order="3">
|
||
<bean class="foo.barSampleInterceptor"/>
|
||
</int:channel-interceptor>]]></programlisting>
|
||
or
|
||
<programlisting language="xml"><![CDATA[<int:channel-interceptor ref="myInterceptor" pattern="input*, bar*, foo" order="3"/>
|
||
|
||
<bean id="myInterceptor" class="foo.barSampleInterceptor"/>]]></programlisting>
|
||
Each <channel-interceptor/> element allows you to define a global interceptor which will be applied on all
|
||
channels that match any patterns defined via the <code>pattern</code> attribute. In the above case the
|
||
global interceptor will be applied on the
|
||
'foo' channel and all other channels that begin with 'bar' or 'input'.
|
||
The <emphasis>order</emphasis> attribute allows you to manage where this interceptor will be injected if there
|
||
are multiple interceptors on a given channel.
|
||
For example, channel 'inputChannel' could have individual interceptors configured locally (see below):
|
||
<programlisting language="xml"><![CDATA[<int:channel id="inputChannel">
|
||
<int:interceptors>
|
||
<int:wire-tap channel="logger"/>
|
||
</int:interceptors>
|
||
</int:channel>]]></programlisting>
|
||
A reasonable question is how will a global interceptor be injected in relation to other interceptors
|
||
configured locally or through other global interceptor definitions? The current implementation provides
|
||
a very simple mechanism for defining the order of interceptor execution.
|
||
A positive number in the <code>order</code> attribute will ensure interceptor injection
|
||
after any existing interceptors and a negative number will ensure that the interceptor is injected before
|
||
existing interceptors.
|
||
This means that in the above example, the global interceptor will be injected <emphasis>AFTER</emphasis>
|
||
(since its order is greater than 0)
|
||
the 'wire-tap' interceptor configured locally. If there were another global interceptor with a matching
|
||
<code>pattern</code>, its order would be determined by comparing the values of the <code>order</code> attribute.
|
||
To inject a global interceptor <emphasis>BEFORE</emphasis> the existing interceptors, use a negative value for the <code>order</code> attribute.
|
||
</para>
|
||
<note>
|
||
Note that both the <code>order</code> and <code>pattern</code> attributes are optional. The default value
|
||
for <code>order</code> will be 0 and for <code>pattern</code>, the default is '*' (to match all channels).
|
||
</note>
|
||
</section>
|
||
|
||
<section id="channel-wiretap">
|
||
<title>Wire Tap</title>
|
||
<para>
|
||
As mentioned above, Spring Integration provides a simple <emphasis>Wire Tap</emphasis> interceptor out of
|
||
the box. You can configure a <emphasis>Wire Tap</emphasis> on any channel within an <interceptors/> element.
|
||
This is especially useful for debugging, and can be used in conjunction with Spring Integration's logging
|
||
Channel Adapter as follows: <programlisting language="xml"><![CDATA[ <int:channel id="in">
|
||
<int:interceptors>
|
||
<int:wire-tap channel="logger"/>
|
||
</int:interceptors>
|
||
</int:channel>
|
||
|
||
<int:logging-channel-adapter id="logger" level="DEBUG"/>]]></programlisting>
|
||
<tip>
|
||
The 'logging-channel-adapter' also accepts an 'expression' attribute so that you can evaluate
|
||
a SpEL expression against 'payload' and/or 'headers' variables. Alternatively, to simply log
|
||
the full Message toString() result, provide a value of "true" for the 'log-full-message' attribute.
|
||
That is <code>false</code> by default so that only the payload is logged. Setting that to
|
||
<code>true</code> enables logging of all headers in addition to the payload. The 'expression'
|
||
option does provide the most flexibility, however (e.g. expression="payload.user.name").
|
||
</tip>
|
||
</para>
|
||
|
||
<para>
|
||
<emphasis>A little more on Wire Tap</emphasis>
|
||
</para>
|
||
<para>
|
||
One of the common misconceptions about the wire tap and other similar components (<xref linkend="message-publishing-config"/>)
|
||
is that they are automatically asynchronous in nature. Wire-tap as a component is not
|
||
invoked asynchronously be default. Instead, Spring Integration focuses on a single unified
|
||
approach to configuring asynchronous behavior: the Message Channel.
|
||
|
||
What makes certain parts of the message flow <emphasis>sync</emphasis> or <emphasis>async</emphasis>
|
||
is the type of <emphasis>Message Channel</emphasis> that has been configured within that flow. That
|
||
is one of the primary benefits of the Message Channel abstraction.
|
||
From the inception of the framework, we have always emphasized the need and the value of the
|
||
<emphasis>Message Channel</emphasis> as a first-class citizen of the framework. It is not
|
||
just an internal, implicit realization of the EIP pattern, it is fully exposed as a configurable
|
||
component to the end user.
|
||
|
||
So, the Wire-tap component is ONLY responsible for performing the following 3 tasks:
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>intercept a message flow by tapping into a channel (e.g., channelA)</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>grab each message</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>send the message to another channel (e.g., channelB)</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
It is essentially a variation of the Bridge, but it is encapsulated within a channel definition
|
||
(and hence easier to enable and disable without disrupting a flow). Also, unlike the bridge, it
|
||
basically forks another message flow. Is that flow <emphasis>synchronous</emphasis> or
|
||
<emphasis>asynchronous</emphasis>? The answer simply depends on the type of <emphasis>Message Channel</emphasis>
|
||
that 'channelB' is. And, now you know that we have: <emphasis>Direct Channel</emphasis>,
|
||
<emphasis>Pollable Channel</emphasis>, and <emphasis>Executor Channel</emphasis> as options.
|
||
The last two do break the thread boundary making communication via such channels
|
||
<emphasis>asynchronous</emphasis> simply because the dispatching of the message from that channel
|
||
to its subscribed handlers happens on a different thread than the one used to send the message to that
|
||
channel. That is what is going to make your wire-tap flow <emphasis>sync</emphasis> or <emphasis>async</emphasis>.
|
||
It is consistent with other components within the framework (e.g., Message Publisher) and actually
|
||
brings a level of consistency and simplicity by sparing you from worrying in advance (other than writing
|
||
thread safe code) whether a particular piece of code should be implemented as <emphasis>sync</emphasis> or
|
||
<emphasis>async</emphasis>. The actual wiring of two pieces of code (component A and component B) via
|
||
<emphasis>Message Channel</emphasis> is what makes their collaboration <emphasis>sync</emphasis> or
|
||
<emphasis>async</emphasis>. You may even want to change from <emphasis>sync</emphasis> to
|
||
<emphasis>async</emphasis> in the future and <emphasis>Message Channel</emphasis> is what's going
|
||
to allow you to do it swiftly without ever touching the code.</para>
|
||
|
||
<para>One final point regarding the Wire Tap is that, despite the rationale provided above for not
|
||
being async be default, one should keep in mind it is usually desirable to hand off the Message as
|
||
soon as possible. Therefore, it would be quite common to use an asynchronous channel option as the
|
||
wire-tap's outbound channel. Nonetheless, another reason that we do not enforce asynchronous behavior
|
||
by default is that you might not want to break a transactional boundary. Perhaps you are using the Wire Tap
|
||
for auditing purposes, and you DO want the audit Messages to be sent within the original transaction.
|
||
As an example, you might connect the wire-tap to a JMS outbound-channel-adapter. That way, you get the
|
||
best of both worlds: 1) the sending of a JMS Message can occur within the transaction while
|
||
2) it is still a "fire-and-forget" action thereby preventing any noticeable delay in the main message flow.
|
||
</para>
|
||
</section>
|
||
<section id="channel-global-wiretap">
|
||
<title>Global Wire Tap Configuration</title>
|
||
<para>It is possible to configure a global wire tap as a special case of the <xref linkend="global-channel-configuration-interceptors" endterm="global-channel-interceptor"/>. Simply configure a top level <code>wire-tap</code> element. Now, in addition to the normal <code>wire-tap</code> namespace support, the <code>pattern</code> and <code>order</code> attributes are supported and work in exactly the same way as with the <code>channel-interceptor</code>
|
||
<programlisting language="xml"><![CDATA[<int:wire-tap pattern="input*, bar*, foo" order="3" channel="wiretapChannel"/>]]></programlisting>
|
||
</para>
|
||
<tip>A global wire tap provides a convenient way to configure a single channel wire tap externally without modifying the existing channel configuration. Simply set the <code>pattern</code> attribute to the target channel name. For example, This technique may be used to configure a test case to verify messages on a channel.
|
||
</tip>
|
||
</section>
|
||
|
||
|
||
</section>
|
||
|
||
|
||
<section id="channel-special-channels">
|
||
<title>Special Channels</title>
|
||
<para>
|
||
If namespace support is enabled, there are two special channels defined within the application context by default:
|
||
<code>errorChannel</code> and <code>nullChannel</code>. The 'nullChannel' acts like <code>/dev/null</code>,
|
||
simply logging any Message sent to it at DEBUG level and returning immediately. Any time you face channel
|
||
resolution errors for a reply that you don't care about, you can set the affected component's <code>output-channel</code> attribute
|
||
to 'nullChannel' (the name 'nullChannel' is reserved within the application context). The 'errorChannel' is
|
||
used internally for sending error messages and may be overridden with a custom configuration. This is
|
||
discussed in greater detail in <xref linkend="namespace-errorhandler"/>.
|
||
</para>
|
||
</section>
|
||
</section>
|