0

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");
    }
}
0

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Disclaimer, this technique has a very low performance rating when reading from the queue as depth is accessed for every message.
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
0

You don't have the required MQ JARs on your classpath. Having the JARs in the pom is one thing, but do you have them in your Maven repository as well?

If you are using JMS, these are the JARs you need: https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.0.1/com.ibm.mq.csqzaw.doc/jm10330_.htm

You would only need the MQ Client to get the MQ JARs from it's install directory. If you can get the MQ JARs from the server, or your Maven repository, then you don't need the client.

Regarding your original question you should first decide which MQ API you will be using, choose either the JMS API, or the MQ classes for Java, but in your question you seem to be mixing the two.

2 Comments

Everything so far has been using spring JMS. So not veering off that path would be great, but from what I read JMS doesn't have any direct way of determining queue size. I decided to use the MQ classes as I already have the dependencies in my .m2 and thought it'd be an easy implementation. I simply need to know when the queue is empty. So I don't necessarily need the depth of the queue for anything other than seeing it is zero so the program can write to a file and terminate
It would appear that I was missing the headers jar in my pom.xml. Silly oversight. I didn't see that in the link you posted but you got me digging.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.