Monday, June 11, 2012

How Are the JSF Tags Processed In The JSF Lifecycle

When the browser first connects to a jsp page, 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. Here is what happens in the Render_Response phase.

(A) It calls ViewHandlerImpl.executePageToBuildView(...)

  1. The servlet reads the page.
  2. The page contains JSF tags. Each tag has an associated tag handler class. As tags are read, the tag handler classes are executed.
  3. The tag handlers collaborate with each other to build a component tree.
Now let's see some more details in step 2. How are those tag handler classes identified and retrieved? A jsp page declares the tag libraries it used in the beginning of the page. It may use one of the following two formats:
  • <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
  • <jsp:root xmlns:h="http://java.sun.com/jsf/html">
The uri value is used as the identifier by the servlet to locate the TLD file. The TLD file defines its uri value inside itself. The servlet will search for the TLD files at the standard locations to find the matching TLD file.

The TLD file also declares the class name for the tag. That is the tag handler to be invoked. The servlet can just use to class name to load the class.

The tag handler has an instance field for each attribute and the corresponding setter method. When the tag is processed, each tag attribute value is converted to a ValueExpression object, and the setter method is called.

The tag handler also knows the corresponding component class. It has the following method:

public String getComponentType(){...}
The implementation of the tag handler class returns a type value. However this type value is not necessarily the classname of the component class. It is used as an ID to find the class name in the faces-config.xml file. An example is as follows:
<component>
  <component-type>
     com.foo.Xyz
  </component-type>
  <component-class>
     com.foo.UIXyz
  </component-class>
</component>
The tag handler class uses the setProperties(UIComponent component) method to save the attribute values into the component class.

Note: A tag handler may not have a component class. For example, the <f:view> tag has the tag handler class com.sun.faces.taglib.jsf_core.ViewTag. Its getComponentType() method is as follows:

 /**
     * This should never get called for PageTag.
     */
 public String getComponentType() {
        assert (false);
        throw new IllegalStateException();
    }
The javadoc of this class says the following:
/**
 * All JSF component tags must be nested within a f:view tag.  This tag
 * corresponds to the root of the UIComponent tree.  It does not have a
 * Renderer. It exists mainly to provide a guarantee that all faces
 * components reside inside of this tag.
 */

The JSF tags implement the JSP Tag interface. So basically this is the JSP technology. What is different in JSF is that many of the JSF tags have associated component class. And these component classes are put together to form a component tree, which is a center piece in JSF technology.

In the JSP technology, a JSP tag can also have attributes. The tag implementation also needs the setter method for each of the attributes. When the JSP engine encounters the attributes in the tag, it calls the setter methods. Then when the doStartTag() method or other methods of the Tag interface is called, it can print out the attribute value or do anything related to the attribute. In JSF, there are also the setter methods for the attributes. But the tag handler has a new method setProperties(), where the attributes are saved to the component class of the tag.

So compared to the JSP technology, the basic strategy of JSF seems to be that instead of processing the tags and the attributes immediately, it temporarily saves the attribute values into the component tree, and then processes these values in different phases in the JSF lifecycle.

All the above happens in step (A). Now the next step is step (B).

(B) It calls ViewHandlerImpl.renderView(....) to render the page

In this step, all text in the JSP file that is not a JSF tag is passed through. The component classes execute the following render mehtods to produce the HTML text:

  1. encodeBegin()
  2. encodeChildren()
  3. encodeEnd()
These methods basically print out the HTML text.They use the attributes that the tag handlers have saved into them in step (A).

Next the encoded page is sent to the browser.

The browser displays the page. The user fills in the form fields and submits the page.

The JSF framework calls the RestoreView phase and then the ApplyRequestValues phase.

In the ApplyRequestValues phase, the decode() method of the component classes are executed. What does the decode() method do? The method will use the FacesContext to get the request parameter, process it, and calls the method setSubmittedValue(Object). This method is inherited from the class javax.faces.component.UIInput. Notice that this method takes only one parameter. So it seems that an UIInput tag can only deal with one value.

So the submitted value is stored in the component regardless if it is valid or not. But the decode() method will set a flag if it is valid. Subsequently in the JSF life cycle, the value will be converted and validated by the JSF framework.

The component class sets the converter for itself. It can also set its renderer.

References

1. Core JavaServer Faces second edition
2. http://java.sun.com/javaee/javaserverfaces/reference/docs/customRenderKit.html

No comments:

Post a Comment