Rules and Best Practices for JSF Component Binding in ADF

Introduction

There is a lot of confusion about how to use JSF component binding in managed beans in a clustered environment to meet high availability. In this post we will highlight important considerations.

Main Article

A very common mistake is to bind a JSF UI Component to a managed bean which is in a scope greater than requestScope or backingBeanScope.
You should not do this for two reasons:

  • UI Components are not serializable, so your application might fail to work correctly in a clustered environment.
  • The UI component tree might not be released correctly, significantly increasing the memory load of your application.

While marking the UIComponent property as transient in the managed bean fixes the first issue, it does not solve the second issue, and is therefore not a good option to use. The only way to address both issues is to use the org.apache.myfaces.trinidad.util.ComponentReference class. The standard UIComponent binding code looks like this

RichInputText nameField;

public void setNameField(RichInputText nameField)
{
  this.nameField = nameField;
}

public RichInputText getNameField()
{
  return nameField;
}

and should be replaced with code like this:

private ComponentReference nameField;

public RichInputText getNameField()
{
  if (nameField!=null)
  {
    return (RichInputText) nameField.getComponent();
  }
  return null; 
}

public void setNameField(RichInputText nameField)
{
  this.nameField = ComponentReference.newUIComponentReference(nameField);
}

ComponentReference stores a path consisting of either child index positions or component ID strings, not UIComponent instances. This makes it so that always the correct component instance is accessed in each view.

Now, what I didn’t know until the aforementioned email discussion is that the above ComponentReference code should also be used with managed beans in requestScope and backingBeanScope.This is because within one request we can still navigate from one JSF view to another. If both views use a component binding by the same name, then potentially a UI component could be moved from one UI tree to another which is not allowed, and can result in unexpected behavior. This issue is vividly illustrated in the post “JSF Component Binding Stinks” by Andrew Robinson. This brings me to the first rule:

1. Always use ComponentReference binding code in your managed beans, regardless of the scope the bean is in.

Having said this, the above rule will not solve additional issues that might happen when your bean is in session scope. Session scope is shared between multiple browser tabs, so if you open the same application page in multiple browser tabs, you can run into nasty threading and concurrency issues that are explained in more detail in this old post from Blake Sullivan (the problems he mentions with ComponentReference have been solved in the meantime). Which results in the second rule:

2. Never use UIComponent binding in session-scoped managed beans.

These are the two golden rules you must apply to get a correctly working ADF-JSF application.

Now, there are additional techniques you should consider to follow best practices. Firstly, there are many situations where you can avoid the use of UIComponent binding alltogether. Duncan Mills wrote a nice blog post about the UIManager pattern which describes a design pattern to avoid component binding at all. Secondly, if you really need a handle to a UIComponent you can also lookup the component by searching the UITree. Frank Nimphius wrote ADF Code Corner sample 58 on using the invokeOnComponent API to do so. Note that if you use the invokeOnComponent API, you should start searching as low as possible in the UI component tree. Starting with the UIViewRoot component might slow down performance considerably if you have large pages with many components.  Often, you can start with the parent or grand parent component of the event component that can be retrieved from the ActionEvent argument in actionListener methods.

As a final note, you might want to know that an enhancement request has been logged against JDeveloper to directly generate the correct ComponentReference code when using the managed bean dialog for setting the binding property on a component.

Add Your Comment