myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Leonardo Uribe (JIRA)" <...@myfaces.apache.org>
Subject [jira] [Commented] (MYFACES-3733) Implement vdl.createComponent(...)
Date Wed, 25 Sep 2013 15:22:03 GMT

    [ https://issues.apache.org/jira/browse/MYFACES-3733?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13777607#comment-13777607
] 

Leonardo Uribe commented on MYFACES-3733:
-----------------------------------------

I have finally committed the last issue to close this issue, with a lot of junit tests. I
have been thinking for a long time about how to do it and the current solution split the problem
in two:

- How to refresh when the component is under a "binding", which means facelets has the "control".
- How to refresh when the component was added from somewhere else, which means facelets base
algorithm is not being executed right now.

Each one has its particular way to deal with the problem. In the "binding" problem, the idea
is just mark the component that holds the binding and use a visitTree call to invoke the refresh
algorithm. In the other scenario, a full visitTree traversal is done, there is a listener
attached listening to the event and that's it. The trick from performance perspective is do
not enable this stuff if is not necessary.

In my opinion the algorithm is just great. At the end it was indeed a very difficult problem
to solve. We can close this issue as fixed, but there are still some improvements to do in
this part.
                
> Implement vdl.createComponent(...)
> ----------------------------------
>
>                 Key: MYFACES-3733
>                 URL: https://issues.apache.org/jira/browse/MYFACES-3733
>             Project: MyFaces Core
>          Issue Type: Task
>          Components: JSR-344
>            Reporter: Leonardo Uribe
>
> This is a difficult issue to do in JSF 2.2 . I have spent a long time to solve this one,
and given the complexity involved and since there is no documentation anywhere about how this
should work, I'll let the required explanation here.
> The idea is allow to include generated vdl fragments into pages programatically. This
includes normal components, composite components or just fragments of markup. The method signature
is this:
> public UIComponent createComponent(FacesContext context, String taglibURI, String tagName,
Map<String,Object> attributes)
> Some valid examples of this are:
> // Normal component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/html", 
>     "outputText", attributes);
> // Composite component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite", 
>     "dynComp_1", attributes);
> // Dynamic include
> 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);
> The javadoc does not suggest the dynamic include is valid, but I think users expect these
kind of stuff work. 
> Theoretically it sounds like something easy to do, but unfortunately it is not. The reasons
why this is so are:
> - Facelets algorithm wraps html markup into UILeaf instances, which is a special "transient"
component. UILeaf instances are never saved or restored from the component tree, but in some
points of the algorithm (restore view and before render response when vdl.buildView() is called)
the component tree is updated, adding or removing UILeaf instances.
> - Facelets has an algorithm that require id generation to be stable, otherwise a duplicate
id exception may arise. A lot of effort has been done to organize this part, and the current
solution works very well. But in this case, we need to generate unique ids that can be refreshed
somehow.
> - Facelets algorithm has an special logic to deal with dynamic sections like the ones
generated by c:if or 
> ui:include src="#{...}" . Add facelets sections programatically could make this algorithm
fail, removing sections that should be.
> - Facelets PSS algorithm needs to be taken into account too. The listener that is used
to register programmatic changes on the tree in DefaultFaceletsStateManagementStrategy uses
 ComponentSupport.MARK_CREATED to identify which component belongs to the component tree and
which one was added by outside. Add facelets sections programatically could make this algorithm
fail, because it could assume some sections of the tree does not need to be saved fully, even
if that's not true.
> The issue in the spec is this:
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-611
> At start the idea was to export FaceletFactory directly, but I told to the EG that it
was a bad idea by multiple reasons (That's a Pandora's Box). See:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/91
> This previous message is useful too:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-06/message/18
> After thinking and trying different strategies to overcome this issue, I finally found
the following solution:
> - Use the compiler for generate a custom Facelet "inline" or "on the fly". It is not
necessary to create an
> xml document and then parse it, just generate the Tag class and pass it to the compiler
to generate an
> Abstract Syntax Tree (AST), with the hierarchy of facelet TagHandler instances.
> - To solve the issue with UILeaf instances, the best is create a stateful ComponentSystemEventListener
that on restore view phase it compiles the custom Facelet and apply it over the fragment.
The ideal and only event to attach the listener is PostRestoreStateEvent, but we need to add
the code in UIComponent.processEvent().
> - In the case of a ui:include, if multiple components are returned, all of them are grouped
into a single
> UIPanel. If the code returns one component, it returns that component.
> - If the code generates a branch, or in other words, multiple nested components, it should
attach the 
> listener to deal with UILeaf instances, if it just generates one component do not do
that because it is
> not necessary.
> - To solve the issue with the ids, just call UIViewRoot.createUniqueId() and use the
generated value to derive unique facelets ids. The new algorithm that generate ids is very
flexible and it will support this case. This base key should be saved in the state so the
same ids are generated for the same fragment.
> - Support composite components needs special treatment. The idea is support something
like this:
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite", 
>     "dynComp_1", attributes);
>     
> // .... add children / facets to the algorithm
> someComponentInTheView.getChildren().add(component);
> In this case the "processing" of the composite component content must be done only when
the component is added to the view. The idea is vdl.createComponent only create the root component
class, and then use a listener attached to PostAddToViewEvent to process the content. We need
to modify the algorithm, because in this case children/facets are created programatically
and not using facelets algorithm. The idea is add an extra facelet in the compiler to detect
when the result is a composite component. The listener attached to PostAddToViewEvent must
be done in a way that only works on the first addition to PostAddToViewEvent.
> - If the call to vdl.createComponent() occur when there is an active FaceletCompositionContext
instance, reuse that instance doing the necessary changes in the context, otherwise instantiate
a clean context.
> - Facelets PSS algorithm will work just fine as long as the returned component does not
have ComponentSupport.MARK_CREATED set when the view is refreshed, saved or restored. It is
enough to just use the component attribute map.
> - The two base cases to test are:
>    * Create components programatically inside a "binding" method.
>    * Create components programatically in PreRenderViewEvent or in the Renderer.
>   The difference is the "binding" occur when there is a FaceletCompositionContext instance
active, but in the other cases there is no active instance.
> Comply with all previous requirements can be difficult, but it is very important, otherwise
the algorithm will not be stable enough.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Mime
View raw message