{scrollbar}

When learning JSF, there's nothing quite like looking at a real implementation.

Here's a suggested list of classes/methods to look at when trying to get to grips with how JSF and MyFaces actually works, together with a brief description of the typical behaviour of interesting methods.

Note that the descriptions below describe typical behaviour of the code. There are all sorts of overrides, configurable behaviours and special cases that can occur; see the real implementation for the full details - or read the O'Reilly book which takes 570 pages to describe this process (smile)

javax.faces.webapp.FacesServlet
  • method init shows basic faces setup. In particular it shows how the FactoryFinder is used to create the LifeCycle and FacesContext factories.
  • method service shows that the Lifecycle object controls most of JSF processing
org.apache.myfaces.lifecycle.LifecycleImpl:
  • every method in this class is worth reading carefully.
  • the execute method is the core of JSF, and shows how the different phases fit together
  • the normal restoreView implementation recreates a tree of "empty" component objects using type names it stored away earlier, calling the default constructor for each component type to create the instances. Each created component's restoreState method is then called to reinitialise the component's fields from the previously saved state data.
  • applyRequestValues calls getRenderer() on each component, then invokes the renderer's decode method which gets a "client id" from the component and looks for a matching key in the request params. If found, the component's _submittedValue member is set to the value found.
  • processValidations causes each "editable" component to execute any validators attached to it, passing the _submittedValue value. If validation fails, a message is inserted into a request-scope set of messages, and the "renderResponse" flag is set so that the following updateModelValues and invokeApplication phases are skipped.
  • assuming no validation problems occurred, updateModelValues causes each component to copy _submittedValue into whatever backing-bean property was bound to it using "#{bean.prop}" syntax. The _submittedValue field is then set to null to indicate that the model was updated.
  • invokeApplication executes callbacks for any value-change or similar events discovered and queued during earlier phases.
  • render iterates over each component, calling its getRenderer() method to get the renderer object, then calling that renderer object to generate the appropriate output (HTML or other). If a component has a _submittedValue, then that is rendered in preference to anything else. This allows components which failed validation to re-render an invalid value entered by the user.
javax.faces.component.UIViewRoot:
  • queueEvent is the method that is called when a component decides to fire a value-change event (or any other type of event). Some interesting places where events are queued are:
  • javax.faces.component.UIInput, method validate
  • org.apache.myfaces.renderkit.html.HtmlButtonRendererBase, method decode
javax.faces.component.UIComponentBase:
  • Method getRenderer shows how the renderkit-id from the current view, the component's declared component-family and the component's declared renderer-type name are used to determine what renderer object to use.
org.apache.myfaces.renderkit.html.HtmlRenderKitImpl:
  • getRenderer shows how a renderkit instance uses the component's component-family and renderer-type to look up a map to determine the renderer class to be used for that component. Note also that only one instance of each specific renderer class is ever created; a renderer instance is expected to be stateless (and threadsafe) and is used to render all instances of the associated components. The renderkit's map is configured in code for the built-in types, and any META-INF/faces.config files found in the classpath can add extra entries to the map for a specific renderkit via:
xml <render-kit> <render-kit-id>some-id</render-kit-id> <renderer> <component-family>javax.faces.Input</component-family> <renderer-type>org.apache.myfaces.InputSuggest</renderer-type> <renderer-class>org.apache.myfaces.custom.suggest.InputSuggestRenderer</renderer-class> </renderer>
javax.faces.webapp.UIComponentTag:
  • Method createComponentInstance shows how a UIComponent is instantiated if a JSP tag references it and it doesn't already exist in the view, by calling Application.createComponent(String). Note that createComponentInstance calls method getComponentType() on itself, which is typically implemented in the leaf JSP tag classes, eg org.apache.myfaces.taglib.html.!HtmlCommandButtonTag.
org.apache.myfaces.application.ApplicationImpl:
  • Method createComponent takes a String component-type, and looks up its component map to find a Class object then calls class.newInstance on it. The component map is configured in code for the built-in types, and any META-INF/faces-config.xml files found in the classpath can add extra entries to this via:
xml <component-type>... <component-class>...
  • The other methods of this class are worth studying for more advanced purposes; this is where a lot of JSF customisation can occur.
javax.faces.component.UIInput:
  • Methods processDecodes, processValidators, and processUpdates shows how form data becomes model data through a process of conversion and validation. For most components, This is where the real work mentioned above in org.apache.myfaces.lifecycle.!LifecycleImpl occurs during the Apply Values, Process Validations, and Update Model phases. Note that data starts in a form, is loaded into a submitted value field on the component, is converted to a local value field on the component, and is copied into the backing bean. Many conditions affect the outcome of this process, such as rendered state, required state, immediate state, conversion errors, and validation errors. {scrollbar}
  • No labels