Exceptions in IBM MQ classes for JMS

An IBM® MQ classes for JMS application must be able to handle exceptions that are thrown by a JMS API calls or delivered to an exception handler.

IBM MQ classes for JMS reports runtime problems by throwing exceptions. JMSException is the root class for exceptions thrown by JMS methods, and catching JMSException exceptions provides a generic way of handling all JMS related exceptions.

Every JMSException exception encapsulates the following information:
  • A provider specific exception message, which an application obtains by calling the Throwable.getMessage() method.
  • A provider specific error code, which an application obtains by calling the JMSException.getErrorCode() method.
  • A linked exception. An exception thrown by a JMS API call is often the result of a lower level problem, which is reported by another exception linked to this exception. An application obtains a linked exception by calling the JMSException.getLinkedException() or the Throwable.getCause() method.
Most exceptions thrown by IBM MQ classes for JMS are instances of subclasses of JMSException. These subclasses implement the com.ibm.msg.client.jms.JmsExceptionDetail interface, which provides the following additional information:
  • An explanation of the exception message, which an application obtains by calling the JmsExceptionDetail.getExplanation() method.
  • A recommended user response to the exception, which an application obtains by calling the JmsExceptionDetail.getUserAction() method.
  • The keys for the message inserts in the exception message. An application obtains an iterator for all the keys by calling the JmsExceptionDetail.getKeys() method.
  • The message inserts in the exception message. For example, a message insert might be the name of the queue that caused the exception, and it might be useful for an application to be able to access that name. An application obtains the message insert corresponding to a specified key by calling the JmsExceptionDetail.getValue() method.
All the methods in the JmsExceptionDetail interface might return null if no details are available.
For example, if an application tries to create a message producer for a IBM MQ queue that does not exist, an exception is thrown with the following information:

Message : JMSWMQ2008: Failed to open MQ queue 'Q_test'.
Class : class com.ibm.msg.client.jms.DetailedInvalidDestinationException
Error Code : JMSWMQ2008
Explanation : JMS attempted to perform an MQOPEN, but WebSphere MQ reported an
              error.
User Action : Use the linked exception to determine the cause of this error. Check
              that the specified queue and queue manager are defined correctly.
The exception thrown, com.ibm.msg.client.jms.DetailedInvalidDestinationException, is a subclass of javax.jms.InvalidDestinationException and implements the com.ibm.msg.client.jms.JmsExceptionDetail interface.

Linked exceptions

A linked exception provides further information about a runtime problem. Therefore, for each JMSException exception that is thrown, an application should check the linked exception. The linked exception itself might have another linked exception, and so the linked exceptions form a chain leading back to the original underlying problem. A linked exception is implemented by using the chained exception mechanism of the java.lang.Throwable class, and an application obtains a linked exception by calling the Throwable.getCause() method. For a JMSException exception, the getLinkedException() method actually delegates to the Throwable.getCause() method.

For example, if an application specifies an incorrect port number when connecting to a queue manager, the exceptions form the following chain:

com.ibm.msg.client.jms.DetailIllegalStateException
   |
   +--->com.ibm.mq.MQException
           |
           +--->com.ibm.mq.jmqi.JmqiException
                   |
                   +--->java.net.ConnectionException
Typically, each exception in a chain is thrown from a different layer in the code. For example, the exceptions in the preceding chain are thrown by the following layers:
  • The first exception, an instance of a subclass of JMSException, is thrown by the common layer in IBM MQ classes for JMS.
  • The next exception, an instance of com.ibm.mq.MQException, is thrown by the IBM MQ messaging provider.
  • The next exception, an instance of com.ibm.mq.jmqi.JmqiException, is thrown by the common Java interface to the MQI.
  • The final exception, an instance of java.net.ConnectionException, is thrown by the Java class library.
For more information about the layered architecture of IBM MQ classes for JMS, see IBM MQ classes for JMS architecture.
Using code similar to the following code, an application can iterate through this chain to extract all the appropriate information:

import com.ibm.msg.client.jms.JmsExceptionDetail;
import com.ibm.mq.MQException;
import com.ibm.mq.jmqi.JmqiException;
import javax.jms.JMSException;
.
.
.
catch (JMSException je) {
    System.err.println("Caught JMSException");

    // Check for linked exceptions in JMSException
    Throwable t = je;
    while (t != null) {
        // Write out the message that is applicable to all exceptions
        System.err.println("Exception Msg: " + t.getMessage());
        // Write out the exception stack trace
        t.printStackTrace(System.err);

        // Add on specific information depending on the type of exception
        if (t instanceof JMSException) {
            JMSException je1 = (JMSException) t;
            System.err.println("JMS Error code: " + je1.getErrorCode());

            if (t instanceof JmsExceptionDetail){
                JmsExceptionDetail jed = (JmsExceptionDetail)je1;
                System.err.println("JMS Explanation: " + jed.getExplanation());
                System.err.println("JMS Explanation: " + jed.getUserAction());
            }
        } else if (t instanceof MQException) {
            MQException mqe = (MQException) t;
            System.err.println("WMQ Completion code: " + mqe.getCompCode());
            System.err.println("WMQ Reason code: " + mqe.getReason());
        } else if (t instanceof JmqiException){
            JmqiException jmqie = (JmqiException)t;
            System.err.println("WMQ Log Message: " + jmqie.getWmqLogMessage());
            System.err.println("WMQ Explanation: " + jmqie.getWmqMsgExplanation());
            System.err.println("WMQ Msg Summary: " + jmqie.getWmqMsgSummary());
            System.err.println("WMQ Msg User Response: "
                               + jmqie.getWmqMsgUserResponse());
            System.err.println("WMQ Msg Severity: " + jmqie.getWmqMsgSeverity());
        }

        // Get the next cause
        t = t.getCause();
    }
}
Note that an application should always check the type of each exception in a chain because the type of exception can vary and exceptions of different types encapsulate different information.

