Spring Boot JMS ActiveMQ Messaging Example

In this article, we will create a simple Spring Boot JMS application that uses Spring’s JmsTemplate to post a single message and subscribes to it with a @JmsListener annotated method of a managed bean.

Spring Boot ActiveMQ Support

Spring boot automatically configures the ConnectionFactory class if it detects ActiveMQ on the classpath. In this case, it also makes use of an embedded broker if does not find any ActiveMQ custom configurations in application.properties. In this example, we will be using the default ActiveMQ configuration.
Below maven dependency provides all the required dependencies to integrate JMS and ActiveMQ with spring boot.
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
The below dependency provides embedded ActiveMQ in the spring boot application:
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
        </dependency>

Short Note on ActiveMQ

There are quite a few JMS implementations out there. Most of them are provided by Middle Oriented Middleware providers, for example, WebSphere MQ, Oracle EMS, JBoss AP, SwiftMQ, TIBCO EMS, SonicMQ, ActiveMQ, and WebLogic JMS. Some are open-source, and some are not.

Apache ActiveMQ, which was chosen as a JMS provider for this example, has the following characteristics:
  •  the most popular and powerful open-source messaging and Integration Patterns server
  •  accepts non-Java clients 
  •  can be used standalone in production environments
  •  supports pluggable transport protocols such as in-VM, TCP, SSL, NIO, UDP, multicast, JGroups, and JXTA transports
  • can be used as an in-memory JMS provider, using an embedded broker, to avoid the overhead of running separate processes when doing unit testing JMS5
  •  the ActiveMQ executable starts with a default configuration
  •  can also be used embedded in an application
  •  can be configured using ActiveMQ or Spring configuration (XML or Java Configuration)
  •  provides advanced messaging features such as message groups, virtual and composite destinations, and wildcards
  •  provides support for Enterprise Integration Patterns when used with Spring Integration or Apache Camel

What we'll Build

We will build a Spring Boot JMS application that sends User instances wrapped up in JMS Messages to the userQueue.A message listener is configured to process the message and send a confirmation message on the confirmationQueue. Another listener is defined that waiting for the confirmation and printing its contents. To simplify the application even more, there is no need for a producer class and a consumer class. There is only one process that publishes and consumes messages from the two queues.
The below diagram shows the Spring Boot JMS application abstract schema:

Create a Spring Boot Application

There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr at http://start.spring.io/, which is an online Spring Boot application generator.

Maven Dependencies - pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.javaguides.springboot</groupId>
    <artifactId>springboot-jms</artifactId>
    <version>0.1.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.4</version>
    </parent>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
spring-boot-starter-activemq: It provides all the required dependencies to integrate JMS and ActiveMQ with spring boot.
activemq-broker : This provides embedded ActiveMQ in the spring boot application. But since we will be configuring our ActiveMQ outside the application we have commented on it for time being.
spring-boot-maven-plugin: It will collect all the jar files present in the classpath and create a single executable jar.

Packaging Structure

Following is the packing structure of our Spring boot JMS application -


UserReceiver and ConfirmationReceiver

UserReceiver and ConfirmationReceiver have the responsibility of receiving the user and confirmation messages. The classes have a more practical implementation, making use of the @JmsListener annotation. 

UserReceiver.java

package net.javaguides.springboot.jms;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.Message;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by Ramesh Fadatare
 */
@Component
public class UserReceiver {

    private Logger logger = LoggerFactory.getLogger(UserReceiver.class);
    private static AtomicInteger id = new AtomicInteger();

    @Autowired
    ConfirmationSender confirmationSender;


    @JmsListener(destination = "userQueue", containerFactory = "connectionFactory")
    public void receiveMessage(User receivedUser, Message message) {
        logger.info(" >> Original received message: " + message);
        logger.info(" >> Received user: " + receivedUser);
        confirmationSender.sendMessage(new Confirmation(id.incrementAndGet(), "User " +
            receivedUser.getEmail() + " received."));

    }
}

ConfirmationReceiver.java

The ConfirmationSender class is just a bean with a JmsTemplate bean injected in it that is being used to send a confirmation object to the confirmationQueue.
package net.javaguides.springboot.jms;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

/**
 * Created by Ramesh Fadatare
 */
@Component
public class ConfirmationReceiver {

    private Logger logger = LoggerFactory.getLogger(ConfirmationReceiver.class);

    @JmsListener(destination = "confirmationQueue", containerFactory = "connectionFactory")
    public void receiveConfirmation(Confirmation confirmation) {
        logger.info(" >>  Received confirmation: " + confirmation);

    }
}

Creating POJO - User and Confirmation

Let's create a simple User.java POJO class that sends over a message queue:

User.java

package net.javaguides.springboot.jms;

/**
 * Created by Ramesh Fadatare
 */
public class User {

    private String email;
    private Double rating;
    private boolean active;

    public User() {}

    public User(String email, Double rating, boolean active) {
        this.email = email;
        this.rating = rating;
        this.active = active;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Double getRating() {
        return rating;
    }

    public void setRating(Double rating) {
        this.rating = rating;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    @Override
    public String toString() {
        return "User{" +
            "email='" + email + '\'' +
            ", rating=" + rating +
            ", active=" + active +
            '}';
    }
}

Confirmation.java

Let's create Confirmation.java class to confirm whether is message is reviewed on not:
package net.javaguides.springboot.jms;

/**
 * Created by Ramesh Fadatare
 */
public class Confirmation {

    private int ackNumber;

    private String verificationComment;

    public Confirmation() {}

    public Confirmation(int ackNumber, String verificationComment) {
        this.ackNumber = ackNumber;
        this.verificationComment = verificationComment;
    }

    public int getAckNumber() {
        return ackNumber;
    }

    public void setAckNumber(int ackNumber) {
        this.ackNumber = ackNumber;
    }

    public String getVerificationComment() {
        return verificationComment;
    }

    public void setVerificationComment(String verificationComment) {
        this.verificationComment = verificationComment;
    }

    @Override
    public String toString() {
        return "Confirmation{" +
            "ackNumber='" + ackNumber + '\'' +
            ", verificationComment=" + verificationComment +
            '}';
    }
}

Send and Receive JMS messages with Spring - Application.java

The Application class is the configuration and starter of the application. It is annotated with the all-powerful @SpringBootApplication, which will make sure to scan for beans in the current package and will inject all infrastructure beans based on the classpath settings. 
The other annotation on this class is @EnableJms, and as you can probably speculate, this annotation enables the bean postprocessor that will process the @JmsListener annotations and will create the message listener container "under the hood".
There is also a converter bean defined as type MappingJackson2MessageConverter implementing the JMS MessageConverter interface that transforms the sent objects into text JSON format.
package net.javaguides.springboot;

import jakarta.jms.ConnectionFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

import net.javaguides.springboot.jms.User;

/**
 * Created by Ramesh Fadatare
 */
@SpringBootApplication
@EnableJms
public class Application {
    private static Logger logger = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

        JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);

        //Send an user
        System.out.println("Sending an user message.");
        jmsTemplate.convertAndSend("userQueue",
            new User("[email protected]", 5 d, true));

        logger.info("Waiting for user and confirmation ...");
        System.in.read();
        context.close();
    }

    @Bean // Serialize message content to json using TextMessage
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }

    @Bean
    public JmsListenerContainerFactory << ? > connectionFactory(ConnectionFactory connectionFactory,
        DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // This provides all boot's default to this factory, including the message converter
        configurer.configure(factory, connectionFactory);
        // You could still override some of Boot's default if necessary.
        return factory;
    }
}
The JmsTemplate makes it very easy to send messages to destinations, and in this example is automatically created by Spring Boot. The connectionFactory is also created by Spring Boot and is backed up by ActiveMQ running in embedded mode.

Demo

Execute the above Application class’s main() method. The main method, aside from starting up the application, instantiates a JmsTemplate object that is used to send a user object.
Clearly, the message was successfully sent and received. That’s all for this quick example of Spring JMSTemplate with embedded ActiveMQ.

Comments