jsr344-experts@javaserverfaces-spec-public.java.net

[jsr344-experts] Re: [1080-ComponentContextId] Blake: request for help

From: Blake Sullivan <blake.sullivan_at_oracle.com>
Date: Fri, 16 Mar 2012 17:10:57 -0700

On 3/16/12 7:34 AM, Edward Burns wrote:
> KM> So, I suppose the question is really whether or not we should support
> KM> the ability to use findComponent() and evaluate the properties outside
> KM> of a tree traversal.
>
> B> We have never been able to support this in the general case, which is
> B> why invokeOnComponent() was invented. We should improve the
> B> documentation and should maybe throw an IllegalStateException when
> B> attempting to evaluate EL attributes on a non-current component during
> B> the development project stage.
>
> EB> Ok, I've made the following change to the documentation to
> EB> UIComponent.findComponent():
> EB>
> EB> Make the second paragraph of the javadoc for that method be:
>
> EB> This method is not intended to be used with components that reside
> EB> inside of an iterating component. To take action on a component inside
> EB> of an iteration, or to find a component given a simple clientId, see
> EB> invokeOnComponent.
>
> B> It's actually more basic than that--you can't perform any operation on a
> B> component returned by findComponent() that depends directly or
> B> indirectly on the component being in the correct traversal context. In
> B> practice, this means that attempting to retrieve an EL-bound attribute
> B> value is not safe. Iteration is only the most obvious of these
> B> problems, and the one that exists in the spec components.
>
> Here is the revised text.
>
> + *<p class="changed_added_2_2">WARNING: The found
> + *<code>UIComponent</code> instance, if any, is returned
> + *<strong>without</strong> regard for its tree traversal context.
> + * Retrieving an EL-bound attribute from the component is not safe.
> + * EL expressions can contain implicit objects, such as
> + *<code>#{component}</code>, which assume they are being evaluated
> + * within the scope of a tree traversal context. Evaluating
> + * expressions with these kinds of implicit objects outside of a
> + * tree traversal context produces undefined results. See {_at_link
> + * #invokeOnComponent} for a method that<strong>does</strong>
> + * correctly account for the tree traversal context when operating
> + * on the found<code>UIComponent</code> instance. {_at_link #invokeOnComponent}
> + * is also useful to find components given a simple<code>clientId</code>.
>
>
> EB> Now, to comply with your recommendation to throw an
> EB> IllegalStateException, let's look at section "5.6.2 ELResolver for
> EB> Facelets and Programmatic Access". In there, you see a diagram showing
> EB> our ELResolver chain. Blake, would the following comply with your
> EB> recommendation?
>
> EB> Modify the table in section 5.6.2.1 so that for all ELResolver methods,
> EB> if ProjectStage is development, and base is a UIComponent, throw an
> EB> IllegalStateException if base is not equal to the component returned
> EB> from UIComponent.getCurrentComponent()?
>
> B> Yes. And this should also ensure that implementation code that needs to
> B> mess with the current component, is doing so correctly.
>
> I think there is another subtlety. Consider this expression
>
> #{component.children[3].clientId}
So what did you do in this case? Is component.children[3] actually
modifying the current component to point to children[3] in this case and
then pops it back off after evaluation? I also don't see how the EL is
necessarily safe even if you did set up the current component in
children[3]--component may need to set up context for children[3] to
execute in.

You need new component apis to manage the pushing and popping of the El
context as the components execute. Currently, with the JSF spec, if
application code executes while in an EL-context that application code
is playing with fire if it then makes a call such an invokeOnComponent
or visitTree that further modifies the EL-context. This is because the
new modifications will be layered on top of the current modifications.
This is incorrect, we really want to pop the current context, assert the
new context, invoke the requisite code and then pop the new context and
reapply the old. If we can do the above more efficiently by only
applying the pops and pushes back to the lowest shared ancestor, all the
better. The Trinidad ComponentContextManager
<https://myfaces.apache.org/trinidad/trinidad-api/apidocs/org/apache/myfaces/trinidad/context/ComponentContextManager.html>
does this.

Of course, we don't want to make application developers deal with this
context problem--we want the components to hide the problem. And as
long as the mapping from clientId<-->component instance is 1-to-1 we
could. When it isn't we need an alternate object that does have a
1-to-1 mapping, which is why a proxy is appropriate.

The 1-to-1 components and the proxies could use an overall context
management api to manage any pushing and popping of EL context when
retrieving their EL-bound values.

-- Blake Sullivan
>
> That's a perfectly valid expression with respect to our assertion, but
> when the EL resolution process gets around to "base is children[3]", the
> assertion that the base is the current component will fail. But the
> assertion should not fail because the context is, in fact, correct.
> I've accounted for this in the implementation, and created UEL-29 into
> the bargain.
>
> Blake, I still need your help as requested earlier in the thread:
>
> B> The issue with clientIds is that portions of the clientId are an
> B> implementation detail of the components and renderkit. The result is
> B> that developers are not supposed to be hardcoding clientIds into their
> B> code. Developers need a different kind of id that specifies context
> B> like a clientId, but is standardized like a findComponent search
> B> expression. Let's call this a contextId.
> EB>
> B> Given a contextId, we would like to be able to do the following:
> B> 1) Efficiently convert the contextId into an object capable of
> B> establishing and tearing down the context for the component relatively
> B> efficiently. This would require new component apis.
> B> 2) Add a function like findComponentInContext that may return a proxy
> B> for the UIComponent executing in the context object from 1)
> B> 3) Since setting up and tearing down context on every attribute
> B> retrieval is inefficient--framework managed context so that the current
> B> context may be torn down lazily.
>
> EB> Ahh, the tantalizingly succinct paragraph that seems like it addresses
> EB> all my problems at first read, but at second read is shown to raise many
> EB> more questions than it answers! I need some help in defining those "new
> EB> component APIs" that Blake easily mentions in passing.
>
> EB> I've filed 1080-ComponentContextId.
>
> ACTION: Blake can you please sketch out what you mean by "new component
> APIs"?
>
> Thank you.
>
> Ed
>