Configuration
Introduction 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 yet another option and is described in detail in . We expect that most users will choose one of the higher-level options, such as the namespace-based or annotation-driven configuration.
Namespace Support 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 Enterprise Integration Patterns. To enable Spring Integration's namespace support within your Spring configuration files, add the following namespace reference and schema mapping in your top-level 'beans' element: xmlns:integration="http://www.springframework.org/schema/integration"http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd"> You can choose any name after "xmlns:"; integration 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: <beans:beans xmlns="http://www.springframework.org/schema/integration"xmlns:beans="http://www.springframework.org/schema/beans"]]> 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 (<beans:bean ... />). 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.
Configuring Message Channels To create a Message Channel instance, you can use the generic 'channel' element: <channel id="exampleChannel"/> The default channel type is Point to Point. To create a Publish Subscribe channel, use the "publish-subscribe-channel" element: <publish-subscribe-channel id="exampleChannel"/> To create a Datatype Channel that only accepts messages containing a certain payload type, provide the fully-qualified class name in the channel element's datatype attribute: ]]> Note that the type check passes for any type that is assignable to the channel's datatype. In other words, the "numberChannel" above would accept messages whose payload is java.lang.Integer or java.lang.Double. Multiple types can be provided as a comma-delimited list: ]]> When using the "channel" element, the creation of the channel instances will be deferred to the ChannelFactory bean whose name is "channelFactory" if defined within the ApplicationContext. If no such bean is defined, the default factory will be used. The default implementation is QueueChannelFactory. It is also possible to use more specific elements for the various channel types (as described in ). Depending on the channel, these may provide additional configuration options. Examples of each are shown below.
The <queue-channel/> element To create a QueueChannel, use the "queue-channel" element. By using this element, you can also specify the channel's capacity: <queue-channel id="exampleChannel" capacity="25"/>
The <publish-subscribe-channel/> element To create a PublishSubscribeChannel, use the "publish-subscribe-channel" element. When using this element, you can also specify the "task-executor" used for publishing Messages (if none is specified it simply publishes in the sender's thread): <publish-subscribe-channel id="exampleChannel" task-executor="someTaskExecutor"/>
The <priority-channel/> element To create a PriorityChannel, use the "priority-channel" element: ]]> By default, the channel will consult the MessagePriority header of the message. However, a custom Comparator reference may be provided instead. Also, note that the PriorityChannel (like the other types) does support the "datatype" attribute. As with the "queue-channel", it also supports a "capacity" attribute. The following example demonstrates all of these: ]]>
The <rendezvous-channel/> element The RendezvousChannel does not provide any additional configuration options. ]]>
The <direct-channel/> element The DirectChannel does not provide any additional configuration options. ]]>
The <thread-local-channel/> element The ThreadLocalChannel does not provide any additional configuration options. ]]>
Message channels may also have interceptors as described in . One or more <interceptor> elements can be added as sub-elements of <channel> (or the more specific element types). Provide the "ref" attribute to reference any Spring-managed object that implements the ChannelInterceptor interface: ]]>]]>]]> In general, it is a good idea to define the interceptor implementations in a separate location since they usually provide common behavior that can be reused across multiple channels.
Configuring Message Endpoints Each of the endpoint types (channel-adapter, service-activator, etc) has its own element in the namespace.
The inbound <channel-adapter/> element with a MessageSource A "channel-adapter" element can connect any implementation of the MessageSource interface to a MessageChannel. When the MessageBus registers the endpoint, it will activate the subscription and if necessary create a poller for the endpoint. The Message Bus delegates to a TaskScheduler for scheduling the poller based on its schedule. To configure the polling 'period' or 'cronExpression' for an individual channel-adapter's schedule, provide a 'poller' sub-element with the 'period' (in milliseconds) or 'cron' attribute: ]]> Cron support does require the Quartz JAR and its transitive dependencies. Also, keep in mind that pollers only apply for PollableChannel implementations. On the other hand, subscribable channels (PublishSubscribeChannel and DirectChannel) will send Messages to their subscribed targets directly.
The outbound <channel-adapter/> with a MessageTarget A "channel-adapter" element can also connect a MessageChannel to any implementation of the MessageTarget interface. ]]> Again, it is possible to provide a poller: ]]>]]>]]>
The <service-activator/> element To create a Service Activator, use the 'service-activator' element with the 'input-channel' and 'ref' attributes: <service-activator input-channel="exampleChannel" ref="exampleHandler"/> The configuration above assumes that "exampleHandler" is an actual implementation of the MessageHandler interface as described in . To delegate to an arbitrary method of any object, simply add the "method" attribute. <service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/> In either case (MessageHandler or arbitrary object/method), when the handling method returns a non-null value, the endpoint will attempt to send the reply message to an appropriate reply channel. To determine the reply channel, it will first check if the NEXT_TARGET header contains a non-null value, next it will check if an "output-channel" was provided in the endpoint configuration: <service-activator input-channel="exampleChannel" output-channel="replyChannel" ref="somePojo" method="someMethod"/> If no "output-channel" is available, it will finally check the message header's RETURN_ADDRESS property. If that value is available, it will then check its type. If it is a MessageTarget, the reply message will be sent to that target. If it is a String, then the endpoint will attempt to resolve the channel by performing a lookup in the ChannelRegistry. If the target cannot be resolved, then a MessageHandlingException will be thrown.
Message Endpoints also support MessageSelectors as described in . To configure a selector with namespace support, simply add the "selector" attribute to the endpoint definition and reference an implementation of the MessageSelector interface. ]]> Another important configuration option for message endpoints is the inclusion of EndpointInterceptors. The interface is defined as follows: preHandle(Message requestMessage); Message aroundHandle(Message requestMessage, MessageHandler handler); Message postHandle(Message replyMessage); }]]> There is also an EndpointInterceptorAdapter that provides no-op methods for convenience when subclassing. Within an endpoint configuration, interceptors can be added within the <interceptors> sub-element. It accepts either "ref" elements or inner "beans": ]]> 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: ]]> Spring Integration also provides support for executing the pollers with a TaskExceutor. 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 <pool-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 characteristics of an endpoint's handler or 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 an 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: ]]> If no 'task-executor' is provided, the endpoint's handler or target will be invoked in the caller's thread. Note that the "caller" is usually the MessageBus' task scheduler except in the case of a subscribable channel. Also, keep in mind that you the 'task-executor' attribute can provide a reference to any implementation of Spring's TaskExecutor interface.
Configuring the Message Bus As described in , the MessageBus plays a central role. Nevertheless, its configuration is quite simple since it is primarily concerned with managing internal details based on the configuration of channels and endpoints. The bus is aware of its host application context, and therefore is also capable of auto-detecting the channels and endpoints. Typically, the MessageBus can be configured with a single empty element: <message-bus/> The Message Bus provides default error handling for its components in the form of a configurable error channel, and it will first check for a channel bean named 'errorChannel' within the context: ]]> When exceptions occur in a scheduled poller task's execution, those exceptions will be wrapped in ErrorMessages 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 RootCauseErrorMessageRouter 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 Exception type. However, since most of the errors will already have been wrapped in MessageDeliveryException or MessageHandlingException, the RootCauseErrorMessageRouter is typically a better option. The 'message-bus' element accepts several more optional attributes. First, you can control whether the MessageBus will be started automatically (the default) or will require explicit startup by invoking its start() method (MessageBus implements Spring's Lifecycle interface): ]]> Another configurable property is the size of the default dispatcher thread pool. The dispatcher threads are responsible for polling channels and then passing the messages to handlers. ]]> When the endpoints are concurrency-enabled as described in the previous section, the invocation of the handling methods will happen within the handler thread pool and not the dispatcher pool. However, when no task-executor is provided to an endpoint's poller, then it will be invoked in the dispatcher's thread (with the exception of subscribable channels). Finally, the type of channel that gets created automatically by the bus can be customized by defining a bean that implements the ChannelFactory interface and whose name is "channelFactory". ]]> With this definition, all the channels created automatically will be PriorityChannel instances. Without a "channelFactory" bean, the Message Bus will assume a default QueueChannelFactory.
Configuring Adapters The most convenient way to configure Source and Target adapters is by using the namespace support. The following examples demonstrate the namespace-based configuration of several source, target, gateway, and handler adapters: ]]> In the examples above, notice that simple implementations of the MessageSource and MessageTarget interfaces do not accept any 'channel' references. To connect such sources and targets to a channel, register them within a 'channel-adapter'. For example, here is a File source with an endpoint whose polling will be scheduled to execute every 30 seconds by the MessageBus. ]]> Likewise, here is an example of a JMS target that is registered within a 'channel-adapter' and whose Messages will be received from the "exampleChannel" that is polled every 500 milliseconds. ]]>
Enabling Annotation-Driven Configuration The next section will describe Spring Integration's support for annotation-driven configuration. To enable those features, add this single element to the XML-based configuration: <annotation-driven/>
Annotations In addition to the XML namespace support for configuring Message Endpoints, it is also possible to use annotations. The class-level @MessageEndpoint annotation indicates that the annotated class is capable of being registered as an endpoint, and the method-level @Handler annotation indicates that the annotated method is capable of handling a message. @MessageEndpoint(input="fooChannel") public class FooService { @Handler public void processMessage(Message message) { ... } } In most cases, the annotated handler method should not require the Message type as its parameter. Instead, the method parameter type can match the message's payload type. @MessageEndpoint(input="fooChannel") public class FooService { @Handler public void bar(Foo foo) { ... } } When the method parameter should be mapped from a value in the MessageHeader, another option is to use the @HeaderAttribute and/or @HeaderProperty parameter annotations. @MessageEndpoint(input="fooChannel") public class FooService { @Handler public void bar(@HeaderAttribute("fooAttrib") Foo foo) { ... } } @MessageEndpoint(input="fooChannel") public class FooService { @Handler public void bar(@HeaderProperty("foo") String input) { ... } } As described in the previous section, when the handler 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 the endpoint's output channel will be used if available, and the message header's 'returnAddress' value will be the fallback. To configure the output channel for an annotation-driven endpoint, provide the 'output' attribute on the @MessageEndpoint. @MessageEndpoint(input="exampleChannel", output="replyChannel") Just as the 'schedule' sub-element and its 'period' attribute can be provided for a namespace-based endpoint, the @Polled annotation can be provided with the @MessageEndpoint annotation. @MessageEndpoint(input="exampleChannel") @Polled(period=3000) public class FooService { ... } Likewise, @Concurrency provides an annotation-based equivalent of the <concurrency/> element: @MessageEndpoint(input="fooChannel") @Concurrency(coreSize=5, maxSize=20) public class FooService { @Handler public void bar(Foo foo) { ... } } Two additional annotations are supported, and both act as a special form of handler method: @Router and @Splitter. As with the @Handler annotation, methods annotated with either of these two annotations can either accept the Message itself or the message payload type as the parameter. When using the @Router annotation, the annotated method can return either the MessageChannel or String type. In the case of the latter, the endpoint will resolve the channel name as it does for the default output. Additionally, the method can return either a single value or a collection. When a collection is returned, the reply message will be sent to multiple channels. To summarize, the following method signatures are all valid. @Router public MessageChannel route(Message message) {...} @Router public List<MessageChannel> route(Message message) {...} @Router public String route(Foo payload) {...} @Router public List<String> route(Foo payload) {...} In addition to payload-based routing, a common requirement is to route based on metadata available within the message header as either a property or attribute. Rather than requiring use of the Message type as the method parameter, the @Router annotation may also use the same parameter annotations that were introduced above. @Router public String route(@HeaderProperty("customerType") String customerType) @Router public List<String> route(@HeaderAttribute("orderStatus") OrderStatus status) The @Splitter annotation is also applicable to methods that expect either the Message type or the message payload type, and the return values of the method should be a collection of any type. If the returned values are not actual Message objects, then each of them will be sent as the payload of a message. Those messages will be sent to the output channel as designated for the endpoint on which the @Splitter is defined. @Splitter List<LineItem> extractItems(Order order) { return order.getItems() } The @Publisher annotation is convenient for sending messages with AOP after-returning advice. For example, each time the following method is invoked, its return value will be sent to the "fooChannel": Similarly, the @Subscriber annotation triggers the retrieval of messages from a channel, and the payload of each message will then be sent as input to an arbitrary method. This is one of the simplest ways to configure asynchronous, event-driven behavior: