简体   繁体   中英

WebSphere MQ connection pooling with Tomcat

Tomcat has a built-in JDBC connection pooling, but unfortunately no built-in JMS connection pooling.

We are migrating a legacy Tomcat web application from WebSphere MQ version 6 to 7. Unfortunately, connection pooling has been removed in WebSphere MQ 7 as described here: http://www-01.ibm.com/support/docview.wss?uid=swg21665128

Now we are afraid that we will run into troubles if we just use the following code for configuring MQ in Tomcat:

        <Resource name="jms/XXXQCF" auth="Container"
            type="com.ibm.mq.jms.MQQueueConnectionFactory" factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
            description="JMS Queue Connection Factory"
            HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
            QMGR="XXX" />

The reason for our concerns is that this will not use a pooled JMS provider when using MQ 7. For details, see also http://activemq.apache.org/jmstemplate-gotchas.html

Alternative solutions we see are:

1) Use of Atomikos

Atomikos has a com.atomikos.jms.AtomikosConnectionFactoryBean that can be used instead of MQQueueConnectionFactory But using an XA transaction manager is a huge overhead when we don't need XA

2) Use Spring's CachingConnectionFactory

looks like a good solution, but unfortunately our legacy application does not use Spring. So we assume that using CachingConnectionFactory would mean quite some effort.

3) Use Apache Commons Pool

looks promising too, but implementing it correctly for JMS will require some good JMS knowledge

Our questions:

  • is there a JMS provider that can be used to wrap MQQueueConnectionFactory and that will pool connections, sessions, producers and consumers?
  • did anyone succeed in implementing one of the alternative solutions we outlined above?

As proposed by Umapathy in option 3, we now opted for the approach using Spring's CachingConnectionFactory, and it works well even for a non-Spring application. All you need to do is add the Spring Jars to the classpath, and wrap the MQQueueConnectionFactory with a CachingConnectionFactory.

We chose to create our own Tomcat QueueConnectionFactoryFactory that enables us to leave the original application code completely untouched, you just need to replace the original MQ connection factory from the Tomcat configuration file (shown above in the question) with the following XML definition:

<Resource name="jms/XXXQCF" auth="Container"
          type="org.springframework.jms.connection.CachingConnectionFactory"
          factory="at.rsf4j.core.utilities.RSFCachingMQQueueConnectionFactoryFactory"
          description="JMS Queue Connection Factory"
          HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
          QMGR="XXX" />

Here is the (simplified) code (without error checking) for the RSFCachingMQQueueConnectionFactoryFactory:

public class RSFCachingMQQueueConnectionFactoryFactory implements ObjectFactory{

public Object getObjectInstance (Object obj, Name name, Context nameCtx, Hashtable<?,?> environment)
    throws NamingException {
            Reference ref = (Reference) obj;
            String beanClassName = ref.getClassName();
            Class<?> beanClass = Class.forName(beanClassName);
            if (CachingConnectionFactory.class.isAssignableFrom(beanClass)){
                MQQueueConnectionFactoryFactory cff = new MQQueueConnectionFactoryFactory();
                Reference mqReference = new Reference(
                        MQQueueConnectionFactory.class.getName());

                Enumeration<RefAddr> allAddrs = ref.getAll();
                while (allAddrs.hasMoreElements()){
                    mqReference.add(allAddrs.nextElement());
                }

                MQQueueConnectionFactory cf = (MQQueueConnectionFactory)cff.getObjectInstance(mqReference, name, nameCtx, environment);
                CachingConnectionFactory ccf = (CachingConnectionFactory)beanClass.newInstance();
                ccf.setTargetConnectionFactory(cf);
                return ccf;
            }
        }

I think you already know the answer.

option 1: Go with a Java EE application server. This has inbuilt JMS pools.

option 2: If this is a simple application that does a single job (like putting on a queue with fire and forget type), you can connect to the JMS provider (qmgr) and create the producer or consumer in the servlet_init method. This means the legacy code needs an update. You also need to take care of the reconnect when something breaks as JMS reconnect properties doesn't kick off a reconnection (as far as my experience goes).

option 3: I have gone with Spring's CachingConnectionFactory when the application happens to be more than a little complex than one in option 2. I have applications that use JMSTemplate and some doesn't.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM