1

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?

JoshMc
  • 10,239
  • 2
  • 19
  • 38
  • It is not clear what the actual version is of the com.ibm.mq.allclient.jar. 0.0.0.3 is not a valid version. Can you please check the manifest of the original com.ibm.mq.allclient.jar for the version? – JoshMc Aug 20 '23 at 03:14
  • I did a test. for my spring boot 2.5 code , I updated the version of com.ibm.mq.allclient.jar from 0.0.0.3 till 9.2. It works fine but as soon as I update it to 9.3 , I start getting the same unauthorized error which I was getting using spring boot 3.1 ! https://mvnrepository.com/artifact/com.ibm.mq/com.ibm.mq.allclient 0.0.0.3 - 9.2 - works fine >9.3 - gives unauthorized error. So in stead of 0.0.0.3 you can refer any version which is < 9.3. It seems to work for those. – Sugandha Mishra Aug 20 '23 at 03:27
  • The MANIFEST.MF inside the 0.0.0.3 jar would tell you the actual version. – JoshMc Aug 20 '23 at 03:49
  • Here are the manifest details for 0.0.0.3 - Specification-Title: Java Message Service Specification-Version: 2.0 Specification-Vendor: Oracle Corporation Implementation-Title: IBM MQ classes for JMS and Java Implementation-Version: 9.0.4.0 - p904-L171030.1 – Sugandha Mishra Aug 20 '23 at 04:14
  • 1
    So that older version is really from 9.0.4 of MQ. – JoshMc Aug 20 '23 at 04:35
  • Thank you for the well written question. – JoshMc Aug 20 '23 at 04:59
  • Thanks a lot for the detailed explanation! – Sugandha Mishra Aug 24 '23 at 19:43

1 Answers1

2

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 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.

JoshMc
  • 10,239
  • 2
  • 19
  • 38
  • Thanks a lot for this great input ! Went with option 1 and it worked for me ! – Sugandha Mishra Aug 20 '23 at 04:27
  • 1
    Thank you for the quick accept. I added some extra information at the end about why it would have broken with 9.3. – JoshMc Aug 20 '23 at 04:48
  • One more follow up query to the above case is that my JMS health check actuator fires up after spring boot app is started and it gives unauthorized . But when I hit it using a test endpoint it works perfectly fine. I guess connection factory for actuator is not able to set those credentials while hitting the queue. My question is - is there any way we can disable the actuator for jms or pass credentials to the connection factory used by the actuator in the above code? – Sugandha Mishra Aug 24 '23 at 20:12
  • Is it setting `JmsConstants.USER_AUTHENTICATION_MQCSP`? – JoshMc Aug 24 '23 at 21:53
  • yes , I have added below code - mqQueueConnectionFactory.setBooleanProperty(com.ibm.msg.client.jakarta.jms.JmsConstants.USER_AUTHENTICATION_MQCSP, false); For now, I have added management.health.jms.enabled=false, so it is not triggering the health check on startup :) – Sugandha Mishra Aug 27 '23 at 13:22