Safely storing UIComponent reference in a managed bean

Always it's recommended not to bind the UIComponent with managed bean with higher scopes(view, session, pageFlow, application etc.). There are multiple reasons to justify this suggestion.
1. JSF components are not Serializable, so your managed bean may fail to serialize in high availability mode.
2. Leaving stale references of the component if the bean survives the life of the view/page etc.

If you really want to store a component reference in managed whose scope is higher than request, then there is utility class for your help - ComponentReference . You can take a look at org.apache.myfaces.trinidad.util.ComponentReference API to learn more. I should admit that I was not aware of this one till I learned about this from an application developer ;)

ComponentReference Usage:

private ComponentReference someUICompReference;

public UIComponent getSomeUIComponent(){
   return someUICompReference == null ?
            null : someUICompReference.getComponent();
}

public void setSomeUIComponent(UIComponent component) {
    someUICompReference =
        ComponentReference.newUIComponentReference(component);
}

ComponentReference API Doc says::
  • This class is not thread-safe, since it depends on UIComponent APIs.
  • The passed in UIComponent is required to have an ID
  • The reference will break if the UIComponent is moved between NamingContainers or if any of the ancestor NamingContainers have their IDs changed.
  • The refrence is persistable. However UIComponents are not Serializable and therefore can not be used at any scope longer than request.
Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/10/oracle-adf-real-world-developers-guide.html

Comments

Roberto said…
Hi Jobinesh, thank you for your article! I really need you help about a issue regarding a similar case.
I'm developing with JDeveloper 11.1.2.1.0 and have a problem:
I have a pageFlowScoped bean MyBean in which I programmatically create some instances of MyInput class that extends RichInputText. I can't do that in a backing bean because it depends of some values inserted by the user at runtime. Then, I put these items into a form and set the binding:

[code]
private HashMap parRefMap;
...
...
...
// Create item
MyInput pInput = new MyInput(...);

// Put item in the form
MyForm.getChildren().add(pInput);

// Save item reference
parRefMap.put(1, ComponentReference.newUIComponentReference(pInput));

// Set the binding to methods that work on ComponentReferences
pInput.setValueExpression("binding", getValueExpression(FacesContext.getCurrentInstance(), "#{pageFlowScope.MyBean.srp1}"));

...
...
...

public void setSrp1(MyInput srp1) {
parRefMap.put(1, ComponentReference.newUIComponentReference(srp1));
}

public MyInput getSrp1() {
return (MyInput)(parRefMap.get(1).getComponent());
}
[/code]

Now, as you can see, the code I've in my pageFlowScope bean uses ComponentReference to reference those items so it shouldn't be a problem. In the page there is a panelTabbed item, everything works great until I click one of those tabs (my items are not in the tabs but outside anyway): when I do that after few seconds it happens that restoreview goes in exception because references to those programmatically created items get lost:

* *
*java.lang.InstantiationException: csc.st3k.system.lib.res.MyInput*
*at java.lang.Class.newInstance0(Class.java:340)*
*at java.lang.Class.newInstance(Class.java:308)*

I can't find a way to avoid this!
Can you help me PLEASE???

Disclaimer

The views expressed on this blog are my own and do not necessarily reflect the views of my employer.