Search code examples
javamavenibm-mqspring-jms

Determining When an MQ Message Queue is empty (all messages have been read)


I'm creating an error monitoring tool using Spring JMS and I am currently reading messages off of the queue and logging the errors. The angle I am going for is storing all the messages from our error queue into a map. The key for the map is basically the error info and the value is the count, as we would like to know how many of these there are. When all of the messages are read off and the queue has 0 messages on it, I would like to then run file creation portion of the application and finally terminate the application.

I have ran across these posts: Check MQ queue depth, How to check whether have message in the queue, Websphere 7 MQueue: how to access queue depth from Java?

I have tried a hodge podge of each of them and also looked over IBM's Knowledge Center and haven't gained any success. This what I currently have:

public int depthOfQueue() throws MQException {
        queueManager = new MQQueueManager(queueManagerName); // fails here
        MQQueue queue = queueManager.accessQueue(queueName, CMQC.MQOO_OUTPUT);
        int depth = queue.getCurrentDepth();
        queueManager.disconnect();
        queue.close();
        return depth;
    }

But in my logs I get stack traces like:

java.lang.NoClassDefFoundError: Could not initialize class com.ibm.mq.internal.MQCommonServices
    at com.ibm.mq.MQSESSION.getJmqiEnv(MQSESSION.java:142) ~[com.ibm.mq-7.0.1.9.jar:7.0.1.9 - k701-109-120705]
    at com.ibm.mq.MQQueueManagerFactory.<init>(MQQueueManagerFactory.java:85) ~[com.ibm.mq-7.0.1.9.jar:7.0.1.9 - k701-109-120705]
    at com.ibm.mq.MQQueueManagerFactory.getInstance(MQQueueManagerFactory.java:112) ~[com.ibm.mq-7.0.1.9.jar:7.0.1.9 - k701-109-120705]
    at com.ibm.mq.MQQueueManager.<clinit>(MQQueueManager.java:156) ~[com.ibm.mq-7.0.1.9.jar:7.0.1.9 - k701-109-120705]
    at org.mycom.casts.MQReader.depthOfQueue(MQReader.java:56) ~[classes/:?]
    at org.mycom.casts.MQReader.processOrder(MQReader.java:36) ~[classes/:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_74]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_74]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_74]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_74]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:198) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:116) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:90) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:66) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:721) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:681) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:315) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:253) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1150) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1142) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1039) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at java.lang.Thread.run(Unknown Source) [?:1.8.0_74]

and

java.lang.NoClassDefFoundError: Could not initialize class com.ibm.mq.MQQueueManager
    at org.mycom.casts.MQReader.depthOfQueue(MQReader.java:56) ~[classes/:?]
    at org.mycom.casts.MQReader.processOrder(MQReader.java:36) ~[classes/:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_74]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_74]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_74]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_74]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:198) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:116) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:90) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:66) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:721) ~[spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:681) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:315) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:253) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1150) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1142) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1039) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at java.lang.Thread.run(Unknown Source) [?:1.8.0_74]

I've read that I could be missing dependencies for MQ but I have what I need in my pom. I also read that I could download a client version of MQ (or something), not in a position to do that.

And as someone pointed out in this post: IBM MQ getCurrentDepth requires accessQueue

Finally, there should NEVER be a need to use the getCurrentDepth method in an MQ application. You either get a single message or loop until an MQException is thrown with MQRC_NO_MSG_AVAILABLE (2033) reason code.

So my question is. How can I resolve the Exceptions that are getting thrown (maybe I overlooked something), or what is the best way to check for MQException MQRC_NO_MSG_AVAILABLE (2033)? As this should also be a signal that the queue is empty.

Last clarification, I am using Spring JMS with annotations to set up and read from the queue, all of which is working, I just need to know when the queue is empty.

And just in case here is the listener method:

@JmsListener(destination = "${mq.queueName}")
public void processOrder(String message) throws MQException {
    int messageCount = depthOfQueue();  //problem line
    String messageType = "";
    if(message.startsWith("MSH|") && message.contains("ZER|")){
        messageType = "HL7";
        manager.filterMessage(messageType, message);
    } else if (message.contains(""/*ADD XML CHECK HERE*/)) {
        messageType = "XML";
        manager.filterMessage(messageType, message);
    } else {
        logger.error("Message:\n"+message+"\n does not meet conditions for error");
    }
}

Solution

  • Important note pointed out by todji for this solution:

    Note: You're user needs to be given inq permissions on the queue for this to work: setmqaut -m QM1 -n Q1 -t queue -p mq-app +inq

    Actually, for anyone digging around for this same issue, I got the functionality I needed from these two methods

    public int depthOfQueue() throws MQException {
            queueManager = createQueueManager();
            int openOptions = MQConstants.MQOO_INPUT_AS_Q_DEF | MQConstants.MQOO_OUTPUT | MQConstants.MQOO_INQUIRE;
            MQQueue queue = queueManager.accessQueue(queueName, openOptions);
            int depth = queue.getCurrentDepth();
            queueManager.disconnect();
            queue.close();
            return depth;
        }
    

    and

    @SuppressWarnings("unchecked")
        public MQQueueManager createQueueManager() throws MQException {
            MQEnvironment.port = port;
            MQEnvironment.hostname = host;
            MQEnvironment.channel = channel; 
            MQEnvironment.properties.put(MQConstants.TRANSPORT_PROPERTY, MQConstants.TRANSPORT_MQSERIES);
            return new MQQueueManager(queueManagerName);
        }
    

    using these:

    import com.ibm.mq.*; import com.ibm.mq.constants.MQConstants;

    And you'll need the proper dependencies in the pom:

    <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mq</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mq.headers</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mq.commonservices</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mq.pcf</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mqjms</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>com.ibm.mq.jmqi</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>com.ibm.mq</groupId>
                <artifactId>dhbcore</artifactId>
                <version>7.0.1.9</version>
            </dependency>
            <dependency>
                <groupId>javax.resource</groupId>
                <artifactId>connector</artifactId>
                <version>1.0</version>
            </dependency>
    

    Probably a better way, but this will do the job.