How to check and display information about the exception and status code in custom JSP error page?
Asked Answered
B

2

4

I know I can put something in the web.xml like this

<error-page>  
   <exception-type>java.lang.Throwable</exception-type>  
   <location>/error.jsp</location>  
</error-page>

However the jsp page won't show any contructive information since it won't get what exactly the exception is. I know we can have different exceptions forwarded to different pages by various exception-type but that's too much to write in web.xml. I hope one page is enough and another for handling errors like 404.

So how should I pass the exception information to the jsp page? Use session?

The ideal situation might be the page gets the exception info and show some relevant messages about it without revealing the exception to the users. Instead it could log it into a file for future reference. What is the best approach to achieve this? Thanks.

Bole answered 4/9, 2012 at 6:31 Comment(0)
J
13

The information about the exception is already available by several request attributes. You can find the names of all those attributes in the RequestDispatcher javadoc:

So, in a nutshell, this JSP example should display all the possible exception detail:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<ul>
    <li>Exception: <c:out value="${requestScope['javax.servlet.error.exception']}" /></li>
    <li>Exception type: <c:out value="${requestScope['javax.servlet.error.exception_type']}" /></li>
    <li>Exception message: <c:out value="${requestScope['javax.servlet.error.message']}" /></li>
    <li>Request URI: <c:out value="${requestScope['javax.servlet.error.request_uri']}" /></li>
    <li>Servlet name: <c:out value="${requestScope['javax.servlet.error.servlet_name']}" /></li>
    <li>Status code: <c:out value="${requestScope['javax.servlet.error.status_code']}" /></li>
</ul>

Additionally, you could also show this useful information:

<%@ 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" />
...
<ul>
    <li>Timestamp: <fmt:formatDate value="${date}" type="both" dateStyle="long" timeStyle="long" /></li>
    <li>User agent: <c:out value="${header['user-agent']}" /></li>
</ul>

The concrete Exception instance itself is in the JSP only available as ${exception} when you mark the page as an error page:

<%@ page isErrorPage="true" %>
...
${exception}

Only if you're using EL 2.2 or newer, then you can print its stacktrace as below:

<%@ page isErrorPage="true" %>
...
<pre>${pageContext.out.flush()}${exception.printStackTrace(pageContext.response.writer)}</pre>

Or if you're not on EL 2.2 yet, then create a custom EL function for that:

public final class Functions {

    private Functions() {}

    public static String printStackTrace(Throwable exception) {
        StringWriter stringWriter = new StringWriter();
        exception.printStackTrace(new PrintWriter(stringWriter, true));
        return stringWriter.toString();
    }

}

Which is registered in /WEB-INF/functions.tld:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib 
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">

    <display-name>Custom Functions</display-name>    
    <tlib-version>1.0</tlib-version>
    <uri>http://example.com/functions</uri>

    <function>
        <name>printStackTrace</name>
        <function-class>com.example.Functions</function-class>
        <function-signature>java.lang.String printStackTrace(java.lang.Throwable)</function-signature>
    </function>
</taglib>

And can be used as

<%@ taglib prefix="my" uri="http://example.com/functions" %>
...
<pre>${my:printStackTrace(exception)}</pre>

As to the logging of the exception, easiest place would be a filter which is mapped on an URL pattern of /* and does basically the following:

try {
    chain.doFilter(request, response);
} catch (ServletException e) {
    log(e.getRootCause());
    throw e;
} catch (IOException e) { // If necessary? Usually not thrown by business code.
    log(e);
    throw e;
}
Janiecejanifer answered 4/9, 2012 at 11:58 Comment(0)
G
0

yes, In my opinion session is a good place to store exceptions relating to the current request.

Don't forget to clear the exception after you finish handling it.

Also, you can pass instead of exception, an error code from your backing code to your presentation layer, where that can be translated using property files to some kind of meaning full error for the user.

Garrott answered 4/9, 2012 at 6:42 Comment(3)
What do you mean by "clear the exception"? Isn't the application terminated once an uncaught exception is aroused?Bole
this will work, if no uncaught exceptions rises from the code. That means that you can try/catch all code that can generate exception and then store that exception in the session object. If uncaught exceptions are thrown from your code then i am afraid that the only solution for this is to declare error pages/error codes in web xmlGarrott
lets say a NullPointerException comes up, how would the server know from where and what field that was left empty generated the exception? Its best to try/catch your error prone code and implement the solution provided obove IMOGarrott

© 2022 - 2024 — McMap. All rights reserved.