JSF2 Composite Component Metadata
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
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:f="http://java.sun.com/jsf/core"
- xmlns:my="http://java.sun.com/jsf/composite/cc"
- xmlns:meta="http://mojarra.dev.java.net/cc-metadata">
- <h:head>
- <style type="text/css">
- .grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9; }
- </style>
- </h:head>
- <h:body>
- <h:form>
- <div id="cc" class="grayBox" style="border: 1px solid #090;">
- <my:myComponent loginAction="#{userBean.loginAction}" />
- </div>
- <div id="foo" class="grayBox" style="border: 1px solid #090;">
- <pre>
- <meta:printMetadata viewName="main.xhtml" libraryName="cc"
- resourceName="myComponent.xhtml"/>
- </pre>
- </div>
- </h:form>
- </h:body>
- </html>
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
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:f="http://java.sun.com/jsf/core"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:fmd="http://java.sun.com/xml/ns/javaee/faces/design-time-metadata"
- xmlns:cc="http://java.sun.com/jsf/composite">
- <head>
- </head>
- <body>
- <cc:interface
- name="component"
- displayName="A really fancy composite component"
- expert="true"
- hidden="false"
- preferred="true"
- shortDescription="This component has all the supported metadata."
- extraTopLevelAttribute="Whatever I want here">
- <!-- start top level attributes -->
- <cc:attribute
- name="model"
- required="true"
- displayName="model"
- expert="false"
- hidden="false"
- preferred="true"
- shortDescription="The model for this component"
- extraTopLevelAttribute="Whatever I want here">
- <cc:extension>
- <fmd:expert>true</fmd:expert>
- </cc:extension>
- <cc:attribute
- name="userid"
- default="guest"
- displayName="User Id"
- expert="false"
- hidden="false"
- preferred="true"
- shortDescription="The model must have a userid property"/>
- <cc:attribute
- name="password"
- default="guest"
- displayName="Password"
- expert="false"
- hidden="false"
- preferred="true"
- shortDescription="The model must have a password property"/>
- </cc:attribute>
- <cc:attribute name="useridLabel" default="Userid:" />
- <cc:attribute name="passwordLabel" default="Password:" />
- <cc:attribute name="loginButtonLabel" default="Login" />
- <cc:attribute name="loginAction" method-signature="java.lang.String f()"
- required="true" />
- <!-- end top level attributes -->
- <!-- start facets -->
- <cc:facet
- name="header"
- displayName="The header facet for the fancy component"
- expert="true"
- hidden="false"
- preferred="false"
- shortDescription="If you want a header, this is where you put it"
- anotherExtraAttribute="Lots of metadata" />
- <cc:facet
- name="footer"
- displayName="The footer facet for the fancy component"
- expert="true"
- hidden="false"
- preferred="false"
- shortDescription="If you want a footer, this is where you put it"
- anotherExtraAttribute="Lots of metadata" />
- <!-- end facets -->
- <!-- start attached objects -->
- <cc:editableValueHolder
- name="userid"
- displayName="Userid field"
- expert="true"
- hidden="false"
- preferred="false"
- shortDescription="Attach a converter or validator here, if you like"
- someExtraMetadata="userid metadata" />
- <cc:editableValueHolder
- name="password"
- displayName="Password field"
- expert="true"
- hidden="false"
- preferred="false"
- shortDescription="Attach a converter or validator here, if you like"
- someExtraMetadata="password metadata" />
- <cc:actionSource
- name="login"
- displayName="Login button"
- expert="true"
- hidden="false"
- preferred="false"
- shortDescription="Attach an actionListener here, if you like"
- someExtraMetadata="login metadata" />
- <!-- end attached objects -->
- </cc:interface>
- <cc:implementation>
- <cc:renderFacet name="header" />
- <p><h:outputLabel for="#{cc.clientId}:userid"
- value="#{cc.attrs.useridLabel}" />
- <h:inputText id="userid" /></p>
- <p><h:outputLabel for="#{cc.clientId}:password"
- value="#{cc.attrs.passwordLabel}" />
- <h:inputText id="password" /></p>
- <p><h:commandButton value="#{cc.attrs.loginButtonLabel}"
- action="#{cc.attrs.loginAction}" /></p>
- <cc:renderFacet name="footer" />
- </cc:implementation>
- </body>
- </html>
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
- <facelet-taglib xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
- xmlns='http://java.sun.com/xml/ns/javaee'
- xsi:schemaLocation='http://java.sun.com/xml/ns/javaee web-facelettaglibrary_2_0.xsd'
- version="2.0">
- <namespace>http://mojarra.dev.java.net/cc-metadata</namespace>
- <tag>
- <tag-name>printMetadata</tag-name>
- <component>
- <component-type>jsf2.PrintMetadata</component-type>
- </component>
- <!-- these are advisory and not enforced by the runtime -->
- <attribute>
- <name>viewName</name>
- <required>true</required>
- </attribute>
- <attribute>
- <name>libraryName</name>
- <required>true</required>
- </attribute>
- <attribute>
- <name>resourceName</name>
- <required>true</required>
- </attribute>
- </tag>
- </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
- package jsf2;
- import java.beans.BeanInfo;
- import java.io.IOException;
- import javax.faces.FacesException;
- import javax.faces.application.Resource;
- import javax.faces.component.FacesComponent;
- import javax.faces.component.UIOutput;
- import javax.faces.context.FacesContext;
- import javax.faces.context.ResponseWriter;
- import javax.faces.view.ViewDeclarationLanguage;
- @FacesComponent(value="jsf2.PrintMetadata")
- public class PrintMetadata extends UIOutput {
- @Override
- ResponseWriter writer = context.getResponseWriter();
- viewName,
- libraryName,
- resourceName;
- try {
- viewName = this.getAttributes().get("viewName").toString();
- libraryName = this.getAttributes().get("libraryName").toString();
- resourceName = this.getAttributes().get("resourceName").toString();
- throw new FacesException("Must supply viewName, libraryName and resourceName attributes");
- }
- ViewDeclarationLanguage vdl = context.getApplication().
- getViewHandler().getViewDeclarationLanguage(context, viewName);
- Resource compositeComponentResource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);
- CompositeComponentMetadataUtils.writeMetadata(metadata, writer);
- }
- }
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
- package jsf2;
- import java.beans.BeanDescriptor;
- import java.beans.BeanInfo;
- import java.beans.FeatureDescriptor;
- import java.beans.PropertyDescriptor;
- import java.io.IOException;
- import java.util.Enumeration;
- import java.util.List;
- import java.util.Map;
- import javax.el.ValueExpression;
- import javax.faces.component.UIComponent;
- import javax.faces.context.FacesContext;
- import javax.faces.context.ResponseWriter;
- import javax.faces.view.AttachedObjectTarget;
- public class CompositeComponentMetadataUtils {
- /**
- * <p>Use the composite component metadata specification
- * in section JSF.3.6.2.1 to print out the metadata to
- * the argument writer.</p>
- * @throws IOException
- */
- // Print out the top level BeanDescriptor stuff.
- writeFeatureDescriptor("composite-component-BeanDescriptor", descriptor,
- writer);
- writeFeatureDescriptorValues(
- "composite-component-BeanDescriptor", descriptor,
- writer);
- writeFeatureDescriptor("composite-component-attribute", cur,
- writer);
- writeFeatureDescriptorValues("composite-component-attribute", cur,
- writer);
- }
- }
- writer.write(prefix + "-name:" +
- fd.getName() + "\n");
- writer.write(prefix + "-displayName:" +
- fd.getDisplayName() + "\n");
- writer.write(prefix + "-shortDescription:" +
- fd.getShortDescription() + "\n");
- writer.write(prefix + "-expert:" +
- fd.isExpert() + "\n");
- writer.write(prefix + "-hidden:" +
- fd.isHidden() + "\n");
- writer.write(prefix + "-preferred:" +
- fd.isPreferred() + "\n");
- }
- Enumeration<String> extraValues = fd.attributeNames();
- String curName;
- while (extraValues.hasMoreElements()) {
- curName = extraValues.nextElement();
- if (curName.equals(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY)) {
- List<AttachedObjectTarget> attachedObjects =
- (List<AttachedObjectTarget>) fd.getValue(curName);
- for (AttachedObjectTarget curTarget : attachedObjects) {
- writer.write(prefix + "-attached-object-" + curTarget.getName() + "\n");
- }
- } else if (curName.equals(UIComponent.FACETS_KEY)) {
- Map<String, PropertyDescriptor> facets =
- (Map<String, PropertyDescriptor>) fd.getValue(curName);
- writeFeatureDescriptor(facetPrefix, facets.get(cur),
- writer);
- writeFeatureDescriptorValues(facetPrefix,
- facets.get(cur), writer);
- }
- } else {
- ValueExpression ve = (ValueExpression) fd.getValue(curName);
- writer.write(prefix + "-extra-attribute-" + curName + ": " +
- ve.getValue(FacesContext.getCurrentInstance().getELContext())
- + "\n");
- }
- }
- }
- }
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>.AttachedObjectTargetencapsulates 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 thePropertyDescriptorrepresents 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.

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: edburns
- Login or register to post comments
- Printer-friendly version
- edburns's blog
- 6540 reads






Comments
Hi, In first thanks for this article and sorry for my ...
by adrien5 - 2011-08-17 12:56
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 ...
by edburns - 2011-08-17 15:18
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?
by jaxent - 2010-03-04 12:13
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
by mcalpay - 2009-10-26 08:20
One common composite component I used with jsf 1.2 + facelets is : Which I use it like :