背景: 我有一个使用Websphere MQ进行消息传递的相对较旧的应用程序。它运行在WAS(Websphere应用程序服务器)上,并使用MDB(消息驱动Bean)。我能够使用Spring Integration-JMS成功替换所有MDB 。我的下一步是尝试查看是否可以将其移植到WAS之外,以便它可以在具有非IBM JRE的任何其他servlet容器上运行(我正在尝试:apache tomcat)。请注意,必须使用SSL保护频道。我更喜欢使用JNDI。
最终目标: 使我的应用程序与应用程序服务器(WAS)和其他基础结构(如消息传递(MQ))脱钩。但是,将WAS中的内容排除在Tomcat之外是第一步。接下来的任务是使用更可扩展的功能更新我的消息传递基础结构。这样一来,我就可以一次更新一件应用程序所依赖的基础结构的各个组件(应用程序服务器,消息传递层,数据存储),而不会像我所进行的那样过多地破坏我的应用程序。
问题: 现在,我的挑战是在可以访问Websphere MQ的tomcat上定义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"/>
我的下一步是使它与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>
我想我终于想出了解决方案的方法…这是步骤的简要说明。如果您需要更多详细信息,请告诉我。
先决条件 :已安装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>
步骤2 :使用SSL(不使用JNDI)获得直接连接。我发现设置起来有些棘手。
显然,这意味着我们至少必须将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>
2b 。创建证书,密钥库(kdbs),交换证书等。有许多方法可以做到这一点。但要留意,你将需要藏匿口令,队列管理器密钥标签必须是“ibmwebspheremq QMGR ” -所有小写,没有空格,(不带引号),密钥标签必须像“ibmwebspheremq 用户ID ” -所有小写形式,没有空格(不带引号),其中userid是运行tomcat的用户ID。如果您需要有关我使用自签名证书的确切方式的更多详细信息,请告诉我。
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"
2d 。使用以下命令启动tomcat:
catalina.bat run > ..\logs\tomcat.log 2>&1
要停止,只需按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"/>
现在在您的spring config中,您无需做直接配置,而要做的只是:
<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" />
请注意,为简便起见,我只是不使用资源引用。如果您这样做,还有一些其他直接的步骤。
步骤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"/>
希望所有这些都对您有用。 祝好运!
一旦配置成功,发送消息就很简单了。但这是使用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>
第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>
步骤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"); } } }
或者,如果您使用的是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>