Blogs


Coercion of the Unified EL

So yes, specifications do not always specify what the user would expect, and the Unified EL is no exception. What do I refer to?

The unified EL has a very interesting behaviour defined with regards to coercion: it tries to coerce a null-value to the expected type.

An example: you bind the collapsed property of a container to a backing-bean property, and return null, to indicate that this container should not be collapsible at all - the specified EL conversion will create "false" out of this value.

An example for what the spec says:

Coerce A to Boolean

  • If A is null or "", return false
  • Otherwise, if A is a Boolean, return A
  • Otherwise, if A is a String, and Boolean.valueOf(A) does not throw an exception, return it

This is really not what you would expect, if you haven't read the spec. Facelets, on the other hand, always creates value-expressions with an expected type - these two behaviours together make bindings deliver null values to components almost impossible.

To solve this, I have implemented the following code for component-classes in a component library I am working on, called in the setValueExpression()-method of these components. What a nasty hack. Can we repair this for the next version of the Unified EL or Facelets? And yes, if you ask me, I believe this should be repaired in the EL (cause I still want coercion from e.g. a String to a Boolean value, I just don't want to loose the null semantics). And - I am open for better suggestions to work around this problem, if there are such.

public static void repairExpectedType(ValueExpression binding) {

    if(binding.getExpectedType()!=null &&
       binding.getExpectedType()!=Object.class) {
      try {
        Class bindingClass = binding.getClass();

        if(bindingClass.getName().equals(
            "com.sun.facelets.el.TagValueExpression")) {

          java.lang.reflect.Field origin = getFieldForName(
            bindingClass, "orig");
          origin.setAccessible(true);

          Object sunValueExpression = origin.get(binding);

          if(sunValueExpression.getClass().getName().equals(
             "com.sun.el.ValueExpressionImpl")) {
            java.lang.reflect.Field expectedType =
             getFieldForName(sunValueExpression.getClass(), "expectedType");
            expectedType.setAccessible(true);

            expectedType.set(sunValueExpression, Object.class);
          }

        }
      }
      catch(Throwable th) {
        throw new IllegalStateException(th);
      }
    }
  } 

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.

JSFdays2009 in retrospect

JSFdays2009 was a huge success! We welcomed over 250 attendees - customers, partners, speakers, sponsors and staff over three action packed days - and you can relive the people, the presenations and the conference itself!

The conference was divided into 2 tracks – JEE and JSF. Speakers like Ed Burns, Agim Emruli, Max Katz and many others talked about actual topics like JSF 2.0, OSGi, RichFaces and many others.

Some impressions about the conference:
JSF-Track
JEE-Track
Relaxing
Coming together
Workshop

JSFdays: to be continued in February 2010 ...

... have you become curious? Find more details about the JSFdays2010 here.