blogger templates blogger widgets
This is part of a list of blog posts.
To browse the contents go to

JSF - Quick but Deep Dive


Below are some of the notes I took while working on a project involving JSF.

JSF LIFE CYCLE

Check out the lifecycle flow diagram from here.


Before we get into the lifecyle. Note that all JSF requests get forwarded to FacesServlet as specified in the web.xml.

<servlet>
 <servlet-name>Faces Servlet</servlet-name>
 <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
 <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
 <servlet-name>Faces Servlet</servlet-name>
 <url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
 <servlet-name>Faces Servlet</servlet-name>
 <url-pattern>*.jsf</url-pattern>
</servlet-mapping>

NOTE You can specify a context parameter saveStateInClient with a value of true to force JSF to save state in the client as opposed to saving it in the server.
If you choose to do so, you must add the following context-param element before the servlet element in your deployment descriptor.


<context-param>
 <param-name>saveStateInClient</param-name><param-value>false</param-value>
</context-param>

FacesServlet creates an object called FacesContext, which contains information necessary for request processing.
To be more precise, FacesContext contains the ServletContext, ServletRequest, and ServletResponse objects that are passed to the service method of FacesServlet by the Web container.
During processing, FacesContext is the object that is modified.

All the processing is done by Lifecycle. The FacesServlet servlet hands over control to the Lifecycle object.


public final class FacesServlet implements Servlet {
 /**
     * Acquire the factory instances we will require.
     */
 public void init(ServletConfig servletConfig) throws ServletException {

        // Save our ServletConfig instance
        this.servletConfig = servletConfig;

        // Acquire our FacesContextFactory instance, which will be used to acquire FacesContext instance
        try {
            facesContextFactory = (FacesContextFactory)
                FactoryFinder.getFactory
                (FactoryFinder.FACES_CONTEXT_FACTORY);
        } catch (FacesException e) {
   
        }

        // Acquire our LifecycleFactory instance, which we use to acquire Lifecycle instance
        try {
            LifecycleFactory lifecycleFactory = (LifecycleFactory)
                FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
            String lifecycleId ;

            // First look in the servlet init-param set
            if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
                // If not found, look in the context-param set 
                lifecycleId = servletConfig.getServletContext().getInitParameter
                    (LIFECYCLE_ID_ATTR);
            }

            if (lifecycleId == null) {
                lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
            }
            lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
        } catch (FacesException e) {
        }

    }
 /**
     * Process an incoming request, and create the corresponding
     * response, by executing the request processing lifecycle.
     *
     * @param request The servlet request we are processing
     * @param response The servlet response we are creating
     *
     * @throws IOException if an input/output error occurs during processing
     * @throws ServletException if a servlet error occurs during processing
     */
    public void service(ServletRequest request,
                        ServletResponse response)
        throws IOException, ServletException {

        // If prefix mapped, then ensure requests for /WEB-INF are
        // not processed.
        String pathInfo = ((HttpServletRequest) request).getPathInfo();
        if (pathInfo != null) {
            pathInfo = pathInfo.toUpperCase();
            if (pathInfo.startsWith("/WEB-INF/")
                || pathInfo.equals("/WEB-INF")
                || pathInfo.startsWith("/META-INF/")
                || pathInfo.equals("/META-INF")) {
                ((HttpServletResponse) response).
                      sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
        }    
        
        if (!initFacesContextReleased) {
            Object obj = servletConfig.getServletContext().getAttribute("com.sun.faces.InitFacesContext");
            if (null != obj && obj instanceof FacesContext) {
                try {
                    ((FacesContext) obj).release();
                    servletConfig.getServletContext().removeAttribute("com.sun.faces.InitFacesContext");
                } catch (Exception e) {
                    // Take no action if another thread released or removed it already.
                }
            }
            initFacesContextReleased = true;
        }

        // Acquire the FacesContext instance for this request
        FacesContext context = facesContextFactory.getFacesContext
            (servletConfig.getServletContext(), request, response, lifecycle);

        // Execute the request processing lifecycle for this request
        try {
            lifecycle.execute(context);
            lifecycle.render(context);
        } catch (FacesException e) {
        }
        finally {
            // Release the FacesContext instance for this request
            context.release();
        }

    }
}

We see that Lifecyle is a abstract class so what does these do
lifecycle.execute(context);
lifecycle.render(context);

Let's look into Lifecycle abstract class.

