Skip to main content

JSF2 Composite Component Metadata

Posted by edburns on September 2, 2009 at 12:53 PM EDT

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

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

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. <facelet-taglib  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.     <namespace>http://mojarra.dev.java.net/cc-metadata</namespace>
  6.     <tag>
  7.         <tag-name>printMetadata</tag-name>
  8.         <component>
  9.             <component-type>jsf2.PrintMetadata</component-type>
  10.         </component>
  11. <!-- these are advisory and not enforced by the runtime -->
  12.         <attribute>
  13.             <name>viewName</name>
  14.             <required>true</required>
  15.         </attribute>
  16.         <attribute>
  17.             <name>libraryName</name>
  18.             <required>true</required>
  19.         </attribute>
  20.         <attribute>
  21.             <name>resourceName</name>
  22.             <required>true</required>
  23.         </attribute>
  24.     </tag>
  25.  
  26. </facelet-taglib>

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.      * <p>Use the composite component metadata specification
  21.      * in section JSF.3.6.2.1 to print out the metadata to
  22.      * the argument writer.</p>
  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 :