2.4 MessageChannel

While the Message plays the crucial role of encapsulating data, it is the MessageChannel that decouples message producers from message consumers. Spring Integration's top-level MessageChannel interface is defined as follows.

public interface MessageChannel extends MessageSource, BlockingTarget {
    String getName();
}

Because it extends BlockingTarget, it inherits the following methods:

boolean send(Message message);

boolean send(Message message, long timeout);

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.

Since Message Channels are also Message Sources, there are two sub-interfaces corresponding to the two source types. Here is the definition of PollableChannel.

public interface PollableChannel extends MessageChannel, BlockingSource {

    List<Message<?>> clear();

    List<Message<?>> purge(MessageSelector selector);

}

Since the PollableChannel interface extends BlockingSource, it also inherits the following methods:

Message<T> receive();

Message<T> receive(long timeout);

Similar to the send methods, when receiving a message, the return value will be null in the case of a timeout or interrupt.

The subscribable channels implement the SubscribableSource interface. Instead of providing receive methods for polling, these channels will send messages directly to their subscribers. The SubscribableSource interface defines the following two methods:

boolean subscribe(MessageTarget target);

boolean unsubscribe(MessageTarget target);

Spring Integration provides several different Message Channel implementations. Each is briefly described in the sections below.

2.4.1 PublishSubscribeChannel

The PublishSubscribeChannel implementation broadcasts any Message sent to it to all of its subscribed consumers. This is most often used for sending Event Messages whose primary role is notification as opposed to Document Messages which are generally intended to be processed by a single consumer. Note that the PublishSubscribeChannel is intended for sending only. Since it broadcasts to its subscribers directly when its send(Message) method is invoked, consumers cannot poll for Messages (it does not implement PollableChannel and therefore has no receive() method). Instead, any subscriber must be a MessageTarget itself, and the subscriber's send(Message) method will be invoked in turn.

2.4.2 QueueChannel

The QueueChannel implementation wraps a queue. Unlike, the PublishSubscribeChannel, the QueueChannel 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 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.

2.4.3 PriorityChannel

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' header within each message. However, for custom priority determination logic, a comparator of type Comparator<Message<?>> can be provided to the PriorityChannel's constructor.

2.4.4 RendezvousChannel

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.

2.4.5 DirectChannel

The DirectChannel has point-to-point semantics, but otherwise is more similar to the PublishSubscribeChannel than any of the queue-based channel implementations described above. In other words, it does not implement the PollableChannel interface, but rather dispatches Messages directly to a subscriber. As a point-to-point channel, however, it will only send each Message to a single subscribed MessageTarget. Its primary purpose is to enable a single thread to perform the operations on "both sides" of the channel. For example, if a receiving target is subscribed to a DirectChannel, then sending a Message to that channel will trigger invocation of that target's send(Message) method directly in the sender's thread. 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 target's invocation can play a role in determining the ultimate result of that transaction (commit or rollback).

2.4.6 ThreadLocalChannel

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 can collect its replies from it.