X

Technical Articles relating to Oracle Development Tools and Frameworks

ADF - Saying No to the Change Manager

Duncan Mills
Architect

A common problem that we get asked about relates to one of the more powerful and less understood aspects of the ADF Faces framework, the Change Manager.  The Change Manager works behind the scenes to remember things for you, things like:

  • What order are my table columns in? 
  • How wide did I stretch that column last time I looked at this table?
  • Which tab did I have selected when I last viewed this screen?

And so on...

Most of the time you don't realise that the Change Manager is doing this for you, it just happens thank you very much!  Of course where the real; power comes in is where you actually persist this memory across sessions using Meta Data Services. See Chapter 50 (Customizing Applications with MDS) and Chapter 51 (Allowing User Customizations at Runtime)  of the Oracle Fusion Middleware - Developing Fusion Web Applications with Oracle Application Development Framework in the Documentation for more information on all of this this.   

The Problem 

So what's the issue here?  Well it relates to the fact that the Change Manager is sometimes a little over enthusiastic in what it remembers. A common example is where you always want to enter a task flow and show the first tab in a tab-set to the user. However, the Change Manager remembers that the user has been here before and dutifully sets things up so that the last tab that the user visited on the last trip through this task flow is the one that is displayed.  Sometimes this just does not meet the requirements of the UI.  So how do we override that? 

From the perspective of using MDS, we can control what gets persisted about a view by configuring the adf-config.xml file as part of the MDS setup.  This gives you a UI through which you can indicate exactly which properties of which component should be persisted across Sessions:

MDS persistence configuration through the adf-config editor  

But this configuration only relates to cross-session persistence, it does not effect the persistence of memory within the lifetime of a single session. 

Just Say No!

OK, we understand the problem, but is there anything we can do about it?  Fortunately the answer is yes, on any given component instance you can add a change filter that will be given the opportunity to inspect the change in question and decide if it should be remembered or not.  This, as you might have guessed involves creating a little code.

As a sample, here's the common case where we want to not remember that a tab (showDetailItem) in a tabset has been selected (disclosed) :


package oracle.adf.demo;

import javax.faces.component.UIComponent;
import oracle.adf.view.rich.component.rich.layout.RichShowDetailItem;
import org.apache.myfaces.trinidad.change.AttributeComponentChange;
import org.apache.myfaces.trinidad.change.ComponentChange;
import org.apache.myfaces.trinidad.change.ComponentChangeFilter;

public class RejectTabComponentChangeFilter extends ComponentChangeFilter {

    public RejectTabComponentChangeFilter() {
        super();
    }

    public ComponentChangeFilter.Result accept(ComponentChange componentChange,
                                               UIComponent uIComponent) {
        if (uIComponent instanceof RichShowDetailItem && 
            "disclosed".equals( 
               ((AttributeComponentChange)componentChange).getAttributeName())){
            return ComponentChangeFilter.Result.REJECT;
        }
        else {
            return ComponentChangeFilter.Result.ACCEPT;
        }
    }
}

Basically you create a simple class which extends the org.apache.myfaces.trinidad.change.ComponentChangeFilter and override the accept() method with your logic.  This method will be called each time that the Change Manager wishes to persist a particular property change, and it will be passed information about the change and the component that the change relates to. The return from the accept call is either ACCEPT or REJECT as you wish.

In this case, I've just illustrated how a single change type on a single type of component can be filtered out, but of course you have enough information passed into the accept() method to have it check for and reject multiple change types for multiple components.

How Do I use it?

The last part of the puzzle is how this should be wired up. Unfortunately it does not just drop in and magically work.  To use the filter class you need to add it to each component instance that might have a change you want to veto.  To do this you can use the addComponentChangeFilter() API which is present on the base class that all the ADF components use (UIXComponentBase).  So for example: 

((UIXComponentBase)tabItem).addComponentChangeFilter(new RejectTabComponentChangeFilter());

When you call the add API is up to you, it can be part of your backing bean code for a particular view, or perhaps you would use a phase listener to more generally call the addComponentChangeFilter() for all components of a certain type on a particular view1. If you take the latter approach, you just want to take care and only add the change filter if it's not already there (remember the lifecycle phases would get called with every post-back to the page). To help with this you can check what's already installed on a particular component by calling the getComponentChangeFilters() API which will return you an array of what filters are currently applied (may be multiple).


1 Now would be a great time to read my article on using the visitTree() API to iterate though the components in a view to detect which components you want to add the change filters to.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha