Phase Events

   

The JSF implementation fires events, known as phase events, before and after each life-cycle phase. Those events are handled by phase listeners. Unlike value change and action listeners that you attach to individual components, you specify phase listeners in a faces configuration file, like this:

 

 <faces-config>     <lifecycle>         <phase-listener>com.corejsf.PhaseTracker</phase-listener>     </lifecycle> </faces-config> 

The preceding code fragment specifies only one listener, but you can specify as many as you want. Listeners are invoked in the order in which they are specified in the configuration file.

You implement phase listeners by means of the PhaseListener interface from the javax.faces.event package. That interface defines three methods:

  • PhaseId getPhaseId()

  • void afterPhase(PhaseEvent)

  • void beforePhase(PhaseEvent)

The getPhaseId method tells the JSF implementation when to deliver phase events to the listener; for example, getPhaseId() could return PhaseId.APPLY_REQUEST_VALUES. In that case, beforePhase() and afterPhase() would be called once per life cycle: before and after the Apply Request Values phase. You could also specify PhaseId.ANY_PHASE, which really means all phases your phase listener's beforePhase and afterPhase methods will be called six times per life cycle: once each for each life-cycle phase.

Phase listeners are useful for debugging and for highly specialized behavior. For example, if you use JSF components in another web application framework such as Struts, you might want to update that framework's locale after the Apply Request Values phase, when JSF internally sets its locale.

Phase listeners can be a useful for debugging, as illustrated by the application shown in Figure 7-9.

Figure 7-9. Using Phase Listeners

graphics/07fig09.jpg


The application shown in Figure 7-9 has a single phase listener that logs messages with a logger, like this:

 

 public class PhaseTracker implements PhaseListener {     ...     private static final Logger logger = Logger.getLogger("com.corejsf.phases");     ...     public void beforePhase(PhaseEvent e) {         logger.info("BEFORE " + e.getPhaseId());     }     public void afterPhase(PhaseEvent e) {          logger.info("AFTER " + e.getPhaseId());     } } 

The phase ID for the listener is set with a listbox that's defined like this:

 

 <h:selectOneListbox         valueChangeListener="#{form.phaseChange}">     <f:selectItems value="#{form.phases}"/> </h:selectOneListbox> 

