La validation en Java JEE JSR-303

Ce billet présente brièvement la manière standard de faire la validation des données dans un projet java et ce quelque soit la nature du projet.
Généralement, la validation des données repose sur les (extensions des) frameworks utilisés (Struts, Spring MVC, .. ou les ORM tel Hibernate, ...).
Chaque tiers du projet possède une façon spécifique de faire la validation.
Nous présentons la validation de la JSR-303 car elle s'applique à tous les tiers.
Nous illustrons la validation JSR 303 avec l'implémentation de référence, v4.x, de JBoss Hibernate.

La démonstration est réalisée avec maven 2.x / Eclipse. Elle est testée sous java6 / Windows.
Mais elle doit fonctionner sur tout autre environnement linux ou u*x.
Maven n'est pas obligatoire mais il facilite la gestion des librairies et de leurs dépendances.

Passons à la pratique.

Etape 1: Création du projet maven

Créons un projet maven à l'aide de la commande Dos comme suit :

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

Cette commande est à lancer depuis le répertoire workspace d'Eclipse.

La console Dos, en mode interactif, demande à renseigner les informations suivantes:

 'groupId': mytest

 'artifactId':validtest

 'version': 1.0

'package': fr.validtest

Puis confirmez la création du projet.
Enfin, lancez, dans le répertoire nommé 'validtest' qui vient d'être créé, la commande dos

mvn eclipse:eclipse

Ceci prépare le projet pour l'éditeur Eclipse.

Il vous reste à lancer Eclipse puis à importer le projet validtest.

NOTE.
Une autre façon de construire le projet rapidement sans passer par la console dos sera
expliquée prochainement.
Cette seconde façon passe par l'utilisation de l'éditeur Eclipse STS (Springsource Tool Suite).

Etape 2: Configuration de l'environnement

Déclarez les jars (dépendances) suivant dans le pom de votre projet:

               <dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>4.2.0.Final</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>

On obtient donc les jars :
les_jars_validJsr303.PNG

Voici une petite explication sur ces dépendances :

Le jar validation-api v1.0.0.GA se trouve dans le répertoire javax de votre repository local ou distant. Ce jar contient les specs de la jsr-303.

Le jar hibernate-validator v4.2.0.Final de jBoss Hibernate est l'implémentation de référence de la jsr-303.

Les jars restants sont des dépendances transitives (résolues par maven) réclamées à l'exécution (exemple, slf4j).

Notez que le projet contient deux classes App.java et AppTest.java que nous allons supprimer du projet car elles sont inutiles.

Etape 3: Ecrire une classe annotée avec validation


package fr.validtest;

import java.io.Serializable;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Etablissement implements Serializable {

	private static final long serialVersionUID = 1L;

	@NotNull @Size(min=8 , max=12)
	private String numSiret;
	
       //............. getters setters omis

}

A noter la présence des annotations @NotNull et @Size sur les attributs de cette classe POJO.
Ces annotations sont issues de l'api bean validation javax.validation du jar validation-api.
Nous avons contraint la propriété numSiret à être non null et à avoir entre 8 et 12 caractères.

Etape 4: Une classe de test JUnit


package fr.validtest;

import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestValidateEtab {
        Logger logger=Logger.getLogger
                          (this.getClass());

	static Validator validator;

	@BeforeClass
	public static void beforeAllTests() {
		validator = Validation.
                                     buildDefaultValidatorFactory().
                                     getValidator();
	}
	@Test public void testNullEtab(){
                // numSiret est null!!
		Etablissement etab=new Etablissement();
		Set<ConstraintViolation<Etablissement>> 
                              violations= validator.validate(etab);
		Assert.assertTrue(violations.size()==1);
	}
	@Test public void testInvalidEtab(){
                //numSiret contient 4 caractères. C insuffisant!!
		Etablissement etab=
                             new Etablissement("1234");
		Set<ConstraintViolation<Etablissement>> 
                              violations= validator.validate(etab);

		for( ConstraintViolation<Etablissement> v:violations){
			logger.warn("v="+v);
		}
		
		Assert.assertEquals(1, violations.size());
	}
	@Test public void testNotValidEtab(){
                //numSiret  contient plus de 12 caractères!!
		Etablissement etab=
                   new Etablissement("1234567891011121314");
		Set<ConstraintViolation<Etablissement>> 
                         violations= validator.validate(etab);
		Assert.assertEquals(1, violations.size());
	}
	@Test public void testValidEtab(){
		Etablissement etab=
                        new Etablissement("12345678");
		Set<ConstraintViolation<Etablissement>> 
                          violations= validator.validate(etab);
		Assert.assertTrue(violations.size()==0);
	}
	@AfterClass public static void afterAllTests(){
		validator=null;
	} 

Vous pouvez constater que seule la méthode 'testValidEtab()' présente un numéro Siret conforme.
Les autres cas produisent des violations ce qui explique que la liste (Set) des contraintes, nommée violations, n'est pas vide.
En particulier, la méthode 'testInvalidEtab' affiche avec l'instruction logger.warn la liste des violations pour ce cas.
L'appel à la méthode validate du validator construit cette liste en fonction des annotations dans la classe Etablissement.
Dans cette classe de test JUnit, vous pouvez voir que seules des interfaces de l'api bean validation (jsr303) sont importés.
L'implémentation est masquée par l'appel de la méthode statique buildDefaultValidatorFactory().getValidator().

Etape 5: Ajouter log4j au pom du projet

Pour cela, ajoutez la dépendance de log4j :

                <!-- ajout log4j -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.12</version>
		</dependency>

N'oubliez pas de générer à nouveau le classpath d'Eclipse via la commande maven

 mvn eclipse:clean eclipse:eclipse

Puis de rafraîchir le projet dans Eclipse.

Aussi, ajoutez un log4j.xml dans le répertoire 'src/main/resources'.
Pensez à créer ce répertoire s'il n'est pas déjà créé et veuillez à ce qu'il soit reconnu comme 'source folder' par Eclipse.

Etape 6: Lancer les tests

Lancez la commande :

mvn test

Sur la console (ou dans un fichier log selon la configuration du log4j.xml) vous verrez les messages log avec les violations.

Une dernière précision concerne les messages de violations.
On peut écrire les messages de violations des contraintes de validation de deux manières :

 - en complétant les annotations ou 
 - en ajoutant un fichier nommé ValidationMessages.properties dans src/main/resources.

Pour le premier cas, reprenons les annotations de l'attribut numSiret comme suit :

   @NotNull (message = "ne doit pas être null!!") 
   @Size(min=8, max=12, message="la chaîne numSiret doit être entre 8 et 12 caractères!")
   private String numSiret;
	

Vous pouvez relever le messages du style :
WARN (TestValidateEtab.java:32) v=ConstraintViolationImpl{interpolatedMessage='la chaîne numSiret doit être entre 8 et 12 caractères!',

 propertyPath=numSiret, rootBeanClass=class fr.validtest.Etablissement, 
 messageTemplate='la chaîne numSiret doit être entre 8 et 12 caractères!'}

Enfin, jetez un œil sur le dossier 'target/surefire-reports' pour voir les rapports de tests générés.

Voilà, c'est simple la validation avec la JSR-303 !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.