Messaging is a mechanism to create and exchange data/information/objects within/between applications. An application that generates some useful data can send it's messages (objects) to an agent and these messages (objects) can then be read from another application. JMS is a Java API that enables applications to be able to communicate with each other using the mechanism described above.
You can read more about JMS here.
In this article, we will see how you can quickly start using JMS using the Spring JMS framework. Spring provides template based APIs which greatly simplify the use of JMS. A lot of work is done behind the scenes which saves the developer time to concentrate on the business requirement.
We will take a look at the important Spring JMS classes and apis as we go through an example. So let's get started!
Step 1: Download and install the JMS provided (ActiveMQ)
The first thing you would need is a JMS provider (which is an implementation of the JMS interface for a MOM - Message Oriented Middleware). There are a lot of providers available, such as Apache's ActiveMQ, IBM's WebSphere M, JBoss messaging, RabbitMQ, Oracle AQ and many others.
For the purpose of this tutorial we will be using ActiveMQ. You can download the zip file and install it using the instructions documented here. Start the provider (open a command prompt to the bin folder of the installation and type 'activemq').
To test the setup, browse to : http://localhost:8161/admin/
To be able to send and receive messages asynchronously, we would first need a queue, which is a destination for the messages. Let's create a fresh queue for our sample application.
Go to the 'Queues' menu on the top of the admin page, and create a new queue called 'orderQueue'.
Step 2: Create a Spring project
You can create a spring project manually or use the STS (SpringSource Tool Suite) like i am using. This IDE helps you get started with Spring projects within no time.
Create a 'Spring Utility Project' using 'File -> New -> Spring Template Project -> Simple Spring Utility Project'.
Type the project name , say 'OrderApp' and choose a base package say - com.test.orderapp
This project type uses maven for dependency management. We would need to add a few dependencies for using spring-jms and activemq.
Open the 'pom.xml' at the root of the project and add the following dependencies:
Step 3: Configure beans in application context
Edit the Spring context file app-context.xml at \src\main\resources\META-INF\spring and add the following to it:
Step 4: Create the domain class
We will be creating a simple domain class called 'Order' in a package com.test.orderapp.domain as follows:
package com.test.orderapp.domain;
We will create a simple service class that creates new orders given some information and then sends them.
The orderSender class is jms aware and has all the logic to convert and send the domain object as a jms message.
OrderSender.java :-
Also note, that we are not specifying the destination in the send method since we have defined a defaultDestination for the jmsTemplate in the spring context. In case you are not going the default way, you can pass a string representing the destination as the first parameter to the jmsTemplate.send() method
for eg:
jmsTemplate.send( "orderQueue", new MessageCreator() ..... )
There are other ways to send messages such as MessageConverter which can also be used to convert between Java objects and JMS messages. The convertAndSend and receiveAndConvert methods of the jmsTemplate class delegate the conversion to the MessageConverter interface. We are not using MessageConverter in this tutorial. Some other time maybe...
Let's update the app-context.xml file to declare two additional bean definitions:
Let's check if we are actually able to send any messages to the ActiveMQ 'orderQueue'. We will create a simple TestJms class as follows:
At this point, if you run the TestJms class, 5 messages would be sent to the 'orderQueue' queue. If you browse to the http://localhost:8161/admin/queues.jsp page you should see 5 messages enqueued on orderQueue.
Step 7: Consuming JMS Messages
Listening to the jms messages is in fact easier than sending them. All we need to do is define a jms Listener Container in the spring context to which we pass the connectionFactory bean which specifies the jms provider, a reference to the destination which specifies the queue name and we also specify here, which method from which bean needs to be called when a message is recieved.
Add the following to the app-context.xml file:
Let's run the TestJms class once again, this time we should see some prints from the OrderListener class too.
Pretty cool, isn't it!
Well, that brings us to the end of the introductory article to Spring JMS, i hope you can now appreciate how much easier it is to write JMS based applications using Spring.
Hope you find this article useful.
Happy coding!
Download the source code of the sample application : OrderApp.rar
You can read more about JMS here.
In this article, we will see how you can quickly start using JMS using the Spring JMS framework. Spring provides template based APIs which greatly simplify the use of JMS. A lot of work is done behind the scenes which saves the developer time to concentrate on the business requirement.
We will take a look at the important Spring JMS classes and apis as we go through an example. So let's get started!
Step 1: Download and install the JMS provided (ActiveMQ)
The first thing you would need is a JMS provider (which is an implementation of the JMS interface for a MOM - Message Oriented Middleware). There are a lot of providers available, such as Apache's ActiveMQ, IBM's WebSphere M, JBoss messaging, RabbitMQ, Oracle AQ and many others.
For the purpose of this tutorial we will be using ActiveMQ. You can download the zip file and install it using the instructions documented here. Start the provider (open a command prompt to the bin folder of the installation and type 'activemq').
To test the setup, browse to : http://localhost:8161/admin/
To be able to send and receive messages asynchronously, we would first need a queue, which is a destination for the messages. Let's create a fresh queue for our sample application.
Go to the 'Queues' menu on the top of the admin page, and create a new queue called 'orderQueue'.
Step 2: Create a Spring project
You can create a spring project manually or use the STS (SpringSource Tool Suite) like i am using. This IDE helps you get started with Spring projects within no time.
Create a 'Spring Utility Project' using 'File -> New -> Spring Template Project -> Simple Spring Utility Project'.
Type the project name , say 'OrderApp' and choose a base package say - com.test.orderapp
This project type uses maven for dependency management. We would need to add a few dependencies for using spring-jms and activemq.
Open the 'pom.xml' at the root of the project and add the following dependencies:
<dependency>Note: Update the version for activemq as per the installation you have used in Step 1.
<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.4.2</version>
</dependency>
Step 3: Configure beans in application context
Edit the Spring context file app-context.xml at \src\main\resources\META-INF\spring and add the following to it:
<?xml version="1.0" encoding="UTF-8"?>Let's take a closer look at the beans we've defined in the spring xml.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
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
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd">
<context:component-scan base-package="com.test.orderapp" />
<context:annotation-config/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="orderQueue"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="destination" />
</bean>
</beans>
1. connectionFactory:
A connectionFactory is required to create a connection to the jms provider i.e. activemq. The javax.jms.ConnectionFactory creates a javax.jms.Connection object, which then creates a javax.jmx.Session object which is used for the message exchange between the application and the jms provider. You need to specify the brocker URL , i.e. the URL at which the jms provider is listening.2. destination:
The destination specifies the queue name to which the messages will be sent. Messages will be stored in and consumed from the destination specified. Destinations can, in general, be of two types: Queue and Topic. Queues represent a point-to-point messaging mechanism whereas Topics represent a publisher-subscriber sort of messaging where a single message is sent to multiple consumers. (we will be working with only queues in this tutorial)3. jmsTemplate:
This is the class that does most of the jms related work, it creates connection factories, creates Sessions, takes care of the transactions, provides apis for creating and sending messages of different types. The jmsTemplate bean here takes a reference to the connectionFactory and a defaultDestination to which it would direct the messages. If you don't specify any defaultDestination, you can pass the destination in the 'send' methods that jmsTemplate provides. The important methods provided by jmsTemplate are : send, receive, convertAndSend and receiveAndConvertStep 4: Create the domain class
We will be creating a simple domain class called 'Order' in a package com.test.orderapp.domain as follows:
package com.test.orderapp.domain;
public class Order {Step 5: Write the code to send the objects
private int orderId;
private int customerId;
private double price;
private String orderCode;
public Order(int orderId, int customerId, double price, String orderCode){
this.orderId = orderId;
this.customerId = customerId;
this.price = price;
this.orderCode = orderCode;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getOrderCode() {
return orderCode;
}
public void setOrderCode(String orderCode) {
this.orderCode = orderCode;
}
}
We will create a simple service class that creates new orders given some information and then sends them.
package com.test.orderapp.producer;Nothing special about the service class. We are wiring the OrderSender class into the service class.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.orderapp.domain.Order;
@Service("orderService")
public class OrderService {
static int orderSequence = 1;
@Autowired
private OrderSender orderSender;
public void setOrderSender(OrderSender orderSender){
this.orderSender = orderSender;
}
public void sendOrder(int customerId, double price)
{
Order order = new Order(orderSequence, 2, price, "ordercd"+ orderSequence++);
orderSender.sendOrder(order);
}
}
The orderSender class is jms aware and has all the logic to convert and send the domain object as a jms message.
OrderSender.java :-
package com.test.orderapp.producer;The OrderSender uses the JmsTemplate's send method to send the object. Notice the use of the MessageCreator class. You can send different types of messages via JMS eg: TextMessage, ObjectMessage, MapMessage (all part of the java.jms package) The most preferred way to send complex domain objects is to use MapMessage to which you can add all the fields of your domain object as key-value pairs. The MessageCreator class is a callback interface for creating a message using the Session object that is passed as a parameter to the createMessage method.
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import com.test.orderapp.domain.Order;
public class OrderSender {
@Autowired
private JmsTemplate jmsTemplate;
public void sendOrder(final Order order){
jmsTemplate.send(
new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
MapMessage mapMessage = session.createMapMessage();
mapMessage.setInt("orderId", order.getOrderId());
mapMessage.setInt("customerId", order.getCustomerId());
mapMessage.setDouble("price", order.getPrice());
mapMessage.setString("orderCode", order.getOrderCode());
return mapMessage;
}
}
);
System.out.println("Order sent - id: "+ order.getOrderId());
}
}
Also note, that we are not specifying the destination in the send method since we have defined a defaultDestination for the jmsTemplate in the spring context. In case you are not going the default way, you can pass a string representing the destination as the first parameter to the jmsTemplate.send() method
for eg:
jmsTemplate.send( "orderQueue", new MessageCreator() ..... )
There are other ways to send messages such as MessageConverter which can also be used to convert between Java objects and JMS messages. The convertAndSend and receiveAndConvert methods of the jmsTemplate class delegate the conversion to the MessageConverter interface. We are not using MessageConverter in this tutorial. Some other time maybe...
Let's update the app-context.xml file to declare two additional bean definitions:
<bean id="orderService" class="com.test.orderapp.source.OrderService" />Step 6: Test the message producer
<bean id="orderSender" class="com.test.orderapp.source.OrderSender" /
Let's check if we are actually able to send any messages to the ActiveMQ 'orderQueue'. We will create a simple TestJms class as follows:
package com.test.orderapp.runner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.test.orderapp.producer.OrderService;
public class TestJms {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
OrderService orderService = (OrderService) ctx.getBean("orderService");
for(int i =1; i<=5; i++)
orderService.sendOrder(1+i, 10.0D+i);
}
}
At this point, if you run the TestJms class, 5 messages would be sent to the 'orderQueue' queue. If you browse to the http://localhost:8161/admin/queues.jsp page you should see 5 messages enqueued on orderQueue.
Step 7: Consuming JMS Messages
Listening to the jms messages is in fact easier than sending them. All we need to do is define a jms Listener Container in the spring context to which we pass the connectionFactory bean which specifies the jms provider, a reference to the destination which specifies the queue name and we also specify here, which method from which bean needs to be called when a message is recieved.
Add the following to the app-context.xml file:
<jms:listener-container connection-factory="connectionFactory">Create a class OrderListener as follows:
<jms:listener destination="orderQueue" ref="orderListener" method="orderReceived" />
</jms:listener-container>
package com.test.orderapp.listener;Simple.
import java.util.Map;
import org.springframework.stereotype.Component;
import com.test.orderapp.domain.Order;
@Component
public class OrderListener {
public void orderReceived(Map<String, Object> message) throws Exception {
int orderId = (Integer) message.get("orderId");
int customerId = (Integer) message.get("customerId");
double price = (Double) message.get("price");
String orderCode = (String) message.get("orderCode");
Order customer = new Order(orderId, customerId, price, orderCode);
System.out.println("Order received: "+ orderId + ", customerId: "+ customerId + ", price: "+ price);
}
}
Let's run the TestJms class once again, this time we should see some prints from the OrderListener class too.
Pretty cool, isn't it!
Well, that brings us to the end of the introductory article to Spring JMS, i hope you can now appreciate how much easier it is to write JMS based applications using Spring.
Hope you find this article useful.
Happy coding!
Download the source code of the sample application : OrderApp.rar
Nice post. Thanks for sharing.
ReplyDeleteHey, thanks.. happy to know it helped!
ReplyDeleteHello there,
ReplyDeleteAwesome post! By the way, is there an email address I can contact you in person ?
Hi Illias,thanks for appreciating..
ReplyDeleteCan i have your email address so i can email you mine :D
awesome post...thanks for taking time to share this with the world....
ReplyDeleteHey thanks, so glad u found it useful :)
ReplyDeleteCan you explain the same example with "Topic" i.e.
ReplyDeletesingle message is sent to multiple consumers
I am getting nullpointerexception while running the OrderListener from TestJms main method.Can you please help me.
ReplyDeleteHi there, First of all thanks for the nice post,
ReplyDeleteI am able to send the message, and i can see that in the queue (using the web console)< but not able to consume the message >>
can you please help
I was having problems consuming the messages also. There was a problem with the active MQ 5.5.0 library that i was using. I changed this to use the following in the pom.
ReplyDeleteorg.apache.activemq
activemq-all
5.4.2
This will work with the 5.5 Active MQ broker. All worked okay after this. Thanks for the great post.
This comment has been removed by the author.
ReplyDeleteHi!
ReplyDeleteI'm a community curator at DZone.com and I really was impressed by your blog and the interesting Spring Integration topics you write about. We’re in the works of putting up a new ‘Enterprise Integration Zone.’ I'm positive our community would be interested to hear about some of your experiences and expertise.
I was going to ask you if you would be interested in giving me permission to republish one of your recent posts to benefit our audience and give some exposure to your content.
If you're interested, I'd also like to let you know about our MVB (most valuable blogger) program, which is now at 400 strong. Here are the details about that: www.dzone.com/aboutmvb With the quality of writing displayed in your blog, I'd be honored to invite you into our program. You get a shiny web-badge :)
Thanks!
Prasant Lokinendi
Community Curator
ploki [at] dzone [dot] com
a quick look on JMS programming using Topic and Queue in JMS Provider (ACTIVEMQ) JMS Spring
ReplyDeleteVery nice ....... thanks
ReplyDeleteThank you, it helped me a lot.
ReplyDeletethank you , I look for method of ActiveMQ installation .
ReplyDeleteI found this is link : http://www.jmkg.co.uk/2010/08/31/installing-activemq-on-ubuntu/
but I have this pb when I open http://localhost:8161/
Error 404 - Not Found.
No context on this server matched or handled this request.
Contexts known to this server are:
/admin ---> o.e.j.w.WebAppContext{/admin,file:/home/wa/stage2012/jms/activemq/apache-activemq-5.7.0/webapps/admin/}
/demo ---> o.e.j.w.WebAppContext{/demo,file:/home/wa/stage2012/jms/activemq/apache-activemq-5.7.0/webapps/demo/}
/fileserver ---> o.e.j.w.WebAppContext{/fileserver,file:/home/wa/stage2012/jms/activemq/apache-activemq-5.7.0/webapps/fileserver/}
Hi,
ReplyDeleteThanks for this tutorial,
I followed your steps, when I tried to run the class : TestJms.java
I got this error :
21:47:19,919 INFO t.support.ClassPathXmlApplicationContext: 456 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c7e553: startup date [Tue Nov 13 21:47:19 EST 2012]; root of context hierarchy
21:47:20,028 INFO eans.factory.xml.XmlBeanDefinitionReader: 315 - Loading XML bean definitions from class path resource [app-context.xml]
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [app-context.xml]; nested exception is java.io.FileNotFoundException: class path resource [app-context.xml] cannot be opened because it does not exist
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:341)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:212)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:126)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:92)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83)
at com.test.orderapp.runner.TestJms.main(TestJms.java:9)
Caused by: java.io.FileNotFoundException: class path resource [app-context.xml] cannot be opened because it does not exist
modify
Delete1. TestJms.java : ClassPathXmlApplicationContext("/META-INF/spring/app-context.xml");
2. app-context.xml
<bean id="orderSender" class="com.test.orderapp.producer.OrderSender" /
Thanks...
ReplyDeleteHi,
ReplyDeleteNice tutorial, I imported it in my STS, but the pom.xml has an error that I did not understand, please your help is appreciated :
Failure to find com.springsource.bundlor:com.springsource.bundlor.maven:pom:1.0.0.RELEASE in http://
repository.codehaus.org/ was cached in the local repository, resolution will not be reattempted until the update
interval of Codehaus has elapsed or updates are forced
Nice one.
ReplyDeleteI have one suggestion, if we configure listener / consumption as a new project. It will give more clarity on real time consumption.
Worked like a breeze !!!
ReplyDelete