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

luni, 16 noiembrie 2015

JSF view metadata facet demystified

By definition, metadata refers to data about the data.

The JSF metadata are declared via view metadata facet (lives in the view or in a template client).
The JSF view metadata facet is tied up to view parameters and view actions.
       - view parameters used  to set in managed beans/ preserve over views, convert and validate the GET parameters (<f:viewParam>)
       - view actions or performing business action on GET parameters (<f:viewAction> and <f:event>)

History
Now, the JSF view is programmatically represented by the component root (UIViewRoot). Some of the supported metadata includes locale, content type, encoding, render kit, phase listeners, etc (all these are information needed by the current view). Probably, you are used to see something like this:

<f:view locale="#{fooBean.fooLocale}" encoding="UTF-8" contentType="text/html">

Starting with JSF 2.2 a view can support stateless feature, and this is indicated as:

 <f:view transient="true">
 ...
</f:view>

But these "per attribute" based metadata are pretty simple, while the goal of supporting view parameters/actions implies complex metadata. But, <f:view> cannot handle such complex metadata, so JSF 2.0 introduced the view metadata facet.

So, this facet (by default, ignored in a UI component tree visit) is dedicated to the UIViewRoot and its name is, javax_faces_metadata. Since the metadata facet can contain UI components, it practically opens a new perspective on supporting metadata. In addition, metadata are "decoupled" from view and accessible on demand. The view metadata facet can be declared as any other JSF facet, only that it lives in the <f:view> tag:

<f:view>
 <f:facet name="javax_faces_metadata">
  ...
 </f:facet>
 ...
</f:view>

