Open In App

Spring – JMS Integration

Improve
Improve
Like Article
Like
Save
Share
Report

JMS is a standard Java API that allows a Java application to send messages to another application. It is highly scalable and allows us to loosely couple applications using asynchronous messaging. Using JMS we can read, send, and read messages.

Benefits of using JMS with Spring Integration

  • Load balancing: Multiple consumers in separate virtual machine processes pull messages from a shared destination at a rate determined by their capabilities.
  • Scalability: Adding enough consumer processes to avoid a backlog increases throughput and decreases response time.
  • Availability: With multiple consumer processes, the overall system can remain operational even if one or more individual processes fail. Likewise, consumer processes can be redeployed one at a time to support a rolling upgrade.

Here are some implementations of JMS is as follows: 

  • Amazon SQS
  • Apache ActiveMQ
  • JBoss Messaging
  • RabbitMQ

JMS Message 

 A JMS message can be divided into three parts that are as follows:  

  • Header: It contains the metadata about the message.
  • Properties: It can further be subdivided into three sections –
    • Application: The java application sending message.
    • Provider: It is used by the JMS provider and is implementation-specific.
    • Standard Properties: These are defined by the JMS API.
  • Payload: This field is the message itself.

Implementation

Here we will be building a sample greeting (Maven-based) application to demonstrate how to integrate and use JMS. For simplicity, we will be using an embedded server instead of creating another application.

The project Structure is as follows:  

Step 1: Create a spring project using spring initializer. Create the folders and files as depicted in the below image as follows: 

Project Structure

Step 2: Add the below dependencies to pom.xml file.

XML




<dependency>
           <groupId>org.apache.activemq</groupId>
           <artifactId>artemis-server</artifactId>
       </dependency>
       <dependency>
           <groupId>org.apache.activemq</groupId>
           <artifactId>artemis-jms-server</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-artemis</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
 
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-devtools</artifactId>
           <scope>runtime</scope>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>


Model Layer

Step 3: Create a simple POJO (Plain old java class) which will act as our model for sending and receiving messages. Here we have used Lombok to reduce boilerplate code. Annotations used are as follows:

  • @Data: This annotation generates getters, setters for all the fields, toString method, and equals & hashCode method.
  • @Builder: This annotation uses a builder pattern to automatically build complex builder APIs.
  • @NoArgsConstructor: This annotation generates a constructor with no arguments.
  • @AllArgsConstructor: This annotation generates a constructor with all arguments.

Example:

Java




// Java Program to Illustrate Model Layer
 
package com.anuanu.springjms.model;
 
// Importing required classes
import java.io.Serializable;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
 
// Annotation
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
 
// Class
// Implementing Serializable interface
public class GreetingMessage implements Serializable {
 
    // Class data members
    static final long serialVersionUID
        = -7462433555964441775L;
    private UUID id;
    private String message;
}


Required Configurations is as follows: 

A. Embedded Server Configuration

As discussed earlier, we will be creating an embedded server for the demonstration of JMS messaging. We will be using ActiveMQServers to create our embedded server. Here is the method signature to newActiveMQServer:

Modifier and Type Methods and Description
static ActiveMQServer newActiveMQServer (Configuration config)

Note: Configuration can be changed as per the requirement.

The embedded server also starts when our spring boot application starts. (as we have called start() method on the server).

Example:

Java




// Java Program to Illustrate Embedded Server Configuration
 
package com.anuanu.springjms;
 
// Importing required classes
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class SpringJmsApplication {
 
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Embedded Server Configuration
        ActiveMQServer activeMQServer
            = ActiveMQServers.newActiveMQServer(
                new ConfigurationImpl()
                    .setPersistenceEnabled(false)
                    .setJournalDirectory(
                        "target/data/journal")
                    .setSecurityEnabled(false)
                    .addAcceptorConfiguration("invm",
                                              "vm://0"));
 
        activeMQServer.start();
        SpringApplication.run(SpringJmsApplication.class,
                              args);
    }
}


B. Task Configuration

The taskConfig class will help us execute tasks asynchronously. The task here is sending messages at fixed intervals of time. For sending messages at fixed intervals we have enabled the scheduler using @EnableScheduling annotation. Annotations used are as follows: 

  • @EnableScheduling: This annotation enables the scheduler for our application.
  • @EnableAsync: This annotation enables spring to run @Async methods in a background thread pool.
  • @Configuration: This annotation indicates that the specified class has @Bean definition methods.
  • @Bean: It is a method-level annotation and is used to explicitly declare a bean.

Example:

Java




// Java Program to Illustrate Task Configuration
 
package com.anuanu.springjms.config;
 
// Importing required classes
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
 
// Annotations
@EnableScheduling
@EnableAsync
@Configuration
 
// Class
public class TaskConfig {
 
    // Task Configuration
    @Bean TaskExecutor taskExecutor()
    {
        return new SimpleAsyncTaskExecutor();
    }
 
