My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

miercuri, 7 octombrie 2015

JSF PostRestoreStateEvent component system event demystified

The PostRestoreStateEvent occurs when the source of this event instance is in a tree that has just had its state restored. 

Unlike the rest of JSF component system events, the PostRestoreStateEvent is "active" out-of-the-box for all JSF UI components. Even if we don't "see" it or "feel" its effect, by default, the PostRestoreStateEvent is there at each request (initial (by default, at initial request only view root will fire this event) or postback) and is emitted (fired) for each UIComponent in the component tree after Restore View phase. This event is emitted even if the application had explicitly set the tree in the context (advanced case). In addition, by default, every UI component acts as a listener for PostRestoreStateEvent event emitted by instances of that component. So, pay attention that this is a component specific event (per-component action), but it acts as a flag that signals that the entire component tree was restored. Practically, this is the earliest moment when you can perform something after the component tree was restored.

System events that are dedicated to UI components (known as component system events) extends the ComponentSystemEvent class, exactly as PostRestoreStateEvent do:

// javax.faces.event.PostRestoreStateEvent source code
public class PostRestoreStateEvent extends ComponentSystemEvent {
   
 static final long serialVersionUID = -1007196479122154347L;

 public PostRestoreStateEvent(UIComponent component) {
  super(component);
 }
   
 public void setComponent(UIComponent newComponent) {
  this.source = newComponent;
 }
}

Every component (including custom components) extends the JSF UIComponent class, which is the base class for all UI components in JSF. Since UIComponent implements the ComponentSystemEventListener it will listen for system events emitted by components. The emitted events are listen via ComponentSystemEventListener#processEvent() method. By default, UIComponent provides the following implementation (notice that it explicitly treat only the PostRestoreStateEvent event):

//javax.faces.component.UIComponent#processEvent() source code
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
 if (event instanceof PostRestoreStateEvent) {
     assert(this == event.getComponent());
     // if this component has a component value reference expression,
     // make sure to populate the ValueExpression for it.
     ValueExpression valueExpression;
     if (null != (valueExpression = this.getValueExpression("binding"))) {
         valueExpression.setValue(FacesContext.getCurrentInstance().getELContext(), this);
     }
     isCompositeComponent = null;
 }
}

So, the default implementation does exactly what the JSF API documentation says: If the argument event is an instance of PostRestoreStateEvent, call this.getValueExpression(java.lang.String) passing the literal string “binding”, without the quotes, as the argument. If the result is non-null, set the value of the ValueExpression to be this.

Usually, when we write custom components that listen for component system events, we will override the processEvent() method. But, in a dummy scenario, if we have a custom component that registers as a listener for certain component system events but it doesn't override this method, then, when the listen events are emitted, they will "hit" the UIComponent#processEvent() method, and nothing will happen. This is why UIComponent#processEvent() explicitly isolates the PostRestoreStateEvent. For example, let's suppose the below custom component:

@FacesComponent(value = TomComponent.COMPONENT_TYPE, createTag = true)
@ListenerFor(systemEventClass = PostAddToViewEvent.class)
public class TomComponent extends UIComponentBase  {
 ...
}

This will not cause any error, but it will not work as expected because the PostAddToViewEvent will "hit" the UIComponent#processEvent(). So, pay attention to provide the TomComponent#processEvent() (JSF novices usually forget about this).

@FacesComponent(value = TomComponent.COMPONENT_TYPE, createTag = true)
@ListenerFor(systemEventClass = PostAddToViewEvent.class)
public class TomComponent extends UIComponentBase implements ComponentSystemEventListener { // optional implementation
 ...
 @Override // override the UIComponent#processEvent()
 public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
  System.out.println("EVENT EMITTED: " + event);
 }
 ...
}

In the above example, at each postback, will take into account the PostRestoreStateEvent. You will see something like this:

EVENT EMITTED: javax.faces.event.PostAddToViewEvent[source=custom.TomComponent@53c8d26e]
EVENT EMITTED: javax.faces.event.PostRestoreStateEvent[source=custom.TomComponent@53c8d26e]

Since PostRestoreStateEvent is emitted  by all UIComponents, you have to explicitly indicate that you are interested only in PostAddToViewEvent:

@Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
 if (event instanceof PostAddToViewEvent) {
     System.out.println("EVENT EMITTED: " + event);
 }
}

Now, let's focus on the moment when the PostRestoreStateEvent is published. Of course, we already know that this happens at the end of Restore View phase, but, for more details, we have to take a closer look in the RestoreViewPhase implementation - in this post, we will focus on Mojarra implementation, com.sun.faces.lifecycle.RestoreViewPhase. More precisely, we have to look into the RestoreViewPhase#execute() method. Practically, this method encapsulates the code that represents the Restore View phase. This is a pretty big method, so we will list here only the parts that are relevant for PostRestoreStateEvent. Mainly, we have to cases here:

·         If an application had explicitly set the tree in the context (advanced and rare case):

public void execute(FacesContext facesContext) throws FacesException {
 ...
 UIViewRoot viewRoot = facesContext.getViewRoot();
 if (viewRoot != null) {
     ...
     deliverPostRestoreStateEvent(facesContext);

     if (!facesContext.isPostback()) {
         facesContext.renderResponse();
     }
     ...
 }
...
}

·         If an application did not explicitly set the tree in the context (obviously, most probably case):

public void execute(FacesContext facesContext) throws FacesException {
 ...
 try {
     // Reconstitute or create the request tree (initial and postback request are treated here)
 } catch (Throwable fe) {
   ...
 } finally {
   ...              
   deliverPostRestoreStateEvent(facesContext);
   ...
 }
}

The deliverPostRestoreStateEvent() represents the centerpiece of emitting PostRestoreStateEvent. This is a private method that:

·         Publish the PostRestoreStateEvent for the view root. If FacesContext#isProcessingEvents() is true and there are one or more listeners for events of the type PostRestoreStateEvent, call those listeners, passing source as the source of the event.
·         Visit the component tree and set each component as a source of PostRestoreStateEvent. Moreover, invoke each component processEvent() method and pass to it the PostRestoreStateEvent. This uses the JSF Visit Tree API, as below:

// Mojarra implementation,
// com.sun.faces.lifecycle.RestoreViewPhase#deliverPostRestoreStateEvent()
private void deliverPostRestoreStateEvent(FacesContext facesContext) throws FacesException {
 UIViewRoot root = facesContext.getViewRoot();
 final PostRestoreStateEvent postRestoreStateEvent = new PostRestoreStateEvent(root);
 try {
     facesContext.getAttributes().put(SKIP_ITERATION_HINT, true);
     facesContext.getApplication().publishEvent(facesContext, PostRestoreStateEvent.class, root);

     Set<VisitHint> hints = EnumSet.of(VisitHint.SKIP_ITERATION);
     VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, hints);
     root.visitTree(visitContext, new VisitCallback() {
       public VisitResult visit(VisitContext context, UIComponent target) {
        postRestoreStateEvent.setComponent(target);
        target.processEvent(postRestoreStateEvent);
        return VisitResult.ACCEPT;
       }
     });
 } catch (AbortProcessingException e) {
   ...
 }
}

Now, you know how PostRestoreStateEvent works!

Read further: JSF Events Tutorial

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors