Using Struts' Declared Exceptions
With a little prodding from Erik Hatcher today, I took another look at Struts' Declared Exceptions feature. At the end of last year, I was wishing I could use declared exceptions to do chained exceptions for my Action classes. Basically, in each of my Actions, I have a try/catch wrapped around a call to the Business Delegate (example: UserAction.java). You'll notice that all the CRUD methods have the same catch block for exception handling:
} catch (Exception e) { e.printStackTrace(); errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("errors.general")); while (e != null) { errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("errors.detail", e.getMessage())); e = (Exception) e.getCause(); } saveErrors(request, errors); return mapping.getInputForward(); }
After talking with Erik this morning, I decided to create an ActionExceptionHandler for java.lang.Exception
. In my struts-config.xml, I added:
<exception type="java.lang.Exception" key="errors.general" handler="org.appfuse.webapp.action.ActionExceptionHandler"/>
I have "errors.general=The process did not complete. Details should follow." Here is the code for ActionExceptionHandler:
public final class ActionExceptionHandler extends ExceptionHandler { public ActionForward execute(Exception ex, ExceptionConfig ae, ActionMapping mapping, ActionForm formInstance, HttpServletRequest request, HttpServletResponse response) throws ServletException { ActionForward forward = null; ActionError error = null; ActionErrors errors = new ActionErrors(); String property = null; // Build the forward from the exception mapping if it exists // or from the form input if (ae.getPath() != null) { forward = new ActionForward(ae.getPath()); } else { forward = mapping.getInputForward(); } ex.printStackTrace(); errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(error.getKey())); while (ex != null) { errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("errors.detail", ex.getMessage())); ex = (Exception) ex.getCause(); } // Store the errors and exception request.setAttribute(Globals.ERROR_KEY, errors); return forward; } }
This allows me to remove my generic try/catches from my action classes - very slick IMO (or at least better than the code smell I had)! Of course, I still have some catch blocks that catch specific exceptions, but I can either (1) leave those intact, or (2) create another declared exception for that particular action/exception. I dig it and will be adding it (in short order) to AppFuse.
Update (June 23, 2003): Here's a more thoroughly tested code sample of this same class.
Posted by Erik Hatcher on June 20, 2003 at 12:17 AM MDT #
The problem I have with the built-in one is that it doesn't support chained exceptions. Rather it will only grab the message from the top-most exception. My chained exceptions implementation is derived from From Ted Husted's use chained exceptions tip. By doing this, the error displayed will not be simply, "Could not connect to database.", but rather:
This gives the user the generic user-friendly message they desire (the first one), and also allows the support team to realize exactly what went wrong.
Posted by Matt Raible on June 20, 2003 at 12:43 AM MDT #
Posted by Erik Hatcher on June 20, 2003 at 01:16 AM MDT #