Using Spring JMS

Last week I discovered a piece of code in our project that didn’t use Spring yet. It was the piece of code that used JMS to access some JBoss queues. Since we did use Spring everywhere in our code where it was possible, I decided to rewrite this piece so it also uses Spring (and so it also profits of all the advantages that Spring offers).
The original code looked like this:

package net.pascalalma.jms;

import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class QueueBase {

   private static final Log LOG = LogFactory.getLog(QueueBase.class);

   private static InitialContext context = null;
   private static QueueConnectionFactory qcf = null;
   private boolean transacted;
   private int ackMode;
   private String queueName;
   private boolean init = false;
   protected QueueConnection qc;
   protected QueueSession qs;
   protected Queue queue;
   protected QueueReceiver qr;

   protected MessageProducer mp;

   protected void init() throws QueueException {
      LOG.info("Queue init started");
      if (context == null) {

         try {
            LOG.info("QueueBase: creating InitialContext for queues (see jndi.properties)");
            context = new InitialContext();
            qcf = (QueueConnectionFactory) context.lookup("/ConnectionFactory");
         } catch (Exception e) {
            throw new QueueException(e);
         }
      }

      try {
         queue = (Queue) context.lookup(queueName);
         qc = qcf.createQueueConnection();
         qs = qc.createQueueSession(transacted, ackMode);

         assert (queue != null) : "queue is null";
         assert (qc != null) : "qc is null";
         assert (qs != null) : "qs is null";

         init = true;
      } catch (Exception e) {
         throw new QueueException(e);
      }
   }

   public QueueBase() {
      this.init = false;
   }

   public QueueBase(String queueName) {
      this.queueName = queueName;
      this.init = false;
   }

   /**
    * Constructor
    */
   public QueueBase(String queueName, boolean transacted, int ackMode) throws QueueException {
      this.queueName = queueName;
      this.transacted = transacted;
      this.ackMode = ackMode;
      this.init = false;
   }

   /**
    * Sends a message to the queue..
    *
    * @param msg
    * @throws QueueException
    */
   public void send(BulkMessage msg) throws QueueException {
      try {
         if ((qs != null) && (mp != null)) {
            ObjectMessage om = qs.createObjectMessage(msg);
            mp.send(om);
         } else {
            LOG.error("Queue is not running / mp==null ....");
         }
      } catch (JMSException e) {
         throw new QueueException(e);
      }
   }

   /*
    * Get / Receive a message from the queue The receive block at most
    *  miliseconds (0=block)
    */
   public ObjectMessage receive(long timeoutInMilisecs) throws QueueException {

      BulkMessage m = null;
      ObjectMessage om = null;
      try {
         if (qr != null) {
            om = (ObjectMessage) qr.receive(timeoutInMilisecs);
            if (om != null) {
               m = (BulkMessage) om.getObject();
            }
         } else {
            throw new QueueException("Queue receiver is null");
         }
      } catch (JMSException e) {
         throw new QueueException(e);
      }
      return om;
   }

   public QueueBrowser getBrowser() throws JMSException, QueueException {
      return qs.createBrowser(queue);
   }

   /**
    *
    */
   public void destroy() {
      try {
         if (qs != null) {
            qs.close();
            qs = null;
         }
         if (qc != null) {
            qc.stop();
            qc.close();
            qc = null;
         }
      } catch (Exception e) {
         LOG.error(e);
      }
   }

   public String getQueueName() {
      return queueName;
   }

   public void setQueueName(String queueName) {
      this.queueName = queueName;
   }

   public int getAckMode() {
      return ackMode;
   }

   public void setAckMode(int ackMode) {
      this.ackMode = ackMode;
   }

   public Queue getQueue() {
      return queue;
   }

   public void setQueue(Queue queue) {
      this.queue = queue;
   }

   public boolean isTransacted() {
      return transacted;
   }

   public void setTransacted(boolean transacted) {
      this.transacted = transacted;
   }

}

It was doing a JNDI lookup of the queuefactory, created a new session and started and closed it.
After I rewrote it to use Spring it looked like:

package nnet.pascalalma.jms;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

public class QueueBaseService {

   private static final Log LOG = LogFactory.getLog(QueueBaseService.class);

   private JmsTemplate jmsTemplate = null;

