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.

Wednesday, 2 April 2008

Tomato juice on airplanes

I've just returned from a two week business trip and there is something that just keeps puzzling me when I fly. The amount of people that drink tomato juice on airplanes just amazes me. I have never dined with anyone who ordered tomato juice neither I have ever tasted it. However, on airplanes it seems like people have the urge to drink tomato juice. Flying back from Uganda (via Nairobi) to Amsterdam, an 8+ hours journey my co-passenger only had tomato juice! Tomorrow I've got to go buy myself some tomato juice to see what the craze is all about.