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