Adding global exception handling using JSF 2.x ExceptionHandler

This a great feature of JSF 2.x: A generic API to manipulate application exception in a global manner. In order to implement this you must implement (extend) two different classes:

ExceptionHandlerWrapper – Provides a simple implementation of ExceptionHandler that can be subclassed by developers wishing to provide specialized behavior to an existing ExceptionHandler instance. The default implementation of all methods is to call through to the wrapped ExceptionHandler instance.

ExceptionHandlerFactory – A factory object that creates (if needed) and returns a new ExceptionHandler instance.

On Duke’s Forest this is the implementation:

CustomExceptionHandlerFactory.java:

package com.forest.exception;

public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
   private ExceptionHandlerFactory parent;

   // this injection handles jsf
   public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
    this.parent = parent;
   }

    @Override
    public ExceptionHandler getExceptionHandler() {

        ExceptionHandler handler = new            CustomExceptionHandler(parent.getExceptionHandler());

        return handler;
    }

}

CustomExceptionHandlerFactory.java:

package com.forest.exception;

public class CustomExceptionHandler extends ExceptionHandlerWrapper {
    private static final Logger log = Logger.getLogger(CustomExceptionHandler.class.getCanonicalName());
    private ExceptionHandler wrapped;

    CustomExceptionHandler(ExceptionHandler exception) {
        this.wrapped = exception;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return wrapped;
    }

    @Override
    public void handle() throws FacesException {

        final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
        while (i.hasNext()) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context =
                    (ExceptionQueuedEventContext) event.getSource();

            // get the exception from context
            Throwable t = context.getException();

            final FacesContext fc = FacesContext.getCurrentInstance();
            final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
            final NavigationHandler nav = fc.getApplication().getNavigationHandler();

            //here you do what ever you want with exception
            try {

                //log error ?
                log.log(Level.SEVERE, "Critical Exception!", t);

                //redirect error page
                requestMap.put("exceptionMessage", t.getMessage());
                nav.handleNavigation(fc, null, "/error");
                fc.renderResponse();

                // remove the comment below if you want to report the error in a jsf error message
                //JsfUtil.addErrorMessage(t.getMessage());

            } finally {
                //remove it from queue
                i.remove();
            }
        }
        //parent hanle
        getWrapped().handle();
    }
}

And add the following lines to your faces-config.xml:

<factory>
        <exception-handler-factory>
            com.forest.exception.CustomExceptionHandlerFactory
        </exception-handler-factory>
</factory>

In the try-finally block you can cast the Throwable to any specific exception and do  any special treatment that you want. For example: ViewExpiredException can be redirected to a “Session Expired” page and on the other hand, NullPointerException can use the common generic page just saying that “An unexpected situation occurred, please try again later.”

In line 40 I’m redirecting to a common error page (/error) which can read error information from the request map.

Please note that when dealing with exceptions and web development, you must ensure that you are not sharing any unwanted sensitive information when returning the error to the web page.

To see the complete running example please check The Java EE 6 Tutorial and check Duke’s Forest case study.

References:

8 comments

  1. Pingback: Integrating Spring & JavaServer Faces : Exception Handling « Phil Webb's Blog
  2. apanag

    Hi, great presentation.
    Could you please publish your faces-config.xml (navigation rule for error page)

    When I use redirect the request map value are null. Without redirect, the user stays at the initial error page.

    Thanks

  3. markito

    Hi and sorry for the long long delay to answer – I was completely out due to some health issues.
    So, there is no navigation rule, the redirect is done only by the class at line 40.

    //redirect error page
    requestMap.put(“exceptionMessage”, t.getMessage());
    nav.handleNavigation(fc, null, “/error”);
    fc.renderResponse();

    What you probably need to do is to populate the requestMap with the data you want to keep from the previous request, since this redirect is almost like a new request, any data you have will not be automatically carried to this redirect.

    Thanks!

  4. stillfelil

    apanag said “Could you please publish your faces-config.xml (navigation rule for error page)”
    so could you lease reply ?
    thx a lot

  5. markito
    <?xml version='1.0' encoding='UTF-8'?>
    
    <!-- =========== FULL CONFIGURATION FILE ================================== -->
    
    <faces-config version="2.0"
                  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-facesconfig_2_0.xsd">
        <application>
            <resource-bundle>
                <base-name>/Bundle</base-name>
                <var>bundle</var>
            </resource-bundle>
            <locale-config>
                <default-locale>en</default-locale>
                <supported-locale>es</supported-locale>
                <supported-locale>pt</supported-locale>
            </locale-config>
        </application>
        <factory>
            <exception-handler-factory>
                com.forest.exception.CustomExceptionHandlerFactory
            </exception-handler-factory>
        </factory>
    
    </faces-config>
    
  6. Pingback: Java ee session/conversation/view timeout redirection | CopyQuery
  7. Evgeny Mironenko

    It does not work with ajax requests. You should use:

    context.setViewRoot(context.getApplication().getViewHandler().createView(context, viewId));
    context.getPartialViewContext().setRenderAll(true);
    context.renderResponse();

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s