Search code examples
springconnectionwebspherejndijmstemplate

Spring JmsTemplate and connection jndi lookup on WebSphere Application Server


I'm working on sending and receiving messages to/from an IBM MQ queue using JmsTemplate. My application is installed on a WebSphere application server 8.5 and, in order to retrieve the connection, I use a jndi lookup.

My Spring beans:

<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> 
    <property name="jndiName" value="jndiTest" /> 
    <property name="lookupOnStartup" value="false" /> 
    <property name="cache" value="true" /> 
    <property name="proxyInterface" value="javax.jms.QueueConnectionFactory" /> 
</bean> 

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="jmsQueueConnectionFactory" />
    <property name="receiveTimeout" value="10000" /> 
    <property name="sessionAcknowledgeMode" value="1" /> 
</bean>

<bean id="mqServerDao" class="MqServerDao" > 
    <constructor-arg name="jmsTemplate" ref="jmsTemplate" />
</bean>

My Java class:

public class MqServerDao {

    private JmsTemplate jmsTemplate;

    public MqServerDao(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public String write(byte[] request, final String correlationId)
                                   throws Exception {

       MQQueue mqQueue = new MQQueue(MQ_INPUT_QUEUE);
       mqQueue.setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);

       MqRequestMessageCreator messageCreator = new MqRequestMessageCreator(
                                       request, correlationId);
       jmsTemplate.send(mqQueue, messageCreator);
       return messageCreator.getMessageId();
    }

    public byte[] read(String messageId, String correlationId) throws Exception {

       MQQueue mqQueue = new MQQueue(MQ_OUTPUT_QUEUE);
       mqQueue.setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);

       String messageSelector = "JMSCorrelationID = 'ID:" + correlationId
                                       + "' AND JMSMessageID = '" + messageId + "'";
       TextMessage receiveMessage = (TextMessage) jmsTemplate.receiveSelected(
                                       mqQueue, messageSelector);
       return receiveMessage.getText().getBytes();
    }
}

I wonder if this is the right way to do it and I have some questions:

  1. Is it recommended to add a CachingConnectionFactory or is it the application server itself that manages che connections?
  2. Is this the right way to use JmsTemplate? Is it safe if the "write" method of MqServerDao is called twice simultaneously? Or would I create a new JmsTemplate instance inside "write" and "read" methods?

Solution

    1. WebSphere will be managing connections for you. From http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/core/JmsTemplate.html:

    In a Java EE environment, make sure that the ConnectionFactory is obtained from the application's environment naming context via JNDI; application servers typically expose pooled, transaction-aware factories there.

    1. Spring Template classes, including JmsTemplate, are designed to be reusable, thread-safe singletons. You definitely don't want to keep creating new ones.

    I looked for an authoritative reference on this, but couldn't find one. (You'd think it would be in the above JavaDoc link. IMO, Spring's documentation often leaves a bit to be desired.) Best I could find thus far was this description of RestTemplate, which says:

    Conceptually, it is very similar to the JdbcTemplate, JmsTemplate, and the various other templates found in the Spring Framework and other portfolio projects. This means, for instance, that the RestTemplate is thread-safe once constructed, and that you can use callbacks to customize its operations.