Passing null to the model in JSF

8 February 2012, by: Development

The problem

JSF allows you to bind an input component to a model bean via a so-called value binding. A notorious major headache is that a null is automatically coerced into a zero (0) when the bounded property is of type Integer or Long. This behavior only seems to rarely be the required one. A number of other types are coerced as well, e.g. in case of Boolean it’s coerced to Boolean.FALSE.

Finding a solution

Although coercing is a literal interpretation of the spec, it seems that basically only Tomcat that implements this somewhat peculiar behavior. They have a setting to turn this off:

-Dorg.apache.el.parser.COERCE_TO_ZERO=false

See e.g. jsf: integer property binded to a inputtext in UI is set to zero on submit

If you’re in a position to use this option, don’t read further and use it.

If you can’t use this option (you have e.g. a big system, lots of testing needed) and a null is needed at only a few places, another option is required. One possibility, that I will describe here, is based on a value-changed listener and a custom tag handler. What we do is breaking up the existing value binding of the input component in a base part and a property part, and then setting a null directly via the Apache BeanUtils library.

The following shows a TagHandler implementation that does this breaking up and installs a value-changed listener on the parent component:

  1. public class MinusOneToNullConverter extends TagHandler {
  2.  
  3.     private static final Class<?>[] VALUECHANGE_LISTENER_ZEROARG_SIG = new Class[] {};
  4.     private static final String LISTENER_EL = "#{minusOneToNullListener.processValueChange(component, %s, '%s')}";
  5.  
  6.     public MinusOneToNullConverter(TagConfig config) {
  7.         super(config);
  8.     }
  9.  
  10.     @Override
  11.     public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
  12.  
  13.         String expression = parent.getValueExpression("value").getExpressionString();
  14.         if (expression.contains(".")) {
  15.             String base = expression.substring(2, expression.lastIndexOf('.'));
  16.             String property = expression.substring(expression.lastIndexOf('.') + 1, expression.length() - 1);
  17.  
  18.             ExpressionFactory expressionFactory = ctx.getExpressionFactory();
  19.  
  20.             ((EditableValueHolder) parent).addValueChangeListener(
  21.                 new MethodExpressionValueChangeListener(expressionFactory.createMethodExpression(ctx, 
  22.                     String.format(LISTENER_EL, base, property),
  23.                     Void.class, VALUECHANGE_LISTENER_ZEROARG_SIG)
  24.             ));
  25.         }
  26.     }
  27. }

Note that the EL expression uses ‘component’ as the first parameter for the processValueChange method. This is an implicit EL object that refers to the current component being processed, which is in the case of this tag the parent component. Implementation wise it’s important to use the FaceletContext as the ELContext, instead of grabbing it from the FacesContext, when creating the method expression. Otherwise the context of the method expression would not be entirely correct and things like <ui:param>s will not be resolved.

The following shows the value-changed listener that will be installed:

  1. @ManagedBean
  2. public class MinusOneToNullListener {
  3.  
  4.     public void processValueChange(UIInput component, Object object, String property) throws AbortProcessingException {
  5.  
  6.         if (component.getValue() != null && component.getValue().toString().equals("-1")) {
  7.             component.resetValue();
  8.             try {
  9.                 PropertyUtils.setProperty(object, property, null);
  10.             }
  11.             catch (IllegalAccessException e) {
  12.                 throw new AbortProcessingException(e);
  13.             }
  14.             catch (InvocationTargetException e) {
  15.                 throw new AbortProcessingException(e);
  16.             }
  17.             catch (NoSuchMethodException e) {
  18.                 throw new AbortProcessingException(e);
  19.             }
  20.         }        
  21.     }
  22. }

As can be seen, the listener uses the input component to check if the value submitted was “-1″. The string representation is used here to be compatible with different types, although there are of course some other possibilities here. The listener will then reset the value of the component, so JSF will not attempt later on to push the -1 to our model object. Finally, bean utils is used to set the actual null value.

In this example, we used -1 to encode the desire for null. Other options might be feasible as well, such as the empty string or perhaps even null itself.

Before using this all, there is the tedious but mandatory registration of the tag handler in a *-taglib.xml file:

  1. <tag>
  2.     <tag-name>minusOneToNullConverter</tag-name>
  3.     <handler-class>com.example.MinusOneToNullConverter</handler-class>
  4. </tag>

Using the ‘converter’

After doing the above, we’re now ready to use this on a Facelet, e.g. :

  1. <h:selectOneMenu value="#{someBean.value}">
  2.     <my:minusOneToNullConverter />
  3.     <f:selectItem itemLabel="ALL" itemValue="-1" />
  4.     <f:selectItems value="#{data.somethings}" var="something" itemLabel="#{something.label}" itemValue="#{something.key}" />
  5. </h:selectOneMenu>

Any other alternatives?

Once again, if on Tomcat, -Dorg.apache.el.parser.COERCE_TO_ZERO=false should be your first choice. If you use an application server that isn’t based on Tomcat, you also probably don’t need this. There’s a spec proposal open that asks to change this behavior, but despite a large amount of votes it hasn’t seen any activity for a long time.

Hopefully the hacky workaround presented here is helpful to someone.

Arjan Tijms

3 comments to “Passing null to the model in JSF”

  1. rodrigo says:

    Hi Arjan,

    I think I’m facing a similarly proplem.
    I’m new to java, jsf and all this world (I used to be a DB admin a year ago), and this week I was trying to use a SelectOneRadio wich is binded with a boolean column in db (throught its ManageBean/ BusinessController/ Domain…etc ) and need to be 3-state (false/ true/ not answered).
    the fact is that I thought it’d work fine because in db a boolean column can be null but when jsf update the boolean property binded in the managed bean it set it to false when SelectOneRadio is not checked… I found it occured because when a null value is passed to the Boolean contructor (Boolean java object) it assumes false when null.
    Now I was trying to find some help and found your post solution. (now I can’t modify database column type)
    I ask… would it work for me? is my case same as your although where you use int or long I’m using boolean?

    thanks in advance for your opinion/ help…. and congratulations for this great post.

  2. Hanynowsky says:

    Check the open source JSF View technologies like PrimeFaces and OpenFaces, they offer Three state checkboxes or radio buttons that solves this null object problem.

  3. Rafael says:

    Hi, excellent post!
    By any chance, do you have the source code of the taglib available for download?
    If not, can you send it to me via mail?

Type your comment below:


3 × five =

css.php best counter