Tuesday, October 7, 2014

Spring JMS with ActiveMQ – hello world example – send message

Before we start, let make some concepts clear .  What’s the role for each part of the JMS/Spring JMS/ActiveMQ combination.
What’s JMS ? In short, JMS is a set of APIs defined in JSR. In practical you can think it’s a set of Java Interfaces. JMS is part of the JEE standard coming with JDK in package javax.jms.*  There are basically 2 versions of JMS, JMS 1.x and JMS 2.0. These 2 sets of APIs are different.
What’s ActiveMQ? In short ActiveMQ is one of the implementation of JMS APIs (Of course there are other implementations, Open JMS, RabbitMQ for example).  ActiveMQ also provide the broker which can be treated somehow  like a server, that you can send message to or receive message from. For now, 2014-10, ActiveMQ only implements the interfaces defined in  JMS 1.1 version. 
What’s Spring JMS? Spring JMS is part of  the whole spring framework. It wrap the real JMS service provider such as ActiveMQ or OpenJMS, provides consistent APIs to upper logic. Spring  JMS’s APIs are quite similar to JMS2.0  in JEE7. Spring JMS can decouple you business logic from the real JMS service provider, which here in our example is apache ActiveMQ.
In this example we are going to create a example to send out a text message by using Spring JMS and ActiveMQ. 

1. What you need

  • JDK 1.7
  • Maven 3.2.1
  • ActiveMQ 5.10.0
  • Spring 4.1.0.RELEASE  (acquired by Maven)
We’ll use maven to manage all dependencies.  To write code there is no need for ActiveMQ binary, since maven will take care of the ActiveMQ library we need. But to run the code, we need the ActiveMQ binary, In this example  we'll run the ActiveMQ broker on a machine of IP 192.168.203.143 with default port 61616. If you run the broker in a different IP or port, don't forget to change the broker URL in Spring configuration file.

2. Configure the maven  pom.xml

<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>org.springframework.samples</groupId>
<artifactId>spring-jms-activemq-send</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<!-- Spring version -->
<spring-framework.version>4.1.0.RELEASE</spring-framework.version>
<!-- ActiveMQ version -->
<activemq.version>5.10.0</activemq.version>
</properties>

<dependencies>
<!-- Spring aritifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- ActiveMQ Artifacts -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-spring</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>

<!-- Using JDK 1.7 for compiling -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

As you can see from the maven pom.xml file, we use 3 key artifacts in this example, spring-context, spring-jms and activemq-spring.  The first 2 belong to spring framework and the last one is ActiveMQ  implementation for jms.  The final part of the pom file specified the JDK version for compiling.



3. Define the Java Class


We are going to define 2 classes. The first one is a simple spring bean:
package com.shengwang.demo;

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

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

@Service
public class JmsMessageSender {

@Autowired
private JmsTemplate jmsTemplate;


/**
* send text to default destination
* @param text
*/
public void send(final String text) {

this.jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
Message message = session.createTextMessage(text);
//set ReplyTo header of Message, pretty much like the concept of email.
message.setJMSReplyTo(new ActiveMQQueue("Recv2Send"));
return message;
}
});
}

/**
* Simplify the send by using convertAndSend
* @param text
*/
public void sendText(final String text) {
this.jmsTemplate.convertAndSend(text);
}

/**
* Send text message to a specified destination
* @param text
*/
public void send(final Destination dest,final String text) {

this.jmsTemplate.send(dest,new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
Message message = session.createTextMessage(text);
return message;
}
});
}
}

In the above JmsMessageSender Class, Spring's JmsTemplate is used to do the send message task. You can find out how this bean is defined in spring configuration file below.  There are 1 sendText method and 2 send methods in the class. The 2 send methods both take String parameter text as message payload to be sent. The different is the later send take one more Destination parameter to specify where to send the message. the first one will simply send the message to the default destination.  The sendText method simplify the send by using Spring JmsTemplate’s convertAndSend method which will automatically create the TextMessage object from the input String object and send it.  convertAndSend  can save you from creating the TextMessage object yourself.

