http://www.developer.com/

Back to article

JSF 2.0: Creating Composite Components


March 8, 2010

The new release of JavaServer Faces, JSF 2.0, is a major release for the specification. This newest version of the Java component UI framework for building dynamic web pages, which will be part of the Java Enterprise Edition 6 platform, has several interesting new features that make development and deployment of JSF applications simple and easy.

At the highest level, JSF technology provides an API for creating, managing, and handling UI components and a tag library for using components within a web page. The JSF 2.0 release simplifies the web developer's life by providing the following:

  • Reusable UI components for easy authoring of web pages
  • Well defined and simple transfer of application data to and from the UI
  • Easy state management across server requests
  • Simplified event handling model
  • Easy creation of custom UI components

A particularly interesting new feature introduced in JSF 2.0 is customized UI components called composite components. A UI component is a reusable piece of a user interface created for specific functionality with a pre-defined set of validators, converters, and listeners. A composite component can be used as a single component in the view.

With JSF 1.x releases, the creation of custom UI components is a complex activity requiring the developer to work extensively with Java code and XML configuration files. JSF 2.0 introduces the notion of composite components with the goal of enabling developers to create really simple, reusable JSF UI components without touching any Java code or XML configuration files.

In this article, we explore the new composite components feature in JSF 2.0 with some code samples and a simple demo application. You will learn how to create a composite component of your own.

Composite UI Components

The creation of custom components in JSF 2.0 has been simplified with two new features, Facelets and Resources (more on these in the next section). A composite component is a Facelet that resides in a resource library. For a better understanding, let's review the sequence of steps that you would follow to create custom components in JSF 1.x releases:

  1. Create a component class that extends UIInput or UIOutput or any subclass of UIComponent that most appropriately fits the requirement.
  2. Create a Renderer class that can generate the appropriate markup.
  3. Register the component and the renderer in faces-config.xml.
  4. Create a JSP tag implementation to use the component in the JSP page.
  5. Create a Tag Library Descriptor (.tld) to use the JSP tag.

Compare that with the sequence of steps for creating a composite component in JSF 2.0:

  1. Create a resource library.
  2. Create a Facelets markup file in the resource library.
  3. Use a composite component in a Facelets page:
    • Use the following namespace: http://java.sun.com/jsf/composite/resLib, where resLib is the name of the resource library.
    • Refer to the component as pre:comp, where pre is the prefix that identifies the above namespace and comp is the name of the Facelets file in the resource library.

Resources Feature

A resource in web terms means anything that contains or can render information to the user. Apart from the usual web pages, resources can be images, style sheets, or even JavaScript files. Previous releases of JSF had no particular facility to specify resources, and web page authors followed their own mechanisms for specifying all these resources. JSF 2.0 provides a new feature for resource handling, which greatly simplifies how developers create custom components.

The new resources feature is achieved through a packaging mechanism. Resources are generally packaged in the web application root or in the classpath. The default implementation looks for resources in the following locations:

  1. Resources in the web application root:
    • resources/<resourceIdentifer> – A folder named resources that contains all the resources is created in the root folder.
  2. Resources in the classpath:
    • META-INF/resources/<resourceIdentifer> – Resources that are packaged in the classpath must be placed under the META-INF folder in the JAR file.

The resource identifier specified in both the above options has several parts as defined below, where the parts mentioned inside the brackets ([...]) are optional:

[localePrefix/] [libraryName/][libraryVersion/] resourceName [/resourceVersion]


Here are a few example resource identifiers:

  1. loginHand.gif – Only resourceName
  2. login/loginHand.giflibraryName/resourceName
  3. login/1_2/loginHand.giflibraryName/libraryVersion/resourceName

An important benefit of this feature is it provides an option for resources to be localized, versioned, and collected into libraries. All the resources, including images, style sheets, and JavaScript files, can be packaged using the same packaging mechanism.

Creating Composite Components in JSF 2.0

