Tomcat + Spring + ActiveMQ

In this tutorial, we are going to learn how to use Queues and Topics as managed resources in Tomcat as client and use Spring to listen and send messages to/from ActiveMQ. We are going to create a war application for listening to queues in ActiveMQ.

Example Scenario:

We have two queues in ActiveMQ – firstQueue and secondQueue. Whenever we send a sentence as message to “firstQueue”, it should be consumed by our client and thereafter, the characters  and their number of occurrences in the sentence should reach “secondQueue” as a message.

Project Artifacts:

1. Technologies used –

a. Eclipse IDE for Java EE developer 4.5.0.20150621-1200 (Mars)
b. Preinstalled Maven with Eclipse Mars
c. Java 1.8
d. ActiveMQ 5.12.0
e. Spring 4.2.2.RELEASE

2. Eclipse project folder structure:

ActiveMQSpringJNDITomcatProjStruc

3. Jars Used

If you are not using Maven, here is the list of jars you would need for this project:

spring-jms-4.2.2.RELEASE.jar
spring-aop-4.2.2.RELEASE.jar
aopalliance-1.0.jar
spring-beans-4.2.2.RELEASE.jar
spring-context-4.2.2.RELEASE.jar
spring-expression-4.2.2.RELEASE.jar
spring-core-4.2.2.RELEASE.jar
commons-logging-1.2.jar
spring-messaging-4.2.2.RELEASE.jar
spring-tx-4.2.2.RELEASE.jar
spring-web-4.2.2.RELEASE.jar
activemq-pool-5.12.0.jar
slf4j-api-1.7.10.jar
activemq-jms-pool-5.12.0.jar
geronimo-jms_1.1_spec-1.1.1.jar
activemq-client-5.12.0.jar
hawtbuf-1.11.jar
geronimo-j2ee-management_1.1_spec-1.0.1.jar
geronimo-jta_1.0.1B_spec-1.0.1.jar
commons-pool2-2.3.jar

4. Maven Dependencies

file:pom.xml . Paste the below content to pom.xml. It has activemq and spring dependencies.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javanbeyond</groupId>
    <artifactId>TomcatSpringActiveMQClient</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>TomcatSpringActiveMQClient Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
            <version>5.12.0</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>TomcatSpringActiveMQClient</finalName>
    </build>
</project>

5. Deployment Descriptor

file:web.xml.

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

6. Tomcat changes for JNDI

Add the following entry to Tomcat context.xml in <TOMCAT_HOME>/config folder as a child of Context root tag . This entry instructs Tomcat to enter these resources in JNDI which would be “looked up” by the application with the same JNDI name. Please note that we have used ActiveMQ pooling connection factory for better performance and scalability.

			<Resource name="jms/activemqConnFact" auth="Container" type="org.apache.activemq.pool.PooledConnectionFactory" description="JMS Connection Factory" factory="org.apache.activemq.jndi.JNDIReferenceFactory" brokerURL="tcp://localhost:61616" brokerName="localhost" maxConnections="10"></Resource>
<Resource name="jms/sendToQueue" auth="Container" type="org.apache.activemq.command.ActiveMQQueue" description="My Sending Queue" factory="org.apache.activemq.jndi.JNDIReferenceFactory" physicalName="secondQueue"></Resource>
<Resource name="jms/receiveFromQueue" auth="Container" type="org.apache.activemq.command.ActiveMQQueue" description="My Receiving Queue" factory="org.apache.activemq.jndi.JNDIReferenceFactory" physicalName="firstQueue"></Resource>

7. Spring Application Context.

