JMS 的 Spring 配置(Websphere MQ - SSL、Tomcat、JNDI、非 IBM JRE)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36291301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Configuration for JMS (Websphere MQ - SSL, Tomcat, JNDI, Non IBM JRE)
提问by code4kix
Background:I have a relatively old application that uses Websphere MQ for messaging. It runs on WAS (Websphere Application Server) and uses MDBs (Message Driven Beans). I was successfully able to replace all MDBs using Spring Integration - JMS. My next step is try to see if I can port it out of WAS so that it can run on any other servlet container with a non-IBM JRE (I am trying: apache tomcat). Note that securing channels using SSL is a requirement. I prefer using JNDI.
背景:我有一个使用 Websphere MQ 进行消息传递的相对较旧的应用程序。它运行在 WAS(Websphere Application Server)上并使用 MDB(消息驱动 Bean)。我成功地使用Spring Integration-JMS替换了所有 MDB 。我的下一步是尝试查看是否可以将它从 WAS 中移植出来,以便它可以在任何其他带有非 IBM JRE 的 servlet 容器上运行(我正在尝试:apache tomcat)。请注意,使用 SSL 保护通道是一项要求。我更喜欢使用 JNDI。
End Goal:To decouple my application from the application server (WAS) and other infrastructure like messaging (MQ). But taking this out of WAS onto tomcat is the first step. Next comes the task of updating my messaging infrastructure with something more scalable. This allows me to update individual components of the infrastructure that my app relies on, one thing at a time (app server, messaging layer, datastore) without disrupting my application too much, as I go.
最终目标:将我的应用程序与应用程序服务器 (WAS) 和其他基础设施(如消息传递 (MQ))分离。但是把这个从 WAS 中取出到 tomcat 是第一步。接下来是使用更具可扩展性的东西更新我的消息传递基础设施的任务。这使我可以一次更新应用程序所依赖的基础架构的各个组件(应用程序服务器、消息层、数据存储),而不会过多地中断我的应用程序。
Question:Now, my challenge is to define JNDI resources on tomcat that can access Websphere MQ. I have made some progress on this using non-SSL channels that I defined in the context.xml file like so:
问:现在,我的挑战是在可以访问 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"/>
My next step is to get this to work with SSL channels. I understand the part that involves setting up the keystores (kdb file and cert generation and exchanging), configuring the SSL channels on the QM etc. I have all that working already. How do I get tomcat to use my keystore, cipher suite etc? Pointers or a working example would be great!
我的下一步是让它与 SSL 通道一起工作。我了解涉及设置密钥库(kdb 文件和证书生成和交换)、在 QM 上配置 SSL 通道等的部分。我已经完成了所有工作。如何让 tomcat 使用我的密钥库、密码套件等?指针或工作示例会很棒!
Note: I am using Spring Integration 4.2, Websphere MQ v8, Tomcat v9, currently.
注意:我目前使用的是 Spring Integration 4.2、Websphere MQ v8、Tomcat v9。
I must add that I did try everything without the JNDI first. So here's my spring jms non-ssl config without the JNDI, that works:
我必须补充一点,我确实在没有 JNDI 的情况下尝试了一切。所以这是我的没有 JNDI 的 spring jms 非 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>
回答by code4kix
I think I finally figured out how to pull this off... here's a brief description of the steps. If you need more details let me know.
我想我终于想出了如何解决这个问题...这里是步骤的简要说明。如果您需要更多详细信息,请告诉我。
Pre-reqs: Websphere MQ Server installed (at least v 8.0.0.2) Configure the QM, SSL and non-SSL channels, create Qs and all that good stuff you need. Needless to say, you need the Websphere MQ jars. Be mindful of any licensing restrictions.
先决条件:已安装 Websphere MQ Server(至少 v 8.0.0.2)配置 QM、SSL 和非 SSL 通道,创建 Q 以及您需要的所有好东西。不用说,您需要 Websphere MQ jar。请注意任何许可限制。
Step 1: Get the direct connection working with no SSL, no JNDI. You will need to use these beans to configure your spring based JMS listeners and JMS Templates etc.
第 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>
Step 2: Get the direct connection working with SSL, no JNDI. I found setting this up a little tricky.
第 2 步:使用 SSL 进行直接连接,无需 JNDI。我发现设置这个有点棘手。
2a. Since I was using a non-IBM JRE, I had to make sure the cipher specs & cipher suites needed to be configured according to the mappings specified here: http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
2a。由于我使用的是非 IBM JRE,我必须确保需要根据此处指定的映射来配置密码规范和密码套件:http: //www-01.ibm.com/support/docview.wss? uid=swg1IV66840
This obviously means that we at least have to have our Websphere MQ upgraded to 8.0.0.2. In my case I used ECDHE_RSA_AES_256_GCM_SHA384 on the SSL channel and configured the jms beans within application to use TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, like so:
这显然意味着我们至少必须将我们的 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. Create certs, keystores (kdbs), exchange certs etc. There are many ways to do this. But be mindful that you will need to stash passwords, the key label for the queue manager must be ‘ibmwebspheremqqmgr' – all in lower case, no spaces, (without quotes), the key label must be like ‘ibmwebspheremquserid' – all in lower case, no spaces, (without quotes) where userid is the userid that runs tomcat. If you need more details on exactly how I did it using self signed certs, let me know.
2b。创建证书、密钥库 (kdbs)、交换证书等。有很多方法可以做到这一点。但请注意,您需要存储密码,队列管理器的密钥标签必须是“ibmwebspheremq qmgr”——全部为小写,没有空格,(不带引号),密钥标签必须像“ibmwebspheremq userid”——所有在小写中,没有空格(没有引号),其中 userid 是运行 tomcat 的 userid。如果您需要有关我如何使用自签名证书进行操作的更多详细信息,请告诉我。
2c. Now you have to get the JVM that tomcat runs, to read your keystores. There are many ways but here's how I did it: Create a setenv.bat file in the tomcat bin folder, with the following contents (debugging SSL is optional)
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. Start tomcat using the following command:
2d。使用以下命令启动 tomcat:
catalina.bat run > ..\logs\tomcat.log 2>&1
To stop, just press ctrl+c (on windows). Whichever way you do it, make sure that setenv.bat is used during start up. Or use JAVA_OPTS to set the keystore properties.
要停止,只需按 ctrl+c(在 Windows 上)。无论您采用哪种方式,请确保在启动期间使用 setenv.bat。或者使用 JAVA_OPTS 设置密钥库属性。
2e. Verify that the using the SSL channel works.
2e。验证使用 SSL 通道是否有效。
Step 3: Get a JNDI connection working with non-SSL, JNDI There are many was to set up JNDI on tomcat. Here's how I did it: Within the web application create a file META-INF/Context.xml with the following contents:
第 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"/>
Now in your spring config, instead of the direct configurations, all you have to do is:
现在在你的 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" />
Note that for brevity, I just didn't use resource references. In case you do, there a few additional steps which are straight forward.
请注意,为简洁起见,我只是没有使用资源引用。如果你这样做了,还有一些额外的步骤是直接的。
Step 4: Now the final step is to use an SSL channel and JNDI. Assuming you have done step 2, this is easy. Modify the META-INF/Context.xml with the following contents:
第 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"/>
Note the line with SCPHS="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384". If you need to set other such parameters, see the "Short Form" column in this link: https://www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.ref.dev.doc/q111800_.htm%23jm10910_?lang=en
请注意带有 SCPHS="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" 的行。如果您需要设置其他此类参数,请参阅此链接中的“Short Form”栏:https: //www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.ref.dev.doc/ q111800_.htm%23jm10910_?lang=en
Hopefully all this works for you. Good luck!
希望这一切对你有用。祝你好运!
Once this configuration works, sending messages is pretty straight forward. But this is how you can listen for a message on a queue using Spring JMS Reference: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html
一旦此配置生效,发送消息就非常简单了。但这就是如何使用 Spring JMS 参考在队列上侦听消息:https: //docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html
Step 1: Use Spring's DefaultMessageListenerContainer and configure your beans in an xml file like so (spring-beans.xml):
第 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>
Step 2: Add this to your web.xml
第 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>
Step 3: Write a Message Listener class like so:
第 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");
}
}
}
Alternatively, instead of step 3, if you are using spring integration, you can do something like so:
或者,如果您使用的是 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>