JMS的Spring配置(Websphere MQ - SSL,Tomcat,JNDI,非IBM JRE)

cod*_*kix 13 spring tomcat jndi jms ibm-mq

背景: 我有一个相对较旧的应用程序,它使用Websphere MQ进行消息传递.它在WAS(Websphere Application Server)上运行并使用MDB(消息驱动Bean).我成功地使用Spring Integration - JMS替换了所有MDB .我的下一步是尝试查看是否可以将其移出WAS,以便它可以在任何其他具有非IBM JRE的servlet容器上运行(我正在尝试:apache tomcat).请注意,使用SSL保护通道是必需的.我更喜欢使用JNDI.

最终目标: 将我的应用程序与应用程序服务器(WAS)和其他基础结构(如消息传递(MQ))分离.但是将这个从WAS中取出来到tomcat是第一步.接下来是使用更具可伸缩性的东西更新我的消息传递基础结构 这使我能够更新我的应用所依赖的基础架构的各个组件,一次一件事(应用服务器,消息传递层,数据存储),而不会过多地中断我的应用程序.

问: 现在,我的挑战是在tomcat上定义可以访问Websphere MQ的JNDI资源.我使用我在context.xml文件中定义的非SSL通道在这方面取得了一些进展,如下所示:

<Resource
   name="jms/qcf_sandbox"
   auth="Container"
   type="com.ibm.mq.jms.MQQueueConnectionFactory"
   factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
   description="JMS Queue Connection Factory for sending messages"
   HOST="localhost"
   PORT="1414"
   CHAN="CHANNEL_SANDBOX"
   TRAN="1"
   QMGR="QM_SANDBOX"/>
<Resource
   name="jms/SandboxQ"
   auth="Container"
   type="com.ibm.mq.jms.MQQueue"
   factory="com.ibm.mq.jms.MQQueueFactory"
   description="JMS Queue"
   QU="SANDBOX_Q"/>
Run Code Online (Sandbox Code Playgroud)

我的下一步是使用SSL通道.我理解涉及设置密钥库(kdb文件和证书生成和交换),在QM等上配置SSL通道的部分.我已经完成了所有工作.如何让tomcat使用我的密钥库,密码套件等?指针或工作示例会很棒!

注意:我目前正在使用Spring Integration 4.2,Websphere MQ v8,Tomcat v9.

我必须补充一点,我确实在没有JNDI的情况下尝试了一切.所以这里是没有JNDI的我的spring jms non-ssl配置,它的工作原理如下:

<bean id="mq-jms-cf-sandbox"
   class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory">
    <ref bean="mqQueueConnectionFactory" />
    </property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
   <property name="hostName" value="localhost" />
   <property name="port" value="1414" />
   <property name="queueManager" value="QM_SANDBOX" />
   <property name="transportType" value="1" />
   <property name="channel" value="CHANNEL_SANDBOX" />
 </bean>
 <bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
   <constructor-arg value="SANDBOX_Q" />
    <property name="baseQueueManagerName">
    <value>QM_SANDBOX</value>
    </property>
    <property name="baseQueueName">
    <value>SANDBOX_Q</value>
    </property>
</bean> 
Run Code Online (Sandbox Code Playgroud)

cod*_*kix 12

我想我终于想出了如何解决这个问题......这里是对步骤的简要描述.如果您需要更多详细信息,请通知我.

预先要求:安装了Websphere MQ Server(至少v 8.0.0.2)配置QM,SSL和非SSL通道,创建Q以及您需要的所有好东西.不用说,您需要Websphere MQ jar.请注意任何许可限制.

步骤1:使用没有SSL,没有JNDI的直接连接.您将需要使用这些bean来配置基于Spring的JMS侦听器和JMS模板等.

<bean id="mq-jms-cf-sandbox"
   class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory">
    <ref bean="mqQueueConnectionFactory" />
    </property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
   <property name="hostName" value="localhost" />
   <property name="port" value="1414" />
   <property name="queueManager" value="QM_SANDBOX" />
   <property name="transportType" value="1" />
   <property name="channel" value="NON_SSL_CHANNEL" />
 </bean>
 <bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
   <constructor-arg value="SANDBOX_Q" />
    <property name="baseQueueManagerName">
    <value>QM_SANDBOX</value>
    </property>
    <property name="baseQueueName">
    <value>SANDBOX_Q</value>
    </property>
</bean>
Run Code Online (Sandbox Code Playgroud)

第2步:使用SSL进行直接连接,没有JNDI.我觉得设置这个有点棘手.

2a.由于我使用的是非IBM JRE,因此我必须确保需要根据此处指定的映射配置密码规范和密码套件:http: //www-01.ibm.com/support/docview.wss? UID = swg1IV66840

这显然意味着我们至少必须将Websphere MQ升级到8.0.0.2.在我的例子中,我在SSL通道上使用了ECDHE_RSA_AES_256_GCM_SHA384,并在应用程序中配置了jms bean以使用TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,如下所示:

<bean id="mq-jms-cf-sandbox"
    class="org.springframework.jms.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory">
        <ref bean="mqQueueConnectionFactory" />
    </property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
  <property name="hostName" value="localhost" />
  <property name="port" value="1414" />
  <property name="queueManager" value="QM_SANDBOX" />
  <property name="transportType" value="1" />
  <property name="channel" value="SSL_CHANNEL" />
  <property name="SSLCipherSuite" value="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"/>
</bean>
<bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="SANDBOX_Q" />
    <property name="baseQueueManagerName">
       <value>QM_SANDBOX</value>
    </property>
    <property name="baseQueueName">
       <value>SANDBOX_Q</value>
    </property>
</bean>
Run Code Online (Sandbox Code Playgroud)

2b.创建证书,密钥库(kdbs),交换证书等.有很多方法可以做到这一点.但请注意,您需要存储密码,队列管理器的密钥标签必须是'ibmwebspheremq qmgr ' - 全部为小写,无空格(无引号),密钥标签必须为'ibmwebspheremq userid ' - 全部小写,没有空格,(没有引号)其中userid是运行tomcat的用户标识.如果您需要有关我如何使用自签名证书的详细信息,请告诉我们.

2c.现在你必须得到tomcat运行的JVM来读取你的密钥库.有很多方法,但这里我是这样做的:在tomcat bin文件夹中创建一个setenv.bat文件,其中包含以下内容(调试SSL是可选的)

set JAVA_OPTS="-Djavax.net.ssl.trustStore=C:\path-to-keystore\key.jks" "-Djavax.net.ssl.trustStorePassword=topsecret" "-Djavax.net.ssl.keyStore=C:\path-to-keystore\key.jks" "-Djavax.net.ssl.keyStorePassword=topsecret" "-Djavax.net.debug=ssl" "-Dcom.ibm.mq.cfg.useIBMCipherMappings=false"
Run Code Online (Sandbox Code Playgroud)

2d.使用以下命令启动tomcat:

catalina.bat run > ..\logs\tomcat.log 2>&1 
Run Code Online (Sandbox Code Playgroud)

要停止,只需按ctrl + c(在Windows上).无论您采用哪种方式,都要确保在启动期间使用setenv.bat.或使用JAVA_OPTS设置密钥库属性.

2e.验证使用SSL通道是否有效.

步骤3:使用非SSL,JNDI获取JNDI连接有很多是在tomcat上设置JNDI.以下是我的工作方式:在Web应用程序中创建一个文件META-INF/Context.xml,其中包含以下内容:

<Resource
   name="jms/qcf_sandbox"
   auth="Container"
   type="com.ibm.mq.jms.MQQueueConnectionFactory"
   factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
   description="JMS Queue Connection Factory for sending messages"
   HOST="localhost"
   PORT="1414"
   CHAN="NON_SSL_CHANNEL"
   TRAN="1"
   QMGR="QM_SANDBOX"/>
<Resource
   name="jms/SandboxQ"
   auth="Container"
   type="com.ibm.mq.jms.MQQueue"
   factory="com.ibm.mq.jms.MQQueueFactory"
   description="JMS Queue"
   QU="SANDBOX_Q"/>
Run Code Online (Sandbox Code Playgroud)

现在在你的spring配置中,而不是直接配置,你所要做的就是:

<jee:jndi-lookup id="mq-jms-cf-sandbox" jndi-name="java:/comp/env/jms/qcf_sandbox" resource-ref="false" />
<jee:jndi-lookup id="jms-destination-sandbox" jndi-name="java:/comp/env/jms/SandboxQ" resource-ref="false" />
Run Code Online (Sandbox Code Playgroud)

请注意,为简洁起见,我只是没有使用资源引用.如果你这样做,还有一些额外的步骤是直截了当的.

第4步:现在最后一步是使用SSL通道和JNDI.假设您已完成第2步,这很容易.使用以下内容修改META-INF/Context.xml:

<Resource
   name="jms/qcf_sandbox"
   auth="Container"
   type="com.ibm.mq.jms.MQQueueConnectionFactory"
   factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
   description="JMS Queue Connection Factory for sending messages"
   HOST="localhost"
   PORT="1414"
   CHAN="SSL_CHANNEL"
   TRAN="1"
   QMGR="QM_SANDBOX"
   SCPHS="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"/>
<Resource
   name="jms/SandboxQ"
   auth="Container"
   type="com.ibm.mq.jms.MQQueue"
   factory="com.ibm.mq.jms.MQQueueFactory"
   description="JMS Queue"
   QU="SANDBOX_Q"/>
Run Code Online (Sandbox Code Playgroud)

注意SCPHS ="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"的行.如果您需要设置其他此类参数,请参阅此链接中的"简短表单"列:https: //www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.ref.dev.doc/ q111800_.htm%23jm10910_?LANG = EN

希望这一切都适合你.祝好运!

一旦此配置有效,发送消息就非常简单.但是你可以使用Spring JMS参考来监听队列中的消息:https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html

步骤1:使用Spring的DefaultMessageListenerContainer并在像这样的xml文件中配置bean(spring-beans.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      
    "http://www.springframework.org/dtd/spring-beans.dtd">

   <!-- this is the Message Driven POJO (MDP) -->
   <bean id="messageListener" class="jmsexample.ExampleListener" />

   <!-- and this is the message listener container -->
   <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
       <property name="connectionFactory" ref="mq-jms-cf-sandbox"/>
       <property name="destination" ref="jms-destination-sandbox"/>
       <property name="messageListener" ref="messageListener" />
   </bean>

</beans>
Run Code Online (Sandbox Code Playgroud)

第2步:将其添加到您的web.xml

<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/context/spring-beans.xml</param-value>
 </context-param>

<listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Run Code Online (Sandbox Code Playgroud)

第3步:编写一个Message Listener类,如下所示:

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class ExampleListener implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                System.out.println(((TextMessage) message).getText());
            }
            catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        }
        else {
            throw new IllegalArgumentException("Message must be of type TextMessage");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您使用的是Spring集成,则可以执行以下操作,而不是执行步骤3:

<int:channel id="jms-inbound"/>
    <int-jms:message-driven-channel-adapter
        id="jms-inbound-adapter" container="jmsContainer" channel="jms-inbound" 
        extract-payload="true" acknowledge="transacted" 
        message-converter="messagingMessageConverter" />

<beans:bean id="messagingMessageConverter" class="org.springframework.jms.support.converter.MessagingMessageConverter">
    </beans:bean>
Run Code Online (Sandbox Code Playgroud)