1

I have an h:selectOneRadio component that I have mapped to a Boolean (not boolean) in the backing bean. On load, the radio buttons do not have a default select option, and the radio button is not a required field.

Example code:

JSF page excerpt:

<h:form>
<h:selectOneRadio value="#{pagecode.test}">
    <f:selectItem itemValue="#{true}" itemLabel="Yes"/>
    <f:selectItem itemValue="#{false}" itemLabel="No"/>
</h:selectOneRadio>
<h:commandButton value="Save" action="#{pagecode.save}"/>
</h:form> 

Backing Java pagecode:

package pagecode;

public class JSFBooleanTestView extends PageCodeBase {

    protected Boolean test;

    public Boolean getTest() {
        return test;
    }

    public void setTest(Boolean test) {
        this.test = test;
    } 

    public String save() {
        return "JSFBooleanTestView";
    }
}

faces-config.xml excerpt:

<managed-bean>
    <managed-bean-name>pagecode</managed-bean-name>
    <managed-bean-class>pagecode.JSFBooleanTestView</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Since test is not being defaulted to a value, the radio buttons start off unselected. Since the field is not required, my expectation was that pressing the Save button with no radio button selected would lead to test being null. Instead, test is assigned false.

Different options that I have tried that have had no effect:

  1. Setting h:selectOneRadio required="false"

  2. Adding to web.xml:

    <context-param>
         <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
         <param-value>true</param-value>
     </context-param>
    
  3. Adding to web.xml:

     <context-param>
         <param-name>org.apache.el.parser.COERCE_TO_ZERO</param-name>
         <param-value>false</param-value>
     </context-param>
    

What did work was adding immediate="true" to h:commandButton.

My questions:

  1. What is making h:selectOneRadio convert nulls to false normally? Is that the intended behavior?

  2. Why does immediate="true" make h:commandButton not convert the nulls to false? What exactly is immediate="true" doing differently in this particular situation that is causing the difference? I understand that the immediate="true" will skip certain phases in the JSF lifecycle, but I don't understand what in those phases is causing the conversion from null to false to occur.

Edit:

I just realized I added immediate="true" to the h:commandButton, not the h:selectOneRadio. My question has been edited accordingly.

This is used in a portlet application on IBM WebSphere Portal 8.0 using Apache MyFaces 2.0.

Chatoyancy
  • 143
  • 1
  • 17
  • 1
    `COERCE_TO_ZERO` is not a context param. It's a VM argument (and server-dependent). Set it as VM argument, retry and then we can talk further. In any case, always include detail about JSF impl/version and server impl/version, otherwise anyone reading this question will assume latest versions of their favourite JSF and server impl. – BalusC Mar 24 '16 at 14:55
  • @BalusC Oh! Thank you. I edited my question to include that additional info. Also, I added COERCE_TO_ZERO as a VM argument, and that did make it work as expected. [Your article](http://balusc.omnifaces.org/2015/10/the-empty-string-madness.html) mostly explains to me why, but can you clarify specifically how this relates to Booleans so I can be certain I am understanding it correctly? – Chatoyancy Mar 24 '16 at 15:20
  • @BalusC Also, we have a lot of portlets on our server, and I am concerned about making a server-wide VM change like this. If I instead set the property using a ServletContextListener, would it only affect the portlets that need that property, or would it affect all portlets? System.setProperty seems pretty global to me, at least in my understanding. – Chatoyancy Mar 24 '16 at 15:22
  • 1
    By default, Apache EL incorrectly treats primitive wrappers as primitives. I.e. `Boolean` is interpreted as `boolean`, `Integer` is interpreted as `int`, `Character` is interpreted as `char`, etc and their default values will become the default value of the wrapped primitive (`false`, `0`, `\u0000`, etc) instead of `null`. And, sorry it's application wide. So if code was incorrectly relying on empty strings being the correct behavior, things may fail (although that would usually only expose hidden bugs which finally get chance to be really fixed, so you'd actually be happy instead ;) ). – BalusC Mar 24 '16 at 15:42
  • Great, thank you very much! :) Can you put your comments in an answer so I can accept it and give you credit? – Chatoyancy Mar 24 '16 at 17:26
  • I believe this is why we generally used the values as Strings, Y/N/U or null (empty). (Yes, No, Unsure, Not Answered) - And then you can basically use the "Y".equalsIgnoreCase.value to ascertain a true or false state if needed. – VeenarM Mar 24 '16 at 22:26

1 Answers1

5

The behavior of primitive wrappers like Boolean, Integer, Double, Character, etc getting primitive's default values of false, 0, 0.0, \u0000, etc assigned instead of null is specific to Apache EL, which is used in a.o. Tomcat, JBoss AS and WebSphere servers. It affects only EL 2.1 and EL 2.2 based versions. Since EL 3.0 this misbehavior is corrected (after my own issue report).

Using org.apache.el.parser.COERCE_TO_ZERO is indeed the solution, but you should set it as a VM argument (system property), not as a context parameter. For this specific problem, the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL is unrelated, but you should most definitely keep this in order to avoid String typed values being set with empty strings instead of null.

All of this and a bit of history is detailed in the blog article The empty String madness.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555