Files
spring-integration/spring-integration-reference/src/configuration.xml
2008-11-26 05:52:44 +00:00

258 lines
17 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">
<appendix id="configuration">
<title>Configuration</title>
<section id="configuration-introduction">
<title>Introduction</title>
<para>
Spring Integration offers a number of configuration options. Which option you choose depends upon your particular
needs and at what level you prefer to work. As with the Spring framework in general, it is also possible to mix
and match the various techniques according to the particular problem at hand. For example, you may choose the
XSD-based namespace for the majority of configuration combined with a handful of objects that are configured with
annotations. As much as possible, the two provide consistent naming. XML elements defined by the XSD schema will
match the names of annotations, and the attributes of those XML elements will match the names of annotation
properties. Direct usage of the API is of course always an option, but we expect that most users will choose one
of the higher-level options, or a combination of the namespace-based and annotation-driven configuration.
</para>
</section>
<section id="configuration-namespace">
<title>Namespace Support</title>
<para>
Spring Integration components can be configured with XML elements that map directly to the terminology and
concepts of enterprise integration. In many cases, the element names match those of the
<ulink url="http://www.eaipatterns.com">Enterprise Integration Patterns</ulink>.
</para>
<para>
To enable Spring Integration's core namespace support within your Spring configuration files, add the following
namespace reference and schema mapping in your top-level 'beans' element:
<programlisting language="xml"><![CDATA[<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
]]><emphasis>xmlns:integration="http://www.springframework.org/schema/integration"</emphasis><![CDATA[
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
]]><emphasis>http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd"</emphasis>&gt;</programlisting>
</para>
<para>
You can choose any name after "xmlns:"; <emphasis>integration</emphasis> is used here for clarity, but you might
prefer a shorter abbreviation. Of course if you are using an XML-editor or IDE support, then the availability of
auto-completion may convince you to keep the longer name for clarity. Alternatively, you can create configuration
files that use the Spring Integration schema as the primary namespace:
<programlisting language="xml"><emphasis>&lt;beans:beans xmlns="http://www.springframework.org/schema/integration"</emphasis><![CDATA[
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
]]><emphasis>xmlns:beans="http://www.springframework.org/schema/beans"</emphasis><![CDATA[
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">]]></programlisting>
</para>
<para>
When using this alternative, no prefix is necessary for the Spring Integration elements. On the other hand, if
you want to define a generic Spring "bean" within the same configuration file, then a prefix would be required
for the bean element (&lt;beans:bean ... /&gt;). Since it is generally a good idea to modularize the
configuration files themselves based on responsibility and/or architectural layer, you may find it appropriate to
use the latter approach in the integration-focused configuration files, since generic beans are seldom necessary
within those same files. For purposes of this documentation, we will assume the "integration" namespace is
primary.
</para>
<para>
Many other namespaces are provided within the Spring Integration distribution. In fact, each adapter type (JMS,
File, etc.) that provides namespace support defines its elements within a separate schema. In order to use these
elements, simply add the necessary namespaces with an "xmlns" entry and the corresponding "schemaLocation" mapping.
For example, the following root element shows several of these namespace declarations:
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xmlns:mail="http://www.springframework.org/schema/integration/mail"
xmlns:rmi="http://www.springframework.org/schema/integration/rmi"
xmlns:ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms-1.0.xsd
http://www.springframework.org/schema/integration/mail
http://www.springframework.org/schema/integration/mail/spring-integration-mail-1.0.xsd
http://www.springframework.org/schema/integration/rmi
http://www.springframework.org/schema/integration/rmi/spring-integration-rmi-1.0.xsd
http://www.springframework.org/schema/integration/ws
http://www.springframework.org/schema/integration/ws/spring-integration-ws-1.0.xsd">
...
</beans>]]></programlisting>
The reference manual provides specific examples of the various elements in their corresponding chapters. Here, the
main thing to recognize is the consistency of the naming for each namespace URI and schema location.
</para>
</section>
<section id="namespace-taskscheduler">
<title>Configuring the Task Scheduler</title>
<para>
In Spring Integration, the ApplicationContext plays the central role of a Message Bus, and there are only a
couple configuration options to be aware of. First, you may want to control the central TaskScheduler instance.
You can do so by providing a single bean with the name "taskScheduler". This is also defined as a constant:
<programlisting><![CDATA[ IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME ]]></programlisting>
By default Spring Integration uses the <classname>SimpleTaskScheduler</classname> implementation. That in turn
just delegates to any instance of Spring's <interfacename>TaskExecutor</interfacename> abstraction. Therefore,
it's rather trivial to supply your own configuration. The "taskScheduler" bean is then responsible for managing
all pollers. The TaskScheduler will startup automatically by default. If you provide your own instance of
SimpleTaskScheduler however, you can set the 'autoStartup' property to <emphasis>false</emphasis> instead.
</para>
<para>
When the endpoints are concurrency-enabled with their own 'taskExecutor' reference, the invocation of the
handling methods will happen within that executor's thread pool and not the main scheduler pool. However,
when no task-executor is provided for an endpoint's poller, it will be invoked in the dispatcher's thread
(with the exception of subscribable channels where the subscribers will be invoked directly). The next
section will describe what happens if Exceptions occur within the asynchronous invocations.
</para>
</section>
<section id="namespace-errorhandler">
<title>Error Handling</title>
<para>
As described in the overview at the very beginning of this manual, one of the main motivations behind a
Message-oriented framework like Spring Integration is to promote loose-coupling between components. The
Message Channel plays an important role in that producers and consumers do not have to know about each
other. However, the advantages also have some drawbacks. Some things become more complicated in a very
loosely coupled environment, and one example is error handling.
</para>
<para>
When sending a Message to a channel, the component that ultimately handles that Message may or may not
be operating within the same thread as the sender. If using a simple default DirectChannel (with the
&lt;channel&gt; element that has no &lt;queue&gt; sub-element), the handling will be in the same thread.
In that case, if an Exception is thrown, it will be catch-able by the sender (or it may propagate past
the sender if it is an uncaught RuntimeException). So far, everything is fine. This is the same behavior
as an Exception-throwing operation in a normal call stack. However, when adding the asynchronous aspect,
things become much more complicated. For instance, if the 'channel' element <emphasis>does</emphasis>
provide a 'queue' sub-element, then the component that handles the Message <emphasis>will</emphasis> be
operating in a different thread than the sender. The sender may have dropped the Message and moved on to
other things. There is no way for the Exception to be thrown directly back to that sender using standard
Exception throwing techniques. Instead, to handle errors for asynchronous processes requires an
asynchronous error-handling mechanism as well.
</para>
<para>
Spring Integration supports error handling for its components by publishing errors to a Message Channel.
Specifically, the Exception will become the payload of a Spring Integration Message. That Message will
then be sent to a Message Channel that is resolved in a way that is very similar to the 'replyChannel'
resolution. First, if the request Message being handled at the time the Exception occurred contains
an 'errorChannel' header (the header name is defined in the constant: MessageHeaders.ERROR_CHANNEL),
the ErrorMessage will be sent to that channel. Otherwise, the error handler will send to a "global"
channel whose bean name is "errorChannel" (this is also defined as a constant:
IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME).
</para>
<para>
Whenever relying on Spring Integration's XML namespace support, a default "errorChannel" bean will be
created behind the scenes. However, you can just as easily define your own if you want to control the
settings.
<programlisting language="xml"><![CDATA[ <channel id="errorChannel" capacity="500"/>]]></programlisting>
</para>
<para>
The most important thing to understand here is that the Messaging-based error handling will only apply
to Exceptions that are thrown by a Spring Integration task that is executing within a TaskExecutor.
This does <emphasis>not</emphasis> apply to Exceptions thrown by a handler that is operating within
the same thread as the sender (e.g. through a DirectChannel as described above). However, when
Exceptions occur in a scheduled poller task's execution, those exceptions will be wrapped in
<classname>ErrorMessages</classname> and sent to the 'errorChannel' by default. To enable global error
handling, simply register a handler on that channel. For example, you can configure Spring Integration's
<classname>ErrorMessageExceptionTypeRouter</classname> as the handler of an endpoint that is subscribed to the
'errorChannel'. That router can then spread the error messages across multiple channels based on
<classname>Exception</classname> type.
</para>
</section>
<section id="annotations">
<title>Annotation Support</title>
<para>
In addition to the XML namespace support for configuring Message Endpoints, it is also possible to use
annotations. First, Spring Integration provides the class-level <interfacename>@MessageEndpoint</interfacename>
as a <emphasis>stereotype</emphasis> annotation meaning that is itself annotated with Spring's @Component
annotation and therefore is recognized automatically as a bean definition when using Spring component-scanning.
</para>
<para>
Even more importantly are the various Method-level annotations that indicate the annotated method is capable of
handling a message. The following example demonstrates both:
<programlisting language="java">@MessageEndpoint
public class FooService {
@ServiceActivator
public void processMessage(Message message) {
...
}
}</programlisting>
</para>
<para>
Exactly what it means for the method to "handle" the Message depends on the particular annotation. The following
are available with Spring Integration, and the behavior of each is described in its own chapter or section within
this reference: @Transformer, @Router, @Splitter, @Aggregator, @ServiceActivator, and @ChannelAdapter.
</para>
<note>
The @MessageEndpoint is not required. If you want to configure a POJO reference from the "ref" attribute
of a &lt;service-activator/&gt; element, it is sufficient to provide the method-level annotations.
</note>
<para>
In most cases, the annotated handler method should not require the <classname>Message</classname> type as its
parameter. Instead, the method parameter type can match the message's payload type.
<programlisting language="java">public class FooService {
@Handler
public void bar(<emphasis>Foo foo</emphasis>) {
...
}
}</programlisting>
</para>
<para>
When the method parameter should be mapped from a value in the <classname>MessageHeader</classname>, another
option is to use the parameter-level <interfacename>@Header</interfacename> annotation. In general, methods
annotated with the Spring Integration annotations can either accept the <classname>Message</classname> itself, the
message payload, or a header value (with @Header) as the parameter. In fact, the method can accept a combination,
such as:
<programlisting language="java">public class FooService {
@ServiceActivator
public void bar(String payload, @Header("x") int valueX, @Header("y") int valueY) {
...
}
}</programlisting>
There is also a @Headers annotation that provides all of the Message headers as a Map:
<programlisting language="java">public class FooService {
@ServiceActivator
public void bar(String payload, @Headers Map&lt;String, Object&gt; headerMap) {
...
}
}</programlisting>
</para>
<para>
For several of these annotations, when a Message-handling method returns a non-null value, the endpoint will
attempt to send a reply. This is consistent across both configuration options (namespace and annotations) in that
the such an endpoint's output channel will be used if available, and the message header's REPLY_CHANNEL value
will be the fallback.
</para>
<para>
In addition to the examples shown here, these annotations also support inputChannel and outputChannel properties.
<programlisting language="java">public class FooService {
@ServiceActivator(inputChannel="input", outputChannel="output")
public void bar(String payload, @Headers Map&lt;String, Object&gt; headerMap) {
...
}
}</programlisting>
That provides a pure annotation-driven alternative to the XML configuration. However, it is generally recommended
to use XML for the endpoints, since it is easier to keep track of the overall configuration in a single, external
location (and besides the XML configuration is not very verbose). If you do prefer to provide channels with the
annotations however, you just need to enable a BeanPostProcessor. The following element should be added:
<programlisting language="xml"><![CDATA[ <annotation-config/> ]]></programlisting>
</para>
</section>
</appendix>