299 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| [[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 Receipt: Message-Driven POJOs]
 | |
| for detailed coverage of Spring's MDP support.) 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 receipt 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 receipt. 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
 | |
| {spring-framework-api}/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
 | |
| receipt 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`.
 | |
| 
 | |
| 
 | |
| 
 |