Sometimes I try to do things but it just doesn't work out the way I want it to, and I get real frustrated and then like I try hard to do it, and I like, take my time but it just doesn't work out the way I want it to. Its like, I concentrate on it real hard, but it just doesn't work out. And everything I do and everything I try, it never turns out. Its like, I need time to figure these things out.
--Institutionalized by Suicidal Tendencies
This blog is for people who feel this way about JSF. Or who just want a Pepsi.

Wednesday, March 31, 2010

Changes Part 2--The MDS ChangeManager

As discussed in Part 1, Oracle ADF Faces ships with an Apache Trinidad ChangeManager implementation that stores the changes persistently across user sessions using the Oracle Metadata Services (MDS).
Actually configuring this is easy using JDeveloper. Open up your project's properties. Select the "ADF View" category. Select "Enable User Customizations" and "Across Sessions using MDS".

However the actual result seems to make no sense:
To web.xml this added:
<context-param>
<param-name>org.apache.myfaces.trinidad.CHANGE_PERSISTENCE</param-name>
<param-value>oracle.adf.view.rich.change.FilteredPersistenceChangeManager</param-value>
</context-param>
And to adf-config.xml, it added:
<persistent-change-manager>
<persistent-change-manager-class>oracle.adf.view.rich.change.MDSDocumentChangeManager</persistent-change-manager-class>
</persistent-change-manager>

Huh?
The issue is that the MDSDocumentChangeManager persists everything to MDS. But we don't want that for a number of reasons.
  1. The current implementation of reading changes applied through MDS rereads and re-reads the entire document. This isn't fast (it would be even slower in the case of JSPs if Oracle didn't use a special JSP engine that interprets the document instead of recompiling it), so we only want to use MDS for the specific changes that users want persisted across sessions (say that layout of their configurable home page).
  2. MDS works by applying deltas to the page's XML document. If there is no way to encode the current value into the document, MDS can't record a change. An example of this are attributes, like the set of expanded nodes in a tree that are only accessed through the Expression Language
  3. MDS security may prevent customizations form being saved for some parts of the document
  4. MDS can't record changes for components that don't have their id attribute set
So, instead of registering the MDSChangeManager with Trinidad directly, we register the FilteredPersistenceChangeManager instead. This ChangeManager implementation handles routing the changes to either the SessionChangeManager or MDSDocumentChangeManager as appropriate. The FilteredPersistenceChangeManager uses the following ordered rules to determine which ChangeManager to persist to:
RulePersist To
Is the change unrepresentable as a change to the page's document?Session
Did attempting to write the change to MDS fail?Session
Is this an attribute change and is the name of the attribute listed in the component's persist attribute?MDS
Is this an attribute change and is the name of the attribute lsited in the componen'ts dontPersist attributeSession
Does this change match an application-wide rule specified in adf-config.xmlMDS
All other casesSession

OK, actually I've oversimplified things. The FilteredPersistenceChangeManager actually writes all of the persistent changes to whatever ChangeManager is returned by AdfFacesContext.getPersistentChangeManager(), which we configured to point to the MDSDocumentChangeManager in our changes to adf-config.xml. (You are probably thinking 'What the hell! Why didn't they just make the MDSDocumentChangeManager work like this all of the time instead of making me perform this extra whacked configuration. Well, at one point internally, it did. But then we had customers, like Oracle Web Center that needed access to an unfiltered version of the persistent ChangeManager. Since I love composition, we went with this approach)
So, even after you have configured ADF Faces to use MDS to store changes, you still need to actually perform some addition configuration to actually get changes stored in MDS rather than in the Session. While you could use the rules in adf-config.xml, for performance reasons, I prefer setting the persist attribute. To remember in MDS whether a showDetailItem is disclosed for a particular instance , you would use:
<af:showDetailItem id="showOff" text="Show Me" persist="disclosed">
... content ...
</af:showDetailItem>
I'll cover random weirdness and gotchas using the various ChangeManagers in part 3.

Changes Part 1--The Trinidad ChangeManager

The Apache Trinidad JSF framework supports the concept of a ChangeManager--a service for automatically persisting changes to components during the Session or persistently across sessions. Applications typically don't call the ChangeManager directly, rather they configure the ChangeManager once in web.xml:
  <context-param>
    <description>
      This parameter turns on the session change persistence.
    </description>
    <param-name>org.apache.myfaces.trinidad.CHANGE_PERSISTENCE</param-name>
    <param-value>session</param-value>
  </context-param>
and rely on the built in support for the ChangeManager in the various UIComponents. Components typically write changes made to UIComponent attributes as a result of user interaction. For example, a tab controls remember the currently selected tab and splitters remember the splitter position. More complicated components, such as tables use the ChangeManager to support column reordering. Out-of-the-box, Trinidad supplies one ChangeManager--the SessionChangeManager, which remember changes for the lifetime of the current user Session. Custom implementations may be specified by specifying the fully-qualified class name of a ChangeManager implementation for the <param-value>
  <context-param>
    <description>
      Store my changes in GMail.  Just because.
    </description>
    <param-name>org.apache.myfaces.trinidad.CHANGE_PERSISTENCE</param-name>
    <param-value>org.example.GMailChangeManager</param-value>
  </context-param>
Oracle ADF Faces ships with an additional ChangeManager implementation, that stores changes across Sessions using the Oracle Metadata Services. We will discuss how to configure the MDS change manager in part 2.