35 Allowing User Customizations at Runtime

This chapter describes how to use the ADF Faces change persistence framework to create JSF pages that users can customize at runtime.

This chapter includes the following sections:

35.1 Introduction to Allowing User Customizations

Certain ADF Faces components have attributes that can be saved for a specific user. For example, the value of the disclosed attribute on a panelBox component can be saved for a specific user during the current session. The myOrders page in the StoreFront module application contains four panelBox components that display order information. By default, they are expanded, as shown in Figure 35-1.

Note:

The query component, which is not discussed in this chapter, can be used to implement saved searches, another type of user customization. For information about the query component, see Chapter 27, "Creating ADF Databound Search Forms." The configuration required for using MDS to store saved searches is discussed in Section 27.2.3, "How to Persist Saved Searches into MDS."

Figure 35-1 panelBox Components are Expanded by Default

MyOrders page with 4 expanded panelBox

However, suppose a user decides to collapse one of the boxes, as shown in Figure 35-2.

Figure 35-2 panelBox Component Remains Collapsed

panelBox Component Remains Collapsed

Because this application is configured to allow user customizations, then during the user's session, anytime that user returns to the page, the Payment Information box remains collapsed. You need only to enable user customizations for the project in order for these changes to be persisted to the user's session.

Table 35-1 shows the attribute value changes persisted by an ADF Faces application, after you configure the application to allow user customizations.

Table 35-1 Implicitly Persisted Attribute Values

Component Attribute Affect at Runtime

panelBoxshowDetail

showDetailHeader

showDetailItem

disclosed

Users can display or hide content using an icon in the header. Detail content will either display or be hidden, based on the last action of the user.

showDetailItem (used in a panelAccordion component)

flex

The height of multiple showDetailItem components are determined by their relative value of the flex attribute. The showDetailItem components with larger flex values will be taller than those with smaller values. Users can change these proportions, and the new values will be persisted.

showDetailItem (used in a panelAccordion component)

inflexibleHeight

Users can change the size of a panel, and that size will remain.

panelSplitter

collapsed

Users can collapse either side of the splitter. The collapsed state will remain as last configured by the user.

panelSplitter

splitterPosition

The position of the splitter in the panel will remain where last moved by the user.

richTextEditor

editMode

The editor will display using the mode (either WYSIWYG or source) last selected by the user.

calendar

activeDay

The day considered active in the current display will remain the active day.

calendar

view

The view (either day, week, month, or list), that currently displays activities will be retained

panelWindow

dialog

contentHeight

Users can change the height of a panelWindow or dialog popup component, and that height will remain.

panelWindow

dialog

contentWidth

Users can change the width of a panelWindow or dialog popup component, and that width will remain.

activeCommandToolbar
Button

commandButton

commandImageLink

commandLink

commandMenuItem

commandNavigationItem

commandToolbarButton

windowHeight

When users change the contentHeight attribute value of a panelWindow or dialog component, any associated windowHeight value on a command component is also changed and will remain.

activeCommandToolbar
Button

commandButton

commandImageLink

commandLink

commandMenuItem

commandNavigationItem

commandToolbarButton

windowWidth

When users change the contentWidth attribute value of a panelWindow or dialog component, any associated windowWidth value on a command component is also changed and will remain.

column

displayIndex

ADF Faces columns can be reordered by the user at runtime. The displayIndex attribute determines the order of the columns. (By default, the value is set to -1 for each column, which means that the columns will display in the same order as the data source). When a user moves a column, the value on each column is changed to reflect the new order. These new values will be persisted.

column

frozen

ADF Faces columns can be frozen so that they will not scroll. When a column's frozen attribute is set to true, all columns before that column (based on the displayIndex value) will not scroll. When you use the table with a panelCollection component, you can configure the table so that a button appears that allows the user to freeze a column. For more information, see the "Displaying Table Menus, Toolbars, and Status Bars" section of the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

column

noWrap

The content of the column will either wrap or not. You need to create code that allows the user to change this attribute value. For example, you might create a context menu that allows a user to toggle the value from true to false.

column

selected

The selected column is based on the column last selected by the user.

column

visible

The column will either be visible or not, based on the last action of the user. You will need to write code that allows the user to change this attribute value. For example, you might create a context menu that allows a user to toggle the value from true to false.

column

width

The width of the column will remain the same size as the user last set it.

table

filterVisible

ADF Faces tables can contain a component that allows users to filter the table rows by an attribute value. For a table that is configured to use a filter, the filter will either be visible or not, based on the last action of the user. You will need to write code that allows the user to change this attribute value. For example, you might create a button that allows a user to toggle the value from true to false.


You can also configure an application so that the value of these attributes can be persisted across sessions using the MDS repository. For example, if the StoreFront module allowed persistence to the MDS repository, then anytime that user entered the application, the Payment Information box would be collapsed.

Note:

Before you can enable persistence to the repository, you must first follow all MDS configuration procedures as documented in the Oracle Fusion Middleware Administrator's Guide.

Along with the automatic persistence available through ADF Faces, you can create your own custom user customization capabilities for the following types of changes:

  • Changing an attribute value

  • Adding or removing a facet

  • Adding or removing a child component

  • Reordering child components

  • Moving a child component to a different parent

If you want to create these types of custom user customizations, you need to add code (for example, in an event handler) that will call the APIs to handle the persistence.

Enabling an application to use the change persistence framework requires that you first enable your application to allow user customizations. Part of this process is determining where those changes should be persisted, either to the session or the MDS repository.

If you choose to persist changes to the session, by default all values as shown in Table 35-1 will be saved for the user's session. However if you choose to persist changes to a repository, you must explicitly configure which of these attribute values will be persisted to the repository. Instead of persisting all these attribute values, you can restrict changes so that only certain attribute value changes for a component are persisted, or so that only specific instances of the components persist changes.

Note:

You cannot persist changes to a component that is contained inside (anywhere in the subtree) of af:forEach or af:iterator tags. While such structure results in multiple copies of a component in the view tree, each component has only a single representation in the JSP document. Therefore, customizations of the component cannot be persisted in MDS.

For any applications that persist changes to an MDS repository, when you deploy your application, you must create a metadata archive (MAR) profile in the application's EAR assembly. For more information, see Section 36.3.2, "How to Create Deployment Profiles."

35.2 Enabling Runtime User Customizations for a Fusion Web Application

Enabling an application to allow user customizations (whether for the default changes that some ADF Faces components provide, or for custom capabilities that you create) requires that you configure your application to use the change persistence framework and that you also determine where those changes should be persisted (either the session or the MDS repository).

Note:

If you are planning on persisting changes to the MDS repository, before configuring an ADF Faces application to use change persistence, you must first follow all MDS configuration procedures as documented in the Oracle Fusion Middleware Administrator's Guide.

35.2.1 How to Enable User Customizations

You enable your application to use the change persistence framework by editing the web.xml and adf-config.xml files.

To enable user customizations:

  1. Double-click the web project in your application to open the Project Properties dialog. In the tree on the left, select the ADF View node.

  2. On the ADF View page, select the Enable User Customizations checkbox. If you want the changes to be persisted to only the session, select the For Duration of Session radio button. If you want the changes to persist to the MDS repository, select Across Sessions Using MDS.

  3. If you chose to persist to the repository, you now need to declare each component tag and associated attribute values that you want persisted to the repository (if you chose to persist only to the session, all values will be persisted). For procedures, see Section 35.3, "Configuring User Customizations." After that configuration is complete, you can override those settings on a per component instance basis. For procedures, see Section 35.4, "Controlling User Customizations in Individual JSF Pages."

    Note:

    If you have created custom user customization capabilities as documented in Section 35.5, "Implementing Custom User Customizations," then you also need to declare those attribute values or operations.

35.2.2 What Happens When You Enable User Customizations

When you elect to save changes only to the session, JDeveloper adds the CHANGE_PERSISTENCE context parameter to the web.xml file, and sets the value to session. This context parameter registers the ChangeManager class that will be used to handle persistence. If you instead elect to save the changes to the MDS repository, the value is set to oracle.adf.view.rich.change.FilteredPersistenceChangeManager, as shown in Example 35-1.

Example 35-1 Context Parameter in web.xml Used For Change Persistence

<context-param>
  <param-name>org.apache.myfaces.trinidad.CHANGE_PERSISTENCE</param-name>
  <param-value>
    oracle.adf.view.rich.change.FilteredPersistenceChangeManager
  </param-value>
</context-param>

Tip:

