20. JMS Support

Spring Integration provides Channel Adapters for receiving and sending JMS messages. There are actually two JMS-based inbound Channel Adapters. The first uses Spring’s JmsTemplate to receive based on a polling period. The second is "message-driven" and relies upon a Spring MessageListener container. There is also an outbound Channel Adapter which uses the JmsTemplate to convert and send a JMS Message on demand.

As you can see from above by using JmsTemplate and MessageListener container Spring Integration relies on Spring’s JMS support. This is important to understand since most of the attributes exposed on these adapters will configure the underlying Spring’s JmsTemplate and/or MessageListener container. For more details about JmsTemplate and MessageListener container please refer to Spring JMS documentation.

Whereas the JMS Channel Adapters are intended for unidirectional Messaging (send-only or receive-only), Spring Integration also provides inbound and outbound JMS Gateways for request/reply operations. The inbound gateway relies on one of Spring’s MessageListener container implementations for Message-driven reception that is also capable of sending a return value to the reply-to Destination as provided by the received Message. The outbound Gateway sends a JMS Message to a request-destination (or request-destination-name or request-destination-expression) and then receives a reply Message. The reply-destination reference (or reply-destination-name or reply-destination-expression) can be configured explicitly or else the outbound gateway will use a JMS TemporaryQueue.

Prior to Spring Integration 2.2, if necessary, a TemporaryQueue was created (and removed) for each request/reply. Beginning with Spring Integration 2.2, the outbound gateway can be configured to use a MessageListener container to receive replies instead of directly using a new (or cached) Consumer to receive the reply for each request. When so configured, and no explicit reply destination is provided, a single TemporaryQueue is used for each gateway instead of one for each request.

20.1 Inbound Channel Adapter

The inbound Channel Adapter requires a reference to either a single JmsTemplate instance or both ConnectionFactory and Destination (a destinationName can be provided in place of the destination reference). The following example defines an inbound Channel Adapter with a Destination reference.

<int-jms:inbound-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel">
    <int:poller fixed-rate="30000"/>
</int-jms:inbound-channel-adapter>
[Tip]Tip

Notice from the configuration that the inbound-channel-adapter is a Polling Consumer. That means that it invokes receive() when triggered. This should only be used in situations where polling is done relatively infrequently and timeliness is not important. For all other situations (a vast majority of JMS-based use-cases), the message-driven-channel-adapter described below is a better option.

[Note]Note

All of the JMS adapters that require a reference to the ConnectionFactory will automatically look for a bean named "connectionFactory" by default. That is why you don’t see a "connection-factory" attribute in many of the examples. However, if your JMS ConnectionFactory has a different bean name, then you will need to provide that attribute.

If extract-payload is set to true (which is the default), the received JMS Message will be passed through the MessageConverter. When relying on the default SimpleMessageConverter, this means that the resulting Spring Integration Message will have the JMS Message’s body as its payload. A JMS TextMessage will produce a String-based payload, a JMS BytesMessage will produce a byte array payload, and a JMS ObjectMessages Serializable instance will become the Spring Integration Message’s payload. If instead you prefer to have the raw JMS Message as the Spring Integration Message’s payload, then set 'extract-payload to false.

<int-jms:inbound-channel-adapter id="jmsIn"
    destination="inQueue"
    channel="exampleChannel"
    extract-payload="false"/>
    <int:poller fixed-rate="30000"/>
</int-jms:inbound-channel-adapter>

20.1.1 Transactions

Starting with version 4.0, the inbound channel adapter supports the session-transacted attribute. In earlier versions, you had to inject a JmsTemplate with sessionTransacted set to true. (The adapter did allow the acknowledge attribute to be set to transacted but this was incorrect and did not work).

Note, however, that setting session-transacted to true has little value because the transaction is committed immediately after the receive() and before the message is sent to the channel.

If you want the entire flow to be transactional (for example if there is a downstream outbound channel adapter), you must use a transactional poller, with a JmsTransactionManager. Or, consider using a jms-message-driven-channel-adapter with acknowledge set to transacted (the default).

20.2 Message-Driven Channel Adapter

The "message-driven-channel-adapter" requires a reference to either an instance of a Spring MessageListener container (any subclass of AbstractMessageListenerContainer) or both ConnectionFactory and Destination (a destinationName can be provided in place of the destination reference). The following example defines a message-driven Channel Adapter with a Destination reference.

