Skip to content

Latest commit

 

History

History
2199 lines (1831 loc) · 95.4 KB

RequestProcessingLifecycle.adoc

File metadata and controls

2199 lines (1831 loc) · 95.4 KB

Request Processing Lifecycle

Web user interfaces generally follow a pattern where the user-agent sends one or more requests to the server with the end goal of displaying a user-interface. In the case of Web browsers, an initial HTTP GET or POST request is made to the server, which responds with a document which the browser interprets and automatically makes subsequent requests on the user’s behalf. The responses to each of these subsequent requests are usually images, JavaScript files, CSS Style Sheets, and other artifacts that fit “into” the original document. If the Jakarta Faces lifecycle is involved in rendering the initial response, the entire process of initial request, the response to that request, and any subsequent requests made automatically by the user-agent, and their responses, is called a Faces View Request/Response for discussion. The following graphic illustrates a Faces View Request/Response.

Faces View Request/Response Lifecycle

Each Faces View Request/Response goes through a well-defined request processing lifecycle made up of phases. There are three different scenarios that must be considered, each with its own combination of phases and activities:

  • Non-Faces Request generates Faces Response

  • Faces Request generates Faces Response

  • Faces Request generates Non-Faces Response

Where the terms being used are defined as follows:

  • Faces Response —A response that was created by the execution of the Render Response phase of the request processing lifecycle.

  • Non-Faces Response —A response that was not created by the execution of the render response phase of the request processing lifecycle. Examples would be a Jakarta Servlet-generated or Jakarta Server Pages-rendered response that does not incorporate Jakarta Faces components, a response that sets an HTTP status code other than the usual 200 (such as a redirect), or a response whose HTTP body consists entirely of the bytes of an in page resource, such as a JavaScript file, a CSS file, an image, or an applet. This last scenario is considered a special case of a Non-Faces Response and will be referred to as a Faces Resource Response for the remainder of this specification.

  • Faces Request —A request that was sent from a previously generated Faces response. Examples would be a hyperlink or form submit from a rendered user interface component, where the request URI was crafted (by the component or renderer that created it) to identify the view to use for processing the request. Another example is a request for a resource that the user-agent was instructed to fetch an artifact such as an image, a JavaScript file, a CSS stylesheet, or an applet. This last scenario is considered a special case of a Faces Request and will be referred to as a Faces Resource Request for the remainder of this specification.

  • Non-Faces Request —A request that was sent to an application component (e.g. a Jakarta Servlet or Jakarta Server Pages page), rather than directed to a Faces view.

In addition, of course, your web application may receive non-Faces requests that generate non-Faces responses. Because such requests do not involve Jakarta Faces at all, their processing is outside the scope of this specification, and will not be considered further.

READER NOTE: The dynamic behavior descriptions in this Chapter make forward references to the sections that describe the individual classes and interfaces. You will probably find it useful to follow the reference and skim the definition of each new class or interface as you encounter them, then come back and finish the behavior description. Later, you can study the characteristics of each Jakarta Faces API in the subsequent chapters.

Request Processing Lifecycle Scenarios

Each of the scenarios described above has a lifecycle that is composed of a particular set of phases, executed in a particular order. The scenarios are described individually in the following subsections.

Non-Faces Request Generates Faces Response

An application that is processing a non-Faces request may use Jakarta Faces to render a Faces response to that request. In order to accomplish this, the application must perform the common activities that are described in the following sections:

Faces Request Generates Faces Response

The most common lifecycle will be the case where a previous Faces response includes user interface controls that will submit a subsequent request to this web application, utilizing a request URI that is mapped to the Jakarta Faces implementation’s controller, as described in Servlet Mapping. Because such a request will be initially handled by the Jakarta Faces implementation, the application need not take any special steps—its event listeners, validators, and application actions will be invoked at appropriate times as the standard request processing lifecycle, described in the following diagrams, is invoked.

Faces Resource Request Lifecycle

The “Handle Resource Request” box, and its subsequent boxes, are explained in Resource Handling. The following diagram explains the “Execute and Render Lifecycle” box.

Faces Execute and Render Lifecycle

The behavior of the individual phases of the request processing lifecycle are described in individual subsections of Standard Request Processing Lifecycle Phases. Note that, at the conclusion of several phases of the request processing lifecycle, common event processing logic (as described in Common Event Processing) is performed to broadcast any FacesEvents generated by components in the component tree to interested event listeners.

Faces Request Generates Non-Faces Response

Normally, a Jakarta Faces-based application will utilize the Render Response phase of the request processing lifecycle to actually create the response that is sent back to the client. In some circumstances, however, this behavior might not be desirable. For example:

  • A Faces Request needs to be redirected to a different web application resource (via a call to HttpServletResponse.sendRedirect).

  • A Faces Request causes the generation of a response using some other technology (such as a Jakarta Servlet, or a Jakarta Server Pages page not containing Jakarta Faces components).

  • A Faces Request causes the generation of a response simply by serving up the bytes of a resource, such as an image, a JavaScript file, a CSS file, or an applet

In any of these scenarios, the application will have used the standard mechanisms of the Jakarta Servlet or Portlet API to create the response headers and content. It is then necessary to tell the Jakarta Faces implementation that the response has already been created, so that the Render Response phase of the request processing lifecycle should be skipped. This is accomplished by calling the responseComplete() method on the FacesContext instance for the current request, prior to returning from event handlers or application actions.

Standard Request Processing Lifecycle Phases

The standard phases of the request processing lifecycle are described in the following subsections.

The default request lifecycle processing implementation must ensure that the currentPhaseId property of the FacesContext instance for this request is set with the proper PhaseId constant for the current phase as early as possible at the beginning of each phase.

Restore View

The Jakarta Faces implementation must perform the following tasks during the Restore View phase of the request processing lifecycle:

  • Call initView() on the ViewHandler. This will set the character encoding properly for this request.

  • Examine the FacesContext instance for the current request. If it already contains a UIViewRoot:

    • Set the locale on this UIViewRoot to the value returned by the getRequestLocale() method on the ExternalContext for this request.

    • Take no further action during this phase, and return. The presence of a UIViewRoot already installed in the FacesContext before the Restore View Phase implementation indicates that the phase should assume the view has already been restored by other means.

  • Derive the viewId according to the following algorithm, or one semantically equivalent to it.

    • Look in the request map for a value under the key jakarta.servlet.include.path_info. If found, let it be the viewId.

    • Call getRequestPathInfo() on the current ExternalContext. If this value is non-null, let this be the viewId .

    • Look in the request map for a value under the key jakarta.servlet.include.servlet_path. If found, let it be the viewId.

    • If none of these steps yields a non-null viewId, throw a FacesException with an appropriate localized message.

  • Determine if this request is a postback or initial request by executing the following algorithm. Find the render-kit-id for the current request by calling calculateRenderKitId() on the Application’s ViewHandler. Get that RenderKit’s ResponseStateManager and call its isPostback() method, passing the current FacesContext. If the current request is an attempt by the servlet container to display a servlet error page, do not interpret the request as a postback, even if it is indeed a postback.

  • If the request is a postback, call setProcessingEvents(false) on the current FacesContext. Then call ViewHandler.restoreView(), passing the FacesContext instance for the current request and the view identifier, and returning a UIViewRoot for the restored view. If the return from ViewHandler.restoreView() is null, throw a ViewExpiredException with an appropriate error message. jakarta.faces.application.ViewExpiredException is a FacesException that must be thrown to signal to the application that the expected view was not returned for the view identifier. An application may choose to perform some action based on this exception.

  • Store the restored UIViewRoot in the FacesContext.

  • Call setProcessingEvents(true) on the current FacesContext.

  • If the request is not a postback, try to obtain the ViewDeclarationLanguage from the ViewHandler, for the current viewId by calling ViewHandler.deriveLogicalViewId() and passing the result to ViewHandler.getViewDeclarationLanguage(). If no such instance can be obtained, call facesContext.renderResponse(). Otherwise, call getViewMetadata() on the ViewDeclarationLanguage instance. If the result is non-null, call createMetadataView() on the ViewMetadata instance. Call ViewMetadata.hasMetadata(), passing the newly created viewRoot. If this method returns false, call facesContext.renderResponse(). If it turns out that the previous call to createViewMetadata() did not create a UIViewRoot instance, call createView() on the ViewHandler.

View Protection
  • Call ViewHandler.getProtectedViewsUnmodifiable() to determine if the view for this viewId is protected. If not, assume the requested view is not protected and take no additional view protection steps. Obtain the value of the value of the request parameter whose name is given by the value of ResponseStateManager.NON_POSTBACK_VIEW_TOKEN_PARAM. If there is no value, throw ProtectedViewException. If the value is present, compare it to the return from ResponseStateManager.getCryptographicallyStrongTokenFromSession(). If the values do not match, throw ProtectedViewException. If the values do match, look for a Referer [sic] request header. If the header is present, use the protected view API to determine if any of the declared protected views match the value of the Referer header. If so, conclude that the previously visited page is also a protected view and it is therefore safe to continue. Otherwise, try to determine if the value of the Referer header corresponds to any of the views in the current web application. If not, throw a ProtectedViewException. If the Origin header is present, additionally perform the same steps as with the Referer header.

  • Call renderResponse() on the FacesContext .

Obtain a reference to the FlowHandler from the Application. Call its clientWindowTransition() method. This ensures that navigation that happened as a result of the renderer for the jakarta.faces.OutcomeTarget component-family is correctly handled with respect to flows. For example, this enables <h:button> to work correctly with flows.

Using Application.publishEvent(), publish a PostAddToViewEvent with the created UIViewRoot as the event source.

In all cases, the implementation must ensure that the restored tree is traversed and the PostRestoreStateEvent is published for every node in the tree.

At the end of this phase, the viewRoot property of the FacesContext instance for the current request will reflect the saved configuration of the view generated by the previous Faces Response, or a new view returned by ViewHandler.createView() for the view identifier.

Apply Request Values

The purpose of the Apply Request Values phase of the request processing lifecycle is to give each component the opportunity to update its current state from the information included in the current request (parameters, headers, cookies, and so on). When the information from the current request has been examined to update the component’s current state, the component is said to have a “local value”.

During the Apply Request Values phase, the Jakarta Faces implementation must call the processDecodes() method of the UIViewRoot of the component tree. This will normally cause the processDecodes() method of each component in the tree to be called recursively, as described in the Javadocs for the UIComponent.processDecodes() method. The processDecodes() method must determine if the current request is a “partial request” by calling FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() . If FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() returns true, perform the sequence of steps as outlined in Apply Request Values Partial Processing. Details of the decoding process follow.

During the decoding of request values, some components perform special processing, including:

  • Components that implement ActionSource (such as UICommand), which recognize that they were activated, will queue an ActionEvent. The event will be delivered at the end of Apply Request Values phase if the immediate property of the component is true, or at the end of Invoke Application phase if it is false.

  • Components that implement EditableValueHolder (such as UIInput), and whose immediate property is set to true, will cause the conversion and validation processing (including the potential to fire ValueChangeEvent events) that normally happens during Process Validations phase to occur during Apply Request Values phase instead.

As described in Common Event Processing, the processDecodes() method on the UIViewRoot component at the root of the component tree will have caused any queued events to be broadcast to interested listeners.

At the end of this phase, all EditableValueHolder components in the component tree will have been updated with new submitted values included in this request (or enough data to reproduce incorrect input will have been stored, if there were conversion errors). In addition, conversion and validation will have been performed on EditableValueHolder components whose immediate property is set to true, as described in the UIInput Javadocs. Conversions and validations that failed will have caused messages to be enqueued via calls to the addMessage() method of the FacesContext instance for the current request, and the valid property on the corresponding component(s) will be set to false.

If any of the decode() methods that were invoked, or an event listener that processed a queued event, called responseComplete() on the FacesContext instance for the current request, clear the remaining events from the event queue and terminate lifecycle processing of the current request. If any of the decode() methods that were invoked, or an event listener that processed a queued event, called renderResponse() on the FacesContext instance for the current request, clear the remaining events from the event queue and transfer control to the Render Response phase of the request processing lifecycle. Otherwise, control must proceed to the Process Validations phase.

Apply Request Values Partial Processing

Call FacesContext.getPartialViewContext(). Call PartialViewContext.processPartial() passing the FacesContext, PhaseID.APPLY_REQUEST_VALUES as arguments.

Process Validations

As part of the creation of the view for this request, zero or more Validator instances may have been registered for each component. In addition, component classes themselves may implement validation logic in their validate() methods.

During the Process Validations phase of the request processing lifecycle, the Jakarta Faces implementation must call the processValidators() method of the UIViewRoot of the tree. This will normally cause the processValidators() method of each component in the tree to be called recursively, as described in the API reference for the UIComponent.processValidators() method. The processValidators() method must determine if the current request is a “partial request” by calling FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() . If FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() returns true, perform the sequence of steps as outlined in Partial Validations Partial Processing. Note that EditableValueHolder components whose immediate property is set to true will have had their conversion and validation processing performed during Apply Request Values phase.

During the processing of validations, events may have been queued by the components and/or Validators whose validate() method was invoked. As described in Common Event Processing, the processValidators() method on the UIViewRoot component at the root of the component tree will have caused any queued events to be broadcast to interested listeners.

At the end of this phase, all conversions and configured validations will have been completed. Conversions and Validations that failed will have caused messages to be enqueued via calls to the addMessage() method of the FacesContext instance for the current request, and the valid property on the corresponding components will have been set to false.

If any of the validate() methods that were invoked, or an event listener that processed a queued event, called responseComplete() on the FacesContext instance for the current request, clear the remaining events from the event queue and terminate lifecycle processing of the current request. If any of the validate() methods that were invoked, or an event listener that processed a queued event, called renderResponse() on the FacesContext instance for the current request, clear the remaining events from the event queue and transfer control to the Render Response phase of the request processing lifecycle. Otherwise, control must proceed to the Update Model Values phase.

Partial Validations Partial Processing

Call FacesContext.getPartialViewContext(). Call PartialViewContext.processPartial() passing the FacesContext, PhaseID.PROCESS_VALIDATIONS as arguments.

Update Model Values

If this phase of the request processing lifecycle is reached, it is assumed that the incoming request is syntactically and semantically valid (according to the validations that were performed), that the local value of every component in the component tree has been updated, and that it is now appropriate to update the application’s model data in preparation for performing any application events that have been enqueued.

During the Update Model Values phase, the Jakarta Faces implementation must call the processUpdates() method of the UIViewRoot component of the tree. This will normally cause the processUpdates() method of each component in the tree to be called recursively, as described in the API reference for the UIComponent.processUpdates() method. The processUpdates() method must determine if the current request is a “partial request” by calling FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() . If FacesContext.getCurrentInstance().getPartialViewContext().isPartialRequest() returns true, perform the sequence of steps as outlined in Update Model Values Partial Processing. The actual model update for a particular component is done in the updateModel() method for that component.

During the processing of model updates, events may have been queued by the components whose updateModel() method was invoked. As described in Common Event Processing, the processUpdates() method on the UIViewRoot component at the root of the component tree will have caused any queued events to be broadcast to interested listeners.

At the end of this phase, all appropriate model data objects will have had their values updated to match the local value of the corresponding component, and the component local values will have been cleared.

If any of the updateModel() methods that were invoked, or an event listener that processed a queued event, called responseComplete() on the FacesContext instance for the current request, clear the remaining events from the event queue and terminate lifecycle processing of the current request. If any of the updateModel() methods that was invoked, or an event listener that processed a queued event, called renderResponse() on the FacesContext instance for the current request, clear the remaining events from the event queue and transfer control to the Render Response phase of the request processing lifecycle. Otherwise, control must proceed to the Invoke Application phase.

Update Model Values Partial Processing

Call FacesContext.getPartialViewContext(). Call PartialViewContext.processPartial() passing the FacesContext, PhaseID.UPDATE_MODEL_VALUES as arguments.

Invoke Application

If this phase of the request processing lifecycle is reached, it is assumed that all model updates have been completed, and any remaining event broadcast to the application needs to be performed. The implementation must ensure that the processApplication() method of the UIViewRoot instance is called. The default behavior of this method will be to broadcast any queued events that specify a phase identifier of PhaseId.INVOKE_APPLICATION. If responseComplete() was called on the FacesContext instance for the current request, clear the remaining events from the event queue and terminate lifecycle processing of the current request. If renderResponse() was called on the FacesContext instance for the current request, clear the remaining events from the event queue.

Advanced applications (or application frameworks) may replace the default ActionListener instance by calling the setActionListener() method on the Application instance for this application. However, the Jakarta Faces implementation must provide a default ActionListener instance that behaves as described in ActionListener Property.

Render Response

This phase accomplishes two things:

  1. Causes the response to be rendered to the client

  2. Causes the state of the response to be saved for processing on subsequent requests.

Jakarta Faces supports a range of approaches that Jakarta Faces implementations may utilize in creating the response text that corresponds to the contents of the response view, including:

  • Deriving all of the response content directly from the results of the encoding methods (on either the components or the corresponding renderers) that are called.

  • Interleaving the results of component encoding with content that is dynamically generated by application programming logic.

  • Interleaving the results of component encoding with content that is copied from a static “template” resource.

  • Interleaving the results of component encoding by embedding calls to the encoding methods into a dynamic resource.

Because of the number of possible options, the mechanism for implementing the Render Response phase cannot be specified precisely. However, all Jakarta Faces implementations of this phase must conform to the following requirements:

  • If it is possible to obtain a ViewDeclarationLanguage instance for the current viewId, from the ViewHandler, its buildView() method must be called.

  • Publish the jakarta.faces.event.PreRenderViewEvent.

  • Jakarta Faces implementations must provide a default ViewHandler implementation that is capable of handling views written in the Faces View Declaration Language (VDL).

  • If all of the response content is being derived from the encoding methods of the component or associated Renderers, the component tree should be walked in the same depth-first manner as was used in earlier phases to process the component tree, but subject to the additional constraints listed here. Generally this is handled by a call to ViewHandler.renderView().

  • If the response content is being interleaved from additional sources and the encoding methods, the components may be selected for rendering in any desired order.

  • During the rendering process, additional components may be added to the component tree based on information available to the ViewHandler implementation. However, before adding a new component, the ViewHandler implementation must first check for the existence of the corresponding component in the component tree. If the component already exists (perhaps because a previous phase has pre-created one or more components), the existing component’s properties and attributes must be utilized.

  • Under no circumstances should a component be selected for rendering when its parent component, or any of its ancestors in the component tree, has its rendersChildren property set to true. In such cases, the parent or ancestor component must render the content of this child component when the parent or ancestor was selected.

  • If the isRendered() method of a component returns false, the renderer for that component must not generate any markup, and none of its facets or children (if any) should be rendered.

  • It must be possible for the application to programmatically modify the component tree at any time during the request processing lifecycle (except during the rendering of the view) and have the system behave as expected. For example, the following must be permitted. Modification of the view during rendering may lead to undefined results. It must be possible to allow components added by the templating system (such as Facelets) to be removed from the tree before rendering. It must be possible to programmatically add components to the tree and have them render in the proper place in the hierarchy. It must be possible to re-order components in the tree before rendering. These manipulations do require that any components added to the tree have ids that are unique within the scope of the closest parent NamingContainer component. The value of the rendersChildren property is handled as expected, and may be either true or false.

  • If running on a container that supports Jakarta Servlet 4.0 or later, after any dynamic component manipulations have been completed, any resources that have been added to the UIViewRoot, such as scripts, images, or stylesheets, and any inline images, must be pushed to the client using the Jakarta Servlet Server Push API. All of the pushes must be started before any of the HTML of the response is rendered to the client.

  • For partial requests, where partial view rendering is required, there must be no content written outside of the view (outside f:view). Response writing must be disabled. Response writing must be enabled again at the start of encodeBegin.

When each particular component in the component tree is selected for rendering, calls to its encodeXxx() methods must be performed in the manner described in Component Specialization Methods. For components that implement ValueHolder (such as UIInput and UIOutput), data conversion must occur as described in the UIOutput Javadocs.

Upon completion of rendering, but before state saving the Jakarta Faces runtime must publish a jakarta.faces.event.PostRenderViewEvent. After doing so the Jakarta Faces runtime must save the completed state using the methods of the class StateManager. This state information must be made accessible on a subsequent request, so that the Restore View can access it. For more on StateManager, see State Saving Methods.

Render Response Partial Processing

According to UIViewRoot.encodeChildren(), FacesContext.processPartial(PhaseId.RENDER_RESPONSE), will be called if and only if the current request is an Ajax request. Take these actions in this case.

On the ExternalContext for the request, call setResponseContentType("text/xml") and addResponseHeader("Cache-control", "no-cache"). Call startDocument() on the PartialResponseWriter.

Call writePreamble("<?xml version='1.0' encoding='currentEncoding'?>\n”) on the PartialResponseWriter, where encoding is the return from the getCharacterEncoding() on the PartialResponseWriter, or UTF-8 if that method returns null.

If isResetValues() returns true, call getRenderIds() and pass the result to UIViewRoot.resetValues().

If isRenderAll() returns true and the view root is not an instance of NamingContainer, call startUpdate(PartialResponseWriter.RENDER_ALL_MARKER) on the PartialResponseWriter. For each child of the UIViewRoot, call encodeAll(). Call endUpdate() on the PartialResponseWriter. Render the state using the algorithm described below in Partial State Rendering, call endDocument() on the PartialResponseWriter and return. If isRenderAll() returns true and this UIViewRoot is a NamingContainer, treat this as a case where isRenderAll() returned false, but use the UIViewRoot itself as the one and only component from which the tree visit must start.

If isRenderAll() returns false, if there are ids to render, visit the subset of components in the tree to be rendered in similar fashion as for other phases, but for each UIComponent in the traversal, call startUpdate(id) on the PartialResponseWriter, where id is the client id of the component. Call encodeAll() on the component, and then endUpdate() on the PartialResponseWriter. If there are no ids to render, this step is un-necessary. After the subset of components (if any) have been rendered, Render the state using the algorithm described below in Partial State Rendering, call endDocument() on the PartialResponseWriter and return.

Partial State Rendering

This section describes the requirements for rendering the <update> elements pertaining to view state and window id in the case of partial response rendering.

If the view root is marked transient, take no action and return.

Obtain a unique id for the view state, as described in the JavaDocs for the constant field ResponseStateManager.VIEW_STATE_PARAM. Pass this id to a call to startUpdate() on the PartialResponseWriter. Obtain the view state to render by calling getViewState() on the application’s StateManager. Write the state by calling write() on the PartialResponseWriter, passing the state as the argument. Call endUpdate() on the PartialResponseWriter.

If getClientWindow() on the ExternalContext, returns non-null, obtain an id for the <update> element for the window id as described in the JavaDocs for the constant ResponseStateManager.WINDOW_ID_PARAM. Pass this id to a call to startUpdate() on the PartialResponseWriter. Call write() on that same writer, passing the result of calling getId() on the ClientWindow. Call endUpdate() on the PartialResponseWriter.

Common Event Processing

For a complete description of the event processing model for Jakarta Faces components, see Event and Listener Model.

During several phases of the request processing lifecycle, as described in Standard Request Processing Lifecycle Phases, the possibility exists for events to be queued (via a call to the queueEvent() method on the source UIComponent instance, or a call to the queue() method on the FacesEvent instance), which must now be broadcast to interested event listeners. The broadcast is performed as a side effect of calling the appropriate lifecycle management method (processDecodes(), processValidators(), processUpdates(), or processApplication()) on the UIViewRoot instance at the root of the current component tree.

For each queued event, the broadcast() method of the source UIComponent must be called to broadcast the event to all event listeners who have registered an interest, on this source component for events of the specified type, after which the event is removed from the event queue. See the API reference for the UIComponent.broadcast() method for the detailed functional requirements.

It is also possible for event listeners to cause additional events to be enqueued for processing during the current phase of the request processing lifecycle. Such events must be broadcast in the order they were enqueued, after all originally queued events have been broadcast, before the lifecycle management method returns.

Common Application Activities

The following subsections describe common activities that may be undertaken by an application that is using Jakarta Faces to process an incoming request and/or create an outgoing response. Their use is described in Request Processing Lifecycle Scenarios, for each request processing lifecycle scenario in which the activity is relevant.

Acquire Faces Object References

This phase is only required when the request being processed was not submitted from a previous response, and therefore did not initiate the Faces Request Generates Faces Response lifecycle. In order to generate a Faces Response, the application must first acquire references to several objects provided by the Jakarta Faces implementation, as described below.

Acquire and Configure Lifecycle Reference

As described in Lifecycle, the Jakarta Faces implementation must provide an instance of jakarta.faces.lifecycle.Lifecycle that may be utilized to manage the remainder of the request processing lifecycle. An application may acquire a reference to this instance in a portable manner, as follows:

LifecycleFactory lFactory = (LifecycleFactory)
    FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifescycle lifecycle =
    lFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

It is also legal to specify a different lifecycle identifier as a parameter to the getLifecycle() method, as long as this identifier is recognized and supported by the Jakarta Faces implementation you are using. However, using a non-default lifecycle identifier will generally not be portable to any other Jakarta Faces implementation.

Acquire and Configure FacesContext Reference

As described in FacesContext, the Jakarta Faces implementation must provide an instance of jakarta.faces.context.FacesContext to contain all of the per-request state information for a Faces Request or a Faces Response. An application that is processing a Non-Faces Request, but wants to create a Faces Response, must acquire a reference to a FacesContext instance as follows

FacesContextFactory fcFactory = (FacesContextFactory)
    FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
FacesContext facesContext =
    fcFactory.getFacesContext(context, request, response, lifecycle);

where the context, request, and response objects represent the corresponding instances for the application environment. For example, in a Jakarta Servlet-based application, these would be the ServletContext, HttpServletRequest, and HttpServletResponse instances for the current request.

Create And Configure A New View

When a Faces response is being intially created, or when the application decides it wants to create and configure a new view that will ultimately be rendered, it may follow the steps described below in order to set up the view that will be used. You must start with a reference to a FacesContext instance for the current request.

Create A New View

Views are represented by a data structure rooted in an instance of jakarta.faces.component.UIViewRoot, and identified by a view identifier whose meaning depends on the ViewHandler implementation to be used during the Render Response phase of the request processing lifecycle. The ViewHandler provides a factory method that may be utilized to construct new component trees, as follows:

String viewId = ... identifier of the desired Tree ...;
ViewHandler viewHandler = application.getViewHandler();
UIViewRoot view = viewHandler.createView(facesContext, viewId);

The UIViewRoot instance returned by the createView() method must minimally contain a single UIViewRoot provided by the Jakarta Faces implementation, which must encapsulate any implementation-specific component management that is required. Optionally, a Jakarta Faces implementation’s ViewHandler may support the automatic population of the returned UIViewRoot with additional components, perhaps based on some external metadata description.

The caller of ViewHandler.createView() must cause the FacesContext to be populated with the new UIViewRoot. Applications must make sure that it is safe to discard any state saved in the view rooted at the UIViewRoot currently stored in the FacesContext. If Facelets is the page definition language, FacesContext.setViewRoot() must be called before returning from ViewHandler.createView(). Refer to Default ViewHandler Implementation for more ViewHandler details.

Configure the Desired RenderKit

The UIViewRoot instance provided by the ViewHandler, as described in the previous subsection, must automatically be configured to utilize the default jakarta.faces.render.RenderKit implementation provided by the Jakarta Faces implementation, as described in RenderKit. This RenderKit must support the standard components and Renderers described later in this specification, to maximize the portability of your application.

However, a different RenderKit instance provided by your Jakarta Faces implementation (or as an add-on library) may be utilized instead, if desired. A reference to this RenderKit instance can be obtained from the standard RenderKitFactory, and then assigned to the UIViewRoot instance created previously, as follows:

String renderKitId = ... identifier of desired RenderKit ...;
RenderKitFactory rkFactory = (RenderKitFactory)
    FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = rkFactory.getRenderKit(renderKitId, facesContext);
view.setRenderKitId(renderKitId);

As described in Chapter 8, changing the RenderKit being used changes the set of Renderers that will actually perform decoding and encoding activities. Because the components themselves store only a rendererType property (a logical identifier of a particular Renderer), it is thus very easy to switch between RenderKits, as long as they support renderers with the same renderer types.

The default ViewHandler must call calculateRenderKitId() on itself and set the result into the UIViewRoot’s renderKitId property. This allows applications that use alternative RenderKits to dynamically switch on a per-view basis.

Configure The View’s Components

At any time, the application can add new components to the view, remove them, or modify the attributes and properties of existing components. For example, a new FooComponent (an implementation of UIComponent) can be added as a child to the root UIViewRoot in the component tree as follows:

FooComponent component = ... create a FooComponent instance ...;
facesContext.getViewRoot().getChildren().add(component);
Store the new View in the FacesContext

Once the view has been created and configured, the FacesContext instance for this request must be made aware of it by calling setViewRoot().

Concepts that impact several lifecycle phases

This section is intended to give the reader a “big picture” perspective on several complex concepts that impact several request processing lifecycle phases.

Value Handling

At a fundamental level, Jakarta Faces is a way to get values from the user, into your model tier for processing. The process by which values flow from the user to the model has been documented elsewhere in this spec, but a brief holistic survey comes in handy. The following description assumes the Jakarta Servlet/HTTP case, and that all components have Renderers.

Apply Request Values Phase

The user presses a button that causes a form submit to occur. This causes the state of the form to be sent as name=value pairs in the POST data of the HTTP request. The Jakarta Faces request processing lifecycle is entered, and eventually we come to the Apply Request Values Phase. In this phase, the decode() method for each Renderer for each UIComponent in the view is called. The Renderer takes the value from the request and passes it to the setSubmittedValue() method of the component, which is, of course, an instance of EditableValueHolder. If the component has the “immediate” property set to true, we execute validation immediately after decoding. See below for what happens when we execute validation.

Process Validators Phase

processValidators() is called on the root of the view. For each EditableValueHolder in the view, If the “immediate” property is not set, we execute validation for each UIInput in the view. Otherwise, validation has already occurred and this phase is a no-op.

Executing Validation

Please see the javadocs for UIInput.validate() for more details, but basically, this method gets the submitted value from the component (set during Apply Request Values), gets the Renderer for the component and calls its getConvertedValue(), passing the submitted value. If a conversion error occurs, it is dealt with as described in the javadocs for that method. Otherwise, all validators attached to the component are asked to validate the converted value. If any validation errors occur, they are dealt with as described in the javadocs for Validator.validate(). The converted value is pushed into the component’s setValue() method, and a ValueChangeEvent is fired if the value has changed.

Update Model Values Phase

For each UIInput component in the view, its updateModel() method is called. This method only takes action if a local value was set when validation executed and if the page author configured this component to push its value to the model tier. This phase simply causes the converted local value of the UIInput component to be pushed to the model in the way specified by the page author. Any errors that occur as a result of the attempt to push the value to the model tier are dealt with as described in the javadocs for UIInput.updateModel().

Localization and Internationalization (L10N/I18N)

Jakarta Faces is fully internationalized. The I18N capability in Jakarta Faces builds on the I18N concepts offered in the Jakarta Servlet and Jakarta Tags specifications. I18N happens at several points in the request processing lifecycle, but it is easiest to explain what goes on by breaking the task down by function.

Determining the active Locale

Jakarta Faces has the concept of an active Locale which is used to look up all localized resources. Converters must use this Locale when performing their conversion. This Locale is stored as the value of the locale JavaBeans property on the UIViewRoot of the current FacesContext. The application developer can tell Jakarta Faces what locales the application supports in the applications’ WEB-INF/faces-config.xml file. For example:

<faces-config>
  <application>
    <locale-config>
      <default-locale>en</default-locale>
      <supported-locale>de</supported-locale>
      <supported-locale>fr</supported-locale>
      <supported-locale>es</supported-locale>
    </locale-config>
  </application>

This application’s default locale is en, but it also supports de, fr, and es locales. These elements cause the Application instance to be populated with Locale data. Please see the javadocs for details.

The UIViewRoot’s Locale is determined and set by the ViewHandler during the execution of the ViewHandler ’s createView() method. This method must cause the active Locale to be determined by looking at the user’s preferences combined with the application’s stated supported locales. Please see the javadocs for details.

The application can call UIViewRoot.setLocale() directly, but it is also possible for the page author to override the UIViewRoot’s locale by using the locale attribute on the <f:view> tag. The value of this attribute must be specified as language[{-|_}country[{-|_}variant]] without the colons, for example "ja_JP_SJIS". The separators between the segments must be ’ - ’ or ’ _ ’.

To facilitate BCP 47 support, the Locale parsing mentioned above is done only if the JDK Locale.languageForTag method does not return a Locale with a language in it. The additional format of the Locale string is as specified by that method.

Determining the Character Encoding

The request and response character encoding are set and interpreted as follows.

On an initial request to a Faces webapp, the request character encoding is left unmodified, relying on the underlying request object (e.g., the Jakarta Servlet or Portlet request) to parse request parameter correctly.

At the beginning of the render-response phase, the ViewHandler must ensure that the response Locale is set to be that of the UIViewRoot, for example by calling ServletResponse.setLocale() when running in the Jakarta Servlet environment. Setting the response Locale may affect the response character encoding, see the Jakarta Servlet and Portlet specifications for details.

At the end of the render-response phase, the ViewHandler must store the response character encoding used by the underlying response object (e.g., the Jakarta Servlet or Portlet response) in the session (if and only if a session already exists) under a well known, implementation-dependent key.

On a subsequent postback, before any of the ExternalContext methods for accessing request parameters are invoked, the ViewHandler must examine the Content-Type header to read the charset attribute and use its value to set it as the request encoding for the underlying request object. If the Content-Type header doesn’t contain a charset attribute, the encoding previously stored in the session (if and only if a session already exists), must be used to set the encoding for the underlying request object. If no character encoding is found, the request encoding must be left unmodified.

The above algorithm allows an application to use the mechanisms of the underlying technologies to adjust both the request and response encoding in an application-specific manner. Note, though, that the character encoding rules prior to Jakarta Servlet 2.4 are imprecise and special care must be taken for portability between containers.

Localized Text

Since most Jakarta Faces components allow pulling their display value from the model tier, it is easy to do the localization at the model tier level. As a convenience, Jakarta Faces provides the <f:loadBundle> tag, which takes a ResourceBundle and loads it into a Map, which is then stored in the scoped namespace in request scope, thus making its messages available using the same mechanism for accessing data in the model tier. For example:

<f:loadBundle basename="com.foo.industryMessages.chemical"
              var="messages" />
<h:outputText value="#{messages.benzene}" />

This must cause the ResourceBundle named com.foo.industryMessages.chemical to be loaded as a Map into the request scope under the key messages. Localized content can then be pulled out of it using the normal value expression syntax.

Localized Application Messages

This section describes how Jakarta Faces handles localized error and informational messages that occur as a result of conversion, validation, or other application actions during the request processing lifecycle. The Jakarta Faces class jakarta.faces.application.FacesMessage is provided to encapsulate summary, detail, and severity information for a message. A Jakarta Faces implementation must provide a jakarta.faces.Messages ResourceBundle containing all of the necessary keys for the standard messages. The required keys (and a non-normative indication of the intended message text) are as follows:

  • jakarta.faces.component.UIInput.CONVERSION={0}: Conversion error occurred

  • jakarta.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required

  • jakarta.faces.component.UIInput.UPDATE= {0}: An error occurred when processing your submitted information

  • jakarta.faces.component.UISelectOne.INVALID={0}: Validation Error: Value is not valid

  • jakarta.faces.component.UISelectMany.INVALID={0}: Validation Error: Value is not valid

  • jakarta.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed decimal number.

  • jakarta.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a signed decimal number consisting of zero or more digits, that may be followed by a decimal point and fraction. Example: {1}

  • jakarta.faces.converter.BigIntegerConverter.BIGINTEGER={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.BigIntegerConverter.BIGINTEGER_detail={2}: ''{0}'' must be a number consisting of one or more digits. Example: {1}

  • jakarta.faces.converter.BooleanConverter.BOOLEAN={1}: ''{0}'' must be 'true' or 'false'.

  • jakarta.faces.converter.BooleanConverter.BOOLEAN_detail={1}: ''{0}'' must be 'true' or 'false'. Any value other than 'true' will evaluate to 'false'.

  • jakarta.faces.converter.ByteConverter.BYTE={2}: ''{0}'' must be a number between -128 and 127.

  • jakarta.faces.converter.ByteConverter.BYTE_detail={2}: ''{0}'' must be a number between -128 and 127. Example: {1}

  • jakarta.faces.converter.CharacterConverter.CHARACTER={1}: ''{0}'' must be a valid character.

  • jakarta.faces.converter.CharacterConverter.CHARACTER_detail={1}: ''{0}'' must be a valid ASCII character.

  • jakarta.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.

  • jakarta.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}

  • jakarta.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' could not be understood as a time.

  • jakarta.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' could not be understood as a time. Example: {1}

  • jakarta.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' could not be understood as a date and time.

  • jakarta.faces.converter.DateTimeConverter.DATETIME_detail={2}: '{0}'' could not be understood as a date and time. Example: {1}

  • jakarta.faces.converter.DateTimeConverter.PATTERN_TYPE={1}: A 'pattern' or 'type' attribute must be specified to convert the value ''{0}''.

  • jakarta.faces.converter.DoubleConverter.DOUBLE={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.DoubleConverter.DOUBLE_detail={2}: ''{0}'' must be a number between 4.9E-324 and 1.7976931348623157E308 Example: {1}

  • jakarta.faces.converter.EnumConverter.ENUM={2}: ''{0}'' must be convertible to an enum.

  • jakarta.faces.converter.EnumConverter.ENUM_detail={2}: ''{0}'' must be convertible to an enum from the enum that contains the constant ''{1}''.

  • jakarta.faces.converter.EnumConverter.ENUM_NO_CLASS={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.

  • jakarta.faces.converter.EnumConverter.ENUM_NO_CLASS_detail={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.

  • jakarta.faces.converter.FloatConverter.FLOAT={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.FloatConverter.FLOAT_detail={2}: ''{0}'' must be a number between 1.4E-45 and 3.4028235E38 Example: {1}

  • jakarta.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between -2147483648 and 2147483647 Example: {1}

  • jakarta.faces.converter.LongConverter.LONG={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.LongConverter.LONG_detail={2}: ''{0}'' must be a number between -9223372036854775808 to 9223372036854775807 Example: {1}

  • jakarta.faces.converter.NumberConverter.CURRENCY={2}: ''{0}'' could not be understood as a currency value.

  • jakarta.faces.converter.NumberConverter.CURRENCY_detail={2}: ''{0}'' could not be understood as a currency value. Example: {1}

  • jakarta.faces.converter.NumberConverter.PERCENT={2}: ''{0}'' could not be understood as a percentage.

  • jakarta.faces.converter.NumberConverter.PERCENT_detail={2}: ''{0}'' could not be understood as a percentage. Example: {1}

  • jakarta.faces.converter.NumberConverter.NUMBER={2}: ''{0}'' is not a number.

  • jakarta.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number. Example: {1}

  • jakarta.faces.converter.NumberConverter.PATTERN={2}: ''{0}'' is not a number pattern.

  • jakarta.faces.converter.NumberConverter.PATTERN_detail={2}: ''{0}'' is not a number pattern. Example: {1}

  • jakarta.faces.converter.ShortConverter.SHORT={2}: ''{0}'' must be a number consisting of one or more digits.

  • jakarta.faces.converter.ShortConverter.SHORT_detail={2}: ''{0}'' must be a number between -32768 and 32767 Example: {1}

  • jakarta.faces.converter.STRING={1}: Could not convert ''{0}'' to a string.

  • jakarta.faces.validator.BeanValidator.MESSAGE={0}

  • jakarta.faces.validator.DoubleRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of ''{0}''

  • jakarta.faces.validator.DoubleRangeValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of ''{0}''

  • jakarta.faces.validator.DoubleRangeValidator.NOT_IN_RANGE={2}: Validation Error: Specified attribute is not between the expected values of {0} and {1}.

  • jakarta.faces.validator.DoubleRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type

  • jakarta.faces.validator.LengthValidator.MAXIMUM={1}: Validation Error: Length is greater than allowable maximum of ''{0}''

  • jakarta.faces.validator.LengthValidator.MINIMUM={1}: Validation Error: Length is less than allowable minimum of ''{0}''

  • jakarta.faces.validator.LongRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of ''{0}''

  • jakarta.faces.validator.LongRangeValidator.MINIMUM={1}: Validation Error Value is less than allowable minimum of ''{0}''

  • jakarta.faces.validator.LongRangeValidator.NOT_IN_RANGE={2}: Validation Error: Specified attribute is not between the expected values of {0} and {1}.

  • jakarta.faces.validator.LongRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type

A Jakarta Faces application may provide its own messages, or overrides to the standard messages by supplying a <message-bundle> element to in the application configuration resources. Since the ResourceBundle provided in the Java platform has no notion of summary or detail, Jakarta Faces adopts the policy that ResourceBundle key for the message looks up the message summary. The detail is stored under the same key as the summary, with detail appended. These ResourceBundle keys must be used to look up the necessary values to create a localized FacesMessage instance. Note that the value of the summary and detail keys in the ResourceBundle may contain parameter substitution tokens, which must be substituted with the appropriate values using java.text.MessageFormat. Replace the last parameter substitution token shown in the messages above with the input component’s label attribute. For example, {1} for “DoubleRangeValidator.MAXIMUM”, {2} for “ShortConverter.SHORT”. The label attribute is a generic attribute. Please see Generic Attributes and Standard HTML RenderKit Implementation for more information on these attributes. If the input component’s label attribute is not specified, use the component’s client identifier.

These messages can be displayed in the page using the UIMessage and UIMessages components and their corresponding tags, <h:message> and <h:messages>.

The following algorithm must be used to create a FacesMessage instance given a message key.

  • Call getMessageBundle() on the Application instance for this web application, to determine if the application has defined a resource bundle name. If so, load that ResourceBundle and look for the message there.

  • If not there, look in the jakarta.faces.Messages resource bundle.

  • In either case, if a message is found, use the above conventions to create a FacesMessage instance.

State Management

Jakarta Faces introduces a powerful and flexible system for saving and restoring the state of the view between requests to the server. It is useful to describe state management from several viewpoints. For the page author, state management happens transparently. For the app assembler, state management can be configured to save the state in the client or on the server by setting the ServletContext InitParameter named jakarta.faces.STATE_SAVING_METHOD to either client or server. The value of this parameter directs the state management decisions made by the implementation.

State Management Considerations for the Custom Component Author

Since the component developer cannot know what the state saving method will be at runtime, they must be aware of state management. As shown in The jakarta.faces.component package, all Jakarta Faces components implement the StateHolder interface. As a consequence the standard components provide implementations of PartialStateHolder to suit their needs. A custom component that extends UIComponent directly, and does not extend any of the standard components, must implement PartialStateHolder (or its older super-interface, StateHolder), manually. The helper class StateHelper exists to simplify this process for the custom component author. Please see PartialStateHolder or StateHolder for details.

A custom component that does extend from one of the standard components and maintains its own state, in addition to the state maintained by the superclass must take special care to implement StateHolder or PartialStateHolder correctly. Notably, calls to saveState() must not alter the state in any way. The subclass is responsible for saving and restoring the state of the superclass. Consider this example. My custom component represents a “slider” ui widget. As such, it needs to keep track of the maximum value, minimum value, and current values as part of its state.

public class Slider extends UISelectOne {
  protected Integer min = null;
  protected Integer max = null;
  protected Integer cur = null;

  // ... details omitted
  public Object saveState(FacesContext context) {
    Object values[] = new Object[4];
    values[0] = super.saveState(context);
    values[1] = min;
    values[2] = max;
    values[3] = cur;
  }

  public void restoreState(FacesContext context, Object state) {
    Object values[] = (Object {}) state; // guaranteed to succeed
    super.restoreState(context, values[0]);
    min = (Integer) values[1];
    max = (Integer) values[2];
    cur = (Integer) values[3];
  }

Note that we call super.saveState() and super.restoreState() as appropriate. This is absolutely vital! Failing to do this will prevent the component from working.

State Management Considerations for the Jakarta Faces Implementor

The intent of the state management facility is to make life easier for the page author, app assembler, and component author. However, the complexity has to live somewhere, and the Jakarta Faces implementor is the lucky role. Here is an overview of the key players. Please see the javadocs for each individual class for more information.

Key Players in State Management
  • StateHelper the helper class that defines a Map -like contract that makes it easier for components to implement PartialStateHolder.

  • ViewHandler the entry point to the state management system. Uses a helper class, StateManager, to do the actual work.

  • StateManager abstraction for the hard work of state saving. Uses a helper class, ResponseStateManager, for the rendering technology specific decisions.

  • ResponseStateManager abstraction for rendering technology specific state management decisions.

  • UIComponent directs process of saving and restoring individual component state.

Resource Handling

This section only applies to pages written using Facelets. Resource Handling is the starting point for the normative specification for Resource Handling. This section gives a non-normative overview of the feature. The following steps walk through the points in the lifecycle where this feature is encountered. Consider a Faces web application that contains resources that have been packaged into the application as specified in Packaging Resources. Assume each page in the application includes references to resources, specifically scripts and stylesheets. The first diagram in this chapter is helpful in understanding this example.

Consider an initial request to the application.

  • The ViewHandler calls ViewDeclarationLanguage.buildView(). This ultimately causes the processEvent() method for the jakarta.faces.resource.Script and jakarta.faces.resource.Stylesheet renderers (which implement ComponentSystemEventListener) to be called after each component that declares them as their renderer is added to the view. This method is specified to take actions that cause the resource to be rendered at the correct part in the page based on user-specified or application invariant rules. Here’s how it works.

  • Every UIComponent instance in a view is created with a call to some variant of Application.createComponent(). The specification for this method now includes some annotation processing requirements. If the component or its renderer has an @ListenerFor or @ListenersFor annotation, and the Script and Stylesheet renderers must, the component or its renderer are added as a component scoped listener for the appropriate event. In the case of Script and Stylesheet renderers, they must listen for the PostAddToViewEvent.

  • When the processEvent() method is called on a Script or Stylesheet renderer, the renderer takes the specified action to move the component to the proper point in the tree based on what kind of resource it is, and on what hints the page author has declared on the component in the view.

  • The ViewHandler calls ViewDeclarationLanguage.renderView(). The view is traversed as normal and because the components with Script and Stylesheet renderers have already been reparented to the proper place in the view, the normal renderering causes the resource to be encoded as described in Rendering Resources.

The browser then parses the completely rendered page and proceeds to issue subsequent requests for the resources included in the page.

Now consider a request from the browser for one of those resources included in the page.

  • The request comes back to the Faces server. The FacesServlet is specified to call ResourceHandler.isResourceRequest() as shown in the diagram in Faces Request Generates Faces Response. In this case, the method returns true. The FacesServlet is specified to call ResourceHandler.handleResourceRequest() to serve up the bytes of the resource.

View Parameters

This section only applies to pages written using Facelets. The normative specification for this feature is spread out across several places, including the View Declaration Language Documentation for the <f:metadata> element, the javadocs for the UIViewParameter, ViewHandler, and ViewDeclarationLanguage classes, and the spec language requirements for the default NavigationHandler and the Request Processing Lifecycle. This leads to a very diffuse field of specification requirements. To aid in understanding the feature, this section provides a non-normative overview of the feature. The following steps walk through the points in the lifecycle where this feature is encountered. Consider a web application that uses this feature exclusively on every page. Therefore every page has the following features in common.

  • Every page has an <f:metadata> tag, with at least one <f:viewParameter> element within it.

  • Every page has at least one <h:link> or < h:button> with the appropriate parameters nested within it.

  • No other kind of navigation components are used in the application.

Consider an initial request to the application.

  • As specified in section Restore View, the restore view phase of the request processing lifecycle detects that this is an initial request and tries to obtain the ViewDeclarationLanguage instance from the ViewHandler for this viewId. Because every page in the app is written in Facelets, there is a ViewDeclarationLanguage instance. Restore view phase calls ViewDeclarationLanguage.getViewMetadata(). Because every view in this particular app does have <f:metadata> on every page, this method returns a ViewMetadata instance. Restore view phase calls MetaData.createMetadataView(). This method creates a UIViewRoot containing only children declared in the <f:metadata> element. Restore view phase calls ViewMetadata.getViewParameters(). Because every <f:metadata> in the app has at least one <f:viewParameter> element within it, this method returns a non empty Collection<UIViewParameter>. Restore view phase uses this fact to decide that the lifecycle must not skip straight to render response, as is the normal action taken on initial requests.

  • The remaining phases of the request processing lifecycle execute: apply request values, process validations, update model values, invoke application, and finally render response. Because the view only contains UIViewParameter children, only these children are traversed during the lifecycle, but because this is an initial request, with no query parameters, none of these compnents take any action during the lifecycle.

  • Because the pages exclusively use <h:link> and <h:button> for their navigation, the renderers for these components are called during the rendering of the page. As specified in the renderkit docs for the renderers for those components, markup is rendered that causes the browser to issue a GET request with query parameters.

Consider when the user clicks on a link in the application. The browser issues a GET request with query parameters

  • Restore view phase takes the same action as in the previously explained request. Because this is a GET request, no state is restored from the previous request.

  • Because this is a request with query parameters, the UIViewParameter children do take action when they are traversed during the normal lifecycle, reading values during the apply request values phase, doing conversion and processing validators attached to the <f:viewParam> elements, if any, and updating models during the update model values phase. Because there are only <h:link> and <h:button> navigation elements in the page, no action action will happen during the invoke application phase. The response is re-rendered as normal. In such an application, the only navigation to a new page happens by virtue of the browser issuing a GET request to a different viewId.

Bookmarkability

Jakarta Faces has a bookmarking capability with the use of two Standard HTML RenderKit additions.

Provided is a component (UIOutcomeTarget) that provides properties that are used to produce a hyperlink at render time. The component can appear in the form of a button or a link. This feature introduces a concept known as “preemptive navigation”, which means the target URL is determined at Render Response time - before the user has activated the component. This feature allows the user to leverage the navigation model while also providing the ability to generate bookmarkable non-faces requests.

Jakarta Bean Validation

Jakarta Faces supports Jakarta Bean Validation. A Jakarta Faces implementation must support Jakarta Bean Validation if the environment in which the Jakarta Faces runtime is included requires Jakarta Bean Validation. Currently the only such environment is when Jakarta Faces is included in a Jakarta EE runtime.

A detailed description of the usage of Jakarta Bean Validation with Jakarta Faces is beyond the scope of this section, but this section will provide a brief overview of the feature, touching on the points of interest to a spec implementor. Consider a simple web application that has one page, written in Facelets, that has several text fields inside of a form. This application is running in a Jakarta Faces runtime in an environment that does require Jakarta Bean Validation, and therefore this feature is available. Assume that every text field is bound to a managed bean property that has at least one Jakarta Bean Validation constraint annotation attached to it.

During the render response phase that always precedes a postback, due to the specification requirements in Validation Registration, every UIInput in this application has an instance of Validator with id jakarta.faces.Bean attached to it.

During the process validations phase, due to the specification for the validate() method of this Validator, Bean Validation is invoked automatically, for the user specified validation constraints, whenever such components are normally validated. The jakarta.faces.Bean standard validator also ensures that every ConstraintViolation that resulted in attempting to validate the model data is wrapped in a FacesMessage and added to the FacesContext as normal with every other kind of validator.

Ajax

Jakarta Faces supports Ajax. The specification contains a JavaScript library for performing basic Ajax operations. The library helps define a standard way of sending an Ajax request, and processing an Ajax response, since these are problem areas for component compatibility. The specification provides two ways of adding Ajax to Jakarta Faces web applications. Page authors may use the JavaScript library directly in their pages by attaching the Ajax request call to a Jakarta Faces component via a JavaScript event (such as onclick). They may also take a more declarative approach and use a core Facelets tag (<f:ajax/>) that they can nest within Jakarta Faces components to “Ajaxify” them. It is also possible to “Ajaxify” regions of a page by “wrapping” the tag around component groups.

The server side aspects of Jakarta Faces Ajax frameworks work with the standard Jakarta Faces lifecycle. In addition to providing a standard page authoring experience, the specification also standardizes the server side processing of Ajax requests. Selected components in a Jakarta Faces view can be processed (known as partial processing) and selected components can be rendered to the client (known as partial rendering).

Component Behaviors

The Jakarta Faces specification contains a type of attached object known as component behaviors. Component behaviors play a similar role to converters and validators in that they are attached to a component instance in order to enhance the component with additional functionality not defined by the component itself. While converters and validators are currently limited to the server-side request processing lifecycle, component behaviors have impact that extends to the client, within the scope of a particular instance component in a view. In particular, the ClientBehavior interface defines a contract for behaviors that can enhance a component’s rendered content with behavior-defined "scripts". These scripts are executed on the client in response to end user interaction, but can also trigger postbacks back into the Jakarta Faces request processing lifecycle.

The usage pattern for client behaviors is as follows:

  • The page author attaches a client behavior to a component, typically by specifying a behavior tag as a child of a component tag.

  • When attaching a client behavior to a component, the page author identifies the name of a client "event" to attach to. The set of valid events are defined by the component.

  • At render time, the component (or renderer) retrieves the client behavior and asks it for its script.

  • The component (or renderer) renders this script at the appropriate location in its generated content (eg. typically in a DOM event handler).

  • When the end user interacts with the component’s content in the browser, the behavior-defined script is executed in response to the page author-specified event.

  • The script provides some client-side interaction, for example, hiding or showing content or validating input on the client, and possibly posts back to the server.

The first client behavior provided by the Jakarta Faces specification is the AjaxBehavior. This behavior is exposed to a page author as a Facelets <f:ajax> tag, which can be embedded within any of the standard HTML components as follows:

<h:commandButton>
  <f:ajax event="mouseover" />
</h:commandButton>

When activated in response to end user activity, the <f:ajax> client behavior generates an Ajax request back into the Jakarta Faces request processing lifecycle.

The component behavior framework is extensible and allows developers to define custom behaviors and also allows component authors to enhance custom components to work with behaviors.

System Events

System Events are normatively specified in System Events. This section provides an overview of this feature as it relates to the lifecycle.

System events expand on the idea of lifecycle PhaseEvents. With PhaseEvents, it is possible to have application scoped PhaseListeners that are given the opportunity to act on the system before and after each phase in the lifecycle. System events provide a much more fine grained insight into the system, allowing application or component scoped listeners to be notified of a variety of kinds of events. The set of events supported in the core specification is given in Event Classes. To accomodate extensibility, users may define their own kinds of events.

The system event feature is a simple publish/subscribe event model. There is no event queue, events are published immediately, and always with a call to Application.publishEvent(). There are several ways to declare interest in a particular kind of event.

  • Call Application.subscribeToEvent() to add an application scoped listener.

  • Call UIComponent.subscribeToEvent() to add a component scoped listener.

  • Use the <f:event> tag to declare a component scoped listener.

  • Use the @ListenerFor or @ListenersFor annotation. The scope of the listener is determined by the code that processes the annotation.

  • Use the <system-event-listener> element in an application configuration resource to add an application scoped listener.

This feature is conceptually related to the lifecycle because there are calls to Application.publishEvent() sprinkled throughout the code that gets executed when the lifecycle runs.

Resource Handling

As shown in the diagram in Faces Request Generates Faces Response, the Jakarta Faces run-time must determine if the current Faces Request is a Faces Resource Request or a View Request. This must be accomplished by calling Application.getResourceHandler().isResourceRequest(). Most of the normative specification for resource handling is contained in the Javadocs for ResourceHandler and its related classes. This section contains the specification for resource handling that fits best in prose, rather than in Javadocs.

Packaging Resources

ResourceHandler defines a path based packaging convention for resources. The default implementation of ResourceHandler must support packaging resources in the web application root or in the classpath, according to the following specification.Other implementations of ResourceHandler are free to package resources however they like.

Packaging Resources into the Web Application Root

The default implementation must support packaging resources in the web application root under the path

resources/<resourceIdentifier>

relative to the web app root. Resources packaged into the web app root must be accessed using the getResource*() methods on ExternalContext.

Packaging Resources into the Classpath

For the default implementation, resources packaged in the classpath must reside under the JAR entry name:

META-INF/resources/<resourceIdentifier>

Resources packaged into the classpath must be accessed using the getResource*() methods of the ClassLoader obtained by calling the getContextClassLoader() method of the curreth Thread.

Resource Identifiers

<resourceIdentifier> consists of several segments, specified as follows.

[localePrefix/][libraryName/][libraryVersion/]resourceName[/resourceVersion]

The run-time must enforce the following rules to consider a <resourceIdentifier> valid. A <resourceIdentifier> that does not follow these rules must not be considered valid and must be ignored silently.

  • The set of characters that are valid for use in the localePrefix, libraryName, libraryVerison, resourceName and resourceVersion segments of the resource identifier is specififed as XML NameChar excluding the path separator and ‘:’ characters. The specification for XML NameChar may be seen at https://www.w3.org/TR/REC-xml/#NT-NameChar.

  • A further restriction applies to libraryName. A libraryName must not be an underscore separated sequence of non-negative integers or a locale string. More rigorously, a libraryName must not match either of the following regular expressions:

    [0-9]+(_[0-9]+)*
    [A-Za-z]{2}(_[A-Za-z]{2}(_[A-Za-z]+)*)?
  • Segments in square brackets [] are optional.

  • The segments must appear in the order shown above.

  • If libraryVersion is present, it must be preceded by libraryName.

  • If libraryVersion is present, any leaf files under libraryName must be ignored.

  • If resourceVersion is present, it must be preceded by resourceName.

  • There must be a ’ / ’ between adjacent segments in a <resourceIdentifier>

  • If libraryVersion or resourceVersion are present, both must be a ’ _ ’ separated list of integers, neither starting nor ending with ’ _ ’

If resourceVersion is present, it must be a version number in the same format as libraryVersion. An optional “file extension” may be used with the resourceVersion. If “file extension” is used, a “.” character, followed by a “file extension” must be appended to the version number. See the following table for an example.

The following examples illustrate the nine valid combinations of the above resource identifier segments.

localePrefx

libraryName

library

Version [optional]

resourceName

resource

Version [optional]

Description

actual resourceIdentifier

__

__  

__

duke.gif

__

A non-localized, non-versioned image resource called "duke.gif", not in a library

duke.gif

__

corporate

__

duke.gif

__

A non-localized, non-versioned image resource called "duke.gif" in a library called "corporate"

corporate/duke.gif

__

corporate

2_3

duke.gif

__

A non-localized, non-versioned image resource called "duke.gif", in version 2_3 of the "corporate" library

corporate/2_3/duke.gif

__

basic

2_3

script.js

1_3_4.js

A non-localized, version 1.3.4 script resource called "script.js", in versioned 2_3

library called "basic".

basic/2_3/script.js/1_3_4.js

de

__

__

header.css

__

A non-versioned style resource called "header.css" localized for locale "de"

de/header.css

de_AT

__

__

footer.css

1_4_2.css

Version 1_4_2 of style resource "footer.css", localized for locale "de_AT"

de_AT/footer.css/1_4_2.css

zh

extraFancy

__

menu-bar.css

2_4.css

Version 2_4 of style resource called, "menu-bar.css" in non-versioned library, "extraFancy", localized for locale "zh"

zh/extraFancy/menu-bar.css/2_4.css

ja

mild

0_1

ajaxTransaction.js

__

Non-versioned script resource called, "ajaxTransaction.js", in version 0_1 of library called "mild", localized for locale "ja"

ja/mild/0_1/ajaxTransaction.js

de_ch

grassy

1_0

bg.png

1_0.png

Version 1_0 of image resource called "bg.png", in version 1_0 of library called "grassy" localized for locale "de_ch"

de_ch/grassy/1_0/bg.png/1_0.png

Libraries of Localized and Versioned Resources

An important feature of the resource handler is the ability for resources to be localized, versioned, and collected into libraries. The localization and versioning scheme is completely hidden behind the API of ResourceHandler and Resource and is not exposed in any way to the Jakarta Faces run-time.

The default implementation of ResourceHandler.createResource(), for all variants of that method, must implement the following to discover which actual resource will be encapsulated within the returned Resource instance. An implementation may perform caching of the resource metadata to improve performance if the ProjectStage is ProjectStage.Production.

Using the resourceName and libraryName arguments to createResource(), and the resource packaging scheme specified in Packaging Resources into the Web Application Root, Packaging Resources into the Classpath, and Resource Identifiers, discover the file or entry that contains the bytes of the resource. If there are multiple versions of the same library, and libraryVersion is not specified, the library with the highest version is chosen. If there are multiple versions of the same resource, and resourceVersion is not specified, the resource with the highest version is chosen. The algorithm is specified in pseudocode.

function createResource(resourceName, libraryName) {
    var resource = null;
    var resourceId = null;
    for (var contract : getLibraryContracts()) {
        resourceId = deriveResourceIdConsideringResourceLoaders(contract,
                resourceName, libraryName)
        if (null != resourceId) {
            resource = create the resource using the resourceId;
            return resource;
        }
    }

    // try without a contract
    resourceId = deriveResourceIdConsideringResourceLoaders(null,
            resourceName, libraryName)
    if (null != resourceId) {
        resource = create the resource using the resourceId;
    }
    return resource;
}

function deriveResourceIdConsideringResourceLoaders(contract,
        resourceName, libraryName) {
    var prefix = web app root resource prefix;
    var resourceLoader = web app resource loader;
    // these are shorthand for the prefix and resource loading
    // facility specified in Section 2.6.1.1. They are
    // not actual API per se.
    var resourceId = deriveResourceIdConsideringLocalePrefix(contract,
            prefix, resourceLoader, resourceName, libraryName);

    if (null == resourceId) {
        prefix = classpath resource prefix;
        resourceLoader = classpath resource loader;
        // these are shorthand for the prefix and resource
        // loading facility specified in Section 2.6.1.2. They are
        // not actual API per se.
        resourceId = deriveResourceIdConsideringLocalePrefix(contract,
                prefix, resourceLoader, resourceName, libraryName);
    }
    return resourceId;
}

function deriveResourceIdConsideringLocalePrefix(contract, prefix,
        resourceLoader, resourceName, libraryName) {
    var localePrefix = getLocalePrefix();
    var result = deriveResourceId(contract, prefix, resourceLoader,
            resourceName, libraryName, localePrefix);
    // If the application has been configured to have a localePrefix,
    // and the resource is not found, try to find it again,
    // without the localePrefix.
    if (null == result && null != localePrefix) {
        result = deriveResourceId(contract, prefix, resourceLoader,
                resourceName, libraryName, null);
    }
    return result;
}

function deriveResourceId(contract, prefix, resourceLoader,
        resourceName, libraryName, localePrefix) {
    var resourceVersion = null;
    var libraryVersion = null;
    var resourceId;
    if (null != localePrefix) {
        prefix = localePrefix + '/' + prefix;
    }
    if (null != contract) {
        prefix = contract + '/' + prefix;
    }

    if (null != libraryName) {
        // actual argument is
        // resourcesInContractInJar/resources/resourcesInContractInJar
        var libraryPaths = resourceLoader.getResourcePaths(
                prefix + '/' + libraryName);

        if (null != libraryPaths && !libraryPaths.isEmpty()) {
            libraryVersion = // execute the comment
            // Look in the libraryPaths for versioned libraries.
            // If one or more versioned libraries are found, take
            // the one with the highest version number as the value
            // of libraryVersion. If no versioned libraries
            // are found, let libraryVersion remain null.
        }
        if (null != libraryVersion) {
            libraryName = libraryName + '/' + libraryVersion;
        }
        var resourcePaths = resourceLoader.getResourcePaths(
            prefix + '/' + libraryName + '/' + resourceName);
        if (null != resourcePaths && !resourcePaths.isEmpty()) {
            resourceVersion = // execute the comment +
            // Look in the resourcePaths for versioned resources.
            // If one or more versioned resources are found, take
            // the one with the "highest" version number as the value
            // of resourceVersion. If no versioned libraries
            // are found, let resourceVersion remain null.
        }
        if (null != resourceVersion) {
            resourceId = prefix + '/' + libraryName + '/' +
                    resourceName + '/' + resourceVersion;
        }
        else {
            resourceId = prefix + '/' + libraryName + '/' + resourceName;
        }
    } // end of if (null != libraryName)
    else {
        // libraryName == null
        var resourcePaths = resourceLoader.getResourcePaths(
                prefix + '/' + resourceName);
        if (null != resourcePaths && !resourcePaths.isEmpty()) {
            resourceVersion = // execute the comment
            // Look in the resourcePaths for versioned resources.
            // If one or more versioned resources are found, take
            // the one with the "highest" version number as the value
            // of resourceVersion. If no versioned libraries
            // are found, let resourceVersion remain null.
        }
        if (null != resourceVersion) {
            resourceId = prefix + '/' + resourceName + '/' +
                    resourceVersion;
        } else {
            resourceId = prefix + '/' + resourceName;
        }
    } // end of else, when libraryName == null
    return resourceId;
}

function getLocalePrefix() {
    var localePrefix;
    var appBundleName = facesContext.application.messageBundle;
    if (null != appBundleName) {
        var locale =
            // If there is a viewRoot on the current facesContext,
            // use its locale.
            // Otherwise, use the locale of the application's ViewHandler
        ResourceBundle appBundle = ResourceBundle.getBundle(
                appBundleName, locale);
        localePrefix = appBundle.getString(ResourceHandler. LOCALE_PREFIX);
    }
    // Any MissingResourceException instances that are encountered
    // in the above code must be swallowed by this method, and null
    // returned;
    return localePrefix;
}

Rendering Resources

Resources such as images, stylesheets and scripts use the resource handling mechanism as outlined in Packaging Resources. So, for example:

<h:graphicImage library="common" name="images/planets.png" />
<h:graphicImage value="#{resource['common:images/planets.png']}" />

These entries render exactly the same markup. In addition to using the name and library attributes, stylesheet and script resources can be “relocated” to other parts of the view. For example, we could specify that a script resource be rendered within an HTML “head”, “body” or “form” element in the page.

Relocatable Resources

Relocatable resources are resources that can be told where to render themselves, and this rendered location may be different than the resource tag placement in the view. For example, a portion of the view may be described in the view declaration language as follows:

<!DOCTYPE html>
<html xmlns:h="jakarta.faces.html">
  <h:head>
    <title>Example View</title>
  </h:head>
  <h:body>
    <h:form>
      <h:outputScript library="jakarta.faces" name="faces.js" target="head" />
    </h:form>
  </h:body>
</html>

The example tag <h:outputScript> which extends from UIOutput refers to the example renderer, ScriptRenderer, implementing ComponentSystemEventListener, that listens for PostAddToViewEvent event types:

@ListenerFor(facesEventClass=PostAddToViewEvent.class,
        sourceClass=UIOutput.class)
public class ScriptRenderer extends Renderer
        implements ComponentSystemEventListener {...

Refer to Event and Listener Model. When the component for this resource is added to the view, the ScriptRenderer.processEvent() method adds the component to a facet (named by the target attribute) under the view root. using the UIViewRoot component resource methods as described in Methods.

The <h:head> and <h:body> tags refer to the renderers HeadRenderer and BodyRenderer respectively. They are described in the Standard HTML Renderkit documentation referred to in Standard HTML RenderKit Implementation. During the rendering phase, the encode methods for these renderers render the HTML “head” and “body” elements respectively. Then they render all component resources under the facet child (named by target) under the UIViewRoot using the UIViewRoot component resource methods as described in Methods.

Existing component libraries (with existing head and body components), that want to use this resource loading feature must follow the rendering requirements described in Standard HTML RenderKit Implementation.

Resource Rendering Using Annotations

Components and renderers may be declared as requiring a resource using the @ResourceDependency annotation. The implementation must scan for the presence of this annotation on the component that was added to the List of child components. Check for the presence of the annotation on the renderer for this component (if there is a renderer for the component). The annotation check must be done immediately after the component is added to the List. Refer to Component Tree Manipulation for detailed information.

Resource Library Contracts

A resource library contract is a resource library, as specified in the preceding section, except that instead of residing in the resources directory of the web-app root, or in the META-INF/resources JAR entry name in a JAR file, it resides in the contracts directory of the web-app root, or in the META-INF/contracts JAR entry name in a JAR file. When packaged in a JAR file, there is one additional packaging requirement: each resource library contract in the JAR must have a marker file. The name of the file is given by the value of the symbolic constant jakarta.faces.application.ResourceHandler.RESOURCE_CONTRACT_XML. This may be a zero length file, though future versions of the specification may use the file to declare the usage contract. The requirement to have a marker file enables implementations to optimize for faster deployment while still enabling automatic discovery of the available contracts.

Following is a listing of the entries in a JAR file containing two resource library contracts.

META-INF/contracts/
                  siteLayout/
                            jakarta.faces.contract.xml
                            topNav_template.xhtml
                            leftNav_foo.xhtml
                            styles.css
                            script.js
                            background.png
                  subSiteLayout/
                                jakarta.faces.contract.xml
                                sub_template.xhtml

All of the other packaging, encoding and decoding requirements are the same as for resource libraries.

See Resource Library Contracts Background for a non-normative overview of the feature, including a brief usage example.