Fix PostAddToViewEvent and PreRemoveFromViewEvent publishing conditions
-----------------------------------------------------------------------

                 Key: MYFACES-2638
                 URL: https://issues.apache.org/jira/browse/MYFACES-2638
             Project: MyFaces Core
          Issue Type: Bug
          Components: JSR-314
    Affects Versions: 2.0.0-beta-3
            Reporter: Leonardo Uribe
            Assignee: Leonardo Uribe


>From a discussion of jsr-314-open mailing list:

Actually the event publishing conditions of 
PostAddToViewEvent/PreRemoveFromViewEvent are not very clear. These conditions 
depends if partial state saving is used or not and if facelets updates the view 
or  not. The problem is this fact is not very intuitive, so for write code 
correctly the user needs to be aware of that, in other words this should be 
documented somewhere. Also, there are some technical questions that could be of 
interest on this mailing list and myfaces dev list too.

In theory, PostAddToViewEvent is used in this cases:

- h:outputScript and h:outputStylesheet renderers uses it to relocate component 
resources to UIViewRoot facet "head" or "body" target (h:outputStylesheet by 
default to "head"). Note "head" and "body" facets are transient.
- cc:insertChildren and cc:insertFacet, to relocate components to some place 
inside cc:implementation. The reason why we do that through a listener attached 
to PostAddToViewEvent is we need to take into account the case of nested 
composite components using cc:insertFacet/cc:insertChildren. Note when the 
component tree is built the first time, PostAddToViewEvent is propagated from 
root to nodes.
- When Partial State Saving is enabled, it is necessary to attach a listener to 
PostAddToViewEvent/PreRemoveFromViewEvent, so if some user add/remove a 
component, we can keep track of those changes and save/restore the component 
tree properly, in other words, support component addition or removal 
programatically.

Now, the instants of time where PostAddToViewEvent is published are:

- With Partial State Saving enabled

   * When the view is build at first time.
   * In a postback when the view is build but before the state of the component 
is restored.

- With Partial State Saving disabled

   * When the view is build at first time.
   * In a postback when the view is "refreshed", because all component nodes 
are detached and attached to the tree. In other words, on render response 
phase, vdl.buildView is called and in this case facelets algorithm add all 
transient components (usually all html markup not saved), and to do that, it 
detach and attach all components to be in right order. This also has some other 
implications, like c:if tag depends on this to work correctly.

A developer that is not aware of this fact could think that PostAddToViewEvent 
is called when the view is build.

This is true with PSS is enabled, but is not true when PSS is disabled. On this 
topic:

[jsr-314-open] add component resources depending on the owner component state

It was described why PostAddToViewEvent cannot be used in that case. But let's 
change a litte bit the problem. We have a custom component that creates some 
other on PostAddToViewEvent and add it as a children. It should work but in 
fact it only works when PSS is enabled, with PSS disabled that code will cause 
a duplicate id exception. Of course, the developer can solve it with some 
effort but the point is we are not consider the element of least surprise.

But this fact this is only the tip of the iceberg. In mojarra (this was already 
fixed on myfaces), components relocated by cc:insertChildren or cc:insertFacet 
does not have state when PSS is disabled, because when the view is "refreshed" 
the components are created again, but facelets algorithm remove all components 
with no bound to a facelet tag handler, and then the listeners attached to 
PostAddToViewEvent relocates the new ones, so this effect is not notice at 
first view.

Do you remember PostAddToViewEvent propagation ordering is important by 
cc:insertChildren/cc:insertFacet? well, with PSS disabled, the ordering now is 
from nodes to root, because all nodes are detached and attached from nodes to 
root, so in myfaces we did something like this (I removed some non relevant 
code to be more clear):

        try
        {
            if (refreshTransientBuild)
            {
                context.setProcessingEvents(false);
            }
            // populate UIViewRoot
            _getFacelet(renderedViewId).apply(context, view);
        }
        finally
        {
            if (refreshTransientBuild)
            {
                context.setProcessingEvents(true);
               
                    // When the facelet is applied, all components are removed 
and added from view,
                    // but the difference resides in the ordering. Since the 
view is
                    // being refreshed, if we don't do this manually, some tags 
like
                    // cc:insertChildren or cc:insertFacet will not work 
correctly, because
                    // we expect PostAddToViewEvent will be propagated from 
parent to child, and
                    // facelets refreshing algorithm do the opposite.
                    
FaceletViewDeclarationLanguage._publishPreRemoveFromViewEvent(context, view);
                    
FaceletViewDeclarationLanguage._publishPostAddToViewEvent(context, view);
            }
       }

In few words, disable event processing, and fire PostAddToViewEvent and 
PreRemoveFromViewEvent in the expected order to build the tree correctly. If we 
don't do this, h:outputScript and h:outputStylesheet will not work correctly 
(remember that UIViewRoot "head" and "body" facets are transient, so if these 
components are relocated, are in fact transient too). Also, care must be taken 
to prevent the listener of programatic component addition/removal register 
components on this time.

The conclusion based on this reasoning, it is clear the need of new event that 
be specific for relocate components (keep it simple for christ sake!). This 
event should be propagated for all components in the tree after the view is 
build from root to nodes. It could be good to have some clearification about 
the real intention of PostAddToViewEvent/PreRemoveFromViewEvent. Really, better 
names for those events are 
PostAddToComponentTreeEvent/PreRemoveFromComponentTreeEvent.

I'll attach a proposal to fix this one soon.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to