21 Understanding the Fusion Page Lifecycle

This chapter describes the ADF page lifecycle, its phases, and how to best use the lifecycle within a Fusion web application.

This chapter includes the following sections:

21.1 Introduction to the Fusion Page Lifecycle

When a page is submitted and a new page requested, the application invokes both the ADF Faces page lifecycle, which extends the standard JSF request lifecycle, and the ADF page lifecycle. The extended JSF lifecycle handles submitting the values on the page, validating component values, navigating pages, displaying components on the resulting page, and saving and restoring state. The JSF lifecycle phases use a UI component tree to manage the display of the faces components. This tree is a runtime representation of a JSF page: each UI component tag in a page corresponds to a UI component instance in the tree. The FacesServlet servlet manages the request processing lifecycle in JSF applications. FacesServlet creates an object called FacesContext, which contains the information necessary for request processing, and invokes an object that executes the lifecycle. For more details about the extended JSF lifecycle, see the "Understanding the JSF and ADF Faces Lifecycles" chapter of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

The ADF page lifecycle handles preparing and updating the data model, validating the data at the model layer, and executing methods on the business layer. The ADF page lifecycle uses the binding container to make data available for easy referencing by the page during the current page request.

The combined JSF and ADF page lifecycle is only one sequence within a larger sequence of events that begins when an HTTP request arrives at the application server and continues until the page is returned to the client. This overall sequence of events can be called the web page lifecycle. It follows processing through the model, view, and controller layers as defined by the MVC architecture. The page lifecycle is not a rigidly defined set of events, but is rather a set of events for a typical use case. Figure 21-1 shows a sequence diagram of the lifecycle of a web page request using JSF and Oracle ADF in tandem.

Figure 21-1 Lifecycle of a Web Page Request Using JSF and Oracle ADF

Control Flow in an ADF application

The basic flow of processing a web page request using JSF and Oracle ADF happens as follows:

  1. A web request for http://yourserver/yourapp/faces/some.jsp arrives from the client to the application server.

  2. The ADFBindingFilter object looks for the ADF binding context in the HTTP session, and if it is not yet present, initializes it for the first time. Some of the functions of the ADFBindingFilter include finding the name of the binding context metadata file, and finding and constructing an instance of each data control.

  3. The ADFBindingFilter object invokes the beginRequest() method on each data control participating in the request. This method gives the data control a notification at the start of every request so that it can perform any necessary setup.

  4. The JSF Lifecycle object, which is responsible for orchestrating the standard processing phases of each request, notifies the ADFPhaseListener class during each phase of the lifecycle, so that it can perform custom processing to coordinate the JSF lifecycle with the ADF Model data binding layer. For more information about the details of the JSF and ADF page lifecycle phases, see Section 21.2, "The JSF and ADF Page Lifecycles."

    Note:

    The FacesServlet class (in javax.faces.webapp), configured in the web.xml file of a JSF application, is responsible for initially creating the JSF Lifecycle class (in javax.faces.lifecycle) to handle each request. However, since it is the Lifecycle class that does all the interesting work, the FacesServlet class is not shown in the diagram.
  5. The ADFPhaseListener object creates an ADF PageLifecycle object to handle each request and delegates appropriate before and after phase methods to corresponding methods in the ADF PageLifecycle class. If the appropriate binding container for the page has never been used before during the user's session, it is created.

  6. The first time an application module data control is referenced during the request, it acquires an instance of the application module from the application module pool.

  7. The JSF Lifecycle object forwards control to the page to be rendered.

  8. The UI components on the page access value bindings and iterator bindings in the page's binding container and render the formatted output to appear in the browser.

  9. The ADFBindingFilter object invokes the endRequest() method on each data control participating in the request. This method gives a data control notification at the end of every request, so that they can perform any necessary resource cleanup.

  10. An application module data control uses the endRequest notification to release the instance of the application module back to the application module pool.

  11. The user sees the resulting page in the browser.

The ADF page lifecycle also contains phases that are defined simply to notify ADF page lifecycle listeners before and after the corresponding JSF phase is executed (that is, there is no implementation for these phases). These phases allow you to create custom listeners and register them with any phase of both the JSF and ADF page lifecycles, so that you can customize the ADF page lifecycle if needed, both globally or at the page level.

21.2 The JSF and ADF Page Lifecycles

