1475 lines
65 KiB
Plaintext
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.
|
|
|===
|