spring-framework/framework-docs/modules/ROOT/pages/integration/jms.adoc

1475 lines
65 KiB
Plaintext

[[jms]]
= JMS (Java Message Service)
Spring provides a JMS integration framework that simplifies the use of the JMS API in much
the same way as Spring's integration does for the JDBC API.
JMS can be roughly divided into two areas of functionality, namely the production and
consumption of messages. The `JmsTemplate` class is used for message production and
synchronous message reception. For asynchronous reception similar to Jakarta EE's
message-driven bean style, Spring provides a number of message-listener containers that
you can use to create Message-Driven POJOs (MDPs). Spring also provides a declarative way
to create message listeners.
The `org.springframework.jms.core` package provides the core functionality for using
JMS. It contains JMS template classes that simplify the use of the JMS by handling the
creation and release of resources, much like the `JdbcTemplate` does for JDBC. The
design principle common to Spring template classes is to provide helper methods to
perform common operations and, for more sophisticated usage, delegate the essence of the
processing task to user-implemented callback interfaces. The JMS template follows the
same design. The classes offer various convenience methods for sending messages,
consuming messages synchronously, and exposing the JMS session and message producer to
the user.
The `org.springframework.jms.support` package provides `JMSException` translation
functionality. The translation converts the checked `JMSException` hierarchy to a
mirrored hierarchy of unchecked exceptions. If any provider-specific subclasses
of the checked `jakarta.jms.JMSException` exist, this exception is wrapped in the
unchecked `UncategorizedJmsException`.
The `org.springframework.jms.support.converter` package provides a `MessageConverter`
abstraction to convert between Java objects and JMS messages.
The `org.springframework.jms.support.destination` package provides various strategies
for managing JMS destinations, such as providing a service locator for destinations
stored in JNDI.
The `org.springframework.jms.annotation` package provides the necessary infrastructure
to support annotation-driven listener endpoints by using `@JmsListener`.
The `org.springframework.jms.config` package provides the parser implementation for the
`jms` namespace as well as the java config support to configure listener containers and
create listener endpoints.
Finally, the `org.springframework.jms.connection` package provides an implementation of
the `ConnectionFactory` suitable for use in standalone applications. It also contains an
implementation of Spring's `PlatformTransactionManager` for JMS (the cunningly named
`JmsTransactionManager`). This allows for seamless integration of JMS as a transactional
resource into Spring's transaction management mechanisms.
[NOTE]
====
As of Spring Framework 5, Spring's JMS package fully supports JMS 2.0 and requires the
JMS 2.0 API to be present at runtime. We recommend the use of a JMS 2.0 compatible provider.
If you happen to use an older message broker in your system, you may try upgrading to a
JMS 2.0 compatible driver for your existing broker generation. Alternatively, you may also
try to run against a JMS 1.1 based driver, simply putting the JMS 2.0 API jar on the
classpath but only using JMS 1.1 compatible API against your driver. Spring's JMS support
adheres to JMS 1.1 conventions by default, so with corresponding configuration it does
support such a scenario. However, please consider this for transition scenarios only.
====
[[jms-using]]
== Using Spring JMS
This section describes how to use Spring's JMS components.
[[jms-jmstemplate]]
=== Using `JmsTemplate`
The `JmsTemplate` class is the central class in the JMS core package. It simplifies the
use of JMS, since it handles the creation and release of resources when sending or
synchronously receiving messages.
Code that uses the `JmsTemplate` needs only to implement callback interfaces that give them
a clearly defined high-level contract. The `MessageCreator` callback interface creates a
message when given a `Session` provided by the calling code in `JmsTemplate`. To
allow for more complex usage of the JMS API, `SessionCallback` provides the
JMS session, and `ProducerCallback` exposes a `Session` and
`MessageProducer` pair.
The JMS API exposes two types of send methods, one that takes delivery mode, priority,
and time-to-live as Quality of Service (QOS) parameters and one that takes no QOS
parameters and uses default values. Since `JmsTemplate` has many send methods,
setting the QOS parameters have been exposed as bean properties to
avoid duplication in the number of send methods. Similarly, the timeout value for
synchronous receive calls is set by using the `setReceiveTimeout` property.
Some JMS providers allow the setting of default QOS values administratively through the
configuration of the `ConnectionFactory`. This has the effect that a call to a
`MessageProducer` instance's `send` method (`send(Destination destination, Message message)`)
uses different QOS default values than those specified in the JMS specification. In order
to provide consistent management of QOS values, the `JmsTemplate` must, therefore, be
specifically enabled to use its own QOS values by setting the boolean property
`isExplicitQosEnabled` to `true`.
For convenience, `JmsTemplate` also exposes a basic request-reply operation that allows
for sending a message and waiting for a reply on a temporary queue that is created as part of
the operation.
IMPORTANT: Instances of the `JmsTemplate` class are thread-safe, once configured. This is
important, because it means that you can configure a single instance of a `JmsTemplate`
and then safely inject this shared reference into multiple collaborators. To be
clear, the `JmsTemplate` is stateful, in that it maintains a reference to a
`ConnectionFactory`, but this state is not conversational state.
As of Spring Framework 4.1, `JmsMessagingTemplate` is built on top of `JmsTemplate`
and provides an integration with the messaging abstraction -- that is,
`org.springframework.messaging.Message`. This lets you create the message to
send in a generic manner.
[[jms-connections]]
=== Connections
The `JmsTemplate` requires a reference to a `ConnectionFactory`. The `ConnectionFactory`
is part of the JMS specification and serves as the entry point for working with JMS. It
is used by the client application as a factory to create connections with the JMS
provider and encapsulates various configuration parameters, many of which are
vendor-specific, such as SSL configuration options.
When using JMS inside an EJB, the vendor provides implementations of the JMS interfaces
so that they can participate in declarative transaction management and perform pooling
of connections and sessions. In order to use this implementation, Jakarta EE containers
typically require that you declare a JMS connection factory as a `resource-ref` inside
the EJB or servlet deployment descriptors. To ensure the use of these features with the
`JmsTemplate` inside an EJB, the client application should ensure that it references the
managed implementation of the `ConnectionFactory`.
[[jms-caching-resources]]
==== Caching Messaging Resources
The standard API involves creating many intermediate objects. To send a message, the
following 'API' walk is performed:
[literal]
[subs="verbatim,quotes"]
----
ConnectionFactory->Connection->Session->MessageProducer->send
----
Between the `ConnectionFactory` and the `Send` operation, three intermediate
objects are created and destroyed. To optimize the resource usage and increase
performance, Spring provides two implementations of `ConnectionFactory`.
[[jms-connection-factory]]
==== Using `SingleConnectionFactory`
Spring provides an implementation of the `ConnectionFactory` interface,
`SingleConnectionFactory`, that returns the same `Connection` on all
`createConnection()` calls and ignores calls to `close()`. This is useful for testing and
standalone environments so that the same connection can be used for multiple
`JmsTemplate` calls that may span any number of transactions. `SingleConnectionFactory`
takes a reference to a standard `ConnectionFactory` that would typically come from JNDI.
[[jdbc-connection-factory-caching]]
==== Using `CachingConnectionFactory`
The `CachingConnectionFactory` extends the functionality of `SingleConnectionFactory`
and adds the caching of `Session`, `MessageProducer`, and `MessageConsumer` instances.
The initial cache size is set to `1`. You can use the `sessionCacheSize` property to
increase the number of cached sessions. Note that the number of actual cached sessions
is more than that number, as sessions are cached based on their acknowledgment mode,
so there can be up to four cached session instances (one for each acknowledgment mode)
when `sessionCacheSize` is set to one. `MessageProducer` and `MessageConsumer` instances
are cached within their owning session and also take into account the unique properties
of the producers and consumers when caching. MessageProducers are cached based on their
destination. MessageConsumers are cached based on a key composed of the destination, selector,
noLocal delivery flag, and the durable subscription name (if creating durable consumers).
[NOTE]
====
MessageProducers and MessageConsumers for temporary queues and topics
(TemporaryQueue/TemporaryTopic) will never be cached. Unfortunately, WebLogic JMS happens
to implement the temporary queue/topic interfaces on its regular destination implementation,
mis-indicating that none of its destinations can be cached. Please use a different connection
pool/cache on WebLogic, or customize `CachingConnectionFactory` for WebLogic purposes.
====
[[jms-destinations]]
=== Destination Management
Destinations, as `ConnectionFactory` instances, are JMS administered objects that you can store
and retrieve in JNDI. When configuring a Spring application context, you can use the
JNDI `JndiObjectFactoryBean` factory class or `<jee:jndi-lookup>` to perform dependency
injection on your object's references to JMS destinations. However, this strategy
is often cumbersome if there are a large number of destinations in the application or if there
are advanced destination management features unique to the JMS provider. Examples of
such advanced destination management include the creation of dynamic destinations or
support for a hierarchical namespace of destinations. The `JmsTemplate` delegates the
resolution of a destination name to a JMS destination object that implements the
`DestinationResolver` interface. `DynamicDestinationResolver` is the default
implementation used by `JmsTemplate` and accommodates resolving dynamic destinations. A
`JndiDestinationResolver` is also provided to act as a service locator for
destinations contained in JNDI and optionally falls back to the behavior contained in
`DynamicDestinationResolver`.
Quite often, the destinations used in a JMS application are only known at runtime and,
therefore, cannot be administratively created when the application is deployed. This is
often because there is shared application logic between interacting system components
that create destinations at runtime according to a well-known naming convention. Even
though the creation of dynamic destinations is not part of the JMS specification, most
vendors have provided this functionality. Dynamic destinations are created with a user-defined name,
which differentiates them from temporary destinations, and are often
not registered in JNDI. The API used to create dynamic destinations varies from provider
to provider since the properties associated with the destination are vendor-specific.
However, a simple implementation choice that is sometimes made by vendors is to
disregard the warnings in the JMS specification and to use the method `TopicSession`
`createTopic(String topicName)` or the `QueueSession` `createQueue(String
queueName)` method to create a new destination with default destination properties. Depending
on the vendor implementation, `DynamicDestinationResolver` can then also create a
physical destination instead of only resolving one.
The boolean property `pubSubDomain` is used to configure the `JmsTemplate` with
knowledge of what JMS domain is being used. By default, the value of this property is
false, indicating that the point-to-point domain, `Queues`, is to be used. This property
(used by `JmsTemplate`) determines the behavior of dynamic destination resolution through
implementations of the `DestinationResolver` interface.
You can also configure the `JmsTemplate` with a default destination through the
property `defaultDestination`. The default destination is with send and receive
operations that do not refer to a specific destination.
[[jms-mdp]]
=== Message Listener Containers
One of the most common uses of JMS messages in the EJB world is to drive message-driven
beans (MDBs). Spring offers a solution to create message-driven POJOs (MDPs) in a way
that does not tie a user to an EJB container. (See <<jms-receiving-async>> for detailed
coverage of Spring's MDP support.) Since Spring Framework 4.1, endpoint methods can be
annotated with `@JmsListener` -- see <<jms-annotated>> for more details.
A message listener container is used to receive messages from a JMS message queue and
drive the `MessageListener` that is injected into it. The listener container is
responsible for all threading of message reception and dispatches into the listener for
processing. A message listener container is the intermediary between an MDP and a
messaging provider and takes care of registering to receive messages, participating in
transactions, resource acquisition and release, exception conversion, and so on. This
lets you write the (possibly complex) business logic
associated with receiving a message (and possibly respond to it), and delegates
boilerplate JMS infrastructure concerns to the framework.
There are two standard JMS message listener containers packaged with Spring, each with
its specialized feature set.
* <<jms-mdp-simple, `SimpleMessageListenerContainer`>>
* <<jms-mdp-default, `DefaultMessageListenerContainer`>>
[[jms-mdp-simple]]
==== Using `SimpleMessageListenerContainer`
This message listener container is the simpler of the two standard flavors. It creates
a fixed number of JMS sessions and consumers at startup, registers the listener by using
the standard JMS `MessageConsumer.setMessageListener()` method, and leaves it up the JMS
provider to perform listener callbacks. This variant does not allow for dynamic adaption
to runtime demands or for participation in externally managed transactions.
Compatibility-wise, it stays very close to the spirit of the standalone JMS
specification, but is generally not compatible with Jakarta EE's JMS restrictions.
NOTE: While `SimpleMessageListenerContainer` does not allow for participation in externally
managed transactions, it does support native JMS transactions. To enable this feature,
you can switch the `sessionTransacted` flag to `true` or, in the XML namespace, set the
`acknowledge` attribute to `transacted`. Exceptions thrown from your listener then lead
to a rollback, with the message getting redelivered. Alternatively, consider using
`CLIENT_ACKNOWLEDGE` mode, which provides redelivery in case of an exception as well but
does not use transacted `Session` instances and, therefore, does not include any other
`Session` operations (such as sending response messages) in the transaction protocol.
IMPORTANT: The default `AUTO_ACKNOWLEDGE` mode does not provide proper reliability guarantees.
Messages can get lost when listener execution fails (since the provider automatically
acknowledges each message after listener invocation, with no exceptions to be propagated to
the provider) or when the listener container shuts down (you can configure this by setting
the `acceptMessagesWhileStopping` flag). Make sure to use transacted sessions in case of
reliability needs (for example, for reliable queue handling and durable topic subscriptions).
[[jms-mdp-default]]
==== Using `DefaultMessageListenerContainer`
This message listener container is used in most cases. In contrast to
`SimpleMessageListenerContainer`, this container variant allows for dynamic adaptation
to runtime demands and is able to participate in externally managed transactions.
Each received message is registered with an XA transaction when configured with a
`JtaTransactionManager`. As a result, processing may take advantage of XA transaction
semantics. This listener container strikes a good balance between low requirements on
the JMS provider, advanced functionality (such as participation in externally managed
transactions), and compatibility with Jakarta EE environments.
You can customize the cache level of the container. Note that, when no caching is enabled,
a new connection and a new session is created for each message reception. Combining this
with a non-durable subscription with high loads may lead to message loss. Make sure to
use a proper cache level in such a case.
This container also has recoverable capabilities when the broker goes down. By default,
a simple `BackOff` implementation retries every five seconds. You can specify
a custom `BackOff` implementation for more fine-grained recovery options. See
{api-spring-framework}/util/backoff/ExponentialBackOff.html[`ExponentialBackOff`] for an example.
NOTE: Like its sibling (<<jms-mdp-simple, `SimpleMessageListenerContainer`>>),
`DefaultMessageListenerContainer` supports native JMS transactions and allows for
customizing the acknowledgment mode. If feasible for your scenario, This is strongly
recommended over externally managed transactions -- that is, if you can live with
occasional duplicate messages in case of the JVM dying. Custom duplicate message
detection steps in your business logic can cover such situations -- for example,
in the form of a business entity existence check or a protocol table check.
Any such arrangements are significantly more efficient than the alternative:
wrapping your entire processing with an XA transaction (through configuring your
`DefaultMessageListenerContainer` with an `JtaTransactionManager`) to cover the
reception of the JMS message as well as the execution of the business logic in your
message listener (including database operations, etc.).
IMPORTANT: The default `AUTO_ACKNOWLEDGE` mode does not provide proper reliability guarantees.
Messages can get lost when listener execution fails (since the provider automatically
acknowledges each message after listener invocation, with no exceptions to be propagated to
the provider) or when the listener container shuts down (you can configure this by setting
the `acceptMessagesWhileStopping` flag). Make sure to use transacted sessions in case of
reliability needs (for example, for reliable queue handling and durable topic subscriptions).
[[jms-tx]]
=== Transaction Management
Spring provides a `JmsTransactionManager` that manages transactions for a single JMS
`ConnectionFactory`. This lets JMS applications leverage the managed-transaction
features of Spring, as described in
<<data-access.adoc#transaction, Transaction Management section of the Data Access chapter>>.
The `JmsTransactionManager` performs local resource transactions, binding a JMS
Connection/Session pair from the specified `ConnectionFactory` to the thread.
`JmsTemplate` automatically detects such transactional resources and operates
on them accordingly.
In a Jakarta EE environment, the `ConnectionFactory` pools Connection and Session instances,
so those resources are efficiently reused across transactions. In a standalone environment,
using Spring's `SingleConnectionFactory` result in a shared JMS `Connection`, with
each transaction having its own independent `Session`. Alternatively, consider the use
of a provider-specific pooling adapter, such as ActiveMQ's `PooledConnectionFactory`
class.
You can also use `JmsTemplate` with the `JtaTransactionManager` and an XA-capable JMS
`ConnectionFactory` to perform distributed transactions. Note that this requires the
use of a JTA transaction manager as well as a properly XA-configured ConnectionFactory.
(Check your Jakarta EE server's or JMS provider's documentation.)
Reusing code across a managed and unmanaged transactional environment can be confusing
when using the JMS API to create a `Session` from a `Connection`. This is because the
JMS API has only one factory method to create a `Session`, and it requires values for the
transaction and acknowledgment modes. In a managed environment, setting these values is
the responsibility of the environment's transactional infrastructure, so these values
are ignored by the vendor's wrapper to the JMS Connection. When you use the `JmsTemplate`
in an unmanaged environment, you can specify these values through the use of the
properties `sessionTransacted` and `sessionAcknowledgeMode`. When you use a
`PlatformTransactionManager` with `JmsTemplate`, the template is always given a
transactional JMS `Session`.
[[jms-sending]]
== Sending a Message
The `JmsTemplate` contains many convenience methods to send a message. Send
methods specify the destination by using a `jakarta.jms.Destination` object, and others
specify the destination by using a `String` in a JNDI lookup. The `send` method
that takes no destination argument uses the default destination.
The following example uses the `MessageCreator` callback to create a text message from the
supplied `Session` object:
[source,java,indent=0,subs="verbatim,quotes"]
----
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.Queue;
import jakarta.jms.Session;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.JmsTemplate;
public class JmsQueueSender {
private JmsTemplate jmsTemplate;
private Queue queue;
public void setConnectionFactory(ConnectionFactory cf) {
this.jmsTemplate = new JmsTemplate(cf);
}
public void setQueue(Queue queue) {
this.queue = queue;
}
public void simpleSend() {
this.jmsTemplate.send(this.queue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("hello queue world");
}
});
}
}
----
In the preceding example, the `JmsTemplate` is constructed by passing a reference to a
`ConnectionFactory`. As an alternative, a zero-argument constructor and
`connectionFactory` is provided and can be used for constructing the instance in
JavaBean style (using a `BeanFactory` or plain Java code). Alternatively, consider
deriving from Spring's `JmsGatewaySupport` convenience base class, which provides
pre-built bean properties for JMS configuration.
The `send(String destinationName, MessageCreator creator)` method lets you send a
message by using the string name of the destination. If these names are registered in JNDI,
you should set the `destinationResolver` property of the template to an instance of
`JndiDestinationResolver`.
If you created the `JmsTemplate` and specified a default destination, the
`send(MessageCreator c)` sends a message to that destination.
[[jms-msg-conversion]]
=== Using Message Converters
To facilitate the sending of domain model objects, the `JmsTemplate` has
various send methods that take a Java object as an argument for a message's data
content. The overloaded methods `convertAndSend()` and `receiveAndConvert()` methods in
`JmsTemplate` delegate the conversion process to an instance of the `MessageConverter`
interface. This interface defines a simple contract to convert between Java objects and
JMS messages. The default implementation (`SimpleMessageConverter`) supports conversion
between `String` and `TextMessage`, `byte[]` and `BytesMessage`, and `java.util.Map`
and `MapMessage`. By using the converter, you and your application code can focus on the
business object that is being sent or received through JMS and not be concerned with the
details of how it is represented as a JMS message.
The sandbox currently includes a `MapMessageConverter`, which uses reflection to convert
between a JavaBean and a `MapMessage`. Other popular implementation choices you might
implement yourself are converters that use an existing XML marshalling package (such as
JAXB or XStream) to create a `TextMessage` that represents the object.
To accommodate the setting of a message's properties, headers, and body that can not be
generically encapsulated inside a converter class, the `MessagePostProcessor` interface
gives you access to the message after it has been converted but before it is sent. The
following example shows how to modify a message header and a property after a
`java.util.Map` is converted to a message:
[source,java,indent=0,subs="verbatim,quotes"]
----
public void sendWithConversion() {
Map map = new HashMap();
map.put("Name", "Mark");
map.put("Age", new Integer(47));
jmsTemplate.convertAndSend("testQueue", map, new MessagePostProcessor() {
public Message postProcessMessage(Message message) throws JMSException {
message.setIntProperty("AccountID", 1234);
message.setJMSCorrelationID("123-00001");
return message;
}
});
}
----
This results in a message of the following form:
[literal]
[subs="verbatim,quotes"]
----
MapMessage={
Header={
... standard headers ...
CorrelationID={123-00001}
}
Properties={
AccountID={Integer:1234}
}
Fields={
Name={String:Mark}
Age={Integer:47}
}
}
----
[[jms-callbacks]]
=== Using `SessionCallback` and `ProducerCallback`
While the send operations cover many common usage scenarios, you might sometimes
want to perform multiple operations on a JMS `Session` or `MessageProducer`. The
`SessionCallback` and `ProducerCallback` expose the JMS `Session` and `Session` /
`MessageProducer` pair, respectively. The `execute()` methods on `JmsTemplate` run
these callback methods.
[[jms-receiving]]
== Receiving a Message
This describes how to receive messages with JMS in Spring.
[[jms-receiving-sync]]
=== Synchronous Reception
While JMS is typically associated with asynchronous processing, you can
consume messages synchronously. The overloaded `receive(..)` methods provide this
functionality. During a synchronous receive, the calling thread blocks until a message
becomes available. This can be a dangerous operation, since the calling thread can
potentially be blocked indefinitely. The `receiveTimeout` property specifies how long
the receiver should wait before giving up waiting for a message.
[[jms-receiving-async]]
=== Asynchronous reception: Message-Driven POJOs
NOTE: Spring also supports annotated-listener endpoints through the use of the `@JmsListener`
annotation and provides an open infrastructure to register endpoints programmatically.
This is, by far, the most convenient way to setup an asynchronous receiver.
See <<jms-annotated-support>> for more details.
In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Message-Driven
POJO (MDP) acts as a receiver for JMS messages. The one restriction (but see
<<jms-receiving-async-message-listener-adapter>>) on an MDP is that it must implement
the `jakarta.jms.MessageListener` interface. Note that, if your POJO receives messages
on multiple threads, it is important to ensure that your implementation is thread-safe.
The following example shows a simple implementation of an MDP:
[source,java,indent=0,subs="verbatim,quotes"]
----
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.MessageListener;
import jakarta.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage textMessage) {
try {
System.out.println(textMessage.getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
----
Once you have implemented your `MessageListener`, it is time to create a message listener
container.
The following example shows how to define and configure one of the message listener
containers that ships with Spring (in this case, `DefaultMessageListenerContainer`):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener"/>
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
See the Spring javadoc of the various message listener containers (all of which implement
{api-spring-framework}/jms/listener/MessageListenerContainer.html[MessageListenerContainer])
for a full description of the features supported by each implementation.
[[jms-receiving-async-session-aware-message-listener]]
=== Using the `SessionAwareMessageListener` Interface
The `SessionAwareMessageListener` interface is a Spring-specific interface that provides
a similar contract to the JMS `MessageListener` interface but also gives the message-handling
method access to the JMS `Session` from which the `Message` was received.
The following listing shows the definition of the `SessionAwareMessageListener` interface:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.jms.listener;
public interface SessionAwareMessageListener {
void onMessage(Message message, Session session) throws JMSException;
}
----
You can choose to have your MDPs implement this interface (in preference to the standard
JMS `MessageListener` interface) if you want your MDPs to be able to respond to any
received messages (by using the `Session` supplied in the `onMessage(Message, Session)`
method). All of the message listener container implementations that ship with Spring
have support for MDPs that implement either the `MessageListener` or
`SessionAwareMessageListener` interface. Classes that implement the
`SessionAwareMessageListener` come with the caveat that they are then tied to Spring
through the interface. The choice of whether or not to use it is left entirely up to you
as an application developer or architect.
Note that the `onMessage(..)` method of the `SessionAwareMessageListener`
interface throws `JMSException`. In contrast to the standard JMS `MessageListener`
interface, when using the `SessionAwareMessageListener` interface, it is the
responsibility of the client code to handle any thrown exceptions.
[[jms-receiving-async-message-listener-adapter]]
=== Using `MessageListenerAdapter`
The `MessageListenerAdapter` class is the final component in Spring's asynchronous
messaging support. In a nutshell, it lets you expose almost any class as an MDP
(though there are some constraints).
Consider the following interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
}
----
Notice that, although the interface extends neither the `MessageListener` nor the
`SessionAwareMessageListener` interface, you can still use it as an MDP by using the
`MessageListenerAdapter` class. Notice also how the various message handling methods are
strongly typed according to the contents of the various `Message` types that they can
receive and handle.
Now consider the following implementation of the `MessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
----
In particular, note how the preceding implementation of the `MessageDelegate` interface (the
`DefaultMessageDelegate` class) has no JMS dependencies at all. It truly is a
POJO that we can make into an MDP through the following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
The next example shows another MDP that can handle only receiving JMS
`TextMessage` messages. Notice how the message handling method is actually called
`receive` (the name of the message handling method in a `MessageListenerAdapter`
defaults to `handleMessage`), but it is configurable (as you can see later in this section). Notice
also how the `receive(..)` method is strongly typed to receive and respond only to JMS
`TextMessage` messages.
The following listing shows the definition of the `TextMessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TextMessageDelegate {
void receive(TextMessage message);
}
----
The following listing shows a class that implements the `TextMessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultTextMessageDelegate implements TextMessageDelegate {
// implementation elided for clarity...
}
----
The configuration of the attendant `MessageListenerAdapter` would then be as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultTextMessageDelegate"/>
</constructor-arg>
<property name="defaultListenerMethod" value="receive"/>
<!-- we don't want automatic message context extraction -->
<property name="messageConverter">
<null/>
</property>
</bean>
----
Note that, if the `messageListener` receives a JMS `Message` of a type
other than `TextMessage`, an `IllegalStateException` is thrown (and subsequently
swallowed). Another of the capabilities of the `MessageListenerAdapter` class is the
ability to automatically send back a response `Message` if a handler method returns a
non-void value. Consider the following interface and class:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResponsiveTextMessageDelegate {
// notice the return type...
String receive(TextMessage message);
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
// implementation elided for clarity...
}
----
If you use the `DefaultResponsiveTextMessageDelegate` in conjunction with a
`MessageListenerAdapter`, any non-null value that is returned from the execution of
the `'receive(..)'` method is (in the default configuration) converted into a
`TextMessage`. The resulting `TextMessage` is then sent to the `Destination` (if
one exists) defined in the JMS `Reply-To` property of the original `Message` or the
default `Destination` set on the `MessageListenerAdapter` (if one has been configured).
If no `Destination` is found, an `InvalidDestinationException` is thrown
(note that this exception is not swallowed and propagates up the
call stack).
[[jms-tx-participation]]
=== Processing Messages Within Transactions
Invoking a message listener within a transaction requires only reconfiguration of the
listener container.
You can activate local resource transactions through the `sessionTransacted` flag
on the listener container definition. Each message listener invocation then operates
within an active JMS transaction, with message reception rolled back in case of listener
execution failure. Sending a response message (through `SessionAwareMessageListener`) is
part of the same local transaction, but any other resource operations (such as
database access) operate independently. This usually requires duplicate message
detection in the listener implementation, to cover the case where database processing
has committed but message processing failed to commit.
Consider the following bean definition:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="sessionTransacted" value="true"/>
</bean>
----
To participate in an externally managed transaction, you need to configure a
transaction manager and use a listener container that supports externally managed
transactions (typically, `DefaultMessageListenerContainer`).
To configure a message listener container for XA transaction participation, you want
to configure a `JtaTransactionManager` (which, by default, delegates to the Jakarta EE
server's transaction subsystem). Note that the underlying JMS `ConnectionFactory` needs to
be XA-capable and properly registered with your JTA transaction coordinator. (Check your
Jakarta EE server's configuration of JNDI resources.) This lets message reception as well
as (for example) database access be part of the same transaction (with unified commit
semantics, at the expense of XA transaction log overhead).
The following bean definition creates a transaction manager:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
Then we need to add it to our earlier container configuration. The container
takes care of the rest. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/> <1>
</bean>
----
<1> Our transaction manager.
[[jms-jca-message-endpoint-manager]]
== Support for JCA Message Endpoints
Beginning with version 2.5, Spring also provides support for a JCA-based
`MessageListener` container. The `JmsMessageEndpointManager` tries to
automatically determine the `ActivationSpec` class name from the provider's
`ResourceAdapter` class name. Therefore, it is typically possible to provide
Spring's generic `JmsActivationSpecConfig`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpecConfig">
<bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig">
<property name="destinationName" value="myQueue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
Alternatively, you can set up a `JmsMessageEndpointManager` with a given
`ActivationSpec` object. The `ActivationSpec` object may also come from a JNDI lookup
(using `<jee:jndi-lookup>`). The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="myQueue"/>
<property name="destinationType" value="jakarta.jms.Queue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
Using Spring's `ResourceAdapterFactoryBean`, you can configure the target `ResourceAdapter`
locally, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
<property name="resourceAdapter">
<bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="workManager">
<bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
</property>
</bean>
----
The specified `WorkManager` can also point to an environment-specific thread pool --
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property. Consider
defining a shared thread pool for all your `ResourceAdapter` instances if you happen to
use multiple adapters.
In some environments (such as WebLogic 9 or above), you can instead obtain the entire `ResourceAdapter` object
from JNDI (by using `<jee:jndi-lookup>`). The Spring-based message
listeners can then interact with the server-hosted `ResourceAdapter`, which also use the
server's built-in `WorkManager`.
See the javadoc for {api-spring-framework}/jms/listener/endpoint/JmsMessageEndpointManager.html[`JmsMessageEndpointManager`],
{api-spring-framework}/jms/listener/endpoint/JmsActivationSpecConfig.html[`JmsActivationSpecConfig`],
and {api-spring-framework}/jca/support/ResourceAdapterFactoryBean.html[`ResourceAdapterFactoryBean`]
for more details.
Spring also provides a generic JCA message endpoint manager that is not tied to JMS:
`org.springframework.jca.endpoint.GenericMessageEndpointManager`. This component allows
for using any message listener type (such as a JMS `MessageListener`) and any
provider-specific `ActivationSpec` object. See your JCA provider's documentation to
find out about the actual capabilities of your connector, and see the
{api-spring-framework}/jca/endpoint/GenericMessageEndpointManager.html[`GenericMessageEndpointManager`]
javadoc for the Spring-specific configuration details.
NOTE: JCA-based message endpoint management is very analogous to EJB 2.1 Message-Driven Beans.
It uses the same underlying resource provider contract. As with EJB 2.1 MDBs, you can use any
message listener interface supported by your JCA provider in the Spring context as well.
Spring nevertheless provides explicit "`convenience`" support for JMS, because JMS is the
most common endpoint API used with the JCA endpoint management contract.
[[jms-annotated]]
== Annotation-driven Listener Endpoints
The easiest way to receive a message asynchronously is to use the annotated listener
endpoint infrastructure. In a nutshell, it lets you expose a method of a managed
bean as a JMS listener endpoint. The following example shows how to use it:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Component
public class MyService {
@JmsListener(destination = "myDestination")
public void processOrder(String data) { ... }
}
----
The idea of the preceding example is that, whenever a message is available on the
`jakarta.jms.Destination` `myDestination`, the `processOrder` method is invoked
accordingly (in this case, with the content of the JMS message, similar to
what the <<jms-receiving-async-message-listener-adapter, `MessageListenerAdapter`>>
provides).
The annotated endpoint infrastructure creates a message listener container
behind the scenes for each annotated method, by using a `JmsListenerContainerFactory`.
Such a container is not registered against the application context but can be easily
located for management purposes by using the `JmsListenerEndpointRegistry` bean.
TIP: `@JmsListener` is a repeatable annotation on Java 8, so you can associate
several JMS destinations with the same method by adding additional `@JmsListener`
declarations to it.
[[jms-annotated-support]]
=== Enable Listener Endpoint Annotations
To enable support for `@JmsListener` annotations, you can add `@EnableJms` to one of
your `@Configuration` classes, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency("3-10");
return factory;
}
}
----
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory`
as the source for the factory to use to create message listener containers. In this
case (and ignoring the JMS infrastructure setup), you can invoke the `processOrder`
method with a core poll size of three threads and a maximum pool size of ten threads.
You can customize the listener container factory to use for each annotation or you can
configure an explicit default by implementing the `JmsListenerConfigurer` interface.
The default is required only if at least one endpoint is registered without a specific
container factory. See the javadoc of classes that implement
{api-spring-framework}/jms/annotation/JmsListenerConfigurer.html[`JmsListenerConfigurer`]
for details and examples.
If you prefer <<jms-namespace, XML configuration>>, you can use the `<jms:annotation-driven>`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:annotation-driven/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationResolver" ref="destinationResolver"/>
<property name="sessionTransacted" value="true"/>
<property name="concurrency" value="3-10"/>
</bean>
----
[[jms-annotated-programmatic-registration]]
=== Programmatic Endpoint Registration
`JmsListenerEndpoint` provides a model of a JMS endpoint and is responsible for configuring
the container for that model. The infrastructure lets you programmatically configure endpoints
in addition to the ones that are detected by the `JmsListener` annotation.
The following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myJmsEndpoint");
endpoint.setDestination("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
}
----
In the preceding example, we used `SimpleJmsListenerEndpoint`, which provides the actual
`MessageListener` to invoke. However, you could also build your own endpoint variant
to describe a custom invocation mechanism.
Note that you could skip the use of `@JmsListener` altogether
and programmatically register only your endpoints through `JmsListenerConfigurer`.
[[jms-annotated-method-signature]]
=== Annotated Endpoint Method Signature
So far, we have been injecting a simple `String` in our endpoint, but it can actually
have a very flexible method signature. In the following example, we rewrite it to inject the `Order` with
a custom header:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Component
public class MyService {
@JmsListener(destination = "myDestination")
public void processOrder(Order order, @Header("order_type") String orderType) {
...
}
}
----
The main elements you can inject in JMS listener endpoints are as follows:
* The raw `jakarta.jms.Message` or any of its subclasses (provided that it
matches the incoming message type).
* The `jakarta.jms.Session` for optional access to the native JMS API (for example, for sending
a custom reply).
* The `org.springframework.messaging.Message` that represents the incoming JMS message.
Note that this message holds both the custom and the standard headers (as defined
by `JmsHeaders`).
* `@Header`-annotated method arguments to extract a specific header value, including
standard JMS headers.
* A `@Headers`-annotated argument that must also be assignable to `java.util.Map` for
getting access to all headers.
* A non-annotated element that is not one of the supported types (`Message` or
`Session`) is considered to be the payload. You can make that explicit by annotating
the parameter with `@Payload`. You can also turn on validation by adding an extra
`@Valid`.
The ability to inject Spring's `Message` abstraction is particularly useful to benefit
from all the information stored in the transport-specific message without relying on
transport-specific API. The following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
@JmsListener(destination = "myDestination")
public void processOrder(Message<Order> order) { ... }
----
Handling of method arguments is provided by `DefaultMessageHandlerMethodFactory`, which you can
further customize to support additional method arguments. You can customize the conversion and validation
support there as well.
For instance, if we want to make sure our `Order` is valid before processing it, we can
annotate the payload with `@Valid` and configure the necessary validator, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory());
}
@Bean
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setValidator(myValidator());
return factory;
}
}
----
[[jms-annotated-response]]
=== Response Management
The existing support in <<jms-receiving-async-message-listener-adapter, `MessageListenerAdapter`>>
already lets your method have a non-`void` return type. When that is the case, the result of
the invocation is encapsulated in a `jakarta.jms.Message`, sent either in the destination specified
in the `JMSReplyTo` header of the original message or in the default destination configured on
the listener. You can now set that default destination by using the `@SendTo` annotation of the
messaging abstraction.
Assuming that our `processOrder` method should now return an `OrderStatus`, we can write it
to automatically send a response, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@JmsListener(destination = "myDestination")
@SendTo("status")
public OrderStatus processOrder(Order order) {
// order processing
return status;
}
----
TIP: If you have several `@JmsListener`-annotated methods, you can also place the `@SendTo`
annotation at the class level to share a default reply destination.
If you need to set additional headers in a transport-independent manner, you can return a
`Message` instead, with a method similar to the following:
[source,java,indent=0,subs="verbatim,quotes"]
----
@JmsListener(destination = "myDestination")
@SendTo("status")
public Message<OrderStatus> processOrder(Order order) {
// order processing
return MessageBuilder
.withPayload(status)
.setHeader("code", 1234)
.build();
}
----
If you need to compute the response destination at runtime, you can encapsulate your response
in a `JmsResponse` instance that also provides the destination to use at runtime. We can rewrite the previous
example as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@JmsListener(destination = "myDestination")
public JmsResponse<Message<OrderStatus>> processOrder(Order order) {
// order processing
Message<OrderStatus> response = MessageBuilder
.withPayload(status)
.setHeader("code", 1234)
.build();
return JmsResponse.forQueue(response, "status");
}
----
Finally, if you need to specify some QoS values for the response such as the priority or
the time to live, you can configure the `JmsListenerContainerFactory` accordingly,
as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
QosSettings replyQosSettings = new QosSettings();
replyQosSettings.setPriority(2);
replyQosSettings.setTimeToLive(10000);
factory.setReplyQosSettings(replyQosSettings);
return factory;
}
}
----
[[jms-namespace]]
== JMS Namespace Support
Spring provides an XML namespace for simplifying JMS configuration. To use the JMS
namespace elements, you need to reference the JMS schema, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:jms="http://www.springframework.org/schema/jms" <1>
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms
https://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- bean definitions here -->
</beans>
----
<1> Referencing the JMS schema.
The namespace consists of three top-level elements: `<annotation-driven/>`, `<listener-container/>`
and `<jca-listener-container/>`. `<annotation-driven/>` enables the use of <<jms-annotated,
annotation-driven listener endpoints>>. `<listener-container/>` and `<jca-listener-container/>`
define shared listener container configuration and can contain `<listener/>` child elements.
The following example shows a basic configuration for two listeners:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:listener-container>
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>
</jms:listener-container>
----
The preceding example is equivalent to creating two distinct listener container bean
definitions and two distinct `MessageListenerAdapter` bean definitions, as shown
in <<jms-receiving-async-message-listener-adapter>>. In addition to the attributes shown
in the preceding example, the `listener` element can contain several optional ones.
The following table describes all of the available attributes:
[[jms-namespace-listener-tbl]]
.Attributes of the JMS <listener> element
[cols="1,6"]
|===
| Attribute | Description
| `id`
| A bean name for the hosting listener container. If not specified, a bean name is
automatically generated.
| `destination` (required)
| The destination name for this listener, resolved through the `DestinationResolver`
strategy.
| `ref` (required)
| The bean name of the handler object.
| `method`
| The name of the handler method to invoke. If the `ref` attribute points to a `MessageListener`
or Spring `SessionAwareMessageListener`, you can omit this attribute.
| `response-destination`
| The name of the default response destination to which to send response messages. This is
applied in case of a request message that does not carry a `JMSReplyTo` field. The
type of this destination is determined by the listener-container's
`response-destination-type` attribute. Note that this applies only to a listener method with a
return value, for which each result object is converted into a response message.
| `subscription`
| The name of the durable subscription, if any.
| `selector`
| An optional message selector for this listener.
| `concurrency`
| The number of concurrent sessions or consumers to start for this listener. This value can either be
a simple number indicating the maximum number (for example, `5`) or a range indicating the lower
as well as the upper limit (for example, `3-5`). Note that a specified minimum is only a hint
and might be ignored at runtime. The default is the value provided by the container.
|===
The `<listener-container/>` element also accepts several optional attributes. This
allows for customization of the various strategies (for example, `taskExecutor` and
`destinationResolver`) as well as basic JMS settings and resource references. By using
these attributes, you can define highly-customized listener containers while
still benefiting from the convenience of the namespace.
You can automatically expose such settings as a `JmsListenerContainerFactory` by
specifying the `id` of the bean to expose through the `factory-id` attribute,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:listener-container connection-factory="myConnectionFactory"
task-executor="myTaskExecutor"
destination-resolver="myDestinationResolver"
transaction-manager="myTransactionManager"
concurrency="10">
<jms:listener destination="queue.orders" ref="orderService" method="placeOrder"/>
<jms:listener destination="queue.confirmations" ref="confirmationLogger" method="log"/>
</jms:listener-container>
----
The following table describes all available attributes. See the class-level javadoc
of the {api-spring-framework}/jms/listener/AbstractMessageListenerContainer.html[`AbstractMessageListenerContainer`]
and its concrete subclasses for more details on the individual properties. The javadoc
also provides a discussion of transaction choices and message redelivery scenarios.
[[jms-namespace-listener-container-tbl]]
.Attributes of the JMS <listener-container> element
[cols="1,6"]
|===
| Attribute | Description
| `container-type`
| The type of this listener container. The available options are `default`, `simple`,
`default102`, or `simple102` (the default option is `default`).
| `container-class`
| A custom listener container implementation class as a fully qualified class name.
The default is Spring's standard `DefaultMessageListenerContainer` or
`SimpleMessageListenerContainer`, according to the `container-type` attribute.
| `factory-id`
| Exposes the settings defined by this element as a `JmsListenerContainerFactory`
with the specified `id` so that they can be reused with other endpoints.
| `connection-factory`
| A reference to the JMS `ConnectionFactory` bean (the default bean name is
`connectionFactory`).
| `task-executor`
| A reference to the Spring `TaskExecutor` for the JMS listener invokers.
| `destination-resolver`
| A reference to the `DestinationResolver` strategy for resolving JMS `Destination` instances.
| `message-converter`
| A reference to the `MessageConverter` strategy for converting JMS Messages to listener
method arguments. The default is a `SimpleMessageConverter`.
| `error-handler`
| A reference to an `ErrorHandler` strategy for handling any uncaught exceptions that
may occur during the execution of the `MessageListener`.
| `destination-type`
| The JMS destination type for this listener: `queue`, `topic`, `durableTopic`, `sharedTopic`,
or `sharedDurableTopic`. This potentially enables the `pubSubDomain`, `subscriptionDurable`
and `subscriptionShared` properties of the container. The default is `queue` (which disables
those three properties).
| `response-destination-type`
| The JMS destination type for responses: `queue` or `topic`. The default is the value of the
`destination-type` attribute.
| `client-id`
| The JMS client ID for this listener container. You must specify it when you use
durable subscriptions.
| `cache`
| The cache level for JMS resources: `none`, `connection`, `session`, `consumer`, or
`auto`. By default (`auto`), the cache level is effectively `consumer`, unless
an external transaction manager has been specified -- in which case, the effective
default will be `none` (assuming Jakarta EE-style transaction management, where the given
ConnectionFactory is an XA-aware pool).
| `acknowledge`
| The native JMS acknowledge mode: `auto`, `client`, `dups-ok`, or `transacted`. A value
of `transacted` activates a locally transacted `Session`. As an alternative, you can specify
the `transaction-manager` attribute, described later in table. The default is `auto`.
| `transaction-manager`
| A reference to an external `PlatformTransactionManager` (typically an XA-based
transaction coordinator, such as Spring's `JtaTransactionManager`). If not specified,
native acknowledging is used (see the `acknowledge` attribute).
| `concurrency`
| The number of concurrent sessions or consumers to start for each listener. It can either be
a simple number indicating the maximum number (for example, `5`) or a range indicating the
lower as well as the upper limit (for example, `3-5`). Note that a specified minimum is just a
hint and might be ignored at runtime. The default is `1`. You should keep concurrency limited to `1` in
case of a topic listener or if queue ordering is important. Consider raising it for
general queues.
| `prefetch`
| The maximum number of messages to load into a single session. Note that raising this
number might lead to starvation of concurrent consumers.
| `receive-timeout`
| The timeout (in milliseconds) to use for receive calls. The default is `1000` (one
second). `-1` indicates no timeout.
| `back-off`
| Specifies the `BackOff` instance to use to compute the interval between recovery
attempts. If the `BackOffExecution` implementation returns `BackOffExecution#STOP`,
the listener container does not further try to recover. The `recovery-interval`
value is ignored when this property is set. The default is a `FixedBackOff` with
an interval of 5000 milliseconds (that is, five seconds).
| `recovery-interval`
| Specifies the interval between recovery attempts, in milliseconds. It offers a convenient
way to create a `FixedBackOff` with the specified interval. For more recovery
options, consider specifying a `BackOff` instance instead. The default is 5000 milliseconds
(that is, five seconds).
| `phase`
| The lifecycle phase within which this container should start and stop. The lower the
value, the earlier this container starts and the later it stops. The default is
`Integer.MAX_VALUE`, meaning that the container starts as late as possible and stops as
soon as possible.
|===
Configuring a JCA-based listener container with the `jms` schema support is very similar,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:jca-listener-container resource-adapter="myResourceAdapter"
destination-resolver="myDestinationResolver"
transaction-manager="myTransactionManager"
concurrency="10">
<jms:listener destination="queue.orders" ref="myMessageListener"/>
</jms:jca-listener-container>
----
The following table describes the available configuration options for the JCA variant:
[[jms-namespace-jca-listener-container-tbl]]
.Attributes of the JMS <jca-listener-container/> element
[cols="1,6"]
|===
| Attribute | Description
| `factory-id`
| Exposes the settings defined by this element as a `JmsListenerContainerFactory`
with the specified `id` so that they can be reused with other endpoints.
| `resource-adapter`
| A reference to the JCA `ResourceAdapter` bean (the default bean name is
`resourceAdapter`).
| `activation-spec-factory`
| A reference to the `JmsActivationSpecFactory`. The default is to autodetect the JMS
provider and its `ActivationSpec` class (see {api-spring-framework}/jms/listener/endpoint/DefaultJmsActivationSpecFactory.html[`DefaultJmsActivationSpecFactory`]).
| `destination-resolver`
| A reference to the `DestinationResolver` strategy for resolving JMS `Destinations`.
| `message-converter`
| A reference to the `MessageConverter` strategy for converting JMS Messages to listener
method arguments. The default is `SimpleMessageConverter`.
| `destination-type`
| The JMS destination type for this listener: `queue`, `topic`, `durableTopic`, `sharedTopic`.
or `sharedDurableTopic`. This potentially enables the `pubSubDomain`, `subscriptionDurable`,
and `subscriptionShared` properties of the container. The default is `queue` (which disables
those three properties).
| `response-destination-type`
| The JMS destination type for responses: `queue` or `topic`. The default is the value of the
`destination-type` attribute.
| `client-id`
| The JMS client ID for this listener container. It needs to be specified when using
durable subscriptions.
| `acknowledge`
| The native JMS acknowledge mode: `auto`, `client`, `dups-ok`, or `transacted`. A value
of `transacted` activates a locally transacted `Session`. As an alternative, you can specify
the `transaction-manager` attribute described later. The default is `auto`.
| `transaction-manager`
| A reference to a Spring `JtaTransactionManager` or a
`jakarta.transaction.TransactionManager` for kicking off an XA transaction for each
incoming message. If not specified, native acknowledging is used (see the
`acknowledge` attribute).
| `concurrency`
| The number of concurrent sessions or consumers to start for each listener. It can either be
a simple number indicating the maximum number (for example `5`) or a range indicating the
lower as well as the upper limit (for example, `3-5`). Note that a specified minimum is only a
hint and is typically ignored at runtime when you use a JCA listener container.
The default is 1.
| `prefetch`
| The maximum number of messages to load into a single session. Note that raising this
number might lead to starvation of concurrent consumers.
|===