views:

92

answers:

3

I'm having a tough time consolidating errors in my application. Currently, my error.jsp looks like following (part of it):

 <%@ page isErrorPage="true" %>
 <%@page contentType="text/html"%>
 <%@page import="java.util.*"%>
 <%@page import="javax.servlet.*"%>
 <%@page import="javax.servlet.http.*"%>
 <%@page import="java.util.Calendar"%>
 <%@page import="java.text.SimpleDateFormat"%>

<html>
<%
String code = null, message = null, type = null, uri = null, time = null;
Object codeObj=null, messageObj=null, typeObj=null;
if (request.getAttribute("javax.servlet.error.status_code") != null)
    codeObj = request.getAttribute("javax.servlet.error.status_code");
if (request.getAttribute("javax.servlet.error.message") != null)
    messageObj = request.getAttribute("javax.servlet.error.message");
if (request.getAttribute("javax.servlet.error.exception_type")!=null)
    typeObj = request.getAttribute("javax.servlet.error.exception_type");

if (codeObj != null) code = codeObj.toString();
if (messageObj != null) message = messageObj.toString();
if (typeObj != null) type = typeObj.toString();
uri = (String) request.getAttribute("javax.servlet.error.request_uri");
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
time = sdf.format(cal.getTime());

String error = "Code:\t\t" + code + "\nType:\t\t" + type + "\nURL:\t\t" + uri + "\nTime:\t\t" + time +"\nMessage:\t" + message;
%>

This works fine in all scenarios except!: Sometimes in my application I am catching built-in exceptions in MyException class with the following code:

catch(MyException ex){
    log.error(ex.getMessage(), uivex);
    String originalURL = "/errorpages/error.jsp?errorcode=" + (ex.getMajor() + ex.getMinor()) + "&errormessage=" + ex.getMessage();
    RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(address);
    dispatcher.forward(request,response);   
}

Now the problem is that when I get forwarded to error.jsp page...Instead of seeing the actual error from MyException class..I'm seeing NullPointerException because nothing is present in javax.servlet.error.status_code and the page is declared as isErrorPage="true"

What should I do in this case? One solution is to make a completely different error.jsp (name it error1.jsp) page and forward the exceptions from MyException class to that page. Though, I would like to have everything in one place.

+1  A: 

Put all details in the logs and show the user only a vague message that something went wrong, regardless of what the exception is. So your error page may look like (to quote twitter):

<%@ page isErrorPage="true" %>

Something went technically wrong. 

And do not catch & forward - simply let the exception bubble. The other alternative is, as you say, make two separate pages that include the common content, and differ only in the isErrorPage definition.

Bozho
but the problem will still be there. when a jsp page is decalared is `isErrorPage` the `javax.servlet.error.status_code` must have something in it or else a Null Pointer Exception is thrown.
Omnipresent
@Omnipresent - correct. I added an update about that.
Bozho
@bozho I thought in last question you suggested that if I want to avoid putting exceptions in server.log then I need to catch them and that's the only option. that's why I am now catching it and forwarding.
learn_plsql
Yes, given the other question (I didn't relate the two initially) then forwarding sounds less bad.
Bozho
+1  A: 

You can create a custom stackTrace jsp tag and include it in a special error page:

The tag file (WEB-INF/tags/stackTrace.tag)

<%@tag description="Prints stack trace of the specified Throwable"
          pageEncoding="UTF-8"%>

<%-- content (prints stack trace) --%>
<%
      java.io.PrintWriter pOut = new java.io.PrintWriter(out);
      try {
         // The Servlet spec guarantees this attribute will be available
         Throwable err = (Throwable) 
             request.getAttribute("javax.servlet.error.exception");

         if(err != null) {
            if(err instanceof ServletException) {
               // It's a ServletException: we should extract the root cause
               ServletException se = (ServletException) err;
               Throwable rootCause = se.getRootCause();
               if(rootCause == null) {
                  rootCause = se;
               }
               out.println("** Root cause is: " + rootCause.getMessage());
               rootCause.printStackTrace(pOut);
            }else {
               // It's not a ServletException, so we'll just show it
               err.printStackTrace(pOut);
            }
         }else {
            out.println("No error information available");
         }

         // Display cookies
         out.println("\nCookies:\n");
         Cookie[] cookies = request.getCookies();
         if(cookies != null) {
            for(int i = 0; i < cookies.length; i++) {
               out.println(cookies[i].getName() + "=[" + 
                   cookies[i].getValue() + "]");
            }
         }

      }catch(Exception ex) {
         ex.printStackTrace(pOut);
      }
%>

The error.jsp could be like so: (Add some humor to it, if you app somewhat casual)

<%@ page isErrorPage="true" %>
<html>
   <head>
    <title>Error</title>
   </head>
   <body>
      <div>
         <textarea class="para error">
            Aww Snap!! :( Something went wrong where it was not supposed to. 
            Must be something
            <a href="http://en.wikipedia.org/wiki/Jack_the_Ripper"&gt;Jack&lt;/a&gt; did!
            Please report this to the dev team.

            <!-- 
               <util:stackTrace />
            -->

         </p>
      </div>
   </body>
</html>

The "Report this Error button can submit the stack trace or mail it!

naikus
having the user reporting an error is not needed in web applications.
Bozho
@Bozho You are right! What a Klutz I am!
naikus
A: 

This code honestly hurts my eyes. Here is how a generic one should look like. You may find it useful.

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<jsp:useBean id="date" class="java.util.Date" />
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Error</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1>Error</h1>
        <p>Unfortunately an unexpected error has occurred. Below you can find the error details.</p>
        <h2>Details</h2>
        <ul>
            <li>Timestamp: <fmt:formatDate value="${date}" type="both" dateStyle="long" timeStyle="long" />
            <li>Action: <c:out value="${requestScope['javax.servlet.forward.request_uri']}" />
            <li>Exception: <c:out value="${requestScope['javax.servlet.error.exception']}" />
            <li>Message: <c:out value="${requestScope['javax.servlet.error.message']}" />
            <li>Status code: <c:out value="${requestScope['javax.servlet.error.status_code']}" />
            <li>User agent: <c:out value="${header['user-agent']}" />
        </ul>
    </body>
</html>

The @page isErrorPage is by the way only useful if you want to have the ${exception} (i.e. request.getAttribute("exception") available in the JSP. In this particular case you don't need it.

And indeed, do not forward in the catch block at all. Just let it go. It will be dealt by the error page then.

} catch (MyException ex) {
    log.error(ex.getMessage(), uivex);
    throw ex; // Or throw new ServletException(ex.getMessage(), ex);
}
BalusC
If I `throw ex` then error stack is written in server.log . I want it to only be written in `my_system.log` (comes from `log.error`)
learn_plsql
I really don't see how that's a problem. You're already logging it to your own log using `log.error()`. If you want to disable server stderr logging, just configure so in the server config.
BalusC
Ideally I would like to do exaclty that! so that Instead of dealing with exceptions I can just log it to my own log and then just throw it out! How would I configure/disabled stderr logging in GlassFish. I can search on google as well but not sure exactly what to look for
learn_plsql
Again, I really don't see how that's a problem. You could just ignore the server's own logfile at its whole own. Anyway, you can configure logging in the admin console.
BalusC