1

I'm using OmniFaces' omnifaces.SelectItemsConverter so I can show an object's string value in a selectOneMenu. Everything works fine until a verification fails (f:validateDoubleRange). Once that verification fails it doesn't matter what I do, I get a NullPointerException in the equals method of the object I showed in the selectOneMenu.

This is the equals method:

@Override
public boolean equals(Object obj) {
    Car other = (Car) obj;
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    if (!number.equals(other.number))
        return false;
    return true;
}

This is where I use the OmniFaces' convertor

<h:selectOneMenu id="id_1" value="#{myBean.car}" converter="omnifaces.SelectItemsConverter">
    <f:selectItems value="#{myBean.cars}" />
</h:selectOneMenu>

And this is the validation that causes the NullPointerException

<h:inputText id="nom" value="#{myBean.num}"
                style="height: 24px; "><f:validateDoubleRange minimum="0.0"/></h:inputText>

I get the exception in this line:

if (!number.equals(other.number))

other.number is OK, but number is null

Why is this.number null?

AwesomeGuy
  • 537
  • 1
  • 6
  • 17
  • You're implying that this problem doesn't occur when you use a custom converter. This isn't true. Please revisit your question accordingly. – BalusC Dec 02 '17 at 22:54
  • @BalusC I am just saying I am getting this error when using that converter, I'm not saying it is that converter's fault. But I am looking for a solution because I don't know what I am doing wrong... – AwesomeGuy Dec 03 '17 at 09:38

1 Answers1

0

Root cause

The root cause of the problem is JSF default handling of input component states after validation fails on any of input components inside view.

It is very well explained in this accepted answer.

Solution

So in your particular case, as previously mentioned answer suggests, you need to instruct JSF to reset input component states (the most important for h:selectOneMenu) and pull/refresh their values from backing bean model after validation fails.

If you are using JSF 2.2 and up, you can do it, for example, like this

<h:commandButton value="Submit"  actionListener="#{myBean.onSubmit()}" >
    <f:ajax execute=":form:nom :form:id_1" 
            render=":form:messages :form:id_1" 
            resetValues="true"/>
</h:commandButton>

UPDATE: If you are pre-JSF 2.2, you can do it using Primefaces p:ajax, for example, like this (and again solution and explanation can be found in referenced accepted answer as well as in Primefaces showcase )

<h:commandButton value="Submit"  actionListener="#{dtSelectionView.onSubmit()}">
       <p:ajax process="@form" update="@form" resetValues="true" />
</h:commandButton>

(although I would not recommend to import Primefaces solely for this purpose... it is better to explore other possibilities)

Actual cause why this.number is null

I've noticed that, if you try to submit after validation failed for the 1st time (and if you do not reset input values of h:selectOneMenu), some of following classes creates new Car object calling default constructor (and initializing number property to default value which in your case equals null)

Severe:   java.lang.NullPointerException
    at entities.Car.equals(Car.java:107)
    at javax.faces.component.UIInput.compareValues(UIInput.java:1224)
    at javax.faces.component.UIInput.validate(UIInput.java:990)
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1248)
    at javax.faces.component.UIInput.processValidators(UIInput.java:712)
    at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:575)
    at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIForm.visitTree(UIForm.java:371)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:403)
    at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:266)
    at org.primefaces.context.PrimePartialViewContext.processPartial(PrimePartialViewContext.java:57)
    at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:219)
    at org.omnifaces.context.OmniPartialViewContext.processPartial(OmniPartialViewContext.java:139)
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1193)
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)

Frankly speaking, I cannot explain where and why this happens because it exceeds my knowledge on this subject.

Dusan Kovacevic
  • 1,377
  • 1
  • 13
  • 19