Spring Jms ActiveMQ – How to create a SpringBoot ActiveMQ Response Management application by @SendTo annotation

In the past posts, we had learned how to consume/produce ActiveMq JMS messages. Today, JavaSampleApproach will show you way to create a SpringBoot ActiveMQ Response Management application by @SendTo annotation.

Related posts:
Spring ActiveMQ JMS Consumer and JMS Producer
Send Java object messages to ActiveMQ server (specially with Bi-Directional relationship Java objects)
Spring JMS ActiveMq Topic (Publisher-Subcribers pattern)


I. Technologies

– Java 8
– Maven 3.6.1
– Spring Tool Suite: Version 3.8.4.RELEASE
– Spring Boot: 1.5.4.RELEASE
– Apache ActiveMQ 5.14.0

II. ActiveMQ Response Management

With the Spring JMS improvements (from 4.1), we can used @SendTo annotation to define the default next destination with @JmsListener:

SpringBoot ActiveMQ Response Management application - sendto aannotation - architecture


@JmsListener(destination = "${jsa.activemq.queue.listen}", containerFactory="jsaFactory")
@SendTo("${jsa.activemq.queue.sendto}")
public Product processOrder(Product product) {
    // process a newProduct
    return newProduct;
}

For additional headers, you could return a Message object instead:


@JmsListener(destination = "${jsa.activemq.queue.listen}", containerFactory="jsaFactory")
@SendTo("${jsa.activemq.queue.sendto}")
public Message  receive(Product product, @Header("company") String companyName){
	
	...
	
	Message  mesage = MessageBuilder
            .withPayload(product)
            .setHeader("type", product.getType())
            .build();
	
	return mesage;	
}
When having several @JmsListener methods, we can also place the @SendTo annotation at the class level to share a default reply destination.

III. Practice

In the tutorial, we create 2 SpringBoot projects {SpringActiveMqProducerConsumer, SpringActiveMqSendTo}:

SpringBoot ActiveMQ Response Management application - sendto aannotation - project structure

Step to do:
– Create SpringBoot projects
– Create model messages
– Configure ActiveMq ConnectionFactory
– Create Jms Producer/Listeners
– Run and check results

1. Create SpringBoot projects

Using SpringToolSuite, create 2 projects SpringBoot projects {SpringActiveMqProducerConsumer, SpringActiveMqSendTo}, then add dependencies:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
</dependency>

2. Create model messages

Create 2 model classes {Company, Product}:

– Company:


package com.javasampleapproach.activemq.model;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
 
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id", scope = Company.class)
public class Company {
    private String name;
 
    private List products;
	
    public Company(){
    }
    
    public Company(String name){
    	this.name = name;
    }
    
    public Company(String name, List products){
    	this.name = name;
    	this.products = products;
    }
    
    // name
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    // products
    public void setProducts(List products){
    	this.products = products;
    }
    
    public List getProducts(){
    	return this.products;
    }
}

– Product:


package com.javasampleapproach.activemq.model;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
 
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id", scope = Product.class)
public class Product {
    private String name;
    private String type;
    
    private Company company;
	
    public Product(){
    }
    
    public Product(String name, String type){
    	this.name = name;
    	this.type = type;
    }
    
    public Product(String name, Company company){
    	this.name = name;
    	this.company = company;
    }
    
    // name
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getType(){
    	return this.type;
    }
    
    public void setType(String type){
    	this.type = type;
    }
    
    // products
    public void setCompany(Company company){
    	this.company = company;
    }
    
    public Company getCompany(){
    	return this.company;
    }
    
    @Override
    public String toString() {
    	String info = String.format("[name = %s, type = %s]", this.name, this.type);
    	return info;
    }
}

3. Configure ActiveMq ConnectionFactory

– Create ActiveMqConnectionFactoryConfig file:


package com.javasampleapproach.activemq.config;

import javax.jms.ConnectionFactory;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
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.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
 
@Configuration
public class ActiveMqConnectionFactoryConfig {
 
	@Value("${jsa.activemq.broker.url}")
	String brokerUrl;
	
	@Value("${jsa.activemq.borker.username}")
	String userName;
	
	@Value("${jsa.activemq.borker.password}")
	String password;
 
	/*
	 * Initial ConnectionFactory
	 */
    @Bean
    public ConnectionFactory connectionFactory(){
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
        connectionFactory.setBrokerURL(brokerUrl);
        connectionFactory.setUserName(userName);
        connectionFactory.setPassword(password);
        return connectionFactory;
    }
    