Now, for a better understanding of how to create a composite component in JSF 2.0, consider a simple demo example: an online quiz application that allows a registered user to answer five simple questions that test his or her general knowledge. Towards the end of the quiz, the application displays the score. Because the user can take the quiz only after a successful login, he or she must first register (see Figure 1 for a screenshot of the registration form).


Figure 1. Online Quiz Registration Form:
The user can take the quiz only after he or she must registers.

The following are the necessary features for the registration form:

  1. The email entered should be validated against a standard format.
  2. Values entered in the Password and Confirm Password fields should be the same.
  3. The date entered should be in mm/dd/yy format.
  4. When a user clicks the Register button, a business method should be invoked that completes the registration process.

Because these requirements are common across most registration forms, you can create a composite component that reflects this behavior.

First, you need to create a resource library named comp. Within comp, you create a Facelets file named register.xhtml, which will contain the necessary code for the composite component. The location of this file will be /resources/comp/register.xhml.

Here is the general structure of a composite component Facelets file:

<!-- XHTML DOCTYPE Declaration -->
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">
    <!—Interface that defines the user contract of the component-->
    <composite:interface>
...      
    </composite:interface>
    <!-- Implementation that specifies the actual markup-->
    <composite:implementation>
...      
    </composite:implementation>
</html></p>


The tags used to define a composite component are defined in the http://java.sun.com/jsf/composite namespace identified by the prefix composite. Each composite component has to provide a usage contract so that the users will know what to provide and what to expect. This is specified using the <composite:interface> tag. The actual markup that implements the component's user contract is specified using the <composite:implementation> tag.

The user contract of the registration component (shown below) allows the component user to provide customized labels for the input fields. These can be passed as values of predefined attributes, which are declared by using <composite:attribute> defined as a child element of <composite:interface>. For example, you have defined an attribute named regButtonText that defines the label for a Register button. The component user must provide this attribute's value. The label for other attributes, such as regPrompt, emailPrompt, and userNamePrompt, are optional and have been given default values.

When the user clicks the Register button, a managed bean method has to be invoked. The bean and the method are specified by the component user using the managedBean and regAction attributes. Notice the declaration of the regAction method that defines the signature of the bean method to be invoked.

<!-- Interface that defines the user contract of the component -->
  <composite:interface>
      <composite:attribute name="regButtonText" required="true"/>
      <composite:attribute name="regPrompt" default="Please fill registration details" />
        <composite:attribute name="emailPrompt" default="Email" />
        <composite:attribute name="userNamePrompt" default="Username"/>
        <composite:attribute name="passwordPrompt" default="Password"/>
        <composite:attribute name="confirmPasswordPrompt" default="Confirm Password"/>
        <composite:attribute name="dobPrompt" default="Date of Birth in mm/dd/yy format"/>
        <composite:attribute name="regAction" method-signature="java.lang.String register()" required="true"/>
        <composite:attribute name="managedBean" required="true"/>
</composite:interface>


The next step is to implement the composite component using appropriate markup in the <composite:implementation> element. In this case, the implementation consists of a single form with different input elements for accepting the email Id, username, password and date of birth of the user. To access the value of a component attribute you use the following syntax:

#{cc.attrs.attr_name}


Where attr_name is the name of the attribute.

Also notice the use of the <f:validateRegex> tag to validate the value in the email field. This new tag in JSF 2.0 allows the value of input component to be validated against a regular expression. Also, the date of birth needs to be converted to a Date object using the <f:convertDateTime> tag.

