Skip to main content

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???

Popular posts from this blog

How to set Bind Variable Values at runtime ?

In this post I'm sharing a couple of approaches for programmatically setting bind variables values at run time. This post is an attempt to explain 'When to use what ?'[ In case if you are familiar with 'Bind Variables' in ADF BC, please refer Section 5.10, Working with Bind Variables in Fusion Developer's Guide ]

1. Set the Bind Variable value using RowSet::setNamedWhereClauseParam(...)

You can use use the setNamedWhereClauseParam(...) method on the ViewObject interface (which extends oracle.jbo.RowSet) to set the value for bind variables. Please note this sets the value on default RowSet. In other words, this doesn't have any effect on the secondary RowSets that you/system generates.
ViewObject vo = am.findViewObject("EmployeesView1"); vo.setNamedWhereClauseParam("bindVarDeptId", new Number(10)); vo.executeQuery();
2. Set the Bind Variable value using ViewObject's VariableValueManager::setVariableValue(...)

VariableValueManager Ma…