Now that you have read about the "what" and the "why" of JavaServer Faces, you may be curious just how the JSF framework does its job. Let us look behind the scenes of our sample application. We'll start at the point when the browser first connects to http://localhost:8080/login/index.faces. The JSF servlet initializes the JSF code and reads the index.jsp page. That page contains tags such as f:form and h:inputText. Each tag has an associated tag handler class. When the page is read, the tag handlers are executed. The JSF tag handlers collaborate with each other to build a component tree (see Figure 1-11). Figure 1-11. Component Tree of the Sample Application The component tree is a data structure that contains Java objects for all user interface elements on the JSF page. For example, the two UIInput objects correspond to the h:inputText and h:inputSecret fields in the JSF file. Rendering Pages Next, the HTML page is rendered. All text that is not a JSF tag is simply passed through. The h:form, h:inputText, h:inputSecret, and h:commandButton tags are converted to HTML. As we just discussed, each of these tags gives rise to an associated component. Each component has a renderer that produces HTML output, reflecting the component state. For example, the renderer for the component that corresponds to the h:inputText tag produces the following output:
<input type="text" name="unique ID" value="current value"/> This process is called encoding. The renderer of the UIInput object asks the framework to look up the unique ID and the current value of the expression user.name. By default, ID strings (such as _id0:_id1) are assigned by the framework. The encoded page is sent to the browser, and the browser displays it in the usual way (see Figure 1-12). Figure 1-12. Encoding and Decoding JSF Pages TIP | Select View->Page source from the browser menu to see the HTML output of the rendering process. Figure 1-13 shows a typical output. This is useful for debugging JSF problems. Figure 1-13. Viewing the Source of the Login Page
|
Decoding Requests After the page is displayed in the browser, the user fills in the form fields and clicks the "Login" button. The browser sends the form data back to the web server, formatted as a "POST request." This is a special format, defined as part of the HTTP protocol. The POST request contains the URL of the form (/login/index.faces), as well as the form data. NOTE | The URL for the POST request is the same as that of the request that renders the form. Navigation to a new page occurs after the form has been submitted. |
The form data is a string of ID/value pairs, such as
_id0:_id1=me&_id0:_id2=secret&_id0:_id3=Login&_id0=_id0 As part of the normal servlet processing, the form data is placed in a hash table that all components can access. Next, the JSF framework gives each component a chance to inspect that hash table, a process called decoding. Each component decides on its own how to interpret the form data. The login form has three component objects: two UIInput objects that correspond to the text fields on the form and a UICommand object that corresponds to the submit button. The UIInput components update the bean properties referenced in the value attributes: they invoke the setter methods with the values that the user supplied. The UICommand component checks whether the button was clicked. If so, it fires an action event to launch the login action referenced in the action attribute. That event tells the navigation handler to look up the successor page, welcome.jsp. Now the cycle repeats. You have just seen the two most important processing steps of the JSF framework: encoding and decoding. However, the processing sequence (also called the "life cycle") is a bit more intricate. If everything goes well, you don't need to worry about the intricacies of the life cycle. However, when an error occurs, you will definitely want to understand what the framework does. In the next section, we look at the life cycle in greater detail. The Life Cycle The JSF specification defines six distinct phases, as shown in Figure 1-14. The normal flow of control is shown with solid lines; alternative flows are shown with dashed lines. Figure 1-14. The JavaServer Faces Life Cycle The Restore View phase retrieves the component tree for the requested page if it was displayed previously or constructs a new component tree if it is displayed for the first time. If the page was displayed previously, all components are set to their prior state. This means that JSF automatically retains form information. For example, when a user posts illegal data that are rejected during decoding, the old inputs are redisplayed so that the user can correct them. If the request has no query data, the JSF implementation skips ahead to the Render Response phase. This happens when a page is displayed for the first time. Otherwise, the next phase is the Apply Request Values phase. In this phase, the JSF implementation iterates over the component objects in the component tree. Each component object checks which request values belong to it and stores them. NOTE | In addition to extracting request information, the "Apply Request Values" phase adds events to an event queue when a command button or link has been clicked. We discuss event handling in detail in Chapter 7. As you can see in Figure 1-14, events can be executed after each phase. In specialized situations, an event handler can "bail out" and skip to the Render Response phase or even terminate request processing altogether. |
In the Process Validations phase, the submitted string values are first converted to "local values," which can be objects of any type. When you design a JSF page, you can attach validators that perform correctness checks on the local values. If validation passes, the JSF life cycle proceeds normally. However, when conversion or validation errors occur, the JSF implementation invokes the Render Response phase directly, redisplaying the current page so that the user has another chance to provide correct inputs. NOTE | To many programmers, this is the most surprising aspect of the JSF life cycle. If a converter or validator fails, the current page is simply redisplayed. You should add tags to display the validation errors so that your users know why they see the old page again. See Chapter 6 for details. |
After the converters and validators have done their work, it is assumed that it is safe to update the model data. During the Update Model phase, the local values are used to update the beans that are wired to the components. In the Invoke Application phase, the action method of the button or link component that caused the form submission is executed. That method can carry out arbitrary application processing. It returns an outcome string that is passed to the navigation handler. The navigation handler looks up the next page. Finally, the Render Response phase encodes the response and sends it to the browser. When a user submits a form, clicks on a link, or otherwise generates a new request, the cycle starts anew. You have now seen the basic mechanisms that make the JSF magic possible. In the following chapters, we examine the various parts of the life cycle in more detail. |