The UIManager Pattern

One of the most common mistakes that I see when reviewing ADF application code, is the sin of storing UI component references, most commonly things like table or tree components in Session or PageFlow scope. The reasons why this is bad are simple; firstly, these UI object references are not serializable so would not survive a session migration between servers and secondly there is no guarantee that the framework will re-use the same component tree from request to request, although in practice it generally does do so.

So there danger here is, that at best you end up with an NPE after you session has migrated, and at worse, you end up pinning old generations of the component tree happily eating up your precious memory. So that's clear, we should never. ever, be storing references to components anywhere other than request scope (or maybe backing bean scope). So double check the scope of those binding attributes that map component references into a managed bean in your applications. 

Why is it Such a Common Mistake? 

At this point I want to examine why there is this urge to hold onto these references anyway? After all, JSF will obligingly populate your backing beans with the fresh and correct reference when needed.  

In most cases, it seems that the rational is down to a lack of distinction within the application between what is data and what is presentation. I think perhaps, a cause of this is the logical separation between business data behind the ADF data binding (#{bindings}) fa├žade and the UI components themselves. Developers tend to think, OK this is my data layer behind the bindings object and everything else is just UI.  Of course that's not the case.  The UI layer itself will have state which is intrinsically linked to the UI presentation rather than the business model, but at the same time should not be tighly bound to a specific instance of any single UI component. So here's the problem.  I think developers try and use the UI components as state-holders for this kind of data, rather than using them to represent that state. An example of this might be something like the selection state of a tabset (panelTabbed), you might be interested in knowing what the currently disclosed tab is. The temptation that leads to the component reference sin is to go and ask the tabset what the selection is.  That of course is fine in context - e.g. a handler within the same request scoped bean that's got the binding to the tabset. However, it leads to problems when you subsequently want the same information outside of the immediate scope.  The simple solution seems to be to chuck that component reference into session scope and then you can simply re-check in the same way, leading of course to this mistake.

Turn it on its Head 

So the correct solution to this is to turn the problem on its head. If you are going to be interested in the value or state of some component outside of the immediate request context then it becomes persistent state (persistent in the sense that it extends beyond the lifespan of a single request). So you need to externalize that state outside of the component and have the component reference and manipulate that state as needed rather than owning it. This is what I call the UIManager pattern. 

Defining the Pattern

The  UIManager pattern really is very simple. The premise is that every application should define a session scoped managed bean, appropriately named UIManger, which is specifically responsible for holding this persistent UI component related state.  The actual makeup of the UIManger class varies depending on a needs of the application and the amount of state that needs to be stored. Generally I'll start off with a Map in which individual flags can be created as required, although you could opt for a more formal set of typed member variables with getters and setters, or indeed a mix. This UIManager class is defined as a session scoped managed bean (#{uiManager}) in the faces-config.xml. 

The pattern is to then inject this instance of the class into any other managed bean (usually request scope) that needs it using a managed property.  So typically you'll have something like this:

  <managed-bean>
    <managed-bean-name>uiManager</managed-bean-name>
    <managed-bean-class>oracle.demo.view.state.UIManager</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean> 

When is then injected into any backing bean that needs it: 

   <managed-bean>
    <managed-bean-name>mainPageBB</managed-bean-name>
    <managed-bean-class>oracle.demo.view.MainBacking</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
      <property-name>uiManager</property-name>
      <property-class>oracle.demo.view.state.UIManager</property-class>
      <value>#{uiManager}</value>
    </managed-property>
  </managed-bean>

In this case the backing bean in question needs a member variable to hold and reference the UIManager:

private UIManager _uiManager; 

Which should be exposed via a getter and setter pair with names that match the managed property name (e.g. setUiManager(UIManager _uiManager), getUiManager()). 

This will then give your code within the backing bean full access to the UI state.

UI components in the page can, of course, directly reference the uiManager bean in their properties, for example, going back to the tab-set example you might have something like this:

<af:paneltabbed>
  <af:showDetailItem text="First"
                     disclosed="#{uiManager.settings['MAIN_TABSET_STATE'].['FIRST']}">
    ...
  </af:showDetailItem>
  <af:showDetailItem text="Second"
                     disclosed="#{uiManager.settings['MAIN_TABSET_STATE'].['SECOND']}">
    ...
  </af:showDetailItem>
  ...
</af:panelTabbed>

Where in this case the settings member within the UI Manger is a Map which contains a Map of Booleans for each tab under the MAIN_TABSET_STATE key. (Just an example you could choose to store just an identifier for the selected tab or whatever, how you choose to store the state within UI Manger is up to you.)

Get into the Habit

So we can see that the UIManager pattern is not great strain to implement for an application and can even be retrofitted to an existing application with ease. The point is, however, that you should always take this approach rather than committing the sin of persistent component references which will bite you in the future or shotgun scattered UI flags on the session which are hard to maintain.  If you take the approach of always accessing all UI state via the uiManager, or perhaps a pageScope focused variant of it, you'll find your applications much easier to understand and maintain. Do it today!

More Information

Another interesting article relating to this topic has been written by Steven Davelaar  subsequent to the original publication of this post. This article is well worth checking out more more information on this area and some best practice around component references.

Comments:

Quote:
"..these UI object references are not serializable so would not survive a session migration between servers.."

This may happen in a cluster environment, with High Availability On. However, I am not sure if ADF runs properly in HA cluster environment.

Thanks,

Florin

Posted by guest on April 05, 2012 at 10:19 AM BST #

It certainly does run well in an HA cluster, and particularly on a platform like Exalogic. However, you do have to flip a couple of switches and of course be much more cautious about what you put in pageflow and session scope. If you look in the latest version of 11.1.2 for example you'll see that we've even installed a bunch of audit rules to check for potential HA problems e.g.
oracle.adfdt.controller.adfc.config.source.audit.AdfScopeHaSupportNotSet
oracle.adfdt.controller.adfc.source.audit.ManagedBeanNotSerializable
oracle.adfdt.controller.adfc.source.audit.ScopeModifiedWithoutDirty

All of which can be switched on through Tools -> Preferences -> Audit -> Profiles.

Posted by Duncan on April 05, 2012 at 10:41 AM BST #

Post a Comment:
Comments are closed for this entry.
About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today