robert trudel
robert trudel

Reputation: 5749

Two queue manager with different channel not allowed

I use spring boot 2.3.2 with ibm mq. I use property file setup mq.

ibm.mq.queueManager=
ibm.mq.channel=
ibm.mq.connName
ibm.mq.user
ibm.mq.password
ibm.mq.useIBMCipherMappings=false
ibm.mq.userAuthenticationMQCSP=false
ibm.mq.sslCipherSuite=TLS_RSA_WITH_AES_128_CBC_SHA256

that works fine.

I need to create another factory to be able to connect to another channel. So I would like to create a similar one which is created by default but with different channel name.

So I created a config class

@EnableJms
@Configuration
public class JmsConfig {
@Bean
    public MQQueueConnectionFactory jmsMQConnectionFactoryPayment() throws JMSException {
        MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
        connectionFactory.setQueueManager("AD_TEST");
        connectionFactory.setChannel("FROM.PAYMENTMNG");
        connectionFactory.setConnectionNameList("wmqd1.int.test.com(1818),wmqd2.int.test.com(1818)");


        connectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, false);

        connectionFactory.setStringProperty(WMQConstants.WMQ_SSL_CIPHER_SUITE, "TLS_RSA_WITH_AES_128_CBC_SHA256");
        connectionFactory.setIntProperty(CommonConstants.WMQ_CONNECTION_MODE, CommonConstants.WMQ_CM_CLIENT);


        System.setProperty("com.ibm.mq.cfg.useIBMCipherMappings", String.valueOf(Boolean.FALSE));

        connectionFactory.createConnection("123", "123");
        return connectionFactory;
    }

    @Bean
    JmsListenerContainerFactory<?> jmsContainerFactoryPayment() throws JMSException {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(jmsMQConnectionFactoryPayment());
        return factory;
    }

    @Bean("payment")
    JmsTemplate jmsTemplatePayment() throws JMSException {
        JmsTemplate template = new JmsTemplate();
        template.setConnectionFactory(jmsMQConnectionFactoryPayment());
        return template;
    }


}

In a class I have

@JmsListener(destination="xxx", containerFactory="jmsContainerFactoryPayment"){
    ....
}

When I start application, I get

com.ibm.msg.client.jms.DetailedIllegalStateException: JMSWMQ0018: E Failed to connect to queue manager 'AD_TEST' with connection mode 'Client' and host name 'wmqd1.int.test.com(1818),wmqd2.int.test.com(1818)'.

                at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:489) ~[com.ibm.mq.allclient-9.2.0.0.jar:9.2.0.0 - p920-L200710.DE]

                at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:215) ~[com.ibm.mq.allclient-9.2.0.0.jar:9.2.0.0 - p920-L200710.DE]

                at com.ibm.msg.client.wmq.internal.WMQConnection.<init>(WMQConnection.java:450) ~[com.ibm.mq.allclient-9.2.0.0.jar:9.2.0.0 - p920-L200710.DE]

                at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createV7ProviderConnection(WMQConnectionFactory.java:8475) ~[com.ibm.mq.allclient-9.2.0.0.jar:9.2.0.0

Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ  call failed with compcode '2' ('MQCC_FAILED') ; reason '2538' ('MQRC_HOST_NOT_AVAILABLE').

                at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:203) ~[com.ibm.mq.allclient-9.2.0.0.jar:9.2.0.0 - p920-L200710.DE]

                ... 51 common frames omitted

Seem like we can't have two queue manager with same host but different channel

Upvotes: 0

Views: 1470

Answers (1)

richc
richc

Reputation: 402

I have configured Spring Boot with IBM MQ to test this. Starting with the sample provided in this IBM Messaging GitHub Repo, I added two additional listeners as follows.

First I added some additional properties to the application.properties file

my.mq.queueManager=QM2
my.mq.channel=DEV.APP.SVRCONN
my.mq.connName=localhost(1415)
my.mq.user=<your_user_name>
my.mq.password=<your_password>

I left Application.java unchanged, copied Listener.java to create ListenerTwo.java and ListenerThree.java. I then added a new ListenerBeanConfig.java class to the sample.

ListenerTwo.java was changed to bind to a new ConnectionFactory configuration listenerTwoFactory which is created later.

@JmsListener(destination = Application.qName, containerFactory = "listenerTwoFactory")

ListenerThree.java was changed to bind to a new listenerThreeFactory configuration and Queue

@JmsListener(destination = "DEV.QUEUE.2", containerFactory = "listenerThreeFactory")

The ListenerBeanConfig.java class declaration was annotated so that I can access my properties by adding Strings for each property e.g., queueManager, channel, connName etc. and providing setter methods for each of them e.g.,

@Configuration
@EnableJms
@ConfigurationProperties(prefix="my.mq")
public class ListenerBeanConfig {

    String connName;
public void setConnName(String value) {
    System.out.println("connName is set to: "+value);
    connName = value;
  }

I registered the two new Listener beans

@Bean
  public ListenerTwo myListenerTwo() {
    return new ListenerTwo();
  }

@Bean
  public ListenerThree myListenerThree() {
    return new ListenerThree();
  }

I then created the new connection factory configurations listenerTwoFactory and listenerThreeFactory

For listenerTwoFactory I used the JMS classes provided by com.ibm.mq.jms in the Spring Boot config

JmsConnectionFactory cf;

@Bean
  public DefaultJmsListenerContainerFactory listenerTwoFactory() {
  DefaultJmsListenerContainerFactory containerFactory = new DefaultJmsListenerContainerFactory();
  try {
    JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
    cf = ff.createConnectionFactory();
    cf.setStringProperty(WMQConstants.WMQ_CONNECTION_NAME_LIST, connName);
    cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channel);
    cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
    cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, queueManager);
    cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "Spring Boot ListenerTwo");
    cf.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
    cf.setStringProperty(WMQConstants.USERID, user);
    cf.setStringProperty(WMQConstants.PASSWORD, password);
  } catch (JMSException jmsex) {
    System.out.println(jmsex);
  }
  containerFactory.setConnectionFactory(cf);
  return containerFactory;
}

For the listenerThreeFactory I used the MQ JMS helper classes from com.ibm.mq.spring.boot.

@Bean
public DefaultJmsListenerContainerFactory listenerThreeFactory() {
    MQConfigurationProperties myProps = new MQConfigurationProperties();
    myProps.setUser(user);
    myProps.setChannel(channel);
    myProps.setConnName(connName);
    myProps.setPassword(password);
    myProps.setQueueManager(queueManager);
    //No customizer
    MQConnectionFactoryFactory mqcff = new MQConnectionFactoryFactory(myProps,null);
    MQConnectionFactory mqcf = mqcff.createConnectionFactory(MQConnectionFactory.class);
    DefaultJmsListenerContainerFactory containerFactory = new DefaultJmsListenerContainerFactory();
    containerFactory.setConnectionFactory(mqcf);
    return containerFactory;
}

Finally, I compiled and ran the new sample configuration. Using the IBM MQ Console for two IBM MQ queue manager docker instances, I put messages to QM1: DEV.QUEUE.1 and QM2: DEV.QUEUE.1, DEV.QUEUE.2. On the terminal see the following output.

========================================
Received message is: message 1
========================================

========================================
ListenerTwo received message is: message 2
========================================

========================================
ListenerThree received message is: message 3
========================================

Also tested with all three listeners connected to QM2 via a two different channels: DEV.APP.SVRCONN and DEV.APP.SVRCONN.TWO.

I am sure there are far more elegant ways to manage the additional properties.

Upvotes: 3

Related Questions