3

I have an issue with OmniFaces 1.11 <o:validateBean /> at class level (http://showcase.omnifaces.org/validators/validateBean)

Basically, i'm trying to validate the same bean using two validation groups according to the button I click :

            <h:commandButton value="Mandatory">
                <o:validateBean value="#{testBean.pojo}" validationGroups="bo.ihm.beans.Mandatory" />
            </h:commandButton>

            <h:commandButton value="Optional">
                <o:validateBean value="#{testBean.pojo}" validationGroups="javax.validation.groups.Default" />
            </h:commandButton>

Whenever I click on a button, I get

GRAVE: Exception occured while doing validation.
javax.el.ELException: /testOmnifaces.xhtml @21,74 value="#{testBean.pojo.forain}": java.lang.IllegalArgumentException: Cannot convert PROPERTY of type class org.omnifaces.el.ExpressionInspector$ValueExpressionType to class java.lang.Boolean
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:114)
    at org.omnifaces.el.ExpressionInspector.getValueReference(ExpressionInspector.java:43)
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:331)
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:326)
    at org.omnifaces.util.Components$ForEach$1.visit(Components.java:508)
    at org.omnifaces.util.Components$ForEach$TypesVisitCallback.visit(Components.java:573)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIForm.visitTree(UIForm.java:371)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:541)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:505)
    at org.omnifaces.taghandler.ValidateBean.forEachInputWithMatchingBase(ValidateBean.java:326)
    at org.omnifaces.taghandler.ValidateBean.access$400(ValidateBean.java:151)
    at org.omnifaces.taghandler.ValidateBean$3.run(ValidateBean.java:286)
    at org.omnifaces.taghandler.ValidateBean$ValidateBeanCallback.invoke(ValidateBean.java:430)
    at org.omnifaces.util.Events$1.invoke(Events.java:278)
    at org.omnifaces.util.Events$4.beforePhase(Events.java:312)
    at org.omnifaces.eventlistener.CallbackPhaseListener.beforePhase(CallbackPhaseListener.java:63)
    at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:696)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:521)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:138)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:564)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:213)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1097)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:448)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:175)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1031)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:200)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
    at org.eclipse.jetty.server.Server.handle(Server.java:446)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:271)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:246)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.el.ELException: java.lang.IllegalArgumentException: Cannot convert PROPERTY of type class org.omnifaces.el.ExpressionInspector$ValueExpressionType to class java.lang.Boolean
    at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:229)
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
    ... 44 more
Caused by: java.lang.IllegalArgumentException: Cannot convert PROPERTY of type class org.omnifaces.el.ExpressionInspector$ValueExpressionType to class java.lang.Boolean
    at com.sun.el.lang.ELSupport.coerceToBoolean(ELSupport.java:189)
    at com.sun.el.lang.ELSupport.coerceToType(ELSupport.java:394)
    at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:227)
    ... 45 more

testWithClassLevelValidation.xhtml :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:o="http://omnifaces.org/ui">

<f:metadata>
    <f:viewAction action="#{testBean.init}" />
</f:metadata>

<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Test</title>
</h:head>

<h:body>

    <h:form id="formaliteACCRE">
        <h:panelGrid columns="1" style="width: 100%;">
            <h:panelGrid columns="2" style="width: 100%; display: inline-block;">
                <h:outputLabel for="forain">Yes or No ?</h:outputLabel>
                <h:selectBooleanCheckbox id="forain" value="#{testBean.pojo.forain}">
                    <f:ajax execute="forain" render="groupeDomicile" />
                </h:selectBooleanCheckbox>
            </h:panelGrid>

            <h:panelGroup id="groupeDomicile">
                <h:outputText value="Yes" rendered="#{testBean.pojo.forain}" />
                <h:outputText value="No" rendered="#{not testBean.pojo.forain}" />
            </h:panelGroup>

            <h:inputText id="numeroSecu" value="#{testBean.pojo.numeroSecuriteSociale}" maxlength="13" size="13" />
            <h:inputText id="numeroSecuCle" value="#{testBean.pojo.cleSecuriteSociale}" maxlength="2" size="3" />

            <h:panelGrid columns="2" style="width: 100%; display: inline-block;">
                <h:inputText id="mandatory" value="#{testBean.pojo.mandatory}" maxlength="13" size="13" />
                <h:outputText value="*" style="color:red;" />
            </h:panelGrid>

            <f:ajax execute="@form" render="@form">

                <h:commandButton value="Mandatory">
                    <o:validateBean value="#{testBean.pojo}" validationGroups="bo.ihm.beans.Mandatory" />
                </h:commandButton>

                <h:commandButton value="Optional">
                    <o:validateBean value="#{testBean.pojo}" validationGroups="javax.validation.groups.Default" />
                </h:commandButton>
            </f:ajax>
        </h:panelGrid>

        <ui:remove> ################################################################################################ </ui:remove>
        <ui:remove>                             ===== MESSAGES D'ERREURS =====                                       </ui:remove>
        <ui:remove> ################################################################################################ </ui:remove>

        <h:panelGroup id="messagesErreur">
            <h:panelGrid class="dr-pnl" width="100%" rendered="#{not empty facesContext.messageList}">
                <h:messages style="color:red;" />
            </h:panelGrid>
        </h:panelGroup>
    </h:form>
