3

I want to implement f:validateWholeBean with JSF 2.3. I tried to implement this example with Mojarra 2.3.0-m05 and Tomcat 8:

<h:form>
    <h:panelGroup>
        <h:inputSecret id="passwd" value="#{bean.dataList['passwd']}">
            <f:ajax event="blur" render="passwdvalidator" />
        </h:inputSecret>
        <h:message id="passwdvalidator" for="passwd" />
    </h:panelGroup>

    <h:panelGroup>Confirm Password</h:panelGroup>
    <h:panelGroup>
        <h:inputSecret id="confurmpasswd" value="#{bean.dataList['passwd']}">
            <f:ajax event="blur" render="confurmpasswdvalidator" />
        </h:inputSecret>
        <h:message id="confurmpasswdvalidator" for="confurmpasswd" />
    </h:panelGroup>
    <h:commandButton action="#{bean.submit}">
        <f:ajax render="@form" execute="@form"></f:ajax>
    </h:commandButton>                          

    <f:validateWholeBean  value="#{contactBean}" validationGroups="validateBean.ContactGroup" />
</h:form>

Custom Validator

@Named
@ViewScoped
public class NewAccountValidator implements Validator, Serializable
{
    @Override
    public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException
    {
        // not used
    }

    public void validatePasswords(FacesContext context, UIComponent component, Object value)
    {
        String l;
        String s = value.toString().trim();

        if (s != null)
        {
            // compare passwords
        }
        else
        {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_INFO,
                s.isEmpty() ? "  This field cannot be empty!" : "  '" + s + "' is not a number!", null));
        }
    }
}

What is the proper way to implement solution with f:validateWholeBean and custom JSF validator?

Peter Penzov
  • 1,126
  • 134
  • 430
  • 808

1 Answers1

5

You shouldn't implement a "standard" validator, but a ConstraintValidator.

You can find an example on Arjan Tijms Weblog:

<h:form>
    <h:inputText value="#{indexBean.foo}">
        <f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
    </h:inputText>
    <h:inputText value="#{indexBean.bar}">
        <f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
    </h:inputText>
 
    <f:validateWholeBean value="#{indexBean}" validationGroups="java.util.RandomAccess"/>
 
    <h:commandButton value="submit"/>
</h:form>

with backing bean:

@Named
@RequestScoped
@ValidIndexBean(groups = java.util.RandomAccess.class)
public class IndexBean implements ConstraintValidator<ValidIndexBean, IndexBean> {
 
    @Constraint(validatedBy = IndexBean.class)
    @Documented
    @Target(TYPE)
    @Retention(RUNTIME)
    public @interface ValidIndexBean {
        String message() default "Invalid Bean";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    @Inject // @EJB
    private PersistenceService service;

    @NotNull
    private String foo;
 
    @NotNull
    private String bar;
 
    @Override
    public void initialize(ValidIndexBean constraintAnnotation) {
        //
    }
 
    @Override
    public boolean isValid(IndexBean other, ConstraintValidatorContext context) {
        // return other.getFoo().equals(other.getBar());

        return service.query("select count(p) from Person p where p.foo like ?1 and p.bar like ?2", other.getFoo(), other.getBar()) == 0;
    }

    ...
}

answer for comments:

  • this is a regular bean, so yes, it can be @ViewScoped.
  • then you should create multiple validators: it's a bad practice to make a single validator perform multiple logics.

unrelated:

As I can see from the code you posted, you are misunderstanding the use of "classic" validator, making it a ManagedBean (CDI flavoured), but this is not the "plain" use of JSF Validators/Converters.

I suppose you are not using a validator, but a validation method instead.

A "classic" Validator should look like (see here):

@FacesValidator("usernameValidator")
public class UsernameValidator implements Validator, Serializable
{
    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
    {
        // you should use THIS method to validate a single Component's Value
        
        if(query("select count(*) from user where username = '?'", String.valueOf(value)) > 0)
        {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "invalid username"));
        }
    }
}

and should be used like:

<h:inputText value="#{someBean.username}" validator="usernameValidator" />

so:

  1. "classic" Faces Validators are tought for validating one component's value
  2. they shouldn't be @ManagedBean or @Named
  3. they should be referenced by name (validator="usernameValidator" without using EL expressions validator="#{usernameValidator}")

However, it's a best practice for Validators/Converters to be "specialized": they should perform a single validation logic.

If you need to validate a component value, i.e. a Date, that must be non-null and greater than 01/01/1970, you'll need two specialized validators.

Community
  • 1
  • 1
Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73
  • Thank you for the example. But I need to do SQL queries into the validator. Can you show me a quick example in which part of the code I can do the queries? – Peter Penzov May 03 '16 at 09:19
  • all the checks should be done in *isValid* method, included database access. see update. – Michele Mariotti May 03 '16 at 09:27
  • I have some additional questions: Can I use `ViewScoped` or it's mandatory to use `RequestScoped`? Also what if I have a case in which I need to have several SQL queries into one validator? Into the "classic" solution I just added additional Java method. – Peter Penzov May 03 '16 at 09:50
  • Thanks. Last question: I would like to use validator like a remind service. For example if I have a h:form with validator when I submit the form I will get error message. Is there a way to submit the form with the error message? I will use AJAX to display the error message when I enter some value. – Peter Penzov May 03 '16 at 10:59
  • you mean ignore validation failed? see [](http://showcase.omnifaces.org/taghandlers/ignoreValidationFailed) and [](http://showcase.omnifaces.org/taghandlers/skipValidators) – Michele Mariotti May 03 '16 at 11:29
  • Ok, but is there any solution with Core JSF? – Peter Penzov May 03 '16 at 12:17
  • nope, otherwise there's no reason for those taghandlers to exist. – Michele Mariotti May 03 '16 at 13:37