Sometimes using FacesMessage
s 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.