When you select a phase and activate the submit button, the phase listener's phase ID is set by a value change listener:

 

 public class FormBean {     ...     // VALUE CHANGE LISTENER: phaseChange     public void phaseChange(ValueChangeEvent e) {         // get a reference to the current lifecycle         ...         PhaseListener[] listeners = lifecycle.getPhaseListeners();         for (int i = 0; i < listeners.length; ++i) {              PhaseListener listener = listeners[i];              if (listener instanceof com.corejsf.PhaseTracker)                  ((com.corejsf.PhaseTracker) listener).setPhase((String)                                                       e.getNewValue());       }    } } 

The top picture in Figure 7-9 shows the application at startup nothing is selected in the listbox. Because we set the listener's default phase to PhaseId.ANY_PHASE, the listener will be invoked before and after every phase until something is selected in the listbox and the form is submitted. Let's see what output the listener writes to the servlet container log file at application startup:

 

 INFO: BEFORE RESTORE_VIEW 1 INFO: AFTER RESTORE_VIEW 1 INFO: BEFORE RENDER_RESPONSE 6 INFO: AFTER RENDER_RESPONSE 6 

Why wasn't the listener notified of phases two (Apply Request Values) through five (Invoke Application)? When the application is started, there's no view to restore because the JSF page has not been loaded before. Without a component tree, there's no sense in processing validations, updating model values, or invoking actions, so the JSF life cycle skips directly to rendering the response. That's the last time that will happen for this application until it's reloaded by the servlet container. If you activate the submit button without selecting a phase, you'll see this output:

 

 INFO: BEFORE RESTORE_VIEW 1 INFO: AFTER RESTORE_VIEW 1 INFO: BEFORE APPLY_REQUEST_VALUES 2 INFO: AFTER APPLY_REQUEST_VALUES 2 INFO: BEFORE PROCESS_VALIDATIONS 3 INFO: AFTER PROCESS_VALIDATIONS 3 INFO: BEFORE UPDATE_MODEL_VALUES 4 INFO: AFTER UPDATE_MODEL_VALUES 4 INFO: BEFORE INVOKE_APPLICATION 5 INFO: AFTER INVOKE_APPLICATION 5 INFO: BEFORE RENDER_RESPONSE 6 INFO: AFTER RENDER_RESPONSE 6 

Now you can see that the listener is notified of all phases because we've set the default phase to PhaseId.ANY_PHASE.

Next, we select Apply Request Values from the listbox and activate the submit button, as shown in the middle picture in Figure 7-9. Here's the listener's output for that form submit:

 

 INFO: BEFORE RESTORE_VIEW 1 INFO: AFTER RESTORE_VIEW 1 INFO: BEFORE APPLY_REQUEST_VALUES 2 INFO: AFTER APPLY_REQUEST_VALUES 2 INFO: BEFORE PROCESS_VALIDATIONS 3 

You might have expected only the Apply Request Values output. Why was our listener notified before and after Restore View and before Process Validations? First, when the form was submitted, the listener's phase was still ANY_PHASE. So when the life cycle began, our listener was interested in all phases and was notified accordingly.

Second, remember that the phase ID for our listener is set with a value change listener, and recall from Figure 7-1 on page 274 that value change listeners are invoked after the Process Validations phase. At the end of Process Validations, the value change listener set the phase listener's phase to APPLY_REQUEST_VALUES. Since Apply Request Values has already executed, our listener was not notified for the remainder of the life cycle. If you click the submit button once again, you'll see the output you expect because the listener's phase ID was set to Apply Request Values from the beginning of the life cycle:

 

 INFO: BEFORE APPLY_REQUEST_VALUES 2 INFO: AFTER APPLY_REQUEST_VALUES 2 

Finally, we select Invoke Application and submit the form. That produces the following output:

 

 INFO: BEFORE APPLY_REQUEST_VALUES 2 INFO: AFTER APPLY_REQUEST_VALUES 2 INFO: BEFORE INVOKE_APPLICATION 5 INFO: AFTER INVOKE_APPLICATION 5 

Does the output make sense this time? When the life cycle starts, the listener's phase ID is Apply Request Values, so the listener is notified of that phase. Later in the life cycle after Process Validation the value change listener changes the listener's phase ID to INVOKE_APPLICATION. Subsequently, the listener is notified of the Invoke Application phase. If you click the submit button again, you'll see this output:

 

 INFO: BEFORE INVOKE_APPLICATION 5 INFO: AFTER INVOKE_APPLICATION 5 

The directory structure for the phase listener example is shown in Figure 7-10. The application shown in Figure 7-9 is listed in Listing 7-14 through Listing 7-19.

Figure 7-10. Phase Listener Example Directory Structure

graphics/07fig10.jpg


TIP

graphics/exclamatory_icon.gif

You can use the phase tracker in your own applications to see when events are fired: any log messages you generate will be mixed with the phase tracker messages. To use the phase tracker, make sure the class file is in your WAR file and you've declared the listener in web.xml as we did in Listing 7-17.


Listing 7-14. phase-tracker/index.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  4.    <f:view>  5.       <head>  6.          <link href="styles.css" rel="stylesheet" type="text/css"/>  7.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  8.          <title>  9.             <h:outputText value="#{msgs.indexWindowTitle}"/> 10.          </title> 11.       </head> 12.       <body> 13.          <h:form> 14.             <h:panelGrid columns="2" columnClasses="phaseFormColumns"> 15.                <h:outputText value="#{msgs.phasePrompt}"/> 16. 17.                <h:selectOneListbox valueChangeListener="#{form.phaseChange}"> 18.                   <f:selectItems value="#{form.phases}"/> 19.                </h:selectOneListbox> 20. 21.                <h:commandButton value="#{msgs.submitPrompt}"/> 22.             </h:panelGrid> 23.          </h:form> 24.       </body> 25.    </f:view> 26. </html> 

Listing 7-15. phase-tracker/WEB-INF/classes/com/corejsf/FormBean.java
  1. package com.corejsf;  2.  3. import javax.faces.FactoryFinder;  4. import javax.faces.event.PhaseListener;  5. import javax.faces.event.ValueChangeEvent;  6. import javax.faces.lifecycle.Lifecycle;  7. import javax.faces.lifecycle.LifecycleFactory;  8. import javax.faces.model.SelectItem;  9. 10. public class FormBean { 11.    private SelectItem[] phases = { 12.       new SelectItem("RESTORE_VIEW"), 13.       new SelectItem("APPLY_REQUEST_VALUES"), 14.       new SelectItem("PROCESS_VALIDATIONS"), 15.       new SelectItem("UPDATE_MODEL_VALUES"), 16.       new SelectItem("INVOKE_APPLICATION"), 17.       new SelectItem("RENDER_RESPONSE"), 18.       new SelectItem("ANY_PHASE"), 19.    }; 20. 21.    public SelectItem[] getPhases() { return phases; } 22. 23.    public void phaseChange(ValueChangeEvent e) { 24.       LifecycleFactory factory = (LifecycleFactory)FactoryFinder.getFactory( 25.             FactoryFinder.LIFECYCLE_FACTORY); 26.       Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory. 27.             DEFAULT_LIFECYCLE); 28. 29.       PhaseListener[] listeners = lifecycle.getPhaseListeners(); 30.       for (int i = 0; i < listeners.length; ++i) { 31.          PhaseListener listener = listeners[i]; 32.          if(listener instanceof com.corejsf.PhaseTracker) 33.             ((com.corejsf.PhaseTracker) listener).setPhase( 34.                   (String) e.getNewValue()); 35.       } 36.    } 37. } 

