Recipe9.1.Simplifying Exception Processing in an Action


Recipe 9.1. Simplifying Exception Processing in an Action

Problem

You want to reduce the number of TRy . . . catch blocks within your Action classes.

Solution

Remove the exception-handling code from your Action, and define global and local exception handlers in your struts-config.xml file, as shown in Example 9-1.

Example 9-1. Global and local exception handling (partial)
... <global-exceptions>     <exception key="error.unknown.user"                type="com.oreilly.strutsckbk.ch09.UnknownUserException"               path="/securityError.jsp"/>     </global-exceptions> ... <action-mappings>     <action    path="/Login"                type="com.oreilly.strutsckbk.ch09.LoginAction"               scope="request"                name="LoginForm"             validate="true"               input="/login.jsp">         <exception key="error.password.match"                    type="com.oreilly.strutsckbk.ch09.PasswordMatchException">         <forward name="success" path="/login_success.jsp"/>     </action> ...

Discussion

Prior to Struts 1.1, the handling of exceptions was left to the devloper's devices. Exceptions returned from calls to the business layer from an Action had to be handled individually in your code. Because the perform( ) method of Struts 1.0 only allowed you to throw IOException and ServletException, you didn't have much choice in the matter:

public ActionForward perform( ActionMapping mapping,                                ActionForm form,                               HttpServletRequest request,                                HttpServletResponse response)     throws IOException, ServletException { ...

Any checked exception thrown within the body of perform() had to be caught and handled. In some cases, it was appropriate to catch the exception, generate an ActionError, and forward to the input page, much like a validation failure. But more often, the exception couldn't be handled by the application, so the developer returned a ServletException wrapped around the application exception. With Struts 1.1, the perform() method was deprecated and the execute( ) method was introduced. Unlike perform(), execute( ) can throw any exception, and once an exception is thrown, you can process using exception handlers configured in your struts-config.xml file.

If you migrated an Action class from Struts 1.0 to Struts without using declarative exception handling, the class might look something like Example 9-2.

Example 9-2. Action without declarative exception handling
package com.oreilly.strutsckbk.ch09; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.PropertyUtils; import org.apache.struts.action.Action; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class LoginAction extends Action {     public ActionForward execute(ActionMapping mapping,                                    ActionForm form,                                   HttpServletRequest request,                                    HttpServletResponse response)              throws Exception {         String username = null;         String password = null;                  ActionErrors errors = new ActionErrors( );         try {             username = (String) PropertyUtils.getSimpleProperty(form,                                                           "username");             password = (String) PropertyUtils.getSimpleProperty(form,                                                           "password");         } catch (Exception e) {             throw new IOException("Unable to retrieve username and                                                            password");         }                  SecurityService service = new SecurityService( );         try {             service.authenticate( username, password);         } catch (UnknownUserException e1) {             errors.add(ActionErrors.GLOBAL_ERROR,                 new ActionError("error.unknown.user"));             saveErrors(request, errors);             return mapping.findForward("securityError");         } catch (PasswordMatchException e) {             errors.add(ActionErrors.GLOBAL_ERROR,              new ActionError("error.password.match"));         }                 // Report any errors we have discovered back to the original form         if (!errors.isEmpty( )) {             saveErrors(request, errors);             return (mapping.getInputForward( ));         }                  User user = new User( );         user.setUsername(username);         request.getSession( ).setAttribute("user", user);         return mapping.findForward("success");     } }

There's a lot of code here whose sole purpose is handling exceptions. The first try . . . catch block handles technical exceptions thrown in getting data from the form. The more interesting application exceptions are thrown by the SecurityService. The UnknownUserException is handled by generating an error, saving the errors in the request, and forwarding to a Struts forward. The PasswordMatchException is handled similarly, except that the page is forwarded to the input path for the action, where the error will probably be reported and the user can try again.

All of this exception-handling code can be eliminated by using declarative exception handling. Example 9-3 shows the LoginAction with the exception handling removed.

Example 9-3. LoginAction without exception handling
package com.oreilly.strutsckbk.ch09; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.PropertyUtils; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class LoginAction extends Action {     public ActionForward execute(ActionMapping mapping,                                    ActionForm form,                                   HttpServletRequest request,                                    HttpServletResponse response)              throws Exception {         String username = (String)               PropertyUtils.getSimpleProperty(form, "username");         String password = (String)               PropertyUtils.getSimpleProperty(form, "password");                  SecurityService service = new SecurityService( );         service.authenticate( username, password);         User user = new User( );         user.setUsername(username);         request.getSession( ).setAttribute("user", user);         return mapping.findForward("success");     } }

The exceptions are now handled declaratively, as shown in the Solution. The UnknownUserException is declared as a global exception:

<global-exceptions>     <exception key="error.unknown.user"                type="com.oreilly.strutsckbk.ch09.UnknownUserException"               path="/securityError.jsp"/>     ...    </global-exceptions>

The key specifies a MessageResources message that will be used to generate an ActionError. The path specifies the name of the resource to forward to. If path is omitted, the input page for an action will be used. The generated ActionError is stored in an ActionErrors object and can be displayed on the destination page using the <html:errors/> tag.

Any ActionErrors stored in the request by your Action will be replaced if your Action throws an exception handled declaratively. If you want to preserve existing ActionErrors, you'll need to create a custom ExceptionHandler .


Because this exception is declared as a global-exception, any action that throws an UnknownUserException will be handled by this declaration unless overridden by a local exception.

A local exception is defined by nesting the exception element within the action element to which it applies. It defines action-specific handling for specified types of exceptions. The PasswordMatchException is handled by a local exception:

<action path="/Login" ...>     <exception key="error.password.match"                type="com.oreilly.strutsckbk.ch09.PasswordMatchException"               path="/login.jsp"/>     <forward .../> </action>

So what happens when you throw an exception that doesn't have a declared handler? These exceptions will be wrapped in a ServletException and ultimately thrown by the ActionServlet.service( ) method to be handled by the application server.

See Also

For more information on custom display of error messages, see Recipe 9-6.

You can provide custom exception handling by extending the Struts exception handler. This approach is discussed in Recipe 9.2.

If you want to provide a fallback exception-handling approach instead of relying on the container, take a look at Recipe 9.4.



    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