You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
17 KiB
296 lines
17 KiB
[[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 xref:integration/jms/receiving.adoc#jms-receiving-async[Asynchronous reception: Message-Driven POJOs] for detailed |
|
coverage of Spring's MDP support.) Since Spring Framework 4.1, endpoint methods can be |
|
annotated with `@JmsListener` -- see xref:integration/jms/annotated.adoc[Annotation-driven Listener Endpoints] 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. |
|
|
|
* xref:integration/jms/using.adoc#jms-mdp-simple[`SimpleMessageListenerContainer`] |
|
* xref:integration/jms/using.adoc#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 (xref:integration/jms/using.adoc#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 |
|
xref:data-access/transaction.adoc[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`. |
|
|
|
|
|
|
|
|