Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Exceptional practices, Part 2

Use exception chaining to preserve debugging information

  • Print
  • Feedback

Page 2 of 3

Don't forget the stack trace

However, the loadResource implementation shown above throws away an important piece of information when it wraps the SQLException in a ResourceLoadException -- the stack trace where the exception originated. When you create and throw a new ResourceLoadException object, the stack trace contained in the new exception is the stack trace where the new exception was created, not where the original error actually occurred. If the error is in the loadResourceFromDB() method, or in some method that it called, you can't determine the error's original location because the stack trace is gone. At best, you can guess where it might have been thrown.

If the original exception was thrown from code for which you don't have source code, such as a vendor-provided class library or a Java Database Connectivity (JDBC) driver, the problem worsens. If you submit a problem report to a vendor, the company will likely ask for information about the exception's location. But since the stack trace has been discarded, all you can tell them is "somewhere in some code called by your library."

The solution to this problem is fairly simple -- make the original exception part of the new exception's state information. You can accomplish that quite easily by creating a base class for chained exceptions and simply modifying ResourceLoader and ResourceLoadException.

The ChainedException class

You can use the class below, ChainedException, as a base class for chained exceptions. It provides constructors that take an exception as one of the arguments, and it overrides the printStackTrace() method to print both stack traces. Concrete exception classes need only extend ChainedException and provide the desired constructors.

public class ChainedException extends Exception { 
  private Throwable cause = null;
  public ChainedException() {
    super();
  }
  public ChainedException(String message) { 
    super(message);
  }
  public ChainedException(String message, Throwable cause) {
    super(message);
    this.cause = cause;
  }
  public Throwable getCause() {
    return cause;
  }
  public void printStackTrace() {
    super.printStackTrace();
    if (cause != null) {
      System.err.println("Caused by:");
      cause.printStackTrace();
    }
  }
  public void printStackTrace(java.io.PrintStream ps) {
    super.printStackTrace(ps);
    if (cause != null) {
      ps.println("Caused by:");
      cause.printStackTrace(ps);
    }
  }
  public void printStackTrace(java.io.PrintWriter pw) {
    super.printStackTrace(pw);
    if (cause != null) {
      pw.println("Caused by:");
      cause.printStackTrace(pw);
    }
  }
}


Integrating ChainedException into our example

Modifying ResourceLoadException to use ChainedException is quite simple; just extend ChainedException instead of Exception and provide appropriate constructors. If you've built a hierarchy of exception classes for your application, as many applications do, just have your base exception class extend ChainedException. (If you don't provide a constructor that accepts an exception as an argument, ChainedException behaves exactly like Exception, therefore, you can safely use it as a base class for all your application's exceptions, whether or not they actually wrap other exceptions.)

  • Print
  • Feedback

Resources