Friday, June 02, 2006

How to handle JSP Error Page

At first glance it looks like a very straightforward task.


1. Each JSP page should use "errorPage" attribute. For example:

<%-- /example.jsp --%>

<%@ page errorPage="/WEB-INF/templates/errorPage.jsp"%>

<%-- some JSP content --%>


If you don't want to repeat this for each JSP page, use Tiles template or specify error page in "web.xml" file:

<web-app>
  ...
 
  <error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/WEB-INF/templates/errorPage.jsp</location>
  </error-page>

  ...
 
</web-app>



This description registers "errorPage.jsp" JSP page as error page for all JSP pages within web application.


2. You need to create error page, which displays runtime exceptions:

<%-- /WEB-INF/templates/errorPage.jsp --%>

<%@ page isErrorPage="true" %>

<%
  out.println("Exception: " + exception);
%>



By using "isErrorPage" attribute you declare this JSP page as error page. As a result, you have access to "exception" JSP variable (in addition to regular "application", "session",
"request", etc.).


This is basic. The problem happened, when your JSP page generates big output (bigger than page buffer) before the exception. In this case your error page will be appended to the current content instead of displaying in the new page.

To overcome the problem you can:


3.A. Play with "buffer" attribute. Say, you know, that the size of your page will never be more than 1MB. In this case add these attributes to the page:


<%-- /example2.jsp --%>

<%@ page errorPage="/WEB-INF/templates/errorPage.jsp"%>

<%@ page buffer="1024kb"%>
<%@ page autoFlush="false"%>

<%-- some JSP content --%>


This approach is not perfect, because you cannot guarantee the maximum page size. Another drawback is that page will be refreshed only after completing the operation. This could hurt user's perception.


3.B.1. Use custom javascript code to clear flushed already output. By using "document.getElementById()" function we canget reference to the body and rewrite it:


<%-- /WEB-INF/templates/errorPage.jsp, ver.1 --%>

<%@ page isErrorPage="true" %>

<html>
  <head>
    <script type="text/javascript">
      function newPage() {
        var exception = '<%= exception %>';

        var body = document.getElementById("body");

        body.innerHTML =
            "<html>" +
            " <body>" +
            " Exception: " + exception +
            " </body>" +
            "</html>";
        }
      }
    </script>
  </head>

  <body/>

</html>


This solution fits for simple error pages only. If you want to display full-featured error page, you have to look for another solution (see 3.B.2).



3.B.2. Redirect to another page.


This scenario is based on 3.B.1 solution. Now, instead of preparing the content to display, we'll submit the form, redirecting the flow to the requested page. In order to get the reference to javascript's "document" obect and clear it, we'll use document.open()" function:


<%-- /WEB-INF/templates/errorPage.jsp, ver.2 --%>

<%@ page isErrorPage="true" %>

<%
  // We want to have exception available on the redirected page.
  session.setAttribute("javax.servlet.error.exception",
                        request.getAttribute("javax.servlet.error.exception"));
%>

<html>
  <head>
    <script type="text/javascript">
      function newPage(action) {
        var newDoc = document.open("text/html", true);

        if(newDoc) {
          var errorFormName = 'errorForm';
          var txt =
            "<html>" +
            " <body>" +
            " <form id='" + errorFormName + "' method='get'/>" +
            " </body>" +
            "</html>";

          newDoc.write(txt);
          newDoc.close();

          var form = newDoc.getElementById(errorFormName);
          form.action = action;
          form.submit();

          return true;
        }

        return false;
      }

      newPage("someURL"); // direct execution of javascript custom function
    </script>
  </head>

  <body/>

</html>


This solution does not work properly yet. The problem is, that at the time of javascript execution the html page is not completely generated yet. As the result, "document.open()" call will return "null" object and the conten for the redirection will not be generated. See 3.B.3 for the solution.


3.B.3. Using timer object.


This scenario is based on 3.B.2 solution. It is exactly the same, the only difference is how we call "newPage()" function. Instead of direct call we'll do it indirectly from the timer:

<%-- /WEB-INF/templates/errorPage.jsp, ver.3 --%>

<%@ page isErrorPage="true" %>

<%
  // We want to have exception available on the redirected page.
  session.setAttribute("javax.servlet.error.exception",
                        request.getAttribute("javax.servlet.error.exception"));
%>

<html>
  <head>
    <script type="text/javascript">
      function newPage(action) { ... }

      var timer = new Timer("timer");
      timer.setScript("newPage('someURL')");

      timer.start();
    </script>
  </head>

  <body/>

</html>


Timer is the special custom javascript class that performs call to a given script periodically until the script returns "true" value. After this, the timer will stop execution.

You can download inplementation for the timer from here:

http://home.comcast.net/~shvets/blog/timer.js

Now JSP error page work properly.

451 comments:

«Oldest   ‹Older   801 – 451 of 451
«Oldest ‹Older   801 – 451 of 451   Newer› Newest»