Upgrading a framework to JSF 2.0

I finally got a chance to upgrade a component library plus an extension framework to JSF 2.0, and I wanted to share my thoughts on this with you.

Good news: the upgrade is pretty straightforward, no major problems to be seen. I have seen 3 hot-spots where code needs to be widely changed all over the library, and we wanted to keep this code mostly 1.2 compatible (so that our merging efforts would not get out of hand) and here are my suggestions on how to tackle this:

1) Decorators (e.g. ViewHandler, NavigationHandler, FacesContext, ExternalContext)

Decorators are a common thing in JSF component libraries and extension frameworks, and they were also widely used in the libraries I was working on. Starting from 2.0, things will be a lot better as the standard now provides abstract wrapper classes you can extend your decorators from. But for existing frameworks which want to remain 1.2 compliant, it is hard to cope with the methods which were added to the decorator-interfaces. My solution for this: provide an additional library (you might call it jsf12compatibility) where you implement the abstract base classes yourself and deploy this additional library in the 1.2 environment.

2) Facelets integration

So if you have changed the FaceletViewHandler, you are on your own - I don't have any suggestions how to tackle this situation, it is gonna be different for everyone. If you have just added custom tag-handlers, you will need to do a few things:

- extend from the new base-classes (in javax.faces.view.facelets.*). If you still want to provide 1.2 compatibility, you can again provide these new base-classes in a compatibility library. However, here it is a little more complicated, as some of the classes or methods you might need to override are final. So I ended up having to create a patched version of facelets to handle this. Also, as the tag/component-config classes will need to be new, you need to follow an interesting pattern. You need to wrap the configs if you pass them out to the new (subclassed) tag-handlers and call the underlying old method if the new method is called from them. As an example, find the TagHandler below:

public abstract class TagHandler extends com.sun.facelets.tag.TagHandler{
  
  public TagHandler(javax.faces.view.facelets.TagConfig tagConfig) {
    super(tagConfig);
  }

  @Override
  public javax.faces.view.facelets.TagAttribute getAttribute(String attributeName) {
    return wrap(super.getAttribute(attributeName));
  }

  @Override
  public javax.faces.view.facelets.TagAttribute getRequiredAttribute(String attributeName) {
    return wrap(super.getRequiredAttribute(attributeName));
  }

  private javax.faces.view.facelets.TagAttribute wrap(com.sun.facelets.tag.TagAttribute wrapped) {
    return wrapped==null?null:new TagAttributeWrapper(wrapped);
  }

  public void apply(com.sun.facelets.FaceletContext faceletContext, 
                    javax.faces.component.UIComponent component) 
        throws java.io.IOException, 
               javax.faces.FacesException, 
               com.sun.facelets.FaceletException, javax.el.ELException {
    apply(wrap(faceletContext), component);
  }

  public abstract void apply(
              javax.faces.view.facelets.FaceletContext faceletContext, 
              UIComponent component)  
      throws java.io.IOException, javax.faces.FacesException, 
             com.sun.facelets.FaceletException, javax.el.ELException;

  private javax.faces.view.facelets.FaceletContext wrap(com.sun.facelets.FaceletContext ctx) {
    return new FaceletContextWrapper(ctx);
  }
}

With this pattern, you should get pretty far. I might be able to open-source our compatibility library somewhere, then it should be easier for other people to follow this pattern.

3) State-handling in components

Finally, you will want to use the new state-handling approach in components. To make this possible, we provided an adapter class in the component-hierarchy which does nothing if JSF 2.0 is around and implements state-saving for the 1.2 case similarly to how it is done within JSF 2.0, but without partial state-saving. This works pretty nicely.

Find below as an example an HtmlColumnAdapter:

public class HtmlColumnAdapter extends HtmlColumn {
  private StateHelperMap stateHelperMap = new StateHelperMap(this);

  public StateHelper getStateHelper() {
    return stateHelperMap;
  }

  /**
   * Save the state of the non transient attributes.
   *
   * @param context
   *          the context
   *
   * @return the object
   *
   * @see javax.faces.component.UIInput#saveState(javax.faces.context.FacesContext)
   */
  @Override
  public Object saveState(FacesContext context) {
    Object[] values = new Object[2];
    values[0] = super.saveState(context);
    values[1] = stateHelperMap.saveState(context);
    return values;
  }

  /**
   * Restore the state of the non transient attributes.
   *
   * @param context
   *          the context
   * @param state
   *          the state to be restored
   *
   * @see javax.faces.component.UIInput#restoreState(javax.faces.context.FacesContext,
   *      Object)
   */
  @Override
  public void restoreState(FacesContext context, Object state) {
    Object[] values = (Object[])state;
    super.restoreState(context, values[0]);
    stateHelperMap.restoreState(context, values[1]);
  }
}

With that, you should again get pretty far with your migration - still preserving 1.2 compatibility in major parts of the source-base.