Lifecycle manages the processing of the entire lifecycle of a particular JavaServer Faces request.
It is responsible for executing all of the phases that have been defined by the JavaServer Faces Specification, in the specified order,
unless otherwise directed by activities that occurred during the execution of each phase.

Below are the methods found in Lifecycle class.

abstract  void addPhaseListener(PhaseListener listener) 
// Register a new PhaseListener instance that is interested in being notified before and after the processing for standard phases of the request processing lifecycle.
abstract  void execute(FacesContext context) 
// Execute all of the phases of the request processing lifecycle, up to but not including the Render Response phase, as described in the JavaServer Faces Specification, in the specified order.
abstract  PhaseListener[] getPhaseListeners() 
// Return the set of registered PhaseListeners for this Lifecycle instance.
abstract  void removePhaseListener(PhaseListener listener) 
// Deregister an existing PhaseListener instance that is no longer interested in being notified before and after the processing for standard phases of the request processing lifecycle.
abstract  void render(FacesContext context) 
// Execute the Render Response phase of the request processing lifecycle, unless the responseComplete() method has been called on the FacesContext instance associated with the current request.


A typical case is the following:

Step 1. The user visits a page the first time. And that page has a form.

Step 2. The user enters the data into the form and then submits the page.

In Step 1, JSF will execute two phases Restore_View and Render_Response.
It creates the view in the Restoer_View phase. But here it is just an empty ViewRoot and it is put into the facescontext.
In the Render_Response phase, JSF will creates the actual UI components and build the view.
It will also save the view into the session using the class com.sun.faces.application.StateManagerImpl in this phase.

In Step 2, by default, the page is submitted to the same page. JSF will go through all the phases in the lifecycle.

RESTORE VIEW phase

There are 4 things that are performed in this phase

1. Get view id from URL
2. Read web.xml to see whether state is saved on client or server
3. Check if its postback or if new request
4. If its a postback restore component tree from previously saved state. If component tree isnot found or if it's a new request, create a new one.
5. Set locale on FacesContext - from saved state, config, or request
6. Set RenderKit for the tree
7. Call FacesContext.setViewRoot() to store the tree that was either retrieved or created

The java file in JSF to do the RESTORE_VIEW phase is com.sun.faces.lifecycle.RestoreViewPhase.java.

Terminology used:

§ View:
A traditional JSP page is referred to, in JSF, as a view. This is appropriate since a JSP page forms the View part of the Model-View-Controller pattern.

§ Component Tree:
In JSF, a view is also known as a component tree. This is also appropriate since a JSP view models the elements of a JSP page (such as the form, input boxes, buttons, and links) using Java objects, where these objects are organized into a tree, based on the hierarchical containment hierarchy of a typical HTML page.

§ View Root:
Each component tree is rooted at a UIViewRoot object - which is represented by the JSF tag.

§ View Identifier:
Each view/component-tree has a unique id. When using JSPs and prefix mapping, this is the part of the URL between the prefix and the query string.
I.e., for a request URL of http://localhost/myapp/faces/admin/users.faces the unique id is /admin/users.jsp.
When using JSPs and suffix/extension mapping, this is the part of the URL with the extension replaced by .jsp.
I.e., for a request URL of http://localhost/myapp/admin/users.faces the unique id is /admin/users.jsp.
Note that a view identifier is a critical concept and comes back into play during the navigation step of the Invoke Application phase.

§ Faces Context:
A FacesContext describes the runtime environment for the request currently in progress.
This is a critical object for many reasons. It allows you to set/retrieve the current view root - thereby affecting the view that will be displayed to the user;
it houses the all important renderResponse() and responseComplete() methods that allow the lifecycle to short circuited; it lets you access the external servlet or portlet context; and much more.

public class RestoreViewPhase extends Phase {

    private WebConfiguration webConfig;

 
 // PhaseId store string constants representing each phase
 
 
 // Sample contents of PhaseId
 
  private PhaseId(String newPhaseName) {
   phaseName = newPhaseName;
  }
  private static final String RENDER_RESPONSE_NAME = "RENDER_RESPONSE";
  
  public static final PhaseId RENDER_RESPONSE = new PhaseId(RENDER_RESPONSE_NAME);
 
 // Sample Ends
 
    public PhaseId getId() {
        return PhaseId.RESTORE_VIEW;
    }
 
 
  // Phase is a single step in the processing of a JSF request throughtout its entire Lifecycle
 

    public void doPhase(FacesContext context,
                        Lifecycle lifecycle,
                        ListIterator listeners) {

        Util.getViewHandler(context).initView(context);
        super.doPhase(context, lifecycle, listeners);
    }

