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.

Friday, February 26, 2010

References to UIComponents in Session-scoped Beans

JSF developers love session-scoped managed beans. I think this is because, like the perfect significant other, a session-scoped bean is always there for you. Later they realize that their session-scoped bean is more like a stalker--a stalker with threading and serialization issues that loafs on the couch consuming memory. But, in the first blush of ardor, when the developer wants to do everything with their new session-scoped bean, they might write some code that looks like this:
import java.util.List;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.apache.myfaces.trinidad.component.UIXSelectMany;

/**
 *  Maintains a list of weather forecasts for locations for this user
 */
public final class WeatherBean {
  /** Set the component binding for the selectMany component */
  public void setRemoveLocationsComponent(UIXSelectMany removeComp) {
    _removeLocationsComp = removeComp;
  }

  /** Get the selectMany component containing the locations to remove */
  public UIXSelectMany getRemoveLocationsComponent() {
    return _removeLocationsComp;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This sets what is checked in the checkbox. */
  public void setLocationsToRemove(List<String> value) {
    _locationsToRemove = value;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This gets what checked in the checkbox. */
  public List<String> getLocationsToRemove() {
    return _locationsToRemove;
  }

  public void removeLocationListener(ActionEvent actionEvent) {
    // get the list of selected items in the selectManyCheckbox.
    // Then, loop through the locations and pull these out
    // of the location list and refresh.
    List<String> locationsToRemove = getLocationsToRemove();

    for (String location : locationsToRemove) {
      _removeLocation(location);
    }

    // force refresh of component value
    UIXEditableValue component = getRemoveLocationsComponent();

    component.setSubmittedValue(null);
    component.setValue(null);
    component.setLocalValueSet(false);
  }

  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List<SelectItem> getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  private void  _removeLocation(String location) {
    ... remove the location from our list of Locations we have weather for ...
  }

  private UIXSelectMany _removeLocationsComponent;
  private List<String>  _locationsToRemove;

  // list of locations to display weather for
  private List<String>  _locations;
}
And use it in a page like this:
<!-- Display list of checkboxes of current locations -->
<tr:selectManyCheckbox label="Locations"
                       id="smc1"
                       valuePassThru="true"
                       binding="#{weatherBean.removeLocationsComponent}"
                       value="#{weatherBean.locationsToRemove}">
  <f:selectItems value="#{weatherBean.locationSelectItems}" id="si1"/>
</tr:selectManyCheckbox>

<!-- Button to delete the selection locations -->
<tr:commandButton id="deleteCB"
                  text="Remove Locations"
                  actionListener="#{weatherBean.removeLocationListener}"/>
The example is from an application that displays weather for various locations using the Apache Trinidad JSF Components. This fragment of the application displays checkboxes for the user's current registered locations, allowing the user to select the locations to remove from the list stored in the _locations field. When the tags for the page are executed, the component binding of the removeLocationsComponent sets the component instance on the session-scoped WeatherBean instance. Then when the user clicks the "Remove Locations" button, the set of location Strings to remove is set on the WeatherBean by the ValueExpression #{weatherBean.locationSelectItems} during the update model JSF phase and the WeatherBean's removeLocationListener(ActionEvent) ActionListener is called during the invoke application JSF phase to remove the list of locations saved earlier from the displayed list of locations. This code works well when tested on the developer's machine. However, the relationship soon sours. There are two separate sets of problems here--Serialization problems and thread safety problems.

Serialization and Session-scoped Beans

When deployed in a clustered environment, Session-scoped beans need to be Serializable so that any beans changed during a request can be distributed to other servers for fail-over. At the end of the first request in a clustered environment, the above code fails with a NotSerializableException. (Since testing in a cluster is often a pain, Trinidad developers often test their applications with the -Dorg.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION=session,tree System property set so that Trinidad aggressively checks the serializability of properties stored into both the session Map and as attributes on the UIComponents in the component tree) This problem is apparently easily fixed by making the WeatherBean Serializable:
public final class WeatherBean implements Serializable{

 ... existing content ...

  private static final long serialVersionUID = 0L;
}
Unfortunately, this doesn't work. The class still isn't Serializable, because UIComponents aren't Serializable, so the serialization fails on the _removeLocationsComp field of WeatherBean. We could try to patch things up by marking _removeLocationsComp transient, but we have only scratched the surface of our problems.

Thread Safety Problems in the Example

There are a whole raft of concurrency and memory leak issues here:
  1. If the user opens two windows on this page, only the last window rendered has the correct UIXSelectMany instance. If the remove locations button is pressed on the first window after rendering the second window, the wrong UIXSelectMany instance will be used.
  2. Since UIComponents aren't thread-safe, the above case could also result in bizarre behavior if requests from both windows were being processed by the application server at the same time.
  3. In the above case, there could be other issues because the _removeLocationsComponent field isn't safely published across the Threads reading and writing its value
  4. If the Trinidad view state token cache isn't used, or if the user navigates to this page using the back button, a new UIXSelectMany instance will be created to handle the request, which also won't match the instance we are holding onto.
  5. If we don't clear the UIXSelectMany instance when we navigate off of this page, we will continue to pin the page's UIComponent tree in memory for the lifetime of the Session.
Well that sucks. I guess it's time to break up.

Breaking Up isn't that Hard to Do

We're breaking our session-scoped bean into two pieces. A request-scoped piece and a session-scoped piece. We move our UIComponent reference and the list of locations to remove (which also had concurrency problems) into our new request-scoped bean:
/** Stores state used for removing Locations at request scoped */
public final class RemoveLocationState {
  /** Set the component binding for the selectMany component */
  public void setRemoveLocationsComponent(UIXSelectMany removeComp) {
    _removeLocationsComp = removeComp;
  }

  /** Get the selectMany component containing the locations to remove */
  public UIXSelectMany getRemoveLocationsComponent() {
    return _removeLocationsComp;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This sets what is checked in the checkbox. */
  public void setLocationsToRemove(List<String> value) {
    _locationsToRemove = value;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This gets what checked in the checkbox. */
  public List<String> getLocationsToRemove() {
    return _locationsToRemove;
  }

  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List<SelectItem> getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  private UIXSelectMany _removeLocationsComponent;
  private List<String>  _locationsToRemove;
}
We then change our Session-scoped bean to use the state stored in the request-scoped bean. For a request-scoped bean named "weatherRemovalState", the code would look like this:
public final class WeatherBean implements Serializable {
  public void removeLocationListener(ActionEvent actionEvent) {

    // get the request-scoped weather removal state
    ExernalContext extContext =
                       FacesContext.getCurrentInstance().getExternalContext();

    RemoveLocationState removeState = (RemoveLocationState)
                        extContext.getRequestMap().get("weatherRemovalState");

    // get the list of selected items in the selectManyCheckbox.
    // Then, loop through the locations and pull these out
    // of the location list and refresh.
    List<String> locationsToRemove = removeState.getLocationsToRemove();

    for (String location : locationsToRemove) {
      _removeLocation(location);
    }

    // force refresh of component value
    UIXEditableValue component = removeState.getRemoveLocationsComponent();

    component.setSubmittedValue(null);
    component.setValue(null);
    component.setLocalValueSet(false);
  }

  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List&lt;SelectItem&gt; getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  private void  _removeLocation(String location) {
    ... remove the location from our list of Locations we have weather for ...
  }

  // list of locations to display weather for
  private List<String>  _locations;

  private static final long serialVersionUID = 0L;
}
And we change to component binding and ValueExpression to use our request-scoped "weatherRemovalState" bean so that our page looks like this:
<!-- Display list of checkboxes of current locations -->
<tr:selectManyCheckbox label="Locations"
                       id="smc1"
                       valuePassThru="true"
                       binding="#{weatherRemovalState.removeLocationsComponent}"
                       value="#{weatherRemovalState.locationsToRemove}">
  <f:selectItems value="#{weatherBean.locationSelectItems}" id="si1"/>
</tr:selectManyCheckbox>

<!-- Button to delete the selection locations -->
<tr:commandButton id="deleteCB"
                  text="Remove Locations"
                  actionListener="#{weatherBean.removeLocationListener}"/>
Now a new instance of RemoveLocationState named "weatherRemovalState" is created on each request when the "Remove Locations" button is pressed. Since this instance is only referenced by the session-scoped WeatherBean from the request thread, we don't have any synchronization issues here. Likewise, since the instance falls away at the end of the request, we don't have any serialization of memory leak issues. But why stop there?

There's Never Too Much of a Good Thing

The only state that we really need to maintain at the Session level is the set of locations to display. Plus, we probably want to maintain the list of locations plus any other preferences (like whether we prefer fahrenheit or celcius) across sessions. We'll make WeatherBean request-scoped and add a new WeatherPreferences bean holding and persisting our preferences that is session-scoped:
/**
 *  Request-scoped bean controlling the weather,
 *  though not as well as Storm
 */
public final class WeatherController {
  /** Set the component binding for the selectMany component */
  public void setRemoveLocationsComponent(UIXSelectMany removeComp) {
    _removeLocationsComp = removeComp;
  }

  /** Get the selectMany component containing the locations to remove */
  public UIXSelectMany getRemoveLocationsComponent() {
    return _removeLocationsComp;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This sets what is checked in the checkbox. */
  public void setLocationsToRemove(List<String> value) {
    _locationsToRemove = value;
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This gets what checked in the checkbox. */
  public List<String> getLocationsToRemove() {
    return _locationsToRemove;
  }

  public void removeLocationListener(ActionEvent actionEvent) {
    // get the preferences for the current session
    WeatherPreferences preferences = 
               WeatherPreferences.getCurrentWeatherPreferences();

    // get the list of selected items in the selectManyCheckbox.
    // Then, pull these out of the location list and refresh.
    List<String> locationsToRemove = getLocationsToRemove();

    preferences.removeLocations(locationsToRemove);

    // force refresh of component value
    UIXEditableValue component = getRemoveLocationsComponent();

    component.setSubmittedValue(null);
    component.setValue(null);
    component.setLocalValueSet(false);
  }

  private UIXSelectMany _removeLocationsComponent;
  private List<String>  _locationsToRemove;
}


/**
 *  Maintains the Session-scoped user preferences for weather
 */
public final class WeatherPreferences implements Serializable {
  /**
   * Retrieves the WeatherPreferences for this Session, loading it
   * if necessary
   */
  public static WeatherPreferences getCurrentWeatherPreferences()
  {
    FacesContext context = FacesContext.getCurrentInstance();
  
    // get the weather preferences from the Session if it has already been instantiated
    Object preferences = context.getExternalContext().getSessionMap().get("weatherPreferences");

    // OK, not is Session, instantiate it using the ELResolver
    if (preferences == null)
      preferences = context.getApplication().getELResolver().getValue(
                        context.getELContext(), null, "weatherPreferences");

    return (WeatherPreferences)preferences;    
  }


  private WeatherPreferences()
  {
    List<String> locations = ... load preferences from repository ...;

    _locations = new CopyOnWriteArrayList<String>(locations);
  }


  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List<SelectItem> getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  /**
   * Remove the locations from the list of locations weather
   * is displayed for */
  public void  removeLocations(Collection<String> removeLocations){
    _locations.removeAll(removeLocations);
  }

  private static final long serialVersionUID = 0L;


  // list of locations to display weather for
  private final List<String>  _locations;
}
And the markup changed to use the request-scoped "weatherController" and session-scoped "weatherPreferences" beans.
<!-- Display list of checkboxes of current locations -->
<tr:selectManyCheckbox label="Locations"
                       id="smc1"
                       valuePassThru="true"
                       binding="#{weatherController.removeLocationsComponent}"
                       value="#{weatherController.locationsToRemove}">
  <f:selectItems value="#{weatherPreferences.locationSelectItems}" id="si1"/>
</tr:selectManyCheckbox>

<!-- Button to delete the selection locations -->
<tr:commandButton id="deleteCB"
                  text="Remove Locations"
                  actionListener="#{weatherController.removeLocationListener}"/>
Now everything is request-scoped except for the code that handles the persistence and threading issues of maintaining the Locations for this user session. While, in this case, we have discussed Session-scoped beans. Similar problems can occur with all scopes longer than request--application scope. The PageFlowScope of Apache Trinidad and Oracle ADF has these problems to a lesser extent. The ViewScope of JSF 2, Trinidad and ADF even less so. In general, bean refactoring is the best solution to the problem, but here are other slightly hacky ways of getting away with one bean.

But All I Need is One Bean to Make Me Happy

The trick to geting away with a single bean is removing the request scoped state from our session-scoped bean. In our original case, these two fields:
  private UIXSelectMany _removeLocationsComponent;
  private List<String>  _locationsToRemove;
The first step is realizing that we can get rid of _locationsToRemove by retrieving the value directly from the component instance rather than binding it. We then lose _removeLocationsComponent by using UIComponent.findComponent to retrieve our instance:
/**
 *  Maintains a list of weather forecasts for locations for this user
 */
public final class WeatherBean implements Serializable{
  public void removeLocationListener(ActionEvent actionEvent) {
    // get the list of selected items in the selectManyCheckbox.
    // and remove them
    List<String> locationsToRemove = _getLocationsToRemove();
    _removeLocations(locationsToRemove);

    // force refresh of component value
    UIXEditableValue component = getRemoveLocationsComponent();

    component.setSubmittedValue(null);
    component.setValue(null);
    component.setLocalValueSet(false);
  }

  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List<SelectItem> getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  /** Get the selectMany component containing the locations to remove */
  private UIXSelectMany _getRemoveLocationsComponent() {
    // find our UIXSelectMany by its id.  If the NamingContainer
    // hierarchy of the page changes, we're toast
    UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
    return (UIXSelectMany)viewRoot.findComponent("smc1");
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This gets what checked in the checkbox. */
  private List<String> getLocationsToRemove() {
    return (List<String>)_getRemoveLocationsComponent().getValue();
  }

  private void  _removeLocations(Collection<String> locations) {
    _locations.removeAll(locations);
  }

  private static final long serialVersionUID = 0L;

  // list of locations to display weather for
  private List<String>  _locations;
}
And the markup:
<!-- Display list of checkboxes of current locations -->
<tr:selectManyCheckbox label="Locations"
                       id="smc1"
                       valuePassThru="true">
  <f:selectItems value="#{weatherBean.locationSelectItems}" id="si1"/>
</tr:selectManyCheckbox>

<!-- Button to delete the selection locations -->
<tr:commandButton id="deleteCB"
                  text="Remove Locations"
                  actionListener="#{weatherBean.removeLocationListener}"/>

This works, but the use of findComponent leaves something to be desired. Hard-coding the NamingContainer path to the component is a little fragile. In addition, findComponent can be a little slow. These issues can be avoided in the future by using the Trinidad ComponentReference instead.

You're the One That I Want

ComponentReference acts as a fast, safe pointer to a UIComponent. Rewriting the above code with ComponentReference gives us:
/**
 *  Maintains a list of weather forecasts for locations for this user
 */
public final class WeatherBean implements Serializable{
  /** Set the component binding for the selectMany component */
  public void setRemoveLocationsComponent(UIXSelectMany removeComp) {
    // we only need to create a new reference the first time since
    // all other references for this page would be identical
    if (_selectManyReference == null)
      _selectManyReference = UIComponentReference.newUIComponentReference(removeComp);
  }

  /** Get the selectMany component containing the locations to remove */
  public UIXSelectMany getRemoveLocationsComponent() {
    // get the UIXSelectMany from the reference
    return _selectManyReference.getComponent();
  }

  public void removeLocationListener(ActionEvent actionEvent) {
    // get the list of selected items in the selectManyCheckbox.
    // and remove them
    List<String> locationsToRemove = _getLocationsToRemove();
    _removeLocations(locationsToRemove);

    // force refresh of component value
    UIXEditableValue component = getRemoveLocationsComponent();

    component.setSubmittedValue(null);
    component.setValue(null);
    component.setLocalValueSet(false);
  }

  /** Lists the current locations in a SelectItem List so
   *  that it can be displayed as a select many checkbox. */
  public List<SelectItem> getLocationSelectItems() {
    ... turn List of Locations into a List of SelectItems ...
  }

  /** The selectManyCheckbox's value is bound to this.
   *  This gets what checked in the checkbox. */
  private List<String> getLocationsToRemove() {
    return (List<String>)getRemoveLocationsComponent().getValue();
  }

  private void  _removeLocations(Collection<String> locations) {
    _locations.removeAll(locations);
  }

  private static final long serialVersionUID = 0L;

  // list of locations to display weather for
  private List<String>  _locations;
  private volatile ComponentReference<UIXSelectMany> _selectManyReference;
}

<!-- Display list of checkboxes of current locations -->
<tr:selectManyCheckbox label="Locations"
                       id="smc1"
                       valuePassThru="true"
                       binding="#{weatherBean.removeLocationsComponent}">
  <f:selectItems value="#{weatherBean.locationSelectItems}" id="si1"/>
</tr:selectManyCheckbox>

<!-- Button to delete the selection locations -->
<tr:commandButton id="deleteCB"
                  text="Remove Locations"
                  actionListener="#{weatherBean.removeLocationListener}"/>
Here we are back to using the component binding again. When setRemoveLocationsComponent is called the first time, we create a ComponentReference to this component and then use that reference whenever we need to retrieve the correct component instance. We continue to fetch the List of Strings to remove all of the component instance directly, as before. So, what's not to like? Well, it doesn't work. The current version of ComponentReference isn't as smart as it could be and chokes when the component binding is called. However that will be fixed soon. The bigger problem is that this solution is still kind of lame--if you wanted to have remove controls on more than one page, you would have to use separate ComponentReference instances for each different hierarchy. ComponentReference is pretty cool, but it isn't really the right solution here--refactoring is. Sometimes the grass isn't greener. I hoped this helped. In future blogs I will be returning to the topics of scopes, ComponentReferences and other JSF topics.