[JMS_SPEC-113] Clarify the difference (if any) between JMSException.getLinkedException() and JMSException.getCause() Created: 08/Jan/13  Updated: 12/Feb/16

Status: Open
Project: jms-spec
Component/s: None
Affects Version/s: 2.0
Fix Version/s: None

Type: Improvement Priority: Major
Reporter: axel_podehl Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: jms21-forreview-minor

 Description   

JMSException.getLinkedException() is usually containing the provider-specific Exception object, but there's also already the inherited Exception.getCause() method.
JMS Providers could potentially return different things in each method, which would be confusing.

Also, 'exception un-nesting code' would typically use Exception.getCause() multiple times until getCause() returns null to get to the root cause which then usually contains a better error description. Since this 'exception un-nesting code' could catch exceptions from multiple API's it would be nice if JMS behaves just like any other library (let getCause() return the cause of the exception).

My proposal would be to require JMS providers to implement JMSException.getCause() like this:

    @Override
    public Throwable getCause()
    {
        return getLinkedException();
    }

That way, JMS wouldn't play a special role when interpreting the cause of an exception.



 Comments   
Comment by Nigel Deakin [ 11/Jan/13 ]

Separately to this, the following comment was posted to users@jms-spec.java.net. I'm pasting it here so it can be considered as well. (Here's the direct link ):

I was wondering why JMSException does not support Java 1.4 exception chaining [1] (#getCause() vs. #getLinkedException()). It should be easy to retrofit (similar to InvocationTargetException) and reduce the confusion and potential for misuse. Also a constructor that takes a message string and a cause (like every other exception class) would be nice.

[1] http://docs.oracle.com/javase/1.4.2/docs/guide/lang/chained-exceptions.html

Cheers
Philippe

Comment by Nigel Deakin [ 11/Jan/13 ]

These are both interesting and valid comments. However now JMS 2.0 has reached the public draft stage we need to avoid adding new features if at all possible and as this feature is unrelated to any of the changes in JMS 2.0 I think we should defer considering until JMS 2.1. This will allow time for both issues to be considered properly.

Tagging appropriately.

Comment by braghest [ 01/Nov/15 ]

The following is an attempt at a patch:

  • implements #getCause() to return the linked exception, this is consistent the the proposal of axel_podehl and how InvocationTargetException has been retrofitted in 1.4
  • pass a cause to the super constructor, this is taken from InvocationTargetException and prevents calls to #initCause
  • add two new constructors which additionally take a cause/linked exception
  • the serialVersionUID is already present and remains unchanged
Index: jms2.1/src/main/java/javax/jms/JMSException.java
===================================================================
--- jms2.1/src/main/java/javax/jms/JMSException.java	(revision 345)
+++ jms2.1/src/main/java/javax/jms/JMSException.java	(working copy)
@@ -49,12 +49,15 @@
  *        the standard exception message and is available via the
  *        {@code getMessage} method.
  *   <LI> A provider-specific string error code 
- *   <LI> A reference to another exception. Often a JMS API exception will 
- *        be the result of a lower-level problem. If appropriate, this 
- *        lower-level exception can be linked to the JMS API exception.
  * </UL>
  * 
- * @version JMS 2.0
+ * <P><p>As of release 2.1, this exception has been retrofitted to conform to
+ * the general purpose exception-chaining mechanism. The "linked exception"
+ * that is accessed via the {@link #getLinkedException()} method is now known
+ * as the <i>cause</i>, and may be accessed via the {@link Throwable#getCause()}
+ * method, as well as the aforementioned "legacy method."
+ * 
+ * @version JMS 2.1
  * @since JMS 1.0
  * 
  **/
@@ -84,7 +87,7 @@
    **/
   public 
   JMSException(String reason, String errorCode) {
-    super(reason);
+    super(reason, null);  // Disallow initCause
     this.errorCode = errorCode;
     linkedException = null;
   }
@@ -96,10 +99,38 @@
    **/
   public 
   JMSException(String reason) {
-    super(reason);
+    super(reason, null);  // Disallow initCause
     this.errorCode = null;
     linkedException = null;
   }
+  
+  /** Constructs a {@code JMSException} with the specified reason 
+   *  error code and linked exception.
+   *
+   *  @param  reason        a description of the exception
+   *  @param  errorCode     a string specifying the vendor-specific
+   *                        error code
+   *  @param  ex            the linked {@code Exception}
+   **/
+  public 
+  JMSException(String reason, String errorCode, Exception ex) {
+    super(reason, null);  // Disallow initCause
+    this.errorCode = errorCode;
+    linkedException = ex;
+  }
+  
+  /** Constructs a {@code JMSException} with the specified reason and
+   * linked exception and with the error code defaulting to null.
+   *
+   *  @param  reason        a description of the exception
+   *  @param  ex            the linked {@code Exception}
+   **/
+  public 
+  JMSException(String reason, Exception ex) {
+    super(reason, null);  // Disallow initCause
+    this.errorCode = null;
+    linkedException = ex;
+  }
 
   /** Gets the vendor-specific error code.
    *  @return   a string specifying the vendor-specific
@@ -112,6 +143,10 @@
 
   /**
    * Gets the exception linked to this one.
+   * 
+   * <p>This method predates the general-purpose exception chaining facility.
+   * The {@link Throwable#getCause()} method is now the preferred means of
+   * obtaining this information.
    *
    * @return the linked {@code Exception}, null if none
   **/
@@ -128,4 +163,16 @@
   public void setLinkedException(Exception ex) {
       linkedException = ex;
   } 
+
+  /**
+   * Returns the cause of this exception (the thrown target exception,
+   * which may be {@code null}).
+   *
+   * @return  the cause of this exception.
+   * @since   2.1
+   */
+  @Override
+  public Throwable getCause() {
+    return (linkedException);
+  }
 }

Comment by braghest [ 12/Feb/16 ]

I just found out that EjbException#getCausedByException() was retrofitted in much the same way.

Generated at Thu Dec 08 20:37:42 UTC 2016 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.