• cdi
  • components
  • contexts
  • converters
  • eventlisteners
  • exceptionhandlers
  • facesviews
  • filters
  • functions
  • managedbeans
  • push
  • renderkits
  • resourcehandlers
  • scripts
  • servlets
  • taghandlers
  • utils
  • validators
  • viewhandlers
-
  • JsfLabelMessageInterpolator
  • RequiredCheckboxValidator
  • ValueChangeValidator
  • validateAll
  • validateAllOrNone
  • validateBean
  • validateEqual
  • validateMultiple
  • validateOne
  • validateOneOrMore
  • validateOneOrNone
  • validateOrder
  • validateUnique
  • validateUniqueColumn

The <o:validateBean> allows the developer to control bean validation on a per-UICommand or UIInput component basis, as well as validating a given bean at the class level.

The standard <f:validateBean> only allows validation control on a per-form or a per-request basis (by using multiple tags and conditional EL expressions in its attributes) which may end up in boilerplate code.

The standard <f:validateBean> also, despite its name, does not actually have any facilities to validate a bean at all.

Usage

Some examples

Control bean validation per component

<h:commandButton value="submit" action="#{bean.submit}">
    <o:validateBean validationGroups="javax.validation.groups.Default,com.example.MyGroup" />
</h:commandButton>
<h:selectOneMenu value="#{bean.selectedItem}">
    <f:selectItems value="#{bean.availableItems}" />
    <o:validateBean disabled="true" />
    <f:ajax execute="@form" listener="#{bean.itemChanged}" render="@form" />
</h:selectOneMenu>

Validate a bean at the class level

<h:inputText value="#{bean.product.item}" />
<h:inputText value="#{bean.product.order}" />

<o:validateBean value="#{bean.product}" validationGroups="com.example.MyGroup" />

Class level validation details

In order to validate a bean at the class level, all values from input components should first be actually set on that bean and only thereafter should the bean be validated. This however does not play well with the JSF approach where a model is only updated when validation passes. But for class level validation we seemingly can not validate until the model is updated. To break this tie, a copy of the model bean is made first, and then values are stored in this copy and validated there. If validation passes, the original bean is updated.

A bean is copied using the following strategies (in the order indicated):

  1. Cloning - Bean must implement the Cloneable interface and support cloning according to the rules of that interface. See CloneCopier
  2. Serialization - Bean must implement the Serializable interface and support serialization according to the rules of that interface. See SerializationCopier
  3. Copy constructor - Bean must have an additional constructor (next to the default constructor) taking a single argument of its own type that initializes itself with the values of that passed in type. See CopyCtorCopier
  4. New instance - Bean should have a public no arguments (default) constructor. Every official JavaBean satisfies this requirement. Note that in this case no copy is made of the original bean, but just a new instance is created. See NewInstanceCopier