<composite:implementation>
   <h:form id="form" prependId="false">
        <div class="prompt">
           #{cc.attrs.regPrompt}
        </div>
        <h:panelGrid columns="2">
         <h:outputText value="#{cc.attrs.emailPrompt}"/>
         <h:inputText id="email" value="#{cc.attrs.managedBean.email}" required="true">
    <f:validateRegex pattern="[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+" />
           </h:inputText>
 
         <h:outputText value="#{cc.attrs.userNamePrompt}"/>
          <h:inputText id="name" value="#{cc.attrs.managedBean.userName}" required="true"/>
 
          <h:outputText value="#{cc.attrs.passwordPrompt}"/>
          <h:inputSecret id="password" value="#{cc.attrs.managedBean.password}" required="true"/>
 
          <h:outputText value="#{cc.attrs.confirmPasswordPrompt}"/>
          <h:inputSecret id="confirmPassword" value="#{cc.attrs.managedBean.confirmPassword}" required="true"/>
 
           <h:outputText value="#{cc.attrs.dobPrompt}"/>
   <h:inputText id="dob" value="#{cc.attrs.managedBean.dob}" required="true">
            <f:convertDateTime dateStyle="short"/>
          </h:inputText>
     </h:panelGrid>
        <p>
          <h:commandButton id="regButton"
    value="#{cc.attrs.regButtonText}" action="#{cc.attrs.regAction}" onclick="return checkPwd(this.form)"/>
       </p>
      </h:form>
   <script type="text/javascript">
      <!—Javascript code to validate that values in password and confirmPasssword field are same -->
    </script>
      <div>
        <h3>Please note that all fields are mandatory</h3>
      </div>
      <div class="error" style="padding-top:10px;">
        <h:messages layout="list" />
      </div>
</composite:implementation>


To ensure that the password and confirmPassword fields have the same value, you use the JavaScript method shown below:

function checkPwd(form){
     var pwd1 = document.getElementById("#{cc.clientId}"+":"+"password")
     var pwd2 = document.getElementById("#{cc.clientId}"+ ":"+"confirmPassword")
 
     if(pwd1.value != pwd2.value){
                    alert("Passwords Do Not Match ")
                    pwd1.value=""
                    pwd2.value=""                    
                    return false
     } else {
                    form.submit();
                    return true;
     }
}


Notice that the ID of the individual components that make up the composite component will be prefixed with the composite component's ID, which is determined at runtime. So, the actual ID of the password element is determined as "#{cc.clientId}"+":"+"password" where ":" is the ID separator. Also note that the prepend attribute of the form element is set to false. This ensures that the ID of the form-form- is not prepended to the form elements' IDs.

The register composite component is used in the Facelets file regform.xhtml. Declare a special namespace declaration that denotes the use of a composite component. To use any composite component in a Facelets file, you have to define a special namespace: http://java.sun.com/jsf/composite/res_lib, where res_lib is the name of the resource library containing the composite component file. In this case, the resource library is named comp.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:comp="http://java.sun.com/jsf/composite/comp" >


Once this is done, you can refer to the component as a Facelets tag, which is the name of the composite component file itself.

<comp:register regButtonText="Register" managedBean="#{regBean}"   regAction="#{regBean.register}"/>


Here, comp is the prefix attached to the special namespace declaration, and register is the name of the registration composite component file. You have to pass values for all the mandatory attributes: regButtonText, managedBean, and regAction.

As this simple demo exercise has shown, the process of creating composite components has been greatly simplified by using resources, Facelets, and a simple naming convention.

Conclusion

In this article, we discussed the new composite components feature in JSF 2.0. Using this feature, you can create simple, reusable JSF UI components without needing to manipulate any Java code or XML configuration files.

Stay tuned as we will explore more notable new features in JSF 2.0 in upcoming articles.

Acknowledgements

The authors would like to sincerely thank Mr. Subrahmanya (SV, VP, ECOM Research Group, E&R) for his ideas, guidance, support and constant encouragement and Ms. Yuvarani Meiyappan (Lead, E&R) for kindly reviewing this article and for her valuable comments.

About the Authors

Sangeetha S. works as a Senior Technical Architect at the E-Commerce Research Labs at Infosys Technologies. She has over 10 years of experience in design and development of Java and Java EE applications. She has co-authored a book on 'J2EE Architecture' and also has written articles for online Java publications.

Nitin KL works at the E-Commerce Research Labs at Infosys Technologies. He is involved in design and development of Java EE applications using Hibernate, iBATIS, and JPA.

Ananya S. works at the E-Commerce Research Labs at Infosys Technologies. She is involved in design and development of Java EE applications using Hibernate, iBATIS, and JPA.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date