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

[jsr372-experts] Re: [JAVASERVERFACES_SPEC_PUBLIC-1007] Discussion - Explicit support for dynamic component tree manipulation

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Sun, 6 Sep 2015 22:19:52 -0500

Hi

MR>> "Dynamically modifying the component tree can happen at any
MR>> time, during and after restoring the view, but not during state
MR>> saving and needs to function properly with respect to
MR>> rendering and state saving"

I remember this problem does not point in that direction. Let's take a look
at this code:

   public UIAddComponent() {
      FacesContext context = FacesContext.getCurrentInstance();
      UIViewRoot root = context.getViewRoot();
      root.subscribeToViewEvent( PreRenderViewEvent.class, this );
   }

The problem here is the constructor is a bad place to do component
manipulation. Why?

- Constructor is called when the component tree is created and when
it is restored.
- The relationship between parent/children are not yet defined.

 So, this workaround aims to wait a little bit to perform the component
manipulation when it is safe to do so.

In JSF 2.0, Richard investigated on a way to do this component
manipulation safely and he just found that PreRenderViewEvent was
a good time to do this dynamic component manipulation. Why?

- PreRenderView is invoked after vdl.buildView(...), so this ensures
facelets algorithm has already processed the tree.
- After that point, any component tree manipulation can be recorded
using a listener.
- It works for both JSF 1.2 state saving and JSF 2.0 Partial state saving

What happen if the dynamic component manipulation is invoked
before Render Response vdl.buildView(...)? In JSF 1.2 state saving,
the component tree is not yet fully built, so it will blow up the id
generation algorithm. In MyFaces there is an implementation specific
algorithm that overrides UIViewRoot.createUniqueId(...) so the
algorithm deals properly with special cases like @ResourceDependency
and the new dynamic composite component creation.

The problem with the solution using a listener to PreRenderView on
the component constructor is high coupling.

- The call to FacesContext.getCurrentInstance(...) is really bad for
performance.
- The constructor relies on the environment to work properly.
- The code is repetitive. Every time we need dynamic component
manipulation we need those lines of code.

Can we do it better? How about this:

@ListenerFor(systemEventClass=PreRenderViewEvent.class)
public class UIAddComponent extends UIComponentBase implements
SystemEventListener {

   public UIAddComponent() {
      //No need to add anything, JSF will do it for you
   }

   public boolean isListenerForSource( Object source ) {

      return ( source instanceof UIViewRoot );
   }

   public void processEvent( SystemEvent event )
      throws AbortProcessingException {

      if ( !FacesContext.getCurrentInstance().isValidationFailed() ) {

         // Safely manipulate component tree here
      }
   }
}

This syntax does not work right now, because PreRenderViewEvent
is propagated to UIViewRoot only, so the listener is not called. Note
the source of this event is the current UIViewRoot instance, but
the target is the component instance. It would be nice to allow this
for the Renderer class as well.

By the previous reasons, I disagree with add the statement proposed,
because it fails to do what it is wanted for this issue, which is define
when and how the developer can do safely dynamic component
manipulation.

But I have to note that MyFaces comply with the statement, because
we did a big refactor over the algorithm to make the component ids
stable under any circunstance and to support the new dynamic
composite component creation.

best regards,

Leonardo Uribe




2015-09-06 6:30 GMT-05:00 arjan tijms <arjan.tijms_at_gmail.com>:

> +1 a very good step in the right direction.
>
> *Optionally* it may not hurt to give a code example of how a component
> can do this, basically the one the first comment suggests:
>
> Short version:
>
> root.subscribeToViewEvent( PreRenderViewEvent.class, this);
>
> Longer version:
>
>
> http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html
>