Obtaining IBM MQ specific information about a problem

Instances of com.ibm.mq.MQException and com.ibm.mq.jmqi.JmqiException encapsulate IBM MQ specific information about a problem.

An MQException exception encapsulates the following information:
  • A completion code, which an application obtains by calling the getCompCode() method
  • A reason code, which an application obtains by calling the getReason() method

A JmqiException exception also encapsulates a completion code and a reason code. Additionally, however, a JmqiException exception encapsulates the information in an AMQ nnnn or CSQ nnnn message, if one is associated with the exception. By calling the appropriate methods of the exception, an application can obtain the various components of this message, such as the severity, explanation, and user response.

For examples of how to use of the methods mentioned in this section, see the sample code in Linked exceptions.

Upgrading from previous versions of IBM MQ classes for JMS

Compared to previous versions of IBM MQ classes for JMS, most error codes and exception messages changed in Version 7.0. The reason for these changes is that from Version 7.0, IBM MQ classes for JMS has a layered architecture and exceptions are thrown from different layers in the code.

For example, if an application tries to connect to a queue manager that does not exist, a previous version of IBM MQ classes for JMS threw a JMSException exception with the following information:

MQJMS2005: Failed to create MQQueueManager for 'localhost:QM_test'.
This exception contained a linked MQException exception with the following information:

MQJE001: Completion Code 2, Reason 2058
By comparison in the same circumstances, Version 7.0 of IBM MQ classes for JMS throws a JMSException exception with the following information:

Message : JMSWMQ0018: Failed to connect to queue manager 'QM_test' with
          connection mode 'Client' and host name 'localhost'.
Class : class com.ibm.msg.client.jms.DetailedJMSException
Error Code : JMSWMQ0018
Explanation : null
User Action : Check the queue manager is started and if running in client mode,
              check there is a listener running. Please see the linked exception
              for more information.
This exception contains a linked MQException exception with the following information:

Message : JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED')
          reason '2058' ('MQRC_Q_MGR_NAME_ERROR').
Class : class com.ibm.mq.MQException
Completion Code : 2
Reason Code : 2058

If your application parses or tests exception messages returned by the Throwable.getMessage() method, or error codes returned by the JMSException.getErrorCode() method, and you are upgrading from a release before Version 7.0, your application probably needs to be modified in order to use Version 7.0 or later of IBM MQ classes for JMS.

Exception listeners

An application can register an exception listener with a Connection object. Subsequently, if a problem occurs that makes the connection unusable, IBM MQ classes for JMS delivers an exception to the exception listener by calling its onException() method. The application then has the opportunity to reestablish the connection. IBM MQ classes for JMS can also deliver an exception to the exception listener if a problem occurs while trying to deliver a message asynchronously.

[V8.0.0.2 Feb 2015]From IBM MQ 8.0.0, Fix Pack 2, to maintain behavior for current JMS applications that configure a JMS MessageListener and a JMS ExceptionListener, and to ensure that the IBM MQ classes for JMS are consistent with the JMS specification, the default value for the ASYNC_EXCEPTIONS JMS ConnectionFactory property is changed to ASYNC_EXCEPTIONS_CONNECTIONBROKEN for the IBM MQ classes for JMS. As a result, by default, only exceptions corresponding to broken connection error codes are delivered to an application's JMS ExceptionListener.

[V8.0.0.6 Jan 2017]APAR IT14820, included from IBM MQ 8.0.0, Fix Pack 6, updates IBM MQ classes for JMS so that:
  • An ExceptionListener registered by an application is invoked for any connection broken exceptions, regardless of whether the application is using synchronous or asynchronous message consumers.
  • An ExceptionListener registered by an application is invoked if a TCP/IP socket used by a JMS Session is broken.
  • Non-connection broken exceptions (for example MQRC_GET_INHIBITED) that arise during message delivery are delivered to an application's ExceptionListener when the application is using asynchronous message consumers and the JMS ConnectionFactory used by the application has the ASYNC_EXCEPTIONS property set to the value ASYNC_EXCEPTIONS_ALL.
Note: An ExceptionListener is only invoked once for a connection broken exception, even if two TCP/IP connections (one used by a JMS Connection and one used by a JMS Session) are broken.

For any other type of problem, a JMSException exception is thrown by the current JMS API call.

If an application does not register an exception listener with a Connection object, any exceptions that would have been delivered to the exception listener are written to the IBM MQ classes for JMS for JMS log.