Tuesday 4 June 2013

JMS, Spring & MQ

Here is a sample configuration for using MQ with JMS and Spring.

Firstly the MQ library is needed.  This can be found in a number of places but if you are using eclipse you can install the Websphere Liberty Profile (8.5.next) from the market place.  The required MQ library is available in

    <liberty_install_dir>/lib/com.ibm.ws.messaging.jms.wmq_1.0.jar

Maven Dependencies

The MQ jar can be used as a maven dependency using the System scope using the install directory above.

    <dependency>
        <groupId>com.ibm.ws.messaging.jms</groupId>
        <artifactId>wmq</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath><liberty_install_dir>/lib/com.ibm.ws.messaging.jms.wmq_1.0.jar</systemPath>
    </dependency>

The JMS interfaces are all that is necessary if installing into an application server / container.

    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
    </dependency>

Note that this is a provided dependency because the implementation of these interfaces are provided by the container.  If the app is standalone and isn't deployed into a container then concrete implementations of the JMS spec needs to be provided.  One example is the geronimo library,

    <dependency>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-jms_1.1_spec</artifactId>
        <version>1.1.1</version>
        <scope>compile</scope>
    </dependency>

And finally the spring jms jar is also required,

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>3.1.1.RELEASE</version>
        <scope>compile</scope>
    </dependency>

(Note: you'll need to add a spring property place holder for the properties or replace the ${...} values in the following examples)

JMS Connection Factory

The JMS connection factory is dependent on the queue / topic implementation that is being used.  In this case this is IBM MQ,

    <bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
        <property name="hostName" value="${ip_address}" />
        <property name="port" value="${port_number}" />
        <property name="queueManager" value="${queue_manager_name}" />
        <property name="transportType" value="1" />
    </bean>
    <bean id="jmsDestination" class="com.ibm.mq.jms.MQQueue">
        <constructor-arg value="${queue_name}" />
    </bean>

This will configure a Queue based connection.  To create a topic based connection is just as simple.  Just replace the 'Queue' with 'Topic' in the configuration above.  This gives,

    <bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQTopicConnectionFactory">
        <property name="hostName" value="${ip_address}" />
        <property name="port" value="${port_number}" />
        <property name="queueManager" value="${queue_manager_name}" />
        <property name="transportType" value="1" />
    </bean>
    <bean id="jmsDestination" class="com.ibm.mq.jms.MQTopic">
        <constructor-arg value="${topic}" />
    </bean>

Spring Connection Factory

The spring connection factory wraps the JMS Connection Factory.  There are a number of connection factories available.  If this is a simple connection with no authentication then just use,

    <bean id="springConnectionFactory"
            class="org.springframework.jms.connection.SingleConnectionFactory">
        <property name="targetConnectionFactory" ref="jmsConnectionFactory" />
    </bean>

otherwise if credentials are required use,

    <bean id="springConnectionFactory"
            class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
        <property name="targetConnectionFactory" ref="jmsConnectionFactory" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

Spring JMS Template

The JMS template does a lot of the boilerplate code required to open and close connections.  It is very like JdbcTemplate from that point of view.

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="springConnectionFactory" />
        <property name="defaultDestination" ref="jmsDestination" />
    </bean>

Message Driven Bean

A message driven bean is triggered when a message is received by the JMS system.  Spring maintains this relationship and in its simplest form it is exactly the same as the standard JMS setup,

    <!-- Message driven bean -->
    <bean id="sampleMessageDrivenBean" class="com.me.SampleMessageDrivenBean">
    </bean>

    <!-- and this is the message listener container -->
    <bean id="springJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="springConnectionFactory" />
        <property name="destination" ref="jmsDestination" />
        <property name="messageListener" ref="sampleMessageDrivenBean" />
    </bean>

In this case the message driven bean just implements the standard MessageListener interface from JMS,

public class SampleMessageDrivenBean implements MessageListener
{
    @Override
    public void onMessage(Message message)
    {
        System.out.println("Message Driven Bean: New Message");
        System.out.println(message.toString());
    }
}

Spring also provides a session message listener which provides the session object in the onMessage() call.

Sending Messages

Creating a class to send a message is very easy too.  Using spring to inject the JmsTemplate and Destination leaves a class to send messages as,


public class MessageSender
{
    
    /**
     * JMSTemplate.
     */
    private JmsTemplate jmsTemplate;
    
    /**
     * JMSTemplate.
     */
    private Destination jmsDestination;

    /**
     * Send a message.
     */
    public void sendMessage()
    {
        jmsTemplate.send(jmsDestination, new MessageCreator()
        {
            @Override
            public Message createMessage(Session session) throws JMSException
            {
                System.out.println("Message Sent");
                final Calendar now = Calendar.getInstance();
                return session.createTextMessage(now.toString());
            }
        });
    }

    /**
     * Sets jmsTemplate to the given value.
     *
     * @param jmsTemplate the jmsTemplate to set
     */
    public void setJmsTemplate(JmsTemplate jmsTemplate)
    {
        this.jmsTemplate = jmsTemplate;
    }

    /**
     * Sets jmsDestination to the given value.
     *
     * @param jmsDestination the jmsDestination to set
     */
    public void setJmsDestination(Destination jmsDestination)
    {
        this.jmsDestination = jmsDestination;
    }
}



2 comments:

  1. Thanks a lot for your post! It was very helpful...

    Please, could you provide us the source code?
    Thanks a lot!

    ReplyDelete
    Replies
    1. Hi Jorge,

      Thanks for taking the time to leave a comment and sorry for the delay in replying.

      Are there any bits in particular that you want to see the source of? It would be quite a lot to post a full example. You could also try the 'Dynamic Spring Message Driven Beans' entry from Jan 2014 which has a few more bits in.

      Andy

      Delete