Next let’s see how to use the above JmsMessageSender class in the main class.
package com.shengwang.demo;

import javax.jms.Queue;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class DemoMain {

public static void main(String[] args) {
// init spring context
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");

// get bean from context
JmsMessageSender jmsMessageSender = (JmsMessageSender)ctx.getBean("jmsMessageSender");

// send to default destination
jmsMessageSender.send("hello JMS");

// send to a code specified destination
Queue queue = new ActiveMQQueue("AnotherDest");
jmsMessageSender.send(queue, "hello Another Message");

// close spring application context
((ClassPathXmlApplicationContext)ctx).close();
}

}

In the main, we send out 2 text message by using the JmsMessageSender bean we have defined.  One thing to notice is how to send out the second message. We create a ActiveMQQueue as destination. and send the second message to this queue. We use ActiveMQ as our JMS service provider. If we use another JMS service provider like OpenJMS, we may need to change ActiveMQQueue  to OpenJMS provided Queue implementation.

Also we use ClassPathXmlApplicationContext to lookup all spring beans in spring configuration file called “app-context.xml”.  At main end, we close the spring context.



4. spring configuration


Our spring configuration file is named “app-context.xml”. It stays in the man resource path /src/main/resources/ to store the spring configuration file. In the configuration file, we define beans in this order:

Connection Factory  bean (by jms provider ActiveMQ) –> cached Connection Factory  bean ( by Spring jms) –> JmsTemplate bean (by spring).

The cached connection factory bean is necessary because the Spring JmsTemplate will open/close connection on every send or receive action.  So in practical there will always a cached connection factory bean beside the direct connection factory bean.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.shengwang.demo" />


<!-- =============================================== -->
<!-- JMS Common, Define JMS connectionFactory -->
<!-- =============================================== -->
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- brokerURL, You may have different IP or port -->
<constructor-arg index="0" value="tcp://192.168.203.143:61616" />
</bean>

<!-- Pooled Spring connection factory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>

<!-- ======================================================= -->
<!-- JMS Send, define default destination and JmsTemplate -->
<!-- ======================================================= -->
<!-- Default Destination Queue Definition -->
<bean id="defaultDestination" class="org.apache.activemq.command.ActiveMQQueue">
<!-- name of the queue -->
<constructor-arg index="0" value="Send2Recv" />
</bean>

<!-- JmsTemplate Definition -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="defaultDestination" />
</bean>

</beans>


As you can see from the Spring configuration file, we use <context:component-scan ...> to let spring find out all beans automatically. In our example we have defined only one bean, jmsMessageSender . Next we define connection factories. The ActiveMQ's connection factory bean works as an input of Spring's pooled connection factory. These 2 connection factory beans will be needed both for sending and receiving messages.  After connection factory beans, we define a destination used as default destination. It's a  queue with name "Send2Recv". Finally there's the Spring JmsTemplate bean. It use the connection factory and defaultDestination bean we create above. If you don't define default destination for JmsTemplate, you will need to explicitly set destination when you try to send any message in your java code.

The Jms service provider's bean, namely ActiveMQ related beans,  will NOT be used in your code directly. There are the input of Spring JMS.  You code use beans from Spring JMS instead.

Everything is almost done now. Let's review the directory structure for our maven project.

image



5. Run the code


Before you can run the code, you need to make sure the ActiveMQ broker is running. So our jave code can connect to it and send message to it.   Make sure the broker IP and port are correct in your spring configuration file "app-context.xml". Run the ActiveMQ broker like with this command.
ACTIVEMQ_INSTALL_DIR/bin/activemq start

Now you can run you main class.  you can run it from IDE such as eclipse, or you can run it direct in command line by using maven.
# run this from project directory
cd spring-jms-activemq-send
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"

