Skip to main content

JSF2 Composite Component Metadata

Posted by edburns on September 2, 2009 at 9:53 AM PDT

This ultra-quick blog entry shows how to use the JSF runtime to
access metadata for a composite component. Note that most of the
metadata is optional when creating a composite component, therefore,
this entry will be of interest to tool vendors and those wishing to
write composite components that stand a chance of showing up nicely in
tools.

The Using Page

When showing a composite component demo, I always like to start out
with the using page.

Listing 1: the using page

  1. <html xmlns="http://www.w3.org/1999/xhtml"
  2.      xmlns:h="http://java.sun.com/jsf/html"
  3.      xmlns:f="http://java.sun.com/jsf/core"
  4.      xmlns:my="http://java.sun.com/jsf/composite/cc"
  5.      xmlns:meta="http://mojarra.dev.java.net/cc-metadata">
  6.   <title>A Simple JavaServer Faces 2.0 View</title>
  7. <style type="text/css">
  8. .grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9;  }
  9.  
  10. </h:head>
  11.  
  12.  
  13.     <p>Composite Component usage:</p>
  14.  
  15. <div id="cc" class="grayBox" style="border: 1px solid #090;">
  16. ="#{userBean.loginAction}" />
  17. </div>
  18.  
  19. <h2>Metada for This Composite Component</h2>
  20.  
  21. <div id="foo" class="grayBox" style="border: 1px solid #090;">
  22.     <pre>
  23.     ="main.xhtml" libraryName="cc"
  24.                        resourceName="myComponent.xhtml"/>
  25.     </pre>
  26. </div>
  27.  
  28.  
  29.     <p>value="submit" /></p>
  30.   </h:form>
  31. </h:body>
  32. </html>
  33.  

Line 5 declares the namespace “my” using the handy
composite component namespace prefix
“http://java.sun.com/jsf/composite” followed by
“cc”. JSF 2 interprets this to mean: treat any .xhtml files
in the directory /resources/cc as a JSF composite
component.

Line 6 declares a Facelet custom tag library with the namespace
“http://mojarra.dev.java.net/cc-metadata”.

Line 20 uses the composite component named “myComponent”
in the “cc” library.

Line 27 uses the “printMetadata” component from the
Facelet custom tag library. We must pass in the name of a Facelet page,
the library name, and the resource name.

The Composite Component

Let's examine the composite component itself. As I mentioned
earlier, most of this metadata is optional and is included here just to
show what the system understands. This component has just about every
kind of metadata that is in the JSF2 spec.

