In this post, I will show you how to receive messages using a Spring JMS Listener.
First, I’ll explain the different options available.
Then, I’ll build a detailed example.
Sound good? Let’s dive right in…
If you want to learn more about Spring JMS – head on over to the Spring JMS tutorials page.
1. What is a Spring JMS Listener?
In order to asynchronously receive JMS messages, Spring offers a solution to create message-driven POJOs (MDP).
A message listener container is used to receive messages from a JMS broker. The container is a wrapper of sorts that calls a simple POJO listener class when a message arrives.
The one restriction on an MDP is that it must implement the MessageListener interface.
We adapt it so that an order message is sent to an order queue. A JMS listener will pick up the message and send a status message to two different status queues. On each queue, a different message listener container will read the status.
2. General Project Overview
We will use the following tools/frameworks:
Spring JMS 5.1
Spring Boot 2.1
ActiveMQ 5.15
Maven 3.6
Our project has the following directory structure:
3. Configure a Spring JMS Listener Container
A message listener container handles all the complexity of receiving JMS messages.
It is responsible for all threading of message reception and dispatches into the listener for processing. The container is the intermediary between an MDP and a messaging provider. It pulls the messages off a queue/topic and feeds them to your message listener.
There are two standard JMS message listener containers packaged with Spring:
The DefaultMessageListenerContaineris considered a recommendable approach in many environments.
It is the only listener container that does not impose the thread management onto the JMS provider (as it does not use/block JMS provider threads). The DMLC is also able to gracefully recover from JMS provider failure, such as connection loss. And it is the only variant that supports external transaction managers, in particular for XA transactions.
Alternatively, consider using SimpleMessageListenerContainer, but only for native JMS usage without XA, and only if your JMS provider gracefully handles thread management and connection recovery.
Let’s create both types of listener containers in the ReceiverConfig class.
Create a DefaultJmsListenerContainerFactory and a SimpleJmsListenerContainerFactory. Note that they both require a ConnectionFactory.
We use the setConcurrency() method on the DMLC to set the “lower-upper” limits. The listener container created will always hold on to the minimum number of consumers and will slowly scale up to the maximum number of consumers in case of an increasing load.
We then use the factories to create a DefaultMessageListenerContainer and a SimpleMessageListenerContainer.
The SimpleJmsListenerEndpoint defines Destination and the MessageListener to invoke to process an incoming message. For testing purposes, we reuse the StatusMessageListener for both containers but give them a different identifier.
4. Create a Spring JMS Listener
To create a JMS listener you need to implement the MessageListener interface. It has an onMessage() method that is triggered for each message that is received.
The below StatusMessageListener tries to cast the received message to a TextMessage. If successful it logs the content and lowers a CountDownLatch that we will use for testing purposes.
As we set a StatusMessageListener instance on both the DMLC and SMLC containers we add an id. This allows us to log in which container a message is received.
There is even an easier way to create a Spring JMS listener.
Simply decorate a Bean method with the @JmsListener annotation. This causes a listener container to be created on the specified destination using a ContainerFactory.
If not set, a default container factory is assumed to be available with a bean name of jmsListenerContainerFactory unless an explicit default has been provided through configuration.
Processing of @JmsListener annotations is performed by registering a JmsListenerAnnotationBeanPostProcessor. This can be done manually or, more conveniently, through the @EnableJms annotation.
So for this example we create an OrderReceiver class that contains a receiveOrder() method annotated with @JmsListener. The method receives a simple order and logs it. We then use an auto-wired JmsTemplate to send a status message to the status1 and status2 destinations.
Note that @EnableJms was specified on the earlier defined ReceiverConfig class.
5. Testing the JMS listener
We change the existing test case to check if our different message listeners work.
Send an order message to the order destination. Then check if the CountDownLatch was lowered in both the DMLC and SMLC message listeners.
Open a command prompt in the project root directory and launch the test case.
In the output logs, we can see that the order message is received by the annotated listener. The status messages arrive in the DMLC and SMLC as shown below.
If you would like to run the above code sample you can get the full source code here.
In this guide, we took a look at the different JMS listeners that the Spring framework offers.