Figure 21-2 shows how the JSF and ADF phases integrate in the lifecycle of a page request. For more information about how the JSF lifecycle operates on its own, see the "Understanding the JSF and ADF Faces Lifecycles" chapter of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

Figure 21-2 Lifecycle of a Page Request in a Fusion Web Application

The ADF and JSF phases work together

In a JSF application that uses the ADF Model layer, the phases in the page lifecycle are as follows:

  • Restore View: The URL for the requested page is passed to the bindingContext object, which finds the page definition file that matches the URL. The component tree of the requested page is either newly built or restored. All the component tags, event handlers, converters, and validators on the submitted page have access to the FacesContext instance. If the component tree is empty, (that is, there is no data from the submitted page), the page lifecycle proceeds directly to the Render Response phase.

    If any discrepancies between the request state and the server-side state are detected, an error will is thrown and the page lifecycle jumps to the Render Response phase.

  • JSF Restore View: Provides before and after phase events for the Restore View phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Restore View phase. The Initialize Context phase of the ADF Model page lifecycle listens for the after(JSF Restore View) event and then executes. The ADF Controller uses listeners for the before and after events of this phase to synchronize the server-side state with the request. For example, it is in this phase that browser back button detection and bookmark reference are handled. After the before and after listeners are executed, the page flow scope is available.

  • Initialize Context: The page definition file is used to create the bindingContainer object, which is the runtime representation of the page definition file for the requested page. The LifecycleContext class used to persist information throughout the ADF page lifecycle phases is instantiated and initialized with values for the associated request, binding container, and lifecycle.

  • Prepare Model: The ADF page lifecycle enters the Prepare Model phase by calling the BindingContainer.refresh(PREPARE_MODEL) method. During the Prepare Model phase, BindingContainer page parameters are prepared and then evaluated. If parameters for a task flow exist, they are passed into the flow.

    Next, any executables that have their refresh attribute set to prepareModel are refreshed based on the order of entry in the page definition file's <executables> section and on the evaluation of their RefreshCondition properties (if present). When an executable leads to an iterator binding refresh, the corresponding data control will be executed, and that leads to execution of one or more collections in the service objects. If an iterator binding fails to refresh, a JBO exception will be thrown and the data will not be available to display. For more information, see Section 21.2.1, "What You May Need to Know About Using the Refresh Property Correctly."

    If the incoming request contains no POST data or query parameters, then the lifecycle forwards to the Render Response phase.

    If the page was created using a template, and that template contains bindings using the ADF Model layer, the template's page definition file is used to create the binding container for the template. The container is then added to the binding context.

    If any taskFlow executable bindings exist (for example, if the page contains a region), the taskFlow binding creates an ADF Controller ViewPortContext object for the task flow, and any nested binding containers for pages in the flow are then executed.

  • Apply Request Values: Each component in the tree extracts new values from the request parameters (using its decode method) and stores those values locally. Most associated events are queued for later processing. If you have set a component's immediate attribute to true, then the validation, conversion, and events associated with the component are processed during this phase and the lifecycle skips the Process Validations, Update Model Values, and Invoke Application phases. Additionally, any associated iterators are invoked. For more information about ADF Faces validation and conversion, see the "Validating and Converting Input" chapter in the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

  • JSF Apply Request Values: Provides before and after phase events for the Apply Request Values phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Apply Request Values phase.

  • Process Validations: Local values of components are converted and validated on the client. If there are errors, the lifecycle jumps to the Render Response phase. At the end of this phase, new component values are set, any validation or conversion error messages and events are queued on FacesContext, and any value change events are delivered. Exceptions are also caught by the binding container and cached.

  • JSF Process Validations: Provides before and after phase events for the Process Validations phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Process Validations phase.

  • Update Model Values: The component's validated local values are moved to the model and the local copies are discarded. For any updateable components (such as an inputText component), corresponding iterators are refreshed, if the refresh condition is set to the default (deferred) and the refresh condition (if any) evaluates to true.

    If you are using a backing bean for a JSF page to manage your UI components, any UI attributes bound to a backing bean property will also be refreshed in this phase.

  • JSF Update Model Values: Provides before and after phase events for the Update Model Values phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Update Model Values phase.

  • Validate Model Updates: The updated model is now validated against any validation routines set on the model. Exceptions are caught by the binding container and cached.

  • Invoke Application: Any action bindings for command components or events are invoked.

  • JSF Invoke Application: Provides before and after phase events for the Invoke Application phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Invoke Application phase.

  • Metadata Commit: Changes to runtime metadata are committed. This phase stores any runtime changes made to the application using the Metadata Service (MDS). For more information about using MDS to persist runtime changes, see Chapter 34, "Customizing Applications with MDS."

  • Initialize Context (only if navigation occurred in the Invoke Application lifecycle): The Initialize Context phase listens for the beforeJSFRenderResponse event to execute. The page definition file for the next page is initialized.

  • Prepare Model (only if navigation occurred in the Invoke Application lifecycle): Any page parameters contained in the next page's definition are set.

  • Prepare Render: The binding container is refreshed to allow for any changes that may have occurred in the Apply Request Values or Validation phases. Any iterators that correspond to read-only components (such as an outputText component) are refreshed. Any dynamic regions are switched, if needed. The prepareRender event is sent to all registered listeners, as is the afterJSFRenderResponse event.

    Note:

    Instead of displaying prepareRender as a valid phase for a selection, JDeveloper displays renderModel, which represents the refresh(RENDER_MODEL) method called on the binding container.
  • Render Response: The components in the tree are rendered as the Java EE web container traverses the tags in the page. State information is saved for subsequent requests and the Restore View phase.

    In order to lessen the wait time required to display both a page and any associated data, certain ADF Faces components such as the table component, use data streaming for their initial request. When a page contains one or more of these components, the page goes through the normal lifecycle. However, instead of fetching the data during that request, a special separate request is run. Because the page has just rendered, only the Render Response phase executes for the components that use data streaming, and the corresponding data is fetched and displayed. If the user's action (for example scrolling in a table), causes a subsequent data fetch another request is executed. Tables, trees, tree tables, and data visualization components all use data streaming.

  • JSF Render Response: Provides before and after phase events for the Render Response phase. You can create a listener and register it with the before or after event of this phase, and the application will behave as if the listener were registered with the Render Response phase.

