3. Introduction
La validation des données est indispensable dans
toute application. Elle peut intervenir à différents
niveaux :
Couche de présentation (informations saisies par
l’utilisateur)
Couche métier
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 3
Couche de persistance (conformité avec les contraintes
définies dans la base de données, … )
Etc…
Bean Validation propose un framework normalisé
(spécification Java, JSR,… ) pour valider les
informations portées par les Java Beans (et les
paramètres de méthodes dans la version 1.1)
4. Introduction
Les contraintes/rêgles de validation sont définies dans
le code source du bean à valider.
Elles sont exprimées par des « annotations » qui
peuvent être appliquées à différents niveaux :
Attribut
Méthode (getter/setter)
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 4
Méthode (getter/setter)
Classe
Elles peuvent aussi être définies dans un fichier XML
Un objet « validator » va ensuite vérifier que toutes
les contraintes du bean sont respectées
Il est possible d’étendre le framework en créant des
contraintes personnalisées
5. Exemples
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
...
public class Person {
@Min(1)
private int id ;
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 5
...
} @NotNull @Size(min=1, max=30)
private String name ;
@Min(0)
private int age ;
@NotNull @DecimalMin("1500.00")
@DecimalMax("99999.99")
private BigDecimal salary ;
...
}
7. Bean Validation version 1.0
Spécification lancée en 2006 et finalisée en 2009
JSR-303
cf : https://jcp.org/en/jsr/detail?id=303
Fait partie de Java EE 6
Cf : http://docs.oracle.com/javaee/6/tutorial/doc/
Utilisé notamment pour les validation dans JSF ( Java
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 7
Utilisé notamment pour les validation dans JSF ( Java
Server Faces )
Packages « javax.validation.* »
8. Spécification et implémentations
Bean Validation 1.0
Specification
API
JSR 303
Application
Java EE 6
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 8
Hibernate
Validator
Apache
Bean Validation
Implémentations
( R.I. )
http://bval.apache.org/http://hibernate.org/validator/
9. Les annotations de la JSR-303
Annotation Applicable aux types…
@Null
@NotNull Object
@Min
@Max
BigDecimal, BigInteger,
byte, short, int, long (+ Wrappers)
@DecimalMin
@DecimalMax
BigDecimal, BigInteger, String
byte, short, int, long (+ Wrappers)
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 9
byte, short, int, long (+ Wrappers)
@Size String, Collection, Map, Array
@Digits BigDecimal, BigInteger, String
byte, short, int, long (+ Wrappers)
@Past
@Future java.util.Date, java.util.Calendar
@Pattern String
@AssertTrue
@AssertFalse Boolean, boolean
11. Exemples
@Past
Date dateNaissance;
@Future
Date nextMeeting;
Dates ( vérification date du passé ou du futur )
Nombres ( vérification valeur mini/maxi )
@Min(0)
@Max(140)
private int age ;
@DecimalMin("5.00")
@DecimalMax("30.00")
BigDecimal discount;
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 11
@Digits(integer=6, fraction=2)
BigDecimal price;
// 'integer' : maximum integral digits for the number
// 'fraction' : maximum fractional digits for the number
@Pattern(regexp="(d{3})d{3}-d{4}")
String phoneNumber;
Chaînes de caractères ( vérification / expression régulière )
Nombres décimaux ( vérification « xxxx.xx »)
12. @NotEmpty // Ni null, ni vide
String firstName;
@NotBlank // Ni null, ni composé de “blancs”
Contraintes spécifiques Hibernate
NB : L’implémentation Hibernate Validator fournit des annotations
supplémentaires qui ne font pas partie de la spécification officielle.
Cf package « org.hibernate.validator.constraints »
Exemples :
NB : L’utilisation de
ces annotations
induit une adhérence
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 12
@NotBlank // Ni null, ni composé de “blancs”
String lastName;
@Email
String email ;
@CreditCardNumber // N° de carte de crédit (Luhn checksum)
@Length(min=2, max=12) // Longueur entre min et max
@ModCheck // “Mod 10” ou “Mod 11” checksum algorithm
@Range(min=2, max=12) // Valeur entre min et max
@SafeHtml(whitelistType=, additionalTags=) // => librairie Jsoup
@ScriptAssert(lang=, script=, alias=)
@URL(protocol=, host=, port=,...) // URL valide
induit une adhérence
à Hibernate !
13. Remarques
@Min(1) @DecimalMin("0.2")
int value;
Toutes les contraintes sont évaluées,
même si elles sont incohérentes ou contradictoires
Si value=0 : 2 violations détectées
@Min(3) @Max(2)
int value;
Toujours 1 violation détectée
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 13
Si une contrainte n’est pas applicable au type Exception
@Past // Pour une date
int value;
javax.validation.UnexpectedTypeException
No validator could be found for type: xxxxx@Pattern(regexp=".+@.+.[a-z]+")
int value;
14. Validation d’un graphe d’objets
public class Car {
@NotNull
@Valid
private Person driver;
Lorsque des objets sont imbriqués, il faut utiliser l’annotation
@Valid pour valider l’objet référencé
public class Person {
@Min(1)
« driver » ne doit pas être « null »
et l’instance de Person doit être « valid »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 14
private Person driver;
public Car(Person driver) {
this.driver = driver;
}
//getters & setters ...
}
@Min(1)
private int id;
@NotNull
private String name;
//getters & setters ...
}
@NotNull
@Valid
private List<Person> passengers = new ArrayList<Person>();
Egalement applicable pour les collections
16. Objet « Validator »
Les annotations (@NotNull, @Size, etc) ne font que
déclarer des contraintes
Ces contraintes sont vérifiées par un moteur de
validation : un objet qui implémente l’interface
« Validator »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 16
Ce « Validator » est généralement embarqué dans
l’environnement d’exécution, il est utilisé pour valider
les beans au moment opportun
17. Objet « Validator »
Validator est utilisé par
Les « containers » ou les « frameworks » qui gèrent le
cycle de vie des objets
Les environnements capables d’intercepter des appels de
méthodes (AOP, … ) et donc de déclencher une validation
lors de l’invocation d’une méthode
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 17
18. Objet « Validator »
Pour tester unitairement la validation des beans,
il faut récupérer une instance de « Validator » et invoquer
ses méthodes de validation.
Le « Validator » est fabriqué par « ValidatorFactory ».
ValidatorFactory validatorFactory =
Validation.buildDefaultValidatorFactory() ;
Validator validator = validatorFactory.getValidator();
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 18
L’appel de la méthode « validate » renvoie une
liste de « ConstraintViolation »:
Validator validator = validatorFactory.getValidator();
//--- Création et initialisation du bean
Author author = new Author();
author.setId(1);
//--- Validation du bean
Set<ConstraintViolation<Author>> constraintViolations =
validator.validate(author);
19. Objet « Validator »
Il est possible de ne valider que certaines propriétés d’un
bean avec validateProperty(…)
//--- Création et initialisation du bean
Author author = new Author();
author.setId(1);
//--- Validation des propriétés du bean
Set<ConstraintViolation<Author>> errors ;
errors = validator.validateProperty( author, "id" );
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 19
errors = validator.validateProperty( author, "id" );
errors = validator.validateProperty( author, "firstName" );
//--- Contraintes définies dans une classe
BeanDescriptor desc = validator.getConstraintsForClass(Author.class)
Récupération de la description des contraintes d’une classe :
//--- Si la valeur de l’attribut était...
validator.validateValue( Author.class, "firstName", "Foo" )
Situation de test pour une valeur d’une propriété (attribut) :
20. Exemple JUnit
public class AuthorValidTest {
private final static Validator validator =
Validation.buildDefaultValidatorFactory().getValidator();
@Test
public void testAuthor() {
Author author = new Author();
author.setId(1);
Set<ConstraintViolation<Author>> constraintViolations =
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 20
Set<ConstraintViolation<Author>> constraintViolations =
validator.validate(author);
print (constraintViolations);
}
private <T> void print( Set<ConstraintViolation<T>> errors ) {
for ( ConstraintViolation<T> cv : errors ) {
System.out.println(" . " + cv.getPropertyPath()
+ " (in " + cv.getRootBeanClass().getSimpleName()
+ ") " + cv.getMessage() );
}
}
22. Messages d’erreurs
Toutes les contraintes prédéfinies ont un message
par défaut disponible dans plusieurs langues.
Par exemple, Hibernate Validator affiche des
messages en français ou en anglais.
Chaque contrainte est identifiée par une « clé ».
A cette clé est associé un message (Java properties)
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 22
A cette clé est associé un message (Java properties)
Le message par défaut peut être forcé pour chaque
utilisation d’une annotation.
Exemple :
@NotNull(message = "Le nom ne doit pas être null" )
String nom;
@Min(value=0, message= "L’age ne peut pas être < 0" )
int age;
23. Messages d’erreurs
Les valeurs de l’annotation peuvent être insérées
dans le message en utilisant la notation {variable}
Exemple :
Les messages par défaut sont définis dans le fichier
@Min(value=0, message="L’age doit être au minimum {value} " )
int age;
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 23
Les messages par défaut sont définis dans le fichier
« ValidationMessages.properties ».
Ce fichier est utilisé par « MessageInterpolator »
(interface) qui va rechercher le fichier des messages
1) à la racine du ClassPath
2) dans le JAR de l’implémentation
( ex « org.hibernate.validator » pour Hibernate Validator )
24. Messages d’erreurs
Extrait du fichier des messages d’Hibernate Validator
javax.validation.constraints.AssertFalse.message =
must be false
javax.validation.constraints.AssertTrue.message =
must be true
javax.validation.constraints.DecimalMax.message =
must be less than or equal to {value}
javax.validation.constraints.DecimalMin.message =
must be greater than or equal to {value}
javax.validation.constraints.Digits.message =
Fichier
ValidationMessages.properties
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 24
javax.validation.constraints.Digits.message =
numeric value out of bounds
(<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Future.message =
must be in the future
javax.validation.constraints.Max.message =
must be less than or equal to {value}
javax.validation.constraints.Min.message =
must be greater than or equal to {value}
javax.validation.constraints.Past.message =
must be in the past
javax.validation.constraints.Pattern.message =
must match "{regexp}"
javax.validation.constraints.Size.message =
size must be between {min} and {max}
26. Contraintes spécifiques
Certaines règles de validation nécessitent de définir
des contraintes spécifiques
( « custom constraints » ).
Elles peuvent être positionnées à différents niveaux
Champ (attribut ) « field-level constraint »
Classe « class-level constraint »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 26
Classe « class-level constraint »
(validation globale d’une instance)
Pour créer une « custom constraint » il faut …
1) créer l’ annotation qui définit la contrainte
2) créer le « validator » qui va vérifier la contrainte
3) définir un message d’erreur par défaut
27. Custom Constraint 1 : « field level »
Exemple 1 :
création d’une contrainte qui vérifie qu’une chaîne de
caractères est bien
en « upper case » ou « lower case »…
=> « field-level »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 27
@CheckCase(value=CaseMode.LOWER)
private String firstName;
@CheckCase(value=CaseMode.UPPER)
private String lastName;
Utilisation attendue :
28. Custom Constraint 1 - Annotation
public enum CaseMode {
UPPER,
LOWER;
}
@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy =
CheckCaseValidator.class)
@Documented
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 28
@Documented
public @interface CheckCase {
String message() default "{org.demo.constraints.checkcase}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
CaseMode value();
}
Clé dans le
fichier des messages
29. Custom Constraint 1 - Validator
public class CheckCaseValidator
implements
ConstraintValidator<CheckCase, String> {
private CaseMode caseMode;
@Override
public void initialize( CheckCase constraintAnnotation ) {
this.caseMode = constraintAnnotation.value(); // MAJUS/MINUS
}
Valide des instances
de « String »
Contrainte
« CheckCase »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 29
@Override
public boolean isValid(String object, // objet à valider
ConstraintValidatorContext constraintContext) {
if (object == null) return true; // considéré comme valide
if (caseMode == CaseMode.UPPER) // test MAJUS. ou MINUS.
return object.equals(object.toUpperCase());
else
return object.equals(object.toLowerCase());
}
}
30. Custom Constraint 1 - Message
org.demo.constraints.checkcase = Invalid case, must be {value}
Fichier « ValidationMessages.properties »
situé à la racine du ClassPath
Pour gérer l’internationalisation des messages et avoir par
exemple des messages en français,
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 30
exemple des messages en français,
créer un fichier « ValidationMessages_fr.properties »
org.demo.constraints.checkcase = Controle MINUSCULE/MAJUSCULE invalide
31. Custom Constraint 2 : « class level »
Exemple 2 :
création d’une contrainte qui vérifie la validité d’une
instance de la classe « Person »
=> « class-level »
Utilisation attendue :
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 31
@PersonValidation // Class-level validation
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String firstName;
private String lastName;
[...]
}
Utilisation attendue :
32. Custom Constraint 2 - Annotation
@Documented
@Constraint(validatedBy = PersonValidator.class)
@Target({ElementType.TYPE}) // Annotation for a CLASS
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonValidation {
String message() default "{org.demo.constraints.person}";
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 32
Class<?>[] groups() default {};
Class<?>[] payload() default {};
// Pas d’autre info (pas de "value")
}
33. Custom Constraint 2 - Validator
public class PersonValidator
implements ConstraintValidator<PersonValidation, Person> {
@Override
public void initialize(PersonValidation constraint) {
// Rien à faire ici
}
@Override
Valide des instances
de « Person »
Validation de la contrainte
« PersonValidation »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 33
@Override
public boolean isValid( Person person,
ConstraintValidatorContext context) {
// Validation des différens champs de l’instance
if ( person.getId() < 1 ) return false ;
if ( person.getId() > 99999 ) return false ;
if ( person.getFirstName() == null ) return false ;
if ( person.getFirstName().length() < 1 ) return false ;
// etc...
return true;
}
35. Bean Validation version 1.1
Spécification approuvée début 2013
JSR-349
cf : https://jcp.org/en/jsr/detail?id=349
Fait partie de Java EE 7
Cf :
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 35
Cf :
https://blogs.oracle.com/theaquarium/entry/java_ee_7_pla
tform_completes
Implémentation de référence :
« Hibernate Validator »
Cf http://hibernate.org/validator/
36. JSR-349 : Quoi de neuf ?
Meilleure intégration avec :
JPA
CDI
JAX-RS
JSF
2 nouveaux packages
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 36
2 nouveaux packages
avec notamment la classe
« ValidateOnExecution »
Validation des paramètres d’une méthode
Utilisation de « EL » pour les messages
Les annotations servant à définir les contraintes
sont les mêmes qu’en JSR-303 (pas de changement)
37. Validation lors de l’appel de méthodes
public class MyBean {
private String name;
Les annotations peuvent aussi être utilisées pour contrôler
la validité des paramètres passés à des méthodes ou des
constructeurs, ainsi que les valeurs renvoyées.
Exemples :
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 37
public MyBean( @NotNull String name ) {
this.name = name;
}
@Size(min = 3)
public String updateName(
@NotNull @Size(min = 1) String firstName,
@NotNull @Size(min = 1) String lastName) {
name = firstName + " " + lastName;
return name;
}
}
38. Validation lors de l’appel de méthodes
// Valider les paramètres simples d’une méthode
public User createUser ( @NotNull String name,
@Past Date birthDate ) {
...
}
// Valider des paramètres de type “bean” (objet spécifique)
Exemples :
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 38
// Valider des paramètres de type “bean” (objet spécifique)
public Order createOrder (..., @Valid User user) {
...
}
// Valider le “résultat” (retour) d’une méthode
@NotNull
public Employee getEmployee() { // Ne doit pas renvoyer null
...
}
39. Annotation « @ValidateOnExecution »
Permet d’indiquer quelles méthodes doivent être validées lors
de l’exécution (méthodes ou constructeurs).
Elle peut être placée au niveau de la classe et/ou
au niveau des méthodes/constructeurs
Si l’annotation @ValidateOnExecution est présente
elle définit explicitement les règles de validation à appliquer
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 39
elle définit explicitement les règles de validation à appliquer
(quelles méthodes,
quels types de méthodes, ...)
Si elle n’est pas présente, c’est la configuration de
« bean validation » qui s’applique.
La configuration est définie à partir …
• du fichier « META-INF/validation.xml » s’il existe
• de la configuration par défaut (cf JavaDoc)
ALL, CONSTRUCTORS, GETTER_METHODS,
NON_GETTER_METHODS, IMPLICIT, NONE
40. Validation des appels de méthodes
Principe de fonctionnement :
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 40
41. Exemples
public class OrderService {
boolean isValidCustomer( @NotNull String customerCode) {
[...]
}
@ValidateOnExecution
@Min(0)
Dans un service avec « @ValidateOnExecution »
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 41
@Min(0)
Integer getBacklog() {
[...]
}
@ValidateOnExecution(type=NONE)
Order placeOrder( @NotNull String customerCode,
@Valid Item item, int quantity) {
[...]
}
}
43. Adoption de la JSR-303
Bean Validation est utilisé dans
Certains composants standards de Java EE (de + en +),
par exemple :
JSF (JavaServer Faces) – Web
Jersey (RI de JAX-RS) – REST
JPA – Persistance
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 43
Et dans de nombreux frameworks Java,
notamment :
Spring MVC
Wicket
Tapestry
44. Bean Validation / JPA
Author author = new Author() ;
author.setId(1);
author.setFirstName("Joe");
public class Author {
@Min(1) @Max(99999)
private int id;
@NotNull @Size(min=1, max=20 )
private String firstName;
[...]
}
JPA valide le Bean lors
de l’invocation des
méthodes de persistance
Si bean invalide Exception
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 44
author.setFirstName("Joe");
EntityManager em = .. ;
try {
em.persist(author); // Validation du bean par JPA
}
catch (ConstraintViolationException exception) {
Set<ConstraintViolation<?>> constraintsViolations =
exception.getConstraintViolations();
int n = constraintsViolations.size();
}
45. Bean Validation / Jersey (REST / JAX-RS )
@Path("/")
class MyResourceClass {
@POST
@Consumes("application/x-www-form-urlencoded")
public void registerUser(
@NotNull @FormParam("firstName") String firstName,
@NotNull @FormParam("lastName") String lastName,
@Email @FormParam("email") String email) {
...
}
}
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 45
@Path("/")
class MyResourceClass {
@Email
@ValidateOnExecution
public String getEmail() {
return email;
}
}
}
@Path("/")
class MyResourceClass {
@POST
@Consumes("application/xml")
public void registerUser(
@Valid User user) {
...
}
}
46. Bean Validation / Spring MVC
Depuis la version 3.0 Spring MVC supporte
intégralement la JSR-303
Exemples :
import javax.validation.Validator ;
@Service
public class MyService {
@Autowired
Injection d’un « Validator »
dans un service
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 46
@Controller
public class MyController {
@RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo( @Valid Foo foo ) {...}
@Autowired
private Validator validator;
Validation des données d’un
formulaire dans « Controller »
47. Bean Validation / Wicket
Par ajout d’un « JSR303FormValidator »
public class HomePage extends WebPage {
public HomePage(final PageParameters parameters) {
Person person = new Person();
final Form<Person> personForm = new Form<Person>("personForm",
new CompoundPropertyModel<Person>(person) );
personForm.add(new TextField<String>("firstName"));
personForm.add(new TextField<String>("lastName"));
personForm.add(new TextField<Integer>("age"));
personForm.add(new Button("submit") {
JAVA – Bean Validation API ( Laurent Guérin / v 1.1 / www.laurent-guerin.fr ) 47
personForm.add(new Button("submit") {
@Override
public void onSubmit() {
personForm.info("Submitted " +
personForm.getDefaultModelObjectAsString());
}
});
personForm.add( new JSR303FormValidator() );
add(personForm);
add(new FeedbackPanel("feedback"));
}
}
Nécessite un
« CompoundPropertyModel »
pour l’accès aux propriétés
du bean par réflexion