Wednesday Oct 27, 2010

TOTD #147: Java Server Faces 2.0 Composite Components using NetBeans - DRY your code

The Java Server Faces 2.0 uses Facelets instead of JSP as the view declaration language. This allows "view" part of MVC to be completely written using XHTML and CSS only and all the business logic resides in the backing bean. This enables a cleaner separation of views with model and controller and thus follows the MVC design pattern in a more intuitive way. JSF 2 also defines how resources can be packaged, located, and rendered by JSF runtime within a web application.

Using these two features of Facelets and Resource Handling, JSF2 defines a composite component as a component that consists of one or more JSF components defined in a Facelet markup file that resides inside of a resource library. The composite component is defined in the defining page and used in the using page. The "defining page" defines the metadata (or parameters) using <cc:interface> and implementation using <cc:implementation> where "cc" is the prefix for "http://java.sun.com/jsf/composite" namespace. Future versions of the JSF 2 specification may relax the requirement to specify metadata as it can be derived from the implementation itself.

A composite component can be defined using JSF 1.2 as well but it requires a much deeper understanding of JSF lifecycle and also authoring multiple files. JSF2 really simplifies the authoring of composite components using just an XHTML file.

Code is king! This Tip Of The Day (TOTD) will explain how to convert an existing code fragment into a JSF2 composite component using NetBeans IDE.

Lets say a Facelet (index.xhtml) has the following code fragment:

<h:form>
    <h:panelGrid columns="3">
    <h:outputText value="Name:" />
    <h:inputText value="#{user.name}" id="name"/>
    <h:message for="name" style="color: red" />
    <h:outputText value="Password:" />
    <h:inputText value="#{user.password}" id="password"/>
    <h:message for="password" style="color: red" />
  </h:panelGrid>

  <h:commandButton actionListener="#{userService.register}"
                   id="loginButton" action="status" value="submit"/>
</h:form>


This fragment displays an HTML form with two text input boxes and a "submit" button. The two input boxes are bound to "user" bean and clicking on the button invokes "register" method of the "userService" bean.

Instead of repeating this code in multiple pages, its beneficial to convert this into a composite component and use the resulting tag instead of the complete fragment again. Why ?

  • Follows the DRY principle and allows to keep the code, that can be potentially be repeated at multiple places, in a single file.
  • It allows developers to author new components without any Java code or XML configuration.

How do you convert an existing code fragment to a composite component ? NetBeans makes it really easy.

In NetBeans IDE select the code fragment, right-click, "Refactor", "Convert to Composite Component..." as shown below:

In the next screen, change the filename to "loginPanel" and take every thing else as default as shown below:

and click on "Finish".

This will generate "web/resources/ezcomp/loginPanel.xhtml" and move the component definition to this file, aka "defining page" and looks like:

<?xml version='1.0' encoding='UTF-8' ?>
<!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:cc="http://java.sun.com/jsf/composite"
      xmlns:h="http://java.sun.com/jsf/html">

 <!-- INTERFACE -->
 <cc:interface>
 </cc:interface>

 <!-- IMPLEMENTATION -->
 <cc:implementation>
   <h:form>
     <h:panelGrid columns="3">
       <h:outputText value="Name:" />
       <h:inputText value="#{user.name}" id="name"/>
       <h:message for="name" style="color: red" />
       <h:outputText value="Password:" />
       <h:inputText value="#{user.password}" id="password"/>
       <h:message for="password" style="color: red" />
     </h:panelGrid>

     <h:commandButton actionListener="#{userService.register}"
                      id="loginButton" action="status" value="submit"/>
   </h:form>
</cc:implementation>
</html>

<cc:interface> defines metadata that describe the characteristics of component, such as supported attributes, facets, and attach points for event listeners. <cc:implementation> contains the markup substituted for the composite component.

<cc:interface> is generated in the page but is empty and may be made optional in a subsequent release of the JSF specification.The "using page" will declare a new namespace as:

xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"

and then replace the code fragment with:

<ez:loginPanel/>

The tag name for the new composite component is the same as the "defining page" file name. This enables "<ez:loginPanel/>" to be used instead of repeating that entire code fragment.

