The Source for Java Technology Collaboration
User: Password:



Bob Lee

Bob Lee's Blog

Exception Handling in Web Applications

Posted by crazybob on February 06, 2004 at 01:45 PM | Comments (8)

If you've ever diagnosed a bug in a web application, you've undoubtedly experienced annoyance digging through a list of fifteen exception stack traces trying to identify the one you're interested in (if it's even present), or a sinking feeling when you tailed the web server log only to find:

  java.lang.NullPointerException

I sure have. The output to the browser client helps even less, typically churning out the de facto, "Cannot display page," message. Avoid these symptoms of exception handling pitfalls and troubleshoot web applications effortlessly with this simple recipe.

AntiPattern: T.M.I. (Too Much Information)

The first exception throwing antipattern occurs when we repeatedly log or wrap and rethrow an exception every time we catch it:

  catch (Exception e) {
    e.printStackTrace();
    throw new WrappingException(e);
  }

The application prints the stack trace and rewraps the exception fifteen times before it finally propagates to the top where we're often subjected to the stack trace for each wrapper exception. Our visual noise filters go into overload as we sift through a proverbial log hay stack in search of the one stack trace we actually care about.

AntiPattern: Lie of Omission

The second antipattern surfaces when we trash the original stack trace:

  catch (Exception e) {
    // print message only.
    System.err.println(e);
    throw new WrapperException(e.getMessage()); 
  }

If we look at the web server log, we'll see our WrapperException instance's stack trace, which will point to where we caught the original exception as opposed to where it was thrown. Where did the original error occur? Your guess is as good as mine.

AntiPattern: Head in the Sand

This brings us to our final, most evil antipattern, ignoring exceptions:

  catch (Exception e) {
    e.printStackTrace();
  }

Printing the stack trace and going on about your business is on par with trying to drive a car after the wheel has fallen off. Doing so leaves the system in an unpredictable state, often leading to security holes and code that's insanely difficult to debug. It's the modern day incarnation of a segmentation fault.

The moral of this story: don't be afraid to throw your hands in the air and refuse to go on. People entrusting you with their credit card numbers may not thank you, but they'll hold on to their identities a little longer.

Solution: NestedException

Failing early helps avoid these pitfalls. The original stack trace in addition to other pertinent state information (user IDs, primary keys, method arguments, etc.) is a troubleshooter's best friend. I've found that when faced with a checked exception I can't possibly handle, it's best to wrap the exception in a runtime exception (once) and throw it to the top where it can ultimately be thrown to the web container. The exception propagates to the top sans explicit handling until the web container catches and logs it once and only once.

We can accomplish this with a class called NestedException which I originally inherited from my friend and mentor Tim Williams and mentioned in my book Bitter EJB. NestedException wraps exceptions only when necessary (so we don't end up with exceptions nested fifteen deep) and keeps the original stack trace intact. The catch block becomes:

  catch (CheckedException e) {
    throw NestedException.wrap(e);
  }

NestedException consists of a simple wrapper class and static factory method, wrap(Throwable). NestedException overrides all methods to delegate to the wrapped exception (you don't even need to unwrap it to get the message or stack trace you're really interested in):

  public class NestedException extends RuntimeException {

    Throwable throwable;

    private NestedException(Throwable t) {
      this.throwable = t;
    }

    /** Wraps another exeception in a RuntimeException. */
    public static RuntimeException wrap(Throwable t) {
      if (t instanceof RuntimeException) 
        return (RuntimeException) t;
      return new NestedException(t);
    }

    public Throwable getCause() {
      return this.throwable;
    }

    public void printStackTrace() {
      this.throwable.printStackTrace();
    }

    ...
  }

Create a Default Error Page

Now that we've scrubbed our logs, we're left with the matter of output to the web browser. From my experience, only the finest of quality assurance testers take time to look at server logs. I often see bugs filed with the following description:

Did so and so... Browser said "Error 500: Cannot display page."

Not a lot of help. On the other hand, most testers are willing to cut and paste browser output into an issue tracking system. A simple solution is to configure your web application's default error page in the WEB-INF/web.xml file:

  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error.jsp</location>
  </error-page>

Now, when we throw an exception to the container, it forwards the request to /error.jsp providing the exception instance as an implicit variable called "exception" (go figure). The error page is a simple JSP with the isErrorPage page directive set to "true":

  <%@ page isErrorPage="true" import="java.io.PrintWriter" %>

  <html><body>

  <h1 style="color: red">Error</h1>

  <pre>
  <%
  // unwrap ServletExceptions.
  while (exception instanceof ServletException)
    exception = ((ServletException) exception).getRootCause();

  // print stack trace.
  exception.printStackTrace(new PrintWriter(out));
  %>
  </pre>

  </body></html>

The resulting page looks something like this:

Error

com.mycompany.ApplicationException
  at _TestError._jspService(_TestError.java:65)
  at com.vendor.SomeClass.service(SomeClass.java:89)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
  ...

Beautify our error page's design a bit, add some logic to show the stack trace to QA users but hide it from customers, and we're ready to go to production!


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Why not this?
    Another solution is to wrap the exception no more than once, and then throw it up as a regular exception to the top and handle it there. This results in typical command class having one exception for each layer below it (usually two, one for the business layer, and one for the data layer) declared in its method signature. I guess I like seeing which layers something is accessing by the types of exceptions that are thrown on up. Rudundantly clear. I am curious though as to why you would prefer a runtime exception. Just so you don't have to add the throws to the signature of whatever uses it? Or is it more to inforce the standard programmatically that exceptions shall be only wrapped once? I opend a thread on my blog as well (cobbie.com). Reply where you wish.

    Posted by: cobbie on February 07, 2004 at 09:14 AM

  • Why not this?

    I geared this entry towards exceptions caused by programming errors, configuration mistakes, etc., not errors that should be gracefully handled by the application. For example, should my application try to handle an object serialization exception, an illegal access exception, or a missing class exception? Probably not. I want the application to blow up ASAP at which point I'll fix the code or configuration and restart.

    That said, your approach works equally well. The checked vs. unchecked exception debate comes down to a matter of personal preference. The fact that .NET only has unchecked exceptions illustrates the viability of either approach.

    I personally prefer using unchecked exceptions within my code and checked exceptions in my public interfaces, though these rules are not set in stone. For example, I may declare that a method throws an unchecked exception. This still makes the client of my API aware that the method may raise the exception (from reading the Javadocs), but does not force them to explicitly catch it.

    Checked or unchecked, the most important thing is maintaining the original stack trace.

    Posted by: crazybob on February 07, 2004 at 09:53 AM

  • Why not this?
    The relativley simple approach I take in the checked vs unchecked debate is anything which can be expected to fail as a result of user action should be a checked exception - things which might fail due to system or environment problems should be unchecked. The toplevel classes then catch runtime exceptions and report them accordingly. Oversimplistict perhaps, but it seems to work well...

    Posted by: samnewman on February 08, 2004 at 03:59 AM

  • How's This Approach
    I've been investigating many different approaches to Exception Handling in our WebApp for the last few days, and have settled on the following approach.

    The application already exists, so the exception handling approach must be able to be factored into the existing code fairly easily. I also have a base class that all servlets extend, and am using the templating design method to catch all exceptions:

    protected void handleRequest(req, res) {
    try {
    this.process(req, res);
    } catch (Throwable e) {
    this.handleException(e, req, res);
    }
    }

    protected abstract process(req, res);

    private final handleException(e, req, res) {
    // handle the exception
    // will involve logging an exception and showing an
    // error page
    }

    this is fairly standard to my knowledge.

    Anyway, I also have a base RuntimeException called ReportableException, which accepts another exception to chain, and a message:

    public class ReportableException extends RuntimeException {

    public ReportableException(String msg, Throwable e) {
    super(msg, e);
    }
    }

    The purpose of the ReportableException class is to wrap an exception at a layer suitable to be reported to the user. As an example, consider the classic Resource Loading problem. We are attempting to read a source from the disk an recieve an IOException. We obviously don't want to keep throwing an IOException, and thus create a general ResourceLoadException.

    Now, I believe that the user can be made aware that a resource failed to load, and as such, decided that ResourceLoadException can extend ReportableException. This means that any layers above the failed resource load will not be executed, and my handleException method can report the message supplied to the ResourceLoadException (e.g. A required resource could not be loaded) and the low level IOException can be reported to the developer.

    The handleException method at the top level can also add exception specific logic that would otherwise be repeated in multiple locations (e.g. reloading db on sql exception, releasing file on IOException, etc).

    The use of the name ReportableException as the base class indicates that only those exceptions which are to be reported to the user should be RuntimeExceptions. Exceptions that should be checked as part of a public interface should simply extend Exception, and be executed as normal.

    The final guideline that this approach imposes, is that all exceptions that thrown from a method should be declared in the throws clause, regardless of whether they are checked or unchecked. This means that the method attempting to load the resource should declare that it throws a ResourceLoadException. This does not have to be caught of course, but it does promote a better interface.

    Sorry if this was not explained very well (i'm writing this quickly) but I think the general idea is clear.

    How do others feel about this approach? Is it viable. Is there any "better" approaches? Is there any downfalls of this approach/

    Thanks,
    Casey Butterworth

    Posted by: kcbworth on February 12, 2004 at 06:37 PM

  • ExceptionAdapter
    I have been using the ExceptionAdapter class I found in Bruce Eckel's blog (http://www.mindview.net/Etc/Discussions/CheckedExceptions). Is there any advantage to using your NestedException over the ExeptionAdapter class. They seem pretty similar, I'm just wondering if I'm missing something.

    Btw-I missed your AOP class this past weekend at NFJS in St. Louis, but I really enjoyed your panel comments. Especially the one concerning guis vs web apps. As a former C++ gui developer who had to start writing web apps for a living I've recently started writing Java GUIs using SWT and swing using Webstart. I'm enjoying programming again.

    Posted by: robjkc on March 22, 2004 at 03:15 PM

  • ExceptionAdapter
    NestedException doesn't rewrap RuntimeException. This means less unwrapping and cleaner code when we actually do need to handle the original exception. Also, we don't need to differentiate between checked and unchecked exceptions when we wrap--just wrap everything, and NestedException will determine whether or not it really needs to be wrapped.

    Posted by: crazybob on March 22, 2004 at 03:23 PM

  • Still useful with jdk1.4?
    Do you still use NestedException when jdk1.4 is available? Obviously 1.4 supports nested exceptions and so maybe we don't need our own class........your class is nice because you don't need to call getCause(), but I'm not sure if that is a good enough reason to create a new class.

    Posted by: davidteare on April 17, 2004 at 08:53 AM

  • Error displaying in EAR (multiple webapps) ?
    I got a filter that I apply to each webapp web.xml in my EAR, the filter is a class I share in a jar in the EAR. I want the errors detected by the filter (at least) to be handled the same way. I didn't know we could create error-page this way, that's pretty nice, but the jsp we point to must be in the webapp itself. How to do so that I don't have to copy the jsps in each webapps of my EAR. Can I redirect it to a given webapp which I would consider the main webapp of the application ?

    Posted by: haryon on November 24, 2005 at 01:17 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds