30 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:

30.1 About the Fusion Page Lifecycle

The Fusion page lifecycle provides support for a web page to interact with ADF Model data bindings. You can learn about the sequence of events that occur while processing a web page request using JSF and Oracle ADF.

When a JSF page is submitted and a new page is requested, the JSF page request lifecycle is invoked. This lifecycle handles the submission of values on the page, validation for components on the current page, navigation to and display of the components on the resulting page, as well as saving and restoring state. The JSF lifecycle phases use a UI component tree to manage the display of the 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 object manages the page request lifecycle in JSF applications. The FacesServlet object creates an object called FacesContext, which contains the information necessary for request processing, and invokes an object that executes the lifecycle.

In addition to the JSF lifecycle, the ADF Faces and ADF page lifecycle are also run. The ADF Faces lifecycle extends the JSF lifecycle, providing additional functionality, such as a client-side value lifecycle, a subform component that allows you to create independent submittable sections on a page without the drawbacks (for example, lost user edits) of using multiple forms on a single page, and additional scopes. For information about both the JSF lifecycle and the ADF Faces lifecycle, including what you can do with the lifecycle for your application, see Using the JSF Lifecycle with ADF Faces.

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 30-1 shows a sequence diagram of the lifecycle of a web page request using JSF and Oracle ADF in tandem.

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

Description of Figure 30-1 follows
Description of "Figure 30-1 Lifecycle of a Web Page Request Using JSF and Oracle ADF"

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 information about the details of the JSF and ADF page lifecycle phases, see About 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 the appropriate before and after phase methods to corresponding methods in the ADF PageLifecycle class. If the 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.

30.2 About the JSF and ADF Page Lifecycles

The JSF and ADF page lifecycles perform a sequence of events that begins when an HTTP request arrives at the application server and continues until the page is returned to the client. You can learn about the different phases of a page lifecycle in a JSF application that uses an ADF Model.

Figure 30-2 shows a high-level view of the combined JSF and ADF page lifecycles.

Figure 30-2 JSF and ADF Page Lifecycles

Description of Figure 30-2 follows
Description of "Figure 30-2 JSF and ADF Page Lifecycles"

Say for example, you have a page with some text displayed in an input text component and a button. When that page is first rendered, the component tree is built during the Restore View phase and then the lifecycle goes straight to the Render Response phase, as the components are rendered. When the user clicks the button, the full lifecycle is invoked. The component tree is rebuilt, the input text component extracts any new value during the Apply Request Values phase and Process Validations phase, and if there is an error, for example due to validation, then the lifecycle jumps to the Render Response phase. Otherwise, the model is then updated with the new value during the Update Model phase, and then any application processing associated with the button (such as navigation), is executed during the Invoke Application phase.

Figure 30-3 shows the details of how the JSF and ADF Faces and ADF Model phases integrate in the lifecycle of a page request.

Figure 30-3 Lifecycle of a Page Request in a Fusion Web Application

Description of Figure 30-3 follows
Description of "Figure 30-3 Lifecycle of a Page Request in a Fusion Web Application"

In a JSF application that uses ADF Model, 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. 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 property 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 about the RefreshCondition property, see pageNamePageDef.xml.

    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 ADF Model, 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 Developing Web User Interfaces with Oracle ADF Faces.

  • 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 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. Collection-based components, such as 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.

30.2.1 What You May Need to Know About Partial Page Rendering and Iterator Bindings

ADF Faces provides an optimized lifecycle that you can use when you want the page request lifecycle (including conversion and validation) to be run only for certain components on a page, usually for components whose values have changed.

One way to use the optimized lifecycle is to manually set up dependencies so that the events from one component act as triggers for another component, known as the target. When any event occurs on the trigger component, the lifecycle is run on any target components, as well as on any child components of both the trigger and the target, causing only those components to be rerendered. This is considered a partial page rendering (PPR)

The following example shows radio buttons as triggers and a panelGroupLayout component that contains the output text to be the target (the panelGroupLayout component must be the target because the outputText component may not always be rendered).

<af:form>
  <af:inputText label="Required Field" required="true"/>
  <af:selectBooleanRadio id="show" autoSubmit="true" text="Show"                                      
                        value="#{validate.show}"/>
  <af:selectBooleanRadio id="hide" autoSubmit="true" text="Hide" 
                         value="#{validate.hide}"/>
  <af:panelGroupLayout partialTriggers="show hide" id="panel">
    <af:outputText value="You can see me!" rendered="#{validate.show}"/>
  </af:panelGroupLayout>
</af:form>

