Spring Integration provides channel adapters for receiving and sending JMS messages.
You need to include this dependency into your project:
Maven.
<dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-jms</artifactId> <version>5.1.0.RELEASE</version> </dependency>
Gradle.
compile "org.springframework.integration:spring-integration-jms:5.1.0.RELEASE"
The javax.jms:javax.jms-api
must be added explicitly via some JMS vendor-specific implementation, e.g. Apache ActiveMQ.
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 on a Spring MessageListener
container.
The outbound channel adapter uses the JmsTemplate
to convert and send a JMS message on demand.
By using JmsTemplate
and the MessageListener
container, Spring Integration relies on Spring’s JMS support.
This is important to understand, since most of the attributes exposed on these adapters configure the underlying JmsTemplate
and MessageListener
container.
For more details about JmsTemplate
and the MessageListener
container, see the 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 and reply operations.
The inbound gateway relies on one of Spring’s MessageListener
container implementations for message-driven reception.
It 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.
You can explicitly configure the reply-destination
reference (or reply-destination-name
or reply-destination-expression
).
Otherwise, the outbound gateway uses a JMS TemporaryQueue.
Prior to Spring Integration 2.2, if necessary, a TemporaryQueue
was created (and removed) for each request or reply.
Beginning with Spring Integration 2.2, you can configure the outbound gateway 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.
The inbound channel adapter requires a reference to either a single JmsTemplate
instance or both a ConnectionFactory
and a Destination
(you can provide a destinationName 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 | |
---|---|
Notice from the preceding configuration that the |
Note | |
---|---|
By default, all of the JMS adapters that require a reference to the |
If extract-payload
is set to true
(the default), the received JMS Message is passed through the MessageConverter
.
When relying on the default SimpleMessageConverter
, this means that the resulting Spring Integration Message has the JMS message’s body as its payload.
A JMS TextMessage
produces a string-based payload, a JMS BytesMessage
produces a byte array payload, and the serializable instance of a JMS ObjectMessage
becomes the Spring Integration message’s payload.
If you prefer to have the raw JMS message as the Spring Integration message’s payload, set extract-payload
to false
, as the following example shows:
<int-jms:inbound-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel" extract-payload="false"/> <int:poller fixed-rate="30000"/> </int-jms:inbound-channel-adapter>
Starting with version 5.0.8, a default value of the receive-timeout
is -1
(no wait) for the org.springframework.jms.connection.CachingConnectionFactory
and cacheConsumers
, otherwise it is 1 second.
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 let you set the acknowledge
attribute 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()
operation 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
.
Alternatively, consider using a jms-message-driven-channel-adapter
with acknowledge
set to transacted
(the default).
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 | |
---|---|
The message-driven adapter also accepts several properties that pertain to the If you have a custom listener container implementation (usually a subclass of |
Important | |
---|---|
Starting with version 4.2, the default |
The extract-payload property has the same effect, and its default value is true.
The poller
element is not applicable for a message-driven channel adapter, as it is actively invoked.
For most scenarios, the message-driven approach is better, since the messages are passed along to the MessageChannel
as soon as they are received from the underlying JMS consumer.
Finally, the <message-driven-channel-adapter>
element also accepts the error-channel attribute.
This provides the same basic functionality, as described in Section 10.4.1, “Enter the GatewayProxyFactoryBean
”.
The following example shows how to set an error channel on a message-driven channel adapter:
<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel" error-channel="exampleErrorChannel"/>
When comparing the preceding example to the generic gateway configuration or the JMS inbound-gateway that we discuss later, the key difference 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 send to a logging handler or it could connect 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 or subscription-shared
for a shared subscription (which requires a JMS 2.0 broker and has been available since version 4.2).
Use subscription-name
to name the subscription.
Starting with version 5.1, when the endpoint is stopped while the application remains running, the underlying listener container is shut down, closing its shared connection and consumers.
Previously, the connection and consumers remained open.
To revert to the previous behavior, set the shutdownContainerOnStop
on the JmsMessageDrivenEndpoint
to false
.
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 is configured to use transactions, the message is rolled back and redelivered repeatedly.
The conversion process occurs before and during message construction so that such errors are 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 roll back and you have an error-channel defined, the integration flow on the error-channel must re-throw the exception (or another exception).
If the error flow does not throw an exception, the transaction is committed and the message is removed.
If no error-channel is defined, the exception is thrown back to the container, as before.
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 jmsConnectionFactory
and destination
references (destinationName
may be provided in place of destination
).
As with the inbound channel adapter, the easiest way to configure this adapter is with the namespace support.
The following configuration produces an adapter that receives Spring Integration messages from the exampleChannel
, 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 to pass the Spring Integration message payload as the JMS message body.
The default value is true.
Therefore, if you pass a Spring Integration message whose payload is a String
, a JMS TextMessage
is created.
If, on the other hand, you want to send the actual Spring Integration message to another system over JMS, set it to false.
Note | |
---|---|
Regardless of the boolean value for payload extraction, the Spring Integration |
Starting with version 5.1, the <int-jms:outbound-channel-adapter>
(JmsSendingMessageHandler
) can be configured with the deliveryModeExpression
and timeToLiveExpression
properties to evaluate an appropriate QoS values for JMS message to send at runtime against request Spring Message
.
The new setMapInboundDeliveryMode(true)
and setMapInboundExpiration(true)
options of the DefaultJmsHeaderMapper
may facilitate as a source of the information for the dynamic deliveryMode
and timeToLive
from message headers:
<int-jms:outbound-channel-adapter delivery-mode-expression="headers.jms_deliveryMode" time-to-live-expression="headers.jms_expiration - T(System).currentTimeMillis()"/>
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 operation is performed within the same transaction.
Otherwise, a new transaction is started.
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 behavior, they also have two distinct properties for "payload extraction
" (as discussed earlier for the channel adapters' extract-payload setting).
For an inbound gateway, the extract-request-payload property determines whether the received JMS Message body is extracted.
If false, the JMS message itself becomes 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 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), set value this to false.
By default, it is also true that the Spring Integration message payload is converted into a JMS Message (for example, a
String
payload becomes a JMS TextMessage).
As with anything else, gateway invocation might result in error.
By default, a producer is not notified of the errors that might have occurred on the consumer side and times 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, you might want to 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 that defines what a caller may expect as an "error
" reply.
You can use the error-channel attribute to configure such a channel, as the following example shows:
<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 10.4.1, “Enter the GatewayProxyFactoryBean
”.
The same idea applies here: The exceptionTransformer
could be a 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.
See Section 23.2.1, “Inbound Conversion Errors”.
When consuming from topics, set the pub-sub-domain
attribute to true.
Set subscription-durable
to true
for a durable subscription or 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 | |
---|---|
Starting with version 4.2, the default |
Starting with version 5.1, when the endpoint is stopped while the application remains running, the underlying listener container is shut down, closing its shared connection and consumers.
Previously, the connection and consumers remained open.
To revert to the previous behavior, set the shutdownContainerOnStop
on the JmsInboundGateway
to false
.
The outbound gateway creates JMS messages from Spring Integration messages and sends them to a request-destination.
It thens 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, by creating JMS TemporaryQueue
instances.
Caution | |
---|---|
Using a If you specify a reply destination, you are advised to not use cached consumers.
Alternatively, consider using a |
The following example shows how to configure an outbound gateway:
<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 earlier discussion). That means that the extract-request-payload property value applies to the Spring Integration message being converted into a JMS message to be sent as a request. The extract-reply-payload property value applies to the JMS message received as a reply and is then converted into a Spring Integration message to be subsequently sent to the reply-channel, as shown in the preceding configuration example.
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 earlier caution.
When using a <reply-listener/>
with an outbound gateway that has no reply-destination
, instead of creating a TemporaryQueue
for each request, a single TemporaryQueue
is used.
(The gateway creates 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.
Caution | |
---|---|
If you specify a reply listener and specify a reply destination (or reply destination name) but provide no correlation key, the gateway logs a warning and falls back to pre-version 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. |
The following example shows a reply listener with default attributes:
<int-jms:outbound-gateway id="jmsOutGateway" request-destination="outQueue" request-channel="outboundJmsRequests" reply-channel="jmsReplies"> <int-jms:reply-listener /> </int-jms-outbound-gateway>
The listener is very lightweight, and we anticipate that, in most cases, you need only a single consumer.
However, you can add attributes such as concurrent-consumers
, max-concurrent-consumers
, and others.
See the schema for a complete list of supported attributes, together with the Spring JMS documentation for their meanings.
Starting with version 4.2, you can start the reply listener as needed (and stop it after an idle time) instead of running for the duration of the gateway’s lifecycle. This can 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 are active, the JMS broker has an active consumer for each gateway. By enabling the idle timeout, each consumer exists only while the corresponding batch job is running (and for a short time after it finishes).
See idle-reply-listener-timeout
in Section 23.5.5, “Attribute Reference”.
This section 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 Section 23.5.5, “Attribute Reference” for complete description of the attributes discussed here.
The following list describes the various scenarios (the numbers are for identification — order does not matter):
No reply-destination*
properties and no <reply-listener>
A TemporaryQueue
is created for each request and deleted when the request is complete (successfully or otherwise).
correlation-key
is irrelevant.
A reply-destination*
property is provided and neither a <reply-listener/>
nor a correlation-key
is provided
The JMSCorrelationID
equal to the outgoing message IS 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 | |
---|---|
When you use this configuration, you should not use a topic for replies. The reply may be lost. |
A reply-destination*
property is provided, no <reply-listener/>
is provided, and correlation-key="JMSCorrelationID"
The gateway generates a unique correlation IS 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.
A reply-destination*
property is provided, no <reply-listener/>
is provided, and 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
.
A reply-destination*
property is provided, no <reply-listener/>
is provided, and 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
.
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.
No reply-destination*
properties is provided, and a <reply-listener>
is provided
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.
A reply-destination*
property is provided, a <reply-listener>
is provided, and no correlation-key
is provided
Not allowed.
The <reply-listener/>
configuration is ignored, and the gateway behaves as in 2
.
A warning log message is written to indicate this situation.
A reply-destination*
property is provided, a <reply-listener>
is provided, and 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 gets only its own replies.
The complete correlation data is used to route the reply to the correct requesting thread.
A reply-destination*
property is provided a <reply-listener/>
is provided, and 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.
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.
A reply-destination*
property is provided, a <reply-listener/>
is provided, and correlation-key="JMSCorrelationID*"
*
(Note the *
in the correlation key)
Not allowed.
User-supplied correlation IDs are not permitted with a reply listener. The gateway does not initialize with this configuration.
Starting with version 4.3, you can now specify async="true"
(or setAsync(true)
in Java) 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.
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.
The following listing shows all the available attributes for an outbound-gateway
:
<int-jms:outbound-gateway connection-factory="connectionFactory" correlation-key="" delivery-persistent="" destination-resolver="" explicit-qos-enabled="" extract-reply-payload="true" extract-request-payload="true" header-mapper="" message-converter="" priority="" receive-timeout="" reply-channel="" reply-destination="" reply-destination-expression="" reply-destination-name="" 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>
Reference to a | ||||
The name of a property that contains correlation data to correlate responses with replies.
If omitted, the gateway expects the responding system to return the value of the outbound
| ||||
A boolean value indicating whether the delivery mode should be | ||||
A | ||||
When set to | ||||
When set to | ||||
When set to | ||||
A | ||||
A reference to a | ||||
The default priority of request messages.
Overridden by the message priority header, if present.
Its range is | ||||
The time (in milliseconds) to wait for a reply.
The default is | ||||
The channel to which the reply message is sent. | ||||
A reference to a | ||||
A SpEL expression evaluating to a | ||||
The name of the destination that is set as the JMSReplyTo header.
It is used by the | ||||
When set to | ||||
The time the gateway waits when sending the reply message to the | ||||
The channel on which this gateway receives request messages. | ||||
A reference to a | ||||
A SpEL expression evaluating to a | ||||
The name of the destination to which request messages are sent.
It is used by the | ||||
When set to | ||||
Specifies the message time to live.
This setting takes effect only if | ||||
Specifies whether this outbound gateway must return a non-null value.
By default, this value is | ||||
When you use a | ||||
When this element is included, replies are received by an asynchronous |
JMS messages can contain meta-information such as JMS API headers and simple properties.
You can map those to and from Spring Integration message headers by using JmsHeaderMapper
.
The JMS API headers are passed to the appropriate setter methods (such as setJMSReplyTo
), whereas other headers are 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 or String
message headers.
You could also provide a custom header mapper by using the header-mapper
attribute of inbound and outbound gateways.
Important | |
---|---|
Since version 4.0, the |
Important | |
---|---|
Since version 4.3, the |
Starting with version 5.1, the DefaultJmsHeaderMapper
can be configured for mapping inbound JMSDeliveryMode
and JMSExpiration
properties:
====
[source,java]
----
@Bean
public DefaultJmsHeaderMapper jmsHeaderMapper() {
DefaultJmsHeaderMapper mapper = new DefaultJmsHeaderMapper();
mapper.setMapInboundDeliveryMode(true)
mapper.setMapInboundExpiration(true)
return mapper;
}
----
====
These JMS properties are mapped to the JmsHeaders.DELIVERY_MODE
and JmsHeaders.EXPIRATION
Spring Message headers respectively.
If you need to convert the message, all JMS adapters and gateways let you provide a MessageConverter
by setting the message-converter
attribute.
To do so, 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.
The following example shows how to do so
<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 | |
---|---|
When you provide your own |
The channel adapters and gateways featured earlier are all intended for applications that integrate 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 a Spring Integration message instance as the body of the JMS message itself (with 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).
Sometimes, both the producer and consumer for a given JMS Destination are intended to be part of the same application, running within the same process.
You can accomplish this by using a pair of inbound and outbound channel adapters.
The problem with that approach is that you need two adapters, 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, as the following example shows:
<int-jms:channel id="jmsChannel" queue="exampleQueue"/>
The channel in the preceding example behaves much like a normal <channel/>
element from the main Spring Integration namespace.
It can be referenced by both the 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.
However, unlike the simpler asynchronous message channels created by adding a <queue/>
element within a non-JMS <channel/>
element, the messages are not 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 is 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 a simple, synchronous <channel/>
element that has no <queue/>
child element.
Since the preceding example above references a JMS Queue instance, it acts as a point-to-point channel. If, on the other hand, you need publish-subscribe behavior, you can use a separate element and reference a JMS Topic instead. The following example shows how to do so:
<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, as the following example shows:
<int-jms:channel id="jmsQueueChannel" queue-name="exampleQueueName"/> <jms:publish-subscribe-channel id="jmsTopicChannel" topic-name="exampleTopicName"/>
In the preceding examples, the destination names are resolved by Spring’s default DynamicDestinationResolver
implementation, but you could provide any implementation of the DestinationResolver
interface.
Also, the JMS ConnectionFactory
is a required property of the channel, but, by default, the expected bean name would be jmsConnectionFactory
.
The following example 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 or 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.
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, myHeaderProperty
, equals something
, you can specify the following expression:
myHeaderProperty = 'something'
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, see chapter "3.8, Message Selection". It contains a detailed explanation of the expressions syntax.
You can specify the JMS message selector
attribute by using XML namespace configuration for the following Spring Integration JMS components:
Important | |
---|---|
You cannot reference message body values by using JMS Message selectors. |
To experiment with these JMS adapters, check out the JMS samples available in the Spring Integration Samples Git repository at https://github.com/SpringSource/spring-integration-samples/tree/master/basic/jms.
That repository includes two samples. One provides inbound and outbound channel adapters, and the other provides inbound and outbound gateways. They are configured to run with an embedded ActiveMQ process, but you can modify the common.xml Spring application context file of each sample 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 run in separate JVMs.
If you have ActiveMQ installed, modify the brokerURL
property within the common.xml file to use tcp://localhost:61616
(instead of vm://localhost
).
Both of the samples accept input from stdin and echo back to stdout.
Look at the configuration to see how these messages are routed over JMS.