Search code examples
jmsibm-mqspring-boot-3

IBM MQ JMS not working after migration to Spring Boot 3 with com.ibm.mq.jakarta.client.jar 9.3


I tried to call an Enterprise MQ in Spring Boot 2.5 using javax.jms MQ dependencies. The connection is being successfully made. Below is my pom.xml and code. I am using an App ID credentials to connect to the queue which I have set in the UserCredentialsProvider class.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.ibm.mq</groupId>
            <artifactId>com.ibm.mq.allclient</artifactId>
            <version>0.0.0.3</version>
        </dependency>
        
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>javax.jms-api</artifactId>
            <version>2.0.1</version>
        </dependency>

Code -

    @Bean
    public MQQueueConnectionFactory mqQueueConnectionFactory() {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        mqQueueConnectionFactory.setHostName(host);
        try {
            mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setCCSID(1208);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);            
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mqQueueConnectionFactory;
    }

    @Bean
    UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter(MQQueueConnectionFactory mqQueueConnectionFactory) {
        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
        userCredentialsConnectionFactoryAdapter.setUsername(username);
        userCredentialsConnectionFactoryAdapter.setPassword(password);
        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
        return userCredentialsConnectionFactoryAdapter;
    }

    @Bean
    @Primary
    public CachingConnectionFactory cachingConnectionFactory(UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }

    @Bean
    public PlatformTransactionManager jmsTransactionManager(CachingConnectionFactory cachingConnectionFactory) {
        JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
        jmsTransactionManager.setConnectionFactory(cachingConnectionFactory);
        return jmsTransactionManager;
    }

    @Bean
    public JmsOperations jmsOperations(CachingConnectionFactory cachingConnectionFactory) throws JMSException {
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout);
   Connection c = jmsTemplate.getConnectionFactory().createConnection();
   System.out.println(c.getMetaData()); //the connection is established successfully
        return jmsTemplate;
    }

Now, what I did was that I tried to migrate to Spring Boot 3.1.1. I had to change my MQ dependencies in POM to use jakarta in place of javax package. Below is my updated POM:

        <dependency>
            <groupId>jakarta.jms</groupId>
            <artifactId>jakarta.jms-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ibm.mq</groupId>
            <artifactId>com.ibm.mq.jakarta.client</artifactId>
            <version>9.3.0.0</version>
        </dependency>

I updated the above code to use jakarta Connection Factory instead of JMS , but now I am getting MQRC_NOT_AUTHORIZED(2035) error.

What modifications do I have to perform in order for this to work in Spring Boot 3.1 with com.ibm.mq.jakarta.client.jar 9.3?


Solution

  • In 9.3 the default for MQ authentication in Java and JMS classes changed from compatibility mode to MQCSP. This is why you are now getting a 2035. To get the connection working again you need to switch back to compatibility mode.

    There are a few methods to set it back to compatibility mode, two of them are:

    1. Set the connection factory property JmsConstants.USER_AUTHENTICATION_MQCSP to false
    2. Set the Java system property com.ibm.mq.cfg.jmqi.useMQCSPauthentication to N.
      For example:
      • System.setProperty("com.ibm.mq.cfg.jmqi.useMQCSPauthentication", "N")
      • -Dcom.ibm.mq.cfg.jmqi.useMQCSPauthentication=N

    For more details see Connection authentication with the Java client.


    9.3 breaking it likely means one of three things:

    1. The queue manager uses a custom security exit to validate the username and password that expects compatibility mode.
    2. The queue manager has CONNAUTH configured with ADOPTCTX(NO).
    3. The queue manager has no security in place and the queue manger just assumes the username you send is correct.

    You can test if it is #3 by turning on compatibility mode and intentionally sending a incorrect password. If it works then you know it is #3.

    You can test if it is #2 by turning off compatibility mode, sending the correct password and specifying -Duser.name=username, if this works then it is likely #2.

    If both of the above fail then it is likely #1.