Spring Framework JMSTemplate Example
In order to send or receive messages through JMS, we need a connection to JMS provider, obtain session, create destination creation, the JMS API involved becomes too verbose and repetitive. JmsTemplate
is a helper class that simplifies receiving and sending of messages through JMS and gets rid of the boilerplate code.
JmsTemplate
simplifies the development efforts on constructing the message to send or processing messages that are received through synchronous JMS access code.
Let’s start with a simple example and then re-factor it to use JmsTemplate
1. Dependencies
In order to send and receive JMS messages to and from a JMS message broker, we need to include the message service library. In this example we are using activeMq so our pom.xml will have dependencies related to spring as well as activeMQ.
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>com.javacodegeeks.camel</groupId> <artifactId>springQuartzScheduler</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>4.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.12.0</version> </dependency> </dependencies> </project>
2. Sending and Receiving Messages without JmsTemplate
We will first start with an example of producer and consumer that works without the use of JMS Template.
We first need to start the broker. We are using ActiveMQ which acts as the JMS Provider.
BrokerLauncher:
package com.javacodegeeks.spring.jms; import java.net.URI; import java.net.URISyntaxException; import org.apache.activemq.broker.BrokerFactory; import org.apache.activemq.broker.BrokerService; public class BrokerLauncher { public static void main(String[] args) throws URISyntaxException, Exception { BrokerService broker = BrokerFactory.createBroker(new URI( "broker:(tcp://localhost:61616)")); broker.start(); } }
Output:
INFO | JMX consoles can connect to service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi INFO | PListStore:[C:\javacodegeeks_ws\springJmsTemplateExample\activemq-data\localhost\tmp_storage] started INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[C:\javacodegeeks_ws\springJmsTemplateExample\activemq-data\localhost\KahaDB] INFO | Apache ActiveMQ 5.12.0 (localhost, ID:INMAA1-L1005-59525-1448470360347-0:1) is starting INFO | Listening for connections at: tcp://127.0.0.1:61616 INFO | Connector tcp://127.0.0.1:61616 started INFO | Apache ActiveMQ 5.12.0 (localhost, ID:INMAA1-L1005-59525-1448470360347-0:1) started INFO | For help or more information please see: http://activemq.apache.org WARN | Store limit is 102400 mb (current store usage is 0 mb). The data directory: C:\javacodegeeks_ws\springJmsTemplateExample\activemq-data\localhost\KahaDB only has 29337 mb of usable space - resetting to maximum available disk space: 29337 mb WARN | Temporary Store limit is 51200 mb, whilst the temporary data directory: C:\javacodegeeks_ws\springJmsTemplateExample\activemq-data\localhost\tmp_storage only has 29337 mb of usable space - resetting to maximum available 29337 mb.
Here is the producer bean. You can see we need to create connection factory, get the connection, session, create destination etc.
JmsProducer:
package com.javacodegeeks.spring.jms; import java.net.URISyntaxException; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; public class JmsProducer { public static void main(String[] args) throws URISyntaxException, Exception { Connection connection = null; try { // Producer ConnectionFactory connectionFactory = new ActiveMQConnectionFactory( "tcp://localhost:61616"); connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = session.createQueue("customerQueue"); MessageProducer producer = session.createProducer(queue); String payload = "SomeTask"; Message msg = session.createTextMessage(payload); System.out.println("Sending text '" + payload + "'"); producer.send(msg); session.close(); } finally { if (connection != null) { connection.close(); } } } }
Output:
Sending text 'SomeTask'
Consumer also needs a connection factory, connection, session and destination objects just like its counterpart.
JmsConsumer:
package com.javacodegeeks.spring.jms; import java.net.URISyntaxException; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.MessageConsumer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnectionFactory; public class JmsConsumer { public static void main(String[] args) throws URISyntaxException, Exception { Connection connection = null; ConnectionFactory connectionFactory = new ActiveMQConnectionFactory( "tcp://localhost:61616"); connection = connectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); try { Queue queue = session.createQueue("customerQueue"); // Consumer MessageConsumer consumer = session.createConsumer(queue); TextMessage textMsg = (TextMessage) consumer.receive(); System.out.println(textMsg); System.out.println("Received: " + textMsg.getText()); } finally { if (session != null) { session.close(); } if (connection != null) { connection.close(); } } } }
Output:
ActiveMQTextMessage {commandId = 5, responseRequired = true, messageId = ID:INMAA1-L1005-59616-1448470447765-1:1:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:INMAA1-L1005-59616-1448470447765-1:1:1:1, destination = queue://customerQueue, transactionId = null, expiration = 0, timestamp = 1448470448008, arrival = 0, brokerInTime = 1448470448010, brokerOutTime = 1448470613044, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@d7b1517, marshalledProperties = null, dataStructure = null, redeliveryCounter = 1, size = 0, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = SomeTask} Received: SomeTask
3. Configuring JmsTemplate
JmsTemplate
takes care of creating a connection, obtaining a session, and the actual sending and receiving of messages. Let’s configure JmsTemplate
.
To use JmsTemplate, we’ll need to declare it as a bean in the Spring configuration XML.
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> </bean>
JmsTemplate
is only a helper class so it still needs to know how to get connections to the message broker.
ConnectionFactory
bean is configured and JmsTemplate
refers to the configured connection factory bean.
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean>
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> <bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="messageQueue1" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> </bean> <bean id="springJmsProducer" class="com.javacodegeeks.spring.jms.SpringJmsProducer"> <property name="destination" ref="messageDestination" /> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> <bean id="springJmsConsumer" class="com.javacodegeeks.spring.jms.SpringJmsConsumer"> <property name="destination" ref="messageDestination" /> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> </beans>
If you have noticed in the spring XML file above, we have also configured the producer and consumer bean. Both consumer and produce beans need JmsTemplate
bean and the destination. JMS Destination is the queue the message will be sent to.
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="messageQueue1" /> </bean>
The destination bean is injected through setter injection to both the producer and consumer beans.
4. Using JMSTemplate to produce messages
Let’s now look into the producer bean’s sendMessage(msg)
method. It in turn calls JmsTemplate.send()
method. The first parameter to the send()
method is the name of the JMS Destination that the message will be sent to and the second parameter is an implementation of MessageCreator
which contains the callback method createMessage()
that JmsTemplate
will use to construct the message that will be sent. Since JmsTemplate
has access to the JMS provider’s connection factory, it takes care of obtaining a JMS connection and session and will send the message on behalf of the sender.
SpringJmsProducer:
package com.javacodegeeks.spring.jms; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; public class SpringJmsProducer { private JmsTemplate jmsTemplate; private Destination destination; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public Destination getDestination() { return destination; } public void setDestination(Destination destination) { this.destination = destination; } public void sendMessage(final String msg) { System.out.println("Producer sends " + msg); jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage(msg); }}); } }
We have seen the producer, let’s now look into the consumer code and see how we can make use of JmsTemplate
.
5. Using JMSTemplate to consume messages
In order to receive the message, we need to call JmsTemplate.receive(destination)
method which takes in the destination. One can also call just the receive()
method without any destination in which case the default destination will be used. We will see in our next section how one can configure a default destination. JmsTemplate
will make use of the connection factory to obtain the connection and session object.
receive()
will block until a message appears on the destination, waiting forever. Its a good practice to specify a receive timeout instead so that receive()
call returns back after the specified time out. receiveTimeout
property is used to set the timeout.
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> </bean>
SpringJmsConsumer:
package com.javacodegeeks.spring.jms; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.TextMessage; import org.springframework.jms.core.JmsTemplate; public class SpringJmsConsumer { private JmsTemplate jmsTemplate; private Destination destination; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public Destination getDestination() { return destination; } public void setDestination(Destination destination) { this.destination = destination; } public String receiveMessage() throws JMSException { TextMessage textMessage = (TextMessage) jmsTemplate.receive(destination); return textMessage.getText(); } }
6. Complete JmsTemplate example to send/receive messages
Let’s now combine the producer and consumer to send and receive message.
- Make sure the broker is started.
- First we load the application context.
- Next, we get the producer bean from the spring container.
- We use the producer bean to send messages.
- Next, we load the consumer bean.
- We will then use the consumer bean to receive messages.
SpringJmsTemplateExample:
package com.javacodegeeks.spring.jms; import java.net.URISyntaxException; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringJmsTemplateExample { public static void main(String[] args) throws URISyntaxException, Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext.xml"); try { SpringJmsProducer springJmsProducer = (SpringJmsProducer) context .getBean("springJmsProducer"); springJmsProducer.sendMessage("SomeTask"); SpringJmsConsumer springJmsConsumer = (SpringJmsConsumer) context .getBean("springJmsConsumer"); System.out.println("Consumer receives " + springJmsConsumer.receiveMessage()); } finally { context.close(); } } }
Output:
Producer sends SomeTask Consumer receives SomeTask
7. JmsTemplate with Default destination
If our scenario demands of a default destination then we can avoid explicitly injecting destination separately to each producer and consumer bean and instead inject it into JmsTemplate
bean. We can do this using the propertydefaultDestination
.
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> <property name="defaultDestination" ref="messageDestination" /> </bean>
We can remove the destination properties from the producer and consumer bean declarations.
appContextWithDefaultDestin.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> <bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="messageQueue1" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> <property name="defaultDestination" ref="messageDestination" /> </bean> <bean id="springJmsProducer" class="com.javacodegeeks.spring.jms.SpringJmsProducer"> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> <bean id="springJmsConsumer" class="com.javacodegeeks.spring.jms.SpringJmsConsumer"> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> </beans>
Since the JmsTemplate
has reference to a default destination, we can simply call jmsTemplate.send(messageCreator)
without passing in the destination. This form of the send()
method only takes a MessageCreator
object. With no destination specified, JmsTemplate
will assume that you want the message sent to the default destination.
SpringJmsProducer:
package com.javacodegeeks.spring.jms; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; public class SpringJmsProducer { private JmsTemplate jmsTemplate; private Destination destination; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public Destination getDestination() { return destination; } public void setDestination(Destination destination) { this.destination = destination; } public void sendMessage(final String msg) { System.out.println("Producer sends " + msg); if (destination == null) { jmsTemplate.send(new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage(msg); } }); } else { jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage(msg); } }); } } }
Likewise, the consumer bean is modified to call jmsTemplate.receive()
which doesn’t take any destination value.
SpringJmsConsumer:
package com.javacodegeeks.spring.jms; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.TextMessage; import org.springframework.jms.core.JmsTemplate; public class SpringJmsConsumer { private JmsTemplate jmsTemplate; private Destination destination; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public Destination getDestination() { return destination; } public void setDestination(Destination destination) { this.destination = destination; } public String receiveMessage() throws JMSException { TextMessage textMessage; if (destination == null) { textMessage = (TextMessage) jmsTemplate.receive(); } else { textMessage = (TextMessage) jmsTemplate.receive(destination); } return textMessage.getText(); } }
We will now modify our previous example of sending and receiving message through JmsTemplate so that it uses the default destination configuration.
SpringJmsTemplateDefaultDestinExample:
package com.javacodegeeks.spring.jms; import java.net.URISyntaxException; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringJmsTemplateDefaultDestinExample { public static void main(String[] args) throws URISyntaxException, Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "appContextWithDefaultDestin.xml"); try { SpringJmsProducer springJmsProducer = (SpringJmsProducer) context .getBean("springJmsProducer"); springJmsProducer.sendMessage("SomeTask"); SpringJmsConsumer springJmsConsumer = (SpringJmsConsumer) context .getBean("springJmsConsumer"); System.out.println("Consumer receives " + springJmsConsumer.receiveMessage()); } finally { context.close(); } } }
Output:
Producer sends SomeTask Consumer receives SomeTask
8. JmsTemplate with MessageConverter
Think of a scenario where we have to send and receive custom objects, in such cases, if you are actual payload object is different from the custom object then you will end up with some conversion code that will manage the conversion of custom object to JMS message Object and from JMS message object to custom object. If we have to do this at multiple points in your application, then there is a possibility that we will end up with duplication of code. Spring supports message conversion through its MessageConverter
interface:
MessageConverter:
public interface MessageConverter { public Message toMessage(Object object, Session session); public Object fromMessage(Message message); }
In our example, the custom object is a simple Person
bean.
Person:
package com.javacodegeeks.spring.jms; public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public Integer getAge() { return age; } public String toString() { return "Person: name(" + name + "), age(" + age + ")"; } }
Here is our converter which converts Person
to MapMessage
and manufactures Person
from MapMessage
. JmsTemplate
interacts with this message converter, for sending messages, toMessage()
is called to convert an object to a Message. On the receiving the message, the fromMessage()
method is called to convert an incoming Message into an Object.
PersonMessageConverter:
package com.javacodegeeks.spring.jms; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.Session; import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConverter; public class PersonMessageConverter implements MessageConverter{ public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException { Person person = (Person) object; MapMessage message = session.createMapMessage(); message.setString("name", person.getName()); message.setInt("age", person.getAge()); return message; } public Object fromMessage(Message message) throws JMSException, MessageConversionException { MapMessage mapMessage = (MapMessage) message; Person person = new Person(mapMessage.getString("name"), mapMessage.getInt("age")); return person; } }
Instead of explicitly calling JmsTemplate.send()
, we now call JmsTemplate.convertAndSend()
method which takes in the Person
object itself.
SpringJmsPersonProducer:
package com.javacodegeeks.spring.jms; import org.springframework.jms.core.JmsTemplate; public class SpringJmsPersonProducer { private JmsTemplate jmsTemplate; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public void sendMessage(final Person person) { getJmsTemplate().convertAndSend(person); } }
Likewise, on the receiving end, we won’t need to call fromMessage()
to convert the message returned from JmsTemplate’s receive()
. Instead, we’ll now call JmsTemplate.receiveAndConvert()
. which receives the message from default destination and converts the message to the custom object.
SpringJmsPersonConsumer:
package com.javacodegeeks.spring.jms; import javax.jms.JMSException; import org.springframework.jms.core.JmsTemplate; public class SpringJmsPersonConsumer { private JmsTemplate jmsTemplate; public JmsTemplate getJmsTemplate() { return jmsTemplate; } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public Person receiveMessage() throws JMSException { Person person = (Person) getJmsTemplate().receiveAndConvert(); return person; } }
9. Configuring MessageConverter
Finally we have to associate the message converter with the JmsTemplate
bean. Let’s configure it as a in Spring. The following XML will handle that:
<bean id="personMessageConverter" class="com.javacodegeeks.spring.jms.PersonMessageConverter" />
Next, the JmsTemplate bean needs to be fixed, we’ll wire the personMessageConverter
bean into JmsTemplate’s messageConverter
property.
appContextWithMessageConverter.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> <bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="messageQueue1" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="receiveTimeout" value="10000" /> <property name="defaultDestination" ref="messageDestination" /> <property name="messageConverter" ref="personMessageConverter" /> </bean> <bean id="personMessageConverter" class="com.javacodegeeks.spring.jms.PersonMessageConverter" /> <bean id="springJmsPersonProducer" class="com.javacodegeeks.spring.jms.SpringJmsPersonProducer"> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> <bean id="springJmsPersonConsumer" class="com.javacodegeeks.spring.jms.SpringJmsPersonConsumer"> <property name="jmsTemplate" ref="jmsTemplate" /> </bean> </beans>
Let’s now test the producer/consumer example by sending a person object.
SpringJmsMessageConverterExample:
package com.javacodegeeks.spring.jms; import java.net.URISyntaxException; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringJmsMessageConverterExample { public static void main(String[] args) throws URISyntaxException, Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "appContextWithMessageConverter.xml"); try { SpringJmsPersonProducer springJmsProducer = (SpringJmsPersonProducer) context .getBean("springJmsPersonProducer"); Person joe = new Person("Joe", 32); System.out.println("Sending person " + joe); springJmsProducer.sendMessage(joe); SpringJmsPersonConsumer springJmsConsumer = (SpringJmsPersonConsumer) context .getBean("springJmsPersonConsumer"); System.out.println("Consumer receives " + springJmsConsumer.receiveMessage()); } finally { context.close(); } } }
Output:
Sending person Person: name(Joe), age(32) Consumer receives Person: name(Joe), age(32)
10. Download the Eclipse Project
This was an example about spring JMSTemplate.
You can download the full source code of this example here: springJmsTemplateExample.zip
where is the SpringJmsGatewayProducer class?
Thanks