Thanks for your detailed response Simon. Thanks also to Paul for his suggestion.

After reading your email, I looked up MYFACES-437. (See [1])

It appears Adam Winer is right in that JSF 1.2 spec demands that f:param accept only literals and deferred expressions. Of course we are not using 1.2 right now, but I suppose some team members felt it better to make the change now and follow the behaviour of the newer spec.

This is fine, but it does represent a rather large change of functionality for the f:param tag, and this change should be prominently documented somewhere. Unfortunately, I'm not quite sure where this "somewhere" should be.

In any case, it appears that I will have to abandon my hack in favor of a new one. (It seems this case will always require some hack, until JSF 1.2 is used) For now, I am trying the hidden outputText approach. It is still not elegant, and there may yet be some side-effects, but at least I have the assurance that the value-binding expression's evaluation won't be deferred. So far it seems to be working.

There are also some inline responses to Simon's email below.

[1] http://issues.apache.org/jira/browse/MYFACES-437

Thanks again for your help in clarifying this issue guys!

Regards,

Jeff Bischoff
Kenneth L Kurz & Associates, Inc.

Simon Kitching wrote:
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*.


Right, or to put it another way: whether the EL expression is evaluated immediately or the evaluation is deferred. Seems JSF 1.1 leaves room for interpretation on this one, but JSF 1.2 does not...

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.

Good catch! That definately explains why my hack has broken. Thanks for your research.

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


Good 'ol Martin. :)

Well it is better compliance with the 1.2 spec, anyway. I haven't tried this with the RI, maybe that is how they implemented it though? If that's the case, then the change makes sense. Usually Martin is pretty aware of side-effects and deals with them if he can... I just wish there was a good place to announce changes like this that might break existing code.

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.


Right, I'm only interested in the side-effect. Painfully inelegant, but it got the job done.

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.


Ahh I should have mentioned that I only ever use this hack to create Session-scoped managed beans. These are constructs for navigation that track where a user currently is in the system. Thus, the behaviour you describe above was exactly what I wanted and worked quite well for the better part of a year. :)

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}" />


Indeed this is rather painfully inelegant. However, I don't see any elegant way to do this in JSF 1.1. Whatever solution I choose, it will be a hack of some kind. Fortunately, I only need to do this in a few places in my code, and like you noted the Strings I am storing are not large.



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


Going to go with this for now, thanks for your help!


Cheers,

Simon





Reply via email to