Spring Boot JMS Example (2024)
In this tutorial, we'll demonstrate how to create a Spring Boot JMS application with ActiveMQ as broker including publisher (producer) and subscriber (consumer).
Q: What is JMS?
The Java Message Service (JMS) API is a Java API that allows applications to produce, publish, receive, and read messages in a secure, asynchronous, and loosely connected manner.
The core elements that make up a JMS application are connections, JMS Broker (middleware), Queues and Topics, and Producers and Consumers.
Q: What is JMS Broker?
JMS Broker is the mediator between the producer and the consumer. Producer send message to broker through Queue/Topic messaging. When the broker receives a message, it transmits it to consumers who have subscribed to the Topic or Queue.
A client application can send and receive messages using below two messaging patterns (or domains) using these fundamental objects.
Point-to-Point domain
A Queue may have multiple receiver, however when a client sent a message to a queue destination from which only one receiver can receive it. No other receiver with access to that destination will be able to receive that message. The receiver form a line and waits in line to receive new messages from the Queue. This is known as P2P (Point-to-Point) messaging.Publish/Subscribe domain
In this pattern, a Topic send message from producer to many consumers at once, this is commonly knows as Publish-and-Subscribe (Pub/Sub) broadcast messaging.In this example, the message broker will be Apache ActiveMQ, an open source, multi-protocol, Java-based message broker. It is compatible with both existing and future JMS infrastructure.
Refer to Install ActiveMQ as message broker, and follow the steps to start the broker.
Create Spring Boot application
Create Spring Boot application from Spring Initializr.
Project Structure
Add dependencies
Add Spring Boot, ActiveMQ and Jackson (JSON convertor) dependencies in 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.techgeeknext</groupId>
<artifactId>spring-boot-jms-topic-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-jms-topic-example</name>
<description>Spring Boot JMS Topic Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Take a look at our suggested posts:
Application Properties
Add ActiveMQ broker connection details. By default it provide point-to-pont communication. Make pub-sub to true for Topic.
spring.jms.pub-sub-domain=true
activemq.broker-url=tcp://localhost:61616
emp.jms.topic=EmpTopic
Data Model
Create Employee
class, contains id
, name
, role
.
package com.techgeeknext.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "name")
private String name;
@Column(name = "role")
private String role;
}
Apart from above basic configuration there other JMS messaging configuration needed for project as
below:
Spring Boot JMS Listener Container Configuration
package com.techgeeknext.config;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
@Configuration
@EnableJms
public class JMSConfig {
@Value("${activemq.broker-url}")
private String brokerUrl;
@Bean
public DefaultJmsListenerContainerFactory empJmsContFactory() {
DefaultJmsListenerContainerFactory containerFactory = new DefaultJmsListenerContainerFactory();
containerFactory.setPubSubDomain(true);
containerFactory.setConnectionFactory(connectionFactory());
containerFactory.setMessageConverter(jacksonJmsMsgConverter());
containerFactory.setSubscriptionDurable(true);
return containerFactory;
}
@Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory factory = new CachingConnectionFactory();
ActiveMQConnectionFactory activeMQConnFactory = new ActiveMQConnectionFactory();
activeMQConnFactory.setBrokerURL(brokerUrl);
factory.setTargetConnectionFactory(activeMQConnFactory);
factory.setClientId("client123");
return factory;
}
@Bean
public MessageConverter jacksonJmsMsgConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Spring Boot JMS Listener
package com.techgeeknext.listener;
import com.techgeeknext.model.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EmployeeListener {
@JmsListener(destination = "${emp.jms.topic}", containerFactory = "empJmsContFactory")
public void getEmployeeListener1(Employee emp) {
log.info("Employee listener1: " + emp);
}
@JmsListener(destination = "${emp.jms.topic}", containerFactory = "empJmsContFactory")
public void getEmployeeListener2(Employee emp) {
log.info("Employee Listener2: " + emp);
}
}
Spring Boot JMS Publisher
package com.techgeeknext.controller;
import com.techgeeknext.model.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.jms.Topic;
@Slf4j
@RestController
public class EmployeeController {
@Autowired
JmsTemplate jmsTemplate;
/**
* Create new employee
*
* @param employee
* @return ResponseEntity
*/
@PostMapping("/employee")
public ResponseEntity<Employee> newEmployee(@RequestBody Employee employee) {
try {
Topic empTopic = jmsTemplate.getConnectionFactory().createConnection()
.createSession().createTopic("EmpTopic");
int empId = (int)(Math.random() * 50 + 1);
Employee emp = Employee.builder().id(empId).name(employee.getName()).role(employee.getRole()).build();
log.info("Sending Employee Object: " + emp);
jmsTemplate.convertAndSend(empTopic, emp);
return new ResponseEntity<>(emp, HttpStatus.OK);
} catch (Exception exception) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Employee Model
package com.techgeeknext.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Employee {
private long id;
private String name;
private String role;
}
Test Spring Boot JMS Example
- Start the ActiveMQ
activemq start
, refer this to Install ActiveMQ and Start ActiveMQ Server. - Start the Spring Boot Application by running
spring-boot:run
or by running main class. Publish JMS Message
Open Postman, use POST method with end point http://localhost:8080/employee and provide Employee details to publish new employee record to jms receiver/subscriber.JMS Listener/Receiver output
We can see that both listeners have received the message from the publisher.. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.2.RELEASE) 19:44:21.746 INFO 38024 --- [main] c.t.SpringBootJmsTopicExampleApplication : Starting SpringBootJmsTopicExampleApplication (D:\spring-boot-jms-topic-example\target\classes started D:\spring-boot-jms-topic-example) 19:44:21.751 INFO 38024 --- [main] c.t.SpringBootJmsTopicExampleApplication : No active profile set, falling back to default profiles: default 19:44:24.207 INFO 38024 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 19:44:24.233 INFO 38024 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 19:44:24.234 INFO 38024 --- [main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37] 19:44:24.384 INFO 38024 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 19:44:24.384 INFO 38024 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2561 ms 19:44:24.836 INFO 38024 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 19:44:25.168 INFO 38024 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 19:44:25.570 INFO 38024 --- [main] c.t.SpringBootJmsTopicExampleApplication : Started SpringBootJmsTopicExampleApplication in 4.488 seconds (JVM running for 5.231) 19:44:39.946 INFO 38024 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 19:44:39.946 INFO 38024 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 19:44:39.958 INFO 38024 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 11 ms 19:44:40.169 INFO 38024 --- [nio-8080-exec-1] c.t.controller.EmployeeController : Sending Employee Object: Employee(id=18, name=TechGeekNext-User, role=Admin) 19:44:40.320 INFO 38024 --- [enerContainer-1] c.t.listener.EmployeeListener : Employee listener1: Employee(id=18, name=TechGeekNext-User, role=Admin) 19:44:40.320 INFO 38024 --- [enerContainer-1] c.t.listener.EmployeeListener : Employee Listener2: Employee(id=18, name=TechGeekNext-User, role=Admin)
ActiveMQ Dashboard
After running the spring boot jms applications, go to activemq dashboard and in topics we can see "Number of consumers" - 2, "Message enqueued" - 1 and "Message dequeued" - 2 in the dashboard.
Download Source Code
The full source code for this article can be found below.
- Download it here - Spring Boot JMS Example