Miles to go …

October 28, 2010

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

Filed under: General — arungupta @ 5:03 am

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

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • StumbleUpon
  • Technorati
  • Twitter
  • Slashdot
Related posts:
  1. TOTD #135: JSF2 Composite Components using NetBeans IDE – lightweight Java EE 6
  2. TOTD #45: Ajaxifying Java Server Faces using JSF Extensions
  3. TOTD #46: Facelets with Java Server Faces 1.2
  4. TOTD #94: A simple Java Server Faces 2.0 + JPA 2.0 application – Getting Started with Java EE 6 using NetBeans 6.8 M1 & GlassFish v3
  5. TOTD #42: Hello JavaServer Faces World with NetBeans and GlassFish

6 Comments »

  1. Clear and very interesting.
    Thanks for this post

    Comment by GLIA — October 28, 2010 @ 8:56 am

  2. Thanks for this post. I came across it while looking for an answer to another problem.

    If one wants to make a composite component of an input group such as:

    <h:outputText value="Name:" />
    <h:inputText value="#{cc.attrs.name}" id="name"/>
    <h:message for="name" style="color: red" />

    and one ends up with something like this:

    <cc:interface>
    <cc:attribute name="value" required="true"/>
    <cc:attribute name="label" required="true"/>
    </cc:interface>

    <cc:implementation>
    <h:outputText value="#{cc.attrs.label}" />
    <h:inputText id="input"
    value="#{cc.attrs.value}"
    converter="trimConverter"
    style="font-weight:bold">
    <f:ajax event="blur" render="msg"/>
    </h:inputText>
    <p:message id="msg" for="input"/>
    </cc:implementation>

    I assume this is a reasonable application of composite components, and one can use it like this:

    <h:panelGrid columns="1">
    <ez:inputField label="Username:" value="#{user.username}"/>
    <ez:inputField label="Password:" value="#{user.password}"/>
    <ez:inputField label="First Name:" value="#{user.firstName}"/>
    <ez:inputField label="Last Name:" value="#{user.lastName}"/>
    </h:panelGrid>

    This works but I found that the h:panelGrid component then treats each composite component tag as one column, so one has to give it a columns attribute of 1, and then all the columns in the various rows of one’s composite components are out of alignment.

    Does JSF2 provide an easy solution to this problem? It seems reasonable to expect that it should.

    Thanks,

    Nic

    Comment by Nic — October 30, 2010 @ 4:29 am

  3. You bring up a good point. The standard h:panelGrid renderer looks at its UIComponent children to determine how many children to put in each row based on the columns attribute. It seems like you want to make it so you get one instance of the composite component in each row, but you’re instead getting one instance of each component in the <cc:implementation> section in each row. Is that correct?

    Comment by Ed Burns — November 2, 2010 @ 8:56 am

  4. Hi Ed
    Thanks so much for your attention.
    If I’ve understood what you are asking me, I’m asking for the opposite. With columns attribute of the h:panelGrid set to 1, I am getting one instance of each composite component in each row.
    Basically I’m looking for the same behaviour I got before I converted the 3 components of each row into a composite component.
    Each row has a label, an input field and a message. The labels are different lengths, e.g. “Name:” and “Password:”. Before converting the row into a composite component the h:panelGrid tag regarded them as 3 columns, and they were neatly lined up. Once they are converted to a composite component, h:panelGrid regards each composite component as 1 child, so although there are 3 components within it, they are treated as 1 column, and as the labels are different lengths and are on the left, the inputText (center) and message (right) of subsequent rows aren’t neatly lined up with the same components of the rows above them.
    I would have thought and want it to behave the same way as before it is made into a composite component.

    Another problem I have is similar. This one uses a third party component suite, but now I tend to think the problem is in JSF and not the third party suite.
    It has a menu component (p: menu) and a submenu (p:submenu) and a menu item (p:menuitem). If one places the p:menuitem in a composite component, it is not rendered, as p:submenu is looking for its immediate descendent to be a p:menuitem, and instead finds the composite component. I would have expected and would like the composite component to be ‘invisible’ to it’s ascendants and descendants.

    Once again thanks for your attention.
    Nic Callias.

    Comment by Nic — November 2, 2010 @ 3:10 pm

  5. I continue to believe that once you make something a component, h:panelGrid should treat it as a component.

    If you want to get the layout you had before, you can declare some attributes in your cc:interface section that you use to affect layout in the cc:implementation section.

    Ed

    Comment by Ed Burns — November 3, 2010 @ 7:46 pm

  6. Thanks for your feedback. Thus far I haven’t been able to find the solution by declaring attributes in the cc:interface to affect layout in cc:implementation section. I’d be very grateful to get this solution, in the hope of which, I commented here in the first place.

    The NetBeans IDE facility to select a code fragment, right-click, "Refactor", "Convert to Composite Component…" as demonstrated by Arun above is a great facility, if the result you get behaves in the same way as it did before the refractor. It does in his particular example because he does the whole form including the h:panelGrid. In my experience a Login form is not used more than once or twice in a well designed system. However, something a label, input text and message fragment may be used hundreds of times, depending on the size of the system. If this is made into a usable composite component it is a great saving. The potential for the composite component feature in JSF to increase productivity through reusability and enhanced maintainability is great, and could make it into a really competitive product. I think the inflexibility of not allowing a composite component to act like a sophisticated include could limit its application so severely as to stunt this potential.

    JSF could at least allow the user to choose the composite component’s behaviour through some sort of switch on the instance level and, maybe, setting a default behaviour in the web.xml.

    Best Regards
    Nic

    Comment by Nic — November 4, 2010 @ 4:52 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.
Powered by WordPress