Search code examples
spring-bootconfigurationjmsibm-mqmessage-queue

How to have single ConnectionFactory, JMSListener and JMSTemplate for Multiple QueueManagers


I am trying to create a single connection factory, single JMS Listener and single JMS template for connecting, listening, sending data to multiple Queue Managers.

I tried to access properties from yml file and running them in a loop inside MQConnectionFactory but it gets overridden by the last one.

This is My configuration class which has logic for connecting to QM:

@Configuration
public class QM1Config extends FixedBackOff {

    public String queueManager;
    public String queue;
    public String channel;
    public String connName;
    public String user;
    public String password;

    @Autowired
    MqServerConfig serverConfig;

    MQConnectionFactory mqQueueConnectionFactory;

    @Bean
    public MQConnectionFactory mqQueueConnectionFactory() {

        serverConfig.getActiveList().stream().map(systemName -> {
            return serverConfig.getHosts().stream().filter(host->host.getName().equalsIgnoreCase(systemName)).findFirst();
        }).filter(Optional::isPresent).forEach(host->{
            this.channel = host.get().getChannel();
            this.user = host.get().getUser();
            this.password = host.get().getPassword();
            this.queueManager = host.get().getQueueManager();
            this.queue = host.get().getQueue();
            this.connName = host.get().getConnName();
            System.out.println(channel+" "+connName+" "+queueManager+" "+user);
            mqQueueConnectionFactory = new MQConnectionFactory();
            try {

                mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
                mqQueueConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, false);
                mqQueueConnectionFactory.setCCSID(1208);
                mqQueueConnectionFactory.setChannel(channel);
                mqQueueConnectionFactory.setStringProperty(WMQConstants.USERID, user);
                mqQueueConnectionFactory.setStringProperty(WMQConstants.PASSWORD, password);
                mqQueueConnectionFactory.setQueueManager(queueManager);
                mqQueueConnectionFactory.setPort(host.get().getPort());
                mqQueueConnectionFactory.setHostName(host.get().getHost());
            } catch (Exception e) {
                e.printStackTrace();
                
            }
        });
        
        return mqQueueConnectionFactory;
    }


    @Bean
    public JmsListenerContainerFactory<?> qm1JmsListenerContainerFactory(@Qualifier("mqQueueConnectionFactory") MQConnectionFactory mqQueueConnectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) throws InterruptedException {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
      factory.setExceptionListener(new MQExceptionListener());
      configurer.configure(factory, mqQueueConnectionFactory);
      return factory;
    }
    
    @Bean("jmsTemplate1")
    public JmsTemplate jmsTemplate1(@Qualifier("mqQueueConnectionFactory") MQConnectionFactory mqQueueConnectionFactory) {
        JmsTemplate jmsTemplate1  = new JmsTemplate(mqQueueConnectionFactory);
        return jmsTemplate1;
    }

This is the Listener:

@Component
public class QueueConsumer {
    
    @JmsListener(destination = "${mq-processor.mq-server-list.hosts[0].queue}" , containerFactory = "qm1JmsListenerContainerFactory")
    public void receive1(String text) {
        System.out.println("In Listener 1");
        System.out.println("Received from qm1: " + text);
    }
    
    @JmsListener(destination = "${mq-processor.mq-server-list.hosts[1].queue}" , containerFactory = "qm1JmsListenerContainerFactory")
    public void receive2(String text) {
        System.out.println("In Listener 2");
        System.out.println("Received from qm2: " + text);

    }   
}

This is my YML file:

mq-processor:
  mq-server-list:
     active-list: [FIRSTQM, SECONDQM]
     hosts:
        - name: FIRSTQM
          message-flow: inflow
          secure: true
          host: localhost
          port: 1417
          queueManager: QM5
          queue: Q5
          channel: AppToQM5
          user: MUSR_MQADMIN
          password: pakalu
        - name: SECONDQM
          message-flow: inflow
          host: localhost
          port: 1415
          secure: true
          queueManager: QM3
          queue: Q3
          channel: AppToQM3
          user: MUSR_MQADMIN
          password: pakalu

I am able to connect to the last Queue Manager but the configurations of the previous one gets overridden with the approach I am following.

While running application I get this error and I know why I am getting his error. I need help to resolve it so that it can listen to both the QM's.

com.ibm.msg.client.jms.DetailedInvalidDestinationException: JMSWMQ2008: Failed to open MQ queue 'Q5'.
com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2085' ('MQRC_UNKNOWN_OBJECT_NAME').

Can anyone please help me with this. What do I need to do in order to have a single connection Factory, Listener and JMSTemplate for Multiple Queue Managers?


Solution

  • A connection factory, in case of IBM MQ, will have connection information, like the queue manager name, host name, port number, channel among others for one queue manager. This is same for other messaging providers as well. You can think connection factory like an assembly line in automobile factory that produces one model of cars because the assembly line has been configured for that specific model. If the factory needs to produce another model, it will need to setup another assembly line.

    You can create seperate connection factories (and connection, session, consumer etc) for each of your queue managers.