MQ

MQ

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only

Why is my JMS application not sharing TCP/IP connections when using TLS?

By Paul Titheridge posted 8 days ago

  

When using the MQ classes for JMS or classes for Jakarta Messaging, it is possible for JMS connections and sessions created within the same application to share a channel instance (or TCP/IP connection) to a queue manager. I wrote an article last year called "Sharing MQ JMS conversations over channel instances" that shows how this works.

Now, I had an interesting question from a customer recently who was saying that when their application was connecting to a queue manager over a secure channel that was configured with TLS, they weren't seeing the expected level of sharing. Every JMS connection that the application created resulted in a new channel instance being created. This was a bit unusual, so I spent some time working with them to find out what was going on.

Rather than looking up connection factories from a JNDI repository, each thread in the application was creating mainit's own connection factory programmatically. Although all of the connection factories were configured to connect to the same queue manager over the same channel instance, each one had its own SSLSocketFactory set on it. This was significant.

Here's a JMS code snippet that effectively does the same thing that the customer's application was doing:

// Create a connection factory called cf1
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf1 = ff.createConnectionFactory();

// Set the properties on cf1
cf1.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "paultV94LTS");
cf1.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf1.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf1.setStringProperty(WMQConstants.WMQ_CHANNEL, "SECURE.SVRCONN");
cf1.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf1.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");

// Next, create an SSLSocketFactory and set it on cf1
SSLContext context1 = SSLContext.getInstance("TLSv1.2");
context1.init(null, null, null);
SSLSocketFactory sslSocketFactory1 = context1.getSocketFactory();
cf1.setObjectProperty(WMQConstants.WMQ_SSL_SOCKET_FACTORY, sslSocketFactory1);

// Now, create a second connection factory with the same properties as the first one,
// with the exception of the SSLSocketFactory
JmsConnectionFactory cf2 = ff.createConnectionFactory();
cf2.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "paultV94LTS");
cf2.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf2.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf2.setStringProperty(WMQConstants.WMQ_CHANNEL, "SECURE.SVRCONN");
cf2.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf2.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");

// Create an SSLSocketFactory for cf2
SSLContext context2 = SSLContext.getInstance("TLSv1.2");
context2.init(null, null, null);
SSLSocketFactory sslSocketFactory2 = context2.getSocketFactory();
cf2.setObjectProperty(WMQConstants.WMQ_SSL_SOCKET_FACTORY, sslSocketFactory2);      

// Finally, create a JMS connection from cf1 and another one from cf2
Connection connection1 = cf1.createConnection();
Connection connection2 = cf2.createConnection();

When this runs, the MQ classes for JMS do indeed create two channel instances to the queue manager even though the SECURE.SVRCONN channel has the SHARECNV attribute set to 10. Here is a screenshot from the MQ Console that shows this:

The TLSTest application is using two instances of the SECURE.SVRCONN channel - one for the connection created from cf1, and one for the connection created from cf2.
The reason for this is quite interesting. 
The MQ classes for JMS maintain an internal list of Remote Connection Specifications, where each entry contains details about how to connect to a queue manager (such as the channel to use, the hostname and port of the queue manager system, and so on). Each Remote Connection Specification has one or more active channel instances associated with it.
When an application uses a connection factory to create a new connection to a queue manager, the MQ classes for JMS will extract information from the factory and then use that to look up a Remote Connection Specification. If they find one, then the MQ classes for JMS will either allocate a new conversation on an existing channel instance associated with that Remote Connection Specification or create a new channel instance and start the conversation over that.
However, if the MQ classes for JMS don't find a suitable Remote Connection Specification, they will create one, add it to the Remote Connection Specification list and then create a new channel instance for the new conversation.
Now, one of the things that the MQ classes for JMS use when building the Remote Connection Specification is the SSLSocketFactory stored on the connection factory. This means that if two connection factories are identical in every way apart from the SSLSocketFactory property, then the MQ classes for JMS will create two Remote Connection Specification objects for them - as a result, the conversations associated with JMS connections and sessions created from the first connection factory will not share the same channel instance as those created from the second connection factory.
This is what is happening here. Even though cf1 and cf2 are both configured to connect to the same queue manager using the same channel, the fact that they have different SSLSocketFactories mean that any JMS connections and sessions created from cf1 cannot be assigned to the same channel instance as the JMS connections and sessions for cf2.
If cf2 is changed to use the same SSLSocketFactory as cf1, then the JMS connections created from both connection factories can share the same channel instance. Here is a modified version of the code above that does this:
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf1 = ff.createConnectionFactory();

// Set the properties
cf1.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "paultV94LTS");
cf1.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf1.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf1.setStringProperty(WMQConstants.WMQ_CHANNEL, "SECURE.SVRCONN");
cf1.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf1.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
      
// Create an SSLSocketFactory for cf1
SSLContext context1 = SSLContext.getInstance("TLSv1.2");
context1.init(null, null, null);
SSLSocketFactory sslSocketFactory1 = context1.getSocketFactory();
cf1.setObjectProperty(WMQConstants.WMQ_SSL_SOCKET_FACTORY, sslSocketFactory1);

// Now, create a second connection factory with the same properties as the first one,
// with the exception of the SSLSocketFactory
JmsConnectionFactory cf2 = ff.createConnectionFactory();
cf2.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "paultV94LTS");
cf2.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf2.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf2.setStringProperty(WMQConstants.WMQ_CHANNEL, "SECURE.SVRCONN");
cf2.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf2.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
      
// Next, set the SSLSocketFactory on cf2 to be the same SSLSocketFactory
// that is being used by cf1
cf2.setObjectProperty(WMQConstants.WMQ_SSL_SOCKET_FACTORY, sslSocketFactory1);
      
// Finally, create a JMS connection from cf1 and another one from cf2
Connection connection1 = cf1.createConnection();
Connection connection2 = cf2.createConnection();
and here is a screenshot from the MQ Console showing both of the JMS connections are on the channel instance:
Here, the connections created from cf1 and cf2 are both sharing the same instance of the SECURE.SVRCONN channel.
As always, I hope this helps! If you have any questions, let me know and I'll be happy to answer them.
0 comments
5 views

Permalink