如果在多个 tomcat context.xml 中定义了 qmanager jndi,则 MQ JNDI 异常

MQ JNDI exception if the qmanager jndi is defined in multiple tomcat context.xml

我在同一个 tomcat jvm 中部署了 2 个应用程序。每个应用程序都有自己的 context.xml: - app1.xml - app2.xml 在每个 app*.xml 中,我定义了 MQ jndi 资源:

<!-- JMS configuration FOR MQSeries - The connection factory -->
 <Resource
      name="jms/JMSQueueConnectionFactory"
      auth="Container"
      type="com.ibm.mq.jms.MQQueueConnectionFactory"
      factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
      description="JMS Queue Connection Factory for sending messages"
      CCDTURL="file:///apps/mqm_opt/creds/AMQCLCHL_XA.TAB"
      QMGR="***Example***QM01***Example***" UCP="Y"/>

我的任何 war 中都没有 MQ 或 jms 罐子。它们在 MQ 客户端安装上。然后我在 class 路径中添加了使用 shared.loader of catalina.properties:

shared.loader=${catalina.home}/shared/*.jar,/opt/mqm/java/lib/*.jar

每个应用程序单独部署时都很好。但是当它们一起部署在同一个 jvm 中时,我在启动其中一个应用程序时得到一个空指针:

Jul 29, 2015 6:15:14 PM org.apache.naming.NamingContext lookup
WARNING: Unexpected exception resolving reference
java.lang.NullPointerException
        at com.ibm.msg.client.jms.internal.JmsFactoryFactoryImpl.getInstance(JmsFactoryFactoryImpl.java:169)
        at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.setProviderFactory(JmsConnectionFactoryImpl.java:176)
        at com.ibm.mq.jms.MQConnectionFactory.<init>(MQConnectionFactory.java:286)
        at com.ibm.mq.jms.MQQueueConnectionFactory.<init>(MQQueueConnectionFactory.java:76)
        at com.ibm.mq.jms.MQQueueConnectionFactoryFactory.getObjectInstance(MQQueueConnectionFactoryFactory.java:73)
        at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:141)
        at javax.naming.spi.NamingManager.getObjectInstance(Unknown Source)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:842)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:830)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:830)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:830)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:167)
        at org.apache.naming.SelectorContext.lookup(SelectorContext.java:156)
        at javax.naming.InitialContext.lookup(Unknown Source)
        at org.springframework.jndi.JndiTemplate.doInContext(JndiTemplate.java:154)
        at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
        at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
        at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178)
        at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
        at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:105)
        at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:201)
        at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:187)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483)

所以 app1 工作正常,但 app2 失败并出现上述异常。我通常注意到这种错误是在我们遇到 class 加载问题时出现的。但在这种情况下,我无法弄清楚问题出在哪里。任何线索都会有所帮助。

编辑:经过一些测试后我发现,如果 tomcat 首先加载 app1 那么这两个应用程序都可以正常工作。但是如果它选择 app2 那么就会有问题。 app2 似乎正在使用错误的 class 加载程序加载 MQ classes。不知道为什么。我检查了 war。它不包含 MQ classes.

根据 http://tomcat.apache.org/tomcat-8.0-doc/jndi-resources-howto.html#Using_resources 中的注释,我会建议 TomCat 中的资源 应该沿着这些路线。

<Resource name="jms/MyJMSCF" auth="Container"
          type="com.ibm.mq.jms.MQConnectionFactory"
          factory="org.apache.naming.factory.BeanFactory"
          hostName="my.host.name"
          transportType="1"/>  <!-- 1 means client mode -->

MQConnectionFactory 应该是兼容的 bean 样式对象;有很多配置选项——完整的 JavaDoc 是 http://www-01.ibm.com/support/knowledgecenter/api/content/SSFKSJ_8.0.0/com.ibm.mq.javadoc.doc/WMQJMSClasses/com/ibm/mq/jms/MQConnectionFactory.html

JNDI 仅使用 com.ibm.mq.jms.MQQueueConnectionFactoryFactory 对象通过 javax.naming.Referencable 接口重新扩充对象存储。

最后的编辑给了我线索。仔细检查后,我意识到我在 app2.war 中打包了一些 MQ jar/classes。这就是为什么当 app2.war 首先加载时,它会使用 WebApp2 class 加载器加载,并且 app1.war 无法访问此 class。这就是 app1 加载失败的原因。 而 app1.war 没有任何 mq classes。因此,当 app1 首先加载时,MQ classes 由系统 class 加载程序加载,这两个应用程序都可以访问。
解决方案显然是从 app2.war

中删除 mq jar