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. Of course, it is also possible to always stick with a single approach. The main point is that these are options for configuration motivated by the need to support a user community with a wide range of preferences. That said, there has also been a concerted effort to provide consistent naming so that, for example, the 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, use the 'channel' element: <channel id="exampleChannel"/> You can also specify the channel's capacity: <channel id="exampleChannel" capacity="100"/> The default channel type is Point to Point. To create a Publish Subscribe channel, provide a value of true for the 'publish-subscribe' attribute of the channel element: <channel id="exampleChannel" publish-subscribe="true"/> When the MessageBus detects and registers channels, it will establish a dispatcher for each channel. The default dispatcher settings were previously displayed in . To customize these settings for a particular channel, add the 'dispatcher-policy' sub-element and provide one or more of the attributes shown below: ]]>
Configuring Message Endpoints To create a Message Endpoint instance, use the 'endpoint' element with the 'input-channel' and 'handler-ref' attributes: <endpoint input-channel="exampleChannel" handler-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 "handler-method" attribute. <endpoint input-channel="exampleChannel" handler-ref="somePojo" handler-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 for a value in the message header's 'replyChannelName' property. If that value is available, it will attempt to resolve the channel by performing a lookup in the ChannelRegistry. If the message header does not contain a 'replyChannelName' property, then it will fallback to its own 'defaultOutputChannel' property. If neither is available, then a MessageHandlingException will be thrown. To configure the default output channel when using the XML namespace, provide the 'default-output-channel' attribute: <endpoint input-channel="exampleChannel" handler-ref="somePojo" handler-method="someMethod" default-output-channel="replyChannel"/> When the MessageBus registers the endpoint, it will activate the subscription by assigning the endpoint to the input channel's dispatcher. The dispatcher is capable of handling multiple endpoint subscriptions for its channel and delegates to a scheduler for managing the tasks that pull messages from the channel and push them to the endpoints. To configure the polling period for an individual endpoint's schedule, provide a 'schedule' sub-element with the 'period' in milliseconds: ]]>]]>]]> Individual endpoint schedules only apply for "Point-to-Point" channels, since in that case only a single subscriber needs to receive the message. On the other hand, when a Spring Integration channel is configured as a "Publish-Subscribe" channel, then the dispatcher will drive all endpoint notifications according to its own default schedule, and any 'schedule' element configured for those endpoints will be ignored. One of the most important configuration options for endpoints is the concurrency policy. Each endpoint is capable of managing a thread pool for its handler, and the values you provide for that pool's core and max size can make a substantial difference in how the handler performs under load. These settings are available per-endpoint since the performance characteristics of an endpoint's handler 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 'concurrency' sub-element and one or more of the properties shown below: ]]>]]>]]> Recall the default concurrency policy values as listed in . The default queue capacity of 0 triggers the creation of a SynchronousQueue. In many cases, this is preferable since the direct handoff eliminates the chance of a message handling task being "stuck" in the queue (thread pool executors will favor adding to the queue rather than increasing the pool size). Specifically, whenever a dispatcher for a Point-to-Point channel has more than one subscribed endpoint, a task that is rejected due to an exhausted thread pool can be handled immediately by another endpoint whose pool has one or more threads available. On the other hand, when a particular channel/endpoint may be expecting bursts of activity, setting a queue capacity value might be the best way to accommodate the volume.
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 the 'message-bus' element accepts a reference with its 'error-channel' attribute: ]]> When exceptions occur in an endpoint's execution of its MessageHandler callback, those exceptions will be wrapped in ErrorMessages and sent to the Message Bus' 'errorChannel' by default. To enable global error handling, simply register a handler on that channel. For example, you can configure Spring Integration's PayloadTypeRouter 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. The 'message-bus' element accepts two more optional attributes. First is the size of the 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. Finally, the Message Bus is capable of automatically creating channel instances (with default settings) if an endpoint registers a subscription by providing the name of a channel that the bus does not recognize. ]]>
Configuring Channel Adapters The most convenient way to configure Channel Adapters is by using the namespace support. The following examples demonstrate the namespace-based configuration of source and target adapters (Spring Integration 1.0 M1 includes namespace support for JMS and Files): ]]>
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 processFoo(Foo foo) { ... } } 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 message header's 'replyChannelName' property will be used if available, and the endpoint's default output is the fallback. To configure the default output for an annotation-driven endpoint, provide the 'defaultOutput' attribute on the @MessageEndpoint. @MessageEndpoint(input="exampleChannel", defaultOutput="replyChannel") Finally, just as the 'schedule' sub-element and its 'period' attribute can be provided for a namespace-based endpoint, the 'pollPeriod' attribute can be provided on the @MessageEndpoint. @MessageEndpoint(input="exampleChannel", pollPeriod=3000) 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 map to either a property or attribute name. @Router(property="customerType") public String route(String customerType) @Router(attribute="orderStatus") public List<String> route(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. The @Splitter annotation expects a 'channel' attribute that specifies the channel name to which those messages should be sent. @Splitter(channel="exampleChannel") List<LineItem> extractItems(Order order) { return order.getItems() } The @Publisher annotation is a convenience 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: