Previous Section  < Day Day Up >  Next Section

12.5 Using a PhaseListener

There's an event type we haven't talked about so far, namely the javax.faces.event.PhaseEvent. This is an event that fires before and after each request processing lifecycle phase, invoking all javax.faces.event.PhaseListener instances registered with the javax.faces.lifecycle.Lifecycle infrastructure object responsible for coordinating the request processing.

A very simple application probably doesn't have any use for this event, but it can come in handy in some scenarios. For instance, say that your application relies on an external resource (such as a database). Instead of dealing with the (hopefully rare) situation that the database is unavailable in all component event handlers, you can use a PhaseListener that checks the resource status and navigates to an error page if it's not available. Here's an outline for such a listener:

package com.mycompany.event;



import javax.faces.application.NavigationHandler;

import javax.faces.context.FacesContext;

import javax.faces.event.PhaseEvent;

import javax.faces.event.PhaseId;

import javax.faces.event.PhaseListener;



public class CheckResourceListener implements PhaseListener {

    public PhaseId getPhaseId( ) {

        return PhaseId.RESTORE_VIEW;

    }



    public void beforePhase(PhaseEvent event) {

    }



    public void afterPhase(PhaseEvent event) {

        FacesContext context = event.getFacesContext( );

        if (!isEverythingOkay(context)) {

            NavigationHandler nh = 

                context.getApplication( ).getNavigationHandler( );

            nh.handleNavigation(context, null, "unavailable");

        }

    }



    private boolean isEverythingOkay(FacesContext context) {

        ...

        return result;

    }

}

The PhaseListener interface has three methods. The getPhaseId() method returns the PhaseId value for the phase the listener wants to be notified in or the PhaseId.ANY_PHASE value in order to be notified in all phases. Here I return PhaseId.RESTORE_VIEW to be notified as early as possible.

The listener is notified by a call to the beforePhase() method before the regular processing for the phase begins and by a call to the afterPhase() method when the regular phase processing is completed. I've chosen to use another JSF infrastructure class called the javax.faces.application.NavigationHandler to navigate to the error page and, because it relies on having access to a view, I implement all logic in the afterPhase() method. If I had implemented the navigation by some other means, e.g., by calling the FacesContext redirect() method with a hardcoded path, I could have done it in the beforePhase() method instead.

The NavigationHandler is a pluggable class. The default implementation reads the navigation rules in the faces-config.xml file and acts accordingly when the handleNavigation() method is called. In this example, I call it with a null value for the argument that holds the action method binding expression when the navigation is initiated by the default ActionListener class, because there is no action binding in this case. The outcome argument is set to "unavailable", matching this navigation rule:

  <navigation-rule>

    <navigation-case>

      <from-outcome>unavailable</from-outcome>

      <to-view-id>/unavailable.jsp</to-view-id>

      <redirect/>

    </navigation-case>

  </navigation-rule>

Because I've left out the <from-view-id> element, the rule applies to all views in the application, redirecting to a view represented by the unavailable.jsp page. This page can display an explanation of what's going on and ask the user to come back later.

The easiest way to register your PhaseListener is by adding a declaration in the faces-config.xml file:

<faces-config>

  ...

  <lifecycle>

    <phase-listener>

      com.mycompany.event.CheckResourceListener

    </phase-listener>

  </lifecycle>

  ...

</faces-config>

Checking the status of an external resource is just one example of how you can use a PhaseListener. Another example is letting a listener handle custom access control as an alternative to the access control filter used for the billboard application we looked at earlier. In the next section, I show you a PhaseListener that captures state information that can then be used for debugging.

    Previous Section  < Day Day Up >  Next Section