Spring JMS Example + ActiveMQ + Annotation/ JavaConfig

Spring JMS Example + ActiveMQ + Annotation/ JavaConfig thumbnail
78K
By Dhiraj 21 December, 2017

In many enterprise application these days asynchronous request processing mechanism is highly used.Today let us integrate JMS and Spring with activemq with an example.This will be a simple JMS producer and consumer app with an asynchronous messaging model.All the configurations are made using java config.

If you are looking for spring boot and activemq integration then visit - Spring Boot JMS ActiveMQ Example

Visit Spring JMS Solace for JMS integration with Solace.

Definitions of JMS, ActiveMQ, Queue

What is JMS

Java Message Service (JMS) is an application program interface (API) that supports the formal communication known as messaging between computers in a network. The messages involved exchange crucial data between computers - rather than between users - and contain information such as event notification and service requests asynchronously.

What is ActiveMQ

ActiveMQ is a message oriented middleware. It is an open source message broker written in Java together with a full Java Message Service (JMS) client. It provides "Enterprise Features" which in this case means fostering the communication from more than one client or server.

What is JMS Queue

Queue follows point-to-point, or p2p messaging model which allows users to send messages both asynchronously and synchronously using different channels. In this model once a message is received by the consumer, the message is destroyed as per p2p messaging model.

What is JMS topic

Topic follows Publish-and-subscribe, or pub/sub messaging model which allows the producer to send messages to many users at the same time. Consumers can subscribe to a particular topic, or channel, and receive all messages within the chosen topic. This model is asynchronous.

Here we will be developing a sample web app using spring JMS having a producer and consumer which will produce and consume message from two different queue.

Environment Setup

1. JDK 8 2. Spring 4 3. Intellij Idea/ eclipse 4. Apache ActiveMQ 5. Maven 6. Apache tomcat

Maven Dependencies

To integrate JMS with Spring, we require 2 extra maven dependencies defined in our pom.xml. They are spring-jms and activemq-all

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.developerstack</groupId>
    <artifactId>jms-spring-integration</artifactId>
    <version>0.1.0</version>

    <properties>
        <spring-framework.version>4.1.0.RELEASE</spring-framework.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
		<version>${spring-framework.version}</version>
        </dependency>
		
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
			<version>${spring-framework.version}</version>
        </dependency>
		
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
		<version>5.10.0</version>
        </dependency>
		
		<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
		<version>3.1.0</version>
        </dependency>
		
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
		<version>2.6.2</version>
        </dependency>
		
    </dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml$gt
				</configuration>
			</plugin>
		</plugins>
	</build>
	
</project>

Spring Bean Configuration

@EnableJms : It enables detection of JmsListener annotations on any Spring-managed bean in the container.

ActiveMQConnectionFactory: It is used for creating connections.

JmsTemplate : It is a helper class that simplifies receiving and sending of messages through JMS and gets rid of the boilerplate code.

DefaultJmsListenerContainerFactory : It is responsible to create the listener container responsible for a particular endpoint.

By default, acivemq broker url is tcp://localhost:61616. The admin console url is http://localhost:8161/admin/ with userId and password as admin and admin.

JmsConfig.java
package com.develperstack.config;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;

@Configuration
@EnableJms
@ComponentScan(basePackages = "com.developerstack")
public class JmsConfig {

	String BROKER_URL = "tcp://localhost:61616"; 
	String BROKER_USERNAME = "admin"; 
	String BROKER_PASSWORD = "admin";
	
	@Bean
	public ActiveMQConnectionFactory connectionFactory(){
	    ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
	    connectionFactory.setBrokerURL(BROKER_URL);
	    connectionFactory.setPassword(BROKER_USERNAME);
	    connectionFactory.setUserName(BROKER_PASSWORD);
	    return connectionFactory;
	}

	@Bean
	public JmsTemplate jmsTemplate(){
	    JmsTemplate template = new JmsTemplate();
	    template.setConnectionFactory(connectionFactory());
	    return template;
	}

	@Bean
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
	    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
	    factory.setConnectionFactory(connectionFactory());
	    factory.setConcurrency("1-1");
	    return factory;
	}

}
 Other Interesting Posts
Spring Boot JMS ActiveMQ Example
Spring MVC Angularjs Integration with complete JavaConfig
Maintaining Spring Session during Websocket Connection
Spring Hibernate Integration with complete JavaConfig
Spring Security Password Encoding using Bcrypt Encoder
Spring Boot Security Redirect after Login with complete JavaConfig
Spring Security Hibernate Example with complete JavaConfig
Spring Boot Security Custom Form Login Example
Websocket spring Boot Integration Without STOMP with complete JavaConfig
Spring Junit Integration with complete JavaConfig
Spring Ehcache Cacheable Example with complete javaConfig
Spring Boot Spring MVC Example
Spring Boot Thymeleaf Example

This configuration is responsible to initialize spring based web application.

ApplicationInitializer.java
package com.developerstack.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class ApplicationInitializer implements WebApplicationInitializer {
	 
    public void onStartup(ServletContext container) throws ServletException {
 
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(JmsConfig.class);
        ctx.setServletContext(container);
        ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
    }
}

