Bean validation with f:validateBean

Probably the most important validation tag provided by JSF 2.0 is the f:validateBean tag. For a start, you have to know that this tag is part of a mechanism whose aim is to integrate Bean Validation with JSF 2.0. Bean Validation—known as JSR 303 (http://jcp.org/en/jsr/detail?id=303); officially part of the new Java EE 6 this defines a metadata model and API for JavaBean validation. The default "metadata source is annotations, with the ability to override and extend the meta-data through the use of XML validation descriptors". In this recipe, you will see how to exploit the Bean Validation.

Getting ready

We developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.

How to do it...

Instead of placing validation rules in different layers of the application and keeping them synchronized, we can take use of Bean Validation and use its constraint annotations in the managed beans—JSF 2 provides built-in integration with JSR-303 constraints—as in the following example:

public class userBean {
@NotEmpty(message = "The name cannot be empty!")
@Size(min = 5, max = 20, message="You must provide a name between 5
and 20 characters!")
private String name;
@Digits(integer = 2, fraction = 0, message = "You must provide a
valid age!")
@Range(min=18, max=99, message="You must be over 18 years old!")
private int age;
@NotEmpty(message = "The email cannot be empty!")
//instead of @Pattern you can use @Email
@Pattern(regexp =
"[a-zA-Z0-9_]*[@]{1}[a-zA-Z0-9_]*[.]{1}[a-zA-Z]{2,3}",
message="You must provide at least
an well-formed e-mail address!")
private String email;
…

In the following table you can see a summary of the Bean Validation annotation with a short description:

Annotation

BVS *

Apply on

Use

@AssertFalse

Yes

Field/property

Check that the annotated element is false.

@AssertTrue

Yes

Field/property

Check that the annotated element is true.

@DecimalMax

Yes

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, and long.

Check that the annotated element is a number whose value is lower than or equal to the specified maximum.

@DecimalMin

Yes

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, long.

Check that the annotated element is a number whose value is higher than or equal to the specified minmum.

@Digits(integer=, fraction=)

Yes

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, long.

Check that the annotated element is a number having up to integer digits and fraction fractional digits.

@Future

Yes

Field/property

Supported types are java.util.Date and java.util.Calendar.

Check that the annotated date is in the future.

@Max

Yes

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, long.

Check that the annotated value is less than or equal to the specified maximum.

@Min

Yes

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, long.

Check that the annotated value is higher than or equal to the specified minimum.

@NotNull

Yes

Field/property

Check that the annotated value is not null.

@Null

Yes

Field/property

Check that the annotated value is null.

@Valid

Yes

Field/property

Perform validation recursively on the associated object.

@Past

Yes

Field/property

Supported types are java.util.Date and java.util.Calendar.

Check that the annotated date is in the past.

@Size(min=, max=)

Yes

field/property

Supported types are String, Collection, Map, and arrays.

Check if the annotated element size is between min and max (inclusive).

@Length(min=, max=)

No

Field/property

Check that the annotated string is between min and max included.

@NotEmpty

No

Field/property

Check that the annotated string is not null or empty.

@Email

No

Field/property

Check that the annotated string is a valid email address.

@Range(min=, max=)

No

Field/property

Supported types are:

BigDecimal, BigInteger, String, byte, short, int, and long.

Check that the annotated value lies between the specified minimum and maximum (inclusive).

* BVS - Bean Validation Specification

Note

The annotations marked as "yes" belong to BVS and they can be found in the javax.validation.constraints package, while the ones marked with "no" can be found in the org.hibernate.validator.constraints package.

After we set our annotation, we can control (fine tune) the validation from the JSF pages with the f:validateBean tag. The f:validateBean supports the following optional attributes:

  • binding: A ValueExpression that evaluates to an object that implements javax.faces.validate.BeanValidator.
  • disabled: A boolean value enabling page-level determination of whether or not this validator is enabled on the enclosing component.
  • validationGroups: A comma-delimited string of type-safe validation groups that are passed to the Bean Validation API when validating the value.
  • By default, the Bean Validator is enabled, therefore our JSF pages may not contain any code fragments that reveal the presence of the Bean Validator. For example, the following JSF page makes use of Bean Validator without our explicit specification:
    …
    <h:form>
    <h:panelGrid columns="2">
    <h:outputText value="Name:"/>
    <h:inputText value="#{userBean.name}"/>
    <h:outputText value="Age:"/>
    <h:inputText value="#{userBean.age}"/>
    <h:outputText value="E-mail:"/>
    <h:inputText value="#{userBean.email}"/>
    </h:panelGrid>
    <h:commandButton value="Submit" action="index?faces- redirect=true"/>
    </h:form>
    …
    

A possible output is in the following screenshot:

How to do it...

Now, if we want to disable the Bean Validator for a specific field, then we must get involved and set the disabled attribute to false, as in the following code, where we disable validation for the user age:

…
<h:outputText value="Age:"/>
<h:inputText value="#{userBean.age}">
<f:validateBean disabled="true" />
</h:inputText>
…

Note

Add a context-param to your web.xml, javax.faces.VALIDATE_EMPTY_FIELDS, by default it is set to auto. If it is true, all submitted fields will be validated. This is necessary to delegate validation of whether a field can be null/empty to the model validator. If it is false, empty values will not be passed to the validators. If it is auto, the default will be true only if Bean Validation is in the environment, false otherwise (which keeps backward compatibility).

There's more...

You also may want to save the validation groups—allowing you to restrict the set of constraints applied during validation—in an attribute on the parent to be used as defaults inherited by any Bean Validator in that context (an empty String is not allowed). If no validation groups are inherited, assume the Default validation group, javax.validation.groups.Default.

The property validationGroups on BeanValidator is used to allow the view designer to specify a comma-separated list of groups that should be validated. A group is represented by the fully qualified class name of its interface. If the validationGroups attribute is omitted, the Default (javax.validation.groups.Default) group will be used. If the model validator is set as the default validator, this tag can be used to specify validation groups for this input.

In practice, groups are just simple Java interfaces. Using interfaces makes the usage of groups type safe and allows for easy refactoring. In addition, groups can inherit from each other via class inheritance. As per the example, we can use two different groups as shown next:

//the usersIdsGroup
public interface usersIdsGroup {
}
//the usersCredentialsGroup
public interface usersCredentialsGroup {
}

Next, we can bind managed beans' properties to groups as shown next:

…
@NotEmpty(message = "The name cannot be empty!",
groups = beans.usersIdsGroup.class)
@Size(min = 5, max = 20, message = "You must provide a name between 5 and 20 characters!", groups = beans.usersIdsGroup.class)
private String name;
@Digits(integer = 2, fraction = 0,
message = "You must provide a valid age!",
groups = beans.usersIdsGroup.class)
@Range(min = 18, max = 99,
message = "You must be over 18 years old!",
groups = beans.usersIdsGroup.class)
private int age;
@NotEmpty(message = "The email cannot be empty!",
groups = beans.usersIdsGroup.class)
//instead of @Pattern you can use @Email
@Pattern(
regexp = "[a-zA-Z0-9_]*[@]{1}[a-zA-Z0-9_]*[.]{1}[a-zA-Z]{2,3}",
message = "You must provide at least an well-formed e-mail
address!",
groups = beans.usersIdsGroup.class)
private String email;
@NotEmpty(message = "The ID cannot be empty!",
groups = beans.usersCredentialsGroup.class)
@Size(min = 5, max = 20,
message = "You must provide an ID between 5 and 20
characters!",
groups = beans.usersCredentialsGroup.class)
private String nickname;
@NotEmpty(message = "The password cannot be empty!",
groups = beans.usersCredentialsGroup.class)
@Size(min = 5, max = 20,
message = "You must provide a password between 5 and 20
characters!",
groups = beans.usersCredentialsGroup.class)
private String password;
…

As you can see, the name, age, and email properties belong to the usersIdsGroup group, while the nickname and password properties belongs to the usersCredentialsGroup group. Next, in a JSF page, we can validate both groups like this:

…
<f:validateBean validationGroups="beans.usersIdsGroup,usersCredentialsGroup">
<h:outputText value="Name:"/>
<h:inputText value="#{userBean.name}"/>
<h:outputText value="Age:"/>
<h:inputText value="#{userBean.age}"/>
<h:outputText value="E-mail:"/>
<h:inputText value="#{userBean.email}"/>
<h:outputText value="ID:"/>
<h:inputText value="#{userBean.nickname}"/>
<h:outputText value="Password"/>
<h:inputSecret value="#{userBean.password}"/>
</f:validateBean>
…

If we want to validate only the usersIdsGroup group, then we remove this group from the value of the validationGroups attribute:

…
<f:validateBean validationGroups="beans.usersIdsGroup">
…
</f:validateBean>
…

You also may call a Bean validator programatically. The following code snippet shows you how to accomplish this:

public class UserValidator {
public boolean validateUser(userBean user) {
ValidatorFactory factory =
Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<userBean>> constraintViolations =
validator.validate(user, Default.class);
if (!constraintViolations.isEmpty())
return false;
constraintViolations = validator.validate(user,
beans.usersIdsGroup.class);
return constraintViolations.isEmpty();
}
}

How it works...

As you just saw, Bean Validation centralized constraint declarations and is based on a several standard constraint annotations (for example @Size, @Min, @Max, @AssertTrue, @AssertFalse, and so on) and also allows custom constraints to be defined. In addition we can use groups that allow us to restrict the set of constraints applied during validation. This time the validator restrictions are taken directly from the bean, instead of using dedicated attributes inside the validator tag.

The complete reference for Bean Validation is JSR-303 available at http://jcp.org/en/jsr/detail?id=303.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: Bean_validation_with_validateBean_1 and Bean_validation_with_validateBean_2.

More details about the f:validateBean tag specification can be found at:

https://javaserverfaces.dev.java.net/nonav/docs/2.0/pdldocs/facelets/f/validateBean.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset