Jeff Bischoff wrote:
Greetings colleagues,

I have used in my web pages a pattern that apparently was not safe, yet it worked fine with all versions of MyFaces until the current trunk (1.1.5). Basically, when I needed to make sure a certain bean was instantiated before using non-JSF EL, I preceded the statement with an f:attribute like so:

<f:attribute name="Content" value="#{publicNavigation.contentPage}" />
<jsp:include page="${publicNavigation.contentPage}" />

Hmm...I guess the question is whether the UIComponent attribute map contains *objects* or *value-bindings*.

I see in the myfaces-1.1.3 code (class AttributesTag) that when the f:attribute tag executes (see below), it always evaluates the provided expression, and puts that value into the attributes map of the enclosing UIComponent. That means the expression is only evaluated once (on first page view). See:
http://svn.apache.org/repos/asf/myfaces/core/tags/1_1_3/api/src/main/java/javax/faces/webapp/AttributeTag.java

In the myfaces trunk, if the value is an expression, I see that a ValueBinding is stored into the attributes map instead, meaning it will be evaluated whenever the attribute is fetched from the parent component (but NOT evaluated until then). See method doStartTag in:
http://svn.apache.org/repos/asf/myfaces/core/trunk/api/src/main/java/javax/faces/webapp/AttributeTag.java

So that's why it is no longer working for you. The commit was r448315:
  Modified Wed Sep 20 19:46:32 2006 UTC (3 months, 2 weeks ago)
  by mmarinschek

  fix for [MYFACES-437] <f:attribute> does not work with <x:dataTable>.
  Thanks to Michal Borowiecki.

Maybe mmarinschek didn't realise that moving from objects to value-bindings in the attribute map would have significant other effects? Or maybe this new behaviour is in fact in better compliance with the spec or the sun RI?

Regardless, it seems to me that there is another flaw in this f:attribute hack, even with the 1.1.3 behaviour.

On first view of this page (ie where there is no component tree to restore), so that the result of the expression can be added to the parent component's attribute map. And a side-effect of this is that the name "publicNavigation" will be registered in the appropriate scope, which of course is what the code above is really wanting to achieve.

However on a later view of this page (where the component tree has been restored), AttributeTag checks whether an attribute with that name exists, and if so does nothing which seems reasonable. This implies that for your usage above, if publicNavigation managed bean is of request scope, then the name only gets registered *on first view*, and won't be available to jsp expressions later (eg if an action on the page causes a submit that re-renders the same page). Note that it *is* "instantiated", but there is no name registered in any scope that references the object.

This behaviour of AttributeTag (only execute on first view) is consistent with other JSF tags; normally, a JSF tag does nothing if the corresponding component already exists. An f:attribute tag doesn't create a UIComponent, but it seems right for it to behave in a consistent manner.

Another disadvantage is that the specified object will be in the attribute-map of the component, so will be "persisted" if the parent component is persisted (eg when using client-side state saving!). This isn't terribly elegant, though bearable as long as the value expression returs something small. In the above case, it returns the relative path to a jsp file, so that's probably ok. Something like this should also be acceptable in most cases:
 <f:attribute name="Content" value="#{publicNavigation.class.name}" />



By the time the jsp:include tag was evaluated, the publicNavigation bean was instantiated.

You don't just want it "instantiated", but also registered in the appropriate scope.


However, when I try with MyFaces Core 1.1.5 (built off
the trunk repeatedly over the last few weeks) this pattern fails catastrophically. The f:attribute tag does not cause the bean to instantiate at all!

So why did f:attribute change to not instantiate beans anymore? What would be a better choice of tag for this pattern? What is the safest way to do this?

I can't think of a better solution for the moment. Maybe the outputText hack (with the style set to display:none to hide it) is a reasonable solution...


Cheers,

Simon

Reply via email to