Now lets say that the code fragment need to pass different value expressions (instead of #{user.name}) and invoke a different method (instead of #{userService.register}) when submit button is clicked in different "using page"s. The "defining page" can then look like:

<!-- INTERFACE -->
<cc:interface>
  <cc:attribute name="name"/>
  <cc:attribute name="password"/>
  <cc:attribute name="actionListener"
      method-signature="void action(javax.faces.event.Event)"
      targets="ccForm:loginButton"/>
</cc:interface>

<!-- IMPLEMENTATION -->
<cc:implementation>
  <h:form id="ccForm">
  <h:panelGrid columns="3">
    <h:outputText value="Name:" />
    <h:inputText value="#{cc.attrs.name}" id="name"/>
    <h:message for="name" style="color: red" />
    <h:outputText value="Password:" />
    <h:inputText value="#{cc.attrs.password}" id="password"/>
    <h:message for="password" style="color: red" />
  </h:panelGrid>

  <h:commandButton id="loginButton"
                   action="status"
                   value="submit"/>
  </h:form>
</cc:implementation>

The changes are highlighted in bold and explained below:

  • All the parameters are explicitly specified in <cc:interface> for clarity. The third parameter has a "targets" attribute referrring to "ccForm:loginButton".
  • In <cc:implementation>
    • The <h:form> in has "id" attribute. This is required such that the button within the form can be explicitly referenced.
    • <h:inputText> is now using #{cc.attrs.xxx} instead of #{user.xxx}. #{cc.attrs} is a default EL expression that is available for composite component authors and provide access to attributes of the current composite component. In this case #{cc.attrs} has "name" and "password" defined as attributes.
    • "actionListener" is an attach point for event listener, defined as a "method-signature" and describes the signature of a method pointed to by the expression.
    • <h:commandButton> has "id" attribute so that it can be clearly identified within the <h:form>.

The "user", "password", and "actionListener" are then passed as required attributes in the "using page" as:

<ez:loginPanel
    name="#{user.name}"
    password="#{user.password}"
    actionListener="#{userService.register}"/>


Now the "using page" can pass different "backing beans" and business method to be invoked when "submit" button is invoked.

The complete source code for this TOTD can be downloaded here.

How are you using JSF 2 composite components ?

The entire source code used in this blog can be downloaded here.

JSF 2 implementation is bundled with GlassFish Server Open Source Edition, try it today!

I realized TOTD #135 already explains how to author composite components. Hey, but this TOTD provides new information on how to attach event listeners :-)

Technorati: totd javaee6 glassfish jsf2 composite component facelets

Sunday Sep 21, 2008

TOTD #46: Facelets with Java Server Faces 1.2


This blog updates TOTD #45 to use Facelets as view technology.

Powerful templating system, re-use and ease-of-development, designer-friendly are the key benefits of Facelets. Facelets are already an integral part of Java Server Faces 2.0. But this blog shows how to use them with JSF 1.2.
  1. Download Facelets from here (or specifically 1.1.14). Facelets Developer Documentation is a comprehensive source of information.
  2. Add "jsf-facelets.jar" from the expanded directory to Project/Libraries as shown:

  3. Change the JSF view documents to ".xhtml" by adding the a new context parameter in "web.xml" as:

    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
      </context-param>

    The updated "web.xml" looks like:

  4. Specify Facelets as the ViewHandler of JSF application by adding the following fragment to "faces-config.xml":

      <application>
        <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>   
      </application>

    The updated document looks like:


  5. Create three new XHTML pages by right-clicking on the project, selecting "New", "XHTML" and name them as "template", "welcome" and "result". This creates "template.xhtml", "welcome.xhtml" and "result.xhtml" in "Web Pages" folder. 
    1. Replace the generated code in "template.xtml" with the code given here. Change the <title> text "Facelets: What's your favorite City ?".
    2. Replace the generated code in "welcome.xhtml" with the code given here. Refactor "welcomeJSF.jsp" such that H1 tag and the associated text goes in <ui:define name="title"> and rest of the content goes in <ui:define name="body">. Also change the value of "template" attribute of <ui:composition> by removing "/". The updated page looks like:

    3. Replace the generated code in "result.xhtml" with the code given here. Refactor "result.jsp" such that H1 tag and the associated text goes in <ui:define name="title"> and rest of the content goes in <ui:define name="body">. Also add a namespace declaration for "http://java.sun.com/jsf/core".

      Optionally change the <h:form> associated with the command button to:

                      <form jsfc="h:form">
                          <input jsfc="h:commandButton" action="back" value="Back"/>
                      </form> 

      The updated page looks like:


  6. Add couple of more navigation rules to "faces-config.xml":

        <navigation-rule>
            <from-view-id>/welcome.xhtml</from-view-id>
            <navigation-case>
                <from-outcome>submit</from-outcome>
                <to-view-id>/result.xhtml</to-view-id>
            </navigation-case>
        </navigation-rule>
        <navigation-rule>
            <from-view-id>/result.xhtml</from-view-id>
            <navigation-case>
                <from-outcome>back</from-outcome>
                <to-view-id>/welcome.xhtml</to-view-id>
            </navigation-case>
        </navigation-rule>
And that's it, Facelets-based application is now available at "http://localhost:8080/Cities/faces/welcome.xhtml". The interaction is exactly similar to as shown in TOTD #45 and some of the sample images (borrowed from TOTD #45) are:


Now this application is using Facelets as the view technology instead of the in-built view definition framework.
Please leave suggestions on other TOTD (Tip Of The Day) that you'd like to see. A complete archive of all tips is available here.

Technorati: totd javaserverfaces facelets netbeans glassfish
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today