21.2.1 What You May Need to Know About Using the Refresh Property Correctly

For scalability reasons, at runtime the iterator bindings in the binding container release any reference they have to a row set iterator at the end of each request. During the next request, each iterator binding rebinds itself to a "live" row set iterator that is tracking the current row of some data collection. The process of rebinding an ADF iterator binding during the ADF page lifecycle is known as refreshing the iterator. By default, this happens only once, on demand, when the iterator is first accessed by the client layer during the lifecycle. This initial access to the iterator typically occurs during page rendering, while EL expressions in the page, which reference the iterator or a control binding related to that iterator, are being evaluated. Alternatively, you can writ code that programmatically causes the first access to the iterator to be before the Prepare Render phase. To force the iterator binding to refresh its row set iterator earlier in the lifecyle, you can set the refresh attribute on the iterator binding to a value other than the default.

Tip:

Refreshing an iterator binding does not forcibly reexecute its query each time. The first time the view object instance's row set iterator is accessed during a particular user's session, it will implicitly execute the view object's query if it was not already executed, as long as a search binding in the page definition related to that iterator has not already cleared the row set in preparation for allowing the user to enter search criteria before executing the query.

Subsequent refreshing of the iterator binding related to that view object instance on page requests that are part of the same logical unit of work will only access the row set iterator again, not forcibly reexecute the query. When you want reexecuting the query to refresh its data, use the Execute or ExecuteWithParams built-in operation, or programmatically call the executeQuery() method on the iterator binding.

The refresh and refreshCondition attributes are used to determine when and whether to invoke an executable, such as an iterator binding, or an invokeAction. The value of the refresh attribute determines the lifecycle phase in which to invoke the executable, while the value of the refreshCondition attribute provides an optional boolean condition whose outcome determines whether the refresh will occur at the indicated lifecycle phase or not. By default, when JDeveloper adds an executable to a page definition (for example, when you drop an operation as a command component), the refresh attribute for the executable binding is set to deferred, which enforces execution whenever the binding is accessed the first time. If no refreshCondition value exists, the executable is invoked. If a value for refreshCondition exists, then that value is evaluated as an EL expression, and if the return value of the evaluation is true, then the executable is invoked. If the value evaluates to false, the executable is not invoked. For details about the refresh attribute, see Section A.8.1, "PageDef.xml Syntax.".

