224 lines
14 KiB
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<?> 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
|
|
<transactional/> 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 <thread-pool-task-executor/> 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 <poller/> 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> |