528 lines
31 KiB
XML
528 lines
31 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
<chapter id="api">
|
|
<title>The Core API</title>
|
|
|
|
<section id="api-message">
|
|
<title>Message</title>
|
|
<para>
|
|
The Spring Integration <interfacename>Message</interfacename> is a generic container for data. Any object can
|
|
be provided as the payload, and each <interfacename>Message</interfacename> also includes a header containing
|
|
user-extensible properties as key-value pairs. Here is the definition of the
|
|
<interfacename>Message</interfacename> interface:
|
|
<programlisting language="java">public interface Message<T> {
|
|
Object getId();
|
|
MessageHeader getHeader();
|
|
T getPayload();
|
|
void setPayload(T payload);
|
|
boolean isExpired();
|
|
void copyHeader(MessageHeader header, boolean overwriteExistingValues);
|
|
}</programlisting>
|
|
And the header provides the following properties:
|
|
<table id="api-message-headerproperties">
|
|
<title>Properties of the MessageHeader</title>
|
|
<tgroup cols="2">
|
|
<colspec align="left" />
|
|
<thead>
|
|
<row>
|
|
<entry align="center">Property Name</entry>
|
|
<entry align="center">Property Type</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>timestamp</entry>
|
|
<entry>java.util.Date</entry>
|
|
</row>
|
|
<row>
|
|
<entry>expiration</entry>
|
|
<entry>java.util.Date</entry>
|
|
</row>
|
|
<row>
|
|
<entry>correlationId</entry>
|
|
<entry>java.lang.Object</entry>
|
|
</row>
|
|
<row>
|
|
<entry>returnAddress</entry>
|
|
<entry>java.lang.Object (can be a String or MessageChannel)</entry>
|
|
</row>
|
|
<row>
|
|
<entry>sequenceNumber</entry>
|
|
<entry>int</entry>
|
|
</row>
|
|
<row>
|
|
<entry>sequenceSize</entry>
|
|
<entry>int</entry>
|
|
</row>
|
|
<row>
|
|
<entry>priority</entry>
|
|
<entry>MessagePriority (an <emphasis>enum</emphasis>)</entry>
|
|
</row>
|
|
<row>
|
|
<entry>properties</entry>
|
|
<entry>java.util.Properties</entry>
|
|
</row>
|
|
<row>
|
|
<entry>attributes</entry>
|
|
<entry>Map<String,Object></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</para>
|
|
<para>
|
|
The base implementation of the <interfacename>Message</interfacename> interface is
|
|
<classname>GenericMessage<T></classname>, and it provides three constructors:
|
|
<programlisting language="java">new GenericMessage<T>(Object id, T payload);
|
|
new GenericMessage<T>(T payload);
|
|
new GenericMessage<T>(T payload, MessageHeader headerToCopy)</programlisting>
|
|
When no id is provided, a random unique id will be generated. The constructor that accepts a
|
|
<classname>MessageHeader</classname> 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:
|
|
<classname>StringMessage</classname> and <classname>ErrorMessage</classname>. The latter accepts any
|
|
<classname>Throwable</classname> object as its payload.
|
|
</para>
|
|
<para>
|
|
The <classname>MessagePriority</classname> is only considered when using a <classname>PriorityChannel</classname>
|
|
(as described in the next section). It is defined as an <emphasis>enum</emphasis> with five possible values:
|
|
<programlisting language="java">public enum MessagePriority {
|
|
HIGHEST,
|
|
HIGH,
|
|
NORMAL,
|
|
LOW,
|
|
LOWEST
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
The <interfacename>Message</interfacename> 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
|
|
<emphasis>does</emphasis> require access to information about the <interfacename>Message</interfacename>, such
|
|
metadata can typically be stored to and retrieved from the metadata in the header (the 'properties' and
|
|
'attributes').
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-source">
|
|
<title>MessageSource</title>
|
|
<para>
|
|
The <interfacename>MessageSource</interfacename> interface defines a single method for receiving
|
|
<interfacename>Message</interfacename> objects.
|
|
<programlisting language="java">public interface MessageSource<T> {
|
|
Message<T> receive();
|
|
}</programlisting>
|
|
Spring Integration also provides a <classname>MethodInvokingSource</classname> 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 <classname>MethodInvokingSource</classname>, provide the Object reference and the method name.
|
|
<programlisting language="java">MethodInvokingSource source = new MethodInvokingSource();
|
|
source.setObject(new SourceObject());
|
|
source.setMethodName("sourceMethod");
|
|
Message<?> result = source.receive();</programlisting>
|
|
It is generally more common to configure a <classname>MethodInvokingSource</classname> in XML by providing a
|
|
bean reference in the "source" attribute of a <channel-adapter> element.
|
|
<programlisting language="xml"><![CDATA[<channel-adapter source="sourceObject" method="sourceMethod" channel="someChannel"/>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-target">
|
|
<title>MessageTarget</title>
|
|
<para>
|
|
The <interfacename>MessageTarget</interfacename> interface defines a single method for sending
|
|
<interfacename>Message</interfacename> objects.
|
|
<programlisting language="java">public interface MessageTarget {
|
|
boolean send(Message<?> message);
|
|
}</programlisting>
|
|
As with the <interfacename>MessageSource</interfacename>, Spring Integration also provides a
|
|
<classname>MethodInvokingTarget</classname> adapter class.
|
|
<programlisting language="java">MethodInvokingTarget target = new MethodInvokingTarget();
|
|
target.setObject(new TargetObject());
|
|
target.setMethodName("targetMethod");
|
|
target.afterPropertiesSet();
|
|
target.send(new StringMessage("test"));</programlisting>
|
|
When creating a Channel Adapter for this target, the corresponding XML configuration
|
|
is very similar to that of <classname>MethodInvokingSource</classname>.
|
|
<programlisting language="xml"><![CDATA[<channel-adapter channel="someChannel" target="targetObject" method="targetMethod"/>]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-messagechannel">
|
|
<title>MessageChannel</title>
|
|
<para>
|
|
While the <interfacename>Message</interfacename> plays the crucial role of encapsulating data, it is the
|
|
<interfacename>MessageChannel</interfacename> that decouples message producers from message consumers.
|
|
Spring Integration's <interfacename>MessageChannel</interfacename> interface is defined as follows.
|
|
<programlisting language="java"><![CDATA[public interface MessageChannel {
|
|
String getName();
|
|
void setName(String name);
|
|
boolean send(Message message);
|
|
boolean send(Message message, long timeout);
|
|
Message receive();
|
|
Message receive(long timeout);
|
|
List<Message<?>> clear();
|
|
List<Message<?>> purge(MessageSelector selector);
|
|
}]]></programlisting>
|
|
When sending a message, the return value will be <emphasis>true</emphasis> if the message is sent successfully.
|
|
If the send call times out or is interrupted, then it will return <emphasis>false</emphasis>. Likewise when
|
|
receiving a message, the return value will be <emphasis>null</emphasis> in the case of a timeout or interrupt.
|
|
</para>
|
|
<para>
|
|
Spring Integration provides several different implementations of the
|
|
<interfacename>MessageChannel</interfacename> interface. Each is briefly described in the sections below.
|
|
</para>
|
|
<section id="api-messagechannel-publishsubscribechannel">
|
|
<title>PublishSubscribeChannel</title>
|
|
<para>
|
|
The <classname>PublishSubscribeChannel</classname> implementation broadcasts any Message
|
|
sent to it to all of its subscribed consumers. This is most often used for sending
|
|
<emphasis>Event Messages</emphasis> whose primary role is notification as opposed to
|
|
<emphasis>Document Messages</emphasis> which are generally intended to be processed by
|
|
a single consumer. Note that the <classname>PublishSubscribeChannel</classname> is
|
|
intended for sending only. Since it broadcasts to its subscribers directly when its
|
|
<methodname>send(Message)</methodname> method is invoked, consumers cannot receive
|
|
Messages by invoking <methodname>receive()</methodname>. Instead, any subscriber must
|
|
be a <interfacename>MessageTarget</interfacename> itself, and the subscriber's
|
|
<methodname>send(Message)</methodname> method will be invoked in turn.
|
|
</para>
|
|
</section>
|
|
<section id="api-messagechannel-queuechannel">
|
|
<title>QueueChannel</title>
|
|
<para>
|
|
The <classname>QueueChannel</classname> implementation wraps a queue. Unlike, the
|
|
<classname>PublishSubscribeChannel</classname>, the <classname>QueueChannel</classname> 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:
|
|
<programlisting language="java">public QueueChannel(int capacity)</programlisting>
|
|
A channel that has not reached its capacity limit will store messages in its internal queue, and the
|
|
<methodname>send()</methodname> 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 <methodname>send()</methodname> and
|
|
<methodname>receive()</methodname> will block indefinitely.
|
|
</para>
|
|
</section>
|
|
<section id="api-messagechannel-prioritychannel">
|
|
<title>PriorityChannel</title>
|
|
<para>
|
|
Whereas the <classname>QueueChannel</classname> enforces first-in/first-out (FIFO) ordering, the
|
|
<classname>PriorityChannel</classname> 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
|
|
'<literal>priority</literal>' property within each message's header. However, for custom priority determination
|
|
logic, a comparator of type <classname>Comparator<Message<?>></classname> can be provided to the
|
|
<classname>PriorityChannel</classname>'s constructor.
|
|
</para>
|
|
</section>
|
|
<section id="api-messagechannel-rendezvouschannel">
|
|
<title>RendezvousChannel</title>
|
|
<para>
|
|
The <classname>RendezvousChannel</classname> enables a "direct-handoff" scenario where a sender will block
|
|
until another party invokes the channel's <methodname>receive()</methodname> method or vice-versa. Internally,
|
|
this implementation is quite similar to the <classname>QueueChannel</classname> except that it uses a
|
|
<classname>SynchronousQueue</classname> (a zero-capacity implementation of
|
|
<interfacename>BlockingQueue</interfacename>). 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
|
|
<classname>QueueChannel</classname>, the message would have been stored to the internal queue and potentially
|
|
never received.
|
|
</para>
|
|
<para>
|
|
The <classname>RendezvousChannel</classname> is also useful for implementing request-reply
|
|
operations. The sender can create a temporary, anonymous instance of <classname>RendezvousChannel</classname>
|
|
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.
|
|
</para>
|
|
</section>
|
|
<section id="api-messagechannel-directchannel">
|
|
<title>DirectChannel</title>
|
|
<para>
|
|
The <classname>DirectChannel</classname> has point-to-point semantics, but otherwise is more similar
|
|
to the <classname>PublishSubscribeChannel</classname> 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 <classname>DirectChannel</classname>, then sending a
|
|
Message to that channel will trigger invocation of that target's <methodname>send(Message)</methodname>
|
|
method <emphasis>directly in the sender's thread</emphasis>. 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).
|
|
</para>
|
|
</section>
|
|
<section id="api-messagechannel-threadlocalchannel">
|
|
<title>ThreadLocalChannel</title>
|
|
<para>
|
|
The final channel implementation type is <classname>ThreadLocalChannel</classname>. 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
|
|
<classname>DirectChannels</classname> 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
|
|
<classname>ThreadLocalChannel</classname>, the original sending thread could collect its replies.
|
|
</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="api-channelinterceptor">
|
|
<title>ChannelInterceptor</title>
|
|
<para>
|
|
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
|
|
<interfacename>Messages</interfacename> are being sent to and received from
|
|
<interfacename>MessageChannels</interfacename>, those channels provide an opportunity for intercepting
|
|
the send and receive operations. The <interfacename>ChannelInterceptor</interfacename> strategy interface
|
|
provides methods for each of those operations:
|
|
<programlisting language="java"><![CDATA[public interface ChannelInterceptor {
|
|
boolean preSend(Message<?> message, MessageChannel channel);
|
|
void postSend(Message<?> message, MessageChannel channel, boolean sent);
|
|
boolean preReceive(MessageChannel channel);
|
|
void postReceive(Message<?> message, MessageChannel channel);
|
|
}]]></programlisting>
|
|
After implementing the interface, registering the interceptor with a channel is just a matter of calling:
|
|
<programlisting language="java">channel.addInterceptor(someChannelInterceptor);</programlisting>
|
|
The methods that return a <literal>boolean</literal> value can return '<literal>false</literal>' to prevent the
|
|
send or receive operation from proceeding (send would return 'false' and receive would return 'null').
|
|
</para>
|
|
<para>
|
|
Because it is rarely necessary to implement all of the interceptor methods, a
|
|
<classname>ChannelInterceptorAdapter</classname> class is also available for sub-classing. It provides no-op
|
|
methods (the <literal>void</literal> methods are empty, and the <literal>boolean</literal> methods return
|
|
<literal>true</literal>). Therefore, it is often easiest to extend that class and just implement the method(s)
|
|
that you need as in the following example.
|
|
<programlisting language="java"><![CDATA[public class CountingChannelInterceptor extends ChannelInterceptorAdapter {
|
|
|
|
private final AtomicInteger sendCount = new AtomicInteger();
|
|
|
|
@Override
|
|
public boolean preSend(Message<?> message, MessageChannel channel) {
|
|
sendCount.incrementAndGet();
|
|
return true;
|
|
}
|
|
}]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-messagehandler">
|
|
<title>MessageHandler</title>
|
|
<para>
|
|
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 <interfacename>Messages</interfacename>:
|
|
<programlisting language="java">public interface MessageHandler {
|
|
Message<?> handle(Message<?> message);
|
|
}</programlisting>
|
|
The handler plays an important role, since it is typically responsible for translating between the generic
|
|
<interfacename>Message</interfacename> 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.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-messagebus">
|
|
<title>MessageBus</title>
|
|
<para>
|
|
So far, you have seen that the <interfacename>MessageChannel</interfacename> provides a
|
|
<methodname>receive()</methodname> method that returns a <interfacename>Message</interfacename>, and the
|
|
<interfacename>MessageHandler</interfacename> provides a <methodname>handle()</methodname> method that accepts a
|
|
<interfacename>Message</interfacename>, but how do the messages get passed from the channel to the handler?
|
|
As mentioned earlier, the <classname>MessageBus</classname> 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.
|
|
</para>
|
|
<para>
|
|
The <interfacename>MessageBus</interfacename> 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
|
|
<interfacename>MessageChannels</interfacename> and endpoints, such as <emphasis>Channel Adapters</emphasis>
|
|
and <emphasis>Service Activators</emphasis>. It recognizes any of these instances that have been defined
|
|
within its <interfacename>ApplicationContext</interfacename>.
|
|
</para>
|
|
<para>
|
|
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).
|
|
</para>
|
|
<para>
|
|
The bus creates and schedules triggers for all of its registered endpoints. When an endpoint
|
|
receives a trigger event, it will poll the <interfacename>MessageSource</interfacename> that
|
|
was provided in its metadata. For example, a <emphasis>Channel Adapter</emphasis> will poll the
|
|
referenced "source", and a <emphasis>Service Activator</emphasis> will poll the referenced
|
|
"input-channel".
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-messageendpoint">
|
|
<title>MessageEndpoint</title>
|
|
<para>
|
|
As described in <xref linkend="overview"/>, there are different types of Message Endpoint, such
|
|
as the <emphasis>Channel Adapter</emphasis> (inbound or outbound) and the <emphasis>Service Activator</emphasis>.
|
|
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
|
|
<classname>MessageBus</classname> can manage its connection to a channel and its polling schedule.
|
|
</para>
|
|
<para>
|
|
The scheduling metadata is provided as an implementation of the <interfacename>Schedule</interfacename> interface.
|
|
This is an abstraction designed to allow extensibility of schedulers for messaging tasks. Currently, there is a
|
|
single implementation named <classname>PollingSchedule</classname> and the endpoint may set the
|
|
<emphasis>period</emphasis> property. The polling period may differ depending on the type of MessageSource
|
|
(e.g. file-system vs. JMS).
|
|
</para>
|
|
<para>
|
|
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 <classname>ConcurrencyInterceptor</classname>
|
|
for this very purpose. The interceptor's configuration is provided by the
|
|
<classname>ConcurrencyPolicy</classname> metadata object. When the <interfacename>MessageBus</interfacename>
|
|
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 <classname>ConcurrencyPolicy</classname> and their default values:
|
|
<table id="api-messagebus-concurrencypolicy">
|
|
<title>Properties of the ConcurrencyPolicy</title>
|
|
<tgroup cols="3">
|
|
<colspec align="left"/>
|
|
<thead>
|
|
<row>
|
|
<entry align="center">Property Name</entry>
|
|
<entry align="center">Default Value</entry>
|
|
<entry align="center">Description</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>coreSize</entry>
|
|
<entry>1</entry>
|
|
<entry>the core size of the thread pool</entry>
|
|
</row>
|
|
<row>
|
|
<entry>maxSize</entry>
|
|
<entry>10</entry>
|
|
<entry>the maximum size the thread pool can reach when under demand</entry>
|
|
</row>
|
|
<row>
|
|
<entry>queueCapacity</entry>
|
|
<entry>0</entry>
|
|
<entry>capacity of the queue which defers an increase of the pool size</entry>
|
|
</row>
|
|
<row>
|
|
<entry>keepAliveSeconds</entry>
|
|
<entry>60</entry>
|
|
<entry>how long added threads (beyond core size) should remain idle before being removed from the pool</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</para>
|
|
<para>
|
|
The details of configuring this and other metadata for each endpoint will be discussed in detail in
|
|
<xref linkend="namespace-endpoint"/>.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-messageselector">
|
|
<title>MessageSelector</title>
|
|
<para>
|
|
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 <emphasis>dynamic</emphasis> logic to
|
|
determine what messages the endpoint should receive. The <interfacename>MessageSelector</interfacename>
|
|
strategy interface fulfills that role.
|
|
<programlisting language="java"><![CDATA[public interface MessageSelector {
|
|
boolean accept(Message<?> message);
|
|
}]]></programlisting>
|
|
A <interfacename>MessageEndpoint</interfacename> 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
|
|
<classname>PayloadTypeSelector</classname> provides similar functionality to Datatype Channels
|
|
(as described in <xref linkend="namespace-channel"/>) except that in this case the type-matching can be done
|
|
by the endpoint rather than the channel.
|
|
<programlisting language="java"><![CDATA[PayloadTypeSelector selector = new PayloadTypeSelector(String.class, Integer.class);
|
|
assertTrue(selector.accept(new StringMessage("example")));
|
|
assertTrue(selector.accept(new GenericMessage<Integer>(123)));
|
|
assertFalse(selector.accept(new GenericMessage<SomeObject>(someObject)));
|
|
]]></programlisting>
|
|
Another simple but useful <interfacename>MessageSelector</interfacename> provided out-of-the-box is the
|
|
<classname>UnexpiredMessageSelector</classname>. As the name suggests, it only accepts messages that have
|
|
not yet expired.
|
|
</para>
|
|
<para>
|
|
Essentially, using a selector provides <emphasis>reactive</emphasis> routing whereas the Datatype Channel
|
|
and Message Router provide <emphasis>proactive</emphasis> routing. However, selectors accommodate additional
|
|
uses. For example, the <interfacename>MessageChannel</interfacename>'s 'purge' method accepts a selector:
|
|
<programlisting language="java">channel.purge(someSelector);</programlisting>
|
|
There is a <classname>ChannelPurger</classname> utility class whose purge operation is a good candidate for
|
|
Spring's JMX support:
|
|
<programlisting language="java">ChannelPurger purger = new ChannelPurger(new ExampleMessageSelector(), channel);
|
|
purger.purge();</programlisting>
|
|
</para>
|
|
<para>
|
|
Implementations of <interfacename>MessageSelector</interfacename> might provide opportunities for reuse on
|
|
channels in addition to endpoints. For that reason, Spring Integration provides a simple selector-wrapping
|
|
<interfacename>ChannelInterceptor</interfacename> that accepts one or more selectors in its constructor.
|
|
<programlisting language="java">MessageSelectingInterceptor interceptor =
|
|
new MessageSelectingInterceptor(selector1, selector2);
|
|
channel.addInterceptor(interceptor);</programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-requestreplytemplate">
|
|
<title>RequestReplyTemplate</title>
|
|
<para>
|
|
Whereas the <interfacename>MessageHandler</interfacename> interface provides the foundation for many of the
|
|
components that enable non-invasive invocation of your application code <emphasis>from the messaging
|
|
system</emphasis>, sometimes it is necessary to invoke the messaging system <emphasis>from your application
|
|
code</emphasis>. Spring Integration provides a <classname>RequestReplyTemplate</classname> that supports a
|
|
variety of request-reply scenarios. For example, it is possible to send a request and wait for a reply.
|
|
<programlisting language="java">RequestReplyTemplate template = new RequestReplyTemplate(requestChannel);
|
|
Message reply = template.request(new StringMessage("test"));</programlisting>
|
|
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.
|
|
<programlisting language="java">RequestReplyTemplate template = new RequestReplyTemplate(requestChannel);
|
|
template.setReplyChannel(replyChannel);
|
|
Message reply = template.request(new StringMessage("test"));</programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="api-gateway">
|
|
<title>MessagingGateway</title>
|
|
<para>
|
|
Even though the <classname>RequestReplyTemplate</classname> 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 <classname>SimpleMessagingGateway</classname> with the following methods:
|
|
<programlisting language="java">public void send(Object object) { ... }
|
|
public Object receive() { ... }
|
|
public Object sendAndReceive(Object object) { ... }
|
|
</programlisting>
|
|
It enables configuration of a request and/or reply channel and delegates to the
|
|
<interfacename>MessageMapper</interfacename> and <interfacename>MessageCreator</interfacename> strategy
|
|
interfaces.
|
|
<programlisting language="java">SimpleMessagingGateway gateway = new SimpleMessagingGateway();
|
|
gateway.setRequestChannel(requestChannel);
|
|
gateway.setReplyChannel(replyChannel);
|
|
gateway.setMessageCreator(messageCreator);
|
|
gateway.setMessageMapper(messageMapper);
|
|
Object result = gateway.sendAndReceive("test");
|
|
</programlisting>
|
|
</para>
|
|
<para>
|
|
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 <classname>GatewayProxyFactoryBean</classname> 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.
|
|
<programlisting language="xml"><![CDATA[<gateway id="fooService"
|
|
service-interface="org.example.FooService"
|
|
request-channel="requestChannel"
|
|
reply-channel="replyChannel"
|
|
message-creator="messageCreator"
|
|
message-mapper="messageMapper"/>]]></programlisting>
|
|
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.
|
|
</para>
|
|
</section>
|
|
</chapter> |