IBM Support

Best Practice: Catching and re-throwing Java Exceptions

Question & Answer


Question

What is the correct Java™ programming best practice to catch, print, and re-throw Java exceptions?

Cause

Problem determination is often hampered by mysterious errors, misleading information, or missing stack traces.

Answer

It is a well-known best practice that a Java application should not suppress caught exceptions with blank catch blocks; however, there are more subtle mistakes that Java applications make which can hamper problem determination. Here are 3 malpractices:




// #1. Worst -- there is no indication that an exception
// occurred and processing continues.
try {
     // do work
} catch (Throwable t) {
}

// #2. Very Bad -- there is an indication that an
// exception occurred but there is no stack trace, and
// processing continues.
try {
     // do work
} catch (Throwable t) {
     System.err.println("Error: " + t.getMessage());
}

// #3. Incorrect. The stack trace of the original
// exception is lost. In the case of an Exception such as
// a NullPointerException, getMessage() will return a
// blank string, so there will be little indication of
// the problem.
try {
     // do work
} catch (Throwable t) {
     throw new ServletException("Error: " + t.getMessage());
}


The problem with #3 is that the ServletException will be shown in SystemOut.log but the stack trace and message will simply point to the ServletException which was created within the catch block. The true root problem is the caught exception, t, which has been lost because of the lack of a call to t.printStackTrace() and the lack of passing the parameter t as an inner exception to the ServletException.

The correct way to catch and re-throw an exception is to pass the caught exception object as the "rootCause" or inner exception parameter to the constructor of the new exception (note that not all exception constructors support inner exceptions, in which case a different wrapper exception should be used). When the exception is later caught and printed to SystemOut.log, the inner exception message and stack will be included:





// #4. Correct. Note the exception, t, is passed as the second
//     parameter to the ServletException constructor.
try {
     // do work
} catch (Throwable t) {
     throw new ServletException("Error: " + t.getMessage(), t);
}

// #5. Also correct.
try {
     // do work
} catch (Throwable t) {
     try {
          // Perform some application logging or auditing
          // e.g. t.printStackTrace();
     } catch (Throwable tAppDebug) {
          tAppDebug.printStackTrace();
          throw t;
     }
}


Customers often have general catch blocks in Servlets, MDBs, EJBs and other core components where they catch all un-handled exceptions and re-throw them as new Exceptions, adding application specific debugging information or auditing information. Exception handling malpractices such as those described above have been a source of many major customer outages.

Finally, there is a case where a developer is "stuck" catching a non-Runtime exception that cannot be re-thrown (For example: "throw t") because the method signature does not allow it, such as a restricted list of checked exceptions. In this case, a developer may throw a RuntimeException, which is unchecked, although it should be clear that this should be the last option used. This is a hack around the underpinnings of Java's exception model, and it should have a proper fix through architectural changes of the code:





// #6. A hack but it is better than suppression.
try {
     // do work
} catch (Throwable t) {
     throw new Error(t);
}

[{"Product":{"code":"SSEQTP","label":"WebSphere Application Server"},"Business Unit":{"code":"BU053","label":"Cloud & Data Platform"},"Component":"General","Platform":[{"code":"PF002","label":"AIX"},{"code":"PF010","label":"HP-UX"},{"code":"PF012","label":"IBM i"},{"code":"PF016","label":"Linux"},{"code":"PF027","label":"Solaris"},{"code":"PF033","label":"Windows"}],"Version":"7.0;6.1.1;6.1;6.0.2;6.0.1;6.0;5.1.1;5.1","Edition":"","Line of Business":{"code":"LOB45","label":"Automation"}},{"Product":{"code":"SS7K4U","label":"WebSphere Application Server for z\/OS"},"Business Unit":{"code":"BU053","label":"Cloud & Data Platform"},"Component":null,"Platform":[{"code":"PF035","label":"z\/OS"}],"Version":"7.0;6.1;6.0","Edition":"","Line of Business":{"code":"LOB45","label":"Automation"}}]

Document Information

Modified date:
15 June 2018

UID

swg21386753