JMS Listener

@JmsListener:It marks a method to be the target of a JMS messagelistener on the specified destination. In our case the destination is inbound.queue This class is responsible to listen messsage from the inbound queue and process the same.

Listener.java
package com.developerstack.jms;

import org.springframework.stereotype.Component;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;

@Component
public class Listener {
	
	@Autowired
	private Producer producer;

	@JmsListener(destination = "inbound.queue")
	public void receiveMessage(final Message jsonMessage) throws JMSException {
		String messageData = null;
		System.out.println("Received message " + jsonMessage);
		if(jsonMessage instanceof TextMessage) {
			TextMessage textMessage = (TextMessage)jsonMessage;
			messageData = textMessage.getText();
		}
		producer.sendMessage("outbound.queue", messageData);
	}

}

JMS Producer

This class will parse the Json message and extract the value against key name and send greeting message to the outbound queue.

Producer.java
package com.developerstack.jms;

import java.util.Map;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;

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

import com.google.gson.Gson;

@Component
public class Producer {

	@Autowired
	JmsTemplate jmsTemplate;

	public void sendMessage(final String queueName, final String message) {
		Map map = new Gson().fromJson(message, Map.class);
		final String textMessage = "Hello" + map.get("name");
		System.out.println("Sending message " + textMessage + "to queue - " + queueName);
		jmsTemplate.send(queueName, new MessageCreator() {

			public Message createMessage(Session session) throws JMSException {
				TextMessage message = session.createTextMessage();
				return message;
			}
		});
	}

}

Working with JMS Topic

While dealing with topic in Spring JMS, we require to manually tell spring to enable it.For this, we only require to set the boolean flag i.e. pubSubDomain to true.To do this, we require our JMSConfig.java and set pubSubDomain to true for the bean definition of JmsTemplate and DefaultJmsListenerContainerFactory.Following are the changes we require to do.

template.setPubSubDomain(true);
factory.setPubSubDomain(true);

Once this is done, rest of the configuration remain same and in place of listening from a queue our aplication can start listening from the topic and publish message to a topic.Following are the lines I have added to consume message from a topic in Listener.java.

@JmsListener(destination = "inbound.topic")
	@SendTo("outbound.topic")
	public String receiveMessageFromTopic(final Message jsonMessage) throws JMSException {
		String messageData = null;
		System.out.println("Received message " + jsonMessage);
		String response = null;
		if(jsonMessage instanceof TextMessage) {
			TextMessage textMessage = (TextMessage)jsonMessage;
			messageData = textMessage.getText();
			Map map = new Gson().fromJson(message, Map.class);
			response  = "Hello " + map.get("name");
		}
		return response;
	}

Once this configuration is done and appliation is deployed you can see the following topics under topics menu.

jms-activemq-topic

Installing Apache ActiveMq

ActiveMq by default exposes a broker url tcp://localhost:61616 and an admin console on tcp://localhost:61616 with userId and password as admin and admin. Following are the steps to download and install activemq. 1. Download apache activemq from here as per your operating system. 2. Extract under some folder. In my case it's under java\apache-activemq-5.11.1-bin. 3. Now traverse to java\apache-activemq-5.11.1-bin\bin\win64 and execute the acivemq.bat file. 4. Open the browser and hit - http://localhost:8161/admin/ 4. Enter userId/password as admin/admin. Then following screen will appear:

activemq-connect

5. Now click on the queues option present in menu bar and you can notice there are no queues available as below image.

activemq-queues

Run Application

1. Deploy the application on tomcat. And you can see one queue automatically created with name inbound.queue which we had configured in our Listener.class. Refresh the page after application restart.

activemq-queue-creation

2. Our application is now listening to this queue and whenever we push messsage to this queue our Listener should automatically pick this message. Now click on the send To option. Enter a JSON string as {"name":"John"} and click on send.

activemq-send-message

3. After the messsage is sent, the message is received by Listener.java and the same message will be processed and send back to outbound.queue by Producer.java. Now you can see one more queue created once you click on the queues option from the menu as shown below:

activemq-message

Conclusion

This demonstrates a simple JMS integration. It is very useful in a mechanism to allow asynchronous request processing. You may wish to implement JMS because the request take a long time to complete or because several parties may be interested in the actual request. Another reason for using it is to allow multiple clients (potentially written in different languages) to access information via JMS.

I hope this article served you that you were looking for. If you have anything that you want to add or share then please share it below in the comment section.

Download the source

Share

If You Appreciate This, You Can Consider:

We are thankful for your never ending support.

About The Author

author-image
A technology savvy professional with an exceptional capacity to analyze, solve problems and multi-task. Technical expertise in highly scalable distributed systems, self-healing systems, and service-oriented architecture. Technical Skills: Java/J2EE, Spring, Hibernate, Reactive Programming, Microservices, Hystrix, Rest APIs, Java 8, Kafka, Kibana, Elasticsearch, etc.

Further Reading on spring-jms