Exception handling - Struts way


Exception handling ‚ Struts way

ServletException , IOException , RuntimeException and their sub classes can be declaratively mapped to appropriate JSP files through the web.xml settings. What about the other Exceptions? Fortunately since Struts1.1, you can assign JSP files for other checked exceptions too. Let us start by examining the features in Struts 1.1 for exception handling.

Declarative exception handling

Consider the method signature for the execute method in the Struts Action class.

 public ActionForward execute(ActionMapping mapping,                              ActionForm form,                              HttpServletRequest request,                              HttpServletResponse response)                       throws java.lang.Exception 

The execute() method has java.lang.Exception in its throws clause. Hence you don ‚ t have to handle the exceptions explicitly in Action. You can let them fall through. Consider the execute() method from an Action class.

 public ActionForward execute(...) throws java.lang.Exception {    ActionForward nextPage = null;       ..    userControllerEJB.createUser(UserInfo info);    ..    mapping.findForward(success); } 

The execute() method invokes the createUser() method on UserControllerEJB ‚ a Session EJB that is responsible for creating the users. The createUser() method throws two Exceptions ‚ RemoteException and DuplicateUserException . If the user cannot be created because another user with same id exists, then the Session EJB throws DuplicateUserException . A RemoteException is thrown if the user cannot be created because of problems in looking up or creating the Session EJB. If everything goes fine, then the user is forwarded to the ActionForward identified by success . However we have made no attempt to catch them and handle. Instead we have deferred their handling to Struts through the declarative exception handling facility.

Listing 9.5 shows the Struts Config file with declarative exception handling for the two exceptions ‚ DuplicateUserexception and RemoteException . For each exception, an < exception > element is defined in the action mapping. The path attribute in the < exception > element specifies the page shown to the user upon that exception. For instance, if a DuplicateUserException is thrown when submitting the modified user profile, the controller will forward control to the UserExists.jsp page. The key attribute is used to retrieve the error message template from the associated resource bundle. Since the < exception > is local to the action mapping, it applies only for that action invocation. As you might have already notice the J2EE and Struts way of declaratively handling exceptions are complementary to one another.

Listing 9.5: Declarative Exception Handling in Struts
 <struts-config>  <action-mappings>       <action      path="/submitCustomerForm"     type="mybank.example.CustomerAction"     name="customerForm"     scope="request"     input="/CustomerDetails.jsp">   <  exception   key="database.error.duplicate"   path="/UserExists.jsp"   type="mybank.account.DuplicateUserException"/  >   <  exception   key="rmi.error"   type="java.rmi.RemoteException"   path="/rmierror.jsp"/  >   </action>  </action-mappings> </struts-config> 
 

In the Listing 9.5, the declarative exception handling was local to the CustomerAction . You can add global declarative exception handling too. For instance, if you want to handle the RemoteException in the same way across the board, use the following approach:

 <struts-config>    <global-exceptions>     <exception        key="rmi.error"       type="java.rmi.RemoteException"       path="/rmierror.jsp"/>          </global-exceptions> </struts-config> 

Before forwarding to the page indicated in the < exception > element, Struts sets the exception as a request attribute with name org.apache.struts.action.EXCEPTION . (This is the value of Globals.EXCEPTION_KEY . Globals is a Java class in org.apache.struts package). The exception can be retrieved in the error page by using the method: request.getAttribute(Globals.EXCEPTION_KEY) .

Using the ExceptionHandler

Apart from key , type and path , the < exception > element also takes several optional attributes of which handler is a significant one. It is the fully qualified class name of the exception handler for that exception. By default org.apache.struts.action.ExceptionHandler is the class used. You can create a custom handler by extending the ExceptionHandler and overriding the execute() method. The execute() method has the following signature:

 public ActionForward execute(Exception ex, ExceptionConfig ae,         ActionMapping mapping, ActionForm formInstance,         HttpServletRequest request,          HttpServletResponse response) throws ServletException 