    /**
     * PRECONDITION: the necessary factories have been installed in the
     * ServletContext attr set. 
     * 

* POSTCONDITION: The facesContext has been initialized with a tree. */ public void execute(FacesContext facesContext) throws FacesException { if (null == facesContext) { // throw exception } // If an app had explicitely set the tree in the context, use that; UIViewRoot viewRoot = facesContext.getViewRoot(); if (viewRoot != null) { facesContext.getViewRoot().setLocale( facesContext.getExternalContext().getRequestLocale()); doPerComponentActions(facesContext, viewRoot); if (!isPostback(facesContext)) { facesContext.renderResponse(); } return; } // Reconstitute or create the request tree String viewId; if (Util.isPortletRequest(facesContext)) { viewId = facesContext.getExternalContext().getRequestPathInfo(); if (viewId == null) { viewId = facesContext.getExternalContext().getRequestServletPath(); } } else { Map requestMap = facesContext.getExternalContext().getRequestMap(); viewId = (String) requestMap.get("javax.servlet.include.path_info"); if (viewId == null) { viewId = facesContext.getExternalContext().getRequestPathInfo(); } // It could be that this request was mapped using // a prefix mapping in which case there would be no // path_info. Query the servlet path. if (viewId == null) { viewId = (String) requestMap.get("javax.servlet.include.servlet_path"); } if (viewId == null) { viewId = facesContext.getExternalContext().getRequestServletPath(); } } if (viewId == null) { // throw exception } boolean isPostBack = (isPostback(facesContext) && !isErrorPage(facesContext)); if (isPostBack) { // try to restore the view ViewHandler viewHandler = Util.getViewHandler(facesContext); viewRoot = viewHandler.restoreView(facesContext, viewId); if (viewRoot == null) { if (is11CompatEnabled(facesContext)) { // try to create view jsf 1.1 way } else { // throw exception } } facesContext.setViewRoot(viewRoot); doPerComponentActions(facesContext, viewRoot); } else { // if that fails, create one viewRoot = (Util.getViewHandler(facesContext)). createView(facesContext, viewId); facesContext.setViewRoot(viewRoot); facesContext.renderResponse(); } assert(null != viewRoot); } /** * Do any per-component actions necessary during reconstitute * @param context the FacesContext for the current request * @param uic the UIComponent to process the * binding attribute */ protected void doPerComponentActions(FacesContext context, UIComponent uic) { // if this component has a component value reference expression, // make sure to populate the ValueExpression for it. ValueExpression valueExpression; if (null != (valueExpression = uic.getValueExpression("binding"))) { valueExpression.setValue(context.getELContext(), uic); } for (Iterator kids = uic.getFacetsAndChildren(); kids.hasNext(); ) { doPerComponentActions(context, kids.next()); } } /** * @param context the FacesContext for the current request * @return true if the {@link ResponseStateManager#isPostback(javax.faces.context.FacesContext)} * returns true and the request doesn't contain the * attribute javax.servlet.error.message which indicates we've been * forwarded to an error page. */ private boolean isPostback(FacesContext context) { // Get the renderKitId by calling viewHandler.calculateRenderKitId(). String renderkitId = context.getApplication().getViewHandler(). calculateRenderKitId(context); ResponseStateManager rsm = RenderKitUtils.getResponseStateManager(context, renderkitId); return rsm.isPostback(context); } /** * The Servlet specification states that if an error occurs * in the application and there is a matching error-page declaration, * the that original request the cause the error is forwarded * to the error page. * * If the error occurred during a post-back and a matching * error-page definition was found, then an attempt to restore * the error view would be made as the javax.faces.ViewState * marker would still be in the request parameters. * * Use this method to determine if the current request is * an error page to avoid the above condition. * * @param context the FacesContext for the current request * @return true if WEBAPP_ERROR_PAGE_MARKER * is found in the request, otherwise return false */ private static boolean isErrorPage(FacesContext context) { return (context.getExternalContext(). getRequestMap().get(WEBAPP_ERROR_PAGE_MARKER) != null); } private WebConfiguration getWebConfig(FacesContext context) { if (webConfig == null) { webConfig = WebConfiguration.getInstance(context.getExternalContext()); } return webConfig; } private boolean is11CompatEnabled(FacesContext context) { return (getWebConfig(context).isOptionEnabled( BooleanWebContextInitParameter.EnableRestoreView11Compatibility)); } }


Now looking at the method execute(...) of RestoreViewPhase, we can see an if-else clause. The condition is whether or not this method.

isPostback(facesContext)
will return true or false. The javadoc of this method states clearly that it will return true if the request method is POST or PUT,
or the method is GET but there are query parameters, or the request is not an instance of HttpServletRequest.

If it is a GET request with no parameters or if its a redirect then isPostback returns false.

So even though the page has been visited before, a brand new view will be created just like in Step 1 described above.
A typical case is that in the Invoke_Application phase of the lifecycle, JSF determines that everything is successful and invokes a redirect call to some page.
Now no matter whether or not that page has been visited before, it will be considered to be a new request.
And immmediately following the Invoke_Application phase, the actual call to the Render_Response phase will be skipped.
Instead, a new JSF lifecycle will start. And since it is regarded a new request, only two phases Restore_View and Render_Response will be executed for the page that is redirected to.

So under the redirect situation, a view will be re-created just as when the page is visited the first time. But this is not the whole story.
To make this more precise, there is some difference here from the first time the page is visited. In the appache tomcat implementation,
when the page is visited the first time, the Render_Reponse phase also does the following:

Calls the jsp compiler ( org.apache.jasper.compiler.Compiler ). This compiler will removeGeneratedFiles, generateJava, and generateClass for the jsp page. Note that the three phrases in bold are from the actual log file.
Creates the managed beans used in the JSP page. If the scope of the bean is session, the bean will be stored in the session.

In the subsequest visists to the same page,
- these compiler actions won't happen.
- JSF would have saved the tree either in this user's session or in a hidden field on the response.
The actual location being determined by the javax.faces.STATE_SAVING_METHOD context parameter in web.xml.
JSF will then either look for the tree in the session using the view id, or will regenerate it from the encrypted hidden field on the response.
- And the session-scoped beans do not need to be created again if it is in the same session.



RENDER_RESPONSE PHASE

Terminology used:

§ Encoding: This is the process of asking each component to write out its value into the response.
The component may delegate this task to its associated renderer, and may also invoke an associated converter to convert its native data type out to a String
that will be sent to the user's browser.

§ State saving: This is the process of dehydrating the component tree so that it can be hydrated back into existence later.

At this point, we are done with all application specific processing, and all that remains is to serialize the component tree out as HTML that will be sent back to the client -
and this task is handled by a javax.faces.lifecycle.ViewHandler.
All JSF implementations must ship with a default ViewHandler that can encode JSPs. However, you can swap this out for a custom implementation
(such as the one that ships with Facelets that can encode XHTML files.)

The component tree's current state (incl. the tree's structure) is then saved either on the client (in the request itself) or on the server (session scope).
Note that we specify this in web.xml.

With client side state saving, the view's state is actually serialized out as an encoded hidden form field within the response payload.
The client then returns it with its next request.

With server side state saving, the view's state is saved in the user's session on the server.

As expected, the trade off here is between memory consumption on the server and response times/bandwidth over a network connection.
You should begin using server side saving, and then switch to client side only if you run into issues with memory usage.

The java file in JSF to do the RENDER_RESPONSE phase is com.sun.faces.lifecycle.RenderResponsePhase.java.


public class RenderResponsePhase extends Phase {

    public PhaseId getId() {
        return PhaseId.RENDER_RESPONSE;
    }

    public void execute(FacesContext facesContext) throws FacesException {

        try {
            //Setup message display LOGGER.
            if (LOGGER.isLoggable(Level.INFO)) {
                Iterator clientIdIter = facesContext.getClientIdsWithMessages();

                //If Messages are queued
                if (clientIdIter.hasNext()) {
                    Set clientIds = new HashSet();

                    //Copy client ids to set of clientIds pending display.
                    while (clientIdIter.hasNext()) {
                        clientIds.add(clientIdIter.next());
                    }
                    RequestStateManager.set(facesContext,
                                            RequestStateManager.CLIENT_ID_MESSAGES_NOT_DISPLAYED,
                                            clientIds);
                }
            }

            //render the view
            facesContext.getApplication().getViewHandler().
                 renderView(facesContext, facesContext.getViewRoot());

        } catch (IOException e) {

        }
    }

} 

/*
 In the Render_Response phase, the method is the following:
 public void execute(FacesContext facesContext) throws FacesException
 {
    ......
    facesContext.getApplication().getViewHandler().renderView(facesContext, facesContext.getViewRoot());
    ......
 }
 The renderView method is from ViewHandlerImpl:
*/

public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {

        // suppress rendering if "rendered" property on the component is
        // false
        if (!viewToRender.isRendered()) {
            return;
        }

        ExternalContext extContext = context.getExternalContext();
        ServletRequest request = (ServletRequest) extContext.getRequest();
        ServletResponse response = (ServletResponse) extContext.getResponse();

        try {
            if (executePageToBuildView(context, viewToRender)) {
                //Execute the target view. If the HTTP status code range is not 2xx, then return true to indicate the response should be 
    //immediately flushed by the caller so that conditions such as 404 are properly handled.
            }
        } catch (IOException e) {
        }

        // set up the ResponseWriter

        RenderKitFactory renderFactory = (RenderKitFactory)
        FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
        RenderKit renderKit =
                renderFactory.getRenderKit(context, viewToRender.getRenderKitId());

        ResponseWriter oldWriter = context.getResponseWriter();

        if (bufSize == -1) {
            WebConfiguration webConfig =
                  WebConfiguration
                        .getInstance(context.getExternalContext());
            try {
                bufSize = Integer
                      .parseInt(webConfig.getOptionValue(
                            WebContextInitParameter.ResponseBufferSize));
            } catch (NumberFormatException nfe) {
                bufSize = Integer
                      .parseInt(WebContextInitParameter.ResponseBufferSize.getDefaultValue());
            }
        }


        WriteBehindStateWriter stateWriter =
              new WriteBehindStateWriter(response.getWriter(),
                                         context,
                                         bufSize);
        ResponseWriter newWriter;
        if (null != oldWriter) {
            newWriter = oldWriter.cloneWithWriter(stateWriter);
        } else {
            newWriter = renderKit.createResponseWriter(stateWriter,
                                                       null,
                                                       request.getCharacterEncoding());
        }
        context.setResponseWriter(newWriter);

        newWriter.startDocument();

        doRenderView(context, viewToRender);

        newWriter.endDocument();

        // replace markers in the body content and write it to response.

        // flush directly to the response
        if (stateWriter.stateWritten()) {
            stateWriter.flushToWriter();
        }

        // clear the ThreadLocal reference.
        stateWriter.release();

        if (null != oldWriter) {
            context.setResponseWriter(oldWriter);
        }

        // write any AFTER_VIEW_CONTENT to the response
        // side effect: AFTER_VIEW_CONTENT removed
        ViewHandlerResponseWrapper wrapper = (ViewHandlerResponseWrapper)
              RequestStateManager.remove(context, RequestStateManager.AFTER_VIEW_CONTENT);
        if (null != wrapper) {
            wrapper.flushToWriter(response.getWriter(),
                    response.getCharacterEncoding());
        }

        response.flushBuffer();

    }

The ViewHandler will eventually call the following:
stateManager.saveView(context);
where stateManager is an instance of com.sun.faces.applicaiton.StateManagerImpl.
StateManager manages state, but you, as an end-user, have to give it hints about what state needs to be kept. That's either via implementing Serializable.

Data is stored in two different ways in JSF:
- in scoped beans or
- in the component tree.

The scope of scoped beans are hopefully self-explanatory. The state of the components themselves are stored in the response, and then restored when a request arrives.
This effectively gives data stored by the components a "page" scope since they exist so long as the page doesn't change.

Note that components store value bindings and method bindings (#{} – el expressions) as string literals, so the backing beans they point to are not stored in the component tree at page scope.

Anyways this method will in turn invokes the following:

   /**
     * Return an opaque Object containing sufficient
     * information for this same instance to restore the state of the
     * current {@link UIViewRoot} on a subsequent request.  The returned
     * object must implement java.io.Serializable. If there
     * is no state information to be saved, return null
     * instead.
     *
     * Components may opt out of being included in the serialized view
     * by setting their transient property to true.
     * This must cause the component itself, as well as all of that component's
     * children and facets, to be omitted from the saved  tree structure
     * and component state information.
     *
     * 
  * This method must also enforce the rule that, for components with
     * non-null ids, all components that are descendants of the
     * same nearest {@link NamingContainer} must have unique identifiers.
     *
     * For backwards compatability with existing
     * StateManager implementations, the default
     * implementation of this method calls {@link #saveSerializedView}
     * and creates and returns a two element Object array
     * with element zero containing the structure property
     * and element one containing the state property of the
     * SerializedView.
     *
     * @since 1.2
     *
     * @param context {@link FacesContext} for the current request
     *
     * @throws IllegalStateException if more than one component or
     * facet within the same {@link NamingContainer} in this view has
     * the same non-null component id
     */
    public Object saveView(FacesContext context) {
  SerializedView view = saveSerializedView(context);
  Object stateArray[] = { view.getStructure(),
   view.getState() 
  };
  return stateArray;
    }
/*
 And the method saveSerializedView(...) does all the dirty work. 
 Basically it will create two objects treeStructure and componentState from the facesContext in the following code:
*/
 public SerializedView saveSerializedView(FacesContext context)
          throws IllegalStateException {
   SerializedView result = null;
   Object treeStructure = null;
   Object componentState = null;
   ......
   result = new SerializedView(treeStructure =
      getTreeStructureToSave(context),
          componentState =
             getComponentStateToSave(context));

   ......
 Object stateArray[] = {treeStructure, componentState};
 ......
 actualMap.put(idInActualMap, stateArray);
 ......
 }

/*
 In the above code, the Map object actualMap is in another Map logicalMap. And the logicalMap is created in sessionMap which is an object in the session.
 Now in another lifecycle when the view needs to be restored, the Restore_View phase will call viewHander.restoreView(facesContext, viewId) in its execute(...) method. 
 And the restoreView(...) method of the ViewHandlerImpl is the following:
*/
 public UIViewRoot restoreView(FacesContext context, String viewId,
                                  String renderKitId) {
 ......
 viewRoot = Util.getStateManager(context).restore(context,viewId,renderKitId);
 ......
 }

//So it basically uses the StateManager again to retrieve the view saved there before.



APPLY REQUEST VALUES PHASE

Terminology used:

§ Decoding:
This is the process by which a component sifts through incoming request parameters for information that may affect its state.
What a component looks for, and how it updates it's state is defined by that component.
For example,
input components expect to see updated values;
buttons and links expect to see an indication as to whether they were clicked;
and forms expect to find an indication that they were submitted.

§ Component identifier:
This is the value assigned to the id attribute for a component's tag.
A component id must be unique within its naming container.

§ Naming container:
A naming container is commonly a form (but can be a subview or a data table), and serves as a namespace for its children components.

§ Client identifier:
This is the true unique identifier for a component, and is composed of the naming container's id and the component's id, delimited by colons.
Note that for a component that is nested within a naming container hierarchy, e.g., a data table in a form in a subview, the client identifier can get fairly long.
It is called a client id because it is usually used to reference a component within Javascript functions.
A client id can be either absolute (if it starts with a colon) or relative.
It is also used by the UIComponent.findComponent() method to locate a component when it's client identifier is known.

§ UI Event:
This is an event that typically redisplays the current page after making some user interface changes to it -
such as toggling the display of a section, or adding/removing items from a selection list or radio buttons from a group.
You will typically want to short circuit processing to avoid validation errors being displayed for other fields on the form.

§ Application Event:
This is an event that invokes backed processing, and is the workhorse of your application.
It is handled by the default ActionListener, which calls the specified action method, and then invokes the default NavigationHandler to
determine the view to be displayed as the result of this action.

§ Action Event:
Represents the user clicking a UI control such as a button or hyperlink.

§ Immediate action Event:
This is a button or link whose immediate attribute is set to true.
This forces the button's associated handler to be invoked right after this phase completes, rather than after the Invoke Application phase.
A common use is to implement a Cancel button, which should not require the content of other input controls to be validated.
Once your handler runs, JSF automatically invokes the navigation handler and transfers control to the Render Response phase.

§ Value Change Event:
Represents the user changing the existing value of an input control such as a text box.
A value change on the client will only be detected when a new connection with the server is established,
e.g., when the user submits a form, or via client side scripting that causes an auto submit.

§ Immediate value change Event/input control:
This is an input control whose immediate attribute is set to true.
This forces the input control to be converted and validated within this phase, and, if its value has been changed,
causes its ValueChangeEvent to be invoked right after this phase completes, rather than after the Process Validations phase.
This is used to implement user interface events, i.e., when the same page must be redisplayed incorporating any changes caused by the event handler,
and no other input controls on the form need be validated just yet.
A common use is to implement a checkbox that, when clicked, toggles the display of a section in the view.
Note that unlike action event handlers, a value change event handler must explicitly call renderResponse() to skip to the Render Response phase -
else its usefulness is questionable since validations will otherwise occur in the Process Validations phase.

JSF allows each component in the current component tree a chance to decode the incoming request.
Each component looks for a request parameter with the same name as this component's client identifier.
When a matching request parameter is found, an input component sets its submitted value property to the parameter's value;
a button/link will queue an action event; and a form will set its submitted property to true.

If an input control's immediate property is set to true, it is converted/validated, and any ValueChangeEvents are invoked
at the end of this phase.
If an ActionSource's immediate property is true, action events are invoked at the end of this phase.

If an event handler, converter, validator, or decode() method calls responseComplete(),
the life cycle is terminated once this phase has been completed;
if they call renderResponse(), control skips to the Render Response phase of the life cycle once this phase has been completed.
E.g., a conversion/validation error for an immediate component, would re-render the view with an error message.


Understanding the JSF EVENT MODEL

The JSF event model is based on the event model defined by the JavaBeans specification.
In this model, an event is represented by an instance of an event class.
An event source object fires an event by calling an event notification method on event listener objects registered
to receive the event, passing a reference to the event object as a notification method argument.

All JSF event classes extend the javax.faces.event.FacesEvent class

import java.util.EventObject;
public abstract class FacesEvent extends EventObject {
    public FacesEvent(UIComponent component) {
        super(component);
    }
    public UIComponent getComponent( ) {
        return ((UIComponent) getSource( ));
    }
    ...
}

//When a user clicks a button, it triggers an event represented by the javax.faces.event.ActionEvent class:

public class ActionEvent extends FacesEvent {
    public ActionEvent(UIComponent component) {
        super(component);
    }
    ...
}

Other events are represented by similar concrete subclasses, such as the javax.faces.event.ValueChangeEvent,
which signals a value change.
Along with the event classes, there are listener interfaces declaring the methods that the event source calls to notify
listeners of the event. A listener interface can contain methods for many related events, but for the JSF component events,
there's a separate interface per event. Here's the javax.faces.event.ActionListener interface:

public interface ActionListener extends FacesListener {
    public void processAction(ActionEvent event) throws AbortProcessingException;
}

The ActionListener interface extends the javax.faces.event.FacesListener interface and defines one method,
taking an ActionEvent instance as the single argument.

Classes that want to be informed about events are called event listeners.
They declare which events they are interested in by implementing the corresponding listener interfaces.
Hence, an event listener that wants to deal with the ActionEvent fired by a command component declares its intent like this:

public class ReportHandler implements ActionListener {
    ...
    public void processAction(ActionEvent e) throws AbortProcessingException {
        ...
    }
}

Event source classes, like UICommand, declare the type of events they can fire by providing methods for registering and deregistering
the corresponding event listeners:

public void addActionListener(ActionListener listener) {
    addFacesListener(listener);
}

public void removeActionListener(ActionListener listener) {
    removeFacesListener(listener);
}

The addFacesListener() and removeFacesListener() methods called by the registration and deregistration methods are protected methods implemented by UIComponentBase, so that the task of maintaining the listener list doesn't have to be implemented by all subclasses.
When a component notices that a user event has happened, it creates an instance of the corresponding event class and adds it to an event list. Eventually, JSF tells the component to fire the event, i.e., loop through the list of listeners for that event and call the event notification method on each one.



PROCESS VALIDATIONS PHASE

§ Converter:
Converts the String value in the input component's submitted value attribute into the appropriate data type for this component's value.

§ Validator:
Applies validation rules to the converted value for a component.

§ FacesMessage:
Represents a message that needs to be displayed to the user. FacesContext maintains a message queue that holds the messages for a given view.
A message can either be general, and apply to the whole view; or it can be specific to a given component.
Messages are displayed to the user using the or actions.

Conversion:

JSF first looks for an appropriate converter that it should use to convert the String value that the component extracted into the target data type
(as defined by the attribute in the managed bean to which this component is bound.)

It first checks with the component's renderer; next it looks if any custom Converter implementation has been attached to this class's action via its
converter attribute or a nested converter action; and finally it looks whether a converter has been registered for this component's target data type;
in this order of precedence. This mechanism is mutually exclusive, i.e., the first converter found is used. If no converter was found at either of
these locations, the submitted value is left untouched.

The converter is then requested to convert the String submitted value to the target data type.

If conversion fails, it asks the component to store the invalid string as its value, so that it can be presented back to the user when the page
is rerendered; throws a ConverterException with an appropriate FacesMessage which is added to the message queue in FacesContext;
and sets the component's valid property to false. JSF then skips to Render Response to notify the user of input errors.

If conversion succeeds, the converted value is marked as the component's converted value.

It is important to note that converters actually do double duty; they convert Strings to the target data types (as is the case in this phase),
and they convert the target data type to a String (which is required during Render Response to write out the HTML response.)

Validation:

If conversion succeeded for all the components in the current tree, JSF asks each UIInput to validate this converted value.

Unlike the search for a converter, the search for a validator is cumulative.
I.e., validation is delegated to all validators that are attached to this component, and all must agree before the component is marked as being valid.
Validators can be attached to a component via multiple mechanisms.
E.g., a component's required attribute causes an implicit validator to be attached;
its validator attribute takes a binding that points to a validator method;
and validators can be attached using nested f:validate actions.

If any of the validators in that stack detects a problem with the component's converted value,
it will throw a ValidatorException with an appropriate FacesMessage, which is then added to the FacesContext message queue,
and the component is marked as invalid by setting its valid property to false.
JSF then skips to Render Response to notify the user of the validation error.

If the validation process succeeds, the converted value is marked as the component's local value.
If the local value has changed, the component also generates a ValueChangeEvent.

If any event handler or validate() method calls responseComplete(), processing of the current request is terminated after this phase is completed;
and if it calls renderResponse(), JSF skips to Render Response once this phase is completed.


UPDATE MODEL VALUES PHASE

§ Value binding: Unified Expression Language syntax can be used to define the bean attribute to which a particular component is bound.
Note that the expression may be several levels deep, if referencing a property of a property of a bean, etc.

Important activities in this phase:

Beans are connected to components via JSF EL expressions.
These expressions are usually value binding expressions that indicate a managed bean in one of the servlet scopes,
and a setter that references the desired property to which the value should be transferred.

If update fails, a message is queued via FacesContext.addMessage(), and the component is marked as invalid. FacesServlet then skips to Render Response to notify the user of input errors.


INVOKE APPLICATION PHASE

§ Action Listener:
A command component (a button or a link) can be associated with handlers via (in order of preference)
its action attribute, its actionListener attribute, or via nested actionListener actions.

All of these action listeners will be invoked when this component is clicked.
The action attribute binding is special in that it is handled by the JSF default ActionListener which also invokes the
default NavigationHandler to navigate to the next view.
The actionListener binding is special since it lets you access the component that fired the event.
The nested actionListener actions are special because they let you bind multiple action listeners to a particular component.

Now that the backing beans and model objects are up-to-date, JSF broadcasts the queued ActionEvents by invoking the registered action listeners
for each component. This is a safe point at which to invoke these listeners because the other components in the tree have picked up and validated
their values. Note that listeners for immediate action events are notified earlier in the cycle because they are designed to handle events that
will skip the validation phase, such as a Cancel button.

For an action method binding, after the action listeners are invoked; the default action listener invokes the NavigationHandler to choose the next page.
If no action method is registered, the default action listener simply redisplays the current view.

References:

http://zachxu.blogspot.in/2011/10/how-jsf-restore-view-phase-restores.html
http://zachxu.blogspot.in/2011/10/how-are-per-web-application-singleton.html#uds-search-results
http://zachxu.blogspot.in/2011/10/note-on-jsf-lifecycle-and-navigation.html
http://zachxu.blogspot.in/2011/10/main-classes-called-by-phase-objects-in.html
http://zachxu.blogspot.in/2012/06/how-is-converter-wired-to-jsf-tag.html
http://zachxu.blogspot.in/2010/03/trick-in-jsf-event-handling.html
http://illegalargumentexception.blogspot.in/2009/02/jsf-working-with-component-ids.html

http://www.softwareengineeringsolutions.com/thoughts/frameworks/JSF-lifecycle.htm
http://www.ibm.com/developerworks/library/wa-dsgnpatjsf/index.html

http://www.softwareengineeringsolutions.com/thoughts/frameworks/JSF.Techniques-Adding.Components.Dynamically.htm
http://www.softwareengineeringsolutions.com/thoughts/frameworks/JSF.Techniques-ELResolvers.htm
http://www.softwareengineeringsolutions.com/thoughts/frameworks/JSF.Techniques-PhaseListeners.htm
http://www.softwareengineeringsolutions.com/thoughts/frameworks/JSF.Techniques-Error.Handling.htm
http://www.softwareengineeringsolutions.com/thoughts/charsets/CharacterSetsAndEncodings.html
http://www.softwareengineeringsolutions.com/thoughts/charsets/WebDevelopmentEncoding.html

2 comments: