Files
spring-integration/spring-integration-reference/src/core-api.xml
2008-07-08 03:33:24 +00:00

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&lt;T&gt; {
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&lt;String,Object&gt;</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
The base implementation of the <interfacename>Message</interfacename> interface is
<classname>GenericMessage&lt;T&gt;</classname>, and it provides three constructors:
<programlisting language="java">new GenericMessage&lt;T&gt;(Object id, T payload);
new GenericMessage&lt;T&gt;(T payload);
new GenericMessage&lt;T&gt;(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&lt;T&gt; {
Message&lt;T&gt; 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&lt;?&gt; 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 &lt;channel-adapter&gt; 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&lt;?&gt; 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&lt;Message&lt;?&gt;&gt;</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&lt;?&gt; handle(Message&lt;?&gt; 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>