Listing 2 the composite component

  1. <html xmlns="http://www.w3.org/1999/xhtml"
  2.      xmlns:h="http://java.sun.com/jsf/html"
  3.      xmlns:f="http://java.sun.com/jsf/core"
  4.      xmlns:ui="http://java.sun.com/jsf/facelets"
  5.      xmlns:fmd="http://java.sun.com/xml/ns/javaee/faces/design-time-metadata"
  6.      xmlns:cc="http://java.sun.com/jsf/composite">
  7.  
  8. <title>Not present in rendered output</title>
  9.  
  10. </head>
  11.  
  12.  
  13.    name="component"
  14.    displayName="A really fancy composite component"
  15.    expert="true"
  16.    hidden="false"
  17.    preferred="true"
  18.    shortDescription="This component has all the supported metadata."
  19.    extraTopLevelAttribute="Whatever I want here">
  20.  
  21.  
  22.  
  23.    name="model"
  24.    required="true"
  25.    displayName="model"
  26.    expert="false"
  27.    hidden="false"
  28.    preferred="true"
  29.    shortDescription="The model for this component"
  30.    extraTopLevelAttribute="Whatever I want here">
  31.    
  32.       true</fmd:expert>
  33.     </cc:extension>
  34.    
  35.      name="userid"
  36.      default="guest"
  37.      displayName="User Id"
  38.      expert="false"
  39.      hidden="false"
  40.      preferred="true"
  41.      shortDescription="The model must have a userid property"/>
  42.    
  43.      name="password"
  44.      default="guest"
  45.      displayName="Password"
  46.      expert="false"
  47.      hidden="false"
  48.      preferred="true"
  49.      shortDescription="The model must have a password property"/>
  50.   </cc:attribute>
  51.  
  52.   name="useridLabel" default="Userid:" />
  53.  
  54.   name="passwordLabel" default="Password:" />
  55.  
  56.   name="loginButtonLabel" default="Login" />
  57.  
  58.   name="loginAction" method-signature="java.lang.String f()"
  59.    required="true" />
  60.  
  61.  
  62.  
  63.  
  64.    name="header"
  65.    displayName="The header facet for the fancy component"
  66.    expert="true"
  67.    hidden="false"
  68.    preferred="false"
  69.    shortDescription="If you want a header, this is where you put it"
  70.    anotherExtraAttribute="Lots of metadata" />
  71.  
  72.  
  73.    name="footer"
  74.    displayName="The footer facet for the fancy component"
  75.    expert="true"
  76.    hidden="false"
  77.    preferred="false"
  78.    shortDescription="If you want a footer, this is where you put it"
  79.    anotherExtraAttribute="Lots of metadata" />
  80.  
  81.  
  82.  
  83.  
  84.    name="userid"
  85.    displayName="Userid field"
  86.    expert="true"
  87.    hidden="false"
  88.    preferred="false"
  89.    shortDescription="Attach a converter or validator here, if you like"
  90.    someExtraMetadata="userid metadata" />
  91.  
  92.  
  93.    name="password"
  94.    displayName="Password field"
  95.    expert="true"
  96.    hidden="false"
  97.    preferred="false"
  98.    shortDescription="Attach a converter or validator here, if you like"
  99.    someExtraMetadata="password metadata" />
  100.  
  101.  
  102.    name="login"
  103.    displayName="Login button"
  104.    expert="true"
  105.    hidden="false"
  106.    preferred="false"
  107.    shortDescription="Attach an actionListener here, if you like"
  108.    someExtraMetadata="login metadata" />
  109.  
  110.  
  111. </cc:interface>
  112.  
  113.  
  114.   name="header" />
  115.  
  116.   <p>for="#{cc.clientId}:userid"
  117.                    value="#{cc.attrs.useridLabel}" />
  118.      id="userid" /></p>
  119.  
  120.   <p>for="#{cc.clientId}:password"
  121.                    value="#{cc.attrs.passwordLabel}" />
  122.      id="password" /></p>
  123.  
  124.   <p>value="#{cc.attrs.loginButtonLabel}"
  125.                      action="#{cc.attrs.loginAction}" /></p>
  126.  
  127.   name="footer" />
  128.  
  129.  
  130. </cc:implementation>
  131.  
  132. </body>
  133.  
  134. </html>
  135.  

Lines 16 - 23 declare the top level composite component metadata.

Lines 25 - 65 declare the attributes that may be passed as XML
elements on the composite component instance in the using page. Note
that the loginAction attribute is declared as required on
line 63. This is useful because Facelets will put up a helpful error if
the page author doesn't supply a value here.

Lines 68 - 88 declare the allowable facets. In this case, they're optional.

Lines 90 - 119 declare the attached objects within the implementation
section that are publically accessible from the using page.

Programmatically accessing the metadata

Up to this point, everything I've shown has been included in other
blog entries elsewhere. What hasn't been shown is how the JSF runtime
uses the metadata API to actually conjure up the composite component
instance. This API is also accessible to tools that want to embed a JSF
runtime into their tool environment. To show this off, I've created a
non-composite JSF component and exposed it in the usual way, with a
Facelet taglib. This still requires a descriptor, which is placed in
WEB-INF/classes/META-INF.

Listing 3 the cc-metadata.taglib.xml

  1.  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
  2.   xmlns='http://java.sun.com/xml/ns/javaee'
  3.   xsi:schemaLocation='http://java.sun.com/xml/ns/javaee web-facelettaglibrary_2_0.xsd'
  4.   version="2.0">
  5.     >http://mojarra.dev.java.net/cc-metadata>
  6.     >
  7.         >printMetadata>
  8.         >
  9.             >jsf2.PrintMetadata>
  10.         >
  11.         >
  12.             >viewName>
  13.             >true>
  14.         >
  15.         >
  16.             >libraryName>
  17.             >true>
  18.         >
  19.         >
  20.             >resourceName>
  21.             >true>
  22.         >
  23.     >
  24.  
  25. >

Lines 11 - 23 are optional but I like to include them for explicit
documentation.

The JSF Component that shows how to access the metadata is in Listing
4 A. This listing covers obtaining a reference to the
BeanInfo that is the composite component metadata.

