Jsf - własna strona błędu

Jak zrobić własną stronę błędu w aplikacji korzystającej z JSF?
U mnie dodanie do web.xml

<error-page>
    <error-code>404</error-code>
    <location>/error.jsf</location>
</error-page>

działało tylko dla błędu 404, natomiast:

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error.jsf</location>
</error-page>

nie działało w ogóle.
Jeżeli to działa u ciebie, to nie musisz czytać dalej, jeżeli jednak nie…

tworzymy własną klasę dekoratora, opakowującego domyślny obiekt dekoratora (delegacja) i przekierowującego na naszą stronę w przypadku wystąpienia błędu:

public class LifecycleDecorator extends Lifecycle {
    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LifecycleDecorator.class);
    private Lifecycle wrapped;

    public LifecycleDecorator(Lifecycle wrapped) {
        this.wrapped = wrapped;
    }

    public void execute(FacesContext context) {
        try {
            wrapped.execute(context);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            context.getApplication().getNavigationHandler().handleNavigation(
                    context, null, "ERROR");
        }
    }

    public void render(FacesContext context) {
        try {
            wrapped.render(context);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            context.getApplication().getNavigationHandler().handleNavigation(
                    context, null, "ERROR";
        }
    }

    public void addPhaseListener(PhaseListener listener) {
        wrapped.addPhaseListener(listener);
    }

    public void removePhaseListener(PhaseListener listener) {
        wrapped.removePhaseListener(listener);
    }

    public PhaseListener [] getPhaseListeners() {
        return wrapped.getPhaseListeners();
    }
}

oraz listenera, który doda powyższego dekoratora:

public class LifecycleContextListener implements ServletContextListener {
    public static final String DECORATOR_LIFECYCLE = "DECORATOR_LIFECYCLE";

    /**
     * 
     */
    public LifecycleContextListener() {
    }

    public void contextDestroyed(ServletContextEvent arg0) {
    }

    public void contextInitialized(ServletContextEvent arg0) {
        // Inicjalizacja lifecycle
        LifecycleFactory factory = (LifecycleFactory) 
                FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
        if (factory != null) {
            Lifecycle defaultLifecycle = 
                    factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
            LifecycleDecorator decorator = new LifecycleDecorator(defaultLifecycle);
            factory.addLifecycle(DECORATOR_LIFECYCLE, decorator);
        }
    }
}

w web.xml dodajemy:

<context-param>
    <param-name>javax.faces.LIFECYCLE_ID</param-name>
    <param-value>DECORATOR_LIFECYCLE</param-value>
</context-param>

i rejestrujemy listenera:

<listener>
    <listener-class>
        pl.na.jawie.LifecycleContextListener
    </listener-class>        
</listener>

jeszcze tylko trzeba w faces-config.xml utworzyć regułę nawigacji:

<navigation-rule>
    <from-view-id>*</from-view-id>
    <navigation-case>
          <from-outcome>ERROR</from-outcome>
            <to-view-id>/error.jsf</to-view-id>
            <redirect/>
    </navigation-case>
</navigation-rule>

i voila. Gdy w aplikacji poleci wyjątek, zostaniemy przekierowani na stronę /error.jsf

O ile nie zaznaczono inaczej, treść tej strony objęta jest licencją Creative Commons Attribution-ShareAlike 3.0 License