Don't Use JmsTemplate in Spring!

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:

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



Future Gains




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

 

 

 

 

Top