<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel"/>
[Note]Note

The Message-Driven adapter also accepts several properties that pertain to the MessageListener container. These values are only considered if you do not provide a container reference. In that case, an instance of DefaultMessageListenerContainer will be created and configured based on these properties. For example, you can specify the "transaction-manager" reference, the "concurrent-consumers" value, and several other property references and values. Refer to the JavaDoc and Spring Integration’s JMS Schema (spring-integration-jms.xsd) for more details.

If you have a custom listener container implementation (usually a subclass of DefaultMessageListenerContainer), you can either provide a reference to an instance of it using the container attribute, or simply provide its fully qualified class name using the container-class attribute. In that case, the attributes on the adapter are transferred to an instance of your custom container.

[Important]Important

Starting with version 4.2, the default acknowledge mode is transacted, unless an external container is provided, in which case the container should be configured as needed. It is recommended to use transacted with the DefaultMessageListenerContainer to avoid message loss.

The extract-payload property has the same effect as described above, and once again its default value is true. The poller sub-element is not applicable for a message-driven Channel Adapter, as it will be actively invoked. For most usage scenarios, the message-driven approach is better since the Messages will be passed along to the MessageChannel as soon as they are received from the underlying JMS consumer.

Finally, the <message-driven-channel-adapter> also accepts the error-channel attribute. This provides the same basic functionality as described in Section 8.3.1, “Enter the GatewayProxyFactoryBean”.

<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue"
    channel="exampleChannel"
    error-channel="exampleErrorChannel"/>

When comparing this to the generic gateway configuration, or the JMS inbound-gateway that will be discussed below, the key difference here is that we are in a one-way flow since this is a channel-adapter, not a gateway. Therefore, the flow downstream from the error-channel should also be one-way. For example, it could simply send to a logging handler, or it could be connected to a different JMS <outbound-channel-adapter> element.

When consuming from topics, set the pub-sub-domain attribute to true; set subscription-durable to true for a durable subscription, subscription-shared for a shared subscription (requires a JMS 2.0 broker and has been available since version 4.2). Use subscription-name to name the subscription.

20.2.1 Inbound Conversion Errors

[Note]Note

Starting with version 4.2 the error-channel is used for the conversion errors, too. Previously, if a JMS <message-driven-channel-adapter/> or <inbound-gateway/> could not deliver a message due to a conversion error, an exception would be thrown back to the container. If the container was configured to use transactions, the message would be rolled back and redelivered repeatedly. The conversion process occurs before and during message construction so such errors were not sent to the error-channel. Now such conversion exceptions result in an ErrorMessage being sent to the error-channel, with the exception as the payload. If you wish the transaction to be rolled back, and you have an error-channel defined, the integration flow on the error-channel must re-throw the exception (or another). If the error flow does not throw an exception, the transaction will be committed and the message removed. If no error-channel is defined, the exception is thrown back to the container, as before.

20.3 Outbound Channel Adapter

The JmsSendingMessageHandler implements the MessageHandler interface and is capable of converting Spring Integration Messages to JMS messages and then sending to a JMS destination. It requires either a jmsTemplate reference or both connectionFactory and destination references (again, the destinationName may be provided in place of the destination). As with the inbound Channel Adapter, the easiest way to configure this adapter is with the namespace support. The following configuration will produce an adapter that receives Spring Integration Messages from the "exampleChannel" and then converts those into JMS Messages and sends them to the JMS Destination reference whose bean name is "outQueue".

<int-jms:outbound-channel-adapter id="jmsOut" destination="outQueue" channel="exampleChannel"/>

As with the inbound Channel Adapters, there is an extract-payload property. However, the meaning is reversed for the outbound adapter. Rather than applying to the JMS Message, the boolean property applies to the Spring Integration Message payload. In other words, the decision is whether to pass the Spring Integration Message itself as the JMS Message body or whether to pass the Spring Integration Message’s payload as the JMS Message body. The default value is once again true. Therefore, if you pass a Spring Integration Message whose payload is a String, a JMS TextMessage will be created. If on the other hand you want to send the actual Spring Integration Message to another system via JMS, then simply set this to false.

[Note]Note

Regardless of the boolean value for payload extraction, the Spring Integration MessageHeaders will map to JMS properties as long as you are relying on the default converter or provide a reference to another instance of HeaderMappingMessageConverter (the same holds true for inbound adapters except that in those cases, it’s the JMS properties mapping to Spring Integration MessageHeaders).