If the above order is not ideal, or if an custom copy strategy is needed (e.g. when it's only needed to copy a few fields for the validation) a strategy can be supplied explicitly via the copier attribute. The value of this attribute can be any of the build-in copier implementations given above, or can be a custom implementation of the Copier interface.

Demo

Leave either the field blank, or enter less than 6 characters





Validating at the class level that number 1 is smaller than number 2 - default (validate copy)

This uses the default validation method, which copies the object, sets the values retrieved from the input components on it, and performs class level validation on the copy. In order to make the copy, cloning, serialization, a copy constructor and a new instance will be attempted (in that order). Since our model object has a copy constructor, but is not cloneable or serializable, the copy constructor will be the one that is used.

Bean values in model:
Number 1: 0
Number 2: 0


Validating at the class level that number 1 is smaller than number 2 - validate actual bean

This uses an alternative validation method, which doesn't copy the bean but lets JSF do its normal job all the way up till the phase UPDATE MODEL VALUES, and only thereafter will it validate the actual object. This has the advantage that no copying and value collecting are needed, but atypical for JSF is that the model is updated even though there are validation errors. The bean developer is also required to manually check in the bean whether there are validation errors.

Bean values in model:
Number 1: 0
Number 2: 0


Validating at the class level that number 1 is smaller than number 2 - using custom copier

This uses the default validation method like the example above, but uses a custom copier. Such copier would be needed when the bean originates from another module, which the front end developer can't modify to be either cloneable, serializable or having a copy constructor. A custom copier also allows customization of the copy process, e.g. when a bean contains many fields but it's clear that only a few are needed for the validation.

Bean values in model:
Number 1: 0
Number 2: 0

Demo source code
<h3>Leave either the field blank, or enter less than 6 characters</h3>
<h:form>
    <h:panelGrid columns="3">
        <o:outputLabel for="value" value="Value" />
        <h:inputText id="value" value="#{validateBean.value}" />
        <h:message for="value"/>

        <h:panelGroup />
        <h:panelGroup>
            <f:ajax execute="@form" render="@form">
                <h:commandButton value="submit (only default validation)" />
                <br/>
                <h:commandButton value="submit (only MyValidationGroup validation)">
                    <o:validateBean validationGroups="org.omnifaces.showcase.validators.MyValidationGroup" />
                </h:commandButton>
                <br/>
                <h:commandButton value="submit (both default and MyValidationGroup validation)">
                    <o:validateBean validationGroups="javax.validation.groups.Default,org.omnifaces.showcase.validators.MyValidationGroup" />
                </h:commandButton>
                <br/>
                <h:commandButton value="submit (no validation)">
                    <o:validateBean disabled="true" />
                </h:commandButton>
            </f:ajax>
        </h:panelGroup>
        <h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
    </h:panelGrid>
</h:form>

<br/>
<h3>Validating at the class level that number 1 is smaller than number 2 - default (validate copy)</h3>
<p>
    <small>
        This uses the default validation method, which copies the object, sets the values retrieved from the input components
        on it, and performs class level validation on the copy. In order to make the copy, cloning, serialization, a copy constructor
        and a new instance will be attempted (in that order). Since our model object has a copy constructor, but is not cloneable or
        serializable, the copy constructor will be the one that is used.
    </small>
</p>

<h:form id="classLevelForm">

    <h:messages styleClass="messages" infoClass="info" warnClass="warn" errorClass="error" />

    <p>
        Bean values in model:<br/>
        Number 1: #{validateClassLevelBean.product.number1}<br/>
        Number 2: #{validateClassLevelBean.product.number2}<br/>
    </p>

    <h:panelGrid columns="2">
        <o:outputLabel for="number1" value="Number 1" />
        <h:inputText id="number1" value="#{validateClassLevelBean.product.number1}" />
     
        <o:outputLabel for="number2" value="Number 2" />
        <h:inputText id="number2" value="#{validateClassLevelBean.product.number2}" />
    </h:panelGrid>
    
    <h:commandButton value="submit" action="#{validateClassLevelBean.action}">
        <f:ajax execute="@form" render="@form"/>
    </h:commandButton>
    
    <o:validateBean value="#{validateClassLevelBean.product}" validationGroups="org.omnifaces.showcase.validators.ProductGroup" />        
</h:form>

<br/>
<h3>Validating at the class level that number 1 is smaller than number 2 - validate actual bean</h3>
  <p>
   <small>
       This uses an alternative validation method, which doesn't copy the bean but lets JSF do its normal job all the way up till
       the phase UPDATE MODEL VALUES, and only thereafter will it validate the actual object. This has the advantage that no copying
       and value collecting are needed, but atypical for JSF is that the model is updated even though there are validation errors.
       The bean developer is also required to manually check in the bean whether there are validation errors.
   </small>
</p>

<h:form id="classLevelFormActualBean">

    <h:messages styleClass="messages" infoClass="info" warnClass="warn" errorClass="error" />

    <p>
        Bean values in model:<br/>
        Number 1: #{validateClassLevelBean.product.number1}<br/>
        Number 2: #{validateClassLevelBean.product.number2}<br/>
    </p>

    <h:panelGrid columns="2">
        <o:outputLabel for="number1" value="Number 1" />
        <h:inputText id="number1" value="#{validateClassLevelBean.product.number1}" />
     
        <o:outputLabel for="number2" value="Number 2" />
        <h:inputText id="number2" value="#{validateClassLevelBean.product.number2}" />
    </h:panelGrid>
    
    <h:commandButton value="submit" action="#{validateClassLevelBean.action}">
        <f:ajax execute="@form" render="@form"/>
    </h:commandButton>
    
    <o:validateBean value="#{validateClassLevelBean.product}" validationGroups="org.omnifaces.showcase.validators.ProductGroup" method="validateActual" />        
</h:form>

<br/>
<h3>Validating at the class level that number 1 is smaller than number 2 - using custom copier</h3>
<p>
    <small>
        This uses the default validation method like the example above, but uses a custom copier. Such copier would be needed when the bean
        originates from another module, which the front end developer can't modify to be either cloneable, serializable or having a copy
        constructor. A custom copier also allows customization of the copy process, e.g. when a bean contains many fields but it's clear
        that only a few are needed for the validation.
    </small>
</p>

<h:form id="classLevelFormCustomCopier">

    <h:messages styleClass="messages" infoClass="info" warnClass="warn" errorClass="error" />

    <p>
        Bean values in model:<br/>
        Number 1: #{validateClassLevelBean.product.number1}<br/>
        Number 2: #{validateClassLevelBean.product.number2}<br/>
    </p>

    <h:panelGrid columns="2">
        <o:outputLabel for="number1" value="Number 1" />
        <h:inputText id="number1" value="#{validateClassLevelBean.product.number1}" />
     
        <o:outputLabel for="number2" value="Number 2" />
        <h:inputText id="number2" value="#{validateClassLevelBean.product.number2}" />
    </h:panelGrid>
    
    <h:commandButton value="submit" action="#{validateClassLevelBean.action}">
        <f:ajax execute="@form" render="@form"/>
    </h:commandButton>
    
    <o:validateBean value="#{validateClassLevelBean.product}" validationGroups="org.omnifaces.showcase.validators.ProductGroup" copier="org.omnifaces.showcase.model.ProductCopier" />        
</h:form>