To understand the ExceptionHandler , you have to understand the RequestProcessor workings on exception. As it does everything else, RequestProcessor invokes the execute() method on the Action instance. Hence it is natural that the exception thrown in the execute() is caught by the RequestProcessor . On receiving the exception, here is what the RequestProcessor does:

  • It checks to see if the exception has an associated < exception > declaration either in local or global scope.

  • If none exists, then the exception is:

  • Thrown AS IS if it is ServletException , IOException or their subclasses.

  • Wrapped in a ServletException and thrown if the above criteria is not satisfied.

  • If there is a < exception > element declared then it retrieves the handler class, instantiates it and invokes execute() method in it. The default exception handler returns the path attribute of the < exception > element as an ActionForward.

As you will see later in this section, you can use a custom Exception Handler to centralize exception logging in the web tier .

When not to use declarative exception handling

Very frequently you would like to generate an ActionError and display it to the user instead of an exception. Let us look back at Listing 9.5 again for a moment. When RemoteException is thrown, the user sees rmierror.jsp . This makes sense since RemoteException is tantamount to a system exception and the only thing you can do is to ask the user to start all over again. However, it does not make sense to ask the user to start all over when DuplicateUserException is thrown since this is an application exception from which the user has a recovery path. A better option is to show this as an ActionError and give the user a chance to change the user id. For situations like this, you have to resort to programmatic exception handling.

Listing 9.6 shows the execute() method with programmatic exception handling. It catches the DuplicateUserException and creates an ActionErrors object to hold the error. The ActionErrors is set into the HTTP request as an attribute and then the same Form is shown back. The last part of showing the same page is achieved by the line mapping.getInput() . In this case you have to remove the declarative exception handling from Struts config file since it is being explicitly handled in the code.

Listing 9.6: Alternative to declarative exception handling
 public ActionForward execute(... ...) throws java.lang.Exception {    ActionForward nextPage = null;       try {      ..      ..      userControllerEJB.createUser(UserInfo info);      ..      mapping.findForward("success");    }    catch (DuplicateUserException due)    {      ActionErrors errors = new ActionErrors();      ActionError error = new ActionError("userid.taken",                                       due.getUserId());      errors.add("userid", error);      // This saves the ActionErrors in the request attribute       // with the key Action.ERROR_KEY       saveErrors(request, errors);      nextPage = mapping.getInput();    }    return nextPage; } 
 

If you use declarative exception handling, the default ExceptionHandler will still generate an ActionErrors object. However, the ActionErrors is associated with the page rather than a particular field. If you don ‚ t have this requirement, declarative exception handling is preferred over programmatic exception handling. Just set the initial JSP as the path for the < exception > and use < html:errors/ > on the JSP and you get the exception as if it was an ActionError without any effort from your side.

Exception handling and I18N

Another important matter of concern with exception handling is I18N. Even though the exception logging can occur in the language of your operating system, the messages should still be displayed in the language of the user ‚ s choice. This is not much of a concern is the message is generic. For instance, in Listing 9.5, the message shown to the user on RemoteException is identified by the key rmi.error . The key can have a generic message in the resource bundle. However the problem starts when the message has to get specific or the message requires replacement values. There are two possible solutions to this problem neither of which is ideal.

Here is the first approach: If you want to keep the internationalization in the web tier, then the specific exceptions from the server side should encapsulate the resource bundle keys and some (if not all) replacement values in them. The key and the replacement values can be exposed through getter methods on the exception class. This approach makes the server side code dependent on the web tier resource bundle. This also requires a programmatic exception handling since you have to pass appropriate replacement values to the ActionError.

The second approach is to send the user ‚ s Locale as one of the arguments to the server side and let the server side generate the entire message. This removes the server ‚ s dependency on the web tier code, but requires the Locale to be sent as a argument on every method call to the server.




Struts Survival Guide. Basics to Best Practices
Struts Survival Guide: Basics to Best Practices (J2ee Survival Series)
ISBN: 0974848808
EAN: 2147483647
Year: 2004
Pages: 96

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