   public void setJmsTemplate(JmsTemplate jmsTemplate) {
      this.jmsTemplate = jmsTemplate;
   }

   public JmsTemplate getJmsTemplate() {
      return jmsTemplate;
   }

   public void sendMessage(final BulkMessage bm) {
      LOG.debug("sendMessage(final BulkMessage bm)");

      jmsTemplate.send( new MessageCreator() {
         public Message createMessage(Session session) throws JMSException {
            ObjectMessage om = session.createObjectMessage();
            om.setObject(bm);
            return om;
         }
      });
   }

   public ObjectMessage receiveMessage() {
      return (ObjectMessage)jmsTemplate.receive();

   }

   public String getQueueName() {
      return jmsTemplate.getDefaultDestinationName();
   }

   public void setAcknowledgeMode(int mode){
      jmsTemplate.setSessionAcknowledgeMode( mode);
   }

}

And of course some configuration of the queues and JNDI/JMS has to be done.

So not only does it take less code to achieve the same thing, it is also much less error prone. At least, that was what I would expect, but when I got back in the office one day later, started my machine and ran some unit tests (after synchronizing my code) I was running into an error:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jmsConnectionFactory' defined in class path resource [jmsContext.xml]: Initialization of bean failed; nested exception is java.lang.NoSuchFieldError: doPruning
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:355)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:176)
	... 33 more
Caused by: java.lang.NoSuchFieldError: doPruning
	at org.jboss.aop.AOPClassPool.(AOPClassPool.java:49)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	at org.jboss.aop.instrument.InstrumentorFactory.class$(InstrumentorFactory.java:44)
	at org.jboss.aop.instrument.InstrumentorFactory.(InstrumentorFactory.java:44)
	at org.jboss.aop.AspectManager$2.run(AspectManager.java:316)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.jboss.aop.AspectManager.instance(AspectManager.java:261)
	at org.jboss.aop.AspectManager.instance(AspectManager.java:254)
	at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate.(ClientConnectionFactoryDelegate.java)
	at sun.misc.Unsafe.ensureClassInitialized(Native Method)
	at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(Unknown Source)
	at sun.reflect.ReflectionFactory.newFieldAccessor(Unknown Source)
	at java.lang.reflect.Field.acquireFieldAccessor(Unknown Source)
	at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
	at java.lang.reflect.Field.getLong(Unknown Source)
	at java.io.ObjectStreamClass.getDeclaredSUID(Unknown Source)
	at java.io.ObjectStreamClass.access$600(Unknown Source)
	at java.io.ObjectStreamClass$2.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.io.ObjectStreamClass.(Unknown Source)
	at java.io.ObjectStreamClass.lookup(Unknown Source)
	at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
	at java.io.ObjectInputStream.readClassDesc(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
	at java.io.ObjectInputStream.readSerialData(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at java.rmi.MarshalledObject.get(Unknown Source)
	at org.jnp.interfaces.MarshalledValuePair.get(MarshalledValuePair.java:72)
	at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:652)
	at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:587)
	at javax.naming.InitialContext.lookup(Unknown Source)
	at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:123)
	at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:85)
	at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:121)
	at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:71)
	at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106)
	at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:125)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1003)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:348)
	... 36 more

After a short search it appeared two libraries were conflicting: I had both javassist-3.0.jar and javassist-3.2.jar at my classpath. It is only working with the 3.2 version from within Eclipse. If you’re using the older version you will get the error stack . So after solving this final issue the code not only looks better, it is also much easier to maintain.

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in JMS, Spring Framework and tagged . Bookmark the permalink.

4 Responses to Using Spring JMS

  1. acnesiac says:

    Could you post the code for QueueException and BulkMessage ?

  2. EDH says:

    Were did the original code for the QueueBrowser go ? How would you tackle that using jmsTemplate ?

  3. Pascal Alma says:

    Acnesiac,

    These classes you mention are really pretty straight forward, therefor I didn’t post them in the Blog.
    The QueueException is extending the Exception class and the BulkMessage is just a javabean holding some values I wanted to be put on the queue. Hope this helps.

  4. Pascal Alma says:

    EDH,

    The queuebrowser was only used to obtain the number of messages in the queue. I chose another way to tackle that issue, see this post: https://pragmaticintegrator.wordpress.com/2007/05/07/using-a-jboss-mbean-in-your-web-application/

Comments are closed.