file:applicationContext.xml. This is the main spring configuration file for our application to listen to ActiveMQ queues. If you are not using JNDI for some reason, please replace the JNDI lookup spring tags with bean declaration of connection factory and queues.

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd             http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd             http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <!-- To recognize Autowire annotation in listener class -->
    <context:annotation-config></context:annotation-config>
    <!-- Template class for sending messages. -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="activemqConnectionFactory"></constructor-arg>
        <property name="sessionTransacted" value="false"></property>
        <property name="defaultDestination" ref="jmsTemplateDest"></property>
        <property name="deliveryPersistent" value="false"></property>
    </bean>
    <bean id="messageListener" class="com.javanbeyond.MyMessageListenerSender"></bean>
    <!-- Listener class which gets any message that arrives at the queue "FirstQueue" -->
    <jms:listener-container container-type="default" connection-factory="activemqConnectionFactory" acknowledge="auto" destination-resolver="myDestinationResolver">
        <!-- The destination should match the id of JNDI lookup. The Destination Resolver decides how to resolve the destination. In this case, it would look for an id with the destination value in Spring application Context. If no destination resolver is mentioned, the name will be taken as Queue or Topic name of Message provider -->
        <jms:listener destination="jmsReceiveJndi" ref="messageListener" concurrency="3-5"></jms:listener>
    </jms:listener-container>
    <bean id="myDestinationResolver" class="org.springframework.jms.support.destination.BeanFactoryDestinationResolver"></bean>
    <!-- Getting connection factory and destination queues from JNDI.Topics can similarly be obtained, if needed -->
    <jee:jndi-lookup id="activemqConnectionFactory" jndi-name="jms/activemqConnFact" expected-type="javax.jms.ConnectionFactory"></jee:jndi-lookup>
    <jee:jndi-lookup id="jmsTemplateDest" jndi-name="jms/sendToQueue" expected-type="javax.jms.Queue"></jee:jndi-lookup>
    <jee:jndi-lookup id="jmsReceiveJndi" jndi-name="jms/receiveFromQueue" expected-type="javax.jms.Queue"></jee:jndi-lookup>
</beans>

8. Listener class which can send message as well.

file:MyMessageListenerSender.java. This class receives the message from ActiveMQ and send it to “secondQueue” with the help of JmsTemplate. Please note that we could have also used JmsTemplate to receive messages synchronously. But listener classes are better suited for getting messages from Message Providers.

 

package com.javanbeyond;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
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;

// Listener class that gets message from one queue and send it to other queue
// Here Sending and Receiving of messages happening in the same class.
// In practical circumstances, these operations may be distributed in different classes.
public class MyMessageListenerSender implements MessageListener {

  @Autowired
  JmsTemplate jmsTemplate;

  public void onMessage(Message msg) {
    if (msg instanceof TextMessage) {
      final TextMessage text = (TextMessagemsg;
      try {
        System.out.println("Received Message : " + text.getText());
        System.out.println("Sending message to Other Queue");
        jmsTemplate.send(new MessageCreator() {
          public Message createMessage(Session session)
              throws JMSException {
            // Creating Map message. We can create
            // Bytes,Stream,Object,Text message as well
            MapMessage mapMsg = session.createMapMessage();
            Map<Character, Integer> characterMap = calculateLetterNumber(
                text.getText());
            for (Entry<Character, Integer> entry : characterMap
                .entrySet()) {
              mapMsg.setInt(entry.getKey().toString(),
                  entry.getValue());
            }
            return mapMsg;
          }
        });
      catch (JMSException e) {
        e.printStackTrace();
      }
    }
  }

  private Map<Character, Integer> calculateLetterNumber(String sentence) {
    Map<Character, Integer> characterMap = new HashMap<Character, Integer>();
    for (Character c : sentence.toCharArray()) {
      if (characterMap.containsKey(c)) {
        characterMap.put(c, characterMap.get(c1);
      else {
        characterMap.put(c, 1);
      }
    }
    return characterMap;

  }
}

Example Execution:

  1. Start ActiveMQ and login as admin/admin and click on “Queues” tab.
  2. Start Tomcat and deploy the application. You should see three listeners listening to “firstQueue”.

ActiveMQTomcatSpringListener

3. Click on “Send To”  in “Operations” column. Write anything of your choice as the body of the message.

TomcatSpringActiveMQSendQueue

4. Click on “Send”. In eclipse console, you will see the message you typed.

Received Message : This message is listened by Spring...
Sending message to Other Queue

5. Refresh the “Queues” tab in ActiveMQ and click on “secondQueue” and then the first message.

6. You should see each letter of the sentence you typed with their number of occurrences.

TomcatActiveMQSpringEndResult

 

 

Leave a Reply

Back to Top
%d bloggers like this: