Files
spring-integration/spring-integration-reference/src/endpoint.xml

224 lines
14 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="endpoint">
<title>Message Endpoints</title>
<para>
As mentioned in the overview, Message Endpoints are responsible for connecting the various messaging components to
channels. Over the next several chapters, you will see a number of different components that consume Messages. Some
of these are also capable of sending reply Messages. Sending Messages is quite straightforward. As shown above in
<xref linkend="channel"/>, it's easy to <emphasis>send</emphasis> a Message to a Message Channel. However,
receiving is a bit more complicated. The main reason is that there are two types of consumers:
<ulink url="http://www.eaipatterns.com/PollingConsumer.html">Polling Consumers</ulink> and
<ulink url="http://www.eaipatterns.com/EventDrivenConsumer.html">Event-Driven Consumers</ulink>.
</para>
<para>
Of the two, Event-Driven Consumers are much simpler. Without any need to manage and schedule a separate poller
thread, they are essentially just listeners with a callback method. When connecting to one of Spring Integration's
subscribable Message Channels, this simple option works great. However, when connecting to a buffering, pollable
Message Channel, some component has to schedule and manage the polling thread(s). Spring Integration provides
two different endpoint implementations to accommodate these two types of consumers. Therefore, the consumers
themselves can simply implement the callback interface. When polling is required, the endpoint acts as a
"container" for the consumer instance. The benefit is similar to that of using a container for hosting
Message-Driven Beans, but since these consumers are simply Spring-managed Objects running within an
ApplicationContext, it more closely resembles Spring's own MessageListener containers.
</para>
<section id="endpoint-handler">
<title>Message Handler</title>
<para>
Spring Integration's <interfacename>MessageHandler</interfacename> interface is implemented by many of the
components within the framework. In other words, this is not part of the public API, and a developer would not
typically implement <interfacename>MessageHandler</interfacename> directly. Nevertheless, it is used by a Message
Consumer for actually handling the consumed Messages, and so being aware of this strategy interface does help in
terms of understanding the overall role of a consumer. The interface is defined as follows:
<programlisting language="java">public interface MessageHandler {
void handleMessage(Message&lt;?&gt; message);
}</programlisting>
Despite its simplicity, this provides the foundation for most of the components that will be covered in the
following chapters (Routers, Transformers, Splitters, Aggregators, Service Activators, etc). Those components
each perform very different functionality with the Messages they handle, but the requirements for actually
receiving a Message are the same, and the choice between polling and event-driven behavior is also the same.
Spring Integration provides two endpoint implementations that "host" these callback-based handlers and allow
them to be connected to Message Channels.
</para>
</section>
<section id="endpoint-eventdrivenconsumer">
<title>Event-Driven Consumer</title>
<para>
Because it is the simpler of the two, we will cover the Event-Driven Consumer endpoint first. You may recall that
the <interfacename>SubscribableChannel</interfacename> interface provides a <methodname>subscribe()</methodname>
method and that the method accepts a <interfacename>MessageHandler</interfacename> parameter (as shown in
<xref linkend="channel-interfaces-subscribablechannel"/>):
<programlisting language="java">
subscribableChannel.subscribe(messageHandler);
</programlisting>
Since a handler that is subscribed to a channel does not have to actively poll that channel, this is an
Event-Driven Consumer, and the implementation provided by Spring Integration accepts a
a <interfacename>SubscribableChannel</interfacename> and a <interfacename>MessageHandler</interfacename>:
<programlisting language="java">SubscribableChannel channel = (SubscribableChannel) context.getBean("exampleSubscribableChannel");
EventDrivenConsumer consumer = new EventDrivenConsumer(channel, exampleHandler);</programlisting>
</para>
</section>
<section id="endpoint-pollingconsumer">
<title>Polling Consumer</title>
<para>
Spring Integration also provides a <classname>PollingConsumer</classname>, and it can be instantiated in
the same way except that the channel must implement <interfacename>PollableChannel</interfacename>:
<programlisting language="java">PollableChannel channel = (PollableChannel) context.getBean("examplePollableChannel");
PollingConsumer consumer = new PollingConsumer(channel, exampleHandler);</programlisting>
</para>
<para>
There are many other configuration options for the Polling Consumer. For example, the trigger is a required property:
<programlisting language="java">
PollingConsumer consumer = new PollingConsumer(channel, handler);
consumer.setTrigger(new IntervalTrigger(30, TimeUnit.SECONDS));</programlisting>
Spring Integration currently provides two implementations of the <interfacename>Trigger</interfacename>
interface: <classname>IntervalTrigger</classname> and <classname>CronTrigger</classname>. The
<classname>IntervalTrigger</classname> is typically defined with a simple interval (in milliseconds), but
also supports an 'initialDelay' property and a boolean 'fixedRate' property (the default is false - i.e.
fixed delay):
<programlisting language="java">IntervalTrigger trigger = new IntervalTrigger(1000);
trigger.setInitialDelay(5000);
trigger.setFixedRate(true);</programlisting>
The <classname>CronTrigger</classname> simply requires the cron expression (see the Javadoc for details):
<programlisting language="java">CronTrigger trigger = new CronTrigger("*/10 * * * * MON-FRI");</programlisting>
</para>
<para>
In addition to the trigger, several other polling-related configuration properties may be specified:
<programlisting language="java">
PollingConsumer consumer = new PollingConsumer(channel, handler);
consumer.setMaxMessagesPerPoll(10);
consumer.setReceiveTimeout(5000);</programlisting>
A Polling Consumer may even delegate to a Spring <interfacename>TaskExecutor</interfacename> and
participate in Spring-managed transactions. The following example shows the configuration of both:
<programlisting language="java">
PollingConsumer consumer = new PollingConsumer(channel, handler);
TaskExecutor taskExecutor = (TaskExecutor) context.getBean("exampleExecutor");
consumer.setTaskExecutor(taskExecutor);
PlatformTransactionManager txManager = (PlatformTransationManager) context.getBean("exampleTxManager");
consumer.setTransactionManager(txManager);</programlisting>
The examples above show dependency lookups, but keep in mind that these consumers will most often be configured
as Spring <emphasis>bean definitions</emphasis>. In fact, Spring Integration also provides a
<interfacename>FactoryBean</interfacename> that creates the appropriate consumer type based on the type of
channel, and there is full XML namespace support to even further hide those details. The namespace-based
configuration will be featured as each component type is introduced.
<note>
Many of the <interfacename>MessageHandler</interfacename> implementations are also capable of generating reply
Messages. As mentioned above, sending Messages is trivial when compared to the Message reception. Nevertheless,
<emphasis>when</emphasis> and <emphasis>how many</emphasis> reply Messages are sent depends on the handler
type. For example, an <emphasis>Aggregator</emphasis> waits for a number of Messages to arrive and is often
configured as a downstream consumer for a <emphasis>Splitter</emphasis> which may generate multiple
replies for each Message it handles. When using the namespace configuration, you do not strictly need to know
all of the details, but it still might be worth knowing that several of these components share a common base
class, the <classname>AbstractReplyProducingMessageHandler</classname>, and it provides a
<methodname>setOutputChannel(..)</methodname> method.
</note>
</para>
</section>
<section id="endpoint-namespace">
<title>Namespace Support</title>
<para>
Throughout the reference manual, you will see specific configuration examples for endpoint elements, such as
router, transformer, service-activator, and so on. Most of these will support an "input-channel" attribute and
many will support an "output-channel" attribute. After being parsed, these endpoint elements produce an instance
of either the <classname>PollingConsumer</classname> or the
<classname>EventDrivenConsumer</classname> depending on the type of the "input-channel" that is
referenced: <interfacename>PollableChannel</interfacename> or <interfacename>SubscribableChannel</interfacename>
respectively. When the channel is pollable, then the polling behavior is determined based on the endpoint
element's "poller" sub-element. For example, a simple interval-based poller with a 1-second interval would be
configured like this: <programlisting language="xml"><![CDATA[ <transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<poller>
<interval-trigger interval="1000"/>
</poller>
</transformer>]]></programlisting>
For a poller based on a Cron expression, use the "cron-trigger" child element instead:
<programlisting language="xml"><![CDATA[ <transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<poller>
<cron-trigger expression="*/10 * * * * MON-FRI"/>
</poller>
</transformer>]]></programlisting>
</para>
<para>
If the input channel is a <interfacename>PollableChannel</interfacename>, then the poller configuration is
required. Specifically, as mentioned above, the 'trigger' is a required property of the PollingConsumer class.
Therefore, if you omit the "poller" sub-element for a Polling Consumer endpoint's configuration, an Exception
will be thrown. However, it is also possible to create top-level pollers in which case only a "ref" is required:
<programlisting language="xml"><![CDATA[ <poller id="weekdayPoller">
<cron-trigger expression="*/10 * * * * MON-FRI"/>
</poller>
<transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<poller ref="weekdayPoller"/>
</transformer>]]></programlisting>
In fact, to simplify the configuration, you can define a global default poller. A single top-level poller within
an ApplicationContext may have the default attribute with a value of "true". In that case, any endpoint with a
PollableChannel for its input-channel that is defined within the same ApplicationContext and has no explicitly
configured 'poller' sub-element will use that default.
<programlisting language="xml"><![CDATA[ <poller id="defaultPoller" default="true" max-messages-per-poll="5">
<interval-trigger interval="3" time-unit="SECONDS"/>
</poller>
<!-- No <poller/> sub-element is necessary since there is a default -->
<transformer input-channel="pollable"
ref="transformer"
output-channel="output"/>]]></programlisting>
</para>
<para>
Spring Integration also provides transaction support for the pollers so that each receive-and-forward
operation can be performed as an atomic unit-of-work. To configure transactions for a poller, simply add the
&lt;transactional/&gt; sub-element. The attributes for this element should be familiar to anyone who has
experience with Spring's Transaction management:
<programlisting language="xml"><![CDATA[<poller>
<interval-trigger interval="1000"/>
<transactional transaction-manager="txManager"
propagation="REQUIRES_NEW"
isolation="REPEATABLE_READ"
timeout="10000"
read-only="false"/>
</poller>]]></programlisting>
</para>
<para>
The polling threads may be executed by any instance of Spring's <interfacename>TaskExecutor</interfacename>
abstraction. This enables concurrency for an endpoint or group of endpoints. As a convenience, there is also
namespace support for creating a simple thread pool executor. The &lt;thread-pool-task-executor/&gt; element
defines attributes for common concurrency settings such as core-size, max-size, and queue-capacity. Configuring
a thread-pooling executor can make a substantial difference in how the endpoint performs under load. These
settings are available per-endpoint since the performance of an endpoint is one of the major factors to consider
(the other major factor being the expected volume on the channel to which the endpoint subscribes). To enable
concurrency for a polling endpoint that is configured with the XML namespace support, provide the 'task-executor'
reference on its &lt;poller/&gt; element and then provide one or more of the properties shown below:
<programlisting language="xml"><![CDATA[ <poller task-executor="pool"/>
<interval-trigger interval="5" time-unit="SECONDS"/>
</poller>
<thread-pool-task-executor id="pool"
core-size="5"
max-size="25"
queue-capacity="20"
keep-alive-seconds="120"/>]]></programlisting>
If no 'task-executor' is provided, the consumer's handler will be invoked in the caller's thread. Note that the
"caller" is usually the MessageBus' task scheduler. Also, keep in mind that the 'task-executor' attribute can
provide a reference to any implementation of Spring's <interfacename>TaskExecutor</interfacename> interface by
specifying the bean name. The thread pool elements is simply provided for convenience.
</para>
</section>
</chapter>