It should run quietly without any error. After running your main, you can check the result through ActiveMQ's administration tool. ActiveMQ can be managed by browser on port 8161.  My ActiveMQ broker is running on IP 192.168.203.143, so I can access URL http://192.168.203.143:8161/admin to browse all queues and all messages in the queues.

image As you can see after running the main, there are 2 queues created on the broker. One is named "Send2Recv" which we used as default destination, the other one is "AnotherDest" which we create in the main class. By click into the queue, all message be list out, click any message can see the detain of that message , including message header and message content.

image image

6. More


Here are more hello world level examples on how to receive message from JMS with Spring jms and ActiveMQ.

29 comments:

  1. Nice detailed article on basics

    ReplyDelete
  2. Best article on spring jms basic

    ReplyDelete
  3. I keep getting 'unrecognised tag' errors everytime I try to compile the pom.xml,
    even though I copy & pasted your code straight from this page?

    ReplyDelete
    Replies
    1. try blanket renaming all instances in the pom.xml as follows:
      groupid --> groupId
      artifactid --> artifactId
      modelversion --> modelVersion

      Delete
    2. Thank you, I'll correct them

      Delete
  4. hello ! how to set the replyto header in app-context.xml? my Send2Recv message doesn`t have the 'Recv2Send' replyto header

    ReplyDelete
    Replies
    1. I've update the code. In short, use message.setJMSReplyTo(new ActiveMQQueue("Recv2Send")) before send the message out.

      Delete
  5. Thankyou for great explanation, what should i change if i wanna make many destination when i send a message ?

    ReplyDelete
    Replies
    1. I guess you just wanna to send out once to trigger many receivers to work. If that's the case, use topic instead of queue. Any message send to a topic will broadcast to all the receivers.

      Delete
    2. Thanks a lot...you made my day... :)

      Delete
  6. Thank you for your good tutorial. I want to read the broker URL from a property file instead of setting as a constructor-arg. How can I do it?

    ReplyDelete
    Replies
    1. In short you set the broker url as a variable ( or call it place holder in spring). Here is another article on how to set up dev/test/production environment for spring projects, It has all the details you need.

      http://shengwangi.blogspot.jp/2015/06/how-to-set-dev-test-prod-in-spring-xml.html

      Delete
  7. How can i start ActiveMQ broker through java code

    ReplyDelete
    Replies
    1. In short :

      BrokerService broker = new BrokerService();
      // configure the broker
      broker.addConnector("tcp://localhost:61616");
      broker.start();

      For more information, check http://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection.html

      Delete
  8. Nice work! thanks!

    ReplyDelete
  9. Nice explanation with quick clearance of doubts of readers, i must appreciate it

    ReplyDelete
  10. Thank you so much for this wonderful tutorial. Worked right away and the explanation was very helpful.

    ReplyDelete
  11. Hi, How can we download the source code?
    Thanks,
    Sati

    ReplyDelete
  12. Hi, Thanks for this tutorial, can you please post the source code ?
    Thanks.

    ReplyDelete
  13. how to run main class from eclipse mars i build-ed a maven project and performed maven clean and maven install.I am using Apache tomcat 8.0 version

    ReplyDelete
  14. would be happy if u could do a tutorial on the listener, i mean the receiver of the message

    ReplyDelete
  15. Very nice, Can you please do a tutorial on the listener, especially using anotated destination. I want to see how the message for anotherDestination is received at the listener

    ReplyDelete
  16. very nice article you saved my day :)

    ReplyDelete
  17. Excellent Article

    ReplyDelete
  18. simple and straight forward....good work...

    ReplyDelete
  19. Very informative for beginners awesome.

    ReplyDelete
  20. Hello, Could you please also create receiver ? Who will receive the messages?

    ReplyDelete
  21. Hi , what would i need to change if i have to use RHEL MQ instead of Active MQ?

    ReplyDelete

Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List