Tuesday, December 12, 2006

Implementing JMS with Spring: Messaging Client

Last week, I described how to implement JMS, using a stand-alone client and a Message Driven Bean. In this post and the next, I will describe how to implement JMS using Spring and Message Driven POJOs. This post will describe how to create a Messaging client using Spring. The next post will describe how to implement a Message driven POJO. For this I used a simple servlet that, when invoked will send a text message "hello", to a destination queue. The Message driven pojo, listening on the queue will then receive and print the message. Follow these steps to run the example
  1. Setup the JMS environment as described in the "Configuring Weblogic JMS" post
  2. Create the Messaging client: This is a simple Java class which uses the spring JmsTemplate to send a message to the queue. The JmsTemplate can be used for message production and synchronous message reception. For asynchronous reception, Spring provides a number of message listener containers that are used to create Message-Driven POJOs (MDPs).
    public class QueueSender {
    private JmsTemplate jmsTemplate;
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
    this.jmsTemplate = jmsTemplate;
    }
    public void sendMesage() {
    jmsTemplate.send("jms/testQueue", new MessageCreator() {
    public Message createMessage(Session session) throws JMSException {
    return session.createTextMessage("Hello");
    }
    });
    }
    }
    QueueSender.java
  3. Configure the Bean in the applicationContext.xml file: The following is alisting of the applicationContext.xml file.
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
    <props>
    <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
    <prop key="java.naming.provider.url">t3://localhost:20001</prop>
    </props>
    </property>
    </bean>

    <bean id="queueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate">
    <ref bean="jndiTemplate" />
    </property>
    <property name="jndiName">
    <value>jms/connectionFactory</value>
    </property>
    </bean>

    <bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
    <property name="jndiTemplate">
    <ref bean="jndiTemplate" />
    </property>
    <property name="cache">
    <value>true</value>
    </property>
    </bean>

    <bean id="queueTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory">
    <ref bean="queueConnectionFactory" />
    </property>
    <property name="destinationResolver">
    <ref bean="jmsDestinationResolver" />
    </property>
    </bean>

    <bean id="jmsSender" class="jms.QueueSender">
    <property name="jmsTemplate">
    <ref bean="queueTemplate" />
    </property>

    </bean>
    </beans>
    WEB-INF/applicationContext.xml

    The JndiDestinationResolver class can be used to obtain the Queue destinations using the JNDI Name. The send method in JmsTemplate (see QueueSender), uses the JNDI name, which is used by the JndiDestinationResolver to obtain the appropriate destination.

  4. Create a servlet to invoke the Message Sender: The following servlet is used to invoke the QueueSender:
     public class QueueSenderServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
    QueueSender sender = (QueueSender)ctx.getBean("jmsSender");
    sender.sendMesage();
    }
    }
    QueueSenderServlet.java
  5. Update the web.xml file to add the servlet and spring application context:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>SpringJMSClientWeb</display-name>
    <servlet>
    <description></description>
    <display-name>QueueSenderServlet</display-name>
    <servlet-name>QueueSenderServlet</servlet-name>
    <servlet-class>jms.QueueSenderServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>QueueSenderServlet</servlet-name>
    <url-pattern>/QueueSenderServlet</url-pattern>
    </servlet-mapping>
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    </web-app>
    web.xml

    The listener defined web.xml (ContextLoaderListener) by default loads the applicationContext.xml file in WEB-INF directory of the web application.
In the next post, we will see how implement the Message Driven POJO to consume the message sent from here.

11 comments:

  1. Thanks for the post it was a good example.

    I have a question about throwing RuntimeException on the onMessage method.
    If a Runtime exception occurs, shouldn't the message still be in the queue, and possibly resent until the onMessage can complete without exception?
    When I throw an exception I lose the message, which isn't a good thing!

    ReplyDelete
  2. Ok, seems that I have to explicitly set the session transacted flag to true in the DefaultMessageListenContainer.

    With that flag set the message is resent until the onMessage method completes without exception.

    Thanks again for the post.

    ReplyDelete
  3. Hi,
    I tried the example and I tried to deploy it to tomcat. But during deployment I am getting this error although I have my weblogic 10 instance running on my PC.
    I included all possible jar files to the classpath.
    SEVERE: Context initialization failed
    org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is java.lang.NoClassDefFoundError: weblogic/utils/NestedException
    Caused by: java.lang.NoClassDefFoundError: weblogic/utils/NestedException

    ReplyDelete
  4. Hi,
    I tried the example and I tried to deploy it to tomcat. But during deployment I am getting this error although I have my weblogic 10 instance running on my PC.
    I included all possible jar files to the classpath.
    SEVERE: Context initialization failed
    org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is java.lang.NoClassDefFoundError: weblogic/utils/NestedException
    Caused by: java.lang.NoClassDefFoundError: weblogic/utils/NestedException

    Thanks for the example,
    Mohan

    ReplyDelete
  5. Good job! Could you post the solution on how you would consume the message using a MDP?

    ReplyDelete
  6. Hi!
    What version of Spring was used in sample?
    I'm having a NameNotFoundException in the jndiName from bean queueConnectionFactory.

    ReplyDelete
  7. Good Job!! will you please post the solution for Synchronous messaging with temporary queue and spring jms template.

    ReplyDelete
  8. chraja (chraja.sekhar@gmail.com)Thursday, November 6, 2008 at 12:40:00 PM EST

    I could use this with spring2.0. jboss messaging . I tried this from a POJO.

    Infact in abhi's sample destination is resolved in run time . i had configured my destination directly in the config file, it worked .

    ReplyDelete

Popular Posts