The Core API
Message The Spring Integration Message is a generic container for data. Any object can be provided as the payload, and each Message also includes a header containing user-extensible properties as key-value pairs. Here is the definition of the Message interface: public interface Message<T> { Object getId(); MessageHeader getHeader(); T getPayload(); void setPayload(T payload); boolean isExpired(); void copyHeader(MessageHeader header, boolean overwriteExistingValues); } And the header provides the following properties: Properties of the MessageHeader Property Name Property Type timestamp java.util.Date expiration java.util.Date correlationId java.lang.Object returnAddress java.lang.Object (can be a String or MessageChannel) sequenceNumber int sequenceSize int priority MessagePriority (an enum) properties java.util.Properties attributes Map<String,Object>
The base implementation of the Message interface is GenericMessage<T>, and it provides three constructors: new GenericMessage<T>(Object id, T payload); new GenericMessage<T>(T payload); new GenericMessage<T>(T payload, MessageHeader headerToCopy) When no id is provided, a random unique id will be generated. The constructor that accepts a MessageHeader will copy properties and attributes as well as the 'returnAddress', 'sequenceNumber', and 'sequenceSize' properties from the provided header. There are also two convenient subclasses available currently: StringMessage and ErrorMessage. The latter accepts any Throwable object as its payload. The MessagePriority is only considered when using a PriorityChannel (as described in the next section). It is defined as an enum with five possible values: public enum MessagePriority { HIGHEST, HIGH, NORMAL, LOW, LOWEST } The Message is obviously a very important part of the API. By encapsulating the data in a generic wrapper, the messaging system can pass it around without any knowledge of the data's type. As the system evolves to support new types, or when the types themselves are modified and/or extended, the messaging system will not be affected by such changes. On the other hand, when some component in the messaging system does require access to information about the Message, such metadata can typically be stored to and retrieved from the metadata in the header (the 'properties' and 'attributes').
MessageSource The MessageSource interface defines a single method for receiving Message objects. public interface MessageSource<T> { Message<T> receive(); } Spring Integration also provides a MethodInvokingSource implementation that serves as an adapter for invoking any arbitrary method on a plain Object (i.e. there is no need to implement an interface). To use the MethodInvokingSource, provide the Object reference and the method name. MethodInvokingSource source = new MethodInvokingSource(); source.setObject(new SourceObject()); source.setMethodName("sourceMethod"); Message<?> result = source.receive(); It is generally more common to configure a MethodInvokingSource in XML by providing a bean reference in the "source" attribute of a <channel-adapter> element. ]]>
MessageTarget The MessageTarget interface defines a single method for sending Message objects. public interface MessageTarget { boolean send(Message<?> message); } As with the MessageSource, Spring Integration also provides a MethodInvokingTarget adapter class. MethodInvokingTarget target = new MethodInvokingTarget(); target.setObject(new TargetObject()); target.setMethodName("targetMethod"); target.afterPropertiesSet(); target.send(new StringMessage("test")); When creating a Channel Adapter for this target, the corresponding XML configuration is very similar to that of MethodInvokingSource. ]]>
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 MessageChannel interface is defined as follows. > clear(); List> 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.
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 receive Messages by invoking receive(). Instead, any subscriber must be a MessageTarget itself, and the subscriber's send(Message) method will be invoked in turn.
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.
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' 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.
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.
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 also dispatches Messages directly but only to a single receiver. 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).
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 could collect its replies.
ChannelInterceptor 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 Messages are being sent to and received from MessageChannels, those channels provide an opportunity for intercepting the send and receive operations. The ChannelInterceptor strategy interface provides methods for each of those operations: message, MessageChannel channel); void postSend(Message message, MessageChannel channel, boolean sent); boolean preReceive(MessageChannel channel); void postReceive(Message message, MessageChannel channel); }]]> After implementing the interface, registering the interceptor with a channel is just a matter of calling: channel.addInterceptor(someChannelInterceptor); The methods that return a boolean value can return 'false' to prevent the send or receive operation from proceeding (send would return 'false' and receive would return 'null'). Because it is rarely necessary to implement all of the interceptor methods, a ChannelInterceptorAdapter class is also available for sub-classing. It provides no-op methods (the void methods are empty, and the boolean methods return true). Therefore, it is often easiest to extend that class and just implement the method(s) that you need as in the following example. message, MessageChannel channel) { sendCount.incrementAndGet(); return true; } }]]>
MessageHandler So far we have seen that generic message objects are sent-to and received-from simple channel objects. Here is Spring Integration's callback interface for handling the Messages: public interface MessageHandler { Message<?> handle(Message<?> message); } The handler plays an important role, since it is typically responsible for translating between the generic Message objects and the domain objects or primitive values expected by business components that consume the message payload. That said, developers will rarely need to implement this interface directly. While that option will always be available, we will soon discuss the higher-level configuration options including both annotation-driven techniques and XML-based configuration with convenient namespace support.
MessageBus So far, you have seen that the MessageChannel provides a receive() method that returns a Message, and the MessageHandler provides a handle() method that accepts a Message, but how do the messages get passed from the channel to the handler? As mentioned earlier, the MessageBus provides a runtime form of inversion of control, and one of the primary responsibilities that it assumes is connecting the channels to the handlers. It also connects MessageSources and MessageTargets to channels, and it manages the scheduling of pollers and dispatchers. The MessageBus is an example of a mediator. It performs a number of roles - mostly by delegating to other strategies. One of its main responsibilities is to manage registration of the MessageChannels and endpoints, such as Channel Adapters and Service Activators. It recognizes any of these instances that have been defined within its ApplicationContext. The message bus handles several of the concerns so that the channels, sources, targets, and Message-handling objects can be as simple as possible. These responsibilities include the lifecycle management of message endpoints, the activation of subscriptions, and the scheduling of dispatchers (including the configuration of thread pools). The bus coordinates all of that behavior based upon the metadata provided in bean definitions. Furthermore, those bean definitions may be provided via XML and/or annotations (we will look at examples of both configuration options shortly). The bus creates and schedules triggers for all of its registered endpoints. When an endpoint receives a trigger event, it will poll the MessageSource that was provided in its metadata. For example, a Channel Adapter will poll the referenced "source", and a Service Activator will poll the referenced "input-channel".
MessageEndpoint As described in , there are different types of Message Endpoint, such as the Channel Adapter (inbound or outbound) and the Service Activator. Spring Integration provides many other components that are also endpoints, such as Routers, Splitters, and Aggregators. Each endpoint may provide its own specific metadata so that the MessageBus can manage its connection to a channel and its polling schedule. The scheduling metadata is provided as an implementation of the Schedule interface. This is an abstraction designed to allow extensibility of schedulers for messaging tasks. Currently, there is a single implementation named PollingSchedule and the endpoint may set the period property. The polling period may differ depending on the type of MessageSource (e.g. file-system vs. JMS). While the MessageBus manages the scheduling of the trigger invocation threads, it may be necessary to have concurrent threads for the endpoint's processing of each receive-and-handle unit of work. Spring Integration provides an endpoint interceptor called ConcurrencyInterceptor for this very purpose. The interceptor's configuration is provided by the ConcurrencyPolicy metadata object. When the MessageBus activates an endpoint that has been defined with a ConcurrencyInterceptor, it will use these properties to configure that endpoint's thread pool. These interceptors are configurable on a per-endpoint basis since different endpoint handlers may have different performance characteristics and may have different expectations with regard to the volume of throughput. The following table lists the available properties of the ConcurrencyPolicy and their default values: Properties of the ConcurrencyPolicy Property Name Default Value Description coreSize 1 the core size of the thread pool maxSize 10 the maximum size the thread pool can reach when under demand queueCapacity 0 capacity of the queue which defers an increase of the pool size keepAliveSeconds 60 how long added threads (beyond core size) should remain idle before being removed from the pool
The details of configuring this and other metadata for each endpoint will be discussed in detail in .
MessageSelector As described above, each endpoint is registered with the message bus and is thereby subscribed to a channel. Often it is necessary to provide additional dynamic logic to determine what messages the endpoint should receive. The MessageSelector strategy interface fulfills that role. message); }]]> A MessageEndpoint can be configured with a selector (or selector-chain) and will only receive messages that are accepted by each selector. Even though the interface is simple to implement, a couple common selector implementations are provided. For example, the PayloadTypeSelector provides similar functionality to Datatype Channels (as described in ) except that in this case the type-matching can be done by the endpoint rather than the channel. (123))); assertFalse(selector.accept(new GenericMessage(someObject))); ]]> Another simple but useful MessageSelector provided out-of-the-box is the UnexpiredMessageSelector. As the name suggests, it only accepts messages that have not yet expired. Essentially, using a selector provides reactive routing whereas the Datatype Channel and Message Router provide proactive routing. However, selectors accommodate additional uses. For example, the MessageChannel's 'purge' method accepts a selector: channel.purge(someSelector); There is a ChannelPurger utility class whose purge operation is a good candidate for Spring's JMX support: ChannelPurger purger = new ChannelPurger(new ExampleMessageSelector(), channel); purger.purge(); Implementations of MessageSelector might provide opportunities for reuse on channels in addition to endpoints. For that reason, Spring Integration provides a simple selector-wrapping ChannelInterceptor that accepts one or more selectors in its constructor. MessageSelectingInterceptor interceptor = new MessageSelectingInterceptor(selector1, selector2); channel.addInterceptor(interceptor);
RequestReplyTemplate Whereas the MessageHandler interface provides the foundation for many of the components that enable non-invasive invocation of your application code from the messaging system, sometimes it is necessary to invoke the messaging system from your application code. Spring Integration provides a RequestReplyTemplate that supports a variety of request-reply scenarios. For example, it is possible to send a request and wait for a reply. RequestReplyTemplate template = new RequestReplyTemplate(requestChannel); Message reply = template.request(new StringMessage("test")); In that example, a temporary anonymous channel would be used internally by the template. However, the 'replyChannel' may be configured explicitly in which case the template will manage the reply correlation. RequestReplyTemplate template = new RequestReplyTemplate(requestChannel); template.setReplyChannel(replyChannel); Message reply = template.request(new StringMessage("test"));
MessagingGateway Even though the RequestReplyTemplate is fairly straightforward, it does not hide the details of messaging from your application code. To support working with plain Objects instead of messages, Spring Integration provides SimpleMessagingGateway with the following methods: public void send(Object object) { ... } public Object receive() { ... } public Object sendAndReceive(Object object) { ... } It enables configuration of a request and/or reply channel and delegates to the MessageMapper and MessageCreator strategy interfaces. SimpleMessagingGateway gateway = new SimpleMessagingGateway(); gateway.setRequestChannel(requestChannel); gateway.setReplyChannel(replyChannel); gateway.setMessageCreator(messageCreator); gateway.setMessageMapper(messageMapper); Object result = gateway.sendAndReceive("test"); Working with Objects instead of Messages is an improvement. However, it would be even better to have no dependency on the Spring Integration API at all - including the gateway class. For that reason, Spring Integration also provides a GatewayProxyFactoryBean that generates a proxy for any interface and internally invokes the gateway methods shown above. Namespace support is also provided as demonstrated by the following example. ]]> Then, the "fooService" can be injected into other beans, and the code that invokes the methods on that proxied instance of the FooService interface has no awareness of the Spring Integration API.