ActiveMQ and Spring Boot Message queue Tutorial (w/ Examples)

Overview

In the previous post, I showed you how to set up ActiveMQ with Docker. In this post, I will create a producer and consumer and see how they communicate with each other via the message queue. I’m also going to show you the status of messages in the ActiveMQ web admin.

Let’s get started.

Spring Boot dependencies

I’m going to create two separate services: producer and consumer. Each is a Sping Boot application. They both have the same dependencies:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

In order to connect to a running ActiveMQ instance, you need to define the connection in application.yml (or applications.properties):

server:
  port: 0
spring:
  activemq:
    broker-url: tcp://localhost:61616
    user: admin
    password: admin

As you can see, besides setting connection properties for ActiveMQ, I also set server.port to 0. This allows Spring Boot to pick a random port (for convenience purposes).

Create a producer to publish messages to ActiveMQ

Creating a producer to publish messages to ActiveMQ is quite simple.

Sending primitive data

Let’s create a GET endpoint so that every time I request that endpoint, a message is sent to the message queue:

@RestController
public class MessageController {

    @Autowired
    JmsTemplate template;
    @GetMapping("/send")
    public String sendMessage() {
        var message = "Hello random string " + UUID.randomUUID();
        template.convertAndSend("test_queue", message);
        return message;
    }

}

In the code snippet above, I created an endpoint at /send. Every time there is a GET request to that endpoint, the application uses an instance of JmsTemplate to send a message to a queue named test_queue. Notice that it is not necessary to define the queue name anywhere before executing this code.

Let’s start the app and try sending a message:

Send a message to ActiveMQ in Spring Boot
Send a message to ActiveMQ in Spring Boot

When checking the queue named test_queue on ActiveMQ web admin page, you can see the message:

Message sent on ActiveMQ
Message sent on ActiveMQ

Sending POJO

Before sending POJO, you need to make sure that the POJO is located in a package/library/module that is accessible by both producer and consumer.

In this example, I create a new module called data to hold the POJO, and both the consumer and producer need to add that module as their dependency.

You can send POJO to ActiveMQ similar to the way you send a String above. Let’s create an object called Letter as below:

public class Letter implements Serializable {
    private String content;
    private String sender;
    private String receiver;

    public Letter(String content, String sender, String receiver) {
        this.content = content;
        this.sender = sender;
        this.receiver = receiver;
    }

    public String getContent() {
        return content;
    }


    public String getSender() {
        return sender;
    }

    public String getReceiver() {
        return receiver;
    }

}

Make sure you implement Serializable for the Letter class. Otherwise, you will get an exception when sending a message similar to this:

org.springframework.jms.support.converter.MessageConversionException: Cannot convert object of type [com.datmt.activemq.data.Letter] to JMS message. Supported message payloads are: String, byte array, Map<String,?>, Serializable object.
	at org.springframework.jms.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:79) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.lambda$convertAndSend$5(JmsTemplate.java:661) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:604) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.lambda$send$3(JmsTemplate.java:586) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:504) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:584) ~[spring-jms-5.3.23.jar:5.3.23]
	at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:661) ~[spring-jms-5.3.23.jar:5.3.23]

Let’s create another endpoint to send letters:

    @GetMapping("/send-letter")
    public String sendLetter() {
        var letter = new Letter("Hello " + UUID.randomUUID(), "Jack", "Jill");
        template.convertAndSend("test_letter", letter);
        return "Letter sent!";
    }

Let’s visit the endpoint. Sure enough, I am able to send the letter.

Send POJO to ActiveMQ successfully
Send POJO to ActiveMQ successfully

You can view the message on ActiveMQ web admin too:

test_letter queue created automatically
test_letter queue created automatically

The message is available in the queue:

POJO message in ActiveMQ queue
POJO message in ActiveMQ queue

You may notice that the Message details showed an error. This is OK since ActiveMQ doesn’t know to deserialize the Letter object.

Create a consumer to consume messages

Consume primitive data

On the consumer side, it’s quite simple to consume primitive messages. To create a consumer of a queue, you can do as follow:

@Component
public class MessageConsumer {

    @JmsListener(destination = "test_queue")
    public void getStringMessage(String testMessage) {
        System.out.printf("Got message %s%n", testMessage);
    }

}

Simply create a method inside a Spring bean and annotate it with @JmsListener specifying the queue name. When there are incoming messages, they will be consumed here (if there is only one consumer).

Consume POJO

Consuming POJO is a bit thicker than consuming String. Because of security concern, you need to declare which package of the pojo you trust (You can find more details here). With Spring, it’s quite simple to declare such packages in application.yml

spring:
  activemq:
    broker-url: tcp://localhost:61616
    user: admin
    password: admin
    packages:
      trusted: com.datmt.activemq.data

Here, I specified the trusted package is com.datmt.activemq.data because the Letter object is from this package. If a producer produces a Letter message but not from the trusted packages, it will not be consumed.

After such configurations, consuming POJO is not much different from that of String:

    @JmsListener(destination = "test_letter")
    public void getTest(Letter letter) {
        System.out.printf("From %s to %s with content %s", letter.getSender(), letter.getReceiver(), letter.getContent());
    }

When starting the consumer, you can see the messages in the application’s console:

Consuming messages (String and POJO) in ActiveMQ
Consuming messages (String and POJO) in ActiveMQ

Conclusion

In this tutorial, I’ve shown you how to send and consume messages using Spring Boot and ActiveMQ. Sending String and POJO are quite similar, there isn’t much difference on the producer side. However, on the consumer side, you need to specify the trusted packages to consume POJO.

The code for this tutorial is available here on Github.

1 thought on “ActiveMQ and Spring Boot Message queue Tutorial (w/ Examples)”

Leave a Comment