For most cases in a Fusion web application, you should not need to change the refresh or refreshCondition values on an iterator binding. These values are set to ensure that the correct data is displayed.

While the invokeAction executable continues to be supported for upward compatibility from previous releases, in Oracle ADF 11g you use a method activity in a task flow to call an action binding (or any backing bean method) to perform some application behavior before the page is rendered. For example, for a page used to create an object, you might have a task flow that begins with a method activity that calls the CreateInsert operation. The task flow then proceeds to the view activity for the page where the user inputs data. Modeling this behavior as discreet method call activities provides a much cleaner separation of application logic and data bindings, making applications both self-documenting and easier to maintain. For more information, see Section 22.6, "Creating an Input Form."

However, for completeness' sake, in case you encounter situations where you decide to change the refresh attribute for iterator bindings or for invokeAction (for example, if you have programmatic code that runs after the Prepare Model phase and needs to access the iterator programmatically), you should do so, informed of the following information regarding the valid values:

  • deferred (the default): On demand

    Tip:

    Any invokeAction executable in a page definition file must have a value other than the default (deferred) for its refresh attribute, or it will not be refreshed and invoked.
  • prepareModel: During the Prepare Model phase.

  • renderModel: During the Prepare Render phase.

    Tip:

    Notice in Figure 21-2 that the key distinction between the Prepare Model phase and the Prepare Render phase is that one comes before JSF's Invoke Application phase, and one after. Since JSF's Invoke Application phase is when action listeners fire, if you need your iterator refreshed or the method or operation associated with the invokeAction to execute after these action listeners have performed their processing, you'll want to set the refresh attribute to renderModel.
  • ifNeeded: During the Prepare Model and Prepare Render phases, only if needed. For iterators, the refresh is considered needed if the binding has not yet been refreshed. To determine if the execution is needed for an invokeAction, the framework compares the current set of evaluated parameter values with the set that was used to invoke the method action binding previously. If the parameter values for the current invocation are exactly the same as those used previously, the invokeAction does not invoke its bound method action binding. Use this setting if the invokeAction executable binds to a method action binding that accepts parameters.

    Tip:

    For invokeAction executables bound to methods that do not take parameters, the invokeAction executable will be called twice. To use the invokeAction executable with parameterless methods, you should use ensure that the condition associated with the refreshCondition attribute evaluates to invoke the method only if the value has changed. This will prevent multiple invocations.
  • prepareModelIfNeeded and renderModelIfNeeded: Same as ifNeeded, except that it is executed during the named phase.

  • never: Not valid for invokeAction executables. For iterators, the iterator will never be refreshed. Use when your own code calls getRowSetIterator() on the iterator binding.

  • always: Not valid for invokeAction executables. For iterators, the iterator will always be refreshed (potentially multiple times) during both the Prepare Model and Prepare Render phases, as well as during the Update Model phase.

  • refreshAfter: Use to handle dependencies between executables. For example, you can set the condition so that this executable refreshes after another executable.

    Tip:

    You can determine the order of executable invocation using the refreshAfter attribute. For example, suppose you have two invokeAction elements—one with an ID of myAction and another with an ID of anotherAction—and you want myAction to fire after anotherAction. You would set the refreshAfter condition on myAction to anotherAction.

21.2.2 What You May Need to Know About Task Flows and the Lifecycle

Task flows are initially refreshed when the parent binding container (the one associated with the page) is refreshed. This happens in the Prepare Model phase. On a subsequent request, the task flow will be refreshed during the Prepare Render phase, depending on its refresh and refreshCondition attributes and its parameter value.

Note:

Any child page fragment's page definition still handles the refresh of the bindings of the child page fragments.

Tip:

If you have a region on a page that is not initially disclosed (for example, a popup dialog), the parameters still need to be available when the parent page is rendered, even though the region might not be displayed. If a region requires parameters, but those parameter values will not be available when the parent page is rendered, then you should use dynamic regions. If the parameters are null, an empty task flow can be used until the parameters for the region are ready and that region can display. To swap in an empty task flow, you set the dynamic region's taskFlowId attribute to an empty string.

If you set an EL expression as the value of the refreshCondition attribute, it will be evaluated during the Prepare Render phase of the lifecycle. When the expression evaluates to true, the task flow will be refreshed again. When refreshCondition evaluates to false, the behavior is the same as if the refreshCondition had not been specified.