Listing 7-16. phase-tracker/WEB-INF/classes/com/corejsf/PhaseTracker.java
  1. package com.corejsf;  2.  3. import java.util.logging.Logger;  4. import javax.faces.context.FacesContext;  5. import javax.faces.event.PhaseEvent;  6. import javax.faces.event.PhaseListener;  7. import javax.faces.event.PhaseId;  8.  9. public class PhaseTracker implements PhaseListener { 10.    private static final String PHASE_PARAMETER ="com.corejsf.phaseTracker.phase"; 11.    private static final Logger logger = Logger.getLogger("com.corejsf.phases"); 12.    private static String phase = null; 13. 14.    public void setPhase(String newValue) { phase = newValue; } 15. 16.    public PhaseId getPhaseId() { 17.       if(phase == null) { 18.          FacesContext context = FacesContext.getCurrentInstance(); 19.          phase = (String)context.getExternalContext().getInitParameter( 20.                                                          PHASE_PARAMETER); 21.       } 22.       PhaseId phaseId = PhaseId.ANY_PHASE; 23. 24.       if(phase != null) { 25.          if("RESTORE_VIEW".equals(phase)) 26.             phaseId = PhaseId.RESTORE_VIEW; 27.          else if("APPLY_REQUEST_VALUES".equals(phase)) 28.             phaseId = PhaseId.APPLY_REQUEST_VALUES; 29.          else if("PROCESS_VALIDATIONS".equals(phase)) 30.             phaseId = PhaseId.PROCESS_VALIDATIONS; 31.          else if("UPDATE_MODEL_VALUES".equals(phase)) 32.             phaseId = PhaseId.UPDATE_MODEL_VALUES; 33.          else if("INVOKE_APPLICATION".equals(phase)) 34.             phaseId = PhaseId.INVOKE_APPLICATION; 35.          else if("RENDER_RESPONSE".equals(phase)) 36.             phaseId = PhaseId.RENDER_RESPONSE; 37.          else if("ANY_PHASE".equals(phase)) 38.             phaseId = PhaseId.ANY_PHASE; 39.       } 40.       return phaseId; 41.    } 42.    public void beforePhase(PhaseEvent e) { 43.       logger.info("BEFORE " + e.getPhaseId()); 44.    } 45.    public void afterPhase(PhaseEvent e) { 46.       logger.info("AFTER " + e.getPhaseId()); 47.    } 48. } 

Listing 7-17. phase-tracker/WEB-INF/faces-config.xml
  1. <?xml version="1.0"?>  2.  3. <!DOCTYPE faces-config PUBLIC  4.    "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"  5.    "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">  6.  7. <faces-config>  8.    <managed-bean>  9.       <managed-bean-name>form</managed-bean-name> 10.       <managed-bean-class>com.corejsf.FormBean</managed-bean-class> 11.       <managed-bean-scope>session</managed-bean-scope> 12.    </managed-bean> 13. 14.    <lifecycle> 15.       <phase-listener>com.corejsf.PhaseTracker</phase-listener> 16.    </lifecycle> 17. </faces-config> 

Listing 7-18. phase-tracker/WEB-INF/classes/com/corejsf/messages.properties
 1. indexWindowTitle=Phase Events 2. phasePrompt=Select a phase: 3. submitPrompt=Submit this form 

Listing 7-19. phase-tracker/styles.css
  1. body {  2.    background: #eee;  3. }  4. .phaseFormColumns {  5.    vertical-align: top;  6.    font-style: italic;  7.    font-size: 1.1em;  8. }  9. .columns { 10.    vertical-align: top; 11. } 



core JavaServer Faces
Core JavaServer Faces
ISBN: 0131463055
EAN: 2147483647
Year: 2003
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net