If needed, you can manually set this value to oracle.adf.view.rich.change.MDSDocumentChangeManager, if you do not want any customizations to be restricted based on configurations in the adf-config.xml file or on the individual JSF pages, and you always want the changes to be persisted to the MDS repository and not the session.

When you elect to persist to the repository, JDeveloper also does the following:

  • Adds the following JARs to the class path if they don't exist:

    • javatools-nodep.jar

    • facesconfigmodel.jar

    • taglib.jar

  • Ensures the ADFBindingFilter is registered. If not, it adds the following entry to the web.xml file.

    <filter>
      <filter-name>adfBindings</filter-name>
      <filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
    </filter>
    
  • Adds another context parameter to web.xml to register the MDSJSPProviderHelper class to handle merging MDS customization documents with the base JSP document, as shown in Example 35-2

    Example 35-2 Context Parameter in web.xml Used For Merging Changes

    <context-param>
      <param-name>oracle.adf.jsp.provider.0</param-name>
      <param-value>oracle.mds.jsp.MDSJSPProviderHelper</param-value>
    </context-param>
    
  • Adds the ADF Faces Change Manager Runtime 11 library to the project.

  • In the adf-config.xml descriptor file, sets the persistent-change-manager element to the MDSDocumentChangeManager, which is the class that will be used to persist the changes. Example 35-3 shows the configuration for persisting to the MDS repository.

    Example 35-3 Registered ChangeManager Class for Restricted Change Persistence

    <persistent-change-manager>
      <persistent-change-manager-class>
        oracle.adf.view.rich.change.MDSDocumentChangeManager
      </persistent-change-manager-class>
    </persistent-change-manager>
    
  • Creates JSF JSP pages as XML documents. For more information, see Section 35.4, "Controlling User Customizations in Individual JSF Pages."

35.3 Configuring User Customizations

If you choose to persist changes to an MDS repository, you must decide which of the attribute values that are by default persisted to the session (as shown in Table 35-1) should also be persisted to the repository. Alternatively, you can configure which changes you do not want persisted.

Tip:

Often, a system administrator is the one to set the configurations in the adf-config.xml. The persist and dontPersist attributes on a component allow page authors to override that setting as needed.

For example, suppose you decide that you don't want the value for the width attribute on columns to be persisted to the repository, but you do want all other default attribute changes for columns to be persisted. You must explicitly set the other default column values that you want to be persisted, and you also must explicitly configure the application to NOT persist the width attribute.

Note:

If you have created custom user customization capabilities as documented in Section 35.5, "Implementing Custom User Customizations," then you must explicitly declare those attribute values or operations as well.

You set (and unset) these values using the overview editor for the adf-config.xml file. Figure 35-3 shows the overview editor where only certain attribute values for the column component will be persisted.

Figure 35-3 Overview Editor for the adf-config.xml File

overview editor of adf-config.xml

Once set, you can override persistence for a specific component on a page. For example, suppose you want to disallow change on the width attribute on only one table's columns. You want the rest of the tables in the application to persist changes to that attribute. You would configure the columns to globally persist changes to the width attribute, but then for that one table, you would override the global configuration directly on the JSF page. For more information, see Section 35.4, "Controlling User Customizations in Individual JSF Pages."

Note:

If you've enabled just session persistence, then all attribute values shown in Table 35-1 will be persisted to the session. There is no way to override this either globally or on an instance.

35.3.1 How to Configure Change Persistence

By default, when you configure your application to use any type of change persistence (that is, to either the session or a repository), the values of all attributes shown in Table 35-1 will always be persisted to the user's session. If you configured your changes to be persisted to a repository, then you must declare the attributes whose values should be persisted to that repository. If there are any values that you don't want persisted, then you need to configure those values as well.

To declare attribute value persistence to a repository:

  1. In the Application Navigator, expand the Application Resources pane, expand the Descriptor > ADF META-INF node, and double-click adf-config.xml to open that file.

  2. In the overview editor, click the View tab.

  3. In the Tags table, select the component whose changes you want to persist (or not persist) to the repository. If the component does not appear in the table, click the Add icon, and select and add it.

    Note:

    The filter rules specified in the adf-config.xml file are applicable only when you have chosen to persist to the MDS repository (see Section 35.2.1, "How to Enable User Customizations"). These rules do not apply for persistence within session scope.

    If persistence fails for any reason, (for example if one of the filter rules fails or the MDS repository errors), then the values will be stored only within the session scope.

  4. The Tag Attributes table displays all the attributes for the selected component whose values can be persisted. Select Persist Changes for all attributes whose values you want persisted. Deselect any if you do not want the values persisted.

Note:

If you are implementing custom user customizations (see Section 35.5, "Implementing Custom User Customizations"), then you will need to edit the adf-config.xml manually to add the configuration. See Example 35-4 for an example on how to configure user customizations.

35.3.2 What Happens When You Configure Change Persistence

When you select the component tags and attribute values to be persisted in the adf-config.xml file, JDeveloper enters tag library information for the components and attributes that are to be persisted. Example 35-4 shows the entry for persisting the value of the disclosed attribute on the panelBox component.

Example 35-4 Registration of Attribute Changes in adf-config.xml

<taglib-config>
  <taglib uri="http://xmlns.oracle.com/adf/faces/rich">
    <tag name="panelBox">
      <attribute name="disclosed">
        <persist-changes>
          true
        </persist-changes>
      </attribute>
...
    </tag>
  </taglib>
</taglib-config>

35.4 Controlling User Customizations in Individual JSF Pages

Once you have enabled your application to use user customizations, you can control user customizations for specific components on the page.

By default, the framework persists changes for all component instances, based on the configuration in the adf-config.xml file. You can override this default behavior by explicitly setting what should be persisted and what should not be persisted on each component instance using the persist and dontPersist attributes.

Note:

The filter rules specified using the persist and dontPersist attributes are applicable only when you have chosen to persist to the MDS repository (see Section 35.2.1, "How to Enable User Customizations"). These rules do not apply for persistence within session scope.

If persistence fails for any reason, (for example if one of the filter rules fails or the MDS repository errors), then the values will be stored only within the session scope.

The following components support the persist and dontPersist attributes:

  • panelBox

  • showDetail

  • showDetailHeader

  • showDetailItem

  • column

  • tree

  • treeTable

  • panelSplitter

  • calendar

35.4.1 How to Implement User Customizations on a JSF Page

You can override any globally set persistence configuration for a component using its persist and dontPersist attributes.

Tip:

Often, a system administrator is the one to set the configurations in the adf-config.xml. The persist and dontPersist attributes allow page authors to override that setting as needed.

To implement user customizations on a JSF Page:

  1. Add components to the page, as needed, including components that will be persisting changes.

  2. If you want to persist all persistable attributes for a component:

    1. In the Property Inspector, expand the Advanced section.

    2. Click the drop-down list for the Persist field and choose All Available.

  3. If you do not want to persist any attributes, repeat Step 2 for the Don'tPersist field.

  4. If more than one attribute can be persisted for the component, and you do not want to persist all of them:

    1. Click the drop-down menu to the right of the Persist field and choose Edit to open the Edit Property dialog.

    2. Shuttle any attributes to be persisted from Available to Selected.

  5. If you do not want to persist an attribute value, repeat Step 4 for the Don't Persist field.

Note:

The filter rules specified using the persist and dontPersist attributes take precedence over any adf-config.xml configuration set for global component-level restrictions.

Values specified for the dontPersist attribute take precedence over values specified for the persist attribute. For example, if for a panelBox component you set disclosed as the value for both the persist and dontPersist attributes, the value of the disclosed attribute will not be persisted.

If you set the value of the persist or dontPersist attribute to All Available, then any values entered as choices using the Edit dialog and the shuttle will be ignored and all available attribute values will be persisted or not persisted.

35.4.2 What Happens at Runtime: How Changes Are Persisted and Restored

When an application is configured to persist changes to the session, any changes made during the session are recorded in a session variable in a data structure that is indexed according to the view ID and the component's ID attribute value. Every time the page is requested, in the subsequent create View or Restore View phase, all changes are applied in the same order as they were added. This means that the changes registered through the session will be applied only during subsequent requests in the same session.

When an application is configured to persist changes to the MDS repository, any changes made during the session are recorded by mutating the Document Object Model that MDS maintains for the JSP document behind the view. A JSF phase listener registered by ADF controller triggers a commit on the MDS session during the appropriate lifecycle phase, resulting in the change document being persisted in the MDS store. Every time the page is requested, Oracle's JSP engine seeks the JSP document from an MDS JSP provider, which provides a flattened document after merging the stored changes to the base document. MDS records the change against the unique value of the component's ID attribute.

Tip:

If changes are applied in response to a partial submit of the page (for example, a commandButton with the partialSubmit attribute set to true), the component for which changes are applied must be set as the value for the partialTarget attribute.

Additionally, be aware that when you run the application from JDeveloper in the Integrated WebLogic Server, MDS creates a local file-based repository to persist metadata customizations. Whereas when the application is deployed to a test or production environment, customizations are persisted to the configured MDS repository. For more information about MDS repository configuration, see the Oracle Fusion Middleware Administrator's Guide. For more information about deploying an application, see Section 36.4, "Deploying the Application."

35.4.3 What You May Need to Know About Using Change Persistence on Templates, Regions, and Declarative Components

The way that changes are persisted for components in templates, regions, and declarative components is handled differently, depending on whether the changes are persisted to the session or to the MDS repository. With session persistence, changes are recorded and restored on components against the viewId for the given session. As a result, when the change is applied on a component that belongs to one of these objects (region, page template, or declarative component), that change is applicable only in the scope of the page that uses the object. It does not span all pages that consume the object.For example, suppose you have pageOne.jspx and pageTwo.jspx, and they both contain the region defined in region.jsff, which in turn contains a showDetail component. When pageOne.jspx is rendered and the disclosed attribute on the showDetail component changes, the implicit attribute change is recorded and will be applied only for pageOne.jspx. If the user navigates to pageTwo.jspx, no attribute change is applied.

When you persist changes to the MDS repository, MDS records and restores customizations for a document identified by a combination of the JSP page path and customization name/value configuration setting as set on the customization class (for more information, see Section 34.2.1.1, "Customization Classes"). As a result, for a given page that is rendered, when MDS applies a change on a component withinone of these objects (region, template, or declarative object), it is applicable for all pages that consume the object and that have the same customization name and value as the source page.

In the previous example, assume that the showDetail component uses the ID of myShowDetail. When pageOne.jspx is rendered and the disclosed attribute on the showDetail component changes, the attribute change is recorded for region.jsff (and not the page that consumes it). This change is applied when any page that contains the region is rendered, as long as the ID remains the same.

Additionally, user customizations are allowed through the persistence change manager on components that are direct children of a page template or a declarative component only if the definition of the page template or declarative component is not private. If it is private, the customization is written into the session store.

Note:

The definition of the page template or declarative component is a tag-only, nonbindable attribute. For declarative components, it is af:componentDef. For page templates, it is af:pageTemplateDef.

35.5 Implementing Custom User Customizations

In addition to the user customization capabilities built in to certain ADF Faces components, you can create your own custom user customization capabilities. The change persistence framework supports the following types of user customizations:

  • Changing an attribute value

  • Adding or removing a facet

  • Adding or removing a child component

  • Reordering child components

  • Moving a child component to a different parent

To create custom user customizations, you must create a customization class for each type of user customization and then configure your application to use that class. You also need to set up the layers of customization for your application. For more information about both of these procedures, see Section 34.2, "Developing a Customizable Application."

Once those prerequisites are satisfied, you add logic that calls methods on the ADF Faces classes that handle persisting change either to the session or the MDS repository. To handle the change, you create code that uses the APIs from one of the ADF Faces specialized component change classes. For most cases, you add this code to the event handler method on a managed bean associated with the page the persisting component is on. If you want all instances of a component to persist the same change, you need to add this code for each page on which that component appears.

If you are creating a custom component, you can implement user customizations for the component by adding code directly to the custom component class. In that case, you will need to add the code only to the component class, and not once for each instance of the component. For more information, see Section 35.6, "Creating Implicit Change Persistence in Custom Components."

35.5.1 Change Persistence Framework API

To better understand what you need to do to create custom user customizations, it may help to have a deeper understanding of the change persistence and MDS frameworks. When you elect to persist changes to the MDS repository, the change persistence framework works in conjunction with the MDS framework. Where and how the customizations are saved are determined by how you set up your MDS repository, your customization layers, and your customization classes. Details about the MDS framework and the repository and how to use it are covered in Chapter 34, "Customizing Applications with MDS."