Note:

If the variable bindings is used within the EL expression, the context refers to the binding container of the parent page, not the page fragment displayed within the region.

The valid values for the refresh property of a task flow executable are as follows:

  • default: The region will be refreshed only once, when the parent page is first displayed.

  • ifNeeded: Refreshes the region only if there has been a change to taskFlow binding parameter values. If the taskFlow binding does not have parameters, then ifNeeded is equivalent to the default. When ifNeeded is used, the refreshCondition attribute is not considered.

    Note:

    Setting the refresh attribute to ifNeeded takes precedence over any value for the refreshCondition attribute. Also note that ifNeeded is not supported when you pass parameters to the taskFlow binding using a dynamic parameter Map. Instead, use refreshCondition="#{EL.Expression}".

Because the only job of the taskFlow binding is to refresh its parameters, setting Refresh to always does not make sense. If the taskFlow binding's parameters don't change, there is no reason to refresh the ADF region.

Note that the child page fragment's page definition still handles the refresh of the bindings of the child page fragments.

21.3 Object Scope Lifecycles

At runtime, ADF objects such as the binding container and managed beans are instantiated. Each of these objects has a defined lifespan set by its scope attribute. You can access a scope as a java.util.Map from the RequestContext API. For example, to access an object named foo in the request scope, you would use the expression #{requestScope.foo}.

There are six types of scopes in a Fusion web application:

  • Application scope: The object is available for the duration of the application.

  • Session scope: The object is available for the duration of the session.

    Note:

    There's no window uniqueness for session scope, all windows in the session share the same session scope instance. If you are concerned about multiple windows being able to access the same object (for example to ensure that managed beans do not conflict across windows), you should use a scope that is window-specific, such as page flow or view scope.
  • Page flow scope: The object is available for the duration of a bounded task flow.

    Note:

    Because this is not a standard JSF scope, EL expressions must explicitly include the scope to reference bean. For example, to reference the MyBean managed bean from the pageFlowScope scope, your expression would be #{pageFlowScope.MyBean}.
  • Request scope: The object is available from the time an HTTP request is made until a response is sent back to the client.

  • Backing bean scope: Used for managed beans for page fragments and declarative components only, the object is available from the time an HTTP request is made until a response is sent back to the client. This scope is needed for fragments and declarative components because there may be more than one page fragment or declarative component on a page, and to prevent collisions, any values must be kept in separate scope instances. Therefore, any managed bean for a page fragment or declarative component must use backing bean scope.

    Note:

    Because this is not a standard JSF scope, EL expressions must explicitly include the scope to reference bean. For example, to reference the MyBean managed bean from the backing bean scope, your expression would be #{backingBeanScope.MyBean}.
  • View scope: The object is available until the view ID for the current view activity changes. This scope can be used to hold values for a given page. However, unlike request scope, which can be used to store a value needed from one page to the next, anything stored in view scope will be lost once the view ID changes.

    Note:

    Because this is not a standard JSF scope, EL expressions must explicitly include the scope to reference bean. For example, to reference the MyBean managed bean from the view scope, your expression would be #{viewScope.MyBean}.

    Note:

    When you create objects (such as a managed bean) that require you to define a scope, you can set the scope to none, meaning that it will not live within any particular scope, but will instead be instantiated each time it is referenced.

Object scopes are analogous to global and local variable scopes in programming languages. The wider the scope, the higher availability of an object. During their life, these objects may expose certain interfaces, hold information, or pass variables and parameters to other objects. For example, a managed bean defined in session scope will be available for use during multiple page requests. However, a managed bean defined in request scope will only be available for the duration of one page request.

By default, the binding container and the binding objects it contains are defined in session scope. However, the values referenced by value bindings and iterator bindings are undefined between requests and for scalability reasons do not remain in session scope. Therefore, the values that binding objects refer to are valid only during a request in which that binding container has been prepared by the ADF lifecycle. What stays in session scope are only the binding container and binding objects themselves.

Figure 21-3 shows the time period during which each type of scope is valid.

Figure 21-3 Relationship Between Scopes and Page Flow

Scopes in ADF lifecycle

