312 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| [[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 xref:integration/jms/annotated.adoc#jms-annotated-support[Enable Listener Endpoint Annotations] 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
 | |
| xref:integration/jms/receiving.adoc#jms-receiving-async-message-listener-adapter[Using `MessageListenerAdapter`]) 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.
 | |
| 
 | |
| 
 | |
| 
 |