	@Bean // Serialize message content to json using TextMessage
	public MessageConverter jacksonJmsMessageConverter() {
	    MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
	    converter.setTargetType(MessageType.TEXT);
	    converter.setTypeIdPropertyName("_type");
	    return converter;
	}
    
    /*
     * Used for Receiving Message
     */
    @Bean
    public JmsListenerContainerFactory jsaFactory(ConnectionFactory connectionFactory,
                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setMessageConverter(jacksonJmsMessageConverter());
        configurer.configure(factory, connectionFactory);
        return factory;
    }
 
    /*
     * Used for Sending Messages.
     */
    @Bean
    public JmsTemplate jmsTemplate(){
        JmsTemplate template = new JmsTemplate();
        template.setMessageConverter(jacksonJmsMessageConverter());
        template.setConnectionFactory(connectionFactory());
        return template;
    }
}

4. Create Jms Producer/Listeners

4.1 For SpringActiveMqProducerConsumer project

– Create a Producer:


import javax.jms.JMSException;
import javax.jms.Message;

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

import com.javasampleapproach.activemq.model.Product;
 
@Component
public class JmsProducer {
	@Autowired
	JmsTemplate jmsTemplate;
	
	@Value("${jsa.activemq.queue.producer}")
	String queue;
	
	public void send(Product product, String companyName){
		jmsTemplate.convertAndSend(queue, product, new MessagePostProcessor() {
	        public Message postProcessMessage(Message message) throws JMSException {
	            message.setStringProperty("company", companyName);
	            return message;
	        }
	    });
	}
}

– Create a Consumer:


package com.javasampleapproach.activemq.jms.consumer;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.javasampleapproach.activemq.model.Product;
 
@Component
public class JmsConsumer {
	
	@JmsListener(destination = "${jsa.activemq.queue.consumer}", containerFactory="jsaFactory")
	public void appleReceive(Product product, @Header("type") String productType){
		if("iphone".equals(productType)){
			System.out.println("Recieved Iphone: " + product);			
		}else if("ipad".equals(productType)){
			System.out.println("Recieved Ipad: " + product);		
		}
	}
}

4.2 For SpringActiveMqSendTo project

– Create a JmsReplyConsumer:


package com.javasampleapproach.activemq.jms.consumer;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import com.javasampleapproach.activemq.model.Company;
import com.javasampleapproach.activemq.model.Product;
 
@Component
public class JmsReplyConsumer {
	
	@JmsListener(destination = "${jsa.activemq.queue.listen}", containerFactory="jsaFactory")
	@SendTo("${jsa.activemq.queue.sendto}")
	public Message  receive(Product product, @Header("company") String companyName){
		
		// Console Log
		System.out.println("Recieved Message: " + product);
		
		// set company
		Company apple = new Company("Apple");
		product.setCompany(apple);
		
		Message  mesage = MessageBuilder
	            .withPayload(product)
	            .setHeader("type", product.getType())
	            .build();
		
		return mesage;	
	}
}

5. Run and check results

– Start ActiveMQ server with commandline: C:\apache-activemq-5.14.5>.\bin\activemq start.
– Build and Run the SpringBoot applications {SpringActiveMqSendTo, SpringActiveMqProducerConsumer} by commandlines: {mvn install -Dmaven.test.skip=true, mvn spring-boot:run}

-> Logs of JmsReplyConsumer from SpringActiveMqSendTo:


Recieved Message: [name = Iphone 7, type = iphone]
Recieved Message: [name = IPadPro, type = ipad]

-> Logs of JmsConsumer from SpringActiveMqProducerConsumer:


Recieved Iphone: [name = Iphone 7, type = iphone, company = Apple]
Recieved Ipad: [name = IPadPro, type = ipad, company = Apple]

-> With ActiveMQ server, go to http://localhost:8161/admin/queues.jsp:

SpringBoot ActiveMQ Response Management application - sendto aannotation - activemq logs

IV. Sourcecode

SpringActiveMqProducerConsumer
SpringActiveMqSendTo

One thought on “Spring Jms ActiveMQ – How to create a SpringBoot ActiveMQ Response Management application by @SendTo annotation”

Leave a Reply

Your email address will not be published. Required fields are marked *