Messaging is a way of communicating between entities. A sender sends a message to a messaging system, and there can be one or multiple consumers listening to the queue to receive the messages. Messaging makes the system loosely coupled as the sender and receiver are not communicating with each other directly.

In this article, we will learn how to use Apache ActiveMQ with Spring Boot for implementing the JMS(Java Messaging System).

Technologies used in this article are:

  • Spring Boot version : 2.3.0.RELEASE
  • Java version 1.8
  • Apache ActiveMQ 5.15.13

Setting up the ActiveMQ

We can download the latest version of the ActiveMQ installer from here.

On a Windows machine, we can start the server by running the .bat file from the extracted zip file. Navigate to the apache-activemq-x.xx.xx\bin\winxx directory and start the server by running the .bat file.

After starting the ActiveMQ server locally, we can access Web console at http://localhost:8161.

The default username is admin, and the password is admin.

Once we login successfully, we get the below screen.

ActiveMQ on windows

Click on the Manage ActiveMQ broker link to open the ActiveMQ console window, as shown below.

Here, we can view the topics, queues, etc. we can notice that currently, there are no messages in the queue.

ActiveMQ empty queue

Creating the producer application

Let us create a Spring Boot application that produces the message to the ActiveMQ message queue.

Create the application with required dependencies

Create a Spring Boot application with spring-boot-starter-activemq, spring-boot-starter-web, and lombok dependencies.

The Spring Boot ActiveMQ starter dependency provides us the required auto-configuration along with an embedded in memory ActiveMQ server.

The dependencies we added to the pom.xml file are given below.

<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.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

Create a DTO class

Create a Student.java DTO class. We will use the student object as message content and send that to the ActiveMQ message queue.

package com.asbnotebook.dto;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Student {
	
	private Integer id;
	private String name;
}

Add required configuration

Create a spring configuration class called StudentConfiguration.java

package com.asbnotebook.config;

import javax.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

@Configuration
public class StudentConfig {

	@Bean // Serialize message content to json using TextMessage
	public MessageConverter jacksonJmsMessageConverter() {
		MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
		converter.setTargetType(MessageType.TEXT);
		converter.setTypeIdPropertyName("_asb_");
		return converter;
	}
}
  • @Configuration: This annotation marks the class as a configuration class and adds it to the Spring application context.
  • MessageConverter: We are using MappingJackson2MessageConverter, and the target type is of Text format. The DTO object sent to the message queue in the JSON text format.
  • TypeId: An id that should match both on the consumer and producer side. We can set any value as TypeID.

Producing the message

Create a StudentProducer.java class

Spring framework provides a JmsTemplate that we can use to publish the message to the ActiveMQ message queue.

The convertAndSend() method serializes the object and sends it to the message queue.

package com.asbnotebook.jms;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

import com.asbnotebook.dto.Student;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class StudentProducer {

	@Autowired
	private JmsTemplate jmsTemplate;

	public void sendTo(String destination, Student student) {
		jmsTemplate.convertAndSend(destination, student);
		log.info("Producer> Message Sent");
	}
}

Create a RESTful POST endpoint to send the Student object to the message queue.

This endpoint allows us to post the Student object to the message queue.

@SpringBootApplication
public class SpringBootActivemqProducerExampleApplication {

	@Autowired
	StudentProducer studentProducer;

	@Value("${activemq.destination}")
	private String destination;

	public static void main(String[] args) {
		SpringApplication.run(SpringBootActivemqProducerExampleApplication.class, args);
	}

	@RestController
	public class StudentController {

		@PostMapping("/")
		public String sendMessage(@RequestBody Student student) {
			studentProducer.sendTo(destination, student);
			return "success";
		}
	}
}

Producer ActiveMQ configuration properties

Add the below configuration properties to the Spring Boot applications application.properties file.

spring.activemq.in-memory=false
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin

activemq.destination=student
  • spring.activemq.in-memory: Value is set to false, as we are using external ActiveMQ server.
  • spring.activemq.broker-url: ActiveMQ broker URL.
  • Set the Username and password if any is using the spring.activemq.user and spring.activemq.password configuration properties.
  • activemq.destination: A queue/topic name to which the data is published.

Creating the consumer application

Create a consumer application with the required dependencies.

Use the same dependencies that we have used during the creation of the JMS producer application.

Copy the Student.java DTO class from the JMS publisher application.

Create a configuration class

Create a configuration class with the name StudentConfig.java

package com.asbnotebook.config;

import javax.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

@Configuration
public class StudentConfig {

	@Bean
	public JmsListenerContainerFactory<?> jmsFactory(ConnectionFactory connectionFactory,
			DefaultJmsListenerContainerFactoryConfigurer configurer) {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setMessageConverter(jacksonJmsMessageConverter());
		configurer.configure(factory, connectionFactory);
		return factory;
	}

	@Bean // Serialize message content to json using TextMessage
	public MessageConverter jacksonJmsMessageConverter() {
		MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
		converter.setTargetType(MessageType.TEXT);
		converter.setTypeIdPropertyName("_asb_");
		return converter;
	}
}
  • We have created a JmsContainerFactory bean and set the message converter to our custom jacksonJmsConverter(). Even though this is not required, we can use this factory bean to customize the Jms listener(Like setting the error handling, etc.)
  • The Message converter is a similar bean that we have created for producer application.

Creating the consumer

package com.asbnotebook.jms;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import com.asbnotebook.dto.Student;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class StudentConsumer {

	@JmsListener(destination = "${activemq.destination}", containerFactory = "jmsFactory")
	public void processToDo(Student student) {
		log.info("Consumer> " + student);
	}
}
  • @JmsListener: The method annotated with this annotation is a JMS listener method. The listener listens to the specified message queue/topic.
  • destination: Name of the JMS topic/queue.
  • containerFactory: This property specifies the custom container factory we have created earlier in the configuration class.

Consumer ActiveMQ configuration properties

These are the configuration properties that are similar to that of producer application configuration.

Update the server port number so that we can simultaneously start both producer and consumer applications.

spring.activemq.in-memory=false
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
activemq.destination=student

server.port=8001

Testing the application

Both producer and consumer applications are ready now. With the ActiveMQ server running, let us start testing the application.

Producer application

Start the producer Spring boot application. This application will run on default server port 8080.

As, we have a POST method, that allows us to pass the Student object as shown below.

Spring boot activeMq

The ActiveMQ server console displays the published messages in the queue.

Spring Boot ActiveMQ example

We can observe the message that is enqueued on the ActiveMQ server.

ActiveMQ producer example

Consumer application

Start the consumer Spring Boot application.

Once the application starts, it will consume the message from the ActiveMQ message queue.

ActiveMQ consumer example spring boot

ActiveMQ admin console displays the consumed messages under the dequeue column.

ActiveMQ consumer example spring boot

Conclusion

In this article, we learned how to use Apache ActiveMQ to implement the JMS with Spring Boot application.

We created a producer and a consumer JMS applications using Spring Boot.
We produced the messages from the producer application to the ActiveMQ message queue.

The consumer Spring Boot application consumed the published message from the ActiveMQ JMS queue.

Example code is available on GitHub.

You may also be interested in