diff --git a/docs/src/reference/docbook/channel.xml b/docs/src/reference/docbook/channel.xml
index f5ed890e9b..4b25c876c4 100644
--- a/docs/src/reference/docbook/channel.xml
+++ b/docs/src/reference/docbook/channel.xml
@@ -13,8 +13,6 @@
Spring Integration's top-level MessageChannel interface is defined as follows.
Similar to the send methods, when receiving a message, the return value will be null in the
case of a timeout or interrupt.
@@ -130,9 +124,11 @@
- Keep in mind that all of these queue-based channels are storing messages in-memory only. When persistence
- is required, you can either invoke a database operation within a handler or use Spring Integration's
- support for JMS-based Channel Adapters. The latter option allows you to take advantage of any JMS provider's
+ 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 . However, when
buffering in a queue is not necessary, the simplest approach is to rely upon the
DirectChannel discussed next.
@@ -261,7 +257,7 @@
If that terminal channel is thread-scoped, the original sending thread can collect its replies from it.
- Now, since channel can be scoped, aside from Thread Local you can define yoru own scopes.
+ Now, since any channel can be scoped, you can define your own scopes in addition to Thread Local.
@@ -352,13 +348,13 @@
and wait for a reply.
MessagingTemplate template = new MessagingTemplate();
-Message reply = template.sendAndReceive(new GenericMessage("test"), someChannel);
+Message reply = template.sendAndReceive(someChannel, new GenericMessage("test"));
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.
- message, final MessageChannel channel) { ... }
+ message) { ... }
-public Message> sendAndReceive(final Message> request, final MessageChannel channel) { .. }
+public Message> sendAndReceive(final MessageChannel channel, final Message> request) { .. }
public Message> receive(final PollableChannel> channel) { ... }]]>
@@ -517,10 +513,10 @@ public Message> receive(final PollableChannel> channel) { ... }]]>
- ThreadLocalChannel Configuration
+ Scoped Channel Configuration
- The ThreadLocalChannel does not provide any additional configuration options.
- ]]>
+ Any channel can be configured with a "scope" attribute.
+ ]]>
@@ -546,8 +542,9 @@ public Message> receive(final PollableChannel> channel) { ... }]]>
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 would not be the most efficient way. To avoid repeated configuration, use global interceptors and apply
- them to multiple channels. Spring Integration provides Global Interceptors to accomplish this.
+ each channel would not be the most efficient way. To avoid repeated configuration while
+ also enabling interceptors to apply to multiple channels, Spring Integration provides
+ Global Interceptors.
Look at the example below:
@@ -557,10 +554,12 @@ public Message> receive(final PollableChannel> channel) { ... }]]>
]]>
- <channel-interceptor/> element allows you to define a global interceptor which will be applied on all
- channels that match patterns defined via pattern attribute. In the above case the global interceptor will be applied on
- 'foo' channel and all other channels that begin with 'bar' and 'input'.
- The order attribute allows you to manage the place where this interceptor will be injected.
+ 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 pattern 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 order 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):
@@ -569,17 +568,19 @@ public Message> receive(final PollableChannel> channel) { ... }]]>]]>
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 and clever mechanism for defining the order of interceptor execution.
+ a very simple mechanism for defining the order of interceptor execution.
A positive number in the order attribute will ensure interceptor injection
- after any existing interceptors and negative number will ensure that interceptor is injected before.
- This means that in the above example global interceptor will be injected AFTER (since its order is greater then 0)
- the 'wire-tap' interceptor configured locally. If there was another global interceptor with matching pattern its
- order would be determined by comparing the values of the order attribute.
- To inject global interceptor BEFORE the existing interceptors use a negative value for the order attribute.
+ 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 AFTER
+ (since its order is greater than 0)
+ the 'wire-tap' interceptor configured locally. If there were another global interceptor with a matching
+ pattern, its order would be determined by comparing the values of the order attribute.
+ To inject a global interceptor BEFORE the existing interceptors, use a negative value for the order attribute.
- Note that order and pattern attributes are optional. The default value for order
- will be 0 and for pattern is '*'
+ Note that both the order and pattern attributes are optional. The default value
+ for order will be 0 and for pattern, the default is '*' (to match all channels).
@@ -597,9 +598,12 @@ public Message> receive(final PollableChannel> channel) { ... }]]>]]>
- The 'logging-channel-adapter' also accepts a boolean attribute: log-full-message.
+ 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 false by default so that only the payload is logged. Setting that to
- true enables logging of all headers in addition to the payload.
+ true 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").
@@ -607,43 +611,60 @@ public Message> receive(final PollableChannel> channel) { ... }]]>A little more on Wire Tap
- One of the common misconception about the wire tap and some time other similar components ()
- that they are asynchronous in nature. Wire-tap as a component is neither sync nor async.
- In fact non of the components in SI are sync or async except for. . . well read on.
+ One of the common misconceptions about the wire tap and other similar components ()
+ 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 sync or async is the Message Channel
- abstraction. That is why from the inception of the framework we always emphasize the need and the value of the Message Channel
- and that is why Spring Integration is the only framework at the time of writing where Message Channel
- is a "first class citizen" of the framework (not an internal realization of EIP pattern) fulle exposed to you - the end user.
+ What makes certain parts of the message flow sync or async
+ is the type of Message Channel 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
+ Message Channel 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, Wire-tap component is ONLY responsible to perform the following 3 tasks:
+ So, the Wire-tap component is ONLY responsible for performing the following 3 tasks:
- wire-tap into a message flow by tapping into a channel (e.g., channelA)
+ intercept a message flow by tapping into a channel (e.g., channelA)
- grab a copy of a message
+ grab each message
- send it to another channel (e.g., channelB)
+ send the message to another channel (e.g., channelB)
+ 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 synchronous or
+ asynchronous? The answer simply depends on the type of Message Channel
+ that 'channelB' is. And, now you know that we have: Direct Channel,
+ Pollable Channel, and Executor Channel as options.
+ The last two do break the thread boundary making communication via such channels
+ asynchronous 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 sync or async.
+ 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 sync or
+ async. The actual wiring of two pieces of code (component A and component B) via
+ Message Channel is what makes their collaboration sync or
+ async. You may even want to change from sync to
+ async in the future and Message Channel is what's going
+ to allow you to do it swiftly without ever touching the code.
- Look at it as a variation of the Bridge (nothing more). But by bridging one channel with another wire-tap is essentially
- initiates (forks) another message flow. Is this flow synchronous or asynchronous?
- That is the ultimate question and the answer simply depends on the type of Message Channel 'channelB' is.
- And as you know we have: Direct Channel, Pollable Channel and Executor Channel.
- The last two do break the thread boundary making communication via such channels asynchronous simply because
- the dispatching of the message from the channel happens on the different thread then the one that sent the message to that channel
- and that is what is going to make your wire-tap flow sync or async.
- It is consistent with other components within the framework (e.g., Message Publisher) and if you think about it its in a way
- brings a level of simplicity by sparing you form worrying in advance (other then writing thread safe code) wether a
- particular piece of code should be implemented as sync or async. In fact its always neither,
- the code is just a function. The actual wiring of two pieces of code (component A and component B) via Message Channel
- is what's going to make their collaboration sync or async. You may even want to change
- from sync to async in the future and Message Channel is what's going
- to allow you to do it swiftly without ever touching the code
+ 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.