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 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:
and here is a screenshot from the MQ Console showing both of the JMS connections are on the channel instance:
As always, I hope this helps! If you have any questions, let me know and I'll be happy to answer them.