When determining what scope to register a managed bean with, always try to use the narrowest scope possible. Only use the session scope for information that is relevant to the whole session, such as user or context information. Avoid using session scope to pass values from one task flow to another. When creating a managed bean for a page fragment or a declarative component, you must use backing bean scope.

Managed beans can be registered in either the adfc-config.xml or the configuration file for a specific task flow. For more information about using managed beans in a Fusion application, see Section 20.4, "Using a Managed Bean in a Fusion Web Application."

Note:

Registering managed beans within the faces-config.xml file is not recommended in a Fusion web application.

21.3.1 What You May Need to Know About Object Scopes and Task Flows

When determining what scope to use for variables within a task flow, you should use any of the scope options other than application or session scope. These two scopes will persist objects in memory beyond the life of the task flow and therefore compromise the encapsulation and reusable aspects of a task flow. In addition, application and session scopes may keep objects in memory longer than needed, causing unneeded overhead.

When you need to pass data values between activities within a task flow, you should use page flow scope. View scope is recommended for variables that are needed only within the current view activity, not across view activities. Request scope should be used when the scope does not need to persist longer than the current request. It is the only scope that should be used to store UI component information. Lastly, backing bean scope must be used for backing beans in your task flow if there is a possibility that your task flow will appear in two region components or declarative components on the same page and you would like to achieve region instance isolations.

21.4 Customizing the ADF Page Lifecycle

The ADF lifecycle contains clearly defined phases that notify ADF lifecycle listeners before and after the corresponding JSF phase is executed. You can customize this lifecycle by creating a custom phase listener that invokes your needed code, and then registering it with the lifecycle to execute during one of these phases.

For example, you can create a custom listener that executes custom code and then register it with the JSF Apply Request Values phase so that it can be invoked either before or after the Apply Request Values phase.

Note:

An application cannot have multiple phase listener instances. An initial ADFPhaseListener instance is by default, registered in the META-INF/faces-config.xml configuration file. Registering, for example, a customized subclass of the ADFPhaseListener creates a second instance. In this scenario, only the instance that was most recently registered is used.

The following warning message indicates when an instance has been replaced by a newer one: "ADFc: Replacing the ADF Page Lifecycle implementation with class name of the new listener."

21.4.1 How to Create a Custom Phase Listener

To create a custom phase listener, you must create a listener class that implements the PagePhaseListener interface. You then add methods that execute code either before or after the phase that the code needs to execute.

Example 21-1 contains a template that you can modify to create a custom phase listener. See Section 4.13.1, "How to Generate Custom Classes," for more information about creating a class in JDeveloper.

Example 21-1 Example Custom Phase Listener

public class MyPagePhaseListener implements PagePhaseListener
{
   public void afterPhase(PagePhaseEvent event)
   {
      System.out.println("In afterPhase " + event.getPhaseId());
   }
 
 
   public void beforePhase(PagePhaseEvent event)
   {
      System.out.println("In beforePhase " + event.getPhaseId());
   }
}

Once you create the custom listener class, you need to register it with the phase in which the class needs to be invoked. You can either register it globally (so that the whole application can use it), or you can register it only for a single page.

21.4.2 How to Register a Listener Globally

To customize the ADF lifecycle globally, register your custom phase listener by editing the adf-settings.xml configuration file. The adf-settings.xml file is shared by several ADF components, including ADF Controller, to store configuration information. If the adf-settings.xml file does not yet exist, you need to create it. For information, see the "Configuration in adf-settings.xml" section of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

To register the listener in adf-settings.xml:

  1. In the META-INF directory, double-click the adf-settings.xml file to open it.

  2. Click the Source tab, and scroll down to <adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">

    If this entry does not exist, add it to the file.

  3. Enter the remaining elements shown in italics in Example 21-2.

    Example 21-2 adf-settings.xml Configuration File with Listener Registration

     <?xml version="1.0" encoding="US-ASCII" ?> <adf-config xmlns="http://xmlns.oracle.com/adf/config">
      .
      .
      .
        <adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">
           <lifecycle>
             <phase-listener>
                <listener-id>MyPagePhaseListener</listener-id>
                <class>mypackage.MyPagePhaseListener</class>        
             </phase-listener>
          </lifecycle>
       </adfc-controller-config> 
      .
      .
      .
    </adf-config>
    
  4. Add values for the following elements:

    • <listener-id> A unique identifier for the listener (you can use the fully qualified class name)

    • <class> The class name of the listener

