While the Message
plays the crucial role of encapsulating data, it is the
MessageChannel
that decouples message producers from message consumers.
Spring Integration's MessageChannel
interface is defined as follows.
public interface MessageChannel { String getName(); void setName(String name); DispatcherPolicy getDispatcherPolicy(); boolean send(Message message); boolean send(Message message, long timeout); Message receive(); Message receive(long timeout); List<Message<?>> clear(); List<Message<?>> purge(MessageSelector selector); }
When sending a message, the return value will be true if the message is sent successfully. If the send call times out or is interrupted, then it will return false. Likewise when receiving a message, the return value will be null in the case of a timeout or interrupt.
Spring Integration provides several different implementations of the
MessageChannel
interface. Each is briefly described in the sections below.
The QueueChannel
implementation wraps a queue. It provides a no-argument constructor
(that uses a default capacity of 100) as well as a constructor that accepts the queue capacity:
public QueueChannel(int capacity)
A channel that has not reached its capacity limit will store messages in its internal queue, and the
send()
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 send()
and
receive()
will block indefinitely.
Whereas the QueueChannel
enforces first-in/first-out (FIFO) ordering, the
PriorityChannel
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
'priority
' property within each message's header. However, for custom priority determination
logic, a comparator of type Comparator<Message<?>>
can be provided to the
PriorityChannel
's constructor.
The RendezvousChannel
enables a "direct-handoff" scenario where a sender will block
until another party invokes the channel's receive()
method or vice-versa. Internally,
this implementation is quite similar to the QueueChannel
except that it uses a
SynchronousQueue
(a zero-capacity implementation of
BlockingQueue
). 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
QueueChannel
, the message would have been stored to the internal queue and potentially
never received.
The RendezvousChannel
is also useful for implementing request-reply
operations. The sender can create a temporary, anonymous instance of RendezvousChannel
which it then sets as the 'returnAddress' on 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.
The DirectChannel
is significantly different than the channel implementations described
thus far. It's primary purpose is to enable a single thread to perform the operations on "both sides" of the
channel. For example, if a HandlerEndpoint
is subscribed to a
DirectChannel
, then sending a Message to that channel will trigger invocation of the
handler directly in the sender's thread. The key motivation for providing a channel
implementation with this behavior is to support transactions. If the send call is invoked within the scope of a
transaction, then the outcome of the handler invocation can play a role in determining the ultimate result of
that transaction (commit or rollback).
The final channel implementation type is ThreadLocalChannel
. 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
DirectChannels
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
ThreadLocalChannel
, the original sending thread could collect its replies.