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.