    // taskExecutor bean is injected into spring context,
    // and spring will use it to execute tasks for us
}


C. Message Converter Configuration

The jmsConfig class provides a common stream for producers and consumers for producing and consuming messages respectively. It also provides a bean for converting Java objects and JMS messages. Annotations used are as follows:

  • @Configuration: This annotation indicates that the specified class has @Bean definition methods.
  • @Bean: It is a method-level annotation and is used to explicitly declare a bean.

Note: Message converter uses Jackson 2.x to convert messages to and from JSON.  It maps an object to a BytesMessage, or to a TextMessage if the targetType is set to MessageType.TEXT.  It converts from a TextMessage or BytesMessage to an object.

Example:

Java




// Java Program to Illustrate Task Configuration
 
package com.anuanu.springjms.config;
 
// Importing required classes
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
 
// Annotations
@Configuration
 
// Class
public class JmsConfig {
 
    // Class data member
    public static final String QUEUE = "greet";
 
    // Annotation
    @Bean
 
    // Class
    public MessageConverter messageConverter()
    {
 
        MappingJackson2MessageConverter converter
            = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
 
        return converter;
    }
 
    // Enabling spring to take jms messages and flip it
    // to a json message. then it can read
    // that jms message as a jms text message and
    // convert it back to java object
}


Sending JMS Messages

The MessageSender class (described below) is mainly used to create and produce/send messages to the common stream from where the consumer can consume messages. We have used JmsTemplate which is a  helper class that makes it easier for us to receive and send messages through JMS. We have also annotated the class with @Scheduled with a value of 2000 ms which tells the scheduler to send the messages at an interval of every 2 seconds. Annotations used are as follows: 

  • @RequiredArgsConstructor : This annotation generates a constructor with required arguments i,e.  arguments with final fields or fields with other constraints.
  • @Component: This annotation marks our class as the component which allows spring to detect any custom-defined beans.
  • @Scheduled:  This annotation marks a method to be scheduled. It must have any one of these cron(), fixedDelay() or fixedRate() attributes.

Example:

Java




// Java Program to Illustrate Sending JMS Messages
 
package com.anuanu.springjms.sender;
 
// Importing required classes
import com.anuanu.springjms.config.JmsConfig;
import com.anuanu.springjms.model.GreetingMessage;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
// Annotations
@RequiredArgsConstructor
@Component
 
// Class
public class MessageSender {
 
    // Class data member
    private final JmsTemplate jmsTemplate;
    private static Integer ID = 1;
 
    // Annotation
    @Scheduled(fixedRate = 2000)
 
    // Method
    public void sendMessage()
    {
        // Display command
        System.out.println("Greetings user");
 
        GreetingMessage message
            = GreetingMessage.builder()
                  .id(UUID.randomUUID())
                  .message("Greetings user " + ID++)
                  .build();
 
        jmsTemplate.convertAndSend(JmsConfig.QUEUE,
                                   message);
 
        // Display command
        System.out.println("Message sent!!!");
    }
}


Sending messages

Receiving JMS Messages

The MessageListener class (described below) acts as a consumer i.e. it consumes/receives messages that reside in the common stream and is not already consumed. The location of the common stream “JmsConfig.QUEUE” is passed to the destination method in @JmsListener. Annotations used are as follows: 

  • @JmsListener: This annotation marks a method to be the target of a JMS message listener on the specified destination().
  • @Component: This annotation marks our class as component which allows spring to detect any custom-defined beans.
  • @Payload: This annotation marks that the payload of the message is to be extracted is annotated parameter.
  • @Headers: This annotation extracts all the headers inside a Map<String, Object>.  It is used here with MessageHeaders class which implements Map<String, Object> and is used for message headers.

Example:

Java




// Java Program to Illustrate Receiving JMS Messages
 
package com.anuanu.springjms.listener;
 
// Importing required classes
import com.anuanu.springjms.config.JmsConfig;
import com.anuanu.springjms.model.GreetingMessage;
import javax.jms.Message;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
 
// Annotation
@Component
 
// Class
public class MessageListener {
 
    @JmsListener(destination = JmsConfig.QUEUE)
 
    public void
    listen(@Payload GreetingMessage greetingMessage,
           @Headers MessageHeaders messageHeaders,
           Message message)
    {
 
        // Display command
        System.out.println("Greeting Received!!!");
 
        System.out.println(greetingMessage);
    }
}


Receiving messages

Output: 

The message is sent and received every 2 seconds. A unique username and id is passed for every new message.

Output - 1

Inspecting the messageListener class:

messageListener class

The below image shows us the jms message that is received at the messageListener class. There is a variety of properties that can be customized in the header field. Notice the one key-value pair we added in the header.

'_type' -> 'com.anuanu.springjms.model.GreetingMessage'



Last Updated : 09 Jan, 2024
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads