Recipe9.2.Custom Processing for Declared Exceptions


Recipe 9.2. Custom Processing for Declared Exceptions

Problem

Your application requires specialized handling for certain types of exceptions.

Solution

Extend the Struts ExceptionHandler with your own class such as the one shown in Example 9-4.

Example 9-4. Extending the Struts exception handler
package com.oreilly.strutsckbk.ch09; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ExceptionHandler; import org.apache.struts.config.ExceptionConfig; public class CustomExceptionHandler extends ExceptionHandler {     public ActionForward execute(Exception ex, ExceptionConfig ae,             ActionMapping mapping, ActionForm formInstance,             HttpServletRequest request, HttpServletResponse response)             throws ServletException {         // TODO Add custom code here to completely control handling         return super.execute(ex, ae, mapping, formInstance, request,                                                             response);     }     protected void logException(Exception e) {         // TODO Add custom code here for exception logging         System.out.println("Customized logException for:"+e);         super.logException(e);     }     protected void storeException(HttpServletRequest request, String      property, ActionMessage error, ActionForward forward, String scope) {         // TODO Add custom code here for storing errors         System.out.println("Customized error storing for:"+error);         super.storeException(request, property, error, forward, scope);     } }

In your struts-config.xml file, specify your ExceptionHandler's class name as the value for the handler attribute on each exception element you want to use the handler:

<exception key="error.exception"            type="com.oreilly.strutsckbk.ch09.CustomException"        handler="com.oreilly.strutsckbk.ch09.CustomExceptionHandler"           path="/some_error_page.jsp"/>

Discussion

You might think that if you use declarative exceptions you are "locked in" to the Struts-way of processing exceptions. In fact, you are not bound to the Struts exception-handling process for two oft overlooked reasons; Struts was designed for extensibility, and Struts is open source.

When an Action throws an Exception, the RequestProcessor handles it in the processException method:

protected ActionForward processException(HttpServletRequest request,                                          HttpServletResponse response,                                          Exception exception,                                          ActionForm form,                                          ActionMapping mapping)     throws IOException, ServletException {      // Is there a defined handler for this exception?     ExceptionConfig config = mapping.findException(exception.getClass( ));     if (config == null) {         log.warn(getInternal( ).getMessage("unhandledException",                                           exception.getClass( )));         if (exception instanceof IOException) {             throw (IOException) exception;         } else if (exception instanceof ServletException) {             throw (ServletException) exception;         } else {             throw new ServletException(exception);         }     }     // Use the configured exception handling     try {         ExceptionHandler handler = (ExceptionHandler)           RequestUtils.applicationInstance(config.getHandler( ));         return (handler.execute(exception, config, mapping, form,                                 request, response));     } catch (Exception e) {         throw new ServletException(e);     } }

First, the method searches for an exception configurationrepresented as an ExceptionConfig objectfor the exception type. It searches for a local declarative exception; if none can be found, it looks for a global declarative exception. The search mechanism takes object inheritance into accountthat is, if FooException extends BarException and there is an exception element for BarException, then mapping.findException( ) will use that configuration when FooException is thrown. If a declarative exception is found, Struts instantiates the associated ExceptionHandler and calls the handler's execute( ) method.

An ExceptionHandler mimics an Action. Its execute() method accepts the same arguments as Action.execute( ) plus additional arguments for the Exception and ExceptionConfig objects. You create a custom exception handler by extending the ExceptionHandler.

The base ExceptionHandler defines two protected methods designed for extension. The first, logException(), is called to log the thrown exception. Here's the source for this method from the ExceptionHandler class:

protected void logException(Exception e){     log.debug(messages.getMessage("exception.log"), e); }

If you need to provide custom logging behavior, you can override this method; however, you should investigate the Struts logging mechanism before you start adding a lot of custom code (see Recipe 13.2).

The second protected method, storeException(), is somewhat misnamed because it stores an ActionMessage (ActionError in Struts 1.1), created for the exception in the ExceptionHandler.execute( ) method, in the HttpServletRequest or HttpSession. Here's the source for this method from the ExceptionHandler class:

protected void storeException(         HttpServletRequest request,         String property,         ActionMessage error,         ActionForward forward,         String scope) {     ActionMessages errors = new ActionMessages( );     errors.add(property, error);     if ("request".equals(scope)) {         request.setAttribute(Globals.ERROR_KEY, errors);     } else {         request.getSession( ).setAttribute(Globals.ERROR_KEY, errors);     } }

The errors are stored in the request or session based on the value of the scope attribute for the declarative exception. You override storeException( ) if you want to change or augment the base behavior. For example, let's say you wanted to store the error message in a database:

protected void storeException(HttpServletRequest request, String property,         ActionMessage error, ActionForward forward, String scope) {     MessageResources msgRes =          MessageResources.getMessageResources("ApplicationResources");     String msg = msgRes.getMessage(error.getKey( ));     saveMessage(msg);     super.storeException(request, property, error, forward, scope); } private void saveMessage(String msg) {     // store error message in database ... }

If you need to do more than customize the logging or storing of the exception, you will need to override the execute() method. Start by cutting and pasting the code from the base ExceptionHandler's execute( ) method, shown in Example 9-5, into your handler's execute( ) method and modify as needed.

Example 9-5. Base ExceptionHandler's execute method
public ActionForward execute(         Exception ex,         ExceptionConfig ae,         ActionMapping mapping,         ActionForm formInstance,         HttpServletRequest request,         HttpServletResponse response)         throws ServletException {     ActionForward forward = null;     ActionMessage error = null;     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( );     }     // Figure out the error     if (ex instanceof ModuleException) {         error = ((ModuleException) ex).getActionMessage( );         property = ((ModuleException) ex).getProperty( );     } else {         error = new ActionMessage(ae.getKey( ), ex.getMessage( ));         property = error.getKey( );     }     logException(ex);     // Store the exception     request.setAttribute(Globals.EXCEPTION_KEY, ex);     storeException(request, property, error, forward, ae.getScope( ));     return forward; }

The execute( ) method takes the following steps:

  1. The forward path is determined from the ExceptionConfig. If no path is specified, then the action's input path is used (accessed using mapping.getInputForward( )).

  2. If the exception is a ModuleException, the contained ActionMessage is retrieved. Otherwise, a new ActionMessage is generated using the key from the ExceptionConfig.

  3. The exception is logged using the logException( ) method.

  4. The exception is added as an attribute to the HttpServletRequest.

  5. The error is stored by calling storeException( ).

  6. The forward is returned.

The ActionMessage generation (step 2) is a common function you might want to customize. Recipe 9.3 shows an example of this.

See Also

If you want to change the logging of exceptions, consider using the customization features of Struts' logging mechanism instead of a custom exception handler. Recipe 13.2 has details on this approach. You can find information at http://struts.apache.org/userGuide/configuration.html#config_logging.

The Struts User's Guide discusses custom exception handlers in the section found at http://struts.apache.org/userGuide/building_controller.html#exception_handler.

Recipe 9.1 shows you how to configure exception handling in your struts-config.xml file. Recipe 9.3 shows a more ambitious use of customized exception handling.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net