Spring Configuration for JMS (Websphere MQ - SSL, Tomcat, JNDI, Non IBM JRE)

Background: I have a relatively old application that uses Websphere MQ for messaging. It runs on WAS (Websphere Application Server) and uses MDB (Message Driven Beans). I managed to replace all MDBs with Spring Integration - JMS . My next step is to try to find out if I can migrate it from WAS so that it can work in any other servlet container with a non-IBM JRE (I try: apache tomcat). Please note that channel security using SSL is a must. I prefer to use JNDI.

The ultimate goal: to separate my application from the application server (WAS) and other infrastructure, such as messaging (MQ). But getting this from WAS to tomcat is the first step. Next up is the task of updating my messaging infrastructure with something more scalable. This allows me to update the individual infrastructure components that my application relies on, one at a time (application server, messaging layer, data store), without interrupting the application.

Question: Now my task is to determine the JNDI resources for tomcat that can access Websphere MQ. I have made some progress on this using non-SSL channels, which I defined in the context.xml file as follows:

<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 includes setting up keystores (creating and sharing kdb files and certificates), setting up SSL channels in QM, etc. All this already works for me. How to get tomcat to use my keystore, cipher suite, etc.? Pointers or a working example would be great!

Note. I am currently using Spring Integration 4.2, Websphere MQ v8, Tomcat v9.

I must add that at first I tried everything without JNDI. So here is my spring jms non-ssl config without JNDI that works:

 <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> 
+12
spring tomcat jms ibm-mq jndi
source share
2 answers

I think I finally figured out how to do this ... here is a brief description of the steps. If you need more information, let me know.

Pre-Reqs : WebSphere MQ Server installed (at least v 8.0.0.2) Configure QM, SSL and non-SSL channels, create Qs and everything you need. Needless to say, you need Websphere MQ banks. Be aware of any licensing restrictions.

Step 1 Get a direct connection without SSL, without JNDI. You will need to use these beans to configure your JMS listeners based on spring and JMS templates, etc.

 <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 a direct connection to SSL, without JNDI. I found this to be a bit complicated.

2a . Since I used a non-IBM JRE, I had to make sure that the specifications for ciphers and cipher suites needed to be configured according to the mappings listed here: http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840

This obviously means that at least we should upgrade our Websphere MQ to 8.0.0.2. In my case, I used ECDHE_RSA_AES_256_GCM_SHA384 on the SSL channel and configured jms beans in the application to use TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, for example:

 <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 certificates, key stores (kdbs), certificates of exchange, etc. There are many ways to do this. But remember that you will need to store passwords, the key label for the queue manager should be "ibmwebspheremqqmgr" - everything is lowercase, without spaces (without quotes), the key label should be like "ibmwebspheremquserid" - everything is lowercase, no spaces ( without quotes), where userid is the identifier of the user who runs tomcat. If you need detailed information on how I did this using self-signed certificates, let me know.

2s Now you should get a JVM that runs tomcat 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 (SSL debugging is optional)

 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 . Launch tomcat using the following command:

 catalina.bat run > ..\logs\tomcat.log 2>&1 

To stop, just press ctrl + c (on windows). Whichever way you do this, make sure setenv.bat is used at startup. Or use JAVA_OPTS to set the keystore properties.

2e . Verify that using the SSL channel works.

Step 3 Get a JNDI connection working with non-SSL, JNDI Many had to create JNDI on tomcat. Here's how I did it: inside the web application, create a META-INF / Context.xml file with the following contents:

 <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 the spring configuration, instead of direct configurations, you only need:

 <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 simply did not use resource links. In case you do this, a few more steps that go straight.

Step 4 Now the last step is to use the SSL channel and JNDI. Assuming you have done step 2, this is easy. Modify META-INF / Context.xml with the following contents:

 <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

Hope all this works for you. Good luck

Once this configuration works, sending messages is pretty simple. But you can listen to the message in the queue using spring JMS Link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html

Step 1 Use spring DefaultMessageListenerContainer and configure your beans in an XML file, for example: 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

 <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 the listener class this way:

 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 use spring integration, you can do something like this:

 <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> 
+12
source share

I know this is an old post, and a lot has changed since then, but I am stuck with a similar problem. I have an application that is currently deployed in IBM WAS and uses MDB to read messages from the queue and stores them in a database, and also uses distributed transactions. Although I wrote code in SpringBoot that reads messages from a queue (JMS) and stores them in a database, I cannot figure out how to implement distributed transactions to roll back a queue message in the event of any database failure. I would also appreciate if you could help me understand how I can deploy this application to tomcat. Many thanks!

0
source share

All Articles