Moved the location of the documentation up a level
This commit is contained in:
444
spring-integration-reference/src/core-api.xml
Normal file
444
spring-integration-reference/src/core-api.xml
Normal file
@@ -0,0 +1,444 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>public interface Message<T> {
|
||||
Object getId();
|
||||
MessageHeader getHeader();
|
||||
T getPayload();
|
||||
boolean isExpired();
|
||||
}</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>int</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>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, attributes, and any 'returnAddress' 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 <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-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><![CDATA[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);
|
||||
}]]></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.
|
||||
The <classname>SimpleChannel</classname> implementation wraps a queue. It provides a no-argument constructor as
|
||||
well as a constructor that accepts the queue capacity:
|
||||
<programlisting>public SimpleChannel(int capacity)</programlisting>
|
||||
Specifying a capacity of 0 will create a "direct-handoff" channel where a sender will block until the channel's
|
||||
<methodname>receive()</methodname> method is called. Otherwise 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.
|
||||
</para>
|
||||
<para>
|
||||
Whereas the <classname>SimpleChannel</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-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><![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>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><![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>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>
|
||||
There is a rather obvious gap in what we have reviewed thus far. 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 so the short answer is: you don't need to worry about it.
|
||||
Nevertheless since this is a reference guide, we will explore this in a bit of detail.
|
||||
</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 fundamental responsibilities is to manage registration of the
|
||||
<interfacename>MessageChannels</interfacename> and <interfacename>MessageHandlers</interfacename>. It provides
|
||||
the following methods:
|
||||
<programlisting>public void registerChannel(String name, MessageChannel channel)
|
||||
public void registerHandler(String name, MessageHandler handler, Subscription subscription)
|
||||
public void registerHandler(String name, MessageHandler handler, Subscription subscription,
|
||||
ConcurrencyPolicy concurrencyPolicy)</programlisting>
|
||||
As those method signatures reveal, the message bus is handling several of the concerns here so that the channel
|
||||
and handler objects can be as simple as possible. These responsibilities include the creation and lifecycle
|
||||
management of message dispatchers, the activation of handler subscriptions, and the configuration of thread
|
||||
pools. The bus coordinates all of that behavior based upon the metadata provided via these registration methods,
|
||||
and typically developers will not even use this API directly since the metadata can be provided in XML and/or
|
||||
annotations. We will briefly take a look at each of those metadata objects.
|
||||
</para>
|
||||
<para>
|
||||
The bus creates and manages dispatchers that pull messages from a channel in order to push those messages to
|
||||
handlers subscribed to that channel. Each channel has a <classname>DispatcherPolicy</classname> that contains
|
||||
metadata for configuring those dispatchers:
|
||||
<table id="api-messagebus-dispatcherpolicy">
|
||||
<title>Properties of the DispatcherPolicy</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>publishSubscribe</entry>
|
||||
<entry>false</entry>
|
||||
<entry>whether the dispatcher should attempt to publish to all of its handlers (rather than just one)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>maxMessagesPerTask</entry>
|
||||
<entry>1</entry>
|
||||
<entry>maximum number of messages to retrieve per poll</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>receiveTimeout</entry>
|
||||
<entry>1000 (milliseconds)</entry>
|
||||
<entry>how long to block on the receive call (0 for no blocking, -1 for indefinite block)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>rejectionLimit</entry>
|
||||
<entry>5</entry>
|
||||
<entry>maximum number of attempts to invoke handlers (e.g. no threads available)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>retryInterval</entry>
|
||||
<entry>1000 (milliseconds)</entry>
|
||||
<entry>amount of time to wait between successive attempts to invoke handlers</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>shouldFailOnRejectionLimit</entry>
|
||||
<entry>true</entry>
|
||||
<entry>whether to throw a <classname>MessageDeliveryException</classname> if the 'rejectionLimit' is
|
||||
reached - if this is set to 'false', then such undeliverable messages would be dropped silently</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
<para>
|
||||
The bus registers handlers with a channel's dispatcher based upon the <classname>Subscription</classname>
|
||||
metadata provided to the <methodname>registerHandler()</methodname> method.
|
||||
<table id="api-messagebus-subscription">
|
||||
<title>Properties of the Subscription</title>
|
||||
<tgroup cols="2">
|
||||
<colspec align="left" />
|
||||
<thead>
|
||||
<row>
|
||||
<entry align="center">Property Name</entry>
|
||||
<entry align="center">Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>channel</entry>
|
||||
<entry>the channel instance to subscribe to (an object reference)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>channelName</entry>
|
||||
<entry>the name of the channel to subscribe to - only used as a fallback if 'channel' is null</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>schedule</entry>
|
||||
<entry>the scheduling metadata (see below)</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
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 called <classname>PollingSchedule</classname> that provides the following
|
||||
properties:
|
||||
<table id="api-messagebus-pollingschedule">
|
||||
<title>Properties of the PollingSchedule</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>period</entry>
|
||||
<entry>N/A</entry>
|
||||
<entry>the delay interval between each poll</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>initialDelay</entry>
|
||||
<entry>0</entry>
|
||||
<entry>the delay prior to the first poll</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>timeUnit</entry>
|
||||
<entry>TimeUnit.MILLISECONDS</entry>
|
||||
<entry>time unit for 'period' and 'initialDelay'</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>fixedRate</entry>
|
||||
<entry>false</entry>
|
||||
<entry>'false' indicates fixed-delay (no backlog)</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
The <classname>PollingSchedule</classname> constructor requires the 'period' value.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>ConcurrencyPolicy</classname> is an optional parameter to provide when registering a handler.
|
||||
When the <interfacename>MessageBus</interfacename> registers a handler, it will use these properties to configure
|
||||
that handler's thread pool. These parameters are configurable on a per-handler basis since 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 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>
|
||||
</section>
|
||||
|
||||
<section id="api-messageendpoint">
|
||||
<title>MessageEndpoint</title>
|
||||
<para>
|
||||
When <interfacename>MessageHandlers</interfacename> are registered with the <classname>MessageBus</classname>,
|
||||
the bus assigns the handler to a dispatcher based on the provided schedule as described above. Internally, the
|
||||
bus is creating and registering an instance that implements the <interfacename>MessageEndpoint</interfacename>
|
||||
interface. This is where other handler metadata enters the picture (e.g. the concurrency settings). Basically,
|
||||
you can consider the endpoint to be a composite handler built from a simple implementation of the
|
||||
<interfacename>MessageHandler</interfacename> along with its metadata. In fact, the
|
||||
<interfacename>MessageEndpoint</interfacename> does extend the <interfacename>MessageHandler</interfacename>
|
||||
interface.
|
||||
<programlisting>public interface MessageEndpoint extends MessageHandler {
|
||||
String getName();
|
||||
Subscription getSubscription();
|
||||
ConcurrencyPolicy getConcurrencyPolicy();
|
||||
}</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
When using the API, it's simpler to register handlers with metadata and leave the message endpoint as an internal
|
||||
responsibility of the bus. However, it is possible to create endpoints directly. Spring Integration provides a
|
||||
single implementation: <classname>DefaultMessageEndpoint</classname>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="api-messageselector">
|
||||
<title>MessageSelector</title>
|
||||
<para>
|
||||
As described above, when a <interfacename>MessageHandler</interfacename> is registered with the message bus, it
|
||||
is hosted by an endpoint and thereby subscribed to a channel. Often it is necessary to provide additional
|
||||
<emphasis>dynamic</emphasis> logic to determine what messages the handler should receive. The
|
||||
<interfacename>MessageSelector</interfacename> strategy interface fulfills that role.
|
||||
<programlisting><![CDATA[public interface MessageSelector {
|
||||
boolean accept(Message<?> message);
|
||||
}]]></programlisting>
|
||||
A <interfacename>MessageEndpoint</interfacename> can be configured with zero or more selectors, 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><![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>channel.purge(someSelector);</programlisting>
|
||||
There is even a <classname>ChannelPurger</classname> utility class whose purge operation is a good candidate for
|
||||
Spring's JMX support:
|
||||
<programlisting>ChannelPurger purger = new ChannelPurger(channel, new ExampleMessageSelector());
|
||||
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>MessageSelectingInterceptor interceptor = new MessageSelectingInterceptor(selector1, selector2);
|
||||
channel.addInterceptor(interceptor);</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user