Listing 4 A, Accessing the composite component metadata programmatically

  1. package jsf2;
  2.  
  3. import java.beans.BeanInfo;
  4. import java.io.IOException;
  5. import javax.faces.FacesException;
  6. import javax.faces.application.Resource;
  7. import javax.faces.component.FacesComponent;
  8. import javax.faces.component.UIOutput;
  9. import javax.faces.context.FacesContext;
  10. import javax.faces.context.ResponseWriter;
  11. import javax.faces.view.ViewDeclarationLanguage;
  12.  
  13. @FacesComponent(value="jsf2.PrintMetadata")
  14. public class PrintMetadata extends UIOutput {
  15.  
  16.     @Override
  17.     public void encodeAll(FacesContext context) throws IOException {
  18.         ResponseWriter writer = context.getResponseWriter();
  19.         String
  20.                 viewName,
  21.                 libraryName,
  22.                 resourceName;
  23.  
  24.         try {
  25.             viewName = this.getAttributes().get("viewName").toString();
  26.             libraryName = this.getAttributes().get("libraryName").toString();
  27.             resourceName = this.getAttributes().get("resourceName").toString();
  28.         } catch (NullPointerException e) {
  29.             throw new FacesException("Must supply viewName, libraryName and resourceName attributes");
  30.         }
  31.  
  32.         ViewDeclarationLanguage vdl = context.getApplication().
  33.                 getViewHandler().getViewDeclarationLanguage(context, viewName);
  34.         Resource compositeComponentResource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);
  35.  
  36.         BeanInfo metadata = vdl.getComponentMetadata(context, compositeComponentResource);
  37.  
  38.         CompositeComponentMetadataUtils.writeMetadata(metadata, writer);
  39.  
  40.     }
  41. }
  42.  

On lines 24 - 29 we extract the required attributes that have been
passed to us via the page author.

On line 32, we obtain a reference to the
ViewDeclarationLanguage instance for Facelets.

On line 34, we create a Resource instance using the
passed in composite component library name and resource name.

Line 36 features the call o getComponentMetadata(). It
is safe to call this method only during the context of the JSF request
processing lifecycle. Because we're doing it from inside the rendering
of a component, we know that this is such a context.

Finally, Listing 4 B shows a traversal of the metadata.

Listing 4 B, what you can do with the metadata

  1. package jsf2;
  2.  
  3. import java.beans.BeanDescriptor;
  4. import java.beans.BeanInfo;
  5. import java.beans.FeatureDescriptor;
  6. import java.beans.PropertyDescriptor;
  7. import java.io.IOException;
  8. import java.util.Enumeration;
  9. import java.util.List;
  10. import java.util.Map;
  11. import javax.el.ValueExpression;
  12. import javax.faces.component.UIComponent;
  13. import javax.faces.context.FacesContext;
  14. import javax.faces.context.ResponseWriter;
  15. import javax.faces.view.AttachedObjectTarget;
  16.  
  17. public class CompositeComponentMetadataUtils {
  18.  
  19.     /**
  20.      *

    Use the composite component metadata specification

  21.      * in section JSF.3.6.2.1 to print out the metadata to
  22.      * the argument writer.

  23.      * @throws IOException
  24.      */
  25.  
  26.     public static void writeMetadata(BeanInfo metadata,
  27.             ResponseWriter writer) throws IOException{
  28.  
  29.         // Print out the top level BeanDescriptor stuff.
  30.         BeanDescriptor descriptor = metadata.getBeanDescriptor();
  31.         writeFeatureDescriptor("composite-component-BeanDescriptor", descriptor,
  32.                 writer);
  33.         writeFeatureDescriptorValues(
  34.                 "composite-component-BeanDescriptor", descriptor,
  35.                 writer);
  36.         PropertyDescriptor attributes[] = metadata.getPropertyDescriptors();
  37.         for (PropertyDescriptor cur : attributes) {
  38.             writeFeatureDescriptor("composite-component-attribute", cur,
  39.                     writer);
  40.             writeFeatureDescriptorValues("composite-component-attribute", cur,
  41.                     writer);
  42.         }
  43.     }
  44.  
  45.     public static void writeFeatureDescriptor(String prefix,
  46.             FeatureDescriptor fd, ResponseWriter writer) throws IOException {
  47.  
  48.         writer.write(prefix + "-name:" +
  49.                 fd.getName() + "\n");
  50.         writer.write(prefix + "-displayName:" +
  51.                 fd.getDisplayName() + "\n");
  52.         writer.write(prefix + "-shortDescription:" +
  53.                 fd.getShortDescription() + "\n");
  54.         writer.write(prefix + "-expert:" +
  55.                 fd.isExpert() + "\n");
  56.         writer.write(prefix + "-hidden:" +
  57.                 fd.isHidden() + "\n");
  58.         writer.write(prefix + "-preferred:" +
  59.                 fd.isPreferred() + "\n");
  60.  
  61.     }
  62.  
  63.     public static void writeFeatureDescriptorValues(String prefix,
  64.             FeatureDescriptor fd, ResponseWriter writer) throws IOException {
  65.  
  66.         Enumeration<String> extraValues = fd.attributeNames();
  67.         String curName;
  68.         while (extraValues.hasMoreElements()) {
  69.             curName = extraValues.nextElement();
  70.             if (curName.equals(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY)) {
  71.                 List<AttachedObjectTarget> attachedObjects =
  72.                         (List<AttachedObjectTarget>) fd.getValue(curName);
  73.                 for (AttachedObjectTarget curTarget : attachedObjects) {
  74.                     writer.write(prefix + "-attached-object-" + curTarget.getName() + "\n");
  75.                 }
  76.             } else if (curName.equals(UIComponent.FACETS_KEY)) {
  77.                 Map<String, PropertyDescriptor> facets =
  78.                         (Map<String, PropertyDescriptor>) fd.getValue(curName);
  79.                 for (String cur : facets.keySet()) {
  80.                     String facetPrefix = prefix + "-facet-" + cur;
  81.                     writeFeatureDescriptor(facetPrefix, facets.get(cur),
  82.                             writer);
  83.                     writeFeatureDescriptorValues(facetPrefix,
  84.                             facets.get(cur), writer);
  85.                 }
  86.             } else {
  87.                 ValueExpression ve = (ValueExpression) fd.getValue(curName);
  88.                 writer.write(prefix + "-extra-attribute-" + curName + ": " +
  89.                         ve.getValue(FacesContext.getCurrentInstance().getELContext())
  90.                         + "\n");
  91.             }
  92.         }
  93.     }
  94.  
  95.  
  96. }
  97.  