21.4.3 What You May Need to Know About Listener Order

You can specify multiple phase listeners in the adf-settings.xml and, optionally, the relative order in which they are called. When registering a new listener in the file, you determine the position in the list of listeners using two parameters:

  • beforeIdSet: The listener is called before any of the listeners specified in beforeIdSet

  • afterIdSet: The listener is called after any of the listeners specified in afterIdSet

Example 21-3 contains an example configuration file in which multiple listeners have been registered for an application.

Example 21-3 adf-settings.xml Configuration File with Multiple Listener Registration

<lifecycle>
   <phase-listener>
      <listener-id>MyPhaseListener</listener-id>
      <class>view.myPhaseListener</class>
      <after-id-set>
         <listener-id>ListenerA</listener-id>
         <listener-id>ListenerC</listener-id>
      </after-id-set>
      <before-id-set>
         <listener-id>ListenerB</listener-id>
         <listener-id>ListenerM</listener-id>
         <listener-id>ListenerY</listener-id>
      </before-id-set>
   </phase-listener>
</lifecycle>

In the example, MyPhaseListener is a registered listener that executes after listeners A and C but before listeners B, M, and Y. To execute MyPhaseListener after listener B, move the <listener-id> element for listener B under the <after-id-set> element.

21.4.4 How to Register a Lifecycle Listener for a Single Page

To customize the lifecycle of a single page, you set the ControllerClass attribute on the page definition file. This listener will be valid only for the lifecycle of the particular page described by the page definition. For more information about the page definition file and its role in a Fusion web application, see Section 12.6, "Working with Page Definition Files."

You specify a different controller class depending on whether it is for a standard JSF page or a page fragment:

To customize the ADF Lifecycle for a single page or page fragment:

  1. In the Application Navigator, right-click the page or page fragment and choose Go To Page Definition.

  2. In the Structure window, select the page definition node.

  3. In the Property Inspector, click the dropdown menu next to the ControllerClass field and choose Edit.

  4. Click Hierarchy and navigate to the appropriate controller class for the page or page fragment. Following are the controller classes to use for different types of pages:

    • Standard JSF page - specify oracle.adf.controller.v2.lifecycle.PageController

      If you need to receive afterPhase/beforePhase events, specify oracle.adf.controller.v2.lifecycle.PagePhaseListener

    • Page fragment - specify oracle.adf.model.RegionController

    Tip:

    You can specify the value of the page definition's ControllerClass attribute as a fully qualified class name or you can enter an EL expression that resolves to a class directly in the ControllerClass field.

    When using an EL expression for the value of the ControllerClass attribute, the Structure window may show a warning indicating that e "#{YourExpression}" is not a valid class. You can safely ignore this warning.

21.4.5 What You May Need to Know About Extending RegionController for Page Fragments

Bindings inside page fragments used as regions are refreshed through the refreshRegion and validateRegion events of the RegionController interface. These events are available if you specify oracle.adf.model.RegionController in the ControllerClass field as described in Section 21.4.4, "How to Register a Lifecycle Listener for a Single Page."

As shown in Example 21-4, you can use the refreshRegion event to add custom code that executes before the region is refreshed. For example, you may want to refresh the bindings used by the page fragment in the region so that the refreshed binding values are propagated to the inner binding container.

To do this, create a new class that implements the RegionController interface. Then, write the following refreshRegion method, including your custom code that you want to execute before the Prepare Model phase.

Example 21-4 regionRefresh Method

public boolean refreshRegion(RegionContext regionCtx)
   {
      int refreshFlag = regionCtx.getRefreshFlag();
      if (refreshFlag == RegionBinding.PREPARE_MODEL)
      {
         // Execute some code before
      }
      // Propagate the refresh to the inner binding container
      regionCtx.getRegionBinding().refresh(refreshFlag);
      
      return false;
   }
 
   public boolean validateRegion(RegionContext regionCtx)
   {
      // Propagate the validate to the inner binding container
      regionCtx.getRegionBinding().validate();
      
      return false;
   }

As shown in Example 21-4, the refresh flag value can be:

  • RegionBinding.PREPARE_MODEL - corresponds to the event occurring during the ADF Lifecycle prepareModel phase

  • RegionBinding.RENDER_MODEL - corresponds to the event occurring during the ADF Lifecycle prepareRender phase