Appendix F. Configuration

F.1 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 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.

F.2 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 core namespace support within your Spring configuration files, add the following namespace reference and schema mapping in your top-level beans element:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:int="http://www.springframework.org/schema/integration"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/integration
           http://www.springframework.org/schema/integration/spring-integration.xsd">

You can choose any name after "xmlns:"; int 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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:beans="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/integration
           http://www.springframework.org/schema/integration/spring-integration.xsd">

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.

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:

<?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:int="http://www.springframework.org/schema/integration"
  xmlns:int-file="http://www.springframework.org/schema/integration/file"
  xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
  xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
  xmlns:int-rmi="http://www.springframework.org/schema/integration/rmi"
  xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    http://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/file
    http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
    http://www.springframework.org/schema/integration/jms
    http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
    http://www.springframework.org/schema/integration/mail
    http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
    http://www.springframework.org/schema/integration/rmi
    http://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
    http://www.springframework.org/schema/integration/ws
    http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
 ...
</beans>

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.

F.3 Configuring the Task Scheduler

In Spring Integration, the ApplicationContext plays the central role of a Message Bus, and there are only a couple configuration options to consider. 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:

IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME

By default Spring Integration relies on an instance of ThreadPoolTaskScheduler as described in the Task Execution and Scheduling section of the Spring Framework reference manual. That default TaskScheduler will startup automatically with a pool of 10 threads, but see Section F.5, “Global Properties”. If you provide your own TaskScheduler instance instead, you can set the autoStartup property to false, and/or you can provide your own pool size value.

When Polling Consumers provide an explicit task-executor reference in their configuration, the invocation of the handler 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 by one of the main scheduler’s threads.

[Caution]Caution

Do not run long-running tasks on poller threads; use a task executor instead. If you have a lot of polling endpoints, you can cause thread starvation, unless you increase the pool size. Also, polling consumers have a default receiveTimeout of 1 second; since the poller thread blocks for this time, it is recommended that a task executor be used when many such endpoints exist, again to avoid starvation. Alternatively, reduce the receiveTimeout.

[Note]Note

An endpoint is a Polling Consumer if its input channel is one of the queue-based (i.e. pollable) channels. Event Driven Consumers are those having input channels that have dispatchers instead of queues (i.e. they are subscribable). Such endpoints have no poller configuration since their handlers will be invoked directly.

[Important]Important

When running in a JEE container, you may need to use Spring’s TimerManagerTaskScheduler as described here, instead of the default taskScheduler. To do that, simply define a bean with the appropriate JNDI name for your environment, for example:

<bean id="taskScheduler" class="o.s.scheduling.commonj.TimerManagerTaskScheduler">
    <property name="timerManagerName" value="tm/MyTimerManager" />
    <property name="resourceRef" value="true" />
</bean>

The next section will describe what happens if Exceptions occur within the asynchronous invocations.

F.4 Error Handling

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.

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 <channel> element that has no <queue> sub-element and no task-executor attribute), the Message-handling will occur in the same thread as the Message-sending. In that case, if an Exception is thrown, it can be caught 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 does provide a queue sub-element, then the component that handles the Message will be operating in a different thread than the sender. The sender may have dropped the Message into the channel 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.

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 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).

A default "errorChannel" bean is created behind the scenes by the Framework. However, you can just as easily define your own if you want to control the settings.

<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
[Note]Note

The default "errorChannel" is a PublishSubscribeChannel.

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 not 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).

[Note]Note

When Exceptions occur in a scheduled poller task’s execution, those exceptions will be wrapped in ErrorMessages and sent to the errorChannel as well.

To enable global error handling, simply register a handler on that channel. For example, you can configure Spring Integration’s ErrorMessageExceptionTypeRouter 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.

F.5 Global Properties

Certain global framework properties can be overridden by providing a properties file on the classpath.

The default properties can be found in /META-INF/spring.integration.default.properties in the spring-integration-core jar. You can see them on GitHub here, but here are the current default values:

spring.integration.channels.autoCreate=true 1
spring.integration.channels.maxUnicastSubscribers=0x7fffffff 2
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff 3
spring.integration.taskScheduler.poolSize=10 4
spring.integration.messagingTemplate.throwExceptionOnLateReply=false 5
spring.integration.messagingAnnotations.require.componentAnnotation=false 6

1

When true, input-channel s will be automatically declared as DirectChannel s when not explicitly found in the application context.

2

This property provides the default number of subscribers allowed on, say, a DirectChannel. It can be used to avoid inadvertently subscribing multiple endpoints to the same channel. This can be overridden on individual channels with the max-subscribers attribute.

3

This property provides the default number of subscribers allowed on, say, a PublishSubscribeChannel. It can be used to avoid inadvertently subscribing more than the expected number of endpoints to the same channel. This can be overridden on individual channels with the max-subscribers attribute.

4

The number of threads available in the default taskScheduler bean; see Section F.3, “Configuring the Task Scheduler”.

5

When true, messages that arrive at a gateway reply channel will throw an exception, when the gateway is not expecting a reply - because the sending thread has timed out, or already received a reply.

6

When true, Messaging Annotation Support (Section F.6, “Annotation Support”) requires a declaration of the @MessageEndpoint (or any other @Component) annotation on the class level.

These properties can be overridden by adding a file /META-INF/spring.integration.properties to the classpath. It is not necessary to provide all the properties, just those that you want to override.

[Note]Note

In versions prior to 4.3, these property names had a typographical error (...integraton...); they have now been corrected (...integration...).

F.6 Annotation Support

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 @MessageEndpoint as a stereotype annotation, meaning that it is itself annotated with Spring’s @Component annotation and is therefore recognized automatically as a bean definition when using Spring component-scanning.

Even more important are the various method-level annotations that indicate the annotated method is capable of handling a message. The following example demonstrates both:

@MessageEndpoint
public class FooService {

    @ServiceActivator
    public void processMessage(Message message) {
        ...
    }
}

Exactly what it means for the method to "handle" the Message depends on the particular annotation. Annotations available in Spring Integration include:

  • @Aggregator
  • @Filter
  • @Router
  • @ServiceActivator
  • @Splitter
  • @Transformer
  • @InboundChannelAdapter
  • @BridgeFrom
  • @BridgeTo
  • @MessagingGateway
  • @IntegrationComponentScan

The behavior of each is described in its own chapter or section within this reference.

[Note]Note

If you are using XML configuration in combination with annotations, the @MessageEndpoint annotation is not required. If you want to configure a POJO reference from the "ref" attribute of a <service-activator/> element, it is sufficient to provide the method-level annotations. In that case, the annotation prevents ambiguity even when no "method" attribute exists on the <service-activator/> element.

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.

public class FooService {

    @ServiceActivator
    public void bar(Foo foo) {
        ...
    }

}

When the method parameter should be mapped from a value in the MessageHeaders, another option is to use the parameter-level @Header annotation. In general, methods annotated with the Spring Integration annotations can either accept the Message itself, the message payload, or a header value (with @Header) as the parameter. In fact, the method can accept a combination, such as:

public class FooService {

    @ServiceActivator
    public void bar(String payload, @Header("x") int valueX, @Header("y") int valueY) {
        ...
    }

}

There is also a @Headers annotation that provides all of the Message headers as a Map:

public class FooService {

    @ServiceActivator
    public void bar(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}
[Note]Note

The value of the annotation can also be a SpEL expression (e.g., someHeader.toUpperCase()) which is useful when you wish to manipulate the header value before injecting it. It also provides an optional required property which specifies whether the attribute value must be available within the headers. The default value for required is true.

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 such an endpoint’s output channel will be used if available, and the REPLY_CHANNEL message header value will be used as a fallback.

[Tip]Tip

The combination of output channels on endpoints and the reply channel message header enables a pipeline approach where multiple components have an output channel, and the final component simply allows the reply message to be forwarded to the reply channel as specified in the original request message. In other words, the final component depends on the information provided by the original sender and can dynamically support any number of clients as a result. This is an example of Return Address.

In addition to the examples shown here, these annotations also support inputChannel and outputChannel properties.

@Service
public class FooService {

    @ServiceActivator(inputChannel="input", outputChannel="output")
    public void bar(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}

The processing of these annotations creates the same beans (AbstractEndpoint s and MessageHandler s (or MessageSource s for the inbound channel adapter - see below) as with similar xml components. The bean names are generated with this pattern: [componentName].[methodName].[decapitalizedAnnotationClassShortName] (e.g for the sample above - fooService.bar.serviceActivator) for the AbstractEndpoint and the same name with an additional .handler (.source) suffix for the MessageHandler (MessageSource) bean. The MessageHandler s (MessageSource s) are also eligible to be tracked by Section 9.3, “Message History”.

Starting with version 4.0, all Messaging Annotations provide SmartLifecycle options - autoStartup and phase to allow endpoint lifecycle control on application context initialization. They default to true and 0 respectively. To change the state of an endpoint (e.g` start()/stop()) obtain a reference to the endpoint bean using the `BeanFactory (or autowiring) and invoke the method(s), or send a command message to the Control Bus (Section 9.6, “Control Bus”). For these purposes you should use the beanName mentioned above.

@Poller

Before Spring Integration 4.0, the above Messaging Annotations required that the inputChannel was a reference to a SubscribableChannel. For PollableChannel s there was need to use a <int:bridge/>, to configure a <int:poller/> to make the composite endpoint - a PollingConsumer. Starting with version 4.0, the @Poller annotation has been introduced to allow the configuration of poller attributes directly on the above Messaging Annotations:

public class AnnotationService {

	@Transformer(inputChannel = "input", outputChannel = "output",
		poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}"))
	public String handle(String payload) {
		...
	}
}

This annotation provides only simple PollerMetadata options. The @Poller's attributes maxMessagesPerPoll, fixedDelay, fixedRate and cron can be configured with _property-placeholder_s. If it is necessary to provide more polling options (e.g. transaction, advice-chain, error-handler), the`PollerMetadata` should be configured as a generic bean with its bean name used for @Poller's value attribute. In this case, no other attributes are allowed (they would be specified on the PollerMetadata bean). Note, if inputChannel is PollableChannel and no @Poller is configured, the default PollerMetadata will be used, if it is present in the application context. To declare the default poller using @Configuration, use:

@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
	PollerMetadata pollerMetadata = new PollerMetadata();
	pollerMetadata.setTrigger(new PeriodicTrigger(10));
	return pollerMetadata;
}

With this endpoint using the default poller:

public class AnnotationService {

	@Transformer(inputChannel = "aPollableChannel", outputChannel = "output")
	public String handle(String payload) {
		...
	}
}

To use a named poller, use:

@Bean
public PollerMetadata myPoller() {
	PollerMetadata pollerMetadata = new PollerMetadata();
	pollerMetadata.setTrigger(new PeriodicTrigger(1000));
	return pollerMetadata;
}

With this endpoint using the default poller:

public class AnnotationService {

	@Transformer(inputChannel = "aPollableChannel", outputChannel = "output"
						poller = @Poller("myPoller")
	public String handle(String payload) {
		...
	}
}

@InboundChannelAdapter

Starting with version 4.0, the @InboundChannelAdapter method annotation is available. This produces a SourcePollingChannelAdapter integration component based on a MethodInvokingMessageSource for the annotated method. This annotation is an analogue of <int:inbound-channel-adapter> XML component and has the same restrictions: the method cannot have parameters, and the return type must not be void. It has two attributes: value - the required MessageChannel bean name and poller - an optional @Poller annotation, as described above. If there is need to provide some MessageHeaders, use a Message<?> return type and build the Message<?> within the method using a MessageBuilder to configure its MessageHeaders.

@InboundChannelAdapter("counterChannel")
public Integer count() {
	return this.counter.incrementAndGet();
}

@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000"))
public String foo() {
	return "foo";
}

Starting with version 4.3 the channel alias for the value annotation attribute has been introduced for better source code readability. Also the target MessageChannel bean is resolved in the SourcePollingChannelAdapter by the provided name (outputChannelName options) on the first receive() call, not during initialization phase. It allows the late binding logic, when the target MessageChannel bean from the consumer perspective is created and registered a bit later than the @InboundChannelAdapter parsing phase.

The first example requires that the default poller has been declared elsewhere in the application context.

@MessagingGateway

See Section 8.4.6, “@MessagingGateway Annotation”.

@IntegrationComponentScan

The standard Spring Framework @ComponentScan annotation doesn’t scan interfaces for stereotype @Component annotations. To overcome this limitation and allow the configuration of @MessagingGateway (see Section 8.4.6, “@MessagingGateway Annotation”), the @IntegrationComponentScan mechanism has been introduced. This annotation must be placed along with a @Configuration annotation, and customized for the scanning options, such as basePackages and basePackageClasses. In this case all discovered interfaces annotated with @MessagingGateway will be parsed and registered as a GatewayProxyFactoryBean s. All other class-based components are parsed by the standard @ComponentScan. In future, more scanning logic may be added to the @IntegrationComponentScan.

F.6.1 Messaging Meta-Annotations

Starting with version 4.0, all Messaging Annotations can be configured as meta-annotations and all user-defined Messaging Annotations can define the same attributes to override their default values. In addition, meta-annotations can be configured hierarchically:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ServiceActivator(inputChannel = "annInput", outputChannel = "annOutput")
public @interface MyServiceActivator {

	String[] adviceChain = { "annAdvice" };
}

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@MyServiceActivator
public @interface MyServiceActivator1 {

	String inputChannel();

	String outputChannel();
}
...

@MyServiceActivator1(inputChannel = "inputChannel", outputChannel = "outputChannel")
public Object service(Object payload) {
   ...
}

This allows users to set defaults for various attributes and enables isolation of framework Java dependencies to user annotations, avoiding their use in user classes. If the framework finds a method with a user annotation that has a framework meta-annotation, it is treated as if the method was annotated directly with the framework annotation.

F.6.2 Annotations on @Beans

Starting with version 4.0, Messaging Annotations can be configured on @Bean method definitions in @Configuration classes, to produce Message Endpoints based on the beans, not methods. It is useful when @Bean definitions are "out of the box" MessageHandler s (AggregatingMessageHandler, DefaultMessageSplitter etc.), Transformer s (JsonToObjectTransformer, ClaimCheckOutTransformer etc.), MessageSource s (FileReadingMessageSource, RedisStoreMessageSource etc.):

@Configuration
@EnableIntegration
public class MyFlowConfiguration {

	@Bean
	@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
	public MessageSource<String> consoleSource() {
		return CharacterStreamReadingMessageSource.stdin();
	}

	@Bean
	@Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel")
	public ObjectToMapTransformer toMapTransformer() {
		return new ObjectToMapTransformer();
	}

	@Bean
	@ServiceActivator(inputChannel = "httpChannel")
	public MessageHandler httpHandler() {
		HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://foo/service");
		handler.setExpectedResponseType(String.class);
		handler.setOutputChannelName("outputChannel");
		return handler;
	}

	@Bean
	@ServiceActivator(inputChannel = "outputChannel")
	public LoggingHandler loggingHandler() {
		return new LoggingHandler("info");
	}

}

The meta-annotation rules work on @Bean methods as well (@MyServiceActivator above can be applied to a @Bean definition).

[Note]Note

When using these annotations on consumer @Bean definitions, if the bean definition returns an appropriate MessageHandler (depending on the annotation type), attributes such as outputChannel, requiresReply etc, must be set on the MessageHandler @Bean definition itself. The only annotation attributes used are adviceChain, autoStartup, inputChannel, phase, poller, all other attributes are for the handler.

[Note]Note

The bean names are generated with this algorithm: * The MessageHandler (MessageSource) @Bean gets its own standard name from the method name or name attribute on the @Bean. This works like there is no Messaging Annotation on the @Bean method. * The AbstractEndpoint bean name is generated with the pattern: [configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName]. For example the endpoint (SourcePollingChannelAdapter) for the consoleSource() definition above gets a bean name like: myFlowConfiguration.consoleSource.inboundChannelAdapter.

[Important]Important

When using these annotations on @Bean definitions, the inputChannel must reference a declared bean; channels are not automatically declared in this case.

[Note]Note

With Java & Annotation configuration we can use any @Conditional (e.g. @Profile) definition on the @Bean method level, meaning to skip the bean registration by some condition reason:

@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("foo")
public MessageHandler skipped() {
	return System.out::println;
}

Together with the existing Spring Container logic, the Messaging Endpoint bean, based on the @ServiceActivator annotation, won’t be registered as well.

F.6.3 Creating a Bridge with Annotations

Starting with version 4.0, the Messaging Annotation and Java configuration provides @BridgeFrom and @BridgeTo @Bean method annotations to mark MessageChannel beans in @Configuration classes. This is just for completeness, providing a convenient mechanism to declare a`BridgeHandler` and its Message Endpoint configuration:

@Bean
public PollableChannel bridgeFromInput() {
	return new QueueChannel();
}

@Bean
@BridgeFrom(value = "bridgeFromInput", poller = @Poller(fixedDelay = "1000"))
public MessageChannel bridgeFromOutput() {
	return new DirectChannel();
}
@Bean
public QueueChannel bridgeToOutput() {
	return new QueueChannel();
}

@Bean
@BridgeTo("bridgeToOutput")
public MessageChannel bridgeToInput() {
	return new DirectChannel();
}

These annotations can be used as meta-annotations as well.

F.6.4 Advising Annotated Endpoints

See Section 8.9.7, “Advising Endpoints Using Annotations”.

F.7 Message Mapping rules and conventions

Spring Integration implements a flexible facility to map Messages to Methods and their arguments without providing extra configuration by relying on some default rules as well as defining certain conventions.

F.7.1 Simple Scenarios

Single un-annotated parameter (object or primitive) which is not a Map/Properties with non-void return type;

public String foo(Object o);

Details:

Input parameter is Message Payload. If parameter type is not compatible with Message Payload an attempt will be made to convert it using Conversion Service provided by Spring 3.0. The return value will be incorporated as a Payload of the returned Message

Single un-annotated parameter (object or primitive) which is not a Map/Properties with Message return type;

public Message  foo(Object o);

Details:

Input parameter is Message Payload. If parameter type is not compatible with Message Payload an attempt will be made to convert it using Conversion Service provided by Spring 3.0. The return value is a newly constructed Message that will be sent to the next destination.

_Single parameter which is a Message or its subclass with arbitrary object/primitive return type; _

public int foo(Message  msg);

Details:

Input parameter is Message itself. The return value will become a payload of the Message that will be sent to the next destination.

Single parameter which is a Message or its subclass with Message or its subclass as a return type;

public Message foo(Message msg);

Details:

Input parameter is Message itself. The return value is a newly constructed Message that will be sent to the next destination.

Single parameter which is of type Map or Properties with Message as a return type;

public Message foo(Map m);

Details:

This one is a bit interesting. Although at first it might seem like an easy mapping straight to Message Headers, the preference is always given to a Message Payload. This means that if Message Payload is of type Map, this input argument will represent Message Payload. However if Message Payload is not of type Map, then no conversion via Conversion Service will be attempted and the input argument will be mapped to Message Headers.

Two parameters where one of them is arbitrary non-Map/Properties type object/primitive and another is Map/Properties type object (regardless of the return)

public Message foo(Map h, <T> t);

Details:

This combination contains two input parameters where one of them is of type Map. Naturally the non-Map parameters (regardless of the order) will be mapped to a Message Payload and the Map/Properties (regardless of the order) will be mapped to  Message Headers giving you a nice POJO way of interacting with Message structure.

No parameters (regardless of the return)

public String foo();

Details:

This Message Handler method will be invoked based on the Message sent to the input channel this handler is hooked up to, however no Message data will be mapped, thus making Message act as event/trigger to invoke such handlerThe output will be mapped according to the rules above

No parameters, void return

public void foo();

Details:

Same as above, but no output 

Annotation based mappings

Annotation based mapping is the safest and least ambiguous approach to map Messages to Methods. There wil be many pointers to annotation based mapping throughout this manual, however here are couple of examples:

public String foo(@Payload String s, @Header("foo") String b) 

Very simple and explicit way of mapping Messages to method. As you’ll see later on, without an annotation this signature would result in an ambiguous condition. However by explicitly mapping the first argument to a Message Payload and the second argument to a value of the foo Message Header, we have avoided any ambiguity.

public String foo(@Payload String s, @RequestParam("foo") String b) 

Looks almost identical to the previous example, however @RequestMapping or any other non-Spring Integration mapping annotation is irrelevant and therefore will be ignored leaving the second parameter unmapped. Although the second parameter could easily be mapped to a Payload, there can only be one Payload. Therefore this method mapping is ambiguous.

public String foo(String s, @Header("foo") String b) 

The same as above. The only difference is that the first argument will be mapped to the Message Payload implicitly.

public String foo(@Headers Map m, @Header("foo") Map f, @Header("bar") String bar)

Yet another signature that would definitely be treated as ambiguous without annotations because it has more than 2 arguments. Furthermore, two of them are Maps. However, with annotation-based mapping, the ambiguity is easily avoided. In this example the first argument is mapped to all the Message Headers, while the second and third argument map to the values of Message Headers foo and bar. The payload is not being mapped to any argument.

F.7.2 Complex Scenarios

Multiple parameters:

Multiple parameters could create a lot of ambiguity with regards to determining the appropriate mappings. The general advice is to annotate your method parameters with @Payload and/or @[email protected] Below are some of the examples of ambiguous conditions which result in an Exception being raised.

public String foo(String s, int i)
  • the two parameters are equal in weight, therefore there is no way to determine which one is a payload.
public String foo(String s, Map m, String b)
  • almost the same as above. Although the Map could be easily mapped to Message Headers, there is no way to determine what to do with the two Strings.
public String foo(Map m, Map f)
  • although one might argue that one Map could be mapped to Message Payload and another one to Message Headers, it would be unreasonable to rely on the order (e.g., first is Payload, second Headers)
[Tip]Tip

Basically any method signature with more than one method argument which is not (Map, <T>), and those parameters are not annotated, will result in an ambiguous condition thus triggering an Exception.

Multiple methods:

Message Handlers with multiple methods are mapped based on the same rules that are described above, however some scenarios might still look confusing.

Multiple methods (same or different name) with legal (mappable) signatures:

public class Foo {
  public String foo(String str, Map m);

  public String foo(Map m);
}

As you can see, the Message could be mapped to either method. The first method would be invoked where Message Payload could be mapped to str  and Message Headers could be mapped to m. The second method could easily also be a candidate where only Message Headers are mapped to m. To make meters worse both methods have the same name which at first might look very ambiguous considering the following configuration:

<int:service-activator input-channel="input" output-channel="output" method="foo">
  <bean class="org.bar.Foo"/>
</int:service-activator>

At this point it would be important to understand Spring Integration mapping Conventions where at the very core, mappings are based on Payload first and everything else next. In other words the method whose argument could be mapped to a Payload will take precedence over all other methods.

On the other hand let’s look at slightly different example:

public class Foo {
  public String foo(String str, Map m);

  public String foo(String str);
}

If you look at it you can probably see a truly ambiguous condition. In this example since both methods have signatures that could be mapped to a Message Payload. They also have the same name. Such handler methods will trigger an Exception. However if the method names were different you could influence the mapping with a method attribute (see below):

public class Foo {
  public String foo(String str, Map m);

  public String bar(String str);
}
<int:service-activator input-channel="input" output-channel="output" method="bar">
  <bean class="org.bar.Foo"/>
</int:service-activator>

Now there is no ambiguity since the configuration explicitly maps to the bar method which has no name conflicts.