Recipe9.4.Using a Global Error Page


Recipe 9.4. Using a Global Error Page

Problem

Your application should display the same error page for any server error or uncaught exception thrown from a Struts Action, a servlet, or a JSP page.

Solution

Declare a global error page, such as the one shown in Example 9-8, to handle all exceptions and errors in your web.xml file as well as your struts-config.xml file.

Example 9-8. Global JSP error page
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <%@ page language="java" isErrorPage="true" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt"%> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %> <html:html> <head>    <title>Struts Cookbook Chapter 9 : Global error page</title>    <style type="text/css">        h2{background:darkblue;color:white}        h3{background:darkblue;color:white}    </style> </head> <body> <div align="center">    <c:choose>       <c:when test="${not empty pageContext.exception}">          <c:set var="problemType">JSP Exception</c:set>          <c:set var="appException" value="${pageContext.exception}"/>           <c:set var="causeException" value="${appException.cause}"/>       </c:when>       <c:when test="${not empty requestScope['javax.servlet.                                                  error.exception']}">          <c:set var="problemType">Servlet Exception</c:set>          <c:set var="appException" value="${requestScope['javax.                                                servlet.error.exception']}"/>           <c:set var="causeException" value="${appException.rootCause}"/>       </c:when>       <c:when test="${not empty requestScope['org.apache.struts.action.                                                  EXCEPTION']}">          <c:set var="problemType">Struts Exception</c:set>          <c:set var="appException" value="${requestScope['org.apache.                                                struts.action.EXCEPTION']}"/>          <c:set var="causeException" value="${appException.cause}"/>       </c:when>       <c:otherwise>          <c:set var="problemType">Unidentified Server Error</c:set>       </c:otherwise>    </c:choose>       <!-- end determine error --> <!-- start framework --> <table cellpadding="0" cellspacing="0" border="0" width="750">     <tr>         <td valign="top" colspan="2">              <table cellpadding="4" cellspacing="0" border="0"            width="100%">             <tr valign="top">                <td>                   <!-- start user review -->                       <table cellpadding="4" cellspacing="0" border="0"                    width="100%">                      <tr>                         <td>                              <h2>System problem</h2>                         </td>                      </tr>                   </table>                   <table cellpadding="2" cellspacing="1" border="0"                    width="80%">                      <tr>                         <td colspan="2">                            A system error has occured. If the                            problem persists, please contact the User Helpdesk.                         </td>                      </tr>                      <tr><td colspan="2">                            <html:errors/>                      </td></tr>                      <tr valign="top">                         <td>                            <b>Problem type</b>                            <br/><c:out value="${problemType}"/>                         </td>                         <td>                            <b>Problem details</b>                            <c:if test="${not empty                             requestScope['javax.servlet.error.message']}">                             <br/>                            <c:out value=                              "${requestScope['javax.servlet.error.message']}"                            />                            </c:if>                            <c:if test="${not empty appException}">                               <br/><c:out value="${appException.message}"/>                               &nbsp;                            </c:if>                         </td>                      </tr>                      <c:if test="${not empty causeException}">                      <tr>                         <td>                            <b>Caused by</b>                            <br/><c:out value="${causeException}"/>                         </td>                         <td>                            <b>Cause details</b>                            <br/><c:out value="${causeException.message}"/>                            &nbsp;                         </td>                      </tr>                      </c:if>                   </table>                   <table  style="{display:inline}"                        cellpadding="2" cellspacing="1" border="0" width="80%">                      <tr>                         <td align="left">                            [ <a href="javascript:showDetails( )">                              Show details</a> ]                         </td>                      </tr>                   </table>                   <table  style="{display:none}"                        cellpadding="2" cellspacing="1" border="0" width="80%">                      <tr>                         <td align="left">                            [ <a href="javascript:hideDetails( )">                              Hide details</a> ]                         </td>                      </tr>                   </table>               <!-- begin details -->                   <div  style="{display:none}">                   <c:if test="${not empty appException}">                      <p></p>                      <table cellpadding="4" cellspacing="0"                              border="0" width="100%">                         <tr>                            <td>                                  <h3>Exception stack trace</h3>                            </td>                         </tr>                      </table>                      <b><c:out value="${appException}"/></b>                      <br/>                      <table align="center" cellpadding="0" cellspacing="0"                             border="0" width="90%" >                         <c:forEach var="stackItem"                                   items="${appException.stackTrace}">                            <tr><td><c:out value="${stackItem}"/></td></tr>                          </c:forEach>                      </table>                   </c:if>                   <c:if test="${not empty causeException}">                      <p></p>                      <table cellpadding="4" cellspacing="0"                              border="0" width="100%">                         <tr>                            <td>                                  <h3>Cause stack trace</h3>                            </td>                         </tr>                      </table>                      <b><c:out value="${causeException}"/></b>                      <br/>                      <table align="center" cellpadding="0" cellspacing="0"                            border="0" width="90%" >                         <c:forEach var="stackItem"                              items="${causeException.stackTrace}">                            <tr><td><c:out value="${stackItem}"/></td></tr>                          </c:forEach>                      </table>                   </c:if>                   </div>               <!-- end details -->                </td>             </tr>          </table>          </td>     </tr> </table>           <script language="javascript">       function showDetails( ) {         document.getElementById("showDetailsLinkDiv").style.display = "none";         document.getElementById("hideDetailsLinkDiv").style.display = "inline";         document.getElementById("stackTraceDiv").style.display = "inline";       }       function hideDetails( ) {         document.getElementById("showDetailsLinkDiv").style.display = "inline";         document.getElementById("hideDetailsLinkDiv").style.display = "none";         document.getElementById("stackTraceDiv").style.display = "none";       }    </script> </div> </body> </html:html>

Create an error-page element for handling HTTP Status 500 errors in your web.xml file, specifying the location as the error page:

<error-page>     <error-code>500</error-code>      <location>/error.jsp</location>  </error-page>

For exceptions thrown from Struts Actions, create a global exception handler for exceptions of type java.lang.Exception in the struts-config.xml file with the path set to the error page:

<global-exceptions>     <exception key="error.general"                type="java.lang.Exception"               path="/error.jsp"/>         ... </global-exceptions>

Discussion

Exception handling tends to be an afterthought. You're sitting there testing the path for your application when a 500 error pops up and your boss says "Ugh. That's ugly. I want the same page to come up no matter what the problem and don't make the details obvious, but the users need to be able to display the details so they can tell tech support what happened."

The JSP shown in Example 9-8 can be used for this because it is designed to handle any type of exception that can be thrown by the container. If the exception has a causing exception, the details can be shown for that exception as well. Figure 9-1 shows the display when a NullPointerException is thrown from an Action.

Figure 9-1. Error page with details hidden


Clicking "Show Details" reveals the stack trace, as shown in Figure 9-2.

Figure 9-2. Error page with details shown


When a ServletException containing a nested root cause is thrown by a servlet, the error page shows the cause details as well. Figure 9-3 shows the error page with the stack trace details shown.

Figure 9-3. Exception thrown by a servlet


If you scroll down the page in Figure 9-3, you will see the stack trace for the causing exception, as shown in Figure 9-4.

Figure 9-4. Stack trace of causing exception


To handle any situation, the error page determines what kind of exception has been thrown. The JSTL c:choose block contains the following logic:

<c:choose>     <c:when test="${not empty pageContext.exception}">         <c:set var="problemType">JSP Exception</c:set>         <c:set var="appException" value="${pageContext.exception}"/>          <c:set var="causeException" value="${appException.cause}"/>     </c:when>     <c:when test="${not empty requestScope['javax.servlet.error.exception']}">         <c:set var="problemType">Servlet Exception</c:set>         <c:set var="appException"              value="${requestScope['javax.servlet.error.exception']}"/>          <c:set var="causeException" value="${appException.rootCause}"/>     </c:when>     <c:when test="${not empty               requestScope['org.apache.struts.action.EXCEPTION']}">         <c:set var="problemType">Struts Exception</c:set>         <c:set var="appException"              value="${requestScope['org.apache.struts.action.EXCEPTION']}"/>         <c:set var="causeException" value="${appException.cause}"/>     </c:when>     <c:otherwise>         <c:set var="problemType">Unidentified Server Error</c:set>     </c:otherwise> </c:choose>

This block determines if the error is a JSPException, ServletException, or an exception thrown by Struts. For each exception, the causing exception is retrieved.

You get the causing exception for a ServletException using the rootCause property instead of the normal cause property.


The rest of the page displays the exception message and exception stack trace for the exception and its cause, if any. For exceptions thrown by Struts, the html:errors tag renders any error messages. JavaScript provides the magic for showing and hiding the exception details. The trick is to put the stack trace details in a div tag and then use a JavaScript function to toggle the CSS display style from none to inline.

The one common error this page doesn't handle is an HTTP Status 404 (Page Not Found) response. If you wanted to use this page for handling these errors, add an additional error-page element to the web.xml. However, you will probably want to handle this error differently, perhaps forwarding to the main menu page of your application or some other location.

This solution works well for intranet applications; for external applications, though, you will probably want to display less information for security purposes. One thing you can do is use this error page in development and testing and then replace it with a terser version when you deploy to production.

See Also

Recipe 9.1 shows you the details on configuring a global exception using Struts declarative exception handling. Recipe 9.6 shows alternatives to html:errors for displaying error messages. Recipe 13-5 shows you a JSP page that you can use specifically for debugging.

For other approaches to localizing exception messages, take a look at some guidelines from Sun's Java Blueprints at http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/i18n/i18n8.html.

Brian Goetz's excellent series on exception handling from JavaWorld is a must-read for any Java developer. He discusses the use of message catalogs for exception localization in Part 3 of the series. You can find the entire series at http://www.javaworld.com/javaworld/jw-08-2001/jw-0803-exceptions.html.



    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