The change persistence framework uses the underlying change manager classes from Apache MyFaces Trinidad (in the org.apache.myfaces.trinidad.change package) along with a few ADF Faces-specific classes (in the oracle.adf.view.rich.change package). The instance of the registered ChangeManager class is accessible through the RequestContext object. It is responsible for gathering changes as they are created and added during a request, and then persisting them. The SessionChangeManager class is an implementation of ChangeManager which handles persistence within a session only, while the MDSDocumentChangeManager class is an implementation that persists to the MDS repository only. The FilteredPersistenceChangeManager class is an implementation of ChangeManager that stores the changes that pass the filter rules into the repository using the registered persistence change manager. Any change that does not get persisted to the repository will be persisted to the session when FilteredPersistenceChangeManager is used.

Additional classes are used to describe the changes to a component. You use these APIs to handle persisting any changes to components other than the implicit value changes the ADF Faces framework provides (as shown in Table 35-1). ComponentChange is the base class for all classes used to implement specific changes that act on the JSF component hierarchy, such as adding or removing a facet or a child component. These changes are automatically applied during subsequent creation of the view, in the same order in which they were added. Classes that extend the ComponentChange class and that also implement the DocumentChange interface can directly persist changes to the MDS repository. Classes that do not implement the DocumentChange interface can persist changes only to the session.

Table 35-2 describes the specialized classes that handle specific customizations. If "yes" appears in the Repository column, then the class implements the DocumentChange interface and it can persist changes to the MDS repository.

Table 35-2 Classes Used to Handle Change Persistence

Class Name Repository Description

AddChildDocumentChange

Yes

Adds a child component using document mark up. While applying this change, the child component is created and added to the document.

AttributeComponentChange

No

Changes the value of an attribute.

AttributeDocumentChange

Yes

Changes the value of an attribute.

MoveChildComponentChange

Yes

Moves a child from one container to another.

RemoveChildComponentChange

Yes

Removes a child component.

SetFacetChildComponentChange

No

Adds a child component to the facet using a document markup. While applying this change, the markup will be added to the document.

SetFacetChildDocumentChange

Yes

Adds a child component to a facet. While applying this change, the DOM element corresponding to the child component is added to the document. If the facet doesn't exist, it will be created. If the facet does exist, all of its content will be removed and the new content added.

RemoveFacetComponentChange

Yes

Removes a facet.

ReorderChildrenComponentChange

Yes

Reorders children of a component.


Aside from a ChangeManager class, you may also need to implement and register the DocumentChangeFactory interface with the ChangeManager class. If the DocumentChangeFactory implementation can provide an equivalent DocumentChange for a ComponentChange, the ChangeManager will use it to persist the DocumentChange to the repository.

35.5.2 How to Create Code for Custom User Customizations

You need to add code to handle any explicit changes you want to create, and to configure the components on the JSF page to handle customization. As with the default user customizations, you also must register the custom changes in the adf-config.xml file.

Note:

When the changes are expressible in more than one form, the change must be recorded in the form with highest precedence. For example:
  • Attribute change for a component: The attribute can be specified on the component tag or it can be expressed using the <f:attribute> tag. In a JSF JSP document, <f:attribute> takes lesser precedence over the attribute specified on the component tag. Therefore, the attribute change on the component tag will be recorded for customization.

  • Column header text in a column component: The header text for the column can be specified using either the headerText attribute or using header facet. In this case, the facet component will have precedence.

To create custom user customizations:

  1. Create a managed bean for the page that contains the component.

  2. Add code to the event handler method for the component that will be used to make the change. This code should obtain the component that contains the change. It should then use the component and the appropriate APIs to create, record, and persist the change.

    Example 35-5 shows the code on the action event handler for a command button for a change that will be persisted to the MDS repository. When a user clicks the button, that source graphic file changes. The event handler method accesses the component and changes the source attribute for the graphic. It then calls the private addAttributeChange method, which first uses the component API to record the change, and then uses the AttributeComponentChange class to set the new source attribute value.

    Example 35-5 Persisting Change to the Repository from an Event Handler on a Managed Bean

    public void modifyObjectImage(ActionEvent event) {
           UIComponent uic = event.getComponent().findComponent("oi1");
           String source = "/images/mediumAd.gif";
           uic.getAttributes().put("source", source);
           _addAttributeChange(uic, "source", source);
        }
    .
    .
    .
        private static void _addAttributeChange(UIComponent uic, String attribName, 
                                                Object attribValue) {
            FacesContext fc = FacesContext.getCurrentInstance();
            ChangeManager cm = 
                RequestContext.getCurrentInstance().getChangeManager();
            ComponentChange cc = 
                new AttributeComponentChange(attribName, attribValue);
            apm.addComponentChange(fc, uic, cc);
        }
    

    Note:

    When you persist changes, in addition to explicitly recording a change on the component (which is done in Example 35-5 using uic.getAttributes().put("source", source) method), you must also directly apply the change using the component API, as was done using the private _addAttributeChange(uic, "source", source) method. Applying the change in this way allows the user to see the change in response to the same request. If the change is recorded on the component, then the change will not be seen until a subsequent request.

    Additionally, if you know that the component will always persist to the repository regardless of any restricted change persistence settings, you can instead call the AdfFacesContext.getCurrentInstance().
    getPersistentChangeManager()
    method.

  3. The ChangeManager class provides support for automatically converting an AttributeComponentChange into an AttributeDocumentChange, thereby allowing persistence to a repository. However, if you need to convert another type of change and you use a specialized change manager class that does not implement the DocumentChange class, you need to create a custom DocumentFactory implementation that converts the component change to a document change.

    Note:

    Automatic conversion of AttributeComponentChange into an AttributeDocumentChange assumes that the component attribute is represented as an attribute of the same name on the associated element in the JSPX document.

    Only those attribute values that are expressible in the JSPX document can be persisted using AttributeDocumentChange. In other words, CharSequence, Number, Boolean and ValueExpression are the only supported data types.

    Only values that implement java.io.Serializable can be persisted using AttributeComponentChange.

  4. If you create a custom DocumentFactory implementation, you need to register it with the appropriate change manager class using the following method in your bean:

    public static void registerDocumentFactory(String targetClassName, 
                                               String converterClassName)
    

    Where targetClassName is the name of the ComponentChange class and converterClassName is the name of your DocumentChangeFactory extension that is capable of converting the target ComponentChange into a DocumentChange. The semantics of name for these classes is same as that of getName() in the java.lang.Class class.

  5. If the class you use to create the component change adds a child that has a subtree of components, and you want to persist the changes to the repository, you must create a DocumentFragment to represent the change.

    Example 35-6 shows how to use the AddComponentDocumentChange specialized class to create a DocumentChange object and use a DocumentFragment to represent the change.

    Example 35-6 Converting a ComponentChange Object to a DocumentChange Object

    public void appendChildToDocument(ActionEvent event)
    {
      UIComponent eventSource = event.getComponent();
      UIComponent uic = eventSource.findComponent("pg1");
      // only allow the image to be added once
      if (_findChildById(uic,"oi3") != null)
        return;
      FacesContext fc = FacesContext.getCurrentInstance();
      DocumentFragment imageFragment = _createDocumentFragment(_IMAGE_MARK_UP);
      DocumentChange change = new AddChildDocumentChange(imageFragment);
      ChangeManager apm = AdfFacesContext.getCurrentInstance().getChangeManager();
      apm.addDocumentChange(fc, uic, change);
    }
     private static final String _IMAGE_MARK_UP =
     "<af:objectImage id='oi3' height='100' width='120' " +
         "source='http://www.somewhere.com/someimage.jpg' " +
         "xmlns:af='http://xmlns.oracle.com/adf/faces'/>";
     
    private static DocumentFragment _createDocumentFragment(
        String markUp)
    {
     // prepend XML declaration
      markUp = "<?xml version = '1.0' encoding = 'ISO-8859-1'?>" + markUp;
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(true);
      factory.setValidating(false);
      DocumentBuilder builder;
      try
      {
        builder = factory.newDocumentBuilder();
      }
      catch (ParserConfigurationException pce)
      {
        _LOG.log(Level.WARNING, "Unable to get XML Parser:", pce);
         return null;
      }
      try
      {
        // use a version explicitly with ISO-8859-1 instead
        byte[] markupBytes = markUp.getBytes();
        Document newDoc = builder.parse(new ByteArrayInputStream(markupBytes));
        DocumentFragment fragment = newDoc.createDocumentFragment();
        // add the document's root element to the fragment
        fragment.appendChild(newDoc.getDocumentElement());
        return fragment;
      }
      catch (SAXException se)
      {
        _LOG.log(Level.WARNING, "Unable to parse markup:" + markUp, se);
        return null;
      }
      catch (IOException ioe)
      {
        _LOG.log(Level.WARNING, "IO Problem with markup:" + markUp, ioe);
        return null;
      }
    }
    
  6. Register the user customizations in the adf-config.xml file, as documented in Section 35.3, "Configuring User Customizations." If the custom changes are of any type other than AttributeDocumentChange, you will need to manually edit the adf-config.xml file and indicate that all changes are allowed for the component, as shown in Example 35-7.

    Example 35-7 Custom Registration in adf-config.xml

    <tag name="inputText">
      <attribute name="label">
        <persist-changes>true</persist-changes>
      </attribute>
      <persist-operations>ALL</persist-operations>
    </tag>
    