On line 30, we ask the argument BeanInfo for its
BeanDescriptor. We pass this to the helper method
writeFeatureDescriptor(). This simple method just prints
out the standard FeatureDescriptor metadata.

On line 33, we pass the same descriptor to the helper method
writeFeatureDescriptorValues(). This method steps through
the Enumeration returned from
attributeNames(), calls getValue() on each
entry, and takes appropriate action on each entry, based on the metadata
specification. Currently there are three kinds of entries.

  • Lines 70 - 75 deal with the attached objects. The type
    for this entry is
    List<AttachedObjectTarget>.
    AttachedObjectTarget encapsulates how an attached
    object, in this declared on lines 90 through 119 of Listing 1,
    exposes an inner component to the page author.

  • Lines 76 - 85 deal with the exposed facets. The type
    for this entry is

    Map<String,
      PropertyDescriptor>
    , where the key is the facet name
    and the PropertyDescriptor represents each
    individual <cc:facet> element. These are
    on lines 68 - 88 of Listing 1.

  • Lines 87 - 91 deal with the else case, which can be
    characterized as “if it's not the attached objects, and
    it's not the facets, then it'll just come to you as a
    ValueExpression.

Back up on line 36, we ask the metadata for the
PropertyDescriptor [] using
getPropertyDescriptors(). This is specified to return a
PropertyDescritor for each of the attributes, in this case
show on lines 25 - 64 of Listing 1. For each attribute, we print out
the FeatureDescriptor and the values. However, note that
in the case of attributes, there will only be
ValueExpression instances in the getValue()
data structure. A screengrab of the running page is show here.

screen grab of page showing component metadata

When the Expert Group designed this api, we wanted to make it as easy
as possible to provide tool support for composite components. I hope
this is helpful to tool authors for this purpose.

Technorati Tags:

Related Topics >>

Comments

&nbsp;Hi, In first thanks for this article and sorry for my ...

Hi,

In first thanks for this article and sorry for my english.

I have a question about composite, I would like to create composite 'autonome'. So I have some difficulities to initialize it.

For example, i take a composite to edit a user like :

<mycomposite userId="theidOfTheUserToShow">

and for implementation

<composite:interface>

<composite:attribute name="userId" required="true" />

</composite:interface>

<composite:implementation>

Edit : #{ctrlEditUser.username} ....

</.....>

My problem is what is the best way to initialize backean? There is a way to do #{ctrlEditUser.init(userId)}, or anything way?

Thanks

In JSF 2.2, we'll have UIViewAction and the ...

In JSF 2.2, we'll have UIViewAction and the <f:viewAction> tag. 

In JSF 2.1 and 2.0 you can use f:event, as shown in this demo:

svn.java.net/svn/mojarra~svn/trunk/jsf-ri/systest/web/viewParameters/events.xhtml

Which is included from

svn.java.net/svn/mojarra~svn/trunk/jsf-ri/systest/web/viewParameters/page02.xhtml

 

Is there a doc tool for this yet?

The Tag Library Document Generator doesn't work against either taglib.xml or Composite component metadata that I have been able to see.

about layout

One common composite component I used with jsf 1.2 + facelets is : Which I use it like :