</h:body>
</html>

TestBean.java :

package bo.ihm.beans;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.validation.Valid;

@ViewScoped
@ManagedBean(name = "testBean")
public class TestBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Valid
    private Pojo pojo;

    public Pojo getPojo() {
        return pojo;
    }

    public void init() {
        pojo = new Pojo();
        pojo.setNumeroSecuriteSociale("2531257351038");
        pojo.setCleSecuriteSociale("22");
    }

    public void setPojo(final Pojo pPojo) {
        this.pojo = pPojo;
    }
}

Pojo.java :

package bo.ihm.beans;

import org.hibernate.validator.constraints.NotEmpty;

@NumeroSecuriteSociale
public class Pojo {

    private boolean forain;
    private String cleSecuriteSociale;
    private String numeroSecuriteSociale;

    @NotEmpty(groups = Mandatory.class)
    private String mandatory;

    public String getCleSecuriteSociale() {
        return cleSecuriteSociale;
    }

    public String getMandatory() {
        return mandatory;
    }

    public String getNumeroSecuriteSociale() {
        return numeroSecuriteSociale;
    }

    public boolean isForain() {
        return forain;
    }

    public void setCleSecuriteSociale(final String pCleSecuriteSociale) {
        cleSecuriteSociale = pCleSecuriteSociale;
    }

    public void setForain(final boolean pForain) {
        forain = pForain;
    }

    public void setMandatory(final String pMandatory) {
        mandatory = pMandatory;
    }

    public void setNumeroSecuriteSociale(final String pNumeroSecuriteSociale) {
        numeroSecuriteSociale = pNumeroSecuriteSociale;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("Pojo [forain=");
        builder.append(forain);
        builder.append(", cleSecuriteSociale=");
        builder.append(cleSecuriteSociale);
        builder.append(", numeroSecuriteSociale=");
        builder.append(numeroSecuriteSociale);
        builder.append(", mandatory=");
        builder.append(mandatory);
        builder.append("]");
        return builder.toString();
    }
}

the full code is available at https://github.com/ErwanLeroux/Bo_ihm_test_omnifaces just run clean jetty:run-war et go to http://localhost:14080/bo_ihm/testWithClassLevelValidation.xhtml

The second page http://localhost:14080/bo_ihm/testWithoutClassLevelValidation.xhtml is her to demonstrate that class level constraints doesn't work, when i click on whenever button, I should get a validation error message like this : 'Pojo [forain=false, cleSecuriteSociale=22, numeroSecuriteSociale=2531257351038, mandatory=null] is not valid'

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Erwan Leroux
  • 108
  • 2
  • 11
  • Reproduced, bug is triggered by h:selectBooleanCheckbox (remove it and problem disappears). We will investigate it and fix for 2.2. – BalusC Jul 03 '15 at 14:59
  • Thanks. I downgraded to version 1.10, and I cannot reproduce this issue. I will ask an new question because does not behave like I think it should at class level. – Erwan Leroux Jul 03 '15 at 15:09
  • https://github.com/omnifaces/omnifaces/issues/144 – Erwan Leroux Jul 24 '15 at 12:10

1 Answers1

1

It turns out that implicit EL coercion is was causing this all. We were passing around internal values like ValueExpressionType.PROPERTY and ValueExpressionType.METHOD to indicate if the EL expression represents a property (getter/setter) call or a method (action) call. However, this approach failed if the value has implicit EL coercion support, like java.lang.Boolean, on which EL would implicitly convert e.g. a string of "true" to a boolean true.

This approach of passing around internal values to indicate the kind of EL expression was since OmniFaces 2.0 used to breakdown the EL expression in <o:grapicImage value> and extract any EL method arguments so they could be converted and passed around as HTTP query string parameters. That explains why it still worked fine in OmniFaces 1.x.

As per this commit, this has been fixed for OmniFaces 2.2.

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