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

[jsr372-experts] Improve vdl.createComponent(...) to use ui:include and user tags

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Wed, 29 Oct 2014 12:39:49 -0500

Hi

I would like to raise the attention of this EG in a unresolved issue we place
on hold in JSF 2.2 because it was thought too inconvenient to fix the way it
was proposed, but on the way we found in MyFaces 2.2 an acceptable solution.

Since part of JSF 2.3 is to solve "community driven features", I would like
to propose this feature, since it was already in our plate and now we have
a good idea about how to fix it properly.

Let's make a recap. In few words, the problem is users want to create facelet
tags programmatically, so they can add branches to the component tree without
the hazzle of create component one by one, just create one facelet and then
attach that facelet in the component tree.

I'm talking about this issue:

https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-611
Export FaceletFactory as a standard artifact vended from FactoryFinder.

In:

[jsr344-experts] implications of use FaceletFactory
(JAVASERVERFACES_SPEC_PUBLIC-611)
https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/91

It was mentioned that add FaceletFactory "as is" was too inconvenient.

But then, in the work done for MyFaces 2.2, we found that it was possible to
extend vdl.createComponent(...) to fix the problems we had with FaceletFactory,
and provide and stable implementation that could be used in many different
situations. This is a real problem, there is a lot of evidence out there that
suggest it is a good idea to get it fixed.

The issue has been mentioned before in the following mail:

[jsr344-experts] VDL.createComponent(...) does not work as it should for
  the RI
https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2014-03/message/12

Later, an user created this issue on the spec issue tracker:

https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1281
Extend vdl.createComponent with support of ui:includes and user tags

This is what the users try to do:

    FacesContext fc = FacesContext.getCurrentInstance();
    FaceletContext f2ctx = (FaceletContext)
fc.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
    UIComponent uic = fc.getViewRoot().findComponent("container");
    if (uic != null && f2ctx != null) {
        f2ctx.includeFacelet(uic, contentUrl);
    }

this is the clean way to get the same done:

        ViewDeclarationLanguage vdl = facesContext.getApplication().
            getViewHandler().getViewDeclarationLanguage(
                facesContext, facesContext.getViewRoot().getViewId());

        Map<String, Object> attributes = new HashMap<String, Object>();
        attributes.put("src", "/addSimpleIncludeVDL_1_1.xhtml");
        UIComponent component = vdl.createComponent(facesContext,
            "http://java.sun.com/jsf/facelets",
            "include", attributes);
        getChildren().add(component);

Please note a component like ui:include is not a JSF UIComponent instance, but
it usually hold one or many JSF components. What we did in MyFaces was
create a component instance to hold the content of ui:include. If it is only
one component, use that instance as return value, if not use the generated
instance as return value.

Of course, there is a lot of issues with this to solve. Html markup that
dissapear on postback, how to refresh the component tree, how to apply PSS
inside this content, but what I can say is all this can be solved (it is
necessary to use a new id generation algorithm and modify facelets engine)
and the result is quite satisfactory. After this change, all users doing
all kind of customized hacks changed to this new way, and problem solved.
No more state get lost, no more duplicate id exception, no more instability
over components added programmatically. Everything works just the way it
should.

This is what the spec says (javadoc of ViewDeclarationLanguage) about
vdl.createComponent(...):

"... Create a component given a ViewDeclarationLanguage specific tag library
URI and tag name. The runtime must support this method operating for the
Facelets VDL. Other kinds of ViewDeclarationLanguage may be supported but
are not required to be supported. For backward compatibility with decorated
ViewDeclrationLanguage implementations that do not override this method, a
default implementation is provided that returns null. However, any
implementation that is compliant with the version of the specification in
which this method was introduced must implement this method. ..."

It is general enough, which is ok. Now, our problem is we need this fixed on
the RI, because otherwise this will hurt compatibility between JSF
implementations.

Implement this change could not require changes over JSF spec, because it was
written in a way that cover what we want. But maybe it is a good idea to say:

"... If a VDL tag is not bound to a UIComponent instance, the runtime must
generate a generic UIPanel instance that will hold the response. If
panel.getChildCount() == 1, the wrapper is discarded and the child instance
is returned. If panel.getChildCount() > 1, return the wrapper. ...".

The only known issue MyFaces solution has is you can't use ui:define and
compose them using ui:decorate, but we can safely ignore this case, because
most of the time the users want to add branches to the tree. If they want
to use facelet engine, they use it on the xhtml file, not programmatically,
but support ui:include is something really important. Please note nobody
has founded this problem yet (note you can overcome this limitation with
composite components added using vdl.createComponent(...) ), and it has
passed some time, so we can be sure it doesn't matter.

If you guys think this is a good idea, please say something, so we can put
more weight in and get it done.

regards,

Leonardo Uribe