20.3.1 Transactions

Starting with version 4.0, the outbound channel adapter supports the session-transacted attribute. In earlier versions, you had to inject a JmsTemplate with sessionTransacted set to true. The attribute now sets the property on the built-in default JmsTemplate. If a transaction exists (perhaps from an upstream message-driven-channel-adapter) the send will be performed within the same transaction. Otherwise a new transaction will be started.

20.4 Inbound Gateway

Spring Integration’s message-driven JMS inbound-gateway delegates to a MessageListener container, supports dynamically adjusting concurrent consumers, and can also handle replies. The inbound gateway requires references to a ConnectionFactory, and a request Destination (or requestDestinationName). The following example defines a JMS "inbound-gateway" that receives from the JMS queue referenced by the bean id "inQueue" and sends to the Spring Integration channel named "exampleChannel".

<int-jms:inbound-gateway id="jmsInGateway"
    request-destination="inQueue"
    request-channel="exampleChannel"/>

Since the gateways provide request/reply behavior instead of unidirectional send or receive, they also have two distinct properties for the "payload extraction" (as discussed above for the Channel Adapters' extract-payload setting). For an inbound-gateway, the extract-request-payload property determines whether the received JMS Message body will be extracted. If false, the JMS Message itself will become the Spring Integration Message payload. The default is true.

Similarly, for an inbound-gateway the extract-reply-payload property applies to the Spring Integration Message that is going to be converted into a reply JMS Message. If you want to pass the whole Spring Integration Message (as the body of a JMS ObjectMessage) then set this to false. By default, it is also true such that the Spring Integration Message payload will be converted into a JMS Message (e.g. String payload becomes a JMS TextMessage).

As with anything else, Gateway invocation might result in error. By default Producer will not be notified of the errors that might have occurred on the consumer side and will time out waiting for the reply. However there might be times when you want to communicate an error condition back to the consumer, in other words treat the Exception as a valid reply by mapping it to a Message. To accomplish this JMS Inbound Gateway provides support for a Message Channel to which errors can be sent for processing, potentially resulting in a reply Message payload that conforms to some contract defining what a caller may expect as an "error" reply. Such a channel can be configured via the error-channel attribute.

<int-jms:inbound-gateway request-destination="requestQueue"
          request-channel="jmsinputchannel"
          error-channel="errorTransformationChannel"/>

<int:transformer input-channel="exceptionTransformationChannel"
        ref="exceptionTransformer" method="createErrorResponse"/>

You might notice that this example looks very similar to that included within Section 8.3.1, “Enter the GatewayProxyFactoryBean”. The same idea applies here: The exceptionTransformer could be a simple POJO that creates error response objects, you could reference the "nullChannel" to suppress the errors, or you could leave error-channel out to let the Exception propagate.

When consuming from topics, set the pub-sub-domain attribute to true; set subscription-durable to true for a durable subscription, subscription-shared for a shared subscription (requires a JMS 2.0 broker and has been available since version 4.2). Use subscription-name to name the subscription.

[Important]Important

Starting with version 4.2, the default acknowledge mode is transacted, unless an external container is provided, in which case the container should be configured as needed. It is recommended to use transacted with the DefaultMessageListenerContainer to avoid message loss.

20.5 Outbound Gateway

The outbound Gateway creates JMS Messages from Spring Integration Messages and then sends to a request-destination. It will then handle the JMS reply Message either by using a selector to receive from the reply-destination that you configure, or if no reply-destination is provided, it will create JMS TemporaryQueue s.

[Warning]Warning

Using a reply-destination (or reply-destination-name), together with a CachingConnectionFactory with cacheConsumers set to true, can cause Out of Memory conditions. This is because each request gets a new consumer with a new selector (selecting on the correlation-key value, or on the sent JMSMessageID when there is no correlation-key). Given that these selectors are unique, they will remain in the cache unused after the current request completes.

If you specify a reply destination, you are advised to NOT use cached consumers. Alternatively, consider using a <reply-listener/> as described below.

<int-jms:outbound-gateway id="jmsOutGateway"
    request-destination="outQueue"
    request-channel="outboundJmsRequests"
    reply-channel="jmsReplies"/>

The outbound-gateway payload extraction properties are inversely related to those of the inbound-gateway (see the discussion above). That means that the extract-request-payload property value applies to the Spring Integration Message that is being converted into a JMS Message to be sent as a request, and the extract-reply-payload property value applies to the JMS Message that is received as a reply and then converted into a Spring Integration Message to be subsequently sent to the reply-channel as shown in the example configuration above.

<reply-listener/>

Spring Integration 2.2 introduced an alternative technique for handling replies. If you add a <reply-listener/> child element to the gateway, instead of creating a consumer for each reply, a MessageListener container is used to receive the replies and hand them over to the requesting thread. This provides a number of performance benefits as well as alleviating the cached consumer memory utilization problem described in the caution above.

When using a <reply-listener/> with an outbound gateway with no reply-destination, instead of creating a TemporaryQueue for each request, a single TemporaryQueue is used (the gateway will create an additional TemporaryQueue, as necessary, if the connection to the broker is lost and recovered).

When using a correlation-key, multiple gateways can share the same reply destination because the listener container uses a selector that is unique to each gateway.

[Warning]Warning

If you specify a reply listener, and specify a reply destination (or reply destination name), but provide NO correlation key, the gateway will log a warning and fall back to pre-2.2 behavior. This is because there is no way to configure a selector in this case, thus there is no way to avoid a reply going to a different gateway that might be configured with the same reply destination.

Note that, in this situation, a new consumer is used for each request, and consumers can build up in memory as described in the caution above; therefore cached consumers should not be used in this case.

<int-jms:outbound-gateway id="jmsOutGateway"
        request-destination="outQueue"
        request-channel="outboundJmsRequests"
        reply-channel="jmsReplies">
    <int-jms:reply-listener />
</int-jms-outbound-gateway>

In the above example, a reply listener with default attributes is used. The listener is very lightweight and it is anticipated that, in most cases, only a single consumer will be needed. However, attributes such as concurrent-consumers, max-concurrent-consumers etc., can be added. Refer to the schema for a complete list of supported attributes, together with the Spring JMS documentation for their meanings.

Idle Reply Listeners

Starting with version 4.2, the reply listener can be started as needed (and stopped after an idle time) instead of running for the duration of the gateway’s lifecycle. This might be useful if you have many gateways in the application context where they are mostly idle. One such situation is a context with many (inactive) partitioned Spring Batch jobs using Spring Integration and JMS for partition distribution. If all the reply listeners were active, the JMS broker would have an active consumer for each gateway. By enabling the idle timeout, each consumer would exist only while the corresponding batch job is running (and for a short time after it finishes).

See idle-reply-listener-timeout in Section 20.5.3, “Attribute Reference”.

20.5.1 Gateway Reply Correlation

The following describes the mechanisms used for reply correlation (ensuring the originating gateway receives replies to only its requests), depending on how the gateway is configured. See the next section for complete description of the attributes discussed here.

1. No reply-destination* properties; no <reply-listener>

A TemporaryQueue is created for each request, and deleted when the request is complete (successfully or otherwise). correlation-key is irrelevant.

2. A reply-destination* property is provided; no <reply-listener/>; no correlation-key

The JMSCorrelationID equal to the outgoing message id is used as a message selector for the consumer:

messageSelector = "JMSCorrelationID = '" + messageId + "'"

The responding system is expected to return the inbound JMSMessageID in the reply JMSCorrelationID - this is a common pattern and is implemented by the Spring Integration inbound gateway as well as Spring’s MessageListenerAdapter for message-driven POJOs.

[Note]Note

When using this configuration, you should not use a topic for replies; the reply may be lost.

3. A reply-destination* property is provided; no <reply-listener/>; correlation-key="JMSCorrelationID"

The gateway generates a unique correlation id and inserts it in the JMSCorrelationID header. The message selector is:

messageSelector = "JMSCorrelationID = '" + uniqueId + "'"

The responding system is expected to return the inbound JMSCorrelationID in the reply JMSCorrelationID - this is a common pattern and is implemented by the Spring Integration inbound gateway as well as Spring’s MessageListenerAdapter for message-driven POJOs.

4. A reply-destination* property is provided; no <reply-listener/>; correlation-key="myCorrelationHeader"

The gateway generates a unique correlation id and inserts it in the myCorrelationHeader message property. The correlation-key can be any user-defined value; the message selector is:

messageSelector = "myCorrelationHeader = '" + uniqueId + "'"

The responding system is expected to return the inbound myCorrelationHeader in the reply myCorrelationHeader.

5. A reply-destination* property is provided; no <reply-listener/>; correlation-key="JMSCorrelationID*"

(Note the * in the correlation key)

The gateway uses the value in the jms_correlationId header (if present) from the request message, and inserts it in the JMSCorrelationID header. The message selector is:

messageSelector = "JMSCorrelationID = '" + headers['jms_correlationId'] + "'"

The user must ensure this value is unique.

If the header does not exist, the gateway behaves as in 3. above.

The responding system is expected to return the inbound JMSCorrelationID in the reply JMSCorrelationID - this is a common pattern and is implemented by the Spring Integration inbound gateway as well as Spring’s MessageListenerAdapter for message-driven POJOs.

6. No reply-destination* properties; with <reply-listener>

A temporary queue is created and used for all replies from this gateway instance. No correlation data is needed in the message but the outgoing JMSMessageID is used internally in the gateway to direct the reply to the correct requesting thread.

7. A reply-destination* property is provided; with <reply-listener>, no correlation-key

NOT ALLOWED

The <reply-listener/> configuration is ignored and the gateway behaves as in 2. above. A warning log message is written indicating this situation.

8. A reply-destination* property is provided; with <reply-listener>, correlation-key="JMSCorrelationID"

The gateway has a unique correlation id and inserts it, together with an incrementing value in the JMSCorrelationID header (gatewayId + "_" + ++seq). The message selector is:

messageSelector = "JMSCorrelationID LIKE '" + gatewayId%'"

The responding system is expected to return the inbound JMSCorrelationID in the reply JMSCorrelationID - this is a common pattern and is implemented by the Spring Integration inbound gateway as well as Spring’s MessageListenerAdapter for message-driven POJOs. Since each gateway has a unique id, each instance only gets its own replies; the complete correlation data is used to route the reply to the correct requesting thread.

9. A reply-destination* property is provided; with <reply-listener/>; correlation-key="myCorrelationHeader"

The gateway has a unique correlation id and inserts it, together with an incrementing value in the myCorrelationHeader property (gatewayId + "_" + ++seq). The correlation-key can be any user-defined value; and the message selector is:

messageSelector = "myCorrelationHeader LIKE '" + gatewayId%'"

The responding system is expected to return the inbound myCorrelationHeader in the reply myCorrelationHeader. Since each gateway has a unique id, each instance only gets its own replies; the complete correlation data is used to route the reply to the correct requesting thread.

10. A reply-destination* property is provided; with <reply-listener/>; correlation-key="JMSCorrelationID*"

(Note the * in the correlation key)

NOT ALLOWED

User-supplied correlation ids are not permitted with a reply listener; the gateway will not initialize with this configuration.

20.5.2 Async Gateway

Starting with version 4.3, you can now specify async="true" (or setAsync(true)) when configuring the outbound gateway.

By default, when a request is sent to the gateway, the requesting thread is suspended until the reply is received and the flow then continues on that thread. If async is true, the requesting thread is released immediately after the send completes, and the reply is returned (and the flow continues) on the listener container thread. This can be useful when the gateway is invoked on a poller thread; the thread is released and is available for other tasks within the framework.

async requires a <reply-listener/> (or setUseReplyContainer(true) when using Java configuration); it also requires a correlationKey (usually JMSCorrelationID) to be specified. If either of these conditions are not met, async is ignored.

20.5.3 Attribute Reference

<int-jms:outbound-gateway
    connection-factory="connectionFactory" 1
    correlation-key="" 2
    delivery-persistent="" 3
    destination-resolver="" 4
    explicit-qos-enabled="" 5
    extract-reply-payload="true" 6
    extract-request-payload="true" 7
    header-mapper="" 8
    message-converter="" 9
    priority="" 10
    receive-timeout="" 11
    reply-channel="" 12
    reply-destination="" 13
    reply-destination-expression="" 14
    reply-destination-name="" 15
    reply-pub-sub-domain="" (16)
    reply-timeout="" (17)
    request-channel="" (18)
    request-destination="" (19)
    request-destination-expression="" (20)
    request-destination-name="" (21)
    request-pub-sub-domain="" (22)
    time-to-live="" (23)
    requires-reply="" (24)
    idle-reply-listener-timeout="" (25)
    async=""> (26)
  <int-jms:reply-listener /> (27)
</int-jms:outbound-gateway>

1

Reference to a javax.jms.ConnectionFactory; default connectionFactory.

2

The name of a property that will contain correlation data to correlate responses with replies. If omitted, the gateway will expect the responding system to return the value of the outbound JMSMessageID header in the JMSCorrelationID header. If specified, the gateway will generate a correlation id and populate the specified property with it; the responding system must echo back that value in the same property. Can be set to JMSCorrelationID, in which case the standard header is used instead of a simple String property to hold the correlation data. When a <reply-container/> is used, the correlation-key MUST be specified if an explicit reply-destination is provided. Starting with version 4.0.1 this attribute also supports the value JMSCorrelationID*, which means that if the outbound message already has a JMSCorrelationID (mapped from the jms_correlationId) header, it will be used, instead of generating a new one. Note, the JMSCorrelationID* key is not allowed when using a <reply-container/> because the container needs to set up a message selector during initialization.IMPORTANT: You should understand that the gateway has no means to ensure uniqueness and unexpected side effects can occur if the provided correlation id is not unique.

3

A boolean value indicating whether the delivery mode should be DeliveryMode.PERSISTENT (true) or DeliveryMode.NON_PERSISTENT (false). This setting will only take effect if explicit-qos-enabled is true.

4

A DestinationResolver; default is a DynamicDestinationResolver which simply maps the destination name to a queue or topic of that name.

5

When set to true, enables the use of quality of service attributes - priority, delivery-mode, time-to-live.

6

When set to true (default), the payload of the Spring Integration reply Message will be created from the JMS Reply Message’s body (using the MessageConverter). When set to false, the entire JMS Message will become the payload of the Spring Integration Message.

7

When set to true (default), the payload of the Spring Integration Message will be converted to a JMSMessage (using the MessageConverter). When set to false, the entire Spring Integration Message will be converted to the the JMSMessage. In both cases, the Spring Integration Message Headers are mapped to JMS headers and properties using the HeaderMapper.

8

A HeaderMapper used to map Spring Integration Message Headers to/from JMS Message Headers/Properties.

9

A reference to a MessageConverter for converting between JMS Messages and the Spring Integration Message payloads (or messages if extract-request-payload is false). Default is a SimpleMessageConverter.

10

The default priority of request messages. Overridden by the message priority header, if present; range 0-9. This setting will only take effect if explicit-qos-enabled is true.

11

The time (in millseconds) to wait for a reply. Default 5 seconds.

12

The channel to which the reply message will be sent.

13

A reference to a Destination which will be set as the JMSReplyTo header. At most, only one of reply-destination, reply-destination-expression, or reply-destination-name is allowed. If none is provided, a TemporaryQueue is used for replies to this gateway.

14

A SpEL expression evaluating to a Destination which will be set as the JMSReplyTo header. The expression can result in a Destination object, or a String, which will be used by the DestinationResolver to resolve the actual Destination. At most, only one of reply-destination, reply-destination-expression, or reply-destination-name is allowed. If none is provided, a TemporaryQueue is used for replies to this gateway.

15

The name of the destination which will be set as the JMSReplyTo header; used by the DestinationResolver to resolve the actual Destination. At most, only one of reply-destination, reply-destination-expression, or reply-destination-name is allowed. If none is provided, a TemporaryQueue is used for replies to this gateway.

(16)

When set to true, indicates that any reply Destination resolved by the DestinationResolver should be a Topic rather then a Queue.

(17)

The time the gateway will wait when sending the reply message to the reply-channel. This only has an effect if the reply-channel can block - such as a QueueChannel with a capacity limit that is currently full. Default: infinity.

(18)

The channel on which this gateway receives request messages.

(19)

A reference to a Destination to which request messages will be sent. One, and only one, of reply-destination, reply-destination-expression, or reply-destination-name is required.

(20)

A SpEL expression evaluating to a Destination to which request messages will be sent. The expression can result in a Destination object, or a String, which will be used by the DestinationResolver to resolve the actual Destination. One, and only one, of reply-destination, reply-destination-expression, or reply-destination-name is required.

(21)

The name of the destination to which request messages will be sent; used by the DestinationResolver to resolve the actual Destination. One, and only one, of reply-destination, reply-destination-expression, or reply-destination-name is required.

(22)

When set to true, indicates that any request Destination resolved by the DestinationResolver should be a Topic rather then a Queue.

(23)

Specify the message time to live. This setting will only take effect if explicit-qos-enabled is true.

(24)

Specify whether this outbound gateway must return a non-null value. This value is true by default, and a MessageTimeoutException will be thrown when the underlying service does not return a value after the receive-timeout. Note, it is important to keep in mind that, if the service is never expected to return a reply, it would be better to use a <int-jms:outbound-channel-adapter/> instead of a <int-jms:outbound-gateway/> with requires-reply="false". With the latter, the sending thread is blocked, waiting for a reply for the receive-timeout period.

(25)

When a <reply-listener /> is used, it’s lifecycle (start/stop) matches that of the gateway by default. When this value is greater than 0, the container is started on demand (when a request is sent). The container continues to run until at least this time elapses with no requests being received (and no replies are outstanding). The container will be started again on the next request. The stop time is a minimum and may actually be up to 1.5x this value.

(26)

See Section 20.5.2, “Async Gateway”.

(27)

When this element is included, replies are received by an asynchronous MessageListenerContainer rather than creating a consumer for each reply. This can be more efficient in many cases.

20.6 Mapping Message Headers to/from JMS Message

JMS Message can contain meta-information such as JMS API headers as well as simple properties. You can map those to/from Spring Integration Message Headers using JmsHeaderMapper. The JMS API headers are passed to the appropriate setter methods (e.g. setJMSReplyTo) whereas other headers will be copied to the general properties of the JMS Message. JMS Outbound Gateway is bootstrapped with the default implementation of JmsHeaderMapper which will map standard JMS API Headers as well as primitive/String Message Headers. Custom header mapper could also be provided via header-mapper attribute of inbound and outbound gateways.

[Important]Important

Since version 4.0, the JMSPriority header is mapped to the standard priority header for inbound messages (previously, the priority header was only used for outbound messages). To revert to the previous behavior (do not map inbound priority), use the mapInboundPriority property of DefaultJmsHeaderMapper with argument set to false.

[Important]Important

Since version 4.3, the DefaultJmsHeaderMapper now maps the standard correlationId header as a message property by invoking its toString() method (correlationId is often a UUID, which is not a type that is supported by JMS). On the inbound side, it is mapped as a String. This is independent of the jms_correlationId header which is mapped to/from the JMSCorrelationID header. The JMSCorrelationID is generally used to correlate requests and replies whereas the correlationId is often used to combine related messages into a group (such as with an aggregator or resequencer).

20.7 Message Conversion, Marshalling and Unmarshalling

If you need to convert the message, all JMS adapters and gateways, allow you to provide a MessageConverter via message-converter attribute. Simply provide the bean name of an instance of MessageConverter that is available within the same ApplicationContext. Also, to provide some consistency with Marshaller and Unmarshaller interfaces Spring provides MarshallingMessageConverter which you can configure with your own custom Marshallers and Unmarshallers

<int-jms:inbound-gateway request-destination="requestQueue"
    request-channel="inbound-gateway-channel"
    message-converter="marshallingMessageConverter"/>

<bean id="marshallingMessageConverter"
    class="org.springframework.jms.support.converter.MarshallingMessageConverter">
    <constructor-arg>
        <bean class="org.bar.SampleMarshaller"/>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.bar.SampleUnmarshaller"/>
    </constructor-arg>
</bean>
[Note]Note

Note, however, that when you provide your own MessageConverter instance, it will still be wrapped within the HeaderMappingMessageConverter. This means that the extract-request-payload and extract-reply-payload properties may affect what actual objects are passed to your converter. The HeaderMappingMessageConverter itself simply delegates to a target MessageConverter while also mapping the Spring Integration MessageHeaders to JMS Message properties and vice-versa.

20.8 JMS Backed Message Channels

The Channel Adapters and Gateways featured above are all intended for applications that are integrating with other external systems. The inbound options assume that some other system is sending JMS Messages to the JMS Destination and the outbound options assume that some other system is receiving from the Destination. The other system may or may not be a Spring Integration application. Of course, when sending the Spring Integration Message instance as the body of the JMS Message itself (with the extract-payload value set to false), it is assumed that the other system is based on Spring Integration. However, that is by no means a requirement. That flexibility is one of the benefits of using a Message-based integration option with the abstraction of "channels" or Destinations in the case of JMS.

There are cases where both the producer and consumer for a given JMS Destination are intended to be part of the same application, running within the same process. This could be accomplished by using a pair of inbound and outbound Channel Adapters. The problem with that approach is that two adapters are required even though conceptually the goal is to have a single Message Channel. A better option is supported as of Spring Integration version 2.0. Now it is possible to define a single "channel" when using the JMS namespace.

<int-jms:channel id="jmsChannel" queue="exampleQueue"/>

The channel in the above example will behave much like a normal <channel/> element from the main Spring Integration namespace. It can be referenced by both "input-channel" and "output-channel" attributes of any endpoint. The difference is that this channel is backed by a JMS Queue instance named "exampleQueue". This means that asynchronous messaging is possible between the producing and consuming endpoints, but unlike the simpler asynchronous Message Channels created by adding a <queue/> sub-element within a non-JMS <channel/> element, the Messages are not just stored in an in-memory queue. Instead those Messages are passed within a JMS Message body, and the full power of the underlying JMS provider is then available for that channel. Probably the most common rationale for using this alternative would be to take advantage of the persistence made available by the store and forward approach of JMS messaging. If configured properly, the JMS-backed Message Channel also supports transactions. In other words, a producer would not actually write to a transactional JMS-backed channel if its send operation is part of a transaction that rolls back. Likewise, a consumer would not physically remove a JMS Message from the channel if the reception of that Message is part of a transaction that rolls back. Note that the producer and consumer transactions are separate in such a scenario. This is significantly different than the propagation of a transactional context across the simple, synchronous <channel/> element that has no <queue/> sub-element.

Since the example above is referencing a JMS Queue instance, it will act as a point-to-point channel. If on the other hand, publish/subscribe behavior is needed, then a separate element can be used, and a JMS Topic can be referenced instead.

<int-jms:publish-subscribe-channel id="jmsChannel" topic="exampleTopic"/>

For either type of JMS-backed channel, the name of the destination may be provided instead of a reference.

<int-jms:channel id="jmsQueueChannel" queue-name="exampleQueueName"/>

<jms:publish-subscribe-channel id="jmsTopicChannel" topic-name="exampleTopicName"/>

In the examples above, the Destination names would be resolved by Spring’s default DynamicDestinationResolver implementation, but any implementation of the DestinationResolver interface could be provided. Also, the JMS ConnectionFactory is a required property of the channel, but by default the expected bean name would be "connectionFactory". The example below provides both a custom instance for resolution of the JMS Destination names and a different name for the ConnectionFactory.

<int-jms:channel id="jmsChannel" queue-name="exampleQueueName"
    destination-resolver="customDestinationResolver"
    connection-factory="customConnectionFactory"/>

For the <publish-subscribe-channel />; set the durable attribute to true for a durable subscription, subscription-shared for a shared subscription (requires a JMS 2.0 broker and has been available since version 4.2). Use subscription to name the subscription.

20.9 Using JMS Message Selectors

With JMS message selectors you can filter JMS Messages based on JMS headers as well as JMS properties. For example, if you want to listen to messages whose custom JMS header property fooHeaderProperty equals bar, you can specify the following expression:

fooHeaderProperty = 'bar'

Message selector expressions are a subset of the SQL-92 conditional expression syntax, and are defined as part of the Java Message Service specification (Version 1.1 April 12, 2002). Specifically, please see chapter "3.8 Message Selection". It contains a detailed explanation of the expressions syntax.

You can specify the JMS message selector attribute using XML Namespace configuration for the following Spring Integration JMS components:

  • JMS Channel
  • JMS Publish Subscribe Channel
  • JMS Inbound Channel Adapter
  • JMS Inbound Gateway
  • JMS Message-driven Channel Adapter
[Important]Important

It is important to remember that you cannot reference message body values using JMS Message selectors.

20.10 JMS Samples

To experiment with these JMS adapters, check out the JMS samples available in the Spring Integration Samples Git repository:

There are two samples included. One provides Inbound and Outbound Channel Adapters, and the other provides Inbound and Outbound Gateways. They are configured to run with an embedded_http://activemq.apache.org/[ActiveMQ]_ process, but the samples' common.xml__Spring Application Context file can easily be modified to support either a different JMS provider or a standalone ActiveMQ process.

In other words, you can split the configuration, so that the Inbound and Outbound Adapters are running in separate JVMs. If you have ActiveMQ installed, simply modify the brokerURL property within the common.xml file to use tcp://localhost:61616 (instead of vm://localhost). Both of the samples accept input via stdin and then echo back to stdout. Look at the configuration to see how these messages are routed over JMS.