Gordon Dickens is an instructor, mentor & consultant. Gordon is currently architecting and teaching several official SpringSource courses and actively tweets about open source technology at http://twitter.com/gdickens. Gordon is active within the Spring Framework community focussed on: Spring training, Spring Roo, Spring Integration, Spring Batch and Eclipse Virgo OSGi projects. Gordon is a DZone MVB and is not an employee of DZone and has posted 39 posts at DZone. You can read more from them at their website. View Full User Profile

Don't Use JmsTemplate in Spring!

04.21.2011
| 30035 views |
  • submit to reddit

JmsTemplate is easy for simple message sending. What if we want to add headers, intercept or transform the message? Then we have to write more code. So, how do we solve this common task with more configurability in lieu of more code? First, lets review JMS in Spring.

Spring JMS Options

  1. JmsTemplate – either to send and receive messages inline
    1. Use send()/convertAndSend() methods to send messages
    2. Use receive()/receiveAndConvert() methods to receive messages. BEWARE: these are blocking methods! If there is no message on the Destination, it will wait until a message is received or times out.
  2. MessageListenerContainer – Async JMS message receipt by polling JMS Destinations and directing messages to service methods or MDBs

Both JmsTemplate and MessageListenerContainer have been successfully implemented in Spring applications, if we have to do something a little different, we introduce new code. What could possibly go wrong?


Future Extensibility?

On many projects new use-cases arise, such as:

  • Route messages to different destinations, based on header values or contents?
  • Log the message contents?
  • Add header values?
  • Buffer the messages?
  • Improved response and error handling?
  • Make configuration changes without having to recompile?
  • and more…

Now we have to refactor code and introduce new code and test cases, run it through QA, etc. etc.



A More Configurable Solution!

It is time to graduate Spring JmsTemplate and play with the big kids. We can easily do this with a Spring Integration flow.


How it is done with Spring Integration

Here we have a diagram illustrating the 3 simple components to Spring Integration replacing the JmsTemplate send.

  1. Create a Gateway interface – an interface defining method(s) that accept the type of data you wish to send and any optional header values.
  2. Define a Channel – the pipe connecting our endpoints
  3. Define an Outbound JMS Adapter – sends the message to your JMS provider (ActiveMQ, RabbitMQ, etc.)

Simply inject this into our service classes and invoke the methods.



Immediate Gains
  • Add header & header values via the methods defined in the interface
  • Simple invokation of Gateway methods from our service classes
  • Multiple Gateway methods
  • Configure method level or class level destinations



Future Gains
  • Change the JMS Adapter (one-way) to a JMS Gateway (two-way) to processes responses from JMS
  • We can change the channel to a queue (buffered) channel
  • We can wire in a transformer for message transformation
  • We can wire in additional destinations, and wire in a “header (key), header value, or content based” router and add another adapter
  • We can wire in other inbound adapters receiving data from another source, such as SMTP, FTP, File, etc.
  • Wiretap the channel to send a copy of the message elsewhere
  • Change the channel to a logging adapter channel which would provide us with logging of the messages coming through
  • Add the “message-history” option to our SI configuration to track the message along its route
  • and more…



Optimal JMS Send Solution

The Spring Integration Gateway Interface

Gateway provides a one or two way communication with Spring Integration. If the method returns void, it is inherently one-way.

The interface MyJmsGateway, has one Gateway method declared sendMyMessage(). When this method is invoked by your service class, the first argument will go into a message header field named “myHeaderKey”, the second argument goes into the payload.

package com.gordondickens.sijms;

import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.Header;

public interface MyJmsGateway {
@Gateway
public void sendMyMessage(@Header("myHeaderKey") String s, Object o);
}
Spring Integration Configuration

Because the interface is proxied at runtime, we need to configure in the Gateway via 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"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xmlns:si="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration/jms

http://www.springframework.org/schema/integration/jms/spring-integration-jms-2.0.xsd

http://www.springframework.org/schema/integration

http://www.springframework.org/schema/integration/spring-integration-2.0.xsd

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<import resource="classpath:META-INF/spring/amq-context.xml"/>

<!-- Pickup the @Gateway annotation -->
<si:annotation-config/>

<si:poller default="true">
<si:interval-trigger interval="500"/>
</si:poller>

<!-- Define the channel (pipe) connecting the endpoints -->
<si:channel id="myRequestChannel"/>

<!-- Configure the Gateway to Send on the channel -->
<si:gateway id="myJmsGateway"
service-interface="com.gordondickens.sijms.MyJmsGateway"
default-request-channel="myRequestChannel"/>

<!-- Send message to JMS -->
<jms:outbound-channel-adapter channel="myRequestChannel"
connection-factory="connectionFactory"
destination="my.inbound.queue"/>
</beans>
Sending the Message
package com.gordondickens.sijms;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("classpath:/com/gordondickens/sijms/JmsSenderTests-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JmsSenderTests {

@Autowired
MyJmsGateway myJmsGateway;

@Test
public void testJmsSend() {
myJmsGateway.sendMyMessage("myHeaderValue", "MY PayLoad");
}
}



Summary

  • Simple implementation
  • Invoke a method to send a message to JMS – Very SOA eh?
  • Flexible configuration
  • Reconfigure & restart WITHOUT recompiling – SWEET!
Published at DZone with permission of Gordon Dickens, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Javin Paul replied on Fri, 2011/04/22 - 11:32pm

Nice article , you have indeed covered topic in details with sample code  and graphics and Test Case and provided an alternative of JmsTemplate in Spring. Though I have used JMS a while ago but I see your solution offers more flexiblity than spring JmsTemplate itself.

Javin
Tibco Rendezvous messaging Tutorial

Marco Tedone replied on Sat, 2011/04/23 - 4:17am

Great article Gordon! I will look into Spring integration. Have you measured what are the performance gains/penalties for using Spring integration vs JmsTemplate?

joshua long replied on Tue, 2011/04/26 - 5:51am

Great post! Though, you could've made the title something a little less scary - like "Use Spring Integration for more Flexibility than JmsTemplate," or something ;-) Anyway, nicely done! And I like the explanation about the Gateway support, too!

Gérald Quintana replied on Wed, 2011/04/27 - 3:32am

I have gone exactly the other way from Spring Integration to Spring JMS, because:

  • It's hard to debug: follow the message in the channel chain to find the problem is a nightmare. Using Spring Integration annotations makes things even worse since, you loose the global picture of message road.
  • It's verbose: I prefer 10 lines of Java code to 30 lines of XML config.
  • Transformation, Error handling and Logging can also be done with Spring JMS
  • If you don't pay attention: Threads introduce Concurrency and Transactions problems in Spring Integration. 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.