Composite components are a great feature of JSF 2.0. The canonical example is a login component with fields for the username and password:

<mylib:login name="#{user.name}"
      password="#{user.password}"
      loginAction="#{user.login}"/>

http://www.ibm.com/developerworks/java/library/j-jsf2fu3/login.jpg

This has been well explained elsewhere. But here is what always baffled me. I want to have a composite date component, with three menus for day, month, and year.

But I want it to have a single value of typejava.util.Date, so I can use it like this:

<mylib:date value="#{user.birthDay}"/>

and not

<mylib:date day="#{user.birthDate}" month="#{user.birthMonth}" year="#{user.birthYear}"/>

Why do I care?

  • My classes aren't all made up of strings and numbers. I use objects when I can. My User class has a propertybirthDay of type java.util.Date. I don't want to write boring code to take dates apart and put them back together.
  • I want to use bean validation for the date property, with a @Past or@Future annotation.

I asked around and people told me that this couldn't be done with composite components—I'd have to write an actual custom component.

But, as I discovered, it is not so. With a small dose of knowledge of the JSF lifecycle, and the poorly documented technique of backing components, this is actually pretty easy. Here goes.

When you make a composite component, it is normally turned into a UINamingContainer that contains the child components in the implementation. But you can also force a different component to be used, provided

  • it implements the NamingContainer marker interface
  • its “family” is"javax.faces.NamingContainer" (don't ask...)

The easiest way of using your own component is to make a class whose name is libraryName.compositeComponentName, such asmylib.date. (It's a bit weird to have a lowercase class name, but that's the price to pay for “convention over configuration”.)

package mylib;
public class date extends UIInput implements NamingContainer { 
    public String getFamily() { return "javax.faces.NamingContainer"; }
    ...
}

Note that I extend UIInput and notUINamingContainer. In the world of JSF,UIInput is an “editable value holder”, a class that holds a value of an arbitrary type (not necessarily numbers or strings), and to which you can attach validators.

The JSF lifecycle starts out like this:

  • The HTTP request (doesn't actually have to be HTTP—in JSF, everything is pluggable) delivers name/value pairs
  • Each component can pick from the request what it wants in thedecode method and sets its submitted value
  • The submitted value is converted to the desired type (integer, date, whatever), becoming the converted value
  • If the converted value passes validation, it becomes thevalue of the component

For a composite component, the submitted value is a combination of the submitted values of the children. You could combine them by putting them into a map, but I simply say that the submitted value is the composite component:

public class date extends UIInput implements NamingContainer { 
    ...
    public Object getSubmittedValue() { return this; }
    ...
}

(If you don't override this method, the submitted value isnull, and that gets into a murky corner of processing that you want to avoid.)

The conversion from a bunch of values to a date happens ingetConvertedValue:

public class date extends UIInput implements NamingContainer { 
    ...
    protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) {
        UIInput dayComponent = (UIInput) findComponent("day");
        UIInput monthComponent = (UIInput) findComponent("month");
        UIInput yearComponent = (UIInput) findComponent("year");
        int day = (Integer) dayComponent.getValue();
        int month = (Integer) monthComponent.getValue();
        int year = (Integer) yearComponent.getValue();
        if (isValidDate(day, month, year)) // helper method that checks for month lengths, leap years
           return new Date(year - 1900, month - 1, day);
        else 
           throw new ConverterException(new FacesMessage(...));
    }
    ...
}

This is very similar to the usual conversion action, except that I combine the values from multiple child components. (I attached ajavax.faces.Integer converter to each of the children so I don't have to convert the submitted strings to integers myself.)

That takes care of processing the input. On the rendering side, I just populate the children before rendering them:

public class date extends UIInput implements NamingContainer { 
    ...
    public void encodeBegin(FacesContext context) throws IOException {
        Date date = (Date) getValue();
        UIInput dayComponent = (UIInput) findComponent("day");
        UIInput monthComponent = (UIInput) findComponent("month");
        UIInput yearComponent = (UIInput) findComponent("year");
        dayComponent.setValue(date.getDate());
        monthComponent.setValue(date.getMonth() + 1);
        yearComponent.setValue(date.getYear() + 1900);
        super.encodeBegin(context);
    }
}

That's all. The same recipe works for any composite component that collects input for a complex data type.

Here is the code of a sample application that works out of the box in GlassFish 3 (but not in Tomcat). Note that the sample application uses the composite component as an input for java.util.Date. It works with bean validation without any effort on the developer's part.

The moral of this is:

  • The much maligned JSF lifecycle is actually pretty good. The decode/convert/validate order is what you need anyway, so why not have the framework manage it for you?
  • The much maligned generality of JSF is pretty good too. They could have said “With HTTP, what comes in is strings, so why not just work with strings?” But here we take advantage that the source and target of the conversion can be any type.
  • The declarative composite components that everyone raves about are great, but sometimes you've got to be able to add code. This blog shows you how to do it.