Because the autoSubmit attribute is set to true on the radio buttons, when they are selected, a SelectionEvent is fired. Because the panelGroupLayout component is set to be a target for both radio components, when that event is fired, only the selectOneRadio (the trigger), the panelGroupLayout component (the trigger's target), and its child component (the outputText component) are processed through the lifecycle. Because the outputText component is configured to render only when the Show radio button is selected, the user is able to select that radio button and see the output text, without having to enter text into the required input field above the radio buttons.

Instead of having to set partial triggers and targets within code manually, the ADF Faces framework provides automatic PPR. Automatic PPR happens when an event has a corresponding event root component, or when the component itself is an event root. An event root component determines the boundaries of PPR. For example, the attributeChangeEvent considers the inputText component that invokes the event, its event root. Therefore, in the previous example, when the value changes for the inputText component, because it is the initial boundary for the PPR, the lifecycle would be run only on that inputText component and any child components.

Along with event roots, by default for Fusion web applications, automatic PPR also occurs for all UI components associated with the same iterator binding. All associated components refresh whenever any one of them has a value change event. This functionality is controlled by setting the changeEventPolicy attribute for iterators to ppr. By default, this is set globally.

For example, say you create a number of inputText components using the Data Controls panel (for example, when you create a form). When the attributeChangeEvent occurs on one of those inputText components, because the other inputText components are associated with the same iterator, all those inputText components will also be refreshed.

Another example might be a page that contains a panelSplitter component, where the left part of the splitter contains a form to create a new customer (created by dropping a form using the Customer collection), and the right part of the splitter contains instructions for using the form. Because the bindings for the inputText components that make up the form are all associated with the Customer iterator, anytime the form is submitted and a value for one of the inputText components has changed, only the inputText components associated with the Customer will be processed through the lifecycle. The rest of the page, including the instructions in the right side of the splitter, will not be refreshed.

Note:

In order for automatic PPR to happen, the event and event root component must be listed in Table 5-1 of the Events and Partial Page Rendering section of Developing Web User Interfaces with Oracle ADF Faces. If it is not listed, you will need to set up PPR manually, as described in the Using Partial Triggers section of the same guide.

For example, say you create a form and a table from the same data control collection. The form shows the details for a row of data, while the table shows all rows in a collection, with the row currently displayed in the form shown as selected. When you click the Next button on the form, you want the table to refresh to show the new row as selected. Because the selection event is not supported by automatic PPR, the table will not refresh, even though the table and form both use the same iterator. You will need to set the Next and Previous buttons to be triggers of PPR for the target table.

Note:

Only UI components that have a control binding defined in the page definition file associated with the configured iterator will be refreshed.

If you directly access the iterator from a managed bean and expose iterator values to UI components through that bean, then automatic PPR will not happen for those UI components. In these cases, you need to configure PPR manually. For more information, see the Using Partial Triggers section of Developing Web User Interfaces with Oracle ADF Faces.

By default, all new applications use global PPR. You do not have to set it. You can however, override the setting in the page definition file for a page.

Before you begin:

It may be helpful to have an understanding of partial page rendering. For more information, see What You May Need to Know About Partial Page Rendering and Iterator Bindings.

To set iterator bindings to use PPR:

  1. To set a specific iterator binding to use or not use PPR, open the associated page definition file, select the iterator, and in the Properties window, set ChangeEventPolicy.

  2. To set all iterator bindings to use PPR:

    1. In the Applications Resource panel, expand the Descriptors and ADF META-INF nodes, and then double click adf-config.xml.

    2. In the overview editor, click the Model navigation tab, and then select Default Change Event Policy is Partial Page Rendering (PPR).

For more information about how the ADF Faces framework uses PPR, see the Rerendering Partial Page Content chapter in Developing Web User Interfaces with Oracle ADF Faces.

30.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.

30.3 About Object Scope Lifecycles

When an object is created in a JSF application, it defines or defaults to a given scope; this object scope describes how widely it is available and who has access to it. You can use the standard JSF scopes or the ADF Faces scopes that are available for an object in a JSF application.

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}.

Object scopes are analogous to global and local variable scopes in programming languages. The wider the scope, the higher and longer the 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.

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 is 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 task flow. As its name suggests, page flow scope is specific to an instance of a task flow (bounded or unbounded). So if you have a page with two or more regions (created by a task flow), separate instances of the page flow scope will exist for each region. Additionally, if the user opens the page with the regions in separate browser tabs, a separate instance of the page flow scope bean will be instantiated for each browser tab.

    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:

    Both JSF and ADF Faces have an implementation of view scope. Only the ADF Faces view scope survives page redirects and refreshes. In a Fusion web application, when you use viewScope in an expression, it resolves to the ADF Faces view scope.

    Because this view scope 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.

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 30-4 shows the time period during which each type of scope is valid.

Figure 30-4 Relationship Between Scopes and Page Flow

Description of Figure 30-4 follows
Description of "Figure 30-4 Relationship Between Scopes and Page Flow"

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 Using a Managed Bean in a Fusion Web Application.

Note:

Do not register managed beans in the faces-config.xml in a Fusion web application.

30.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 should be used 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.

30.4 Customizing the ADF Page Lifecycle

The ADF page lifecycle performs a sequence of events that occur while processing a web page request using JSF and Oracle ADF. You can create and register a custom phase listeners to customize 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.

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."

30.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.

The following example contains a template that you can modify to create a custom phase listener. See How to Generate Custom Classes, for more information about creating a class in JDeveloper.

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.

30.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.

To register the listener in adf-settings.xml:

  1. In the Applications window, beneath the META-INF node, double-click the adf-settings.xml file.
  2. In the editor window, click the Source tab
  3. In the source editor, 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.

  4. Enter the remaining elements shown in italics:
     <?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>
    
  5. 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

30.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

The following example contains an example configuration file in which multiple listeners have been registered for an application.

<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.

30.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 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 Applications window, 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 Properties window, click the icon that appears when you hover over the ControllerClass field, and choose Edit.

  4. Click the Hierarchy tab 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.