Hi

Instead open the previous ones, I created this issue:

http://java.net/jira/browse/JAVASERVERFACES-1985

regards,

Leonardo

2011/1/23 Leonardo Uribe <lu4242@gmail.com>
Hi

Some days ago, I tried to check if publishing conditions for PostAddToViewEvent were fixed, but unfortunately it was not. These issues:

- PostAddToViewEvent delivery specification needs clarification

http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-805 (for 2.0 rev A)

- Fix PostAddToViewEvent and PreRemoveFromViewEvent publishing conditions

http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-782 (for 2.1_gf31_m5)

were marked as fixed.

This is what javadoc for JSF 2.1 says about it (I put the javadoc to make it more easier to understand):

- (see UIComponent.getChildren method, the part in orange)

"... After the child component has been added to the view, Application.publishEvent(javax.faces.context.FacesContext, java.lang.Class, java.lang.Object) must be called, passing PostAddToViewEvent.class as the first argument and the newly added component as the second argument if any the following cases are true.

    * FacesContext.getCurrentPhaseId() returns PhaseId.RESTORE_VIEW and partial state saving is enabled.
    * FacesContext.isPostback() returns false and FacesContext.getCurrentPhaseId() returns something other than PhaseId.RESTORE_VIEW..."

- Another reference to the previous point (see PostAddToViewEvent class description)

"... The implementation must guarantee that Application.publishEvent(javax.faces.context.FacesContext, java.lang.Class, java.lang.Object) is called, immediately after any UIComponent instance is added to the view hierarchy except in the case where ResponseStateManager.isPostback(javax.faces.context.FacesContext) returns true at the same time as FacesContext.getCurrentPhaseId() returns PhaseId.RESTORE_VIEW. When both of those conditions are met, Application.publishEvent(javax.faces.context.FacesContext, java.lang.Class, java.lang.Object) must not be called. ..."

- (see UIComponent.setParent method)

"... Set  the parent UIComponent of this UIComponent. If parent.isInView() returns true, calling this method will first cause a PreRemoveFromViewEvent  to be published, for this node, and then the children of this node. Then, once the re-parenting has occurred, a PostAddToViewEvent will be published as well, first for this node, and then for the node's children, but only if any of the following conditions are true.

    * FacesContext.getCurrentPhaseId() returns PhaseId.RESTORE_VIEW and partial state saving is enabled.
    * FacesContext.isPostback() returns false and FacesContext.getCurrentPhaseId() returns something other than PhaseId.RESTORE_VIEW ..."

The first thing to notice is the description here is not what is happening right now in Mojarra (2.0 and 2.1). The description found in UIComponent.setParent is more close to the reality (the conditions related to parent.isInView() ) but the part that says "... but only if any of the following conditions are true ..." is not being preserved.

The intention of those conditions are clear. See this coment from Andy Schwartz (JAVASERVERFACES_SPEC_PUBLIC-805):

"... 2. We don't actually want to deliver PostAddToViewEvents during render response when Facelets temporarily removes/re-adds existing components from the tree. ..."

Unfortunately, in practice the previous two conditions does not warrant that. Its more, there are valid scenarios in facelets when it is required to throw PostAddToViewEvent (for example, an ui:include src="#{someEL}" should propagate that event so the view could be restored).

- (see PostAddToViewEvent class description)

"... When an instance of this event is passed to SystemEventListener.processEvent(javax.faces.event.SystemEvent)  or ComponentSystemEventListener.processEvent(javax.faces.event.ComponentSystemEvent), the listener implementation may assume that the source of this event instance is a UIComponent instance and that either that instance or an ancestor of that instance was just added to the view. Therefore, the implementation may assume it is safe to call UIComponent.getParent(), UIComponent.getClientId(), and other methods that depend upon the component instance being added into the view. ..."

In practice, that's not true in Mojarra, but it is on MyFaces. The reason is Mojarra still uses a Listener attached to PostAddToViewEvent, making UIComponent.getClientId() "unstable". If the developer has a listener that is executed before the one doing relocation, UIComponent.getClientId() call will not work correctly.

The central point of this mail is how should PostAddToViewEvent should really behave?

In theory, and according to the latest JSF 2.1 spec, the original intention must be preserved. That means:

- Deliver PostAddToViewEvents during restore view when partial state saving is enabled.
- Don't deliver PostAddToViewEvents during render response when Facelets temporarily removes/re-adds existing components from the tree, but propagate it when the component is removed or added permanently.

The current behavior of Mojarra, from the point of view of the spec should be consider a bug. How to do fix it? I propose do the following for Mojarra:

1. Check when a component is temporally removed/added on ComponentTagHandlerDelegate implementation. If that so, disable event processing using FacesContext.setProcessingEvents() method temporally while removal and addition is done.
2. Propagate another internal event (for example PostAddToViewOnRefreshBuild or something like that) to notify the listeners used for relocation (cc:insertChildren, cc:insertFacet, h:outputScript, h:outputStylesheet) to do their job like before.

Anyway, it could be good to know how this should really work, and fix the relevant points on the spec.

Suggestions are welcome.

regards,

Leonardo Uribe