My blog has moved!

You should automatically be redirected in 6 seconds. If not, visit
http://blogs.i2m.dk/allan
and update your bookmarks.

Friday 18 April 2008

Creating a generic error page for JSF

Sometimes using FacesMessages is not sufficient for reporting errors. For example, if you have a runtime error (invalid JSF mark-up or NullPointerExceptions) it is difficult to report them using FacesMessage.

Creating a generic error page for JSF is similar to creating a generic error page for plain JSP applications. You'll need a configuration in web.xml to tell the servlet container to redirect errors to a given page. Then you'll need to create that page as well as a backing bean that can show you the stacktrace or error message.

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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-app_2_5.xsd">

... markup for your web app ...

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

</web-app>


With that we will get the servlet container to forward any Internal Server Errors (HTTP 500) to the page Error.jsp.

Okay, now we need to create a bean that will prepare the error message based on the exception that was thrown:

Error.java

package bigallan.beans;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;

public class Error {

public Error() {}

public String getStackTrace() {

// Get the current JSF context
FacesContext context = FacesContext.getCurrentInstance();
Map requestMap = context.getExternalContext().getRequestMap();

// Fetch the exception
Throwable ex = (Throwable) requestMap.get("javax.servlet.error.exception");

// Create a writer for keeping the stacktrace of the exception
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);

// Fill the stack trace into the write
fillStackTrace(ex, pw);

return writer.toString();
}

/**
* Write the stack trace from an exception into a writer.
*
* @param ex
* Exception for which to get the stack trace
* @param pw
* PrintWriter to write the stack trace
*/
private void fillStackTrace(Throwable ex, PrintWriter pw) {
if (null == ex) {
return;
}

ex.printStackTrace(pw);

// The first time fillStackTrace is called it will always be a ServletException
if (ex instanceof ServletException) {
Throwable cause = ((ServletException) ex).getRootCause();

if (null != cause) {
pw.println("Root Cause:");
fillStackTrace(cause, pw);
}
} else {
// Embedded cause inside the ServletException
Throwable cause = ex.getCause();

if (null != cause) {
pw.println("Cause:");
fillStackTrace(cause, pw);
}
}
}
}


Now that we have the Backing Bean we need to register it in faces-config.xml:


.. usual faces-config stuff ..

<managed-bean>
<managed-bean-name>Error</managed-bean-name>
<managed-bean-class>bigallan.beans.Error</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>


And lastly the JSP page (Error.jsp) displaying the error:

.. usual JSP header stuff ..

<h:panelGrid>
<h:outputText value="The follow error occured:" />
<h:inputTextarea style="width: 100%;" rows="20" readonly="true" value="#{Error.stackTrace}" />
</h:panelGrid>


That's all!

Enjoy.

13 comments:

Anonymous said...

Hi there!
Nice article.

Do you know if this solution only works within Servler 2.5 or it should also works with Servlet 2.4?

Thanks in advance!
Leo

Allan Lykke Christensen said...

As far as I know, the solution should work with an Servlet version supporting JSF.

Anshuman said...

Hi Allan,
Nice article. Would you mind sharing the filter pattern for error.jsp.

does that go to the faces servlet?

Allan Lykke Christensen said...

Actually, the error file should be the view-id of the JSF file. So say, FacesServlet is matching *.jsf, then your error directive should point to /Error.jsf.

Hope that makes sense.

Anonymous said...

thank you for your article. But i still have 500 error. i hate this. i have looked everywhere but still gets 500 error. Please help me.
You said that enter web.xml this this what ever. there is two web.xml. one is in tomcat/conf the other is in \WEB-INF. Which should i edit? And i create jsp file not visual web jsf file. Is that right? And i create java class file. But in the IE, i still get http status 500. Also i add jsp file, isErrorPage="true". I use netbeans 6.1 and apache tomcat 6.0.16. Please help me. Thank you.

Anonymous said...

Hi Allen,
I tried out the steps in your article but I'm unable to get the error page instead I'm getting server defined error page(500). Could you please give me some advice, here is my environment that I'm working on(JSF 1.2 , NetBeans 6.0.1, Server 6.0.14).
Thanks in advance.

Anonymous said...

Hi,
Very nice article.

Where are you registering / Error bean so that it catches all runtime exceptions and then redirects to error jsf file.

appreciate if you can tell the steps to set up this stuff.

Allan Lykke Christensen said...

Instead of specifying 500 as the error code you could specify the full class name of the Exception you which to catch and re-direct. So, if you specified java.lang.Throwable you should (theoretically) be able to catch all uncaught exceptions. I haven't actually tried this myself, but I believe it should work. Let me know if it works.

Anonymous said...

When there is exception in the backing bean, a blank page is displayed. I was expecting response with status code 500. When I debug the http response the status code is 200. I have configured the webapp to display error page on status code 500. Since it is not returning status code 500, this page is not displayed.


I am using Sun RI Implementation of JSF on weblogic 10.3

Allan Lykke Christensen said...

Please see my comment of 2. March. I think this can be solved by explicitly specifying the Exception instead of the error codes. Let me know if it works out.

Anonymous said...

Nice example...
There is a typo in the web.xml file...
It should NOT be '500<', you have one too many '<' characters.
thanks,
clay

Anonymous said...

Another comment.

You asked to be notified about using java.lang.Throwable...

I supplied the following in my web.xml and it worked perfectly...

I hope the html special characters show up...

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


Notice using the 'exception-type' instead of 'error-code'

thanks again,
clay

Allan Lykke Christensen said...

Thanks! The extra greater than (<) has been removed.