35.6 Creating Implicit Change Persistence in Custom Components

When you create a custom component, you may decide that you want certain attribute values on that component to be persisted whenever change persistence is enabled in an application. Setting implicit change on a custom component is similar to setting explicit change persistence on existing components. You add code that executes the actual persistence, but instead of you placing that code on a managed bean, that code can be handled directly by the component class. If your component's attribute values are synchronized with the server using events, then you can use the broadcast method to persist the changes. If the attribute value that you want to persist does not use events, then you need to add code in the renderer and component class.

35.6.1 How to Set Implicit Change Persistence For Attribute Values that Use Events

When an attribute value uses events, you need to add code to the component class.

To set implicit change persistence for attribute values that use events:

  1. Open the custom component class java file.

  2. Add code to the broadcast method that will use the specialized class to create a new ComponentChange object and then call the ChangeManager to add the change.

    Example 35-8 shows the code added to the UIXShowDetail class that persists a change to the disclosed attribute. In this case, the AttributeComponentChange class is used.

    Example 35-8 Persisting Change from a Component Class

    public class UIXShowDetail extends UIXComponentBase
    {
      ...
      public void broadcast(FacesEvent event) throws AbortProcessingException
      {
        super.broadcast(event);
        ...
        if (event instanceof DisclosureEvent)
        {
          boolean isDisclosed = ((DisclosureEvent) event).isExpanded();
          setDisclosed(isDisclosed);
          //Record a Change for 'disclosed' attribute
          AttributeComponentChange aa =
           new AttributeComponentChange('disclosed', isDisclosed ? Boolean.TRUE : Boolean.FALSE);
          AdfFacesContext adfContext = AdfFacesContext.getCurrentInstance();
          adfContext.getChangeManager().addComponentChange(getFacesContext(), this, aa);
          ...
        }
      }
      ...
    

35.6.2 How to Set Implicit Change Persistence For Other Attribute Values

When an attribute does not use events, you need to place code in the component's renderer class.

To set implicit change persistence for other attribute values:

  1. Open the custom component's render class java file.

  2. Use the findTypeConstants method, which takes a ClientMetadata instance and use the addPersistedProperty method to mark certain properties as persisted. Example 35-9 shows a code snippet from the renderer class used for the ADF Faces PanelSplitter component, which implicitly persists the splitterPosition attribute value.

    Example 35-9 Method in Component Renderer Class to Implicitly Persist Changes

    // Code snippet from PanelSplitterRenderer.java  
    protected void findTypeConstants(
        FacesBean.Type type,
        ClientMetadata metadata)
      {
        super.findTypeConstants(type, metadata);
        metadata.addRequiredProperty(
          _orientationKey = type.findKey("orientation"));
        metadata.addRequiredProperty(
          _positionedFromEndKey = type.findKey("positionedFromEnd"));
        metadata.addRequiredProperty(
          _disabledKey = type.findKey("disabled"));
        metadata.addRequiredProperty(
          _splitterPositionKey = type.findKey("splitterPosition"));
        metadata.addPersistedProperty(_splitterPositionKey);
      }
    
  3. In the JavaScript component peer class, define the attribute value to be persisted, using the setProperty function. This function needs to be invoked with the attribute name (as defined in the renderer in the previous step), the value, and "true", meaning the value of the attribute will be set. Example 35-10 shows a code snippet from the panelSplitter class that sets the splitter position.

    Example 35-10 Method in Component Class to Implicitly Persist Changes

    // Code snippet from AdfDhtmlPanelSplitterPeer.js file where we set the 
       splitter position
    
      var component = this.getComponent();
     component.setProperty("splitterPosition",position, true);
    // position is the value to be set