Beside the "per attribute" based metadata , starting with JSF 2.0 more metadata are supported. First, we have the view parameters that instructs JSF  about how request parameters should be handled when a view is either requested or linked (e.g. from bookmark). Beside the view parameters, in the meta-model we have view actions. The story behind view actions is pretty simple: view parameters cannot provide action invocation and navigation, which implies lazy loading the data. JSF 2.0 fixes this issue via <f:event>, but this has several drawbacks (e.g. the pointed listener is invoke at each request, it doesn't support navigations, etc), so starting with JSF 2.2, we have view actions (<f:viewAction>).

Well, after the view parameters have been added, the view metadata facet also gets a shortcut for the above <f:facet> form. This is <f:metadata> tag. So, now we write:

<f:view>
 <f:metadata>
  ...
 </f:metadata>
 ...
</f:view>

Or, even without the optional <f:view>:

<f:metadata>
 ...
</f:metadata>

Code
The ViewMetadata contract is defined in java.faces.view as an abstract class. Beside its abstract methods, this class implements the methods for obtaining the current view parameters/actions and a flag method that determines if the provided UIViewRoot has a view metadata facet with children.

Let's take a look to the  ViewMetadata#getViewParameters() - check out the highlighted comments:

// this method return a collection of view parameters associated with the passed root
public static Collection<UIViewParameter> getViewParameters(UIViewRoot root) {

 // the collection of view parameters
 Collection<UIViewParameter> params;
 // public static final String METADATA_FACET_NAME = "javax_faces_metadata";
 UIComponent metadataFacet = root.getFacet(UIViewRoot.METADATA_FACET_NAME);

 // the presence of view metadata facet present is mandatory
 if (metadataFacet == null) {
     // there is no view metadata facet present,
     // so return an empty collection of view parameters
     params = Collections.emptyList();
 } else {
     // start collecting view parameters
     params = new ArrayList<UIViewParameter>();
     // view parameters are instances of UIViewParamter class
     // and children of view metadata facet
     List<UIComponent> children = metadataFacet.getChildren();
     int len = children.size();
     for (int i = 0; i < len; i++) {
          UIComponent c = children.get(i);
          // ensure that the current children is a view parameter
          if (c instanceof UIViewParameter) {            
              params.add((UIViewParameter) c);
          }
     }
 }

 // return the list of collected view parameters
 return params;
}

Further, let's take a look to the ViewMetadata#getViewActions() - check out the highlighted comments:

// this method return a collection of view actions associated with the passed root
public static Collection<UIViewAction> getViewActions(UIViewRoot root) {

 // the collection of view actions
 Collection<UIViewAction> actions;
 // public static final String METADATA_FACET_NAME = "javax_faces_metadata";
 UIComponent metadataFacet = root.getFacet(UIViewRoot.METADATA_FACET_NAME);

 // the presence of view metadata facet present is mandatory
 if (metadataFacet == null) {
     // there is no view metadata facet present,
     // so return an empty collection of view actions
     actions = Collections.emptyList();
 } else {
     actions = new ArrayList<UIViewAction>();
     // view actions are instances of UIViewAction class
     // and children of view metadata facet
     List<UIComponent> children = metadataFacet.getChildren();
     int len = children.size();
     for (int i = 0; i < len; i++) {
          UIComponent c = children.get(i);
          // ensure that the current children is a view action
          if (c instanceof UIViewAction) {
              actions.add((UIViewAction) c);
          }
     }
 }
     
 // return the list of collected view actions
 return actions;
}

Finally, let's have the same thing for ViewMetadata#hasMetadata():

// this method check the view metadata facet presence for the passed root
public static boolean hasMetadata(UIViewRoot root) {
 // "suppose" it doesn't exit
 boolean result = false;
       
 // public static final String METADATA_FACET_NAME = "javax_faces_metadata";
 UIComponent metadataFacet = root.getFacet(UIViewRoot.METADATA_FACET_NAME);
 if (null != metadataFacet) {
     // return true if the facet has children
     // return false otherwise
     result = 0 < metadataFacet.getChildCount();
 }
       
 return result;
}

Now, an implementation (extension) of ViewMetadata must create the facet (createMetadataView()) and return the view ID (getViewId()) for it. Before we will talk about the Mojarra implementation (com.sun.faces.application.view.ViewMetadataImpl) let's point out the moment when the view metadata facet enters into scene.

For this, we need to focus on the Restore View phase execution. This is the first phase in JSF lifecycle, and when the current request is not postback, JSF will create the view metadata facet via ViewMetadata#createMetadataView(). So, the view metadata facet is created only once per view, at initial request! This is not re-created on postbacks! The relevant code is:

// Mojarra 2.2.9, com.sun.faces.lifecycle.RestoreViewPhase#execute() snippet of code
public void execute(FacesContext facesContext) throws FacesException {
 ...
 UIViewRoot viewRoot = facesContext.getViewRoot();
 ...
 boolean isPostBack = (facesContext.isPostback() && !isErrorPage(facesContext));
 if (isPostBack) {
     ...
     // this should be an initial request only (not postback)
 } else {
     ...
     ViewMetadata metadata  = null;

     // vdl is ViewDeclarationLanguage instance
     if (vdl != null) {
         // instantiate the ViewMetadataImpl with the current view ID
         // behind vdl.getViewMetadata() we have:
         /*
            @Override
            public ViewMetadata getViewMetadata(FacesContext context, String viewId) {
             Util.notNull("context", context);
             Util.notNull("viewId", viewId);

             return new ViewMetadataImpl(viewId);
            }
         */  
         metadata = vdl.getViewMetadata(facesContext, viewId);
                   
         if (metadata != null) {
             // create the ViewRoot; this will have, at most
             // the UIViewRoot and its metadata facet
             viewRoot = metadata.createMetadataView(facesContext);
                       
             // if there is no metadata go to render response;
             // so, if there is a metadata JSF "forces" the execution of all JSF
             // phases at the initial request
             if (!ViewMetadata.hasMetadata(viewRoot)) {
                 facesContext.renderResponse();
             }
         }
     }
 }
 ...
}

So, now you know when the view metadata facet is created. The creation take place in ViewMetadataImpl#createMetadataView(), via the javax.faces.view.facelets.Facelet:

// Mojarra 2.2.9, ViewMetadataImpl#createMetadataView() snippet of code
import javax.faces.view.facelets.Facelet;
...
public class ViewMetadataImpl extends ViewMetadata {
 ...
 @Override
 public UIViewRoot createMetadataView(FacesContext context) {
  ...
  Facelet f = faceletFactory.getMetadataFacelet(context, result.getViewId());
  f.apply(context, result);
  ...
 }
}

Now, you should easily understand how the below code for validating view parameters works:

<h:body>
 #{facesContext.validationFailed ?
  (vdl = facesContext.application.viewHandler.getViewDeclarationLanguage(facesContext, view.viewId);
   viewMetadata = vdl.getViewMetadata(facesContext, view.viewId);
   allViewParams = viewMetadata.getViewParameters(view);
   allViewParams.size() gt 0 ? (allViewParams.stream().forEach((t) ->
    (t.valid ? '': (facesContext.externalContext.responseCommitted ? '' :
   facesContext.externalContext.responseSendError(400,"One of your view parameters is invalid !");
   facesContext.responseComplete())))):''):''}
 <h:outputText value="All view parameters are valid!"/>
</h:body>

So, the main advantages of view metadata facet are:

- we can use complex metadata as view parameters/actions
- metadata declarations can be shared across multiple views
- metadata lives in a view or in a template client
- can be easily processed

Metadata is just a way of configuring the view parameters and actions that should take place before the view is rendered without user interaction. Think to a view parameter which is basically a UIInput, only that it doesn't render a <input type="text" value="..."/> and it is submitted without the need for the user to press a button. In a rough manner, you can think that the content of <f:metadata> is auto-submitted. Normally, an user cannot submit a form until the view is ready. So, <f:metadata> is perfect for initializing the current view (and executing actions) based on information provided from previous views. Think that the <f:metadata> gives to JSF the current view's metadata. Since this tag is about current view metadata it doesn't participate in XHTML templates (the page author must ensure that the <f:metadata> element does not appear on a template or included page; it can be in a template client) and it is direct child of <f:view>.

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