You are on page 1of 605

Crez votre application web avec Java EE

Par Correcteur et Coyote

www.siteduzero.com

Licence Creative Commons 6 2.0 Dernire mise jour le 8/01/2013

2/606

Sommaire
Sommaire ........................................................................................................................................... 2 Lire aussi ............................................................................................................................................ 7 Crez votre application web avec Java EE ........................................................................................ 9
Comment lire ce cours ? ................................................................................................................................................... 9

Partie 1 : Les bases du Java EE ....................................................................................................... 10


Introduction au Java EE .................................................................................................................................................. 10
Pour commencer ....................................................................................................................................................................................................... 10 Comment lire ce cours ? ........................................................................................................................................................................................... 10 Prrequis ................................................................................................................................................................................................................... 10 Qu'est-ce que Java EE ? ........................................................................................................................................................................................... 10 Java EE n'est pas Java ............................................................................................................................................................................................. 10 Java EE n'est pas Javascript .................................................................................................................................................................................... 10 Internet n'est pas le web ! .......................................................................................................................................................................................... 11 Comment a marche ................................................................................................................................................................................................. 11 Les langages du web ................................................................................................................................................................................................ 12

Le Java EE mis nu ! ..................................................................................................................................................... 12


Principes de fonctionnement ..................................................................................................................................................................................... 13 Le modle MVC : en thorie ...................................................................................................................................................................................... 14 Le modle MVC : en pratique .................................................................................................................................................................................... 15

Outils et environnement de dveloppement ................................................................................................................... 17


L'IDE Eclipse ............................................................................................................................................................................................................. Prsentation .............................................................................................................................................................................................................. Tlchargement et installation .................................................................................................................................................................................. Configuration ............................................................................................................................................................................................................. Le serveur Tomcat ..................................................................................................................................................................................................... Prsentation .............................................................................................................................................................................................................. Installation ................................................................................................................................................................................................................. Cration du projet web avec Eclipse ......................................................................................................................................................................... Structure d'une application Java EE ......................................................................................................................................................................... Structure standard ..................................................................................................................................................................................................... Votre premire page web .......................................................................................................................................................................................... 17 17 17 18 19 19 19 22 32 32 33

Partie 2 : Premiers pas avec Java EE ............................................................................................... 37


La servlet ......................................................................................................................................................................... 38
Derrire les rideaux ................................................................................................................................................................................................... Retour sur HTTP ....................................................................................................................................................................................................... Pendant ce temps-l, sur le serveur ...................................................................................................................................................................... Cration ..................................................................................................................................................................................................................... Mise en place ............................................................................................................................................................................................................ Dfinition de la servlet ............................................................................................................................................................................................... Mapping de la servlet ................................................................................................................................................................................................ Mise en service ......................................................................................................................................................................................................... Do you GET it? .................................................................................................................................................................................................... Cycle de vie d'une servlet ......................................................................................................................................................................................... Envoyer des donnes au client ................................................................................................................................................................................. 38 38 39 39 42 43 44 45 45 47 48

Servlet avec vue .......................................................................................................................................................... 51


Introduction aux JSP ................................................................................................................................................................................................. 51 Nature d'une JSP ...................................................................................................................................................................................................... 51 Mise en place d'une JSP ........................................................................................................................................................................................... 52 Cration de la vue ..................................................................................................................................................................................................... 52 Cycle de vie d'une JSP ............................................................................................................................................................................................. 54 Mise en relation avec notre servlet ............................................................................................................................................................................ 56

Transmission de donnes ............................................................................................................................................... 58


Donnes issues du serveur : les attributs ................................................................................................................................................................. 59 Donnes issues du client : les paramtres ............................................................................................................................................................... 60

Le JavaBean ................................................................................................................................................................... 63
Objectifs .................................................................................................................................................................................................................... Pourquoi le JavaBean ? ............................................................................................................................................................................................ Un JavaBean n'est pas un EJB ................................................................................................................................................................................. Structure .................................................................................................................................................................................................................... Mise en place ............................................................................................................................................................................................................ Cration de notre bean d'exemple ............................................................................................................................................................................ Configuration du projet sous Eclipse ......................................................................................................................................................................... Mise en service dans notre application ..................................................................................................................................................................... Les balises ................................................................................................................................................................................................................ Les directives ............................................................................................................................................................................................................ La porte des objets .................................................................................................................................................................................................. Les actions standard ................................................................................................................................................................................................. L'action standard useBean ........................................................................................................................................................................................ L'action standard getProperty ................................................................................................................................................................................... L'action standard setProperty .................................................................................................................................................................................... L'action standard forward .......................................................................................................................................................................................... 64 64 64 64 65 65 68 70 72 73 76 78 78 79 79 80

La technologie JSP (1/2) ................................................................................................................................................. 72

La technologie JSP (2/2) ................................................................................................................................................. 82

www.siteduzero.com

Sommaire

3/606

Expression Language ................................................................................................................................................................................................ 82 Prsentation .............................................................................................................................................................................................................. 82 La ralisation de tests ............................................................................................................................................................................................... 82 La manipulation d'objets ............................................................................................................................................................................................ 84 Dsactiver l'valuation des expressions EL .............................................................................................................................................................. 89 Les objets implicites .................................................................................................................................................................................................. 91 Les objets de la technologie JSP .............................................................................................................................................................................. 91 Les objets de la technologie EL ................................................................................................................................................................................ 94

Des problmes de vue ? ............................................................................................................................................... 100


Nettoyons notre exemple ........................................................................................................................................................................................ 100 Compltons notre exemple... .................................................................................................................................................................................. 103 Le point sur ce qu'il nous manque ........................................................................................................................................................................... 107 Documentation ........................................................................................................................................................................................................ 107 Liens utiles .............................................................................................................................................................................................................. 107

TP Fil rouge - tape 1 ................................................................................................................................................... 109


Objectifs .................................................................................................................................................................................................................. 109 Contexte .................................................................................................................................................................................................................. 109 Fonctionnalits ........................................................................................................................................................................................................ 109 Contraintes .............................................................................................................................................................................................................. 109 Conseils ................................................................................................................................................................................................................... 112 propos des formulaires ......................................................................................................................................................................................... 112 Le modle ................................................................................................................................................................................................................ 113 Les contrleurs ........................................................................................................................................................................................................ 113 Les vues .................................................................................................................................................................................................................. 113 Cration du projet .................................................................................................................................................................................................... 114 Illustration du comportement attendu ...................................................................................................................................................................... 114 Exemples de rendu du comportement attendu ........................................................................................................................................................ 115 Correction ................................................................................................................................................................................................................ 118 Le code des beans .................................................................................................................................................................................................. 118 Le code des servlets ............................................................................................................................................................................................... 120 Le code des JSP ..................................................................................................................................................................................................... 123

Partie 3 : Une bonne vue grce la JSTL ...................................................................................... 125


Objectifs et configuration ............................................................................................................................................... 125
C'est sa raison d'tre ........................................................................................................................................................................................... Lisibilit du code produit .......................................................................................................................................................................................... Moins de code crire ............................................................................................................................................................................................ Vous avez dit MVC ? ............................................................................................................................................................................................... retenir ................................................................................................................................................................................................................... Plusieurs versions ................................................................................................................................................................................................... Configuration ........................................................................................................................................................................................................... Configuration de la JSTL ......................................................................................................................................................................................... Les variables et expressions ................................................................................................................................................................................... Affichage d'une expression ..................................................................................................................................................................................... Gestion d'une variable ............................................................................................................................................................................................. Les conditions ......................................................................................................................................................................................................... Une condition simple ............................................................................................................................................................................................... Des conditions multiples ......................................................................................................................................................................................... Les boucles ............................................................................................................................................................................................................. Boucle "classique" ................................................................................................................................................................................................... Itration sur une collection ...................................................................................................................................................................................... Itration sur une chane de caractres .................................................................................................................................................................... Ce que la JSTL ne permet pas (encore) de faire .................................................................................................................................................... Les liens .................................................................................................................................................................................................................. Liens ........................................................................................................................................................................................................................ Redirection .............................................................................................................................................................................................................. Imports .................................................................................................................................................................................................................... Les autres bibliothques de la JSTL ....................................................................................................................................................................... 125 125 126 126 127 127 127 127 131 131 132 135 135 136 136 136 138 141 141 141 142 144 145 146

La bibliothque Core ..................................................................................................................................................... 130

JSTL core : exercice d'application ................................................................................................................................. 147


Les bases de l'exercice ........................................................................................................................................................................................... 148 Correction ................................................................................................................................................................................................................ 150

La bibliothque xml ....................................................................................................................................................... 154


La syntaxe XPath .................................................................................................................................................................................................... Le langage XPath .................................................................................................................................................................................................... Les actions de base ................................................................................................................................................................................................ Rcuprer et analyser un document ....................................................................................................................................................................... Afficher une expression ........................................................................................................................................................................................... Crer une variable ................................................................................................................................................................................................... Les conditions ......................................................................................................................................................................................................... Les conditions ......................................................................................................................................................................................................... Les boucles ............................................................................................................................................................................................................. Les boucles ............................................................................................................................................................................................................. Les transformations ................................................................................................................................................................................................. Transformations ...................................................................................................................................................................................................... 154 154 155 155 158 159 159 159 160 160 161 161

JSTL xml : exercice d'application .................................................................................................................................. 164


Les bases de l'exercice ........................................................................................................................................................................................... 165 Correction ................................................................................................................................................................................................................ 166

Faisons le point ! ........................................................................................................................................................... 168


Reprenons notre exemple ....................................................................................................................................................................................... 168 Quelques conseils ................................................................................................................................................................................................... 170 Utilisation de constantes ......................................................................................................................................................................................... 170

www.siteduzero.com

Sommaire

4/606
172 173 178 178 180 180 180 180 181 182 182 183 183 186

Inclure automatiquement la JSTL Core toutes vos JSP ....................................................................................................................................... Formater proprement et automatiquement votre code avec Eclipse ...................................................................................................................... Documentation ........................................................................................................................................................................................................ Liens utiles .............................................................................................................................................................................................................. Objectifs .................................................................................................................................................................................................................. Utilisation de la JSTL ............................................................................................................................................................................................... Application des bonnes pratiques ........................................................................................................................................................................... Exemples de rendus ............................................................................................................................................................................................... Conseils .................................................................................................................................................................................................................. Utilisation de la JSTL ............................................................................................................................................................................................... Application des bonnes pratiques ........................................................................................................................................................................... Correction ................................................................................................................................................................................................................ Code des servlets ................................................................................................................................................................................................... Code des JSP .........................................................................................................................................................................................................

TP Fil rouge - tape 2 ................................................................................................................................................... 179

Partie 4 : Une application interactive ! ............................................................................................. 191


Formulaires : le b.a.-ba ................................................................................................................................................. 192
Mise en place .......................................................................................................................................................................................................... JSP & CSS .............................................................................................................................................................................................................. La servlet ................................................................................................................................................................................................................. L'envoi des donnes ............................................................................................................................................................................................... Contrle : ct servlet ............................................................................................................................................................................................. Affichage : ct JSP ................................................................................................................................................................................................ 192 193 195 196 197 201

Formulaires : la mode MVC ....................................................................................................................................... 208


Analyse de notre conception ................................................................................................................................................................................... 209 Cration du modle ................................................................................................................................................................................................. 209 Reprise de la servlet ............................................................................................................................................................................................... 213 Reprise de la JSP .................................................................................................................................................................................................... 215

TP Fil rouge - tape 3 ................................................................................................................................................... 217


Objectifs .................................................................................................................................................................................................................. Fonctionnalits ........................................................................................................................................................................................................ Exemples de rendus ............................................................................................................................................................................................... Conseils .................................................................................................................................................................................................................. Correction ................................................................................................................................................................................................................ Code des objets mtier ........................................................................................................................................................................................... Code des servlets ................................................................................................................................................................................................... Code des JSP ......................................................................................................................................................................................................... Le formulaire ........................................................................................................................................................................................................... Le principe de la session ......................................................................................................................................................................................... Le modle ............................................................................................................................................................................................................... La servlet ................................................................................................................................................................................................................. Les vrifications ...................................................................................................................................................................................................... Test du formulaire de connexion ............................................................................................................................................................................. Test de la destruction de session ............................................................................................................................................................................ Derrire les rideaux ................................................................................................................................................................................................. La thorie : principe de fonctionnement .................................................................................................................................................................. La pratique : scrutons nos requtes et rponses .................................................................................................................................................... En rsum ............................................................................................................................................................................................................... Restreindre l'accs une page ............................................................................................................................................................................... Les pages d'exemple .............................................................................................................................................................................................. La servlet de contrle .............................................................................................................................................................................................. Test du systme ...................................................................................................................................................................................................... Le problme ............................................................................................................................................................................................................ Le principe du filtre .................................................................................................................................................................................................. Gnralits .............................................................................................................................................................................................................. Fonctionnement ...................................................................................................................................................................................................... Cycle de vie ............................................................................................................................................................................................................. Restreindre l'accs un ensemble de pages ......................................................................................................................................................... Restreindre un rpertoire ........................................................................................................................................................................................ Restreindre l'application entire .............................................................................................................................................................................. Dsactiver le filtre .................................................................................................................................................................................................... Modifier le mode de dclenchement d'un filtre ........................................................................................................................................................ Retour sur l'encodage UTF-8 .................................................................................................................................................................................. 217 217 217 219 220 220 226 228 233 233 236 238 240 240 242 247 247 248 261 263 263 264 265 266 266 266 267 268 269 269 273 280 280 281

La session : connectez vos clients ................................................................................................................................ 232

Le filtre : crez un espace membre ............................................................................................................................... 262

Le cookie : le navigateur vous ouvre ses portes ........................................................................................................... 283


Le principe du cookie .............................................................................................................................................................................................. 284 Ct HTTP .............................................................................................................................................................................................................. 284 Ct Java EE .......................................................................................................................................................................................................... 284 Souvenez-vous de vos clients ! ............................................................................................................................................................................... 284 Reprise de la servlet ............................................................................................................................................................................................... 285 Reprise de la JSP .................................................................................................................................................................................................... 289 Vrifications ............................................................................................................................................................................................................. 291 propos de la scurit ........................................................................................................................................................................................... 294

TP Fil rouge - tape 4 ................................................................................................................................................... 294


Objectifs .................................................................................................................................................................................................................. Fonctionnalits ........................................................................................................................................................................................................ Exemples de rendus ............................................................................................................................................................................................... Conseils .................................................................................................................................................................................................................. Correction ................................................................................................................................................................................................................ Le code des vues .................................................................................................................................................................................................... 295 295 295 298 300 300

www.siteduzero.com

Sommaire

5/606

Le code des servlets ............................................................................................................................................................................................... 306 Le code des objets mtiers ..................................................................................................................................................................................... 312

Formulaires : l'envoi de fichiers ..................................................................................................................................... 316


Cration du formulaire ............................................................................................................................................................................................. 317 Rcupration des donnes ..................................................................................................................................................................................... 317 Mise en place .......................................................................................................................................................................................................... 317 Traitement des donnes .......................................................................................................................................................................................... 318 La diffrence entre la thorie et la pratique ............................................................................................................................................................. 325 Enregistrement du fichier ........................................................................................................................................................................................ 327 Dfinition du chemin physique ................................................................................................................................................................................ 327 criture du fichier sur le disque ............................................................................................................................................................................... 328 Test du formulaire d'upload ..................................................................................................................................................................................... 331 Problmes et limites ................................................................................................................................................................................................ 331 Comment grer les fichiers de mmes noms ? ....................................................................................................................................................... 331 Comment viter les doublons ? ............................................................................................................................................................................... 331 O stocker les fichiers reus ? ................................................................................................................................................................................ 332 Rendre le tout entirement automatique ................................................................................................................................................................. 332 Intgration dans MVC .............................................................................................................................................................................................. 333 Cration du bean reprsentant un fichier ................................................................................................................................................................ 333 Cration de l'objet mtier en charge du traitement du formulaire ........................................................................................................................... 333 Reprise de la servlet ............................................................................................................................................................................................... 338 Adaptation de la page JSP aux nouvelles informations transmises ........................................................................................................................ 339 Comportement de la solution finale ......................................................................................................................................................................... 340

Le tlchargement de fichiers ....................................................................................................................................... 341


Une servlet ddie .................................................................................................................................................................................................. Cration de la servlet .............................................................................................................................................................................................. Paramtrage de la servlet ....................................................................................................................................................................................... Analyse du fichier .................................................................................................................................................................................................... Gnration de la rponse HTTP ............................................................................................................................................................................. Lecture et envoi du fichier ....................................................................................................................................................................................... Vrification de la solution ........................................................................................................................................................................................ Une solution plus simple ......................................................................................................................................................................................... L'tat d'un tlchargement ...................................................................................................................................................................................... Raliser des statistiques ......................................................................................................................................................................................... Objectifs .................................................................................................................................................................................................................. Fonctionnalits ........................................................................................................................................................................................................ Exemples de rendus ............................................................................................................................................................................................... Conseils .................................................................................................................................................................................................................. Envoi du fichier ........................................................................................................................................................................................................ Validation et enregistrement du fichier .................................................................................................................................................................... Affichage d'un lien vers l'image ............................................................................................................................................................................... R-affichage de l'image ........................................................................................................................................................................................... Correction ................................................................................................................................................................................................................ Le code de l'objet mtier ......................................................................................................................................................................................... Le code des servlets ............................................................................................................................................................................................... Le code des JSP ..................................................................................................................................................................................................... 342 342 342 344 345 346 347 348 348 349 350 350 350 353 353 353 354 354 354 355 360 363

TP Fil rouge - tape 5 ................................................................................................................................................... 350

Partie 5 : Les bases de donnes avec Java EE .............................................................................. 369


Introduction MySQL et JDBC ..................................................................................................................................... 370
Prsentation des bases de donnes ....................................................................................................................................................................... Structure .................................................................................................................................................................................................................. SGBD ...................................................................................................................................................................................................................... SQL ......................................................................................................................................................................................................................... Prparation de la base avec MySQL ....................................................................................................................................................................... Installation ............................................................................................................................................................................................................... Cration d'une base ................................................................................................................................................................................................ Cration d'un utilisateur ........................................................................................................................................................................................... Cration d'une table ................................................................................................................................................................................................ Insertion de donnes d'exemple ............................................................................................................................................................................. Mise en place de JDBC dans le projet .................................................................................................................................................................... JDBC ....................................................................................................................................................................................................................... Mise en place .......................................................................................................................................................................................................... Cration d'un bac sable ........................................................................................................................................................................................ Cration de l'objet Java ........................................................................................................................................................................................... Cration de la servlet .............................................................................................................................................................................................. Cration de la page JSP ......................................................................................................................................................................................... En rsum ............................................................................................................................................................................................................... 370 370 371 372 372 372 374 374 375 375 376 376 377 377 377 378 379 379

Communiquez avec votre BDD ..................................................................................................................................... 379


Chargement du driver .............................................................................................................................................................................................. 380 Connexion la base, cration et excution d'une requte ..................................................................................................................................... 380 Connexion la base de donnes ............................................................................................................................................................................ 380 Cration d'une requte ............................................................................................................................................................................................ 382 Excution de la requte ........................................................................................................................................................................................... 383 Accs aux rsultats de la requte ........................................................................................................................................................................... 383 Libration des ressources ....................................................................................................................................................................................... 385 Mise en pratique ...................................................................................................................................................................................................... 386 Afficher le contenu de la table Utilisateur ................................................................................................................................................................ 386 Insrer des donnes dans la table Utilisateur ......................................................................................................................................................... 388 Les limites du systme ............................................................................................................................................................................................ 390 Insrer des donnes saisies par l'utilisateur ........................................................................................................................................................... 390 Le problme des valeurs nulles .............................................................................................................................................................................. 390 Le cas idal ............................................................................................................................................................................................................. 392

www.siteduzero.com

Sommaire

6/606
392 393 393 395 397 400 401 401 401 402 402 403 403 403 404 405 407 407 410 411 417 417 419 421 424 424 424 425 426

Les injections SQL .................................................................................................................................................................................................. Les requtes prpares .......................................................................................................................................................................................... Pourquoi prparer ses requtes ? ........................................................................................................................................................................... Comment prparer ses requtes ? ......................................................................................................................................................................... Mise en pratique ...................................................................................................................................................................................................... En rsum ............................................................................................................................................................................................................... Objectifs .................................................................................................................................................................................................................. Inconvnients de notre solution .............................................................................................................................................................................. Isoler le stockage des donnes ............................................................................................................................................................................... Principe ................................................................................................................................................................................................................... Constitution ............................................................................................................................................................................................................. Intgration ............................................................................................................................................................................................................... Cration ................................................................................................................................................................................................................... Modification de la table Utilisateur .......................................................................................................................................................................... Reprise du bean Utilisateur ..................................................................................................................................................................................... Cration des exceptions du DAO ............................................................................................................................................................................ Cration d'un fichier de configuration ...................................................................................................................................................................... Cration d'une Factory ............................................................................................................................................................................................ Cration de l'interface du DAO Utilisateur ............................................................................................................................................................... Cration de l'implmentation du DAO ..................................................................................................................................................................... Intgration ............................................................................................................................................................................................................... Chargement de la DAOFactory ............................................................................................................................................................................... Utilisation depuis la servlet ...................................................................................................................................................................................... Reprise de l'objet mtier .......................................................................................................................................................................................... Cration d'une exception ddie aux erreurs de validation .................................................................................................................................... Vrifications ............................................................................................................................................................................................................. Le code final ............................................................................................................................................................................................................ Le scnario de tests ................................................................................................................................................................................................ En rsum ...............................................................................................................................................................................................................

Le modle DAO ............................................................................................................................................................. 400

TP Fil rouge - tape 6 ................................................................................................................................................... 428


Objectifs .................................................................................................................................................................................................................. 428 Fonctionnalits ........................................................................................................................................................................................................ 428 Conseils .................................................................................................................................................................................................................. 428 Cration de la base de donnes ............................................................................................................................................................................. 428 Mise en place de JDBC ........................................................................................................................................................................................... 430 Rutilisation de la structure DAO dveloppe dans le cadre du cours ................................................................................................................... 430 Cration des interfaces et implmentations du DAO .............................................................................................................................................. 430 Intgration dans le code existant ............................................................................................................................................................................ 431 Correction ................................................................................................................................................................................................................ 433 Code de la structure DAO ....................................................................................................................................................................................... 433 Code des interfaces DAO ........................................................................................................................................................................................ 435 Code des implmentations DAO ............................................................................................................................................................................. 436 Code des beans ...................................................................................................................................................................................................... 442 Code des objets mtier ........................................................................................................................................................................................... 444 Code des servlets ................................................................................................................................................................................................... 455 Code du filtre ........................................................................................................................................................................................................... 463 Code des JSP ......................................................................................................................................................................................................... 465

Grer un pool de connexions avec BoneCP ................................................................................................................. 470


Contexte .................................................................................................................................................................................................................. Une application multi-utilisateurs ............................................................................................................................................................................ Le cot d'une connexion la BDD .......................................................................................................................................................................... La structure actuelle de notre solution .................................................................................................................................................................... Principe ................................................................................................................................................................................................................... Rutilisation des connexions ................................................................................................................................................................................... Remplacement du DriverManager par une DataSource ......................................................................................................................................... Choix d'une implmentation .................................................................................................................................................................................... Mise en place .......................................................................................................................................................................................................... Ajout des jar au projet ............................................................................................................................................................................................. Prise en main de la bibliothque ............................................................................................................................................................................. Modification de la DAOFactory ................................................................................................................................................................................ Vrifications ............................................................................................................................................................................................................. Configuration fine du pool ....................................................................................................................................................................................... En rsum ............................................................................................................................................................................................................... 471 471 471 471 472 472 473 473 473 473 474 474 477 477 477

Partie 6 : Aller plus loin avec JPA et JSF ......................................................................................... 479


Les annotations ............................................................................................................................................................. 479
Prsentation ............................................................................................................................................................................................................ 479 crire des mta-donnes ........................................................................................................................................................................................ 479 Pallier certaines carences ....................................................................................................................................................................................... 479 Simplifier le dveloppement .................................................................................................................................................................................... 480 Principe ................................................................................................................................................................................................................... 480 Syntaxe sans paramtres ........................................................................................................................................................................................ 480 Syntaxe avec paramtres ........................................................................................................................................................................................ 480 Avec l'API Servlet 3.0 .............................................................................................................................................................................................. 481 WebServlet .............................................................................................................................................................................................................. 481 WebFilter ................................................................................................................................................................................................................. 482 WebInitParam .......................................................................................................................................................................................................... 483 WebListener ............................................................................................................................................................................................................ 483 MultipartConfig ........................................................................................................................................................................................................ 484 Et le web.xml dans tout a ? ................................................................................................................................................................................... 485 En rsum ............................................................................................................................................................................................................... 486

La persistance des donnes avec JPA ......................................................................................................................... 486

www.siteduzero.com

Sommaire

7/606
487 488 488 489 489 489 493 495 496 499 501 502 502 502 504 504 506 507 507 507 507 507 509 509 510 513 515 524

Gnralits .............................................................................................................................................................................................................. Principe ................................................................................................................................................................................................................... Des EJB dans un conteneur ................................................................................................................................................................................... Un gestionnaire dentits ......................................................................................................................................................................................... Mise en place .......................................................................................................................................................................................................... Le serveur dapplications GlassFish ....................................................................................................................................................................... Cration du projet .................................................................................................................................................................................................... Cration dune entit Utilisateur .............................................................................................................................................................................. Cration dun EJB Session ..................................................................................................................................................................................... Modification de la servlet ......................................................................................................................................................................................... Modification de lobjet mtier ................................................................................................................................................................................... Tests et vrifications ................................................................................................................................................................................................ Vrification du bon fonctionnement dune inscription .............................................................................................................................................. Analyse des requtes SQL gnres lors dune inscription .................................................................................................................................... Aller plus loin ........................................................................................................................................................................................................... ORM, ou ne pas ORM ? .......................................................................................................................................................................................... En rsum ............................................................................................................................................................................................................... Objectifs .................................................................................................................................................................................................................. Fonctionnalits ........................................................................................................................................................................................................ Conseils .................................................................................................................................................................................................................. Environnement de dveloppement ......................................................................................................................................................................... Reprise du code existant ......................................................................................................................................................................................... Correction ................................................................................................................................................................................................................ Le code de configuration ......................................................................................................................................................................................... Le code des EJB Entity ........................................................................................................................................................................................... Le code des EJB Session ....................................................................................................................................................................................... Le code des servlets ............................................................................................................................................................................................... Le code du filtre .......................................................................................................................................................................................................

TP Fil rouge - tape 7 ................................................................................................................................................... 506

Introduction aux frameworks MVC ................................................................................................................................ 525


Gnralits .............................................................................................................................................................................................................. 526 Rappel concernant MVC ......................................................................................................................................................................................... 526 Qu'est-ce qu'un framework MVC ? .......................................................................................................................................................................... 526 quels besoins rpond-il ? ..................................................................................................................................................................................... 526 Quand utiliser un framework MVC, et quand s'en passer ? .................................................................................................................................... 526 Framework MVC bas sur les requtes .................................................................................................................................................................. 527 Dfinition ................................................................................................................................................................................................................. 527 Principe ................................................................................................................................................................................................................... 527 Solutions existantes ................................................................................................................................................................................................ 527 Framework MVC bas sur les composants ............................................................................................................................................................. 527 Dfinition ................................................................................................................................................................................................................. 527 Principe ................................................................................................................................................................................................................... 528 Solutions existantes ................................................................................................................................................................................................ 528 Les "dissidents" ....................................................................................................................................................................................................... 528 En rsum ............................................................................................................................................................................................................... 529

Premiers pas avec JSF ................................................................................................................................................. 529


Qu'est-ce que JSF ? ................................................................................................................................................................................................ Prsentation ............................................................................................................................................................................................................ Principe ................................................................................................................................................................................................................... Historique ................................................................................................................................................................................................................ Structure d'une application JSF .............................................................................................................................................................................. Facelets et composants .......................................................................................................................................................................................... La page JSP mise au placard ? .............................................................................................................................................................................. Structure et syntaxe ................................................................................................................................................................................................ Comment a marche ? ............................................................................................................................................................................................ Crer un template de Facelet par dfaut avec Eclipse ........................................................................................................................................... Premier projet .......................................................................................................................................................................................................... De quoi avons-nous besoin ? .................................................................................................................................................................................. Cration du projet .................................................................................................................................................................................................... Cration du bean ..................................................................................................................................................................................................... Cration des facelets .............................................................................................................................................................................................. Configuration de l'application .................................................................................................................................................................................. Tests & observations ............................................................................................................................................................................................... Les ressources ........................................................................................................................................................................................................ JSF, ou ne pas JSF ? .............................................................................................................................................................................................. En rsum ............................................................................................................................................................................................................... 530 530 530 530 532 533 533 533 536 537 540 540 541 541 542 544 545 548 550 550

La gestion d'un formulaire avec JSF ............................................................................................................................. 552


Une inscription classique ........................................................................................................................................................................................ 552 Prparation du projet ............................................................................................................................................................................................... 552 Cration de la couche d'accs aux donnes ........................................................................................................................................................... 552 Cration du backing bean ....................................................................................................................................................................................... 553 Cration de la vue ................................................................................................................................................................................................... 555 Tests & observations ............................................................................................................................................................................................... 558 Amliorations des messages affichs lors de la validation ..................................................................................................................................... 560 Une inscription ajaxise .......................................................................................................................................................................................... 563 Prsentation ............................................................................................................................................................................................................ 563 L'AJAX avec JSF ..................................................................................................................................................................................................... 563 L'importance de la porte d'un objet ....................................................................................................................................................................... 566 Une inscription contrle ......................................................................................................................................................................................... 566 Dporter la validation de la vue vers l'entit ............................................................................................................................................................ 566 Affiner les contrles effectus ................................................................................................................................................................................. 568 Ajouter des contrles "mtier" ................................................................................................................................................................................. 569 En rsum ............................................................................................................................................................................................................... 576

www.siteduzero.com

Lire aussi

8/606
577 578 578 578 579 580 580 582 583 585 585 586 587

L'envoi de fichiers avec JSF .......................................................................................................................................... 576


Le problme ............................................................................................................................................................................................................ Les bibliothques de composants ........................................................................................................................................................................... L'envoi de fichier avec Tomahawk ........................................................................................................................................................................... Prparation du projet ............................................................................................................................................................................................... Cration de la Facelet ............................................................................................................................................................................................. Cration du JavaBean ............................................................................................................................................................................................. Cration du backing-bean ....................................................................................................................................................................................... Tests et vrifications ................................................................................................................................................................................................ Limitation de la taille maximale autorise ............................................................................................................................................................... Et plus si affinits... ................................................................................................................................................................................................. TP Fil rouge - tape 8 ............................................................................................................................................................................................. Ce que l'avenir vous rserve ................................................................................................................................................................................... En rsum ...............................................................................................................................................................................................................

Partie 7 : Annexes ........................................................................................................................... 588


Dbugger un projet ....................................................................................................................................................... 588
Les fichiers de logs ................................................................................................................................................................................................. Quels fichiers ? ....................................................................................................................................................................................................... Comment les utiliser ? ............................................................................................................................................................................................. Le mode debug d'Eclipse ........................................................................................................................................................................................ Principe ................................................................................................................................................................................................................... Interface .................................................................................................................................................................................................................. Exemple pratique .................................................................................................................................................................................................... Conseils au sujet de la thread-safety ...................................................................................................................................................................... Quelques outils de tests .......................................................................................................................................................................................... Les tests unitaires ................................................................................................................................................................................................... Les tests de charge ................................................................................................................................................................................................. 588 588 590 591 591 591 592 596 597 597 599

Empaquetage et dploiement d'un projet ..................................................................................................................... 600


Mise en bote du projet ............................................................................................................................................................................................ 601 JAR, WAR ou EAR ? ............................................................................................................................................................................................... 601 Mise en pratique ...................................................................................................................................................................................................... 602 Dploiement du projet ............................................................................................................................................................................................. 603 Contexte .................................................................................................................................................................................................................. 603 Mise en pratique ...................................................................................................................................................................................................... 604 Avancement du cours .............................................................................................................................................................................................. 604 Et aprs ? ................................................................................................................................................................................................................ 604

www.siteduzero.com

Lire aussi

9/606

Crez votre application web avec Java EE

Par

Coyote

Mise jour : 08/01/2013 Difficult : Intermdiaire

Dure d'tude : 2 mois

21 962 visites depuis 7 jours, class 12/799 La cration d'applications web avec Java EE semble complique beaucoup de dbutants. Une norme nbuleuse de sigles en tout genre gravite autour de la plate-forme, un nombre consquent de technologies et d'approches diffrentes existent : servlet, JSP, Javabean, MVC, JDBC, JNDI, EJB, JPA, JMS, JSF, Struts, Spring, Tomcat, Glassfish, JBoss, WebSphere, WebLogic... La liste n'en finit pas, et pour un novice ne pas touffer sous une telle avalanche est bien souvent mission impossible ! Soyons honntes, ce tutoriel ne vous expliquera pas le fonctionnement et l'utilisation de toutes ces technologies. Car a aussi, c'est mission impossible ! Il faudrait autant de tutos... Non, ce cours a pour objectif de guider vos premiers pas dans l'univers Java EE : aprs quelques explications sur les concepts gnraux et les bonnes pratiques en vigueur, vous allez entrer dans le vif du sujet et dcouvrir comment crer un projet web, en y ajoutant de la complexit au fur et mesure que le cours avancera. la fin du cours, vous serez capables de crer une application web qui respecte les standards reconnus dans le domaine et vous disposerez des bases ncessaires pour utiliser la plupart des technologies se basant sur Java EE. Je profite de cette introduction pour tordre le coup une erreur trop courante : l'appellation JEE n'existe pas ! Les crateurs de Java EE ont mme ddi une page web cette fausse appellation.

Comment lire ce cours ?


Un contenu consquent est prvu, mais je ne vais volontairement pas tre exhaustif : les technologies abordes sont trs vastes, et l'objectif du cours est de vous apprendre crer une application. Si je vous rcrivais la documentation de la plate-forme Java EE en franais, a serait tout simplement imbuvable. Je vais ainsi fortement insister sur des points non documents et des pratiques que je juge importantes, et tre plus expditif sur certains points, pour lesquels je me contenterai de vous prsenter les bases et de vous renvoyer vers les documentations et sources officielles pour plus d'informations. Je vous invite donc ne pas vous limiter la seule lecture de ce cours, et parcourir chacun des liens que j'ai mis en place tout au long des chapitres. Enfin, avant d'attaquer sachez que ce cours ne part pas totalement de zro : il vous faut des bases en Java afin de ne pas vous sentir largus ds les premiers chapitres. Ainsi, si vous n'tes pas encore familier avec le langage, vous pouvez lire les parties 1 et 2 du tutoriel sur le Java du Site du Zro.

www.siteduzero.com

Crez votre application web avec Java EE

10/606

Partie 1 : Les bases du Java EE


Dans cette courte premire partie, nous allons poser le dcor : quelles sont les briques de base d'une application Java EE, comment elles interagissent, quels outils utiliser pour dvelopper un projet...

Introduction au Java EE
Avant de nous plonger dans l'univers Java EE, commenons par faire une mise au point sur ce que vous devez connatre avant d'attaquer ce cours, et penchons-nous un instant sur ce qu'est le web, et sur ce qu'il n'est pas. Simples rappels pour certains d'entre vous, dcouverte pour d'autres, nous allons ici expliquer ce qui se passe dans les coulisses lorsque l'on accde un site web depuis son navigateur. Nous aborderons enfin brivement les autres langages existants, et les raisons qui nous poussent choisir Java EE.

Pour commencer Comment lire ce cours ?


Un contenu consquent est prvu dans ce cours, mais je ne vais volontairement pas tre exhaustif : les technologies abordes sont trs vastes, et l'objectif est avant tout de vous apprendre crer une application. Si je vous rcrivais la documentation de la plate-forme Java EE en franais, a serait tout simplement imbuvable. Je vais ainsi fortement insister sur des points non documents et des pratiques que je juge importantes, et tre plus expditif sur certains points, pour lesquels je me contenterai de vous prsenter les bases et de vous renvoyer vers les documentations et sources officielles pour plus d'informations. Je vous invite donc ne pas vous limiter la seule lecture de ce cours, et parcourir chacun des liens que j'ai mis en place tout au long des chapitres ; plus vous ferez preuve de curiosit et d'assiduit, plus votre apprentissage sera efficace.

Prrequis
Avant d'attaquer, sachez que ce cours ne part pas totalement de zro : des notions en dveloppement Java sont ncessaires (lire les parties 1 et 2 du cours de Java) ; des notions en langages HTML et CSS sont prfrables, pour une meilleure comprhension des exemples (lire le cours de HTML5 / CSS3) ; des notions en langage SQL sont prfrables, pour une meilleure comprhension de la partie 5 du cours (lire le cours de MySQL).

Qu'est-ce que Java EE ?


Pour commencer, tordons le coup certaines confusions plutt tenaces chez les dbutants

Java EE n'est pas Java


Le terme Java fait bien videmment rfrence un langage, mais galement une plate-forme : son nom complet est Java SE pour Java Standard Edition , et tait anciennement raccourci J2SE . Celle-ci est constitue de nombreuses bibliothques, ou API : citons par exemple java.lang, java.io, java.math, java.util, etc. Bref, toutes ces bibliothques que vous devez dj connatre et qui contiennent un nombre consquent de classes et de mthodes prtes l'emploi pour effectuer toutes sortes de tches. Le terme Java EE signifie Java Enterprise Edition , et tait anciennement raccourci en J2EE . Il fait quant lui rfrence une extension de la plate-forme standard. Autrement dit, la plate-forme Java EE est construite sur le langage Java et la plate-forme Java SE, et elle y ajoute un grand nombre de bibliothques remplissant tout un tas de fonctionnalits que la plate-forme standard ne remplit pas d'origine. L'objectif majeur de Java EE est de faciliter le dveloppement d'applications web robustes et distribues, dployes et excutes sur un serveur d'applications. Inutile de rentrer plus loin dans les dtails, tout ceci tant bien entendu l'objet des chapitres venir. Si le cur vous en dit, vous pouvez consulter les spcifications de la plate-forme Java EE actuelle, finalises depuis dcembre 2009.

Java EE n'est pas Javascript


S'il est vrai que Java EE permet la cration d'applications web, il ne faut pas pour autant le confondre avec le langage Javascript, souvent raccourci en JS , qui est lui aussi massivement utilis dans les applications web. Ce sont l deux langages totalement

www.siteduzero.com

Partie 1 : Les bases du Java EE


diffrents, qui n'ont comme ressemblance que leur nom ! En d'autres termes, Java est au Javascript ce que le bal est la balustrade

11/606

Ne vous leurrez donc pas, et lorsque vous entendrez parler de scripts Java , rappelez-vous bien que cela dsigne simplement du code Java, et surtout pas du code Javascript.

Internet n'est pas le web !


Avant tout, il ne faut pas confondre l'internet et le web : l'internet est le rseau, le support physique de l'information. Pour faire simple, c'est un ensemble de machines, de cbles et d'lments rseau en tout genre parpills sur la surface du globe ; le web constitue une partie seulement du contenu accessible sur l'internet. V ous connaissez et utilisez d'autres contenus, comme le courrier lectronique ou encore la messagerie instantane. Un site web est un ensemble constitu de pages web (elles-mmes faites de fichiers HTML, CSS, Javascript, etc.). Lorsqu'on dveloppe puis publie un site web, on met en ralit en ligne du contenu sur internet. On distingue deux types de sites : les sites internet statiques : ce sont des sites dont le contenu est fixe , il n'est modifiable que par le propritaire du site. Ils sont raliss l'aide des technologies HTML, CSS et Javascript uniquement. les sites internet dynamiques : ce sont des sites dont le contenu est dynamique , parce que le propritaire n'est plus le seul pouvoir le faire changer ! En plus des langages prcdemment cits, ils font intervenir d'autres technologies : Java EE est l'une d'entre elles !

Comment a marche
Lorsqu'un utilisateur consulte un site, ce qui se passe derrire les rideaux est un simple change entre un client et un serveur (voir la figure suivante). le client : dans la plupart des cas, c'est le navigateur install sur votre ordinateur. Retenez que ce n'est pas le seul moyen d'accder au web, mais c'est celui qui nous intresse dans ce cours. le serveur : c'est la machine sur laquelle le site est hberg, o les fichiers sont stocks et les pages web gnres.

Echange dynamique client <-> serveur

La communication qui s'effectue entre le client et le serveur est rgie par des rgles bien dfinies : le protocole HTTP (voir la figure suivante). Entrons donc un peu plus dans le dtail, et regardons de quoi est constitu un change simple : 1. l'utilisateur saisit une URL dans la barre d'adresses de son navigateur ; 2. le navigateur envoie alors une requte HTTP au serveur pour lui demander la page correspondante ; 3. le serveur reoit cette requte, l'interprte et gnre alors une page web qu'il va renvoyer au client par le biais d'une rponse HTTP ; 4. le navigateur reoit, via cette rponse, la page web finale, qu'il affiche alors l'utilisateur.

www.siteduzero.com

Partie 1 : Les bases du Java EE

12/606

Echange dynamique HTTP client <-> serveur Ce qu'il faut comprendre et retenir de tout a : les donnes sont changes entre le client et le serveur via le protocole HTTP ; le client ne comprend que les langages de prsentation de l'information, en d'autres termes les technologies HTML, CSS et Javascript ; les pages sont gnres sur le serveur de manire dynamique, partir du code source du site.

Les langages du web


Nous venons de le voir dans le dernier paragraphe, le client ne fait que recevoir des pages web, les afficher l'utilisateur et transmettre ses actions au serveur. V ous savez dj que les langages utiliss pour mettre en forme les donnes et les afficher l'utilisateur sont le HTML, le CSS et ventuellement le Javascript. Ceux-ci ont une caractristique commune importante : ils sont tous interprts par le navigateur, directement sur la machine client. D'ailleurs, le client est uniquement capable de comprendre ces quelques langages, rien de plus ! Eh bien le serveur aussi dispose de technologies bien lui, que lui seul est capable de comprendre : une batterie complte ayant pour objectif final de gnrer les pages web envoyer au client, avec tous les traitements que cela peut impliquer au passage : analyse des donnes reues via HTTP, transformation des donnes, enregistrement des donnes dans une base de donnes ou des fichiers, intgration des donnes dans le design Seulement, la diffrence du couple HTML & CSS qui est un standard incontournable pour la mise en forme des pages web, il existe plusieurs technologies capables de traiter les informations sur le serveur. Java EE est l'une d'entre elles, mais il en existe d'autres : PHP, .NET, Django et Ruby on Rails, pour ne citer que les principales. Toutes offrent sensiblement les mmes possibilits, mais toutes utilisent un langage et un environnement bien elles ! Comment choisir la technologie la mieux adapte son projet ?

C'est en effet une trs bonne question : qu'est-ce qui permet de se dcider parmi cet ventail de possibilits ? C'est un dbat presque sans fin. Toutefois, dans la vie relle le choix est bien souvent influenc, voire dict par : votre propre exprience : si vous avez dj dvelopp en Java, Python ou C# auparavant, il semble prudent de vous orienter respectivement vers Java EE, Django et .NET ; vos besoins : rapidit de dveloppement, faible utilisation des ressources sur le serveur, ractivit de la communaut soutenant la technologie, ampleur de la documentation disponible en ligne, cot, etc. Quoi qu'il en soit, peu importent les raisons qui vous ont pousss lire ce cours, nous sommes bien l pour apprendre le Java EE ! Java EE est une extension de la plate-forme standard Java SE, principalement destine au dveloppement d'applications web. Internet dsigne le rseau physique ; le web dsigne le contenu accessible travers ce rseau. Pour interagir avec un site web (le serveur), l'utilisateur (le client) passe par son navigateur. travers le protocole HTTP, le navigateur envoie des requtes au serveur et le serveur lui renvoie des rponses : le travail du serveur est de recevoir des requtes, de gnrer les pages web et de les envoyer au client. le travail du navigateur est de transmettre les actions de l'utilisateur au serveur, et d'afficher les informations qu'il renvoie.

www.siteduzero.com

Partie 1 : Les bases du Java EE

13/606

Le Java EE mis nu !
Le Java Enterprise Edition , comme son nom l'indique, a t cr pour le dveloppement d'applications d'entreprises. Nous nous y attarderons dans le chapitre suivant, mais sachez d'ores et dj que ses spcifications ont t penses afin, notamment, de faciliter le travail en quipe sur un mme projet : l'application est dcoupe en couches, et le serveur sur lequel tourne l'application est lui-mme dcoup en plusieurs niveaux. Pour faire simple, Java EE fournit un ensemble dextensions au Java standard afin de faciliter la cration dapplications centralises. V oyons comment tout cela s'agence !

Principes de fonctionnement
Nous venons de dcouvrir qu'afin de pouvoir communiquer entre eux, le client et le serveur doivent se parler via HTTP. Nous savons dj que, ct client, le navigateur s'en occupe. Ct serveur, qui s'en charge ? C'est un composant que l'on nomme logiquement serveur HTTP. Son travail est simple : il doit couter tout ce qui arrive sur le port utilis par le protocole HTTP, le port 80, et scruter chaque requte entrante. C'est tout ce qu'il fait, c'est en somme une interface de communication avec le protocole. titre informatif, voici les deux plus connus : Apache HTTP Server et IIS (Microsoft). Cependant, nous n'allons directement utiliser ni l'un ni l'autre. Pourquoi ?

tre capable de discuter via HTTP c'est bien, mais notre serveur doit permettre d'effectuer d'autres tches. En effet, une fois la requte HTTP lue et analyse, il faut encore traiter son contenu et ventuellement renvoyer une rponse au client en consquence. V ous devez probablement dj savoir que cette responsabilit vous incombe en grande partie : c'est le code que vous allez crire qui va dcider ce qu'il faut faire lorsque telle requte arrive ! Seulement, comme je viens de vous l'annoncer, un serveur HTTP de base ne peut pas grer votre application, ce n'est pas son travail. Remarque : cette affirmation est en partie fausse, dans le sens o la plupart des serveurs HTTP sont devenus des serveurs web part entire, incluant des plugins qui les rendent capables de supporter des langages de script comme le PHP, l'ASP, etc. Ainsi, nous avons besoin d'une solution plus globale : ce composant, qui va se charger d'excuter votre code en plus de faire le travail du serveur HTTP, se nomme le serveur d'applications . Donner une dfinition exacte du terme est difficile : ce que nous pouvons en retenir, c'est qu'un tel serveur inclut un serveur HTTP, et y ajoute la gestion d'objets de diverses natures au travers d'un composant que nous allons pour le moment nommer le conteneur (voir la figure suivante).

Architecture serveur Concrtement, le serveur d'applications va : rcuprer les requtes HTTP issues des clients ; les mettre dans des botes, des objets, que votre code sera capable de manipuler ; faire passer ces objets dans la moulinette qu'est votre application, via le conteneur ; renvoyer des rponses HTTP aux clients, en se basant sur les objets retourns par votre code. L encore, il en existe plusieurs sur le march, que l'on peut dcouper en deux secteurs :

www.siteduzero.com

Partie 1 : Les bases du Java EE

14/606

les solutions propritaires et payantes : WebLogic et WebSphere, respectivement issues de chez Oracle et IBM, sont les rfrences dans le domaine. Massivement utilises dans les banques et la finance notamment, elles sont la fois robustes, finement paramtrables et trs coteuses. les solutions libres et gratuites : Apache Tomcat, JBoss, GlassFish et Jonas en sont les principaux reprsentants. Comment faire un choix parmi toutes ces solutions ?

Hormis les problmatiques de cots qui sont videntes, d'autres paramtres peuvent influencer votre dcision ; citons par exemple la rapidit de chargement et dexcution, ainsi que la quantit de technologies supportes. En ce qui nous concerne, nous partons de zro : ainsi, un serveur d'applications basique, lger et gratuit fera trs bien l'affaire. a tombe bien, il en existe justement un qui rpond parfaitement tous nos besoins : Apache Tomcat. Pour information, c'est d'ailleurs souvent ce type de serveurs qui est utilis lors des phases de dveloppement de grands projets en entreprise. Le cot des licences des solutions propritaires tant lev, ce n'est que lors de la mise en service sur la machine finale (on parle alors de mise en production) que l'on opte ventuellement pour une telle solution.

Avant de dcouvrir et de prendre en main Tomcat, il nous reste encore quelques concepts cls aborder !

Le modle MVC : en thorie


Qu'est-ce qu'un modle de conception ?

En anglais design pattern , un modle de conception (ou encore patron de conception) est une simple bonne pratique, qui rpond un problme de conception d'une application. C'est en quelque sorte une ligne de conduite qui permet de dcrire les grandes lignes d'une solution. De tels modles sont issus de l'exprience des concepteurs et dveloppeurs d'applications : c'est en effet uniquement aprs une certaine priode d'utilisation que peuvent tre mises en vidence des pratiques plus efficaces que d'autres, pratiques qui sont alors structures en modles et considres comme standard. Maintenant que nous sommes au point sur ce qu'est un modle, la seconde question se poser concerne bien videmment le Java EE. Que recommandent les dveloppeurs Java EE expriments ?

Il faut bien vous rendre compte qu' l'origine, Java EE permet plus ou moins de coder son application comme on le souhaite : en d'autres termes, on peut coder n'importe comment ! Or on sait que dans Java EE, il y a Entreprise , et que a n'est pas l pour faire joli ! Le dveloppement en entreprise implique entre autres : que l'on puisse tre amen travailler plusieurs contributeurs sur un mme projet ou une mme application (travail en quipe) ; que l'on puisse tre amen maintenir et corriger une application que l'on n'a pas cre soi-mme ; que l'on puisse tre amen faire voluer une application que l'on n'a pas cre soi-mme. Pour toutes ces raisons, il est ncessaire d'adopter une architecture plus ou moins standard, que tout dveloppeur peut reconnatre, c'est--dire dans laquelle tout dveloppeur sait se reprer. Il a t trs vite remarqu qu'un modle permettait de rpondre ces besoins, et qu'il s'appliquait particulirement bien la conception d'applications Java EE : le modle MVC (Modle-Vue-Contrleur). Il dcoupe littralement l'application en couches distinctes, et de ce fait impacte trs fortement l'organisation du code ! V oici dans les grandes lignes ce qu'impose MVC : tout ce qui concerne le traitement, le stockage et la mise jour des donnes de l'application doit tre contenu dans la couche nomme "Modle" (le M de MVC) ; tout ce qui concerne l'interaction avec l'utilisateur et la prsentation des donnes (mise en forme, affichage) doit tre contenu dans la couche nomme "Vue" (le V de MVC) ; tout ce qui concerne le contrle des actions de l'utilisateur et des donnes doit tre contenu dans la couche nomme "Contrle" (le C de MVC).

www.siteduzero.com

Partie 1 : Les bases du Java EE


Ce modle peut tre reprsent par la figure suivante.

15/606

MVC Ne vous faites pas de souci si c'est encore flou dans votre esprit : nous reviendrons maintes reprises sur ces concepts au fur et mesure que nous progresserons dans notre apprentissage.

Le modle MVC : en pratique


Le schma prcdent est trs global, afin de vous permettre de bien visualiser l'ensemble du systme. Tout cela est encore assez abstrait, et c'est volontaire ! En effet, chaque projet prsente ses propres contraintes, et amne le dveloppeur faire des choix. Ainsi, on observe normment de variantes ds que l'on entre un peu plus dans le dtail de chacun de ces blocs. Prenons l'exemple du sous-bloc reprsentant les donnes (donc l'intrieur la couche Modle) : Quel est le type de stockage dont a besoin votre application ? Quelle est l'envergure de votre application ? Disposez-vous d'un budget ? La quantit de donnes produites par votre application va-t-elle tre amene fortement voluer ? La liste de questions est souvent longue, et ralisez bien que tous ces points sont autant de paramtres qui peuvent influencer la conception de votre application, et donc vos choix au niveau de l'architecture. Ainsi, dtailler plus finement les blocs composant une application n'est faisable qu'au cas par cas, idem pour les relations entre ceux-ci, et dpend fortement de l'utilisation ou non de frameworks Qu'est-ce qu'un framework ?

Il est encore bien trop tt pour que nous nous penchions sur ce sujet. Toutefois, vous pouvez d'ores et dj retenir qu'un framework est un ensemble de composants qui servent crer l'architecture et les grandes lignes d'une application. V ous pouvez le voir comme une bote outils gante, conue par un ou plusieurs dveloppeurs et mise disposition d'autres dveloppeurs, afin de faciliter leur travail. Il existe des frameworks dans beaucoup de langages et plate-formes, ce n'est pas un concept propre Java EE ni au dveloppement web en particulier. En ce qui concerne Java EE, nous pouvons par exemple citer JSF, Spring, Struts ou encore Hibernate. Toutes ces solutions sont des frameworks que les dveloppeurs sont libres d'utiliser ou non dans leurs projets. Bref, nous sommes encore loin d'tre assez l'aise avec la plate-forme Java EE pour tudier ces fameux frameworks, mais nous pouvons d'ores et dj tudier les applications Java EE "nues", sans frameworks ni fioritures. V oici donc une courte introduction de chacune des couches composant une telle application web suivant le modle MVC.

Modle : des traitements et des donnes


Dans le modle, on trouve la fois les donnes et les traitements appliquer ces donnes. Ce bloc contient donc des objets Java d'une part, qui peuvent contenir des attributs (donnes) et des mthodes (traitements) qui leur sont propres, et un systme capable de stocker des donnes d'autre part. Rien de bien transcendant ici, la complexit du code dpendra bien videmment de la complexit des traitements effectuer par votre application.

www.siteduzero.com

Partie 1 : Les bases du Java EE

16/606

Vue : des pages JSP


Une page JSP est destine la vue. Elle est excute ct serveur et permet l'criture de gabarits (pages en langage "client" comme HTML, CSS, Javascript, XML, etc.). Elle permet au concepteur de la page d'appeler de manire transparente des portions de code Java, via des balises et expressions ressemblant fortement aux balises de prsentation HTML.

Contrleur : des servlets


Une servlet est un objet qui permet d'intercepter les requtes faites par un client, et qui peut personnaliser une rponse en consquence. Il fournit pour cela des mthodes permettant de scruter les requtes HTTP. Cet objet n'agit jamais directement sur les donnes, il faut le voir comme un simple aiguilleur : il intercepte une requte issue d'un client, appelle ventuellement des traitements effectus par le modle, et ordonne en retour la vue d'afficher le rsultat au client.

Rien que dans ces quelques lignes, il y a beaucoup d'informations. Pas de panique, nous reviendrons sur tout cela en long, en large et en travers dans les parties suivantes de ce cours ! Afin de bien visualiser qui fait quoi, reprenons notre schma en mettant des noms sur nos blocs (voir la figure suivante).

MVC avec Java EE Un serveur d'applications est constitu d'un serveur HTTP et d'un conteneur web. Le modle de conception MVC impose une rpartition stricte des tches au sein d'une application : la couche Modle se charge des traitements effectuer sur les donnes et de leur stockage ; la couche V ue se charge de la prsentation des donnes pour l'utilisateur et de l'interaction ; la couche Contrle se charge d'aiguiller les requtes entrantes vers les traitements et vues correspondants. Un framework est une bote outils mise disposition du dveloppeur pour lui allger certaines tches. Dans une application Java EE sans frameworks : la couche Modle est constitue d'objets Java ; la couche V ue est constitue de pages JSP ; la couche Contrle est constitue de servlets.

www.siteduzero.com

Partie 1 : Les bases du Java EE

17/606

Outils et environnement de dveloppement


La cration d'une application web avec Java EE s'effectue gnralement l'aide d'un Environnement de Dveloppement Intgr, trs souvent raccourci l'anglaise en IDE. C'est un logiciel destin faciliter grandement le dveloppement dans son ensemble. S'il est possible pour certains projets Java de s'en passer, en ne se servant que d'un diteur de texte pour crire le code et d'une invite de commandes pour mettre en place l'application, ce n'est srieusement plus envisageable pour la cration d'une application web complexe. Nous allons donc dans ce chapitre apprendre en utiliser un, et y intgrer notre serveur d'applications. Si malgr mes conseils, votre ct extrmiste du bloc-notes prend le dessus et que vous souhaitez tout faire la main, ne vous inquitez pas, je prendrai le temps de dtailler ce qui se trame en coulisses lorsque c'est important !

L'IDE Eclipse Prsentation


J'utiliserai l'IDE Eclipse tout au long de ce cours. Ce n'est pas le seul existant, c'est simplement celui que je matrise le mieux. Massivement utilis en entreprise, c'est un outil puissant, gratuit, libre et multiplateforme. Les avantages d'un IDE dans le dveloppement d'applications web Java EE sont multiples, et sans toutefois tre exhaustif en voici une liste : intgration des outils ncessaires au dveloppement et au dploiement d'une application ; paramtrage ais et centralis des composants d'une application ; multiples moyens de visualisation de l'architecture d'une application ; gnration automatique de portions de code ; assistance la vole lors de l'criture du code ; outils de dbogage

Tlchargement et installation
Comme vous pouvez le constater en vous rendant sur la page de tlchargements du site, Eclipse est dclin en plusieurs versions. Nous avons bien entendu besoin de la version spcifique au dveloppement Java EE (voir la figure suivante).

Page de tlchargement d'Eclipse pour Java EE Cliquez sur "Eclipse IDE for Java EE Developers", puis choisissez et tlchargez la version correspondant votre systme d'exploitation, comme indiqu la figure suivante.

www.siteduzero.com

Partie 1 : Les bases du Java EE

18/606

Choix de la version correspondant votre systme d'exploitation Une fois le logiciel tlcharg, installez-le de prfrence dans un rpertoire situ directement la racine de votre disque dur, et dont le titre ne contient ni espaces ni caractres spciaux. Typiquement, vitez les dossiers du genre "Program Files" et consorts. Ce n'est pas une obligation mais un simple conseil, qui vous vitera bien des ennuis par la suite. Je l'ai pour ma part install dans un rpertoire que j'ai nomm eclipse et plac la racine de mon disque dur : on peut difficilement faire plus clair. Pour ceux d'entre vous qui ont dj sur leur poste une version "Eclipse for Java developers" et qui ne souhaitent pas tlcharger et installer la version pour Java EE, sachez qu'il est possible - mais bien moins agrable - d'y ajouter des plugins afin d'y reproduire l'intgration de l'environnement Java EE. Si vous y tenez, voici les tapes suivre depuis votre fentre Eclipse : 1. 2. 3. 4. Allez dans Help > Install New Software. Choisissez le site "Indigo - http://download.eclipse.org/releases/indigo". Droulez "Web, XML, and Java EE Development". Cochez alors "JST Server Adapters" et "JST Server Adapters Extentions".

a rsoudra une partie des problmes que vous pourriez rencontrer par la suite en suivant ce cours. Notez bien toutefois que je vous conseille de ne pas procder ainsi, et de repartir d'une version vierge d'Eclipse pour Java EE.

Configuration
Ci-dessous, je vous donne quelques conseils pour configurer votre Eclipse efficacement. Je ne vais pas vous expliquer en dtail pourquoi ces rglages sont importants, faites-moi simplement confiance et suivez le guide !

Modification de l'encodage par dfaut


Si vous ouvrez Eclipse pour la premire fois, commencez par fermer l'onglet de bienvenue qui s'affiche. Rendez-vous alors dans la barre de menus suprieure, et cliquez sur Window, puis Preferences. Dans la fentre qui s'affiche alors, il y a un champ vous permettant de taper du texte en haut gauche. Saisissez-y le mot " encoding ", et dans chaque section qui apparat alors dans le volet de gauche, changez l'encodage par dfaut (il est gnralement rgl Cp1252 ou ISO-8859-1) par la valeur UTF-8 , comme indiqu la figure suivante.

Validez pour finir en cliquant sur Ok afin d'appliquer les modifications.

Dsactivation de la vrification de l'orthographe


Rendez-vous nouveau dans le menu Window > Preferences, puis dans le volet de gauche rendez-vous dans General > Editors > Text Editors, et dans le volet de droite cochez la case "Show line numbers". Dans le volet de gauche, cliquez

www.siteduzero.com

Partie 1 : Les bases du Java EE

19/606

alors sur le sous-menu Spelling, et dans le nouveau volet de droite qui apparat, dcochez la case "Enable spell checking" (voir la figure suivante).

Validez pour finir en cliquant sur Ok afin d'appliquer les modifications.

Le serveur Tomcat Prsentation


Nous l'avons dcouvert dans le second chapitre : pour faire fonctionner une application web Java EE, nous avons besoin de mettre en place un serveur d'applications . Il en existe beaucoup sur le march : j'ai, pour le dbut de ce cours, choisi d'utiliser Tomcat, car c'est un serveur lger, gratuit, libre, multiplateforme et assez complet pour ce que nous allons aborder. On le rencontre d'ailleurs trs souvent dans des projets en entreprise, en phase de dveloppement comme en production. Si vous souhaitez vous renseigner sur les autres serveurs existants et sur leurs diffrences, vous savez o chercher. Wikipdia en propose par ailleurs une liste non exhaustive. Pour information, sachez que Tomcat tire sa lgret du fait qu'il n'est en ralit que l'assemblage d'un serveur web (gestion des requtes/rponses HTTP) et d'un conteneur web (nous parlerons en temps voulu de conteneur de servlets , et reviendrons sur ce que cela signifie concrtement). Pour le moment, retenez simplement que ce n'est pas un serveur d'applications Java EE au sens complet du terme, car il ne respecte pas entirement ses spcifications et ne supporte pas toutes ses technologies.

Installation
Nous allons utiliser la dernire version en date ce jour, savoir Tomcat 7.0 . Rendez-vous sur la page de tlchargement de Tomcat, puis choisissez et tlchargez la version correspondant votre systme d'exploitation, comme indiqu la figure suivante.

www.siteduzero.com

Partie 1 : Les bases du Java EE

20/606

Page de tlchargement de Tomcat

Sous Windows
Rcuprez la dernire version Core au format zip, puis dcompressez son contenu dans le rpertoire o vous souhaitez installer Tomcat. Au sujet du rpertoire d'installation, mme conseil que pour Eclipse : choisissez un chemin dont les noms de dossiers ne comportent pas d'espaces : pour ma part, je l'ai plac dans un dossier nomm tomcat7 la racine de mon disque. Un dossier nomm apache-tomcat-7.0.xx (les deux derniers numros changeant selon la version que vous utiliserez) contient alors l'installation. Pour information, ce dossier est souvent rfrenc dans les cours et documentations sous lappellation Tomcat Home. V oici la figure suivante ce que j'obtiens sur mon poste.

www.siteduzero.com

Partie 1 : Les bases du Java EE

21/606

Rpertoire d'installation de Tomcat Dans ce rpertoire d'installation de Tomcat, vous trouverez un dossier nomm webapps : c'est ici que seront stockes par dfaut vos applications. Pour ceux d'entre vous qui souhaiteraient jeter un il ce qui se passe derrire les rideaux, vous trouverez dans le dossier conf les fichiers suivants : server.xml : contient les lments de configuration du serveur ; context.xml : contient les directives communes toutes les applications web dployes sur le serveur ; tomcat-users.xml : contient entre autres l'identifiant et le mot de passe permettant d'accder l'interface d'administration de votre serveur Tomcat ; web.xml : contient les paramtres de configuration communs toutes les applications web dployes sur le serveur. Je ne m'attarde pas sur le contenu de chacun de ces fichiers : nous y effectuerons des modifications indirectement via l'interface d'Eclipse au cours des exemples venir. Je n'aborde volontairement pas dans le dtail la configuration fine d'un serveur Tomcat, ceci mritant sans aucun doute un tutoriel part entire. V ous pouvez nanmoins trouver beaucoup d'informations sur Tomcat's Corner; bien que ce site traite des versions plus anciennes, la plupart des concepts prsents sont toujours d'actualit. Je vous renvoie bien sr la documentation officielle de Tomcat 7, pour plus d'exactitude.

Sous Linux
Rcuprez la dernire version Core au format tar.gz : une archive nomme apache-tomcat-7.0.xx.tar.gz est alors enregistre sur votre poste, o xx correspond la sous-version courante. Au moment o j'cris ces lignes, la version est la 7.0.20 : pensez adapter les commandes qui suivent la version que vous tlchargez. Dcompressez ensuite son contenu dans le rpertoire o vous souhaitez installer Tomcat. Par exemple : Code : Console cd /usr/local

www.siteduzero.com

Partie 1 : Les bases du Java EE


mkdir tomcat cd /usr/local/tomcat cp ~/apache-tomcat-7.0.20.tar.gz . tar -xvzf apache-tomcat-7.0.20.tar.gz

22/606

Un dossier nomm apache-tomcat-7.0.20 contient alors l'installation. Pour information, ce dossier est souvent rfrenc dans les cours et documentations sous lappellation Tomcat Home. Vrifiez alors que l'installation s'est bien effectue : Code : Console cd /usr/local/tomcat/apache-tomcat-7.0.20 cd bin ./version.sh

Ce script montre que Tomcat 7.0 a t install avec succs sur votre distribution Linux : Code : Console Server version: Server built: Server number: OS Name: Apache Tomcat/7.0.20 Aug 28 2011 15:13:02 7.0.20.0 Linux

Sous Mac OS
Je n'ai malheureusement pas ma disposition de machine tournant sous Mac OS. Si vous tes un aficionado de la marque la pomme, voici deux liens qui expliquent comment installer Tomcat 7 sur OS X : Installation de Tomcat 7.0.x sur Mac OS X Installation sous Mac OS X Snow Leopard Bonne lecture, et n'hsitez pas me prvenir d'ventuelles erreurs ou changements dans le procd prsent, je modifierai cette section du chapitre en consquence.

Cration du projet web avec Eclipse


Depuis Eclipse, suivez le chemin suivant : File > New > Project... (voir la figure suivante). Ceci peut d'ailleurs tre raccourci en tapant au clavier Ctrl + N.

www.siteduzero.com

Partie 1 : Les bases du Java EE

23/606

Nouveau projet web sous

Eclipse Slectionnez alors Dynamic Web Project comme le montre l'image ci-dessus, puis cliquez sur Next >. J'appelle ici mon projet test. Remarquez ensuite la figure suivante le passage concernant le serveur.

www.siteduzero.com

Partie 1 : Les bases du Java EE

24/606

Mise en place de Tomcat - tape

1 Cliquez sur le bouton New Runtime... et slectionnez alors Apache Tomcat 7.0 dans la liste des possibilits, comme indiqu la figure suivante.

www.siteduzero.com

Partie 1 : Les bases du Java EE

25/606

Mise en place de Tomcat - tape 2

Cochez la case comme indiqu ci-dessus, ce qui signifie que nous allons en plus du projet crer localement une nouvelle instance d'un serveur, instance que nous utiliserons par la suite pour dployer notre application. Cliquez ensuite sur Next > et remplissez correctement les informations relatives votre installation de Tomcat en allant chercher le rpertoire d'installation de Tomcat sur votre poste. Les champs devraient alors ressembler ceux de la figure suivante, le rpertoire d'installation et le numro de version de Tomcat 7 pouvant tre diffrents chez vous selon ce que vous avez choisi et install.

www.siteduzero.com

Partie 1 : Les bases du Java EE

26/606

Mise en place de Tomcat - tape 3

Validez alors en cliquant sur Finish, puis cliquez deux fois sur Next >, jusqu' obtenir cette fentre (voir la figure suivante).

www.siteduzero.com

Partie 1 : Les bases du Java EE

27/606

Mise en place de Tomcat - tape

4 Avant d'aller plus loin, il est ncessaire de parler contexte ! Souvenez-vous, je vous ai dj parl d'un fichier context.xml associ toutes les applications. Pour permettre plus de souplesse, il est possible de spcifier un contexte propre chaque webapp . Comme je vous l'ai dj dit, ces applications web sont empiriquement contenues dans le dossier webapps de votre Tomcat Home. C'est ici que, par dfaut, Tomcat ira chercher les applications qu'il doit grer et dployer. Jusque-l, vous suivez Le souci, et certains d'entre vous l'auront peut-tre dj compris, c'est que notre projet nous, cr depuis Eclipse, se trouve dans un rpertoire de notre workspace Eclipse : il n'est pas du tout dans ce fameux rpertoire webapps de Tomcat. Pour que notre serveur prenne en compte notre future application, il va donc falloir arranger le coup ! Plusieurs solutions s'offrent nous : crer un rpertoire du mme nom que notre projet sous Eclipse, directement dans le dossier webapps de Tomcat, et y copier-coller nos fichiers, et ce chaque modification de code ou configuration effectue ; crer un nouveau projet depuis Eclipse, en utilisant directement le rpertoire webapps de votre Tomcat Home comme workspace Eclipse ; modifier le server.xml ou le context.xml de votre Tomcat, afin qu'il sache o chercher ; utiliser les proprits d'un projet web dynamique sous Eclipse.

www.siteduzero.com

Partie 1 : Les bases du Java EE

28/606

tant donn la dernire fentre qui s'est affiche, vous avez probablement devin sur quelle solution notre choix va se porter. Je vous conseille bien videmment ici d'utiliser la quatrime et dernire solution. Conservez le nom de votre projet sous Eclipse comme contexte de dploiement sur votre serveur Tomcat (" Context root " sur l'image prcdente), afin de rester cohrent. Utiliser les paramtres ci-dessus permet alors de ne pas avoir modifier vous-mmes le contexte de votre serveur, ou encore de ne pas avoir utiliser le dossier webapps de votre serveur Tomcat en guise de workspace. Toute modification sur vos futures pages et classes sera ainsi automatiquement prise en compte par votre serveur Tomcat, qui s'occupera de recharger le contexte chaque modification sauvegarde, lorsque le serveur sera lanc. Comme diraient les ttes claques, isn't it amazing? V oici maintenant la figure suivante ce quoi doit ressembler votre fentre Eclipse.

Mise en place de Tomcat - tape 5 V ous noterez l'apparition d'une entre Tomcat v7.0 dans l'onglet Servers, et de l'arborescence de votre projet test dans le volet de gauche. Faites maintenant un clic droit sur le titre de votre projet dans l'arborescence Eclipse, et suivez Run As > Run on Server, comme indiqu la figure suivante.

www.siteduzero.com

Partie 1 : Les bases du Java EE

29/606

Mise en place

de Tomcat - tape 6 Dans la fentre qui s'ouvre alors (voir la figure suivante), nous allons slectionner le serveur Tomcat que nous venons de mettre en place lors de la cration de notre projet web, et prciser que l'on souhaite associer par dfaut notre projet ce serveur.

www.siteduzero.com

Partie 1 : Les bases du Java EE

30/606

Mise en place de Tomcat - tape

7 Cliquez alors sur Next >, puis vrifiez que votre nouveau projet est bien pris en compte par votre serveur (voir la figure suivante).

www.siteduzero.com

Partie 1 : Les bases du Java EE

31/606

Mise en place de Tomcat - tape

8 Validez enfin en cliquant sur Finish, et voil la mise en place de votre projet et de son serveur termine ! Pour la petite histoire, une section est ajoute dans le fichier server.xml de votre instance de Tomcat, qui est maintenant accessible depuis le dossier Servers de votre arborescence Eclipse, comme vous pouvez le voir sur la figure suivante.

www.siteduzero.com

Partie 1 : Les bases du Java EE

32/606

Mise en

place de Tomcat - tape 9 Si vous tes curieux, ditez-le ! V ous verrez qu'il contient effectivement en fin de fichier une section de ce type : Code : XML <Context docBase="test" path="/test" reloadable="true" source="org.eclipse.jst.jee.server:test"/>

Dornavant, pour piloter votre serveur Tomcat il vous suffira de vous rendre dans l'onglet Servers en bas de votre fentre Eclipse, et d'utiliser un des boutons selon le besoin (redmarrage, arrt, debug), comme indiqu la figure suivante.

Mise

en place de Tomcat - tape 10 Sachez pour finir, que cette manipulation n'est pas limite Tomcat. V ous pouvez utiliser d'autres types de serveurs, cela ne pose pas de problmes. De mme, une fois que vous avez correctement paramtr un serveur Tomcat depuis Eclipse, vous n'tes pas forcs de recrer localement un nouveau serveur pour chacun de vos projets, vous pouvez trs bien rutiliser la mme instance de Tomcat en y dployant plusieurs applications web diffrentes.

Si vous tes arrivs jusqu'ici, c'est que votre instance de serveur Tomcat est fonctionnelle et que vous pouvez la piloter depuis Eclipse. V oyons maintenant o placer notre premier essai, et comment y accder.

Structure d'une application Java EE


www.siteduzero.com

Partie 1 : Les bases du Java EE

33/606

Structure standard
Toute application web Java EE doit respecter une structure de dossiers standard, qui est dfinie dans les spcifications de la plate-forme. V ous en trouverez le schma la figure suivante.

Structure des fichiers d'une application web JSP/Servlet Quelques prcisions : La racine de l'application, en violet sur le schma, est le dossier qui porte le nom de votre projet et qui contient l'intgralit des dossiers et fichiers de l'application. Le dossier nomm WEB-INF est un dossier spcial. Il doit obligatoirement exister et tre plac juste sous la racine de l'application. Il doit son tour obligatoirement contenir : le fichier de configuration de l'application (web.xml) ; un dossier nomm classes , qui contient son tour les classes compiles (fichiers .class) ; un dossier nomm lib, qui contient son tour les bibliothques ncessaires au projet (archives .jar). Bref, tous les dossiers et fichiers marqus en rouge sur le schma doivent obligatoirement tre nomms et placs comme indiqu sur le schma. Les fichiers et dossiers persos placs directement sous la racine, en bleu sur le schma, sont publics et donc accessibles directement par le client via leurs URL. (*) Les fichiers et dossiers persos placs sous le rpertoire WEB-INF, en orange sur le schma, sont privs et ne sont donc pas accessibles directement par le client. (*) (*) Nous reviendrons en temps voulu sur le caractre priv du dossier WEB-INF , et sur la distinction avec les dossiers publics. V oil tout concernant la structure officielle : si votre application n'est pas organise de cette manire, le serveur d'applications ne sera pas capable de la dployer ni de la faire fonctionner correctement.

Votre premire page web


www.siteduzero.com

Partie 1 : Les bases du Java EE

34/606

Eclipse, ce fourbe !
Ce que vous devez savoir avant de continuer, c'est qu'Eclipse joue souvent au fourbe, en adaptant certaines spcificits son mode de fonctionnement. En l'occurrence, Eclipse modifie comme suit la structure d'une application Java EE (voir la figure suivante).

Structure des fichiers d'une application web sous Eclipse Comme vous pouvez le voir en vert sur le schma, Eclipse dplace la structure standard de l'application vers un dossier nomm WebContent, et ajoute sous la racine un dossier src qui contiendra le code source de vos classes (les fichiers .java). En outre (je ne les ai pas reprsents ici), sachez qu'Eclipse ajoute galement sous la racine quelques fichiers de configuration qui lui permettront, via une tambouille interne, de grer correctement l'application ! Attendez... Je viens de vous dire que si notre application n'tait pas correctement structure, notre serveur d'applications ne saurait pas la grer. Si Eclipse vient mettre son nez dans cette histoire, comment notre application vat-elle pouvoir fonctionner ? Eh bien comme je viens de vous l'annoncer, Eclipse se dbrouille via une tambouille interne pour que la structure qu'il a modifie soit, malgr tout, utilisable sur le serveur d'applications que nous lui avons intgr. Ceci implique donc deux choses trs importantes : le dossier WebContent n'existe lgitimement qu'au sein d'Eclipse. Si vous dveloppez sans IDE, ce rpertoire ne doit pas exister et votre application doit imprativement suivre la structure standard prsente prcdemment ; pour cette mme raison, si vous souhaitez utiliser votre application en dehors de l'IDE, il faudra obligatoirement utiliser l'outil d'export propos par Eclipse. Raliser un simple copier-coller des dossiers ne fonctionnera pas en dehors d'Eclipse ! L encore, nous y reviendrons plus tard.

Cration d'une page web

www.siteduzero.com

Partie 1 : Les bases du Java EE

35/606

V ous avez maintenant en mains toutes les informations pour bien dbuter. V otre projet dynamique frachement cr, vous pouvez maintenant placer votre premire page HTML dans son dossier public, c'est--dire sous le dossier WebContent d'Eclipse (voir le bloc bleu sur notre schma). Pour cela, tapez une nouvelle fois Ctrl + N au clavier, puis cherchez HTML File dans le dossier Web de l'arborescence qui apparat alors. Slectionnez ensuite le dossier parent, en l'occurrence le dossier WebContent de votre projet, puis donnez un nom votre page et enfin validez. Je nomme ici ma page test.html (voir les figures suivantes).

Cration d'une page HTML dans

votre projet

www.siteduzero.com

Partie 1 : Les bases du Java EE

36/606

Saisie du dossier parent et du

nom de la page HTML Une page HTML est donc apparue dans votre projet, sous le rpertoire WebContent. Remplacez alors le code automatiquement gnr par Eclipse dans votre page par ce code HTML basique : Code : HTML - test.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page HTML.</p> </body> </html>

V ous pouvez maintenant tenter d'accder votre page web frachement cre. Pour ce faire, lancez le serveur Tomcat, via le bouton si vous avez bien suivi les instructions que je vous ai prsentes prcdemment. Ouvrez ensuite votre navigateur prfr, et entrez l'URL suivante afin d'accder votre serveur : Code : URL

www.siteduzero.com

Partie 1 : Les bases du Java EE


http://localhost:8080/test/test.html

37/606

V otre page s'affiche alors sous vos yeux dus !? C'est quoi toute cette histoire ? Tout un flan pour afficher trois mots ?

Patience, patience Notre serveur tant maintenant fonctionnel, nous voici prts entrer dans le vif du sujet. Un IDE permet de simplifier le dveloppement d'un projet dans son ensemble. Tomcat n'est pas un serveur d'applications Java EE au sens complet du terme. La configuration du serveur passe principalement par deux fichiers : server.xml et web.xml. Une application web Java EE doit respecter une architecture bien dfinie. Eclipse modifie l'architecture des applications pour les intgrer correctement son systme. Nous sommes maintenant prts pour dvelopper notre premire application web. Allons-y !

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

38/606

Partie 2 : Premiers pas avec Java EE


Le travail srieux commence : au programme, dcouverte & cration de votre premire servlet, de votre premire page JSP et de votre premier JavaBean, et apprentissage du langage JSP ! Cette partie se terminera enfin par un rcapitulatif de ce que nous serons alors capables de faire, et de ce qui nous manquera encore.

La servlet
Nous y voil enfin ! Nous allons commencer par dcouvrir ce qu'est une servlet, son rle au sein de l'application et comment elle doit tre mise en place. J'adopte volontairement pour ce chapitre un rythme assez lent, afin que vous preniez bien conscience des fondements de cette technologie. Pour ceux qui trouveraient cela barbant, comprenez bien que c'est important de commencer par l et rassurez-vous, nous ne nous soucierons bientt plus de tous ces dtails !

Derrire les rideaux Retour sur HTTP


Avant d'tudier le code d'une servlet, nous devons nous pencher un instant sur le fonctionnement du protocole HTTP. Pour le moment, nous avons simplement appris que c'tait le langage qu'utilisaient le client et le serveur pour s'changer des informations. Il nous faudrait idalement un chapitre entier pour l'tudier en dtail, mais nous ne sommes pas l pour a ! Je vais donc tcher de faire court Si nous observions d'un peu plus prs ce langage, nous remarquerions alors qu'il ne comprend que quelques mots, appels mthodes HTTP. Ce sont les mots qu'utilise le navigateur pour poser des questions au serveur. Mieux encore, je vous annonce d'emble que nous ne nous intresserons qu' trois de ces mots : GET, POST et HEAD.

GET
C'est la mthode utilise par le client pour rcuprer une ressource web du serveur via une URL. Par exemple, lorsque vous tapez www.siteduzero.com dans la barre d'adresses de votre navigateur et que vous validez, votre navigateur envoie une requte GET pour rcuprer la page correspondant cette adresse et le serveur la lui renvoie. La mme chose se passe lorsque vous cliquez sur un lien. Lorsqu'il reoit une telle demande, le serveur ne fait pas que retourner la ressource demande, il en profite pour l'accompagner d'informations diverses son sujet, dans ce qui s'appelle les en-ttes ou headers HTTP : typiquement, on y trouve des informations comme la longueur des donnes renvoyes ou encore la date d'envoi. Enfin, sachez qu'il est possible de transmettre des donnes au serveur lorsque l'on effectue une requte GET, au travers de paramtres directement placs aprs l'URL (paramtres nomms query strings) ou de cookies placs dans les en-ttes de la requte : nous reviendrons en temps voulu sur ces deux manires de faire. La limite de ce systme est que, comme la taille d'une URL est limite, on ne peut pas utiliser cette mthode pour envoyer des donnes volumineuses au serveur, par exemple un fichier. Les gens qui ont crit la norme dcrivant le protocole HTTP ont mis des recommandations d'usage, que les dveloppeurs sont libres de suivre ou non. Celles-ci prcisent que via cette mthode GET, il est uniquement possible de rcuprer ou de lire des informations, sans que cela ait un quelconque impact sur la ressource demande : ainsi, une requte GET est cense pouvoir tre rpte indfiniment sans risques pour la ressource concerne.

POST
La taille du corps du message d'une requte POST n'est pas limite, c'est donc cette mthode qu'il faut utiliser pour soumettre au serveur des donnes de tailles variables, ou que l'on sait volumineuses. C'est parfait pour envoyer des fichiers par exemple. Toujours selon les recommandations d'usage, cette mthode doit tre utilise pour raliser les oprations qui ont un effet sur la ressource, et qui ne peuvent par consquent pas tre rptes sans l'autorisation explicite de l'utilisateur. V ous avez probablement dj reu de votre navigateur un message d'alerte aprs avoir actualis une page web, vous prvenant qu'un rafrachissement de la page entranera un renvoi des informations : eh bien c'est simplement parce que la page que vous souhaitez recharger a t rcupre via la mthode POST, et que le navigateur vous demande confirmation avant de renvoyer

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


nouveau la requte.

39/606

HEAD
Cette mthode est identique la mthode GET, ceci prs que le serveur n'y rpondra pas en renvoyant la ressource accompagne des informations la concernant, mais seulement ces informations . En d'autres termes, il renvoie seulement les enttes HTTP ! Il est ainsi possible par exemple de vrifier la validit d'une URL ou de vrifier si le contenu d'une page a chang ou non sans avoir rcuprer la ressource elle-mme : il suffit de regarder ce que contiennent les diffrents champs des en-ttes. Ne vous inquitez pas, nous y reviendrons lorsque nous manipulerons des fichiers.

Pendant ce temps-l, sur le serveur


Rappelez-vous notre schma global : la requte HTTP part du client et arrive sur le serveur. L'lment qui entre en jeu est alors le serveur HTTP (on parle galement de serveur web), qui ne fait qu'couter les requtes HTTP sur un certain port, en gnral le port 80. Que fait-il lorsqu'une requte lui parvient ?

Nous savons dj qu'il la transmet un autre lment, que nous avons jusqu' prsent qualifi de conteneur : il s'agit en ralit d'un conteneur de servlets , galement nomm conteneur web (voir la figure suivante). Celui-ci va alors crer deux nouveaux objets : HttpServletRequest : cet objet contient la requte HTTP, et donne accs toutes ses informations, telles que les en-ttes (headers) et le corps de la requte. HttpServletResponse : cet objet initialise la rponse HTTP qui sera renvoye au client, et permet de la personnaliser, en initialisant par exemple les en-ttes et le corps (nous verrons comment par la suite).

Conteneur et paire d'objets requte/rponse

Et ensuite ? Que fait-il de ce couple d'objets ?

Eh bien ce moment prcis, c'est votre code qui va entrer en jeu (reprsent par la srie de rouages sur le schma). En effet, le conteneur de servlets va les transmettre votre application, et plus prcisment aux servlets et filtres que vous avez ventuellement mis en place. Le cheminement de la requte dans votre code commence peine, et nous devons dj nous arrter : qu'est-ce qu'une servlet ?

Cration
www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

40/606

Une servlet est en ralit une simple classe Java, qui a la particularit de permettre le traitement de requtes et la personnalisation de rponses . Pour faire simple, dans la trs grande majorit des cas une servlet n'est rien d'autre qu'une classe capable de recevoir une requte HTTP envoye depuis le navigateur de l'utilisateur, et de lui renvoyer une rponse HTTP. C'est tout ! En principe, une servlet dans son sens gnrique est capable de grer n'importe quel type de requte, mais dans les faits il s'agit principalement de requtes HTTP. Ainsi, l'usage veut qu'on ne s'embte pas prciser "servlet HTTP" lorsque l'on parle de ces dernires, et il est donc extrmement commun d'entendre parler de servlets alors qu'il s'agit bien en ralit de servlets HTTP. Dans la suite de ce cours, je ferai de mme.

Un des avantages de la plate-forme Java EE est sa documentation : trs fournie et offrant un bon niveau de dtails, la Javadoc permet en un rien de temps de se renseigner sur une classe, une interface ou un package de l'API Java EE. Tout au long de ce cours, je mettrai votre disposition des liens vers les documentations des objets importants, afin que vous puissiez facilement, par vous-mmes, complter votre apprentissage et vous familiariser avec ce systme de documentation. Regardons donc ce qu'elle contient au chapitre concernant le package servlet : on y trouve une quarantaine de classes et interfaces, parmi lesquelles l'interface nomme Servlet. En regardant celle-ci de plus prs, on apprend alors qu'elle est l'interface mre que toute servlet doit obligatoirement implmenter. Mieux encore, on apprend en lisant sa description qu'il existe dj des classes de base qui l'implmentent, et qu'il nous suffit donc d'hriter d'une de ces classes pour crer une servlet (voir la figure suivante).

Javadoc de

l'interface Servlet Nous souhaitons traiter des requtes HTTP, nous allons donc faire hriter notre servlet de la classe HttpServlet ! De retour sur votre projet Eclipse, faites un clic droit sur le rpertoire src, puis choisissez New > Class. Renseignez alors la fentre qui s'ouvre comme indiqu sur les figures suivantes.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

41/606

text

Cration d'une servlet

Renseignez le champ package par un package de votre choix : pour notre projet, j'ai choisi de le nommer com.sdzee.servlets ! Renseignez le nom de la servlet, puis cliquez ensuite sur le bouton Browse... afin de dfinir de quelle classe doit hriter notre

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


servlet, puis allez chercher la classe HttpServlet et validez. V oici le code que vous obtenez alors automatiquement : Code : Java - com.sdzee.servlets.Test package com.sdzee.servlets; import javax.servlet.http.HttpServlet; public class Test extends HttpServlet { }

42/606

Rien d'extraordinaire pour le moment, notre servlet tant absolument vide. D'ailleurs puisqu'elle ne fait encore rien, sautons sur l'occasion pour prendre le temps de regarder ce que contient cette classe HttpServlet hrite, afin de voir un peu ce qui se passe derrire. La Javadoc nous donne des informations utiles concernant le fonctionnement de cette classe : pour commencer c'est une classe abstraite, ce qui signifie qu'on ne pourra pas l'utiliser telle quelle et qu'il sera ncessaire de passer par une servlet qui en hrite. On apprend ensuite que la classe propose les mthodes Java ncessaires au traitement des requtes et rponses HTTP ! Ainsi, on y trouve les mthodes : doGet() pour grer la mthode GET ; doPost() pour grer la mthode POST ; doHead() pour grer la mthode HEAD. Comment la classe fait-elle pour associer chaque type de requte HTTP la mthode Java qui lui correspond ?

V ous n'avez pas vous en soucier, ceci est gr automatiquement par sa mthode service() : c'est elle qui se charge de lire l'objet HttpServletRequest et de distribuer la requte HTTP la mthode doXXX() correspondante. Ce qu'il faut retenir pour le moment : une servlet HTTP doit hriter de la classe abstraite HttpServlet ; une servlet doit implmenter au moins une des mthodes doXXX(), afin d'tre capable de traiter une requte entrante. Puisque ce sont elles qui prennent en charge les requtes entrantes, les servlets vont tre les points d'entre de notre application web, c'est par elles que tout va passer. Contrairement au Java SE, il n'existe pas en Java EE de point d'entre unique prdfini, comme pourrait l'tre la mthode main()

Mise en place
V ous le savez, les servlets jouent un rle trs particulier dans une application. Je vous ai parl d'aiguilleurs en introduction, on peut encore les voir comme des gendarmes : si les requtes taient des vhicules, les servlets seraient charges de faire la circulation sur le gigantesque carrefour qu'est votre application ! Eh bien pour obtenir cette autorit et tre reconnues en tant que telles, les servlets ncessitent un traitement de faveur : il va falloir les enregistrer auprs de notre application. Revenons notre exemple. Maintenant que nous avons cod notre premire servlet, il nous faut donc un moyen de faire comprendre notre application que notre servlet existe, la fois pour lui donner l'autorit sur les requtes et pour la rendre accessible au public ! Lorsque nous avions mis en place une page HTML statique dans le chapitre prcdent, le problme ne se posait pas : nous accdions directement la page en question via une URL directe pointant vers le fichier depuis notre navigateur. Mais dans le cas d'une servlet qui, rappelons-le, est une classe Java, comment faire ?

Concrtement, il va falloir configurer quelque part le fait que notre servlet va tre associe une URL. Ainsi lorsque le client la saisira, la requte HTTP sera automatiquement aiguille par notre conteneur de servlet vers la bonne servlet, celle qui est en charge de rpondre cette requte. Ce "quelque part" se prsente sous la forme d'un simple fichier texte : le fichier web.xml . C'est le cur de votre application : ici vont se trouver tous les paramtres qui contrlent son cycle de vie. Nous n'allons pas apprendre d'une traite toutes les options intressantes, mais y aller par tapes. Commenons donc par apprendre lier notre servlet une URL : aprs tous les efforts que nous avons fournis, c'est le minimum syndical que nous sommes en droit de lui

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


demander !

43/606

Ce fichier de configuration doit imprativement se nommer web.xml et se situer juste sous le rpertoire /WEB-INF de votre application. Si vous avez suivi la lettre la procdure de cration de notre projet web, alors ce fichier est dj prsent. ditez-le, et supprimez le contenu gnr par dfaut. Si jamais le fichier est absent de votre arborescence, crez simplement un nouveau fichier XML en veillant bien le placer sous le rpertoire /WEB-INF et le nommer web.xml . V oici la structure vide du fichier : Code : XML - Fichier /WEB-INF/web.xml vide <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> </web-app>

L'intgralit de son contenu devra tre place entre les balises <web-app> et </web-app>. Pour le moment, ne prtez pas attention aux nombreux attributs prsents au sein de cette balise <web-app>, nous reviendrons sur leur rle lorsque nous dcouvrirons les expressions EL. La mise en place d'une servlet se droule en deux tapes : nous devons d'abord dclarer la servlet, puis lui faire correspondre une URL.

Dfinition de la servlet
La premire chose faire est de dclarer notre servlet : en quelque sorte il s'agit de lui donner une carte d'identit, un moyen pour le serveur de la reconnatre. Pour ce faire, il faut ajouter une section au fichier qui se prsente ainsi sous sa forme minimale : Code : XML - Dclaration de notre servlet <servlet> <servlet-name>Test</servlet-name> <servlet-class>com.sdzee.servlets.Test</servlet-class> </servlet>

La balise responsable de la dfinition d'une servlet se nomme logiquement <servlet>, et les deux balises obligatoires de cette section sont trs explicites : <servlet-name> permet de donner un nom une servlet. C'est ensuite via ce nom qu'on fera rfrence la servlet en question. Ici, j'ai nomm notre servlet Test. <servlet-class> sert prciser le chemin de la classe de la servlet dans votre application. Ici, notre classe a bien pour nom Test et se situe bien dans le package com.sdzee.servlets . Bonne pratique : gardez un nom de classe et un nom de servlet identiques. Bien que ce ne soit en thorie pas ncessaire, cela vous vitera des ennuis ou des confusions par la suite.

Il est par ailleurs possible d'insrer au sein de la dfinition d'une servlet d'autres balises facultatives : Code : XML - Dclaration de notre servlet avec options <servlet> <servlet-name>Test</servlet-name> <servlet-class>com.sdzee.servlets.Test</servlet-class>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<description>Ma premire servlet de test.</description> <init-param> <param-name>auteur</param-name> <param-value>Coyote</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>

44/606

On dcouvre ici trois nouveaux blocs : <description> permet de dcrire plus amplement le rle de la servlet. Cette description n'a aucune utilit technique et n'est visible que dans ce fichier ; <init-param> permet de prciser des paramtres qui seront accessibles la servlet lors de son chargement. Nous y reviendrons en dtail plus tard dans ce cours ; <load-on-startup> permet de forcer le chargement de la servlet ds le dmarrage du serveur. Nous reviendrons sur cet aspect un peu plus loin dans ce chapitre.

Mapping de la servlet
Il faut ensuite faire correspondre notre servlet frachement dclare une URL, afin qu'elle soit joignable par les clients : Code : XML - Mapping de notre servlet sur l'URL relative /toto <servlet-mapping> <servlet-name>Test</servlet-name> <url-pattern>/toto</url-pattern> </servlet-mapping>

La balise responsable de la dfinition du mapping se nomme logiquement <servlet-mapping>, et les deux balises obligatoires de cette section sont, l encore, trs explicites. <servlet-name> permet de prciser le nom de la servlet laquelle faire rfrence. Cette information doit correspondre avec le nom dfini dans la prcdente dclaration de la servlet. <url-pattern> permet de prciser la ou les URL relatives au travers desquelles la servlet sera accessible. Ici, a sera /toto ! Pourquoi un "pattern" et pas simplement une URL ?

En effet il s'agit bien d'un pattern, c'est--dire d'un modle, et pas ncessairement d'une URL fixe. Ainsi, on peut choisir de rendre notre servlet responsable du traitement des requtes issues d'une seule URL, ou bien d'un groupe d'URL. V ous n'imaginez pour le moment peut-tre pas de cas qui impliqueraient qu'une servlet doive traiter les requtes issues de plusieurs URL, mais rassurez-vous nous ferons la lumire sur ce type d'utilisation dans la partie suivante de ce cours. De mme, nous dcouvrirons qu'il est tout fait possible de dclarer plusieurs sections <servlet-mapping> pour une mme section <servlet> dans le fichier web.xml . Que signifie "URL relative" ?

Cela veut dire que l'URL ou le pattern que vous renseignez dans le champ <url-pattern> sont bass sur le contexte de votre application. Dans notre cas, souvenez-vous du contexte de dploiement que nous avons prcis lorsque nous avons cr notre projet web : nous l'avions appel test. Nous en dduisons donc que notre <url-pattern>/toto</url-pattern> fait rfrence l'URL absolue /test/toto. Nous y voil, notre servlet est maintenant joignable par le client via l'URL http://localhost:8080/test/toto.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


Pour information, le code final de notre fichier web.xml est donc : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>Test</servlet-name> <servlet-class>com.sdzee.servlets.Test</servlet-class> </servlet> <servlet-mapping> <servlet-name>Test</servlet-name> <url-pattern>/toto</url-pattern> </servlet-mapping> </web-app>

45/606

L'ordre des sections de dclaration au sein du fichier est important : il est impratif de dfinir une servlet avant de spcifier son mapping.

Mise en service Do you GET it?


Nous venons de crer un fichier de configuration pour notre application, nous devons donc redmarrer notre serveur pour que ces modifications soient prises en compte. Il suffit pour cela de cliquer sur le bouton "start" de l'onglet Servers , comme indiqu la figure suivante.

Bouton de

redmarrage du serveur Tomcat dans Eclipse Faisons le test, et observons ce que nous affiche notre navigateur lorsque nous tentons d'accder l'URL http://localhost:8080/test/toto que nous venons de mapper sur notre servlet (voir la figure suivante).

Mthode HTTP non supporte Nous voici devant notre premier code de statut HTTP. En l'occurrence, c'est la fois une bonne et une mauvaise nouvelle : une bonne nouvelle, car cela signifie que notre mapping a fonctionn et que notre serveur a bien contact notre servlet ! une mauvaise nouvelle, car notre serveur nous retourne le code d'erreur 405 et nous prcise que la mthode GET n'est pas supporte par la servlet que nous avons associe l'URL

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

46/606

Par qui a t gnre cette page d'erreur ?

Tout est parti du conteneur de servlets. D'ailleurs, ce dernier effectue pas mal de choses dans l'ombre, sans vous le dire ! Dans ce cas prcis, il a : 1. reu la requte HTTP depuis le serveur web ; 2. gnr un couple d'objets requte/rponse ; 3. parcouru le fichier web.xml de votre application la recherche d'une entre correspondant l'URL contenue dans l'objet requte ; 4. trouv et identifi la servlet que vous y avez dclare ; 5. contact votre servlet et transmis la paire d'objets requte/rponse. Dans ce cas, pourquoi cette page d'erreur a-t-elle t gnre ?

Nous avons pourtant bien fait hriter notre servlet de la classe HttpServlet, notre servlet doit pouvoir interagir avec HTTP ! Qu'est-ce qui cloche ? Eh bien nous avons oubli une chose importante : afin que notre servlet soit capable de traiter une requte HTTP de type GET, il faut y implmenter une mthode doGet() ! Souvenez-vous, je vous ai dj expliqu que la mthode service() de la classe HttpServlet s'occupera alors elle-mme de transmettre la requte GET entrante vers la mthode doGet() de notre servlet a vous revient ? Maintenant, comment cette page d'erreur a-t-elle t gnre ?

C'est la mthode doGet() de la classe mre HttpServlet qui est en la cause. Ou plutt, disons que c'est grce elle ! En effet, le comportement par dfaut des mthodes doXXX() de la classe HttpServlet est de renvoyer un code d'erreur HTTP 405 ! Donc si le dveloppeur a bien fait son travail, pas de problme : c'est bien la mthode doXXX() de la servlet qui sera appele. Par contre, s'il a mal fait son travail et a oubli de surcharger la mthode doXXX() voulue, alors c'est la mthode de la classe mre HttpServlet qui sera appele, et un code d'erreur sera gentiment et automatiquement renvoy au client. Ainsi, la classe mre s'assure toujours que sa classe fille - votre servlet ! - surcharge bien la mthode doXXX() correspondant la mthode HTTP traite ! Par ailleurs, votre conteneur de servlets est galement capable de gnrer lui-mme des codes d'erreur HTTP. Par exemple, lorsqu'il parcourt le fichier web.xml de votre application la recherche d'une entre correspondant l'URL envoye par le client, et qu'il ne trouve rien, c'est lui qui va se charger de gnrer le fameux code d'erreur 404 !

Nous voil maintenant au courant de ce qu'il nous reste faire : il nous suffit de surcharger la mthode doGet() de la classe HttpServlet dans notre servlet Test. V oici donc le code de notre servlet : Code : Java - Surcharge de la mthode doGet() dans notre servlet Test package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class Test extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ } }

Comme vous pouvez le constater, l'ajout de cette seule mthode vide fait intervenir plusieurs imports qui dfinissent les objets et

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


exceptions prsents dans la signature de la mthode : HttpServletRequest, HttpServletResponse, ServletException et IOException.

47/606

Ressayons alors de contacter notre servlet via notre URL : tout se passe comme prvu, le message d'erreur HTTP disparat. Cela dit, notre servlet ne fait strictement rien de la requte HTTP reue : le navigateur nous affiche alors une page blanche ! Comment le client sait-il que la requte est arrive bon port ?

C'est une trs bonne remarque. En effet, si votre navigateur vous affiche une simple page blanche, c'est parce qu'il considre la requte comme termine avec succs : si ce n'tait pas le cas, il vous afficherait un des codes et messages d'erreur HTTP (voir la figure suivante). Si vous utilisez le navigateur Firefox, vous pouvez utiliser l'onglet Rseau de l'outil Firebug pour visualiser qu'effectivement, une rponse HTTP est bien reue par votre navigateur (si vous utilisez le navigateur Chrome, vous pouvez accder un outil similaire en appuyant sur F12 ).

En-ttes de la rponse HTTP avec Firebug On y observe : un code HTTP 200 OK, qui signifie que la requte s'est effectue avec succs ; la longueur des donnes contenues dans la rponse (Content-Length ) : 0... Eh bien encore une fois, c'est le conteneur de servlets qui a fait le boulot sans vous prvenir ! Quand il a gnr la paire d'objets requte/rponse, il a initialis le statut de la rponse avec une valeur par dfaut : 200. C'est--dire que par dfaut, le conteneur de servlets cre un objet rponse qui stipule que tout s'est bien pass. Ensuite, il transmet cet objet votre servlet, qui est alors libre de le modifier sa guise. Lorsqu'il reoit nouveau l'objet en retour, si le code de statut n'a pas t modifi par la servlet, c'est que tout s'est bien pass. En d'autres termes, le conteneur de servlets adopte une certaine philosophie : pas de nouvelles, bonne nouvelle !

Le serveur retourne donc toujours une rponse au client, peu importe ce que fait notre servlet avec la requte ! Dans notre cas, la servlet n'effectue aucune modification sur l'objet HttpServletResponse, et par consquent n'y insre aucune donne et n'y modifie aucun en-tte. D'o la longueur initialise zro dans l'en-tte de la rponse, le code de statut initialis 200 et la page blanche en guise de rsultat final !

Cycle de vie d'une servlet


www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

48/606

Dans certains cas, il peut s'avrer utile de connatre les rouages qui se cachent derrire une servlet. Toutefois, je ne souhaite pas vous embrouiller ds maintenant : vous n'en tes qu'aux balbutiements de votre apprentissage et n'avez pas assez d'exprience pour intervenir proprement sur l'initialisation d'une servlet. Je ne vais par consquent qu'aborder rapidement son cycle de vie au sein du conteneur, travers ce court apart. Nous lverons le voile sur toute cette histoire dans un chapitre en annexe de ce cours, et en profiterons pour utiliser le puissant outil de debug d'Eclipse !

Quand une servlet est demande pour la premire fois ou quand l'application web dmarre, le conteneur de servlets va crer une instance de celle-ci et la garder en mmoire pendant toute l'existence de l'application. La mme instance sera rutilise pour chaque requte entrante dont les URL correspondent au pattern d'URL dfini pour la servlet. Dans notre exemple, aussi longtemps que notre serveur restera en ligne, tous nos appels vers l'URL /test/toto seront dirigs vers la mme et unique instance de notre servlet, gnre par Tomcat lors du tout premier appel. En fin de compte, l'instance d'une servlet est-elle cre lors du premier appel cette servlet, ou bien ds le dmarrage du serveur ? Ceci dpend en grande partie du serveur d'applications utilis. Dans notre cas, avec Tomcat, c'est par dfaut au premier appel d'une servlet que son unique instance est cre. Toutefois, ce mode de fonctionnement est configurable. Plus tt dans ce chapitre, je vous expliquais comment dclarer une servlet dans le fichier web.xml, et j'en ai profit pour vous prsenter une balise facultative : <load-on-startup>N</loadon-startup>, o N doit tre un entier positif. Si dans la dclaration d'une servlet vous ajoutez une telle ligne, alors vous ordonnez au serveur de charger l'instance de la servlet en question directement pendant le chargement de l'application. Le chiffre N correspond la priorit que vous souhaitez donner au chargement de votre servlet. Dans notre projet nous n'utilisons pour le moment qu'une seule servlet, donc nous pouvons marquer n'importe quel chiffre suprieur ou gal zro, a ne changera rien. Mais dans le cas d'une application contenant beaucoup de servlets, cela permet de dfinir quelle servlet doit tre charge en premier. L'ordre est tabli du plus petit au plus grand : la ou les servlets ayant un load-on-startup initialis zro sont les premires tre charges, puis 1, 2, 3, etc. V oil tout pour cet apart. En ce qui nous concerne, nous n'utiliserons pas cette option de chargement dans nos projets, le chargement des servlets lors de leur premire sollicitation nous ira trs bien !

Envoyer des donnes au client


Avec tout cela, nous n'avons encore rien envoy notre client, alors qu'en mettant en place une simple page HTML nous avions affich du texte dans le navigateur du client en un rien de temps. Patience, les rponses vont venir Utilisons notre servlet pour reproduire la page HTML statique que nous avions cre lors de la mise en place de Tomcat. Comme je vous l'ai expliqu dans le paragraphe prcdent, pour envoyer des donnes au client il va falloir manipuler l'objet HttpServletResponse. Regardons d'abord ce qu'il est ncessaire d'inclure notre mthode doGet(), et analysons tout cela ensuite : Code : Java public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ response.setContentType("text/html"); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE html>"); out.println("<html>"); out.println("<head>"); out.println("<meta charset=\"utf-8\" />"); out.println("<title>Test</title>"); out.println("</head>"); out.println("<body>"); out.println("<p>Ceci est une page gnre depuis une servlet.</p>"); out.println("</body>"); out.println("</html>"); }

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


Comment procdons-nous ?

49/606

1. Nous commenons par modifier l'en-tte Content-Type de la rponse HTTP, pour prciser au client que nous allons lui envoyer une page HTML, en faisant appel la mthode setContentType() de l'objet HttpServletResponse. 2. Par dfaut, l'encodage de la rponse envoye au client est initialis ISO-8859-1 . Si vous faites quelques recherches au sujet de cet encodage, vous apprendrez qu'il permet de grer sans problme les caractres de notre alphabet, mais qu'il ne permet pas de manipuler les caractres asiatiques, les alphabets arabes, cyrilliques, scandinaves ainsi que d'autres caractres plus exotiques. Afin de permettre une gestion globale d'un maximum de caractres diffrents, il est recommand d'utiliser l'encodage UTF-8 la place. V oil pourquoi nous modifions l'encodage par dfaut en ralisant un appel la mthode setCharacterEncoding() de l'objet HttpServletResponse. Par ailleurs, c'est galement pour cette raison que je vous ai fait modifier les encodages par dfaut lors de la configuration d'Eclipse ! Si vous regardez la documentation de cette mthode, vous dcouvrirez qu'il est galement possible de s'en passer et d'initialiser l'encodage de la rponse directement via un appel la mthode setContentType( "text/html; charset=UTF-8"). 3. Nous rcuprons ensuite un objet PrintWriter qui va nous permettre d'envoyer du texte au client, via la mthode getWriter() de l'objet HttpServletResponse. V ous devrez donc importer java.io.PrintWriter dans votre servlet. Cet objet utilise l'encodage que nous avons dfini prcdemment, c'est--dire UTF-8. 4. Nous crivons alors du texte dans la rponse via la mthode println() de l'objet PrintWriter. Enregistrez, testez et vous verrez enfin la page s'afficher dans votre navigateur : a y est, vous savez maintenant utiliser une servlet et transmettre des donnes au client.

Rien que pour reproduire ce court et pauvre exemple, il nous a fallu 10 appels out.println() ! Lorsque nous nous attaquerons des pages web un peu plus complexes que ce simple exemple, allons-nous devoir crire tout notre code HTML l'intrieur de ces mthodes println() ? Non, bien sr que non ! V ous imaginez un peu l'horreur si c'tait le cas ?! Si vous avez suivi le topo sur MVC, vous vous souvenez d'ailleurs que la servlet n'est pas cense s'occuper de l'affichage, c'est la vue qui doit s'en charger ! Et c'est bien pour a que je ne vous ai rien fait envoyer d'autre que cette simple page d'exemple HTML Toutefois, mme si nous ne procderons plus jamais ainsi pour la cration de nos futures pages web, il tait trs important que nous dcouvrions comment cela se passe. Pour le moment, voici la figure suivante ce que nous avons ralis.

Servlet seule Note : dornavant et afin d'allger les schmas, je ne reprsenterai plus le serveur HTTP en amont du conteneur. Ici, le bloc intitul "Serveur" correspond en ralit au conteneur de servlets. Pour information, nous nous resservirons plus tard de cette technique d'envoi direct de donnes depuis une servlet, lorsque nous manipulerons des fichiers.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

50/606

La leon retenir en cette fin de chapitre est claire : le langage Java n'est pas du tout adapt la rdaction de pages web ! Notre dernier exemple en est une excellente preuve, et il nous faut nous orienter vers quelque chose de plus efficace. Il est maintenant grand temps de revenir au modle MVC : l'affichage de contenu HTML n'ayant rien faire dans le contrleur (notre servlet), nous allons crer une vue et la mettre en relation avec notre servlet. Le client envoie des requtes au serveur grce aux mthodes du protocole HTTP, notamment GET, POST et HEAD. Le conteneur web place chaque requte reue dans un objet HttpServletRequest, et place chaque rponse qu'il initialise dans l'objet HttpServletResponse. Le conteneur transmet chaque couple requte/rponse une servlet : c'est un objet Java assign une requte et capable de gnrer une rponse en consquence. La servlet est donc le point d'entre d'une application web, et se dclare dans son fichier de configuration web.xml. Une servlet peut se charger de rpondre une requte en particulier, ou un groupe entier de requtes. Pour pouvoir traiter une requte HTTP de type GET, une servlet doit implmenter la mthode doGet() ; pour rpondre une requte de type POST, la mthode doPost() ; etc. Une servlet n'est pas charge de l'affichage des donnes, elle ne doit donc pas s'occuper de la prsentation (HTML, CSS, etc.).

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

51/606

Servlet avec vue


Le modle MVC nous conseille de placer tout ce qui touche l'affichage final (texte, mise en forme, etc.) dans une couche part : la vue. Nous avons rapidement survol dans la premire partie de ce cours comment ceci se concrtisait en Java EE : la technologie utilise pour raliser une vue est la page JSP. Nous allons dans ce chapitre dcouvrir comment fonctionne une telle page, et apprendre en mettre une en place au sein de notre embryon d'application.

Introduction aux JSP


quoi ressemble une page JSP ?

C'est un document qui, premire vue, ressemble beaucoup une page HTML, mais qui en ralit en diffre par plusieurs aspects : l'extension d'une telle page devient .jsp et non plus .html ; une telle page peut contenir des balises HTML, mais galement des balises JSP qui appellent de manire transparente du code Java ; contrairement une page HTML statique directement renvoye au client, une page JSP est excute ct serveur , et gnre alors une page renvoye au client. L'intrt est de rendre possible la cration de pages dynamiques : puisqu'il y a une tape de gnration sur le serveur, il devient possible de faire varier l'affichage et dinteragir avec l'utilisateur, en fonction notamment de la requte et des donnes reues ! Bien que la syntaxe d'une page JSP soit trs proche de celle d'une page HTML, il est thoriquement possible de gnrer n'importe quel type de format avec une page JSP : du HTML donc, mais tout aussi bien du CSS, du XML, du texte brut, etc. Dans notre cas, dans la trs grande majorit des cas d'utilisation il s'agira de pages HTML destines l'affichage des donnes de l'application sur le navigateur du client.

Ne vous fiez pas au titre de ce sous-chapitre, nous n'allons pas pour le moment nous intresser la technologie JSP en ellemme, ceci faisant l'objet des chapitres suivants. Nous allons nous limiter l'tude de ce qu'est une JSP, de la manire dont elle est interprte par notre serveur et comment elle s'insre dans notre application.

Nature d'une JSP


Quoi ?
Les pages JSP sont une des technologies de la plate-forme Java EE les plus puissantes, simples utiliser et mettre en place. Elles se prsentent sous la forme d'un simple fichier au format texte, contenant des balises respectant une syntaxe part entire. Le langage JSP combine la fois les technologies HTML, XML, servlet et JavaBeans (nous reviendrons sur ce terme plus tard, pour le moment retenez simplement que c'est un objet Java) en une seule solution permettant aux dveloppeurs de crer des vues dynamiques.

Pourquoi ?
Pour commencer, mettons noir sur blanc les raisons de l'existence de cette technologie. La technologie servlet est trop difficile d'accs et ne convient pas la gnration du code de prsentation : nous l'avons soulign en fin de chapitre prcdent, crire une page web en langage Java est horriblement pnible. Il est ncessaire de disposer d'une technologie qui joue le rle de simplification de l'API servlet : les pages JSP sont en quelque sorte une abstraction "haut niveau" de la technologie servlet. Le modle MVC recommande une sparation nette entre le code de contrle et la prsentation. Il est thoriquement envisageable d'utiliser certaines servlets pour effectuer le contrle, et d'autres pour effectuer l'affichage, mais nous rejoignons alors le point prcdent : la servlet n'est pas adapte la prise en charge de l'affichage Le modle MVC recommande une sparation nette entre le code mtier et la prsentation : dans le modle on doit trouver le code Java responsable de la gnration des lments dynamiques, et dans la vue on doit simplement trouver l'interface utilisateur ! Ceci afin notamment de permettre aux dveloppeurs et designers de travailler facilement sur la vue, sans avoir y faire intervenir directement du code Java.

Comment ?

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

52/606

On peut rsumer la technologie JSP en une technologie offrant les capacits dynamiques des servlets tout en permettant une approche naturelle pour la cration de contenus statiques. Ceci est rendu possible par : un langage ddi : les pages JSP sont des documents au format texte, l'oppos des classes Java que sont les servlets, qui dcrivent indirectement comment traiter une requte et construire une rponse. Elles contiennent des balises qui combinent la fois simplicit et puissance, via une syntaxe simple, semblable au HTML et donc aisment comprhensible par un humain ; la simplicit d'accs aux objets Java : des balises du langage rendent l'utilisation directe d'objets au sein d'une page trs aise ; des mcanismes permettant l'extension du langage utilis au sein des pages JSP : il est possible de mettre en place des balises qui n'existent pas dans le langage JSP, afin d'augmenter les fonctionnalits accessibles. Pas de panique, a parat complexe a priori mais nous y reviendrons calmement dans la partie concernant la JSTL, et tout cela n'aura bientt plus aucun secret pour vous ! Bon, assez gamberg ! Maintenant que nous avons une bonne ide de ce que sont les pages JSP, rentrons dans le concret en tudiant leur vie au sein d'une application !

Mise en place d'une JSP Cration de la vue


Le contexte tant pos, nous pouvons maintenant crer notre premire page JSP. Pour ce faire, depuis votre projet Eclipse faites un clic droit sur le dossier WebContent de votre projet, puis choisissez New > JSP File, et dans la fentre qui apparat renseignez le nom de votre page JSP, ainsi qu'indiqu aux figures suivantes.

Cration

d'une page JSP - tape 1

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

53/606

Cration d'une page JSP - tape 2

Une page JSP par dfaut est alors gnre par Eclipse : supprimez tout son contenu, et remplacez-le par notre modle d'exemple : Code : HTML - test.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> </body> </html>

Rendez-vous ensuite dans la barre d'adresses de votre navigateur, et entrez l'URL correspondant la page que vous venez de crer : Code : URL http://localhost:8080/test/test.jsp

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

54/606

Nous obtenons alors le rsultat de la figure suivante.

Qu'est-ce que c'est que cette horreur ? Que s'est-il pass ?

Le problme que vous voyez ici, c'est l'encodage ! Eh oui, dans le chapitre prcdent nous avions modifi l'encodage par dfaut directement depuis notre servlet, et je ne vous avais alors pas expliqu pourquoi c'tait vraiment ncessaire de procder ainsi, je vous avais uniquement expliqu l'intrt d'utiliser UTF-8. Cette fois dans notre JSP, nous n'avons rien modifi. Par consquent, la rponse cre par notre page utilise la valeur par dfaut, c'est--dire l'encodage latin ISO-8859-1.

Si l'encodage latin est utilis par dfaut, alors pourquoi les lettres accentues ne s'affichent-elles pas correctement ? Ce sont bien des caractres de notre alphabet ! Nous y voil, vous devez bien comprendre comment le principe d'encodage fonctionne. Il s'agit d'un processus en deux tapes, avec d'une part la manire dont sont encods et grs les caractres sur le serveur, et d'autre part la manire dont les donnes contenues dans la rponse envoye vont tre interprtes par le navigateur : en ce qui concerne le serveur, c'est simple : si vous avez bien suivi mes conseils lors de la configuration d'Eclipse, vos fichiers source sont tous encods en UTF-8 ; en ce qui concerne le navigateur, celui-ci va uniquement se baser sur le contenu de l'en-tte Content-type de la rponse HTTP afin d'interprter les donnes reues. Or, comme je viens de vous le dire, votre page JSP a par dfaut envoy la rponse au client en spcifiant l'encodage latin dans l'en-tte HTTP. V oil donc l'explication de l'affreux micmac observ : votre navigateur a essay d'afficher des caractres encods en UTF-8 en utilisant l'encodage latin ISO-8859-1, et il se trouve que l'encodage des caractres accentus en ISO n'a rien voir avec celui des caractres accentus en UTF ! D'o les symboles bizarrodes observs Comment modifier l'en-tte HTTP depuis notre page JSP, afin de faire savoir au navigateur qu'il doit utiliser l'encodage UTF-8 pour interprter les donnes reues ? Pour cela, il va falloir ajouter une instruction en tte de votre page JSP. Je ne vais pas vous l'expliquer ds maintenant, je reviendrai sur son fonctionnement dans un chapitre venir. Contentez-vous simplement d'ajouter cette ligne tout en haut du code de votre page pour le moment : Code : JSP <%@ page pageEncoding="UTF-8" %>

Une fois la modification effectue et enregistre, actualisez la page dans votre navigateur et vous constaterez qu'il vous affiche maintenant correctement le texte attendu. Tout va bien donc, notre JSP est fonctionnelle !

Cycle de vie d'une JSP


En thorie
Tout tient en une seule phrase : quand une JSP est demande pour la premire fois, ou quand l'application web dmarre, le conteneur de servlets va vrifier, traduire puis compiler la page JSP en une classe hritant de HttpServlet , et l'utiliser durant l'existence de l'application. Cela signifie-t-il qu'une JSP est littralement transforme en servlet par le serveur ?

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

55/606

C'est exactement ce qui se passe. Lors de la demande d'une JSP, le moteur de servlets va excuter la classe JSP auparavant traduite et compile et envoyer la sortie gnre (typiquement, une page HTML/CSS/JS) depuis le serveur vers le client travers le rseau, sortie qui sera alors affiche dans son navigateur ! Pourquoi ?

Je vous l'ai dj dit, la technologie JSP consiste en une vritable abstraction de la technologie servlet : cela signifie concrtement que les JSP permettent au dveloppeur de faire du Java sans avoir crire de code Java ! Bien que cela paraisse magique, rassurez-vous il n'y a pas de miracles : vous pouvez voir le code JSP crit par le dveloppeur comme une succession de raccourcis en tous genres qui, dans les coulisses, appellent en ralit des portions de code Java toutes prtes ! Que se passe-t-il si le contenu d'une page JSP est modifi ? Que devient la servlet auto-gnre correspondante ?

C'est une trs bonne question ! une JSP :

V oici ce qui se passe au sein du conteneur de servlets lorsqu'une requte HTTP est destine

le conteneur vrifie si la JSP a dj t traduite et compile en une servlet : si non, il vrifie la syntaxe de la page, la traduit en une servlet (du code Java) et la compile en une classe excutable prte l'emploi ; si oui, il vrifie que l'ge de la JSP et de la servlet est identique : si non, cela signifie que la JSP est plus rcente que la servlet et donc qu'il y a eu modification, le conteneur effectue alors nouveau les tches de vrification, traduction et compilation ; il charge ensuite la classe gnre, en cre une instance et l'excute pour traiter la requte. J'ai reprsent cette suite de dcisions sur la figure suivante, afin de vous faciliter la comprhension du cycle.

Cycle de vie d'une JSP

De tout cela, il faut retenir que le processus initial de vrification/traduction/compilation n'est pas effectu chaque appel ! La servlet gnre et compile tant sauvegarde, les appels suivants la JSP sont beaucoup plus rapides : le conteneur se contente d'excuter directement l'instance de la servlet stocke en mmoire.

En pratique
Avant de passer la suite, revenons sur cette histoire de traduction en servlet. Je vous ai dit que le conteneur de servlets, en l'occurrence ici Tomcat, gnrait une servlet partir de votre JSP. Eh bien sachez que vous pouvez trouver le code source ainsi

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


gnr dans le rpertoire de travail du serveur : sous Tomcat, il s'agit du rpertoire /work . Qu'en est-il de notre premire JSP ? Existe-t-il une servlet auto-gnre depuis nos quelques lignes de texte ?

56/606

La rponse est oui bien entendu, partir du moment o vous avez appel au moins une fois cette JSP depuis votre navigateur ! Cette servlet est bien prsente dans le rpertoire de travail de Tomcat, seulement comme nous grons notre serveur directement depuis Eclipse, par dfaut ce dernier va en quelque sorte prendre la main sur Tomcat, et mettre tous les fichiers dans un rpertoire lui ! Le fourbe... Bref, voil o se trouve ma servlet pour cet exemple : Code : URI

C:\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\ C:\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\

Elle doit se situer sensiblement au mme endroit chez vous, un ou deux noms de dossier prs selon la configuration que vous avez mise en place et bien entendu selon le systme d'exploitation que vous utilisez. V ous remarquerez que Tomcat suffixe les servlets qu'il auto-gnre partir de pages JSP avec "_jsp". Je vous conseille alors d'diter le fichier .java et de consulter les sources gnres, c'est un exercice trs formateur pour vous que de tenter de comprendre ce qui y est fait : n'oubliez pas, la Javadoc est votre amie pour comprendre les mthodes qui vous sont encore inconnues. Ne prenez surtout pas peur devant ce qui s'apparente un joyeux bordel, et passez faire un tour sur le forum Java si vous avez des questions prcises sur ce qui s'y trouve !

Sans surprise, au milieu du code gnr par Tomcat nous retrouvons bien des instructions trs semblables celles que nous avions d crire dans notre servlet dans le chapitre prcdent, et qui correspondent cette fois ce que nous avons crit dans notre JSP : Code : Java - Extrait de la servlet test_jsp.java auto-gnre par Tomcat depuis notre page test.jsp out.write("<!DOCTYPE html>\r\n"); out.write("<html>\r\n"); out.write(" <head>\r\n"); out.write(" <meta charset=\"utf-8\" />\r\n"); out.write(" <title>Test</title>\r\n"); out.write(" </head>\r\n"); out.write(" <body>\r\n"); out.write(" <p>Ceci est une page gnre depuis une JSP.</p>\r\n"); out.write(" </body>\r\n"); out.write("</html>");

Retenez bien que c'est cette classe Java qui est compile et excute lorsque votre JSP est appele. Ce court apart se termine ici, dornavant nous ne nous proccuperons plus de ce qui se trame dans les coulisses de notre serveur : nous aurons bien assez de travail avec nos JSP et le reste !

Mise en relation avec notre servlet


Garder le contrle
Dans l'exemple que nous venons de raliser, nous nous sommes contents d'afficher du texte statique, et avons visualis le rsultat en appelant directement la page JSP depuis son URL. C'tait pratique pour le coup, mais dans une application Java EE il ne faut jamais procder ainsi ! Pourquoi ? La rponse tient en trois lettres : MVC. Ce modle de conception nous recommande en effet la mise en place d'un contrleur, et nous allons donc tcher de toujours associer une servlet une vue. Mais je viens de vous montrer qu'une JSP tait de toute faon traduite en servlet Quel est l'intrt de mettre en place une autre servlet ? Une JSP est en effet automatiquement traduite en servlet, mais attention ne pas confondre : les contrleurs du MVC sont bien

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

57/606

reprsents en Java EE par des servlets, mais cela ne signifie pas pour autant que toute servlet joue le rle d'un contrleur dans une application Java EE. En l'occurrence, les servlets rsultant de la traduction des JSP dans une application n'ont pour rle que de permettre la manipulation des requtes et rponses HTTP. En aucun cas elles n'interviennent dans la couche de contrle, elles agissent de manire transparente et font bien partie de la vue : ce sont simplement des traductions en un langage que comprend le serveur (le Java !) des vues prsentes dans votre application (de simples fichiers textes contenant de la syntaxe JSP). Dornavant, nous allons donc systmatiquement crer une servlet lorsque nous crerons une page JSP. a peut vous sembler pnible au dbut, mais c'est une bonne pratique prendre ds maintenant : vous gardez ainsi le contrle, en vous assurant qu'une vue ne sera jamais appele par le client sans tre passe travers une servlet. Souvenez-vous : la servlet est le point d'entre de votre application !

Nous allons mme pousser le vice plus loin, et dplacer notre page JSP dans le rpertoire /WEB-INF. Si vous vous souvenez de ce que je vous ai dit dans le chapitre sur la configuration de Tomcat, vous savez que ce dossier a une particularit qui nous intresse : il cache automatiquement les ressources qu'il contient. En d'autres termes, une page prsente sous ce rpertoire n'est plus accessible directement par une URL ct client ! Il devient alors ncessaire de passer par une servlet ct serveur pour donner l'accs cette page Plus d'oubli possible ! Faites le test. Essayez depuis une URL de joindre votre page JSP aprs l'avoir dplace sous /WEB-INF : vous n'y arriverez pas ! Nous devons donc associer notre servlet notre vue. Cette opration est ralise depuis la servlet, ce qui est logique puisque c'est elle qui dcide d'appeler la vue. Reprenons notre servlet, vidons la mthode doGet() du contenu que nous avons depuis fait migrer dans la JSP, et regardons le code mettre en place pour effectuer l'association : Code : Java - com.sdzee.servlets.Test ... public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

Analysons ce qui se passe : depuis notre instance de servlet (this), nous appelons la mthode getServletContext(). Celle-ci nous retourne alors un objet ServletContext, qui fait rfrence au contexte commun toute l'application : celui-ci contient un ensemble de mthodes qui permettent une servlet de communiquer avec le conteneur de servlet ; celle qui nous intresse ici est la mthode permettant de manipuler une ressource, getRequestDispatcher(), que nous appliquons notre page JSP. Elle retourne un objet RequestDispatcher, qui agit ici comme une enveloppe autour de notre page JSP. V ous pouvez considrer cet objet comme la pierre angulaire de votre servlet : c'est grce lui que notre servlet est capable de faire suivre nos objets requte et rponse une vue. Il est impratif d'y prciser le chemin complet vers la JSP, en commenant obligatoirement par un / (voir l'avertissement et la prcision ci-dessous) ; nous utilisons enfin ce dispatcher pour rexpdier la paire requte/rponse HTTP vers notre page JSP via sa mthode forward(). De cette manire notre page JSP devient accessible au travers de la servlet ; d'ailleurs, notre servlet ne faisant actuellement rien d'autre, son seul rle est de transfrer le couple requte reue et rponse vers la JSP finale. Ne soyez pas leurrs : comme je vous l'ai dj dit lorsque je vous ai prsent la structure d'un projet, le dossier WebContent existe uniquement dans Eclipse ! Je vous ai dj expliqu qu'il correspondait en ralit la racine de l'application, et c'est donc pour a qu'il faut bien crire /WEB-INF/test.jsp en argument de la mthode getRequestDispatcher(), et non pas /WebContent/WEB-INF/test.jsp ! retenir donc : nulle part dans votre code ne doit tre mentionn le rpertoire WebContent ! C'est une reprsentation de la racine de l'application, propre Eclipse uniquement. Si vous parcourez la documentation de l'objet HttpServletRequest, vous remarquerez qu'il contient lui aussi une mthode getRequestDispatcher() ! Toutefois, cette dernire prsente une diffrence notable : elle peut prendre en argument un chemin relatif, alors que sa grande sur n'accepte qu'un chemin complet. Je vous conseille, afin d'viter

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

58/606

de vous emmler les crayons, de passer par la mthode que je vous prsente ci-dessus. Quand vous serez plus l'aise avec ces histoires de chemins relatifs et absolus, vous pourrez alors dcider d'utiliser l'une ou l'autre de ces mthodes.

Faites nouveau le test, en essayant cette fois d'appeler l'URL correspondant votre servlet (souvenez-vous, c'est celle que nous avons mise en place dans le fichier web.xml, savoir /test/toto) : tout fonctionne, notre requte est bien achemine jusqu' notre JSP, et en retour notre navigateur nous affiche bien le contenu de la page ! V oici la figure suivante ce que nous venons de raliser.

Servlet + JSP Une page JSP ressemble en apparence une page HTML, mais en ralit elle est bien plus proche d'une servlet : elle contient des balises derrire lesquelles se cache du code Java. Une page JSP est excute sur le serveur, et la page finale gnre et envoye au client est une simple page HTML : le client ne voit pas le code de la JSP. Idalement dans le modle MVC, une page JSP est accessible l'utilisateur travers une servlet, et non pas directement. Le rpertoire /WEB-INF cache les fichiers qu'il contient l'extrieur de l'application. La mthode forward() de l'objet RequestDispatcher permet depuis une servlet de rediriger la paire requte/rponse HTTP vers une autre servlet ou vers une page JSP.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

59/606

Transmission de donnes
Nous nous sommes jusqu' prsent contents d'afficher une page web au contenu fig, comme nous l'avions fait via une simple page HTML crite en dur en tout dbut de cours. Seulement cette fois, notre contenu est prsent dans une page JSP laquelle nous avons associ une servlet. Nous disposons ainsi de tout ce qui est ncessaire pour ajouter du dynamisme notre projet. Il est donc grand temps d'apprendre faire communiquer entre eux les diffrents lments constituant notre application !

Donnes issues du serveur : les attributs


Transmettre des variables de la servlet la JSP
Jusqu' prsent nous n'avons pas fait grand-chose avec notre requte HTTP, autrement dit avec notre objet HttpServletRequest : nous nous sommes contents de le transmettre la JSP. Pourtant, vous avez d vous en apercevoir lorsque vous avez parcouru sa documentation, ce dernier contient normment de mthodes ! Puisque notre requte HTTP passe maintenant au travers de la servlet avant d'tre transmise la vue, profitons-en pour y apporter quelques modifications ! Utilisons donc notre servlet pour mettre en place un semblant de dynamisme dans notre application : crons une chane de caractres depuis notre servlet, et transmettons-la notre vue pour affichage. Code : Java - Transmission d'une variable public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ String message = "Transmission de variables : OK !"; request.setAttribute( "test", message ); this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

Comme vous pouvez le constater, le principe est trs simple et tient en une ligne (la ligne 3). Il suffit d'appeler la mthode setAttribute() de l'objet requte pour y enregistrer un attribut ! Cette mthode prend en paramtre le nom que l'on souhaite donner l'attribut suivi de l'objet lui-mme. Ici, l'attribut que j'ai cr est une simple chane de caractres - un objet de type String - que j'ai choisi de nommer test lors de son enregistrement dans la requte. Ne confondez pas le nom que vous donnez votre objet au sein du code et le nom que vous donnez l'attribut au sein de la requte. Ici mon objet se nomme message mais j'ai nomm par la suite test l'attribut qui contient cet objet dans la requte. Ct vue, c'est par ce nom d'attribut que vous pourrez accder votre objet !

C'est tout ce qu'il est ncessaire de faire ct servlet. Regardons maintenant comment rcuprer et afficher l'objet ct vue : Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); out.println( attribut ); %> </p> </body> </html>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

60/606

Ne paniquez pas, vous ne connaissez pas cette notation (lignes 11 14) et c'est bien normal puisque je ne vous l'ai pas encore prsente V oici donc une premire information concernant la technologie JSP : elle permet d'inclure du code Java dans une page en entourant ce code des balises <% et %> . Ce sont des marqueurs qui dlimitent les portions contenant du code Java du reste de la page, contenant ici simplement des balises HTML et du texte. l'intrieur, tout se passe comme si on crivait du code directement dans une servlet : on fait appel la mthode println() de l'objet PrintWriter out pour afficher du contenu. La seule diffrence rside dans le fait que depuis une JSP, il n'est plus ncessaire de spcifier le content-type de la rponse HTTP ni d'y rcuprer l'objet PrintWriter, comme nous l'avions fait deux chapitres auparavant depuis notre servlet. Ceci est rendu possible grce l'existence d'objets implicites , sur lesquels nous allons revenir trs bientt ! En ce qui concerne la rcupration de l'attribut depuis la requte, je pense que vous tes assez grands pour faire vous-mmes l'analogie avec sa cration : tout comme il suffit d'appeler setAttribute() pour crer un attribut dans une requte depuis une servlet, il suffit d'appeler la mthode getAttribute() pour en rcuprer un depuis une JSP ! Ici, je rcupre bien mon objet nomm test . Nous avons ici transmis un simple objet String, mais il est possible de transmettre n'importe quel objet, comme un entier ou une liste par exemple. Remarquez ce sujet la ncessit de convertir (cast ) l'objet rcupr dans la JSP au type souhait, la mthode getAttribute() renvoyant un objet global de type Object.

Alors c'est a une JSP ? Une autre page dans laquelle on remet une couche de Java ?

Non, bien sr que non ! crire du Java dans une JSP n'a aucun sens : l'intrt mme de ces pages est de s'affranchir du langage Java ! ce compte-l, autant n'utiliser qu'une servlet et ne pas mettre en place de JSP Cependant comme vous pouvez le voir, cela fonctionne trs bien ainsi : a confirme ce que je vous disais dans la premire partie de ce cours. En Java EE, rien n'impose au dveloppeur de bien travailler, et il est possible de coder n'importe comment sans que cela n'impacte le fonctionnement de l'application. V oil donc un premier exemple destin vous faire comprendre ds maintenant que mettre du code Java dans une page JSP, c'est mal. Pour ce qui est de notre exemple, ne vous y mprenez pas : si je vous fais utiliser du code Java ici, c'est uniquement parce que nous n'avons pas encore dcouvert le langage JSP. D'ailleurs, autant vous prvenir tout de suite : partir du chapitre suivant, nous allons tout mettre en uvre pour ne plus jamais crire de Java directement dans une JSP !

Donnes issues du client : les paramtres


Qu'est-ce qu'un paramtre de requte ?

Si vous tes assidus, vous devez vous souvenir de la description que je vous ai faite de la mthode GET du protocole HTTP : elle permet au client de transmettre des donnes au serveur en les incluant directement dans l'URL, dans ce qui s'appelle les paramtres ou query strings en anglais. Eh bien c'est cela que nous allons apprendre manipuler ici : nous allons rendre notre projet interactif, en autorisant le client transmettre des informations au serveur.

La forme de l'URL
Les paramtres sont transmis au serveur directement via l'URL. V oici des exemples des diffrentes formes qu'une URL peut prendre : Code : HTML <!-- URL sans paramtres --> /page.jsp <!-- URL avec un paramtre nomm 'cat' et ayant pour valeur 'java' --> /page.jsp?cat=java <!-- URL avec deux paramtres nomms 'lang' et 'admin', et ayant pour valeur respectivement 'fr' et 'true' --> /page.jsp?lang=fr&admin=true

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

61/606

Il y a peu de choses retenir : le premier paramtre est spar du reste de l'URL par le caractre ? ; les paramtres sont spars entre eux par le caractre & ; une valeur est attribue chaque paramtre via l'oprateur = . Il n'existe pas d'autre moyen de dclarer des paramtres dans une requte GET, ceux-ci doivent imprativement apparatre en clair dans l'URL demande. ce propos, souvenez-vous de ce dont je vous avais avertis lors de la prsentation de cette mthode GET : la taille d'une URL tant limite, la taille des donnes qu'il est ainsi possible d'envoyer est limite galement ! vrai dire, la norme ne spcifie pas de limite proprement parler, mais les navigateurs imposent d'eux-mmes une limite : par exemple, la longueur maximale d'une URL est de 2 083 caractres dans Internet Explorer 8. Au-del de a, autrement dit si votre URL est si longue qu'elle contient plus de 2 000 caractres, ce navigateur ne saura pas grer cette URL ! V ous disposez donc d'une certaine marge de manuvre ; pour des chanes de caractres courtes comme dans notre exemple cette limite ne vous gne absolument pas. Mais d'une manire gnrale et mme si les navigateurs rcents savent grer des URL bien plus longues, lorsque vous avez beaucoup de contenu transmettre ou que vous ne connaissez pas l'avance la taille des donnes qui vont tre envoyes par le client, prfrez la mthode POST. J'en profite enfin pour vous reparler des recommandations d'usage HTTP : lorsque vous envoyez des donnes au serveur et qu'elles vont avoir un impact sur la ressource demande, il est, l encore, prfrable de passer par la mthode POST du protocole, plutt que par la mthode GET. Que signifie "avoir un impact sur la ressource" ?

Eh bien cela veut dire "entraner une modification sur la ressource", et en fin de compte tout dpend de ce que vous faites de ces donnes dans votre code. Prenons un exemple concret pour bien visualiser. Imaginons une application proposant une page compte.jsp qui autoriserait des actions diverses sur le compte en banque de l'utilisateur. Ces actions ne se drouleraient bien videmment pas comme cela dans une vraie application bancaire, mais c'est simplement pour que l'exemple soit parlant. Si le code attend un paramtre prcisant le mois pour lequel l'utilisateur souhaite afficher la liste des entres et sorties d'argent de son compte, par exemple compte.jsp?mois=avril, alors cela n'aura pas d'impact sur la ressource. En effet, nous pouvons bien renvoyer 10 fois la requte au serveur, notre code ne fera que rafficher les mmes donnes l'utilisateur sans les modifier. Si par contre le code attend des paramtres prcisant des informations ncessaires en vue de raliser un transfert d'argent, par exemple compte.jsp?montant=100&destinataire=01K87B612, alors cela aura clairement un impact sur la ressource : en effet, si nous renvoyons 10 fois une telle requte, notre code va effectuer 10 fois le transfert ! Ainsi, si nous suivons les recommandations d'usage, nous pouvons utiliser une requte GET pour le premier cas, et devons utiliser une requte POST pour le second. Nous reviendrons sur les avantages de la mthode POST lorsque nous aborderons les formulaires, dans une des parties suivantes de ce cours. N'importe quel client peut-il envoyer des paramtres une application ?

Oui, effectivement. Par exemple, lorsque vous naviguez sur le site du zro, rien ne vous empche de rajouter des paramtres tout droit issus de votre imagination lors de l'appel de la page d'accueil du site : par exemple, www.siteduzero.com/?mascotte=zozor. Le site n'en fera rien, car la page n'en tient pas compte, mais le serveur les recevra bien. C'est en partie pour cela, mais nous aurons tout le loisir d'y revenir par la suite, qu'il est impratif de bien vrifier le contenu des paramtres envoys au serveur avant de les utiliser.

Rcupration des paramtres par le serveur


Modifions notre exemple afin d'y inclure la gestion d'un paramtre nomm auteur : Code : Java - Servlet public void doGet( HttpServletRequest request, HttpServletResponse

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


response ) throws ServletException, IOException{ String paramAuteur = request.getParameter( "auteur" ); String message = "Transmission de variables : OK ! " + paramAuteur; request.setAttribute( "test", message ); this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

62/606

La seule ligne ncessaire pour cela est la ligne 2 : il suffit de faire appel la mthode getParameter() de l'objet requte, en lui passant comme argument le nom du paramtre que l'on souhaite rcuprer. La mthode retournant directement le contenu du paramtre, je l'ai ici insr dans une String que j'ai nomme paramAuteur. Pour vous montrer que notre servlet rcupre bien les donnes envoyes par le client, j'ai ajout le contenu de cette String au message que je transmets ensuite la JSP pour affichage. Si vous appelez nouveau votre servlet depuis votre navigateur, rien ne va changer. Mais si cette fois vous l'appelez en ajoutant un paramtre nomm auteur l'URL, par exemple : Code : URL http://localhost:8080/test/toto?auteur=Coyote

Alors vous observerez que le message affich dans le navigateur contient bien la valeur du paramtre prcis dans l'URL : Code : HTML - Contenu de la page finale <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> Transmission de variables : OK ! Coyote </p> </body> </html>

V ous avez donc ici la preuve que votre paramtre a bien t rcupr par la servlet. Comprenez galement que lorsque vous envoyez un paramtre, il reste prsent dans la requte HTTP durant tout son cheminement. Par exemple, nous pouvons trs bien y accder depuis notre page JSP sans passer par la servlet, de la manire suivante : Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); out.println( attribut ); String parametre = request.getParameter( "auteur" ); out.println( parametre ); %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</p> </body> </html>

63/606

En procdant ainsi, lorsque nous appelons l'URL en y prcisant un paramtre nomm auteur, nous obtenons bien le rsultat escompt : notre JSP affiche une seconde fois "Coyote", cette fois en rcuprant directement la valeur du paramtre depuis la requte HTTP. Si vous ne prcisez pas de paramtre auteur dans l'URL, alors la mthode getParameter() renverra null en lieu et place du contenu attendu. Note : il est trs imprudent de procder l'utilisation ou l'affichage d'un paramtre transmis par le client sans en contrler et ventuellement scuriser son contenu auparavant. Ici il s'agit d'un simple exemple, nous ne nous proccupons pas encore des problmes potentiels. Mais c'est une bonne pratique de toujours contrler ce qu'on affiche au client. Nous y reviendrons plusieurs reprises dans la suite du cours dans des cas plus concrets, et vous comprendrez alors mieux de quoi il retourne.

Nous allons nous arrter l pour le moment. L'utilisation la plus courante des paramtres dans une application web est la rcupration de donnes envoyes par le client via des formulaires, mais nous ne sommes pas encore prts pour cela. Avant de passer la suite, une dernire petite prcision s'impose. Quelle est la diffrence entre ces paramtres et les attributs que nous avons dcouverts en dbut de chapitre ?

Il ne faut pas faire de confusion ici : les paramtres de requte sont un concept appartenant au protocole HTTP. Ils sont envoys par le client au serveur directement au sein de l'URL, et donc sous forme de chanes de caractres. Il n'est pas possible de forger des paramtres dans l'objet HttpServletRequest, il est uniquement possible d'y accder en lecture. Ce concept n'tant absolument pas spcifique la plate-forme Java EE mais commun toutes les technologies web, il ne peut pas tre "objectifi". C'est la raison pour laquelle la mthode getParameter() retourne quoi qu'il arrive un objet de type String, et il n'est pas possible d'ajouter une quelconque logique supplmentaire un tel objet. les attributs de requte sont un concept appartenant au conteneur Java, et sont donc crs ct serveur : c'est au sein du code de l'application que l'on procde leur initialisation, et qu'on les insre dans la version "objectifie" de la requte, savoir l'objet HttpServletRequest. Contrairement aux paramtres, ils ne sont pas prsents directement dans la requte HTTP mais uniquement dans l'objet Java qui l'enveloppe, et peuvent contenir n'importe quel type de donnes. Ils sont utiliss pour permettre une servlet de communiquer avec d'autres servlets ou pages JSP. En rsum, les paramtres de requte sont propres au protocole HTTP et font partie intgrante de l'URL d'une requte, alors que les attributs sont des objets purement Java crs et grs par le biais du conteneur. Un attribut de requte est en ralit un objet stock dans l'objet HttpServletRequest, et peut contenir n'importe quel type de donnes. Les attributs de requte sont utiliss pour permettre une servlet de transmettre des donnes d'autres servlets ou des pages JSP. Un paramtre de requte est une chane de caractres place par le client la fin de l'URL de la requte HTTP. Les paramtres de requte sont utiliss pour permettre un client de transmettre des donnes au serveur.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

64/606

Le JavaBean
Ce court chapitre a pour unique objectif de vous prsenter un type d'objet un peu particulier : le JavaBean. Souvent raccourci en "bean", un JavaBean dsigne tout simplement un composant rutilisable. Il est construit selon certains standards, dfinis dans les spcifications de la plate-forme et du langage Java eux-mmes : un bean n'a donc rien de spcifique au Java EE. Autrement dit, aucun concept nouveau n'intervient dans la cration d'un bean : si vous connaissez les bases du langage Java, vous tes dj capables de comprendre et de crer un bean sans problme particulier. Son utilisation ne requiert aucune bibliothque ; de mme, il nexiste pas de superclasse dfinissant ce qu'est un bean, ni d'API. Ainsi, tout objet conforme ces quelques rgles peut tre appel un bean. Dcouvrons pour commencer quels sont les objectifs d'un bean, puis quels sont ces standards d'criture dont je viens de vous parler. Enfin, dcouvrons comment l'utiliser dans un projet !

Objectifs Pourquoi le JavaBean ?


Avant d'tudier sa structure, intressons-nous au pourquoi d'un bean. En ralit, un bean est un simple objet Java qui suit certaines contraintes, et reprsente gnralement des donnes du monde rel. V oici un rcapitulatif des principaux concepts mis en jeu. Je vous donne ici des dfinitions plutt abstraites, mais il faut bien en passer par l. Les proprits : un bean est conu pour tre paramtrable. On appelle "proprits" les champs non publics prsents dans un bean. Qu'elles soient de type primitif ou objets, les proprits permettent de paramtrer le bean, en y stockant des donnes. La srialisation : un bean est conu pour pouvoir tre persistant. La srialisation est un processus qui permet de sauvegarder l'tat d'un bean, et donne ainsi la possibilit de le restaurer par la suite. Ce mcanisme permet une persistance des donnes, voire de l'application elle-mme. La rutilisation : un bean est un composant conu pour tre rutilisable. Ne contenant que des donnes ou du code mtier, un tel composant n'a en effet pas de lien direct avec la couche de prsentation, et peut galement tre distant de la couche d'accs aux donnes (nous verrons cela avec le modle de conception DAO). C'est cette indpendance qui lui donne ce caractre rutilisable. L'introspection : un bean est conu pour tre paramtrable de manire dynamique. L'introspection est un processus qui permet de connatre le contenu d'un composant (attributs, mthodes et vnements) de manire dynamique, sans disposer de son code source. C'est ce processus, coupl certaines rgles de normalisation, qui rend possible une dcouverte et un paramtrage dynamique du bean ! Dans le cas d'une application Java EE, oublions les concepts lis aux vnements, ceux-ci ne nous concernent pas. Tout le reste est valable, et permet de construire des applications de manire efficace : la simplicit inhrente la conception d'un bean rend la construction d'une application base sur des beans relativement aise, et le caractre rutilisable d'un bean permet de minimiser les duplications de logiques dans une application.

Un JavaBean n'est pas un EJB


Certains d'entre vous ont peut-tre dj entendu parler d'un composant Java EE nomm EJB , signifiant Enterprise JavaBean . Si ce nom ressemble trs fortement aux beans que nous tudions ici, ne tombez pas dans le pige et ne confondez pas les deux : les EJB suivent un concept compltement diffrent. Je ne m'attarde pas sur le sujet mais ne vous inquitez pas, nous reviendrons sur ce que sont ces fameux EJB en temps voulu.

Structure
Un bean : doit tre une classe publique ; doit avoir au moins un constructeur par dfaut, public et sans paramtres . Java l'ajoutera de lui-mme si aucun constructeur n'est explicit ; peut implmenter l'interface Serializable, il devient ainsi persistant et son tat peut tre sauvegard ; ne doit pas avoir de champs publics ; peut dfinir des proprits (des champs non publics), qui doivent tre accessibles via des mthodes publiques getter et setter, suivant des rgles de nommage.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

65/606

V oici un exemple illustrant cette structure : Code : Java - Exemple /* Cet objet est une classe publique */ public class MonBean{ /* Cet objet ne possde aucun constructeur, Java lui assigne donc un constructeur par dfaut public et sans paramtre. */ /* Les champs de l'objet ne sont pas publics (ce sont donc des proprits) */ private String proprieteNumero1; private int proprieteNumero2; /* Les proprits de l'objet sont accessibles via des getters et setters publics */ public String getProprieteNumero1() { return this.proprieteNumero1; } public int getProprieteNumero2() { return this.proprieteNumero2; } public void setProprieteNumero1( String proprieteNumero1 ) { this.proprieteNumero1 = proprieteNumero1; } public void setProprieteNumero2( int proprieteNumero2 ) { this.proprieteNumero2 = proprieteNumero2; } /* Cet objet suit donc bien la structure nonce : c'est un bean ! */ }

Ce paragraphe se termine dj : comme je vous le disais en introduction, un bean ne fait rien intervenir de nouveau. V oil donc tout ce qui dfinit un bean, c'est tout ce que vous devez savoir et retenir. En outre, nous n'allons pas pour le moment utiliser la srialisation dans nos projets : si vous n'tes pas familiers avec le concept, ne vous arrachez pas les cheveux et mettez cela de ct ! Plutt que de paraphraser, passons directement la partie qui nous intressera dans ce cours, savoir la mise en place de beans dans notre application web !

Mise en place
J'imagine que certains d'entre vous, ceux qui n'ont que trs peu, voire jamais, dvelopp d'applications Java ou Java EE, peinent comprendre exactement quel niveau et comment nous allons faire intervenir un objet Java dans notre projet web. V oyons donc tout d'abord comment mettre en place un bean dans un projet web sous Eclipse, afin de le rendre utilisable depuis le reste de notre application.

Cration de notre bean d'exemple


Dfinissons pour commencer un bean simple qui servira de base nos exemples : Code : Java - com.sdzee.beans.Coyote package com.sdzee.beans; public class Coyote { private String nom;

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


private String prenom; private boolean genius; public String getNom() { return this.nom; } public String getPrenom() { return this.prenom; } public boolean isGenius() { return this.genius; } public void setNom( String nom ) { this.nom = nom; } public void setPrenom( String prenom ) { this.prenom = prenom; } public void setGenius( boolean genius ) { /* Wile E. Coyote fait toujours preuve d'une ingniosit hors du commun, c'est indniable ! Bip bip... */ this.genius = true; } }

66/606

Rien de compliqu ici, c'est du pur Java sans aucune fioriture ! J'ai ici cr un bean contenant seulement trois proprits, savoir les trois champs non publics nom, prenom et genius. Inutile de s'attarder sur la nature des types utiliss ici, ceci n'est qu'un exemple totalement bidon qui ne sert rien d'autre qu' vous permettre de bien visualiser le concept. Maintenant, passons aux informations utiles. V ous pouvez remarquer que cet objet respecte bien les rgles qui rgissent l'existence d'un bean : un couple de getter/setter publics pour chaque champ priv ; aucun champ public ; un constructeur public sans paramtres (aucun constructeur tout court en l'occurrence). Cet objet doit tre plac dans le rpertoire des sources "src" de notre projet web. J'ai ici, dans notre exemple, prcis le package com.sdzee.beans. Jetez un il aux figures suivantes pour visualiser la dmarche sous Eclipse.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

67/606

Mise en place d'un

bean sous Eclipse - tape 1

Mise en place d'un bean sous

Eclipse - tape 2

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

68/606

V ous savez dornavant comment mettre en place des beans dans vos projets web. Cela dit, il reste encore une tape cruciale afin de rendre ces objets accessibles notre application ! En effet, actuellement vous avez certes plac vos fichiers sources au bon endroit, mais vous savez trs bien que votre application ne peut pas se baser sur ces fichiers sources, elle ne comprend que les classes compiles !

Configuration du projet sous Eclipse


Afin de rendre vos objets accessibles votre application, il faut que les classes compiles partir de vos fichiers sources soient places dans un dossier "classes", lui-mme plac sous le rpertoire /WEB-INF. Souvenez-vous, nous en avions dj parl dans le troisime chapitre de la premire partie. Par dfaut Eclipse, toujours aussi fourbe, ne procde pas ainsi et envoie automatiquement vos classes compiles dans un dossier nomm "build". Afin de changer ce comportement, il va falloir modifier le Build Path de notre application. Pour ce faire, faites un clic droit sur le dossier du projet, slectionnez "Build Path" puis "Configure Build Path...", comme indiqu la figure suivante.

Configuration du build

path d'un projet sous Eclipse - tape 1 Slectionnez alors l'onglet source, puis regardez en bas le champ Default output folder, comme sur la figure suivante.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

69/606

Configuration du build path d'un projet sous Eclipse - tape 2 C'est ici qu'il faut prciser le chemin vers WEB-INF/classes afin que nos classes, lors de leur compilation, soient automatiquement dposes dans le dossier pris en compte par notre serveur d'applications. Le rpertoire souhait n'existant pas par dfaut, Eclipse va le crer automatiquement pour nous. Validez, et c'est termin ! V otre application est prte, vos classes compiles seront bien dposes dans le rpertoire de l'application, et vous allez ainsi pouvoir manipuler vos beans directement depuis vos servlets et vos JSP ! Par dfaut, Eclipse ne vous montre pas ce rpertoire "classes" dans l'arborescence du projet, simplement parce que a n'intresse pas le dveloppeur de visualiser les fichiers .class. Tout ce dont il a besoin depuis son IDE est de pouvoir travailler sur les fichiers sources .java ! Si toutefois vous souhaitez vrifier que le dossier est bien prsent dans votre projet, il vous suffit d'ouvrir le volet Navigator, comme indiqu la figure suivante.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

70/606

Visualisation du rpertoire contenant les classes sous Eclipse

Mise en service dans notre application


Notre objet tant bien insr dans notre application, nous pouvons commencer le manipuler. Reprenons notre servlet d'exemple prcdente : Code : Java - com.sdzee.servlets.Test ... import com.sdzee.beans.Coyote; ... public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Cration et initialisation du message. */ String paramAuteur = request.getParameter( "auteur" ); String message = "Transmission de variables : OK ! " + paramAuteur; /* Cration du bean */ Coyote premierBean = new Coyote(); /* Initialisation de ses proprits */ premierBean.setNom( "Coyote" ); premierBean.setPrenom( "Wile E." ); /* Stockage du message et du bean dans l'objet request */ request.setAttribute( "test", message ); request.setAttribute( "coyote", premierBean ); /* Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

Et modifions ensuite notre JSP pour qu'elle ralise l'affichage des proprits du bean. Nous n'avons pas encore dcouvert le langage JSP, et ne savons pas encore comment rcuprer proprement un bean Utilisons donc une nouvelle fois, faute de mieux pour le moment, du langage Java directement dans notre page JSP : Code : JSP - /WEB-INF/test.jsp

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); out.println( attribut ); String parametre = request.getParameter( "auteur" ); out.println( parametre ); %>

71/606

Rcupration du bean : <% com.sdzee.beans.Coyote notreBean = (com.sdzee.beans.Coyote) request.getAttribute("coyote"); out.println( notreBean.getPrenom() ); out.println( notreBean.getNom() ); %> </p> </body> </html>

</p> <p>

Remarquez ici la ncessit de prciser le chemin complet (incluant le package) afin de pouvoir utiliser notre bean de type Coyote. Retournez alors sur votre navigateur et ouvrez http://localhost:8080/test/toto. V ous observez alors : Citation Ceci est une page gnre depuis une JSP. Transmission de variables : OK ! Rcupration du bean : Wile E. Coyote Tout se passe comme prvu : nous retrouvons bien les valeurs que nous avions donnes aux proprits nom et prenom de notre bean, lors de son initialisation dans la servlet ! L'objectif de ce chapitre est modeste : je ne vous offre ici qu'une prsentation concise de ce que sont les beans , de leurs rles et utilisations dans une application Java EE. Encore une fois, comme pour beaucoup de concepts intervenant dans ce cours, il faudrait un tutoriel entier pour aborder toutes leurs spcificits, et couvrir en dtail chacun des points importants mis en jeu. Retenez toutefois que l'utilisation des beans n'est absolument pas limite aux applications web : on peut en effet trouver ces composants dans de nombreux domaines, notamment dans les solutions graphiques bases sur les composants Swing et AWT (on parle alors de composant visuel). Maintenant que nous sommes au point sur le concept, revenons nos moutons : il est temps d'apprendre utiliser un bean depuis une page JSP sans utiliser de code Java ! Un bean est un objet Java rutilisable qui reprsente une entit, et dont les donnes sont reprsentes par des proprits. Un bean est une classe publique et doit avoir au moins un constructeur par dfaut, public et sans paramtres. Une proprit d'un bean est un champ non public, qui doit tre accessible travers un couple de getter/setter. Il faut configurer le build-path d'un projet web sous Eclipse pour qu'il y dpose automatiquement les classes compiles depuis les codes sources Java de vos objets. Un bean peut par exemple tre transmis d'une servlet vers une page JSP (ou une autre servlet) en tant qu'attribut de requte.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

72/606

La technologie JSP (1/2)


Cet ensemble est consacr l'apprentissage de la technologie JSP : nous y tudierons la syntaxe des balises, directives et actions JSP ainsi que le fonctionnement des expressions EL, et enfin nous tablirons une liste de documentations utiles sur le sujet. Trop volumineux pour entrer dans un unique chapitre, j'ai prfr le scinder en deux chapitres distincts. Ce premier opus a pour objectif de vous prsenter les bases de la syntaxe JSP et ses actions dites standard, toutes illustres par de brefs exemples.

Les balises
Balises de commentaire
Tout comme dans les langages Java et HTML, il est possible d'crire des commentaires dans le code de vos pages JSP. Ils doivent tre compris entre les balises <%-- et --%>. V ous pouvez les placer o vous voulez dans votre code source. Ils sont uniquement destins au(x) dveloppeur(s), et ne sont donc pas visibles par l'utilisateur final dans la page HTML gnre : Code : JSP <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Exemple</title> </head> <body> <%-- Ceci est un commentaire JSP, non visible dans la page HTML finale. --%> <!-- Ceci est un simple commentaire HTML. --> <p>Ceci est un simple texte.</p> </body> </html>

Balises de dclaration
Cette balise vous permet de dclarer une variable l'intrieur d'une JSP. V ous savez dj et nous y reviendrons par la suite qu'il est dconseill d'crire du code Java dans vos JSP mais, la balise existant, je prfre vous la prsenter. Si vous tombez dessus un jour, ne soyez pas dstabiliss : Code : JSP <%! String chaine = "Salut les zros."; %>

Il est possible d'effectuer plusieurs dclarations au sein d'un mme bloc. Ci-dessous, les dclarations d'une variable puis d'une mthode : Code : JSP <%! String test = null; public boolean jeSuisUnZero() { return true; } %>

Balises de scriptlet
Derrire ce mot trange, un mlange atroce entre "script" et "servlet", se cache simplement du code Java. Cette balise, vous la connaissez dj, puisque nous l'avons utilise dans le chapitre prcdent. Elle sert en effet inclure du code Java au sein de vos

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

73/606

pages mais, tout comme la balise prcdente, elle est proscrire dans la mesure du possible ! titre d'information seulement donc, voici le tag en question, ici au sein d'une balise HTML <form> : Code : JSP <form action="/tirage" method="post"> <% for(int i = 1; i < 3; i++){ out.println("Numro " + i + ": <select name=\"number"+i+"\">"); for(int j = 1; j <= 10; j++){ out.println("<option value=\""+j+"\">"+ j + "</option>"); } out.println("</select><br />"); } %> <br /> <input type="submit" value="Valider" /> </form>

Oui je sais, c'est un exemple trs moche, car il y a du code Java dans une JSP, code qui contient son tour des lments de prsentation HTML Mais c'est juste pour l'exemple ! Je vous prviens : le premier que je vois coder comme a, je le pends un arbre !

Balises d'expression
La balise d'expression est en quelque sorte un raccourci de la scriptlet suivante : Code : JSP <% out.println("Bip bip !"); %>

Elle retourne simplement le contenu d'une chane. V oici sa syntaxe : Code : JSP <%= "Bip bip !" %>

Notez bien l'absence de point-virgule lors de l'utilisation de ce raccourci.

Les directives
Les directives JSP permettent : d'importer un package ; d'inclure d'autres pages JSP ; d'inclure des bibliothques de balises (nous y reviendrons dans un prochain chapitre) ; de dfinir des proprits et informations relatives une page JSP. Pour gnraliser, elles contrlent comment le conteneur de servlets va grer votre JSP. Il en existe trois : taglib, page et include. Elles sont toujours comprises entre les balises <%@ et %>, et hormis la directive d'inclusion de page qui peut tre place n'importe o, elles sont placer en tte de page JSP.

Directive taglib
Le code ci-dessous inclut une bibliothque personnalise nomme maTagLib : Code : JSP <%@ taglib uri="maTagLib.tld" prefix="tagExemple" %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

74/606

Je ne dtaille pas, nous reviendrons plus tard sur ce qu'est exactement une bibliothque et sur cet attribut "prefix".

Directive page
La directive page dfinit des informations relatives la page JSP. V oici par exemple comment importer des classes Java : Code : JSP <%@ page import="java.util.List, java.util.Date" %>

Ici, l'import de deux classes est ralis : List et Date. Cette fonctionnalit n'est utile que si vous mettez en place du code Java dans votre page JSP, afin de rendre disponibles les diffrentes classes et interfaces des API Java. En ce qui nous concerne, puisque notre objectif est de faire disparatre le Java de nos vues, nous allons trs vite apprendre nous en passer ! D'autres options sont utilisables via cette balise page, comme le contentType ou l'activation de la session. Toutes ont des valeurs par dfaut, et je ne vais pas m'attarder sur les dtails de chacune d'elles ici. V ous ne vous en servirez que dans des cas trs spcifiques que nous dcouvrirons au cas par cas dans ce cours. V oici titre d'information l'ensemble des proprits accessibles via cette directive : Code : JSP <%@ page

%>

language="..." extends="..." import="..." session="true | false" buffer="none | 8kb | sizekb" autoFlush="true | false" isThreadSafe="true | false" isELIgnored ="true | false" info="..." errorPage="..." contentType="..." pageEncoding="..." isErrorPage="true | false"

V ous retrouvez ici celle que je vous ai fait utiliser depuis la mise en place de votre premire JSP : le pageEncoding . C'est travers cette option que vous pouvez spcifier l'encodage qui va tre prcis dans l'en-tte de la rponse HTTP envoye par votre page JSP.

Directive include
Lorsque vous dveloppez une vue, elle correspond rarement une JSP constitue d'un seul bloc. En pratique, il est trs courant de dcouper littralement une page web en plusieurs fragments, qui sont ensuite rassembls dans la page finale destination de l'utilisateur. Cela permet notamment de pouvoir rutiliser certains blocs dans plusieurs vues diffrentes ! Regardez par exemple le menu des cours sur le site du zro : c'est un bloc part entire, qui est rutilis dans l'ensemble des pages du site. Pour permettre un tel dcoupage, la technologie JSP met votre disposition une balise qui inclut le contenu d'un autre fichier dans le fichier courant. Via le code suivant par exemple, vous allez inclure une page interne votre application (en l'occurrence une page JSP nomme uneAutreJSP, mais cela pourrait trs bien tre une page HTML ou autre) dans votre JSP courante : Code : JSP <%@ include file="uneAutreJSP.jsp" %>

La subtilit retenir, c'est que cette directive ne doit tre utilise que pour inclure du contenu "statique" dans votre page :

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


l'exemple le plus courant pour un site web tant par exemple le header ou le footer de la page, trs souvent identiques sur l'intgralit des pages du site.

75/606

Attention, ici quand je vous parle de contenu "statique", je n'insinue pas que ce contenu est fig et ne peut pas contenir de code dynamique Non, si je vous parle d'inclusion "statique", c'est parce qu'en utilisant cette directive pour inclure un fichier, l'inclusion est ralise au moment de la compilation ; par consquent, si le code du fichier est chang par la suite, les rpercussions sur la page l'incluant n'auront lieu qu'aprs une nouvelle compilation ! Pour simplifier, cette directive peut tre vue comme un simple copier-coller d'un fichier dans l'autre : c'est comme si vous preniez l'intgralit de votre premier fichier, et que vous le colliez dans le second. V ous pouvez donc bien visualiser ici qu'il est ncessaire de procder cette copie avant la compilation de la page : on ne va pas copier un morceau de page JSP dans une servlet dj compile

Action standard include


Une autre balise d'inclusion dite "standard" existe, et permet d'inclure du contenu de manire "dynamique". Le contenu sera ici charg l'excution, et non la compilation comme c'est le cas avec la directive prcdente : Code : JSP <%-- L'inclusion dynamique d'une page fonctionne par URL relative : --%> <jsp:include page="page.jsp" /> <%-- Son quivalent en code Java est : --%> <% request.getRequestDispatcher( "page.jsp" ).include( request, response ); %> <%-- Et il est impossible d'inclure une page externe comme ci-dessous : --%> <jsp:include page="http://www.siteduzero.com" />

Cela dit, ce type d'inclusion a un autre inconvnient : il ne prend pas en compte les imports et inclusions faits dans la page rceptrice. Pour clarifier, prenons un exemple. Si vous utilisez un type List dans une premire page, et que vous comptez utiliser une liste dans une seconde page que vous souhaitez inclure dans cette premire page, il vous faudra importer le type List dans cette seconde page Je vous ai perdus ? V oyons tout cela au travers d'un exemple trs simple. Crez une page test_inc.jsp contenant le code suivant, sous le rpertoire WebContent de votre projet Eclipse, c'est--dire la racine de votre application : Code : JSP - /test_inc.jsp <% ArrayList<Integer> liste = new ArrayList<Integer>(); liste.add( 12 ); out.println( liste.get( 0 ) ); %>

Ce code ne fait qu'ajouter un entier une liste vide, puis l'affiche. Cependant cette page ne contient pas de directive d'import, et ne peut par consquent pas fonctionner directement : l'import de la classe ArrayList doit obligatoirement tre ralis auparavant pour que nous puissions l'utiliser dans le code. Si vous tentez d'accder directement cette page via http://localhost:8080/test/test_inc.jsp, vous aurez droit une jolie exception : Citation : Exception org.apache.jasper.JasperException: Unable to compile class for JSP: An error occurred at line: 2 in the jsp file: /test_inc.jsp ArrayList cannot be resolved to a type

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


Crez maintenant une page test_host.jsp, toujours la racine de votre application, qui va raliser l'import de la classe ArrayList puis inclure la page test_inc.jsp : Code : JSP - test_host.jsp <%@ page import="java.util.ArrayList" %> <%@ include file="test_inc.jsp" %>

76/606

Pour commencer, vous dcouvrez ici en premire ligne une application de la directive page, utilise ici pour importer la classe ArrayList. la seconde ligne, comme je vous l'ai expliqu plus haut, la directive d'inclusion peut tre vue comme un copiercoller : ici, le contenu de la page test_inc.jsp est copi dans la page test_host.jsp, puis la nouvelle page test_host.jsp contenant tout le code est compile. V ous pouvez donc appeler la page test_host.jsp, et la page web finale affichera bien "12" ! Mais si maintenant nous dcidons de remplacer la directive prsente dans notre page test_host.jsp par la balise standard d'inclusion : Code : JSP - test_host.jsp <%@ page import="java.util.ArrayList" %> <jsp:include page="test_inc.jsp" />

Eh bien lorsque nous allons tenter d'accder la page test_host.jsp, nous retrouverons la mme erreur que lorsque nous avons tent d'accder directement test_inc.jsp ! La raison est la suivante : les deux pages sont compiles sparment, et l'inclusion ne se fera que lors de lexcution. Ainsi fatalement, la compilation de la page test_inc.jsp ne peut qu'chouer, puisque l'import ncessaire au bon fonctionnement du code n'est ralis que dans la page hte. Pour faire simple, les pages incluses via la balise <jsp:include ... /> doivent en quelque sorte tre "indpendantes" ; elles ne peuvent pas dpendre les unes des autres et doivent pouvoir tre compiles sparment. Ce n'est pas le cas des pages incluses via la directive <%@ include ... %> .

Pour terminer sur ces problmatiques d'inclusions, je vous donne ici quelques informations et conseils supplmentaires. Certains serveurs d'applications sont capables de recompiler une page JSP incluant une autre page via la directive d'inclusion, et ainsi clipser sa principale contrainte. Ce n'est toutefois pas toujours le cas, et a reste donc viter si vous n'tes pas srs de votre coup Pour inclure un mme header et un mme footer dans toutes les pages de votre application ou site web, il est prfrable de ne pas utiliser ces techniques d'inclusion, mais de spcifier directement ces portions communes dans le fichier web.xml de votre projet. J'en reparlerai dans un prochain chapitre. Trs bientt, nous allons dcouvrir une meilleure technique d'inclusion de pages avec la JSTL !

La porte des objets


Un concept important intervient dans la gestion des objets par la technologie JSP : la porte des objets . Souvent appele visibilit, ou scope en anglais, elle dfinit tout simplement leur dure de vie. Dans le chapitre traitant de la transmission de donnes, nous avions dcouvert un premier type d'attributs : les attributs de requte. Eh bien de tels objets, qui je vous le rappelle sont accessibles via l'objet HttpServletRequest, ne sont visibles que durant le traitement d'une mme requte. Ils sont crs par le conteneur lors de la rception d'une requte HTTP, et disparaissent ds lors que le traitement de la requte est termin. Ainsi, nous avions donc, sans le savoir, cr des objets ayant pour porte la requte !

Il existe au total quatre portes diffrentes dans une application : page (JSP seulement) : les objets dans cette porte sont uniquement accessibles dans la page JSP en question ; requte : les objets dans cette porte sont uniquement accessibles durant l'existence de la requte en cours ; session : les objets dans cette porte sont accessibles durant l'existence de la session en cours ; application : les objets dans cette porte sont accessibles durant toute l'existence de l'application.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

77/606

Pourquoi prciser "JSP seulement" pour la porte page ?

Eh bien c'est trs simple : il est possible de crer et manipuler des objets de portes requte, session ou application depuis une page JSP ou depuis une servlet. Nous avions d'ailleurs dans le chapitre traitant de la transmission de donnes cr un objet de porte requte depuis notre servlet, puis utilis cet objet depuis notre page JSP. En revanche, il n'est possible de crer et manipuler des objets de porte page que depuis une page JSP, ce n'est pas possible via une servlet. Qu'est-ce qu'une session ?

Une session est un objet associ un utilisateur en particulier. Elle existe pour la dure pendant laquelle un visiteur va utiliser l'application, cette dure se terminant lorsque l'utilisateur ferme son navigateur, reste inactif trop longtemps, ou encore lorsqu'il se dconnecte du site. Ainsi, il est possible de garder en mmoire des donnes concernant un visiteur d'une requte l'autre, autrement dit de page en page : la session permet donc de garder une trace de la visite effectue. Plus prcisment, une session correspond en ralit un navigateur particulier, plutt qu' un utilisateur : par exemple, si un mme instant vous utilisez deux navigateurs diffrents pour vous rendre sur le mme site, le site crera deux sessions distinctes, une pour chacun des navigateurs. Un objet session concernant un utilisateur est conserv jusqu' ce qu'une certaine dure dinactivit soit atteinte. Pass ce dlai, le conteneur considre que ce client n'est plus en train de visiter le site, et dtruit alors sa session. Pour que vous visualisiez bien le principe, voici la figure suivante un schma regroupant les diffrentes portes existantes.

Portes des objets Remarquez bien les points suivants :

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

78/606

un objet de porte page n'est accessible que sur une page JSP donne ; un objet de porte requte n'est accessible que durant le cheminement d'une requte dans l'application, et n'existe plus ds lors qu'une rponse est renvoye au client ; un objet de porte session est accessible durant l'intgralit de la visite d'un client donn, condition bien sr que le temps d'inactivit dfini par le conteneur ne soit pas dpass durant cette visite ; un objet de porte application est accessible durant toute l'existence de l'application et par tous les clients. V ous devez bien raliser que l'utilisation dans votre code d'objets ayant pour porte l'application est dlicate. Rendezvous compte : ces objets sont accessibles partout, tout le temps et par tout le monde ! Afin d'viter notamment des problmes de modifications concurrentes, si vous avez besoin de mettre en place de tels objets, il est recommand de les initialiser ds le chargement de l'application, puis de ne plus toucher leur contenu et d'y accder depuis vos classes et pages uniquement en lecture seule. Nous tudierons ce scnario dans un prochain chapitre. Nous reviendrons au cas par cas sur chacune de ces portes dans certains exemples des chapitres venir.

Les actions standard


Autant vous prvenir tout de suite, le droulement de ce chapitre peut vous perturber : je vais dans cette partie du chapitre vous prsenter une certaine manire de faire pour accder des objets depuis une page JSP. Ensuite, je vais vous expliquer dans la partie suivante qu'il existe un autre moyen, plus simple et plus propre, et que nous n'utiliserons alors plus jamais cette premire faon de faire

Maintenant que vous connaissez les beans et les portes, vous avez presque tout en main pour constituer le modle de votre application (le M de MVC) ! C'est lui et uniquement lui qui va contenir les donnes de votre application, et les traitements y appliquer. La seule chose qui vous manque encore, c'est la manipulation de ces beans depuis une page JSP. V ous avez dj fait connaissance avec l'action standard <jsp:include>, je vais vous en prsenter quatre autres : <jsp:useBean>, <jsp:getProperty>, <jsp:setProperty> et enfin <jsp:forward>.

L'action standard useBean


V oici pour commencer l'action standard permettant d'utiliser un bean, ou de le crer s'il n'existe pas, depuis une page JSP : Code : JSP <%-- L'action suivante rcupre un bean de type Coyote et nomm "coyote" dans la porte requte s'il existe, ou en cre un sinon. --%> <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" scope="request" /> <%-- Elle a le mme effet que le code Java suivant : --%> <% com.sdzee.beans.Coyote coyote = (com.sdzee.beans.Coyote) request.getAttribute( "coyote" ); if ( coyote == null ){ coyote = new com.sdzee.beans.Coyote(); request.setAttribute( "coyote", coyote ); } %>

tudions les diffrents attributs de cette action. La valeur de l'attribut id est le nom du bean rcuprer, ou le nom que vous souhaitez donner au bean crer. L'attribut class correspond logiquement la classe du bean. Il doit obligatoirement tre spcifi si vous souhaitez crer un bean, mais pas si vous souhaitez simplement rcuprer un bean existant. L'attribut optionnel scope correspond la porte de l'objet. Si un bean du nom spcifi en id existe dj dans ce scope, et qu'il est du type ou de la classe prcis(e), alors il est rcupr, sinon une erreur survient. Si aucun bean de ce nom n'existe dans ce scope, alors un nouveau bean est cr. Enfin, si cet attribut n'est pas renseign, alors le scope par dfaut sera limit la page en cours. L'attribut optionnel type doit indiquer le type de dclaration du bean. Il doit tre une superclasse de la classe du bean, ou

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


une interface implmente par le bean. Cet attribut doit tre spcifi si class ne l'est pas, et vice-versa.

79/606

En rsum, cette action permet de stocker un bean (nouveau ou existant) dans une variable, qui sera identifie par la valeur saisie dans l'attribut id. Il est galement possible de donner un corps cette balise, qui ne sera excut que si le bean est cr : Code : JSP <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote"> <%-- Ici, vous pouvez placer ce que vous voulez : dfinir des proprits, crer d'autres objets, etc. --%> <p>Nouveau bean !</p> </jsp:useBean>

Ici, le texte qui est prsent entre les balises ne sera affich que si un bean est bel et bien cr, autrement dit si la balise <jsp:useBean> est appele avec succs. l'inverse, si un bean du mme nom existe dj dans cette page, alors le bean sera simplement rcupr et le texte ne sera pas affich.

L'action standard getProperty


Lorsque l'on utilise un bean au sein d'une page, il est possible par le biais de cette action d'obtenir la valeur d'une de ses proprits : Code : JSP <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" /> <%-- L'action suivante affiche le contenu de la proprit 'prenom' du bean 'coyote' : --%> <jsp:getProperty name="coyote" property="prenom" /> <%-- Elle a le mme effet que le code Java suivant : --%> <%= coyote.getPrenom() %>

Faites bien attention la subtilit suivante ! Alors que <jsp:useBean> rcupre une instance dans une variable accessible par l'id dfini, cette action standard ne rcupre rien, mais ralise seulement l'affichage du contenu de la proprit cible. Deux attributs sont utiles ici : name : contient le nom rel du bean, en l'occurrence l'id que l'on a saisi auparavant dans la balise de rcupration du bean ; property : contient le nom de la proprit dont on souhaite afficher le contenu.

L'action standard setProperty


Il est enfin possible de modifier une proprit du bean utilis. Il existe pour cela quatre faons de faire via l'action standard ddie cette tche : Code : JSP - Syntaxe 1 <%-- L'action suivante associe une valeur la proprit 'prenom' du bean 'coyote' : --%> <jsp:setProperty name="coyote" property="prenom" value="Wile E." /> <%-- Elle a le mme effet que le code Java suivant : --%> <% coyote.setPrenom("Wile E."); %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

80/606

Code : JSP - Syntaxe 2 <%-- L'action suivante associe directement la valeur rcupre depuis le paramtre de la requte nomm ici 'prenomCoyote' la proprit 'prenom' : --%> <jsp:setProperty name="coyote" property="prenom" param="prenomCoyote"/> <%-- Elle a le mme effet que le code Java suivant : --%> <% coyote.setPrenom( request.getParameter("prenomCoyote") ); %>

Code : JSP - Syntaxe 3 <%-- L'action suivante associe directement la valeur rcupre depuis le paramtre de la requte nomm ici 'prenom' la proprit de mme nom : --%> <jsp:setProperty name="coyote" property="prenom" /> <%-- Elle a le mme effet que le code Java suivant : --%> <% coyote.setPrenom( request.getParameter("prenom") ); %>

Code : JSP - Syntaxe 4 <%-- L'action suivante associe automatiquement la valeur rcupre depuis chaque paramtre de la requte la proprit de mme nom : -%> <jsp:setProperty name="coyote" property="*" /> <%-- Elle a le mme effet que le code Java suivant : --%> <% coyote.setNom( request.getParameter("nom") ); %> <% coyote.setPrenom( request.getParameter("prenom") ); %> <% coyote.setGenius( Boolean.valueOf( request.getParameter("genius") ) ); %>

L'action standard forward


La dernire action que nous allons dcouvrir permet d'effectuer une redirection vers une autre page. Comme toutes les actions standard, elle s'effectue ct serveur et pour cette raison il est impossible via cette balise de rediriger vers une page extrieure l'application. L'action de forwarding est ainsi limite aux pages prsentes dans le contexte de la servlet ou de la JSP utilise : Code : JSP <%-- Le forwarding vers une page de l'application fonctionne par URL relative : --%> <jsp:forward page="/page.jsp" /> <%-- Son quivalent en code Java est : --%> <% request.getRequestDispatcher( "/page.jsp" ).forward( request, response ); %> <%-- Et il est impossible de rediriger vers un site externe comme ci-dessous : --%> <jsp:forward page="http://www.siteduzero.com" />

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

81/606

Une particularit du forwarding est qu'il n'implique pas d'aller/retour passant par le navigateur de l'utilisateur final. Autrement dit, l'utilisateur final n'est pas au courant que sa requte a t redirige vers une ou plusieurs JSP diffrentes, puisque l'URL qui est affiche dans son navigateur ne change pas. Pas d'inquitude, nous y reviendrons en dtail lorsque nous tudierons un cas particulier, dans le chapitre concernant les sessions. Sachez enfin que lorsque vous utilisez le forwarding , le code prsent aprs cette balise dans la page n'est pas excut. Je vous prsente toutes ces notations afin que vous sachiez qu'elles existent, mais vous devez comprendre que la plupart de celles-ci taient d'actualit il y a une petite dizaine d'annes maintenant ! Depuis, d'importantes volutions ont chang la donne et tout cela n'est aujourd'hui utilis que dans des cas bien spcifiques. La vraie puissance de la technologie JSP, c'est dans le chapitre suivant que vous allez la dcouvrir ! Les commentaires compris entre <%-- et --%> ne sont pas visibles dans la page finale gnre. L'insertion directe de code Java dans une JSP est possible mais trs dconseille. Les directives se placent en dbut de fichier et permettent de configurer une JSP sous diffrents angles. Il existe 4 portes d'objets diffrentes, reprsentant 4 dures de vie diffrentes : page, request, session et application. Une session suit un visiteur de son arrive sur le site jusqu' son dpart. Les actions standard permettent pour la plupart de manipuler des objets au sein d'une JSP, mais sont aujourd'hui de l'histoire ancienne.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

82/606

La technologie JSP (2/2)


Nous allons dans ce chapitre terminer l'apprentissage de la technologie JSP, travers la dcouverte des expressions EL et des objets implicites.

Expression Language Prsentation


Dans cette seconde moiti, nous allons dcouvrir ensemble les bases de l'Expression Language, que l'on raccourcit trs souvent EL. Je rpte ce dont je vous ai avertis dans la partie prcdente : une fois que vous aurez assimil cette technologie, vous n'aurez plus jamais utiliser les actions standard d'utilisation des beans que nous venons de dcouvrir ! Rassurezvous, je ne vous les ai pas prsentes juste pour le plaisir : il est important que vous connaissiez ce mode de fonctionnement, afin de ne pas tre surpris si un jour vous tombez dessus. Seulement c'est une approche diffrente du modle MVC, une approche qui n'est pas compatible avec ce que nous apprenons ici.

En quoi consistent les expressions EL ?

Ces expressions sont indispensables une utilisation optimale des JSP. C'est grce elles que l'on peut s'affranchir dfinitivement de l'criture de scriptlets (du code Java, pour ceux qui n'ont pas suivi) dans nos belles pages JSP. Pour faire simple et concis, les expressions EL permettent via une syntaxe trs pure d'effectuer des tests basiques sur des expressions, et de manipuler simplement des objets et attributs dans une page, et cela sans ncessiter l'utilisation de code ni de script Java ! La maintenance de vos pages JSP, en fournissant des notations simples et surtout standard, est ainsi grandement facilite. Avant tout, tudions la forme et la syntaxe d'une telle expression : Code : JSP ${ expression }

Ce type de notation ne devrait pas tre inconnu ceux d'entre vous qui ont dj programm en Perl. Ce qu'il faut bien retenir, c'est que ce qui est situ entre les accolades va tre interprt : lorsqu'il va analyser votre page JSP, le conteneur va reprer ces expressions entoures d'accolades et il saura ainsi qu'il doit en interprter le contenu. Aussi, ne vous tonnez pas si dans la suite de ce chapitre j'voque l'intrieur d'une expression EL : je parle tout simplement de ce qui est situ entre les accolades !

La ralisation de tests
La premire chose que vous devez savoir, c'est qu' l'intrieur d'une expression, vous pouvez effectuer diverses sortes de tests. Pour raliser ces tests, il vous est possible d'inclure tout une srie d'oprateurs. Parmi ceux-ci, on retrouve les traditionnels : oprateurs arithmtiques, applicables des nombres : +, -, *, /, % ; oprateurs logiques, applicables des boolens : &&, ||, ! ; oprateurs relationnels, bass sur l'utilisation des mthodes equals() et compareTo() des objets compars : == ou eq, != ou ne, < ou lt, > ou gt, <= ou le, >= ou ge. V oyons concrtement ce que tout cela donne travers quelques exemples. Crez pour l'occasion une page nomme test_el.jsp la racine de votre application, et placez-y ces quelques lignes : Code : JSP - /test_el.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des expressions EL</title> </head>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<body> <p> <!-- Logiques sur des boolens --> ${ true && true } <br /> <!-- Affiche true --> ${ true && false } <br /> <!-- Affiche false --> ${ !true || false } <br /> <!-- Affiche false --> <!-- Calculs arithmtiques --> ${ 10 / 4 } <br /> <!-- Affiche 2.5 --> ${ 10 mod 4 } <br /> <!-- Affiche le reste de la division entire, soit 2 --> ${ 10 % 4 } <br /> <!-- Affiche le reste de la division entire, soit 2 --> ${ 6 * 7 } <br /> <!-- Affiche 42 --> ${ 63 - 8 } <br /> <!-- Affiche 55 --> ${ 12 / -8 } <br /> <!-- Affiche -1.5 --> ${ 7 / 0 } <br /> <!-- Affiche Infinity --> <!-- Compare les caractres 'a' et 'b'. Le caractre 'a' tant bien situ avant le caractre 'b' dans l'alphabet ASCII, cette EL affiche true. --> ${ 'a' < 'b' } <br /> <!-- Compare les chanes 'hip' et 'hit'. Puisque 'p' < 't', cette EL affiche false. --> ${ 'hip' gt 'hit' } <br /> <!-- Compare les caractres 'a' et 'b', puis les chanes 'hip' et 'hit'. Puisque le premier test renvoie true et le second false, le rsultat est false. --> ${ 'a' < 'b' && 'hip' gt 'hit' } <br /> <!-- Compare le rsultat d'un calcul une valeur fixe. Ici, 6 x 7 vaut 42 et non pas 48, le rsultat est false. --> ${ 6 * 7 == 48 } <br /> </p> </body> </html>

83/606

Rendez-vous alors sur http://localhost:8080/test/test_el.jsp, et vrifiez que les rsultats obtenus correspondent bien aux commentaires que j'ai placs sur chaque ligne. Remarquez la subtilit dvoile ici dans l'exemple de la ligne 27, au niveau des chanes de caractres : contrairement du code Java, dans lequel vous ne pouvez dclarer une String qu'en utilisant des double quotes (guillemets), vous pouvez utiliser galement des simple quotes (apostrophes) dans une expression EL. Pour information, ceci a t rendu possible afin de simplifier l'intgration des expressions EL dans les balises JSP : celles-ci contenant dj bien souvent leurs propres guillemets, cela vite au dveloppeur de s'emmler les crayons ! Pas de panique, vous comprendrez o je veux en venir dans la partie suivante, lorsque nous pratiquerons la JSTL ! Attention ici aux oprateurs relationnels : si vous souhaitez vrifier l'galit d'objets de type non standard via une EL, il vous faudra probablement rimplmenter les mthodes cites dans le troisime point de la liste prcdente ; si vous souhaitez effectuer une comparaison, il vous faudra vrifier que votre objet implmente bien l'interface Comparable. Autrement dit, pas besoin de vous casser la tte pour un objet de type String ou Integer, pour lesquels tout est dj prt nativement, mais pour des objets de votre propre cration et/ou de types personnaliss, pensez-y ! Les oprateurs suivent comme toujours un ordre de priorit : comme on a d vous l'apprendre en cours lmentaire, la multiplication est prioritaire sur l'addition, etc. J'omets ici volontairement certaines informations, notamment certains oprateurs que je ne juge pas utile de vous prsenter ; je vous renvoie vers la documentation officielle pour plus d'informations. Les applications que nous verrons dans ce cours ne mettront pas en jeu d'expressions EL trs complexes, et vous serez par la suite assez l'aise avec le concept pour comprendre par vous-mme les subtilits de leur utilisation dans des cas plus labors.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


En outre, deux autres types de test sont frquemment utiliss au sein des expressions EL : les conditions ternaires, de la forme : test ? si oui : sinon ; les vrifications si vide ou null, grce l'oprateur empty. Trs pratiques, ils se prsentent sous cette forme : Code : JSP <!-- Vrifications si vide ou null --> ${ empty 'test' } <!-- La chane teste n'est pas vide, le rsultat est false --> ${ empty '' } <!-- La chane teste est vide, le rsultat est true --> ${ !empty '' } <!-- La chane teste est vide, le rsultat est false --> <!-- Conditions ternaires --> ${ true ? 'vrai' : 'faux' } <!-- Le boolen test vaut true, vrai est affich --> ${ 'a' > 'b' ? 'oui' : 'non' } <!-- Le rsultat de la comparaison vaut false, non est affich --> ${ empty 'test' ? 'vide' : 'non vide' } <!-- La chane teste n'est pas vide, non vide est affich -->

84/606

Pour terminer, sachez enfin que la valeur retourne par une expression EL positionne dans un texte ou un contenu statique sera insre l'endroit mme o est situe l'expression : Code : JSP <!-- La ligne suivante : --> <p>12 est infrieur 8 : ${ 12 lt 8 }.</p> <!-- Sera rendue ainsi aprs interprtation de l'expression, 12 n'tant pas infrieur 8 : --> <p>12 est infrieur 8 : false.</p>

La manipulation d'objets
Toutes ces fonctionnalits semblent intressantes, mais ne nous serviraient pas grand-chose si elles ne pouvaient s'appliquer qu' des valeurs crites en dur dans le code de nos pages, comme nous l'avons fait l'instant dans nos exemples. La vraie puissance des expressions EL, leur vritable intrt, c'est le fait qu'elles permettent de manipuler des objets et de leur appliquer tous ces tests ! Quels types d'objets ? V oyons cela au cas par cas

Des beans
Sous la couverture, la technologie EL est base sur les spcifications des JavaBeans. Qu'est-ce que cela signifie concrtement ? Eh bien tout simplement qu'il est possible via une expression EL d'accder directement une proprit d'un bean ! Pour illustrer cette fonctionnalit sans trop compliquer notre exemple, nous allons utiliser les actions standard que nous avons dcouvertes dans le chapitre prcdent. ditez le fichier test_el.jsp que nous avons mis en place, et remplacez son contenu par ce code : Code : JSP - /test_el.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" />

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<title>Test des expressions EL</title> </head> <body> <p> <!-- Initialisation d'un bean de type Coyote avec une action standard, pour l'exemple : --> <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" /> <!-- Initialisation de sa proprit 'prnom' : --> <jsp:setProperty name="coyote" property="prenom" value="Wile E."/> <!-- Et affichage de sa valeur : --> <jsp:getProperty name="coyote" property="prenom" /> </p> </body> </html>

85/606

Grce aux deux premires actions (lignes 10 et 12), la base de notre exemple est pose : nous crons un bean de type Coyote dans notre page JSP, et initialisons sa proprit prenom avec la valeur "Wile E.". V ous retrouvez ensuite la ligne 14 l'action qui affiche le contenu de cette proprit, et lorsque vous vous rendez sur http://localhost:8080/test/test_el.jsp, le rsultat affich par le navigateur est logiquement "Wile E.". Maintenant, remplacez cette ligne 14 par l'expression EL suivante : Code : JSP ${ coyote.prenom }

Actualisez alors la page de tests dans votre navigateur, et vous observerez que le contenu n'a pas chang, la valeur de la proprit prnom est toujours correctement affiche. Eh bien oui, c'est tout ce qu'il est ncessaire d'crire avec la technologie EL ! Cette expression retourne le contenu de la proprit prenom du bean nomm coyote. Remarquez bien la syntaxe et la convention employes : coyote est le nom du bean, que nous avions ici dfini dans l'attribut id de l'action <jsp:useBean> ; prenom est un champ priv du bean (une proprit) accessible par sa mthode publique getPrenom() ; l'oprateur point permet de sparer le bean vis de sa proprit. Ainsi de manire gnrale, il suffit d'crire ${ bean.propriete } pour accder une proprit d'un bean. Simple et efficace ! Pour information, mais nous y reviendrons un peu plus tard, voici ce quoi ressemble le code Java qui est mis en uvre dans les coulisses lors de l'interprtation de l'expression ${ coyote.prenom } : Code : Java Coyote bean = (Coyote) pageContext.findAttribute( "coyote" ); if ( bean != null ) { String prenom = bean.getPrenom(); if ( prenom != null ) { out.print( prenom ); } }

Peu importe que nous la comparions avec une scriptlet Java ou avec une action standard, l'expression EL simplifie l'criture de manire frappante ! Ci-dessous, je vous propose d'tudier quelques exemples des utilisations et erreurs de syntaxe les plus courantes : Code : JSP

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<!-- Syntaxe conseille pour rcuprer la proprit 'prenom' du bean 'coyote'. --> ${ coyote.prenom } <!-- Syntaxe correcte, car il est possible d'expliciter la mthode d'accs la proprit. Prfrez toutefois la notation prcdente. --> ${ coyote.getPrenom() } <!-- Syntaxe errone : la premire lettre de la proprit doit tre une minuscule. --> ${ coyote.Prenom }

86/606

V oil pour la syntaxe employer pour accder des objets. Maintenant comme je vous l'annonais en dbut de paragraphe, la vraie puissance de la technologie EL rside non seulement dans le fait qu'elle permet de manipuler des beans, mais galement dans le fait qu'elle permet de les faire intervenir au sein de tests ! V oici quelques exemples d'utilisations, toujours bass sur la proprit prenom du bean coyote : Code : JSP <!-- Comparaison d'galit entre la proprit prenom et la chane "Jean-Paul" --> ${ coyote.prenom == "Jean-Paul" } <!-- Vrification si la proprit prenom est vide ou nulle --> ${ empty coyote.prenom } <!-- Condition ternaire qui affiche la proprit prnom si elle n'est ni vide ni nulle, et la chane "Veuillez prciser un prnom" sinon --> ${ !empty coyote.prenom ? coyote.prenom : "Veuillez prciser un prnom" }

En outre, sachez galement que les expressions EL sont protges contre un ventuel retour null : Code : JSP <!-- La scriptlet suivante affiche "null" si la proprit "prenom" n'a pas t initialise, et provoque une erreur la compilation si l'objet "coyote" n'a pas t initialis : --> <%= coyote.getPrenom() %> <!-- L'action suivante affiche "null" si la proprit "prenom" n'a pas t initialise, et provoque une erreur l'excution si l'objet "coyote" n'a pas t initialis : --> <jsp:getProperty name="coyote" property="prenom" /> <!-- L'expression EL suivante n'affiche rien si la proprit "prenom" n'a pas t initialise, et n'affiche rien si l'objet "coyote" n'a pas t initialis : --> ${ coyote.prenom }

Des collections
Les beans ne sont pas les seuls objets manipulables dans une expression EL, il est galement possible d'accder aux collections

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


au sens large. Cela inclut donc tous les objets de type java.util.List, java.util.Set, java.util.Map, etc.

87/606

Nous allons, pour commencer, reprendre notre page test_el.jsp, et remplacer son code par cet exemple illustrant l'accs aux lments d'une liste : Code : JSP - /test_el.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des expressions EL</title> </head> <body> <p> <% /* Cration d'une liste de lgumes et insertion de quatre lments */ java.util.List<String> legumes = new java.util.ArrayList<String>(); legumes.add( "poireau" ); legumes.add( "haricot" ); legumes.add( "carotte"); legumes.add( "pomme de terre" ); request.setAttribute( "legumes" , legumes ); %> <!-- Les quatre syntaxes suivantes retournent le deuxime lment de la liste de lgumes --> ${ legumes.get(1) }<br /> ${ legumes[1] }<br /> ${ legumes['1'] }<br /> ${ legumes["1"] }<br /> </p> </body> </html>

Toujours afin de ne pas compliquer l'exemple, j'initialise directement une liste de lgumes l'aide d'une scriptlet Java. Rappelezvous bien que je procde ainsi uniquement pour gagner du temps dans la mise en place de notre exemple, et que lors du dveloppement d'une vraie application, il est hors de question d'crire une page JSP aussi sale. V ous dcouvrez aux lignes 20 23 quatre nouvelles notations : l'appel une mthode de l'objet legumes . En l'occurrence notre objet est une liste, et nous appelons sa mthode get() en lui passant l'indice de l'lment voulu ; l'utilisation de crochets la place de l'oprateur point , contenant directement l'indice de l'lment voulu ; l'utilisation de crochets la place de l'oprateur point , contenant l'indice de l'lment entour d'apostrophes ; l'utilisation de crochets la place de l'oprateur point , contenant l'indice de l'lment entour de guillemets. Chacune d'elles est valide, et retourne bien le second lment de la liste de lgumes que nous avons initialise (souvenez-vous, l'indice du premier lment d'une collection est 0 et non pas 1). Rendez-vous sur la page http://localhost:8080/test/test_el.jsp et constatez par vous-mmes ! Remplacez ensuite le code de l'exemple par le suivant, illustrant l'accs aux lments d'un tableau : Code : JSP - /test_el.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des expressions EL</title> </head> <body> <p>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<% /* Cration d'un tableau */ String[] animaux = {"chien", "chat", "souris", "cheval"}; request.setAttribute("animaux" , animaux); %> <!-- Les trois syntaxes suivantes retournent le troisime lment du tableau --> ${ animaux[2] }<br /> ${ animaux['2'] }<br /> ${ animaux["2"] }<br /> </p> </body> </html>

88/606

Rendez-vous nouveau sur la page de tests depuis votre navigateur, et vous constaterez que les trois notations avec les crochets, appliques une liste dans le prcdent exemple, fonctionnent de la mme manire avec un tableau. Sachez par ailleurs qu'il est impossible d'utiliser directement l'oprateur point pour accder un lment d'une liste ou d'un tableau. Les syntaxes ${entiers.1} ou ${animaux.2} enverront une exception l'excution.

Enfin, remplacez le code de l'exemple par le suivant, illustrant l'accs aux lments d'une Map : Code : JSP - /test_el.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des expressions EL</title> </head> <body> <p> <% /* Cration d'une Map */ java.util.Map<String,Integer> desserts = new java.util.HashMap<String, Integer>(); desserts.put("cookies", 8); desserts.put("glaces", 3); desserts.put("muffins", 6); desserts.put("tartes aux pommes", 2); request.setAttribute("desserts" , desserts); %> <!-- Les quatre syntaxes suivantes retournent la valeur associe la cl "cookies" de la Map de desserts --> ${ desserts.cookies }<br /> ${ desserts.get("cookies") }<br /> ${ desserts['cookies'] }<br /> ${ desserts["cookies"] }<br /> <% /* Cration d'une chane nomme "element" et contenant le mot "cookies" */ String element = "cookies"; request.setAttribute("element",element); %> <!-- Il est galement possible d'utiliser un objet au lieu d'initialiser la cl souhaite directement dans l'expression --> ${ desserts[element] }<br /> </p> </body> </html>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


Rendez-vous une nouvelle fois sur la page de tests depuis votre navigateur, et remarquez deux choses importantes :

89/606

la notation avec l'oprateur point fonctionne, la ligne 21, de la mme manire que lors de l'accs une proprit d'un bean ; une expression peut en cacher une autre ! En l'occurrence la ligne 32, lorsque le conteneur va analyser l'expression EL il va d'abord rcuprer l'attribut de requte element, puis utiliser son contenu en guise de cl afin d'accder la valeur associe dans la Map de desserts. Par ailleurs, faites bien attention la syntaxe ${desserts[element]} employe dans ce dernier cas : il est impratif de ne pas entourer le nom de l'objet d'apostrophes ou de guillemets au sein des crochets. En effet, crire ${desserts["element"]} reviendrait essayer d'accder la valeur associe la cl nomme "element", ce qui n'est pas le comportement souhait ici ; il ne faut pas entourer l'expression contenue dans l'expression englobante avec des accolades. Autrement dit, il ne faut pas crire ${desserts[${element}]}, cette syntaxe n'est pas valide. Une seule paire d'accolades suffit !

Puisque les notations avec l'oprateur point et avec les crochets fonctionnent toutes deux dans le cas d'une Map, quelle est la notation recommande ? Une pratique efficace veut que l'on rserve la notation avec l'oprateur point pour l'accs aux proprits d'un bean, et que l'on utilise les crochets pour accder aux contenus d'une Map. Ainsi en lisant le code d'une page JSP, il devient trs simple de savoir si l'objet manipul dans une expression EL est un bean ou une Map. Toutefois, ce n'est pas une obligation et vous tes libres de suivre ou non cette bonne pratique dans vos projets, je ne vous conseille rien de particulier ce sujet. Personnellement j'utilise ds que j'en ai l'occasion la notation avec l'oprateur point , car je trouve qu'elle a tendance moins surcharger visuellement les expressions EL dans le code de mes pages JSP.

Pour clore sur ce sujet, je ne vous l'ai pas dit lorsque nous avons dcouvert la manipulation des beans dans une expression EL, mais sachez que l'oprateur point n'est pas la seule manire d'accder une proprit d'un bean, il est galement possible d'utiliser la notation avec les crochets : ${bean["proprit"]}.

Dsactiver l'valuation des expressions EL


Le format utilis par le langage EL, savoir ${ ... }, n'tait pas dfini dans les premires versions de la technologie JSP. Ainsi, si vous travaillez sur de vieilles applications non mises jour, il est possible que vous soyez amens empcher de telles expressions d'tre interprtes, afin d'assurer la rtro-compatibilit avec le code. C'est pour cela qu'il est possible de dsactiver lvaluation des expressions EL : au cas par cas, grce la directive page que nous avons brivement dcouverte dans le chapitre prcdent ; sur tout ou partie des pages, grce une section ajouter dans le fichier web.xml.

Avec la directive page


La directive suivante dsactive l'valuation des EL dans une page JSP : Code : JSP - Directive placer en tte d'une page JSP <%@ page isELIgnored ="true" %>

Les seules valeurs acceptes par l'attribut isELIgnored sont true et false : s'il est initialis true, alors les expressions EL seront ignores et apparatront en tant que simples chanes de caractres ; s'il est initialis false, alors elles seront interprtes par le conteneur.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

90/606

V ous pouvez d'ailleurs faire le test dans votre page test_el.jsp. ditez le fichier et insrez-y en premire ligne la directive. Enregistrez la modification et actualisez alors l'affichage de la page dans votre navigateur. V ous observerez que vos expressions ne sont plus values, elles sont cette fois simplement affiches telles quelles.

Avec le fichier web.xml


V ous pouvez dsactiver l'valuation des expressions EL sur tout un ensemble de pages JSP dans une application grce l'option <el-ignored> du fichier web.xml. Elle se prsente dans une section de cette forme : Code : XML - Section ajouter dans le fichier web.xml <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group> </jsp-config>

V ous connaissez dj le principe du champ <url-pattern>, que nous avons dcouvert lorsque nous avons mis en place notre premire servlet. Pour rappel donc, il permet de dfinir sur quelles pages appliquer ce comportement. En l'occurrence, la notation *.jsp ici utilise signifie que toutes les pages JSP de l'application seront impactes par cette configuration. Le champ <el-ignored> permet logiquement de dfinir la valeur de l'option. Le comportement est bien entendu le mme qu'avec la directive prcdente : si true est prcis alors les EL seront ignores, et si false est prcis elles seront interprtes.

Comportement par dfaut


Si vous ne mettez pas de configuration spcifique en place, comme celles que nous venons l'instant de dcouvrir, la valeur de l'option isELIgnored va dpendre de la version de l'API servlet utilis par votre application : si la version est suprieure ou gale 2.4, alors les expressions EL seront values par dfaut ; si la version est infrieure 2.4, alors il est possible que les expressions EL soient ignores par dfaut, pour assurer la rtro-compatibilit dont je vous ai dj parl. Comment savoir si une application permet d'utiliser les expressions EL ou non ?

Pour commencer, il faut s'assurer de la version de l'API servlet supporte par le conteneur qui fait tourner l'application. En l'occurrence, nous utilisons Tomcat 7 et celui-ci implmente la version 3.0 : tout va bien de ce ct, notre conteneur est capable de grer les expressions EL. Ensuite, il faut s'assurer que l'application est dclare correctement pour utiliser cette version. Cela se passe au niveau de la balise <web-app> du fichier web.xml . Lorsque nous avions mis en place celui de notre projet, je vous avais dit de ne pas vous soucier des dtails et de vous contenter d'crire : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> ... </web-app>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


La mise en place de tous ces attributs implique que notre application va se baser sur la version 3.0 de l'API servlet.

91/606

Ainsi, puisque notre conteneur le supporte et que notre application est correctement configure, nous pouvons en dduire que les expressions EL seront interprtes par dfaut dans notre application.

Si nous changeons de serveur d'applications, que va-t-il se passer ?

Eh oui, si jamais vous dployez votre application sur un autre serveur que Tomcat 7, il est tout fait envisageable que la version supporte par le conteneur utilis soit diffrente. Si par exemple vous travaillez sur une application un peu ancienne tournant sur un conteneur Tomcat 6, alors la version maximum de l'API servlet supporte est 2.5. V oici alors comment vous devrez dclarer votre application : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> ... </web-app>

Cette introduction la technologie EL se termine l. Il y en a bien assez dire sur le sujet pour crire un tutoriel entier, mais ce que nous avons appris ici nous suffira dans la plupart des cas. Retenez bien que ces expressions vont vous permettre de ne plus faire intervenir de Java dans vos JSP, et puisque vous savez maintenant de quoi il retourne, vous ne serez pas surpris de retrouver la syntaxe ${...} dans tous les futurs exemples de ce cours.

Les objets implicites

Il nous reste un concept important aborder avant de passer la pratique : les objets implicites . Il en existe deux types : ceux qui sont mis disposition via la technologie JSP ; ceux qui sont mis disposition via la technologie EL.

Les objets de la technologie JSP


Pour illustrer ce nouveau concept, revenons sur la premire JSP que nous avions crite dans le chapitre sur la transmission des donnes : Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); out.println( attribut ); %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</p> </body> </html>

92/606

Je vous avais alors fait remarquer qu' la ligne 13, nous avions directement utilis l'objet out sans jamais l'avoir instanci auparavant. De mme, la ligne 12 nous accdions directement la mthode request.getAttribute() sans jamais avoir instanci d'objet nomm request Comment est-ce possible ?

Pour rpondre cette question, nous devons nous intresser une nouvelle fois au code de la servlet auto-gnre par Tomcat, comme nous l'avions fait dans le second chapitre de cette partie. Retournons donc dans le rpertoire work du serveur, qui rappelez-vous est subtilis par Eclipse, et analysons nouveau le code du fichier test_jsp.java : Code : Java - Extrait de la servlet auto-gnre par Tomcat ... public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; try { response.setContentType("text/html"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("<!DOCTYPE html>\r\n"); out.write("<html>\r\n"); out.write(" <head>\r\n"); out.write(" <meta charset=\"utf-8\" />\r\n"); out.write(" <title>Test</title>\r\n"); out.write(" </head>\r\n"); out.write(" <body>\r\n"); out.write(" <p>Ceci est une page gnre depuis une JSP.</p>\r\n"); out.write(" <p>\r\n"); out.write(" "); String attribut = (String) request.getAttribute("test"); out.println( attribut ); out.write("\r\n"); out.write(" </p>\r\n"); out.write(" </body>\r\n"); out.write("</html>");

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


...

93/606

Analysons ce qui se passe dans le cas de l'objet out : la ligne 10, un objet nomm out et de type JspWriter est cr ; la ligne 24, il est initialis avec l'objet writer rcupr depuis la rponse ; la ligne 39, c'est tout simplement notre ligne de code Java, base sur l'objet out, qui est recopie telle quelle de la JSP vers la servlet auto-gnre ! Pour l'objet request, c'est un peu diffrent. Comme je vous l'ai dj expliqu dans le second chapitre, notre JSP est ici transforme en servlet. Si elle en diffre par certains aspects, sa structure globale ressemble toutefois beaucoup celle de la servlet que nous avons cre et manipule dans nos exemples jusqu' prsent. Regardez la ligne 3 : le traitement de la paire requte/rponse est contenu dans une mthode qui prend pour arguments les objets HttpServletRequest et HttpServletResponse, exactement comme le fait notre mthode doGet() ! V oil pourquoi il est possible d'utiliser directement les objets request et response depuis une JSP. V ous devez maintenant comprendre pourquoi vous n'avez pas besoin d'instancier ou de rcuprer les objets out et request avant de les utiliser dans le code de votre JSP : dans les coulisses, le conteneur s'en charge pour vous lorsqu'il traduit votre page en servlet ! Et c'est pour cette raison que ces objets sont dits "implicites" : vous n'avez pas besoin de les dclarer de manire explicite. Logique, non ?

Par ailleurs, si vous regardez attentivement le code ci-dessus, vous constaterez que les lignes 6 13 correspondent en ralit toutes des initialisations d'objets : pageContext, session, application En fin de compte, le conteneur met votre disposition toute une srie d'objets implicites, tous accessibles directement depuis vos pages JSP. En voici la liste : Identifiant Type de l'objet Description Il fournit des informations utiles relatives au contexte d'excution. Entre autres, il permet d'accder aux attributs prsents dans les diffrentes portes de l'application. Il contient galement une rfrence vers tous les objets implicites suivants. Il permet depuis une page JSP d'obtenir ou de modifier des informations relatives l'application dans laquelle elle est excute. Il reprsente une session associe un client. Il est utilis pour lire ou placer des objets dans la session de l'utilisateur courant. Il reprsente la requte faite par le client. Il est gnralement utilis pour accder aux paramtres et aux attributs de la requte, ainsi qu' ses en-ttes.

pageContext PageContext

application session request

ServletContext HttpSession HttpServletRequest

response

Il reprsente la rponse qui va tre envoye au client. Il est gnralement utilis HttpServletResponse pour dfinir le Content-Type de la rponse, lui ajouter des en-ttes ou encore pour rediriger le client. Throwable JspWriter ServletConfig Il est uniquement disponible dans les pages d'erreur JSP. Il reprsente l'exception qui a conduit la page d'erreur en question. Il reprsente le contenu de la rponse qui va tre envoye au client. Il est utilis pour crire dans le corps de la rponse. Il permet depuis une page JSP d'obtenir les ventuels paramtres d'initialisation disponibles. Il est l'quivalent de la rfrence this et reprsente la page JSP courante. Il est dconseill de l'utiliser, pour des raisons de dgradation des performances notamment.

exception out config

page

objet this

De la mme manire que nous avons utilis les objets request et out dans notre exemple prcdent, il est possible d'utiliser n'importe lequel de ces neuf objets travers le code Java que nous crivons dans nos pages JSP Hein ?! Encore du code Java dans nos pages JSP ?

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

94/606

Eh oui, tout cela est bien aimable de la part de notre cher conteneur, mais des objets sous cette forme ne vont pas nous servir grand-chose ! Souvenez-vous : nous avons pour objectif de ne plus crire de code Java directement dans nos pages.

Les objets de la technologie EL


J'en vois dj quelques-uns au fond qui sortent les cordes V ous avez peine digr les objets implicites de la technologie JSP, je vous annonce maintenant qu'il en existe d'autres rendus disponibles par les expressions EL ! Pas de panique, reprenons tout cela calmement. En ralit, et heureusement pour nous, la technologie EL va apporter une solution lgante au problme que nous venons de soulever : nous allons grce elle pouvoir profiter des objets implicites sans crire de code Java ! Dans les coulisses, le concept est sensiblement le mme que pour les objets implicites JSP : il s'agit d'objets grs automatiquement par le conteneur lors de l'valuation des expressions EL, et auxquels nous pouvons directement accder depuis nos expressions sans les dclarer auparavant. V oici un tableau des diffrents objets implicites mis disposition par la technologie EL : Catgorie JSP Identifiant pageContext pageScope requestScope Portes sessionScope applicationScope param Paramtres de requte paramValues header En-ttes de requte headerValues Cookies cookie Description Objet contenant des informations sur l'environnement du serveur. Une Map qui associe les noms et valeurs des attributs ayant pour porte la page. Une Map qui associe les noms et valeurs des attributs ayant pour porte la requte. Une Map qui associe les noms et valeurs des attributs ayant pour porte la session. Une Map qui associe les noms et valeurs des attributs ayant pour porte l'application. Une Map qui associe les noms et valeurs des paramtres de la requte. Une Map qui associe les noms et multiples valeurs ** des paramtres de la requte sous forme de tableaux de String. Une Map qui associe les noms et valeurs des paramtres des en-ttes HTTP. Une Map qui associe les noms et multiples valeurs ** des paramtres des en-ttes HTTP sous forme de tableaux de String. Une Map qui associe les noms et instances des cookies. Une Map qui associe les donnes contenues dans les champs <param-name> et <param-value> de la section <init-param> du fichier web.xml.

Paramtres dinitialisation initParam

La premire chose remarquer dans ce tableau, c'est que le seul objet implicite en commun entre les JSP et les expressions EL est le pageContext. Je ne m'attarde pas plus longtemps sur cet aspect, nous allons y revenir dans le chapitre suivant. La seconde, c'est la diffrence flagrante avec les objets implicites JSP : tous les autres objets implicites de la technologie EL sont des Map ! D'ailleurs, qu'est-ce que c'est que toute cette histoire de Map et d'associations entre des noms et des valeurs ?

a peut vous paratre compliqu, mais en ralit c'est trs simple. C'est un outil incontournable en Java, et nous venons d'en manipuler une lorsque nous avons dcouvert les expressions EL. Mais si jamais vous ne vous souvenez pas bien des collections Java, sachez qu'une Map est un objet qui peut se reprsenter comme un tableau deux colonnes :

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


la premire colonne contient ce que l'on nomme les cls , qui doivent obligatoirement tre uniques ; la seconde contient les valeurs, qui peuvent quant elles tre associes plusieurs cls. Chaque ligne du tableau ne peut contenir qu'une cl et une valeur. V oici un exemple d'une Map<String, String> reprsentant une liste d'aliments et leurs types : Aliments (Cls) Types (Valeurs) pomme carotte boeuf aubergine fruit lgume viande lgume

95/606

V ous voyez bien ici qu'un mme type peut tre associ diffrents aliments, mais qu'un mme aliment ne peut exister qu'une seule fois dans la liste. Eh bien c'est a le principe d'une Map : c'est un ensemble d'lments uniques auxquels on peut associer n'importe quelle valeur. Quel est le rapport avec la technologie EL ?

Le rapport, c'est que comme nous venons de le dcouvrir, nos expressions EL sont capables d'accder au contenu d'une Map, de la mme manire qu'elles sont capables d'accder aux proprits d'un bean. En guise de rappel, continuons notre exemple avec la liste d'aliments, et crons une page test_map.jsp, dans laquelle nous allons implmenter rapidement cette Map d'aliments : Code : JSP - /test_map.jsp <%@ page import="java.util.Map, java.util.HashMap" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des Maps et EL</title> </head> <body> <p> <% Map<String, String> aliments = new HashMap<String, String>(); aliments.put( "pomme","fruit" ); aliments.put( "carotte","lgume" ); aliments.put( "boeuf","viande" ); aliments.put( "aubergine","lgume" ); request.setAttribute( "aliments", aliments ); %> ${ aliments.pomme } <br /> <!-- affiche fruit --> ${ aliments.carotte } <br /> <!-- affiche lgume --> ${ aliments.boeuf } <br /> <!-- affiche viande --> ${ aliments.aubergine } <br /><!-- affiche lgume --> </p> </body> </html>

J'utilise ici une scriptlet Java pour initialiser rapidement la Map et la placer dans un attribut de la requte nomm aliments . Ne prenez bien videmment pas cette habitude, je ne procde ainsi que pour l'exemple et vous rappelle que nous cherchons liminer le code Java de nos pages JSP ! Rendez-vous alors sur http://localhost:8080/test/test_map.jsp, et observez le bon affichage des valeurs. Comme je vous l'ai annonc un peu plus tt, j'utilise la notation avec l'oprateur point - ici dans les lignes 18 21 - pour accder aux valeurs contenues dans la Map, mais il est tout fait possible d'utiliser la notation avec les crochets. D'accord, avec des expressions EL, nous pouvons accder au contenu d'objets de type Map. Mais a, nous le savions

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


dj Quel est le rapport avec les objets implicites EL ?

96/606

Le rapport, c'est que tous ces objets sont des Map, et que par consquent nous sommes capables d'y accder depuis des expressions EL, de la mme manire que nous venons de parcourir notre Map d'aliments ! Pour illustrer le principe, nous allons laisser tomber nos fruits et lgumes et crer une page nomme test_obj_impl.jsp, encore et toujours la racine de notre session, application, et y placer le code suivant : Code : JSP - /test_obj_impl.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des objets implicites EL</title> </head> <body> <p> <% String paramLangue = request.getParameter("langue"); out.println( "Langue : " + paramLangue ); %> <br /> <% String paramArticle = request.getParameter("article"); out.println( "Article : " + paramArticle ); %> </p> </body> </html>

V ous reconnaissez aux lignes 10 et 15 la mthode request.getParameter() permettant de rcuprer les paramtres transmis au serveur par le client travers l'URL. Ainsi, il vous suffit de vous rendre sur http://localhost:8080/test/test_obj_im [...] r&article=782 pour que votre navigateur vous affiche ceci (voir la figure suivante).

Cherchez maintenant, dans le tableau fourni prcdemment, l'objet implicite EL ddi l'accs aux paramtres de requte Trouv ? Il s'agit de la Map nomme param. La technologie EL va ainsi vous mettre disposition un objet dont le contenu peut, dans le cas de notre exemple, tre reprsent sous cette forme : Nom du paramtre (Cl) Valeur du paramtre (Valeur) langue article fr 782

Si vous avez compris l'exemple avec les fruits et lgumes, alors vous avez galement compris comment accder nos paramtres de requtes depuis des expressions EL, et vous tes capables de rcrire notre prcdente page d'exemple sans utiliser de code Java ! ditez votre fichier test_obj_impl.jsp et remplacez le code prcdent par le suivant : Code : JSP - /test_obj_impl.jsp <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test des objets implicites EL</title>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</head> <body> <p> Langue : ${ param.langue } <br /> Article : ${ param.article } </p> </body> </html>

97/606

Actualisez la page dans votre navigateur, et observez le mme affichage que dans l'exemple prcdent. Pratique et lgant, n'est-ce pas ? D'accord, dans ce cas cela fonctionne bien : chaque paramtre a un nom unique, et est associ une seule valeur quelconque. Mais qu'en est-il des lignes marques avec ** dans le tableau ? Est-il possible d'associer un unique paramtre plusieurs valeurs la fois ? Oui, il est tout fait possible d'associer une cl des valeurs multiples. C'est d'ailleurs tout fait logique, puisque derrire les rideaux il s'agit tout simplement d'objets de type Map ! L'unique diffrence entre les objets implicites param et paramValues , ainsi qu'entre header et headerValues , se situe au niveau de la nature de l'objet utilis dans la Map et des valeurs qui y sont stockes : pour param et header, une seule valeur est associe chaque nom de paramtre, via une Map<String,String> ; pour paramValues et headerValues par contre, ce sont plusieurs valeurs qui vont tre associes un mme nom de paramtre, via une Map<String,String[]>. Quand pouvons-nous rencontrer plusieurs valeurs pour un seul et mme paramtre ?

Tout simplement en prcisant plusieurs fois un paramtre d'URL avec des valeurs diffrentes ! Par exemple, accdez cette fois la page de tests avec l'URL http://localhost:8080/test/test_obj_im [...] 782&langue=zh. Cette fois, la technologie EL va vous mettre disposition un objet dont le contenu peut tre reprsent ainsi : Nom du paramtre (Cl) Valeur du paramtre (Valeur) langue article [fr,zh] 782

La Map permettant d'accder aux valeurs du paramtre langue n'est plus une Map<String,String>, mais une Map<String,String[]>. Si vous ne modifiez pas le code de l'expression EL dans votre page JSP, alors vous ne pourrez qu'afficher la premire valeur du tableau des langues, retourne par dfaut lorsque vous utilisez l'expression ${param.langue}. Afin d'afficher la seconde valeur, il faut cette fois non plus utiliser l'objet implicite param, mais utiliser l'objet implicite nomm paramValues . Remplacez la ligne 9 de votre fichier test_obj_impl.jsp l'expression ${param.langue} par l'expression ${paramValues.langue[1]}. Actualisez alors la page dans votre navigateur, et vous verrez alors s'afficher la valeur zh ! Le principe est simple : alors qu'auparavant en crivant ${param.langue} vous accdiez directement la String associe au paramtre langue, cette fois en crivant ${paramValues.langue} vous accdez non plus une String, mais un tableau de String. V oil pourquoi il est ncessaire d'utiliser la notation avec crochets pour accder aux diffrents lments de ce tableau !

En l'occurrence, puisque seules deux langues ont t prcises dans l'URL, il n'existe que les lments d'indices 0 et 1 dans le tableau, contenant les valeurs fr et zh. Si vous essayez d'accder un lment non dfini, par exemple en crivant ${paramValues.langue[4]}, alors l'expression EL dtectera une valeur nulle et n'affichera rien. De mme, vous devez obligatoirement cibler un des lments du tableau ici. Si vous n'crivez que ${paramValues.langue}, alors l'expression EL vous affichera la rfrence de l'objet Java contenant votre tableau Par ailleurs, sachez qu'il existe d'autres cas impliquant plusieurs valeurs pour un mme paramtre. Prenons un exemple HTML trs

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


simple : un <select> choix multiples ! Code : HTML <form method="post" action=""> <p> <label for="pays">Dans quel(s) pays avez-vous dj voyag ?</label><br /> <select name="pays" id="pays" multiple="multiple"> <option value="france">France</option> <option value="espagne">Espagne</option> <option value="italie">Italie</option> <option value="royaume-uni">Royaume-Uni</option> <option value="canada">Canada</option> <option value="etats-unis">Etats-Unis</option> <option value="chine" selected="selected">Chine</option> <option value="japon">Japon</option> </select> </p> </form>

98/606

Alors que via un <select> classique, il n'est possible de choisir qu'une seule valeur dans la liste droulante, dans cet exemple grce l'option multiple="multiple", il est tout fait possible de slectionner plusieurs valeurs pour le seul paramtre nomm pays . Eh bien dans ce genre de cas, l'utilisation de l'objet implicite paramValues est ncessaire galement : c'est le seul moyen de rcuprer la liste des valeurs associes au seul paramtre nomm pays ! Pour ce qui est de l'objet implicite headerValues par contre, sa relle utilit est discutable. En effet, s'il est possible de dfinir plusieurs valeurs pour un seul paramtre d'un en-tte HTTP, celles-ci sont la plupart du temps spares par de simples pointsvirgules et concatnes dans une seule et mme String, rendant l'emploi de cet objet implicite inutile. Bref, dans 99 % des cas, utiliser la simple Map header est suffisant. Ci-dessous un exemple d'en-ttes HTTP : Code : HTTP GET / HTTP/1.1 Host: www.google.fr User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; enUS; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 ( .NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive

V ous remarquez bien dans cet exemple que chaque paramtre (Host, User-Agent, Accept, etc.) n'est dfini qu'une seule fois, et que les valeurs sont simplement concatnes les unes la suite des autres sur la mme ligne.

V oil donc la solution notre problme : les objets implicites EL sont des raccourcis qui rendent l'accs aux diffrentes portes et aux diffrents concepts lis HTTP extrmement pratiques !

Nous allons nous arrter l pour les explications sur les objets implicites, l'important pour le moment est que vous compreniez bien leur mode de fonctionnement. Ne vous inquitez pas si vous ne saisissez pas l'utilit de chacun d'entre eux, c'est tout fait normal, certains concepts vous sont encore inconnus. La pratique vous fera prendre de l'aisance, et j'apporterai de plus amples explications au cas par cas dans les exemples de ce cours. Avant de passer la suite, un petit avertissement quant au nommage de vos objets. Faites bien attention aux noms des objets implicites lists ci-dessus. Il est fortement dconseill de dclarer une variable portant le mme nom qu'un objet implicite, par exemple param ou cookie. En effet, ces noms sont dj utiliss pour identifier des objets implicites, et cela pourrait causer des comportements plutt inattendus dans vos pages et

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

99/606

expressions EL. Bref, ne cherchez pas les ennuis : ne donnez pas vos variables un nom dj utilis par un objet implicite.

Beaucoup de nouvelles notations vous ont t prsentes, prenez le temps de bien comprendre les exemples illustrant l'utilisation des balises et des expressions. Lorsque vous vous sentez prts, passez avec moi au chapitre suivant, et tentez alors de rcrire notre prcdente page d'exemple JSP, en y faisant cette fois intervenir uniquement ce que nous venons d'apprendre ! La technologie EL est fonde sur les JavaBeans et sur les collections Java, et existe depuis la version 2.4 de l'API Servlet. Les expressions EL remplacent les actions standard de manipulation des objets. Une expression EL permet d'effectuer des tests, interprts la vole lors de l'excution de la page. L'interprtation des expressions EL peut tre dsactive via une section dans le fichier web.xml. Un objet implicite n'est pas gr par le dveloppeur, mais par le conteneur de servlets. Chaque objet implicite JSP donne accs un objet mis disposition par le conteneur. Chaque objet implicite EL est un raccourci vers des donnes de l'application.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

100/606

Des problmes de vue ?


Passons sur ce subtil jeu de mot, et revenons un instant sur notre premier exemple de page dynamique. Maintenant que nous connaissons la technologie JSP et les EL, nous sommes capables de remplacer le code Java que nous avions crit en dur dans notre vue par quelque chose de propre, lisible et qui suit les recommandations MVC !

Nettoyons notre exemple


Pour rappel, voici o nous en tions aprs l'introduction d'un bean dans notre exemple : Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); out.println( attribut ); String parametre = request.getParameter( "auteur" ); out.println( parametre ); %>

Rcupration du bean : <% com.sdzee.beans.Coyote notreBean = (com.sdzee.beans.Coyote) request.getAttribute("coyote"); out.println( notreBean.getPrenom() ); out.println( notreBean.getNom() ); %> </p> </body> </html>

</p> <p>

Avec tout ce que nous avons appris, nous sommes maintenant capables de modifier cette page JSP pour qu'elle ne contienne plus de langage Java ! Pour bien couvrir l'ensemble des mthodes existantes, divisons le travail en deux tapes : avec des scripts et balises JSP pour commencer, puis avec des EL.

Avec des scripts et balises JSP


Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> <% String attribut = (String) request.getAttribute("test"); %> <%= attribut %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<% String parametre = request.getParameter( "auteur" ); </p> <p> <%= parametre %>

101/606

%>

Rcupration du bean : <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" scope="request" /> <jsp:getProperty name="coyote" property="prenom" /> <jsp:getProperty name="coyote" property="nom" /> </p> </body> </html>

V ous pouvez remarquer : l'affichage de l'attribut et du paramtre via la balise d'expression <%= ... %> ; la rcupration du bean depuis la requte via la balise <jsp:useBean> ; l'affichage du contenu des proprits via les balises <jsp:getProperty>. Quel est l'objet implicite utilis ici pour rcuprer le bean "coyote" ?

Lorsque vous utilisez l'action : Code : JSP <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" scope="request" />

Celle-ci s'appuie derrire les rideaux sur l'objet implicite request (HttpServletRequest) : elle cherche un bean nomm "coyote" dans la requte, et si elle n'en trouve pas elle en cre un et l'y enregistre. De mme, si vous aviez prcis "session" ou "application" dans l'attribut scope de l'action, alors elle aurait cherch respectivement dans les objets session (HttpSession) et application (ServletContext). Enfin, lorsque vous ne prcisez pas d'attribut scope : Code : JSP <jsp:useBean id="coyote" class="com.sdzee.beans.Coyote" />

Ici, l'action s'appuie par dfaut sur l'objet implicite page (this) : elle cherche un bean nomm "coyote" dans la page courante, et si elle n'en trouve pas elle en cre un et l'y enregistre. En fin de compte notre exemple est dj bien plus propre qu'avant, mais nous avons toujours besoin de faire appel du code Java pour rcuprer et afficher nos attributs et paramtres depuis la requte

Avec des EL
Code : JSP - /WEB-INF/test.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


<body> <p>Ceci est une page gnre depuis une JSP.</p> <p> ${test} ${param.auteur} </p> <p> Rcupration du bean : ${coyote.prenom} ${coyote.nom} </p> </body> </html>

102/606

a se passe de commentaires V ous comprenez maintenant pourquoi je vous ai annonc dans le chapitre prcdent qu'une fois les EL dcouvertes, vous n'utiliserez plus jamais le reste ? Leur simplicit d'utilisation est dconcertante, et notre page ne contient dsormais plus une seule ligne de Java ! Pourquoi est-ce que nous n'utilisons aucun objet implicite pour accder aux attributs test et coyote ?

J'ai jusqu' prsent lchement vit ce sujet pour ne pas vous embrouiller, mais cette question mrite effectivement d'tre pose : comment notre expression EL est-elle capable de trouver nos diffrents objets ? Quand nous avions dcouvert les actions standard, le problme ne se posait pas, car il tait ncessaire de prciser dans quelle porte nous souhaitions rcuprer un objet, sans quoi l'action cherchait par dfaut dans la porte page Eh bien le fonctionnement de la technologie EL est fondamentalement diffrent de celui des actions standard. Dans une expression EL, lorsque vous accdez un objet prsent dans une des quatre portes de votre application, vous n'avez pas besoin de spcifier l'objet implicite (c'est--dire la porte) auquel vous souhaitez accder. D'ailleurs vous le voyez bien dans notre exemple, nous n'avons pas crit ${request.test} pour accder l'objet test prsent dans la porte request, ni ${request.coyote.prenom} pour accder au bean coyote prsent lui aussi dans la porte request. D'ailleurs, si vous aviez fait ainsi a n'aurait pas fonctionn ! N'oubliez pas que l'objet implicite request ne reprsente pas la porte request, mais directement l'objet requte en cours d'utilisation. En ralit, le mcanisme de la technologie EL est un peu volu : une expression est capable de raliser d'elle-mme un parcours automatique des diffrentes portes accessibles la recherche d'un objet portant le nom prcis, de la plus petite la plus grande porte. Comment ce mcanisme fonctionne-t-il ?

V ous vous souvenez de l'objet implicite pageContext ? Je vous l'avais prsent comme celui qui donne accs toutes les portes Concrtement, lorsque vous crivez par exemple l'expression ${test} dans votre JSP, derrire les rideaux le conteneur va se rendre compte qu'il ne s'agit pas d'un objet implicite mais bien d'un objet de votre cration, et va appeler la mthode findAttribute() de l'objet PageContext. Ensuite, cette mthode va son tour parcourir chacune des portes - page, puis request, puis session et enfin application - pour retourner le premier objet nomm "test" trouv ! La bonne pratique de dveloppement est de ne jamais donner le mme nom des objets existant dans des portes diffrentes ! Par exemple, si vous crivez l'expression ${test} pour cibler un objet nomm "test" que vous avez enregistr en session, alors qu'il existe un autre objet nomm "test" en requte, puisque la porte request sera toujours parcourue avant la porte session lors de la recherche automatique du mcanisme EL, c'est l'objet enregistr dans la requte qui vous sera renvoy

Dans ce cas, comment viter ce parcours automatique et cibler directement une porte ?

Pour cela, il faut utiliser les objets implicites fournis par la technologie EL donnant accs aux attributs existant : pageScope, requestScope, sessionScope et applicationScope. Ainsi, dans notre prcdent exemple nous aurions trs bien pu crire ${requestScope.test} la place de ${test}, et cela aurait fonctionn tout aussi bien. Lors de l'analyse de l'expression EL, le conteneur aurait ainsi reconnu l'objet implicite requestScope, et n'aurait pas effectu le parcours des portes : il aurait

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


directement recherch un objet nomm "test" au sein de la porte request uniquement.

103/606

La bonne pratique veut qu'en complment de la rigueur dans le nommage des objets conseille prcdemment, le dveloppeur prcise toujours la porte qu'il souhaite cibler dans une expression EL. Ainsi pour accder un objet prsent par exemple dans la porte request, cette pratique recommande non pas d'crire ${test}, mais ${requestScope.test}. En procdant ainsi, le dveloppeur s'assure qu'aucun objet ne sera cibl par erreur.

En ce qui me concerne dans la suite de ce cours, je prendrai toujours garde ne jamais donner le mme nom deux objets diffrents. Je me passerai donc de prciser la porte dans chacune des expressions EL que j'crirai dans mes exemples, afin de ne pas les alourdir. Il va de soi que lors du dveloppement d'une vraie application web, je vous recommande de suivre les bonnes pratiques que je vous ai nonces l'instant.

Qu'en est-il du paramtre "auteur" ?

Notez bien que lorsque vous souhaitez cibler un objet qui n'est pas prsent dans une des quatre portes, il est bien entendu ncessaire d'expliciter l'objet implicite qui permet d'y accder au sein de l'expression EL. V oil pourquoi pour accder au paramtre de requte auteur, nous devons bien prciser ${param.auteur}. Si nous avions simplement crit ${auteur} cela n'aurait pas fonctionn, car le mcanisme de recherche automatique aurait tent de trouver un objet nomm "auteur" dans une des quatre portes - page, request, session puis application - et n'aurait logiquement rien trouv. Rappelez-vous bien qu'un paramtre de requte est diffrent d'un attribut de requte ! Enfin, comprenez bien que je prends ici l'exemple d'un paramtre de requte pour illustrer le principe, mais que ceci est valable pour tout objet implicite diffrent des quatre portes : param comme nous venons de le voir, mais galement header, cookie, paramValues , etc.

Compltons notre exemple...


Tout cela se goupille plutt bien pour le moment, oui mais voil il y a un "mais". Et un gros mme ! V ous ne vous en tes peut-tre pas encore aperus, mais les EL, mmes couples des balises JSP, ne permettent pas de mettre en place tout ce dont nous aurons couramment besoin dans une vue. Prenons deux exemples pourtant trs simples : nous souhaitons afficher le contenu d'une liste ou d'un tableau l'aide d'une boucle ; nous souhaitons afficher un texte diffrent selon que le jour du mois est pair ou impair. Eh bien a, nous ne savons pas encore le faire sans Java !

Manipulation d'une liste


Reprenons notre exemple, en crant une liste depuis une servlet et en essayant d'afficher son contenu depuis la JSP : Code : Java - Ajout d'une liste depuis la servlet ... public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Cration et initialisation du message. */ String paramAuteur = request.getParameter( "auteur" ); String message = "Transmission de variables : OK ! " + paramAuteur; /* Cration du bean et initialisation de ses proprits */ Coyote premierBean = new Coyote(); premierBean.setNom( "Coyote" ); premierBean.setPrenom( "Wile E." ); /* Cration de la liste et insertion de quatre lments */ List<Integer> premiereListe = new ArrayList<Integer>(); premiereListe.add( 27 );

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


premiereListe.add( 12 ); premiereListe.add( 138 ); premiereListe.add( 6 ); /* Stockage du message, du bean et de la liste dans l'objet request */ request.setAttribute( "test", message ); request.setAttribute( "coyote", premierBean ); request.setAttribute( "liste", premiereListe ); /* Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

104/606

Rien de problmatique ici, c'est encore le mme principe : nous initialisons notre objet et nous le stockons dans l'objet requte pour transmission la JSP. Regardons maintenant comment raliser l'affichage des lments de cette liste : Code : JSP - Rcupration et affichage du contenu de la liste depuis la JSP <%@ page pageEncoding="UTF-8" %> <%@ page import="java.util.List" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> ${test} ${param.auteur} </p> <p> Rcupration du bean : ${coyote.prenom} ${coyote.nom} </p> <p> Rcupration de la liste : <% List<Integer> liste = (List<Integer>) request.getAttribute( "liste" ); for( Integer i : liste ){ out.println(i + " : "); } %> </p> </body> </html>

V oil quoi nous en sommes rduits : raliser un import avec la directive page, pour pouvoir utiliser ensuite le type List lors de la rcupration de notre liste depuis l'objet requte, et afficher son contenu via une boucle for. Certes, ce code fonctionne, vous pouvez regarder le rsultat obtenu depuis votre navigateur. Mais nous savons d'ores et dj que cela va l'encontre du modle MVC : souvenez-vous, le Java dans une page JSP, c'est mal !

Utilisation d'une condition


V oyons maintenant comment raliser notre second exemple. Puisque la mise en place d'une condition n'a vraiment rien de passionnant (vous savez tous crire un if !), je profite de ce petit exemple pour vous faire dcouvrir une API trs utile ds lors que votre projet fait intervenir la manipulation de

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


dates : JodaTime.

105/606

Si vous avez dj programm en Java, vous avez trs certainement dj remarqu ce problme : la manipulation de dates en Java est horriblement peu intuitive ! Que ce soit via l'objet Date ou via l'objet Calendar, c'est trs dcevant et trs loin de ce que l'on est en droit d'attendre d'une plate-forme volue comme Java ! Afin de pouvoir utiliser les mthodes et objets de cette API, il vous faut : 1. tlcharger l'archive nomme joda-time-2.1-dist disponible sur cette page, par exemple au format zip ; 2. la dcompresser et y chercher le fichier joda-time-2.1.jar ; 3. l'inclure votre application, en le plaant sous le rpertoire /WEB-INF/lib/ de votre projet (un simple glisser-dposer depuis votre fichier vers Eclipse suffit, voir la figure suivante).

Mise en place de l'API JodaTime dans un projet Eclipse Une fois le fichier .jar en place, vous pouvez alors utiliser l'API depuis votre projet. Code : Java - Rcupration du jour du mois depuis la servlet ... public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Cration et initialisation du message. */ String paramAuteur = request.getParameter( "auteur" ); String message = "Transmission de variables : OK ! " + paramAuteur; /* Cration du bean et initialisation de ses proprits */ Coyote premierBean = new Coyote(); premierBean.setNom( "Coyote" ); premierBean.setPrenom( "Wile E." ); /* Cration de la liste et insertion de quatre lments */ List<Integer> premiereListe = new ArrayList<Integer>(); premiereListe.add( 27 ); premiereListe.add( 12 ); premiereListe.add( 138 ); premiereListe.add( 6 );

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


/** On utilise ici la libraire Joda pour manipuler les dates, pour deux raisons : * - c'est tellement plus simple et limpide que de travailler avec les objets Date ou Calendar ! * - c'est (probablement) un futur standard de l'API Java. */ DateTime dt = new DateTime(); Integer jourDuMois = dt.getDayOfMonth(); /* Stockage du message, du bean, de la liste et du jour du mois dans l'objet request */ request.setAttribute( "test", message ); request.setAttribute( "coyote", premierBean ); request.setAttribute( "liste", premiereListe ); request.setAttribute( "jour", jourDuMois ); /* Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); }

106/606

Remarquez la facilit d'utilisation de l'API Joda. N'hsitez pas parcourir par vous-mmes les autres objets et mthodes proposs, c'est d'une simplicit impressionnante. Code : JSP - Rcupration du jour du mois et affichage d'un message dpendant de sa parit <%@ page pageEncoding="UTF-8" %> <%@ page import="java.util.List" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> ${test} ${param.auteur} </p> <p> Rcupration du bean : ${coyote.prenom} ${coyote.nom} </p> <p> Rcupration de la liste : <% List<Integer> liste = (List<Integer>) request.getAttribute( "liste" ); for( Integer i : liste ){ out.println(i + " : "); } %> </p> <p> Rcupration du jour du mois : <% Integer jourDuMois = (Integer) request.getAttribute( "jour" ); if ( jourDuMois % 2 == 0 ){ out.println("Jour pair : " + jourDuMois); } else { out.println("Jour impair : " + jourDuMois); } %>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</p> </body> </html>

107/606

Encore une fois, voil quoi nous en sommes rduits ct JSP : crire du code Java pour faire un simple test sur un entier D'autant plus que je ne vous ai ici propos que deux exemples basiques, mais nous pourrions lister bien d'autres fonctionnalits qu'il serait intressant de pouvoir utiliser dans nos vues, et qui ne nous sont pas accessibles travers la technologie JSP sans utiliser de scriptlets ! Dans ce cas, comment faire ? Comment ne pas crire de code Java dans nos pages JSP ?

Eh bien des dveloppeurs se sont pos la mme question il y a plus de dix ans dj , et ont imagin la JSTL : une bibliothque de balises prconues qui permettent, la manire des balises JSP, de mettre en place les fonctionnalits dont nous avons besoin couramment dans une vue, mais qui ne sont pas accessibles nativement au sein de la technologie JSP.

Le point sur ce qu'il nous manque


Avant d'attaquer l'apprentissage de cette fameuse JSTL, prenons deux minutes pour faire le point sur les limites de ce que nous avons appris jusqu' prsent.

La vue
Nous devons imprativement nettoyer nos lunettes ! Nous savons afficher des choses basiques, mais ds que notre vue se complexifie un minimum, nous ne savons plus faire. V ous tes dj au courant, c'est vers la JSTL que nous allons nous tourner : l'intgralit de la partie suivante lui est d'ailleurs consacre.

L'interaction
V ous ne vous en tes peut-tre pas encore rendu compte, mais nous n'avons qu'effleur la rcupration de donnes envoyes par le client ! Nous devons mettre en place de l'interaction : une application web qui ne demande rien l'utilisateur, c'est un site statique ; nous, ce que nous souhaitons, c'est une application dynamique ! Pour le moment, nous avons uniquement dvelopp des vues basiques, couples des servlets qui ne faisaient presque rien. Trs bientt, nous allons dcouvrir que les servlets auront pour objectif de TOUT contrler : tout ce qui arrivera dans notre application et tout ce qui en sortira passera par nos servlets.

Les donnes
Nous devons apprendre grer nos donnes : pour le moment, nous avons uniquement dcouvert ce qu'tait un bean. Nous avons une vague ide de comment seront reprsentes nos donnes au sein du modle : chaque entit de donnes correspondra un bean Toutefois, nous nous heurtons ici de belles inconnues : d'o vont venir nos donnes ? Qu'allons nous mettre dans nos beans ? Comment allons-nous sauvegarder les donnes qu'ils contiendront ? Comment enregistrer ce que nous transmet le client ? Nous devrons, pour rpondre tout cela, apprendre manipuler une base de donnes depuis notre application. Ainsi, nous allons dcouvrir que notre modle sera en ralit constitu non pas d'une seule couche, mais de deux ! Miam !

Documentation
On n'insistera jamais assez sur l'importance, pour tout zro souhaitant apprendre quoi que ce soit, d'avoir recours aux documentations et ressources externes en gnral. Le Site du Zro n'est pas une bible, tout n'y est pas ; pire, des lments sont parfois volontairement omis ou simplifis afin de bien vous faire comprendre certains points au dtriment d'autres, jugs moins cruciaux. Les tutoriaux d'auteurs diffrents vous feront profiter de nouveaux points de vue et angles d'attaque, et les documentations officielles vous permettront un accs des informations justes et maintenues jour (en principe).

Liens utiles
Base de connaissances portant sur Tomcat et les serveurs d'applications, sur Tomcat's Corner propos des servlets, sur stackoverflow.com propos des JSP, sur stackoverflow.com propos des expressions EL, sur stackoverflow.com Base de connaissances portant sur les servlets, sur novocode.com FAQ gnrale propos du dveloppement autour de Java, sur jguru.com

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


Tutoriel sur les Expression Language, dans la documentation officielle J2EE 1.4 sur sun.com La syntaxe JSP, sur sun.com

108/606

V ous l'aurez compris, cette liste ne se veut pas exhaustive, et je vous recommande d'aller chercher par vous-mmes l'information sur les forums et sites du web. En outre, faites bien attention aux dates de cration des documents que vous lisez : les ressources primes sont lgion sur le web, notamment au sujet de la plate-forme Java EE qui est en constante volution. N'hsitez pas demander la communaut sur le forum Java du Site du Zro si vous ne parvenez pas trouver l'information que vous cherchez. Les expressions EL remplacent lgamment scriptlets et actions standard. La technologie EL ne rpond malheureusement pas tous nos besoins. La documentation est indispensable, condition qu'elle soit jour.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

109/606

TP Fil rouge - tape 1


Comme je vous lannonais en avant-propos, vous tes ici pour apprendre crer un projet web, en y ajoutant de la complexit au fur et mesure que le cours avance. Avec tout ce que vous venez de dcouvrir, vous voici prts pour poser la premire pierre de l'difice ! Je vous propose, dans cette premire tape de notre TP fil rouge qui vous accompagnera jusqu'au terme de votre apprentissage, de revoir et appliquer l'ensemble des notions abordes jusqu' prsent.

Objectifs Contexte
J'ai choisi la thmatique du commerce en ligne comme source d'inspiration : vous allez crer un embryon d'application qui va permettre la cration et la visualisation de clients et de commandes. C'est la fois assez global pour ne pas impliquer d'lments qui vous sont encore inconnus, et assez spcifique pour coller avec ce que vous avez appris dans ces chapitres et tes capables de raliser. L'objectif premier de cette tape, c'est de vous familiariser avec le dveloppement web sous Eclipse. V ous allez devoir mettre en place un projet en partant de zro dans votre environnement, et y crer vos diffrents fichiers. Le second objectif est que vous soyez l'aise avec l'utilisation de servlets, de pages JSP et de beans, et de manire gnrale avec le principe gnral d'une application Java EE.

Fonctionnalits
Cration d'un client
travers notre petite application, l'utilisateur doit pouvoir crer un client en saisissant des donnes depuis un formulaire, et visualiser la fiche client en rsultant. Puisque vous n'avez pas encore dcouvert les formulaires, je vais vous fournir une page qui vous servira de base. V otre travail sera de coder : un bean, reprsentant un client ; une servlet, charge de rcuprer les donnes envoyes par le formulaire, de les enregistrer dans le bean et de les transmettre une JSP ; une JSP, charge d'afficher la fiche du client cr, c'est--dire les donnes transmises par la servlet.

Cration d'une commande


L'utilisateur doit galement pouvoir crer une commande, en saisissant des donnes depuis un formulaire, et visualiser la fiche en rsultant. De mme, puisque vous n'avez pas encore dcouvert les formulaires, je vais vous fournir une page qui vous servira de base. V otre travail sera de coder : un bean, reprsentant une commande ; une servlet, charge de rcuprer les donnes envoyes par le formulaire, de les enregistrer dans le bean et de les transmettre une JSP ; une JSP, charge d'afficher la fiche de la commande cre, c'est--dire les donnes transmises par la servlet.

Contraintes
Comme je viens de vous l'annoncer, vous devez utiliser ces deux formulaires comme base pour votre application. V ous les placerez directement la racine de votre application, sous le rpertoire WebContent d'Eclipse.

Cration d'un client


Code : JSP - /creerClient.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'un client</title> <link type="text/css" rel="stylesheet" href="inc/style.css"

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


/> </head> <body> <div> <form method="get" action="creationClient"> <fieldset> <legend>Informations client</legend>

110/606

<label for="nomClient">Nom <span class="requis">*</span></label> <input type="text" id="nomClient" name="nomClient" value="" size="20" maxlength="20" /> <br /> <label for="prenomClient">Prnom </label> <input type="text" id="prenomClient" name="prenomClient" value="" size="20" maxlength="20" /> <br /> <label for="adresseClient">Adresse de livraison <span class="requis">*</span></label> <input type="text" id="adresseClient" name="adresseClient" value="" size="20" maxlength="20" /> <br /> <label for="telephoneClient">Numro de tlphone <span class="requis">*</span></label> <input type="text" id="telephoneClient" name="telephoneClient" value="" size="20" maxlength="20" /> <br /> <label for="emailClient">Adresse email</label> <input type="email" id="emailClient" name="emailClient" value="" size="20" maxlength="60" /> <br /> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body> </html>

Cration d'une commande


Code : JSP - /creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'une commande</title> <link type="text/css" rel="stylesheet" href="inc/style.css" /> </head> <body> <div> <form method="get" action="creationCommande"> <fieldset> <legend>Informations client</legend> <label for="nomClient">Nom <span class="requis">*</span></label> <input type="text" id="nomClient"

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


name="nomClient" value="" size="20" maxlength="20" /> <br /> <label for="prenomClient">Prnom </label> <input type="text" id="prenomClient" name="prenomClient" value="" size="20" maxlength="20" /> <br /> <label for="adresseClient">Adresse de livraison <span class="requis">*</span></label> <input type="text" id="adresseClient" name="adresseClient" value="" size="20" maxlength="20" /> <br /> <label for="telephoneClient">Numro de tlphone <span class="requis">*</span></label> <input type="text" id="telephoneClient" name="telephoneClient" value="" size="20" maxlength="20" /> <br /> <label for="emailClient">Adresse email</label> <input type="email" id="emailClient" name="emailClient" value="" size="20" maxlength="60" /> <br /> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="dateCommande" name="dateCommande" value="" size="20" maxlength="20" disabled /> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="" size="20" maxlength="20" /> <br /> <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label> <input type="text" id="modePaiementCommande" name="modePaiementCommande" value="" size="20" maxlength="20" /> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

111/606

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="" size="20" maxlength="20" /> <br /> <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="" size="20" maxlength="20" /> <br /> la livraison</label> <label for="statutLivraisonCommande">Statut de

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="" size="20" maxlength="20" /> <br /> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</html>

112/606

Feuille de style
V oici pour finir une feuille de style que je vous propose d'utiliser pour ce TP. Toutefois, si vous tes motivs vous pouvez trs bien crer et utiliser vos propres styles, cela n'a pas d'importance. Code : CSS - /inc/style.css /* Gnral ------------------------------------------------------------------------------------ */ body, p, legend, label, input { font: normal 8pt verdana, helvetica, sans-serif; } /* Forms -------------------------------------------------------------------------------------- */ fieldset { padding: 10px; border: 1px #0568CD solid; margin: 10px; } legend { font-weight: bold; color: #0568CD; } form label { float: left; width: 200px; margin: 3px 0px 0px 0px; } form input { margin: 3px 3px 0px 0px; border: 1px #999 solid; } form input.sansLabel { margin-left: 200px; } /* Styles et couleurs ------------------------------------------------------------------------- */ .requis { color: #c00; } .erreur { color: #900; } .succes { color: #090; } .info { font-style: italic; color: #E8A22B; }

Conseils
www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

113/606

propos des formulaires


V oici les seules informations que vous devez connatre pour attaquer : ces deux formulaires sont configurs pour envoyer les donnes saisies vers les adresses /creationClient et /creationCommande. Lorsque vous mettrez en place vos deux servlets, vous devrez donc effectuer un mapping respectivement sur chacune de ces deux adresses dans votre fichier web.xml ; les donnes seront envoyes via la mthode GET du protocole HTTP, vous devrez donc implmenter la mthode doGet() dans vos servlets ; le nom des paramtres crs lors de l'envoi des donnes correspond au contenu des attributs "name" de chaque balise <input> des formulaires. Par exemple, le nom du client tant saisi dans la balise <input name="nomClient"... /> du formulaire, il sera accessible depuis votre servlet par un appel request.getParameter("nomClient") ; j'ai volontairement dsactiv le champ de saisie de la date dans le formulaire de cration d'une commande. Nous intgrerons bien plus tard un petit calendrier permettant le choix d'une date, mais pour le moment nous ferons sans.

Le modle
V ous n'allez travailler que sur deux entits, savoir un client et une commande. Ainsi, deux objets suffiront : un bean Client reprsentant les donnes rcupres depuis le formulaire creerClient.jsp (nom, prnom, adresse, etc.) ; un bean Commande reprsentant les donnes rcupres depuis la seconde partie du formulaire creerCommande.jsp (date, montant, mode de paiement, etc.). N'hsitez pas relire le chapitre sur les Javabeans pour vous rafrachir la mmoire sur leur structure. Note 1 : au sujet du type des proprits de vos beans, je vous conseille de toutes les dclarer de type String, sauf le montant de la commande que vous pouvez ventuellement dclarer de type double. Note 2 : lors de la cration d'une commande, l'utilisateur va devoir saisir des informations relatives au client. Plutt que de crer une proprit pour chaque champ relatif au client (nom, prnom, adresse, etc.) dans votre bean Commande, vous pouvez directement y inclure une proprit de type Client, qui son tour contiendra les proprits nom, prnom, etc.

Les contrleurs
Les deux formulaires que je vous fournis sont paramtrs pour envoyer les donnes saisies au serveur par le biais d'une requte de type GET. V ous aurez donc crer deux servlets, que vous pouvez par exemple nommer CreationClient et CreationCommande, qui vont pour chaque formulaire : rcuprer les paramtres saisis, en appelant request.getParameter() sur les noms des diffrents champs ; les convertir dans le type souhait si certaines des proprits de vos beans ne sont pas des String, puis les enregistrer dans le bean correspondant ; vrifier si l'utilisateur a oubli de saisir certains paramtres requis (ceux marqus d'une toile sur le formulaire de saisie) : si oui, alors transmettre les beans et un message d'erreur une page JSP pour affichage ; si non, alors transmettre les beans et un message de succs une page JSP pour affichage. Puisque le champ de saisie de la date est dsactiv, vous allez devoir initialiser la proprit date du bean Commande avec la date courante. Autrement dit, vous allez considrer que la date d'une commande est simplement la date courante lors de la validation du formulaire. V ous devrez donc rcuprer directement la date courante depuis votre servlet et l'enregistrer au format String dans le bean.

Les vues
Je vous fournis les deux pages JSP contenant les formulaires de saisie des donnes. Les seules vues que vous avez dvelopper sont celles qui affichent les donnes saisies, aprs validation du formulaire. V ous pouvez par exemple les nommer afficherClient.jsp et afficherCommande.jsp. Elles vont recevoir un ou plusieurs beans et un message depuis leur servlet respective, et devront afficher les donnes contenues dans ces objets. V ous l'avez probablement dj devin, les expressions EL sont la solution idale ici ! V ous tes libres au niveau de la mise en forme des donnes et des messages affichs ; ce qui importe n'est pas le rendu graphique de vos pages, mais bien leur capacit afficher correctement ce qu'on attend d'elles !

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

114/606

Cration du projet
Avant de vous lcher dans la nature, revenons rapidement sur la mise en place du projet. N'hsitez pas relire le chapitre sur la configuration d'un projet si vous avez encore des incertitudes ce sujet. Je vous conseille de crer un projet dynamique en partant de zro dans Eclipse, que vous pouvez par exemple nommer tp1 , bas sur le serveur Tomcat 7 que nous avons dj mis en place dans le cadre du cours. V ous devrez alors configurer le build-path comme nous avons appris le faire dans le chapitre sur les Javabeans. V ous allez par ailleurs devoir manipuler une date lors de la cration d'une commande : je vous encourage pour cela utiliser la bibliothque JodaTime que nous avons dcouverte dans le chapitre prcdent. Pour conclure, voici la figure suivante ce quoi est suppose ressembler l'architecture de votre projet fini si vous avez suivi mes conseils et exemples de nommage.

V ous pouvez observer en encadr sur cette image le positionnement des trois fichiers dont je vous ai fourni le code.

Illustration du comportement attendu


la figure suivante, voici sous forme d'un schma ce que vous devez raliser dans le cas de la cration d'un client.

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

115/606

Je ne vous illustre pas la cration d'une commande, le principe tant trs similaire !

Exemples de rendu du comportement attendu


Cration d'un client
Avec succs (voir les figures suivantes).

Saisie de donnes valides dans le

formulaire

Affichage du message de

succs et des donnes Avec erreur (voir les figures suivantes).

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

116/606

Oubli d'un champ obligatoire

dans le formulaire

Affichage du message d'erreur

et des donnes

Cration d'une commande


Avec succs (voir les figures suivantes).

Saisie de donnes valides dans

le formulaire

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

117/606

Affichage du message de

succs et des donnes Avec erreur (voir les figures suivantes).

Oubli de champs obligatoires et

saisie d'un montant erron dans le formulaire

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

118/606

Affichage du message d'erreur

et des donnes

Correction
Je vous propose cette solution en guise de correction. Ce n'est pas la seule manire de faire. Ne vous inquitez pas si vous avez procd diffremment, si vous avez nomm vos objets diffremment ou si vous avez bloqu sur certains lments. Le code est comment et vous est parfaitement accessible : il ne contient que des instructions et expressions que nous avons dj abordes dans les chapitres prcdents. Prenez le temps de crer votre projet depuis le dbut, puis de chercher et de coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire certains chapitres. La pratique est trs importante : ne vous jetez pas sur la solution avant d'avoir essay russi !

Le code des beans


Code : Java - com.sdzee.tp.beans.Client package com.sdzee.tp.beans; public class Client { /* Proprits du bean */ private String nom; private String prenom; private String adresse; private String telephone; private String email; public void setNom( String nom ) { this.nom = nom; } public String getNom() { return nom; } public void setPrenom( String prenom ) { this.prenom = prenom; } public String getPrenom() {

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


} return prenom;

119/606

public void setAdresse( String adresse ) { this.adresse = adresse; } public String getAdresse() { return adresse; } public String getTelephone() { return telephone; } public void setTelephone( String telephone ) { this.telephone = telephone; } public void setEmail( String email ) { this.email = email; } public String getEmail() { return email; }

Code : Java - com.sdzee.tp.beans.Commande package com.sdzee.tp.beans; public class Commande { /* Proprits du bean */ private Client client; private String date; private Double montant; private String modePaiement; private String statutPaiement; private String modeLivraison; private String statutLivraison; public Client getClient() { return client; } public void setClient( Client client ) { this.client = client; } public String getDate() { return date; } public void setDate( String date ) { this.date = date; } public Double getMontant() { return montant; } public void setMontant( Double montant ) { this.montant = montant;

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


} public String getModePaiement() { return modePaiement; } public void setModePaiement( String modePaiement ) { this.modePaiement = modePaiement; } public String getStatutPaiement() { return statutPaiement; } public void setStatutPaiement( String statutPaiement ) { this.statutPaiement = statutPaiement; } public String getModeLivraison() { return modeLivraison; } public void setModeLivraison( String modeLivraison ) { this.modeLivraison = modeLivraison; } public String getStatutLivraison() { return statutLivraison; } public void setStatutLivraison( String statutLivraison ) { this.statutLivraison = statutLivraison; }

120/606

Le code des servlets


Configuration des servlets dans le fichier web.xml : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>CreationClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationClient</servletclass> </servlet> <servlet> <servlet-name>CreationCommande</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationCommande</servletclass> </servlet> <servlet-mapping> <servlet-name>CreationClient</servlet-name> <url-pattern>/creationClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>CreationCommande</servlet-name> <url-pattern>/creationCommande</url-pattern> </servlet-mapping> </web-app>

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

121/606

Servlet grant le formulaire de cration d'un client : Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.tp.beans.Client; public class CreationClient extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Rcupration des donnes saisies, envoyes en tant que paramtres de * la requte GET gnre la validation du formulaire */ String nom = request.getParameter( "nomClient" ); String prenom = request.getParameter( "prenomClient" ); String adresse = request.getParameter( "adresseClient" ); String telephone = request.getParameter( "telephoneClient" ); String email = request.getParameter( "emailClient" ); String message; /* * Initialisation du message afficher : si un des champs obligatoires * du formulaire n'est pas renseign, alors on affiche un message * d'erreur, sinon on affiche un message de succs */ if ( nom.trim().isEmpty() || adresse.trim().isEmpty() || telephone.trim().isEmpty() ) { message = "Erreur - Vous n'avez pas rempli tous les champs obligatoires. <br> <a href=\"creerClient.jsp\">Cliquez ici</a> pour accder au formulaire de cration d'un client."; } else { message = "Client cr avec succs !"; } /* * Cration du bean Client et initialisation avec les donnes rcupres */ Client client = new Client(); client.setNom( nom ); client.setPrenom( prenom ); client.setAdresse( adresse ); client.setTelephone( telephone ); client.setEmail( email ); /* Ajout du bean et du message l'objet requte */ request.setAttribute( "client", client ); request.setAttribute( "message", message ); /* Transmission la page JSP en charge de l'affichage des donnes */ this.getServletContext().getRequestDispatcher( "/afficherClient.jsp" ).forward( request, response );

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


} }

122/606

Servlet grant le formulaire de cration d'une commande : Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; public class CreationCommande extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Rcupration des donnes saisies, envoyes en tant que paramtres de * la requte GET gnre la validation du formulaire */ String nom = request.getParameter( "nomClient" ); String prenom = request.getParameter( "prenomClient" ); String adresse = request.getParameter( "adresseClient" ); String telephone = request.getParameter( "telephoneClient" ); String email = request.getParameter( "emailClient" ); /* Rcupration de la date courante */ DateTime dt = new DateTime(); /* Conversion de la date en String selon le format dfini

*/

DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd/MM/yyyy HH:mm:ss" ); String date = dt.toString( formatter ); double montant; try { /* Rcupration du montant */ montant = Double.parseDouble( request.getParameter( "montantCommande" ) ); } catch ( NumberFormatException e ) { /* Initialisation -1 si le montant n'est pas un nombre correct */ montant = -1; } String modePaiement = request.getParameter( "modePaiementCommande" ); String statutPaiement = request.getParameter( "statutPaiementCommande" ); String modeLivraison = request.getParameter( "modeLivraisonCommande" ); String statutLivraison = request.getParameter( "statutLivraisonCommande" );

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


String message; /* * Initialisation du message afficher : si un des champs obligatoires * du formulaire n'est pas renseign, alors on affiche un message * d'erreur, sinon on affiche un message de succs */ if ( nom.trim().isEmpty() || adresse.trim().isEmpty() || telephone.trim().isEmpty() || montant == -1 || modePaiement.isEmpty() || modeLivraison.isEmpty() ) { message = "Erreur - Vous n'avez pas rempli tous les champs obligatoires. <br> <a href=\"creerCommande.jsp\">Cliquez ici</a> pour accder au formulaire de cration d'une commande."; } else { message = "Commande cre avec succs !"; } /* * Cration des beans Client et Commande et initialisation avec les * donnes rcupres */ Client client = new Client(); client.setNom( nom ); client.setPrenom( prenom ); client.setAdresse( adresse ); client.setTelephone( telephone ); client.setEmail( email ); Commande commande = new Commande(); commande.setClient( client ); commande.setDate( date ); commande.setMontant( montant ); commande.setModePaiement( modePaiement ); commande.setStatutPaiement( statutPaiement ); commande.setModeLivraison( modeLivraison ); commande.setStatutLivraison( statutLivraison ); /* Ajout du bean et du message l'objet requte */ request.setAttribute( "commande", commande ); request.setAttribute( "message", message ); /* Transmission la page JSP en charge de l'affichage des donnes */ this.getServletContext().getRequestDispatcher( "/afficherCommande.jsp" ).forward( request, response ); } }

123/606

Le code des JSP


Page d'affichage d'un client : Code : JSP - afficherClient.jsp <%@ page pageEncoding="UTF-8" <!DOCTYPE html> <html> <head> <meta charset="utf-8" <title>Affichage d'un <link type="text/css" /> </head> %>

/> client</title> rel="stylesheet" href="inc/style.css"

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE


</head> <body> <%-- Affichage de la chane "message" transmise par la servlet --%> <p class="info">${ message }</p> <%-- Puis affichage des donnes enregistres dans le bean "client" transmis par la servlet --%> <p>Nom : ${ client.nom }</p> <p>Prnom : ${ client.prenom }</p> <p>Adresse : ${ client.adresse }</p> <p>Numro de tlphone : ${ client.telephone }</p> <p>Email : ${ client.email }</p> </body> </html>

124/606

Page d'affichage d'une commande : Code : JSP - afficherCommande.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'une commande</title> <link type="text/css" rel="stylesheet" href="inc/style.css" /> </head> <body> <%-- Affichage de la chane "message" transmise par la servlet --%> <p class="info">${ message }</p> <%-- Puis affichage des donnes enregistres dans le bean "commande" transmis par la servlet --%> <p>Client</p> <%-- Les 5 expressions suivantes accdent aux proprits du client, qui est lui-mme une proprit du bean commande --%> <p>Nom : ${ commande.client.nom }</p> <p>Prnom : ${ commande.client.prenom }</p> <p>Adresse : ${ commande.client.adresse }</p> <p>Numro de tlphone : ${ commande.client.telephone }</p> <p>Email : ${ commande.client.email }</p> <p>Commande</p> <p>Date : ${ commande.date }</p> <p>Montant : ${ commande.montant }</p> <p>Mode de paiement : ${ commande.modePaiement }</p> <p>Statut du paiement : ${ commande.statutPaiement }</p> <p>Mode de livraison : ${ commande.modeLivraison }</p> <p>Statut de la livraison : ${ commande.statutLivraison }</p> </body> </html>

Encore une fois, prenez votre temps, lisez bien et analysez attentivement les codes. Ils vous serviront de base pour les prochaines tapes du fil rouge !

www.siteduzero.com

Partie 2 : Premiers pas avec Java EE

125/606

Partie 3 : Une bonne vue grce la JSTL


Aprs une brve introduction sur les objectifs de la JSTL, dcouvrez-ici sa mise en place dans un projet, les bases de sa bibliothque principale et la manipulation de documents XML.

Objectifs et configuration
Aprs une brve introduction sur quelques concepts intervenant dans la suite de ce cours, et sur les versions de la JSTL, vous allez dcouvrir ici les fichiers de configuration cls de votre projet ainsi que les paramtres importants modifier pour mettre en place la bibliothque dans votre projet web Java EE.

C'est sa raison d'tre


La JSTL est une bibliothque, une collection regroupant des balises implmentant des fonctionnalits des fins gnrales, communes aux applications web. Citons par exemple la mise en place de boucles, de tests conditionnels, le formatage des donnes ou encore la manipulation de donnes XML. Son objectif est de permettre au dveloppeur d'viter l'utilisation de code Java dans les pages JSP, et ainsi de respecter au mieux le dcoupage en couches recommand par le modle MVC. En apparence, ces balises ressemblent comme deux gouttes d'eau aux balises JSP que vous avez dcouvertes dans les chapitres prcdents ! La liste d'avantages que je vous prsente ci-dessous n'est probablement pas exhaustive. Je vais tenter de vous faire comprendre l'intrt de l'utilisation des balises en vous exposant les aspects positifs qui me semblent les plus importants, et vous illustrer pourquoi l'utilisation de code Java dans vos pages JSP est dconseille.

Lisibilit du code produit


Un des gros avantages de l'utilisation des balises JSTL, c'est sans aucun doute la lisibilit du code, et donc sa maintenabilit. Un exemple tant bien plus parlant que des mots, voici une simple boucle dans une JSP, d'abord en Java ( base de scriptlet donc), puis via des balises JSTL. Ne vous inquitez pas de voir apparatre des notations qui vous sont, pour le moment, inconnues : les explications viendront par la suite.

Une boucle avec une scriptlet Java


Code : JSP <%@ page import="java.util.List, java.util.ArrayList" %> <% List<Integer> list = (ArrayList<Integer>)request.getAttribute("tirage"); for(int i = 0; i < list.size();i++){ out.println(list.get(i)); } %>

Pas besoin de vous faire un dessin : c'est du Java

La mme boucle avec des tags JSTL


Code : JSP <c:forEach var="item" items="${tirage}" > <c:out value="${item}" /> </c:forEach>

La boucle ainsi ralise est nettement plus lisible ; elle ne fait plus intervenir d'attributs et de mthodes Java comme size(), get() ou encore des dclarations de variable, ni de types d'objets (List, ArrayList, Date, etc.), mais uniquement des balises la syntaxe proche du XML qui ne gnent absolument pas la lecture du code et de la structure de la page. Pour information, mais vous le saurez bien assez tt, la bibliothque de balises (on parle souvent de tags) ici utilise, indique par le prfixe c:, est la bibliothque Core, que vous dcouvrirez dans le chapitre suivant.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

126/606

Moins de code crire


Un autre gros avantage de l'utilisation des balises issues des bibliothques standard est la rduction de la quantit de code crire. En effet, moins vous aurez crire de code, moins vous serez susceptibles d'introduire des erreurs dans vos pages. La syntaxe de nombreuses actions est simplifie et raccourcie en utilisant la JSTL, ce qui permet d'viter les problmes dus des fautes de frappe ou d'inattention dans des scripts en Java. En outre, l'usage des scriptlets (le code Java entour de <% %>) est fortement dconseill, et ce depuis l'apparition des TagLibs (notamment la JSTL) et des EL, soit depuis une dizaine d'annes maintenant. Les principaux inconvnients des scriptlets sont les suivants : 1. Rutilisation : il est impossible de rutiliser une scriptlet dans une autre page, il faut la dupliquer. Cela signifie que lorsque vous avez besoin d'effectuer le mme traitement dans une autre page JSP, vous n'avez pas d'autre choix que de recopier le morceau de code dans l'autre page, et ce pour chaque page ncessitant ce bout de code. La duplication de code dans une application est, bien entendu, l'ennemi du bien : cela compromet normment la maintenance de l'application. 2. Interface : il est impossible de rendre une scriptlet abstract. 3. POO : il est impossible dans une scriptlet de tirer parti de l'hritage ou de la composition. 4. Debug : si une scriptlet envoie une exception en cours d'excution, tout s'arrte et l'utilisateur rcupre une page blanche 5. Tests : on ne peut pas crire de tests unitaires pour tester les scriptlets. Lorsqu'un dveloppeur travaille sur une application relativement large, il doit s'assurer que ses modifications n'impactent pas le code existant et utilise pour cela une batterie de tests dits "unitaires", qui ont pour objectif de vrifier le fonctionnement des diffrentes mthodes implmentes. Eh bien ceux-ci ne peuvent pas s'appliquer au code Java crit dans une page JSP : l encore, cela compromet normment la maintenance et l'volutivit de l'application. 6. Maintenance : inluctablement, il faut passer normment plus de temps maintenir un code mlang, encombr, dupliqu et non testable !

titre informatif, la maison mre Oracle elle-mme recommande dans ses JSP coding conventions d'viter l'utilisation de code Java dans une JSP autant que possible, notamment via l'utilisation de balises : Citation : Extrait des conventions de codage JSP Where possible, avoid JSP scriptlets whenever tag libraries provide equivalent functionality. This makes pages easier to read and maintain, helps to separate business logic from presentation logic, and will make your pages easier to evolve [...]

Vous avez dit MVC ?


Ne plus crire de Java directement dans vos JSP
V ous l'avez probablement remarqu dans les exemples prcdents : le Java complique normment la lecture d'une page JSP. Certes, ici je ne vous ai prsent qu'une gentille petite boucle, donc la diffrence n'est pas si flagrante. Mais imaginez que vous travailliez sur un projet de plus grande envergure, mettant en jeu des pages HTML avec un contenu autrement plus riche, voire sur un projet dans le cadre duquel vous n'tes pas l'auteur des pages que vous avez maintenir ou modifier : que prfreriezvous manipuler ? Sans aucune hsitation, lorsque les balises JSTL sont utilises, la taille des pages est fortement rduite. La comprhension et la maintenance s'en retrouvent grandement facilites.

Rendre la vue son vrai rle


Soyez bien conscients d'une chose : je ne vous demande pas de proscrire le Java de vos pages JSP juste pour le plaisir des yeux ! Si je vous encourage procder ainsi, c'est pour vous faire prendre de bonnes habitudes : la vue, en l'occurrence nos JSP, ne doit se consacrer qu' l'affichage. Ne pas avoir dclarer de mthodes dans une JSP, ne pas modifier directement des donnes depuis une JSP, ne pas y insrer de traitement mtier tout cela est recommand, mais la frontire peut paratre bien mince si on se laisse aller utiliser des scriptlets Java ds que l'occasion se prsente. Avec les tags JSTL, la sparation est bien plus nette. Un autre point positif, qui ne vous concerne pas vraiment si vous ne travaillez pas en entreprise sur des projets de grande

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

127/606

envergure, est que le modle MVC permet une meilleure sparation des couches de l'application. Par exemple, imaginez une application dont le code Java est bien cach dans la couche mtier (au hasard, dans des beans) : le(s) programmeur(s) UI trs performant(s) en interface utilisateur peu(ven)t donc se baser sur la simple documentation du code mtier pour travailler sur la couche de prsentation en crant les vues, les JSP donc, et ce sans avoir crire ni lire de Java, langage qu'ils ne matrisent pas aussi bien, voire pas du tout.

retenir
Si vous ne deviez retenir qu'une phrase de tout cela, c'est que bafouer MVC en crivant du code Java directement dans une JSP rend la maintenance d'une application extrmement complique, et par consquent rduit fortement son volutivit. Libre vous par consquent de dcider de l'avenir que vous souhaitez donner votre projet, en suivant ou non les recommandations. Dernire couche : on crit du code Java directement dans une JSP uniquement lorsqu'il nous est impossible de faire autrement, ou lorsque l'on dsire vrifier un fonctionnement via une simple feuille de tests ; et enfin pourquoi pas lorsque l'on souhaite rapidement crire un prototype temporaire afin de se donner une ide du fonctionnement d'une application de trs faible envergure. V oil, j'espre que maintenant vous l'avez bien assimil, ce n'est pas faute de vous l'avoir rpt

Plusieurs versions
La JSTL a fait l'objet de plusieurs versions : JSTL 1.0 pour la plate-forme J2EE 3, et un conteneur JSP 1.2 (ex: Tomcat 4) ; JSTL 1.1 pour la plate-forme J2EE 4, et un conteneur JSP 2.0 (ex: Tomcat 5.5) ; JSTL 1.2, qui est partie intgrante de la plate-forme Java EE 6, avec un conteneur JSP 2.1 ou 3.0 (ex: Tomcat 6 et 7) . Les diffrences entre ces versions rsident principalement dans le conteneur JSP ncessaire. Le changement majeur retenir dans le passage de la premire version la seconde version de ce conteneur, c'est la gestion de la technologie EL. Le conteneur JSP 1.2 sur lequel est base la JSTL 1.0 ne grait pas les expressions EL, cette dernire proposait donc deux implmentations pour pallier ce manque : une les interprtant et l'autre non. Ceci se traduisait alors par l'utilisation d'adresses diffrentes lors de la dclaration des bibliothques, nous allons revenir sur cela un petit peu plus loin. La version 1.1 est base sur le conteneur JSP 2.0, qui intgre nativement un interprteur d'expressions EL, et ne propose par consquent plus qu'une seule implmentation. Ce tutoriel se base quant lui sur la version actuelle, savoir la JSTL 1.2, qui d'aprs le site officiel apporte des EL "unifies", ainsi qu'une meilleure intgration dans le framework JSF. Ces changements par rapport la prcdente version n'ont aucun impact sur ce cours : tout ce qui suit sera valable, que vous souhaitiez utiliser la version 1.1 ou 1.2 de la JSTL.

Configuration Configuration de la JSTL


Il y a plusieurs choses que vous devez savoir ici. Plutt que de vous donner tout de suite les solutions aux problmes qui vous attendent, fonons ttes baisses, et je vous guiderai lorsque cela savrera ncessaire. On apprend toujours mieux en faisant des erreurs et en apprenant les corriger, qu'en suivant btement une srie de manipulations.

D'erreur en erreur
Allons-y gaiement donc, et tentons navement d'insrer une balise JSTL ni vu ni connu dans notre belle et vierge page JSP : Code : JSP - Une balise JSTL dans notre page <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <title>Test</title> </head> <body> <c:out value="test" /> </body>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</html>

128/606

Pour le moment, cette notation vous est inconnue, nous y reviendrons en temps voulu. V ous pouvez d'ores et dj constater que cette balise a une syntaxe trs similaire celle des actions standard JSP. Pour votre information seulement, il s'agit ici d'un tag JSTL issu de la bibliothque Core, permettant d'afficher du texte dans une page. Relativement basique donc Basique, sur le principe, oui. Mais Eclipse vous signale alors une premire erreur (voir la figure suivante).

Warning Eclipse : balise inconnue ! Il ne connat visiblement pas cette balise. Et pour cause : puisqu'il est issu d'une bibliothque (la JSTL), il est ncessaire de prciser Eclipse o ce tag est rellement dfini ! Et si vous avez suivi la partie prcdente de ce cours, vous devez vous souvenir d'une certaine directive JSP, destine inclure des bibliothques a vous revient en mmoire ? Tout juste, c'est la directive taglib que nous allons utiliser ici. V oici donc notre code modifi pour inclure la bibliothque Core : Code : JSP - Ajout de la directive taglib <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <title>Test</title> </head> <body> <c:out value="test" /> </body> </html>

tudions cette directive : dans le paramtre uri se trouve le lien vers la dfinition de la bibliothque. Remarquez bien ici l'arborescence de ce lien : /jsp/jstl/core. Si vous travaillez sur des codes qui ne sont pas de vous, vous serez ventuellement amens rencontrer dans cette balise un lien de la forme /jstl/core. Sachez que ce type de lien est celui de la version antrieure 1.0 de la JSTL. En effet, le dossier jsp a t rajout afin d'viter toute ambigut avec les prcdentes versions qui, comme je vous l'ai prcis en premire partie, ne graient pas les EL. Faites bien attention utiliser le bon lien selon la version de la JSTL que vous souhaitez utiliser, sous peine de vous retrouver avec des erreurs peu comprhensibles... dans le paramtre prefix se trouve l'alias qui sera utilis dans notre page JSP pour faire appel aux balises de la bibliothque en question. Concrtement, cela signifie que si je souhaite appeler le tag if de la bibliothque Core, je dois crire <c:if>. Si j'avais entr "core" dans le champ prefix de la directive au lieu de "c", j'aurais alors d crire <core:if>. L, je suppose que vous vous apprtez me jeter des bches. En effet, s'il est vrai qu'Eclipse vous signalait une alerte auparavant, vous vous retrouvez maintenant avec une nouvelle erreur en plus de la prcdente (voir la figure suivante) !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

129/606

Erreur Eclipse : bibliothque introuvable ! Effectivement, nouvelle erreur. Pourquoi ?

Eh bien cette fois, c'est Tomcat qui est en cause ! Lorsque je vous avais prsent Tomcat, je vous avais bien prcis qu'il n'tait pas un serveur d'applications Java EE au sens complet du terme. Nous voil devant une premire illustration de cet tat de fait : alors que la JSTL fait partie intgrante de la plate-forme Java EE 6, Tomcat 7 n'est pas, par dfaut, livr avec la JSTL. Si vous utilisez par exemple le serveur Glassfish d'Oracle, qui quant lui respecte bien les spcifications Java EE, vous ne rencontrerez pas de problme : la JSTL y est bien incluse. La lumire tant faite sur l'origine de cette erreur, il est temps de la corriger. Maintenant que nous avons prcis la dfinition de notre bibliothque, il faut dfinir quelque part o se situe physiquement cette bibliothque, et donc configurer notre projet afin qu'il puisse accder ses fichiers sources. Si vous tes un peu curieux et que vous vous souvenez de ce que nous avons d faire pour utiliser l'API JodaTime dans la partie prcdente, vous avez probablement dj remarqu que dans le dossier /WEBINF de votre projet, il y a un dossier nomm... lib ! Le chemin semble donc tout trac : nous devons aller chercher notre bibliothque. O la trouver ? J'y reviendrai dans le chapitre suivant, la JSTL contient nativement plusieurs bibliothques, et Core est l'une d'entre elles. Par consquent, c'est l'archive jar de la JSTL tout entire que nous allons devoir ajouter notre projet. V ous pouvez tlcharger le jar jstl-1.2.jar en cliquant sur ce lien de tlchargement direct. V ous voil donc en possession du fichier que vous allez devoir copier dans votre rpertoire lib, comme indiqu la figure suivante.

Ajout du jar JSTL notre projet Eclipse

a commence bien faire, nous tournons en rond ! Nous avons inclus notre bibliothque, mais nous avons toujours nos deux erreurs ! Que s'est-il pass ? Pas d'inquitude, nous apercevons le bout du tunnel... Effectivement, Eclipse vous crie toujours dessus. Mais ce n'est cette fois que pure illusion ! Note : je ne suis pas vraiment certain de la raison pour laquelle Eclipse ne met pas directement ses avertissements jour. J'imagine que l'environnement a besoin d'une modification postrieure la mise en place des bibliothques pour prendre en compte compltement la modification. Bref, modifiez simplement votre page JSP, en y ajoutant un simple espace ou ce que vous voulez, et sauvez. Comme par magie, Eclipse cesse alors de vous crier dessus ! Il ne vous reste plus qu' dmarrer votre Tomcat si ce n'est pas dj fait, et vrifier que tout se passe bien, en accdant votre JSP depuis votre navigateur via l'adresse http://localhost:8080/TestJSTL/test.jsp. Le mot "test" devrait alors s'afficher : flicitations, vous venez de mettre en place et utiliser avec succs votre premier tag JSTL !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


la JSTL est compose de cinq bibliothques de balises standard ; elle permet d'viter l'utilisation de code Java dans les pages JSP ; elle permet de rduire la quantit de code crire ; elle rend le code des pages JSP plus lisible ; sous Tomcat, il faut placer son fichier .jar sous /WEB-INF/lib pour qu'elle soit correctement intgre.

130/606

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

131/606

La bibliothque Core
Nous voici prts tudier la bibliothque Core, offrant des balises pour les principales actions ncessaires dans la couche prsentation d'une application web. Ce chapitre va en quelque sorte faire office de documentation : je vais vous y prsenter les principales balises de la bibliothque, et expliciter leur rle et comportement via des exemples simples. Lorsque ces bases seront poses, nous appliquerons ce que nous aurons dcouvert ici dans un TP. S'il est vrai que l'on ne peut se passer de la thorie, pratiquer est galement indispensable si vous souhaitez assimiler et progresser.

Les variables et expressions


Pour commencer, nous allons apprendre comment afficher le contenu d'une variable ou d'une expression, et comment grer une variable et sa porte. Avant cela, je vous donne ici la directive JSP ncessaire pour permettre l'utilisation des balises de la bibliothque Core dans vos pages : Code : JSP <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Cette directive devra tre prsente sur chacune des pages de votre projet utilisant les balises JSTL que je vous prsente dans ce chapitre. Dans un prochain chapitre, nous verrons comment il est possible de ne plus avoir se soucier de cette commande. En attendant, ne l'oubliez pas !

Affichage d'une expression


La balise utilise pour l'affichage est <c:out value="" />. Le seul attribut obligatoirement requis pour ce tag est value. Cet attribut peut contenir une chane de caractres simple, ou une expression EL. V oici quelques exemples : Code : JSP <c:out value="test" /> <%-- Affiche test --%> <c:out value="${ 'a' < 'b' }" /> <%-- Affiche true --%>

celui-ci s'ajoutent deux attributs optionnels : default : permet de dfinir une valeur affiche par dfaut si le contenu de l'expression value est vide ; escapeXml : permet de remplacer les caractres de scripts < , > , " , ' et & par leurs quivalents en code html &lt;, &gt;, &#034;, &#039;, &amp;. Cette option est active par dfaut, et vous devez expliciter <c:out ... escapeXml="false" /> pour la dsactiver. Pourquoi utiliser une balise pour afficher simplement du texte ou une expression ?

C'est une question lgitime. Aprs tout c'est vrai, pourquoi ne pas directement crire le texte ou l'expression dans notre page JSP ? Pourquoi s'embter inclure le texte ou l'expression dans cette balise ? Eh bien la rponse se trouve dans l'explication de l'attribut optionnel escapeXml : celui-ci est activ par dfaut ! Cela signifie que l'utilisation de la balise <c:out> permet d'chapper automatiquement les caractres spciaux de nos textes et rendus d'expressions, et c'est l une excellente raison d'utilisation (voir ci-dessous l'avertissement concernant les failles XSS). V oici des exemples d'utilisation de l'attribut default : Code : JSP <%-- Cette balise affichera le mot 'test' si le bean n'existe pas : --%> <c:out value="${bean}"> test </c:out> <%-- Elle peut galement s'crire sous cette forme : --%>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<c:out value="${bean}" default="test" /> <%-- Et il est interdit d'crire : --%> <c:out value="${bean}" default="test"> une autre chaine </c:out>

132/606

Pour le dernier cas, l'explication est simple : l'attribut default jouant dj le rle de valeur par dfaut, le corps du tag ne peut exister que lorsqu'aucune valeur par dfaut n'est dfinie. Pour information, Eclipse vous signalera une erreur si vous tentez d'crire la balise sous cette forme. Pour en finir avec cette balise, voici un exemple d'utilisation de l'attribut escapeXml : Code : JSP <%-- Sans prciser d'attribut escapeXml : --%> <c:out value="<p>Je suis un 'paragraphe'.</p>" /> <%-- La balise affichera : --%> &lt;p&gt;Je suis un &#039;paragraphe&#039;.&lt;/p&gt; <%-- Et en prcisant l'attribut false :--%> <c:out value="<p>Je suis un 'paragraphe'.</p>" escapeXml="false" /> <%-- La balise affichera : --%> <p>Je suis un 'paragraphe'.</p>

V ous pouvez constater dans cet exemple l'importance de l'activation par dfaut de l'option escapeXml : elle empche l'interprtation de ce qui est affich par le navigateur, en modifiant les lments de code HTML prsents dans le contenu trait (en l'occurrence les caractres <, > et '). V ous devez prendre l'habitude d'utiliser ce tag JSTL lorsque vous affichez des variables, notamment lorsqu'elles sont rcupres depuis le navigateur, c'est--dire lorsqu'elles sont saisies par l'utilisateur. Prenons l'exemple d'un formulaire : Code : JSP <%-- Mauvais exemple --%> <input type="text" name="donnee" value="${donneeSaisieParUnUtilisateur}" /> <%-- Bon exemple --%> <input type="text" name="donnee" value="<c:out value="${donneeSaisieParUnUtilisateur}"/>" />

Nous le dcouvrirons plus tard, mais sachez que les donnes rcupres depuis un formulaire sont potentiellement dangereuses, puisqu'elles permettent des attaques de type XSS ou d'injection de code. L'utilisation du tag <c:out> permet d'chapper les caractres spciaux responsables de cette faille, et ainsi de prvenir tout risque ce niveau. Ne vous posez pas trop de questions au sujet de cet exemple, nous reviendrons en dtail sur cette faille dans le chapitre sur les formulaires.

Gestion d'une variable


Avant de parler variable, revenons sur leur porte ! La porte (ou visibilit) d'une variable correspond concrtement l'endroit dans lequel elle est stocke, et par corollaire aux endroits depuis lesquels elle est accessible. Selon la porte affecte votre variable, elle sera par exemple accessible depuis toute votre application, ou seulement depuis une page particulire, etc. Il y a quatre portes diffrentes (ou scopes en anglais), que vous connaissez dj et redcouvrirez au fur et mesure des exemples de ce chapitre : la page : les objets crs avec la porte page ne sont accessibles que depuis cette mme page, et une fois la rponse retourne au navigateur ces donnes ne sont plus accessibles ; la requte : les objets crs avec la porte request ne sont accessibles que depuis les pages qui traitent cette mme

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

133/606

requte. Si la requte est transmise une autre page, les donnes sont conserves, mais sont perdues en cas de redirection ; la session : les objets crs avec la porte session ne sont accessibles que depuis les pages traitant les requtes cres dans cette mme session. Concrtement, une session correspond la dure pendant laquelle un visiteur va utiliser l'application, cette dure se terminant lorsque l'utilisateur ferme son navigateur ou encore lorsque l'application le dcide (le dveloppeur, pour tre exact) ; par exemple via un lien de dconnexion ou encore un temps maximum de validit impos aprs lequel la session est automatiquement dtruite. Les donnes ainsi cres ne sont plus accessibles une fois que le visiteur quitte le site ; l'application : les objets crs avec la porte application sont accessibles depuis toutes les pages JSP de l'application web ! C'est en quelque sorte une variable globale, accessible partout.

Cration
La balise utilise pour la cration d'une variable est <c:set>. Abordons pour commencer la mise en place d'un attribut dans la requte. En JSP/servlets, vous savez tous faire a, mais qu'en est-il avec la JSTL ? Il suffit d'utiliser les trois attributs suivants : var, value et scope. Code : JSP <%-- Cette balise met l'expression "Salut les zros !" dans l'attribut "message" de la requte : --%> <c:set var="message" value="Salut les zros !" scope="request" /> <%-- Et est l'quivalent du scriplet Java suivant : --%> <% request.setAttribute( "message", "Salut les zros !" ); %>

L'attribut var contient le nom de la variable que l'on veut stocker, value sa valeur, et scope la porte de cette variable. Simple, rapide et efficace ! V oyons maintenant comment rcuprer cette valeur pour l'afficher l'utilisateur, par exemple : Code : JSP <%-- Affiche l'expression contenue dans la variable "message" de la requte --%> <c:out value="${requestScope.message}" />

V ous remarquerez que nous utilisons ici dans l'expression EL l'objet implicite requestScope, qui permet de rechercher un objet dans la porte requte uniquement. Les plus avertis d'entre vous ont peut-tre tent d'accder la valeur frachement cre via un simple <c:out value="${ message }"/>. Et effectivement, dans ce cas cela fonctionne galement. Pourquoi ? Nous retrouvons ici une illustration du mcanisme dont je vous ai parl lorsque nous avons appliqu les EL dans notre code d'exemple. Par dfaut, si le terme de l'expression n'est ni un type primitif ( int, char, boolean, etc.) ni un objet implicite de la technologie EL, l'expression va d'elle-mme chercher un attribut correspondant ce terme dans les diffrentes portes de votre application : page, puis request, puis session et enfin application. Souvenez-vous : je vous avais expliqu que c'est grce l'objet implicite pageContext que le mcanisme parcourt toutes les portes, et qu'il renvoie alors automatiquement le premier objet trouv lors de son parcours. V oil donc pourquoi cela fonctionne avec la seconde criture : puisque nous ne prcisons pas de porte, l'expression EL les parcourt automatiquement une par une jusqu' ce qu'elle trouve un objet nomm message, et nous le renvoie ! N'oubliez pas : la bonne pratique veut que vous ne donniez pas le mme nom deux variables diffrentes, prsentes dans des portes diffrentes. Toutefois, afin d'viter toute confusion si jamais des variables aux noms identiques venaient coexister, il est galement conseill de n'utiliser la seconde criture que lorsque vous souhaitez faire rfrence des attributs de porte page, et d'utiliser la premire criture que je vous ai prsente pour le reste (session, request et application).

Modification

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

134/606

La modification d'une variable s'effectue de la mme manire que sa cration. Ainsi, le code suivant crera une variable nomme "maVariable" si elle n'existe pas dj, et initialisera son contenu "12" : Code : JSP <%-- L'attribut scope n'est pas obligatoire. Rappelez-vous, le scope par dfaut est dans ce cas la page, puisque c'est le premier dans la liste des scopes parcourus --%> <c:set var="maVariable" value="12" />

Pour information, il est galement possible d'initialiser une variable en utilisant le corps de la balise, plutt qu'en utilisant l'attribut value : Code : JSP <c:set var="maVariable"> 12 </c:set>

ce sujet, sachez d'ailleurs qu'il est possible d'imbriquer d'autres balises dans le corps de cette balise, et pas seulement d'utiliser de simples chanes de caractres ou expressions. V oici par exemple comment vous pourriez initialiser la valeur d'une variable de session depuis une valeur lue dans un paramtre de l'URL : Code : JSP <c:set var="locale" scope="session"> <c:out value="${param.lang}" default="FR"/> </c:set>

Plusieurs points importants ici : vous constatez bien ici l'utilisation de la balise <c:out> l'intrieur du corps de la balise <c:set> ; vous pouvez remarquer l'utilisation de l'objet implicite param, pour rcuprer la valeur du paramtre de la requte nomm lang ; si le paramtre lang n'existe pas ou s'il est vide, c'est la valeur par dfaut "FR" spcifie dans notre balise <c:out> qui sera utilise pour initialiser notre variable en session.

Modification des proprits d'un objet


Certains d'entre vous se demandent probablement comment il est possible de dfinir ou modifier une valeur particulire lorsqu'on travaille sur certains types d'objets... Et ils ont bien raison ! En effet, avec ce que je vous ai prsent pour le moment, vous tes capables de dfinir une variable de n'importe quel type, type qui est dfini par l'expression que vous crivez dans l'attribut value du tag <c:set> : Code : JSP <%-- Cre un objet de type String --%> <c:set scope="session" var="description" value="Je suis une loutre." /> <%-- Cre un objet du type du bean ici spcifi dans l'attribut 'value'--%> <c:set scope="session" var="tonBean" value="${monBean}" />

Et c'est ici que vous devez vous poser la question suivante : comment modifier les proprits du bean cr dans cet exemple ? En effet, il vous manque deux attributs pour y parvenir ! Regardons donc de plus prs quels sont ces attributs, et comment ils fonctionnent :

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


target : contient le nom de l'objet dont la proprit sera modifie ; property : contient le nom de la proprit qui sera modifie. Code : JSP <!-- Dfinir ou modifier la proprit 'prenom' du bean 'coyote' --> <c:set target="${coyote}" property="prenom" value="Wile E."/> <!-- Dfinir ou modifier la proprit 'prenom' du bean 'coyote' via le corps de la balise --> <c:set target="${coyote}" property="prenom"> Wile E. </c:set> <!-- Passer null la valeur de la proprit 'prenom' du bean 'coyote' --> <c:set target="${coyote}" property="prenom" value="${null}" />

135/606

Remarquez dans le dernier exemple qu'il suffit d'utiliser une EL avec pour mot-cl null dans l'attribut value pour faire passer la valeur d'une proprit null. Pour information, lorsque l'objet trait n'est pas un bean mais une simple Map, cette action a pour effet de directement supprimer l'entre de la Map concerne : le comportement est alors identique avec la balise prsente dans le paragraphe suivant.

Suppression
Dernire tape : supprimer une variable. Une balise est ddie cette tche, avec pour seul attribut requis var. Par dfaut toujours, c'est le scope page qui sera parcouru si l'attribut scope n'est pas explicit : Code : JSP <%-- Supprime la variable "maVariable" de la session --%> <c:remove var="maVariable" scope="session" />

V oil dj un bon morceau de fait ! Ne soyez pas abattus si vous n'avez pas tout compris lorsque nous avons utilis des objets implicites. Nous y reviendrons de toute manire quand nous en aurons besoin dans nos exemples, et vous comprendrez alors avec la pratique.

Les conditions Une condition simple


La JSTL fournit deux moyens d'effectuer des tests conditionnels. Le premier, simple et direct, permet de tester une seule expression, et correspond au bloc if() du langage Java. Le seul attribut obligatoire est test. Code : JSP <c:if test="${ 12 > 7 }" var="maVariable" scope="session"> Ce test est vrai. </c:if>

Ici, le corps de la balise est une simple chane de caractres. Elle ne sera affiche dans la page finale que si la condition est vraie, savoir si l'expression contenue dans l'attribut test renvoie true. Ici, c'est bien entendu le cas, 12 est bien suprieur 7. Les attributs optionnels var et scope ont ici sensiblement le mme rle que dans la balise <c:set>. Le rsultat du test conditionnel sera stock dans la variable et dans le scope dfini, et sinon dans le scope page par dfaut. L'intrt de cette utilisation rside principalement dans le stockage des rsultats de tests coteux, un peu la manire d'un cache, afin de pouvoir les rutiliser en accdant simplement des variables de scope.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

136/606

Des conditions multiples


La seconde mthode fournie par la JSTL est utile pour traiter les conditions mutuellement exclusives, quivalentes en Java une suite de if() / else if() ou au bloc switch(). Elle est en ralit constitue de plusieurs balises : Code : JSP <c:choose> <c:when test="${expression}">Action ou texte.</c:when> ... <c:otherwise>Autre action ou texte.</c:otherwise> </c:choose>

La balise <c:choose> ne peut contenir aucun attribut, et son corps ne peut contenir qu'une ou plusieurs balises <c:when> et une ou zro balise <c:otherwise>. La balise <c:when> ne peut exister qu' l'intrieur d'une balise <c:choose>. Elle est l'quivalent du mot-cl case en Java, dans un bloc switch(). Tout comme la balise <c:if>, elle doit obligatoirement se voir dfinir un attribut test contenant la condition. l'intrieur d'un mme bloc <c:choose>, un seul <c:when> verra son corps valu, les conditions tant mutuellement exclusives. La balise <c:otherwise> ne peut galement exister qu' l'intrieur d'une balise <c:choose>, et aprs la dernire balise <c:when>. Elle est l'quivalent du mot-cl default en Java, dans un bloc switch(). Elle ne peut contenir aucun attribut, et son corps ne sera valu que si aucune des conditions la prcdant dans le bloc n'est vrifie. V oil pour les conditions avec la JSTL. Je ne pense pas qu'il soit ncessaire de prendre plus de temps ici, la principale diffrence avec les conditions en Java tant la syntaxe utilise.

Les boucles
Abordons prsent la question des boucles. Dans la plupart des langages, les boucles ont une syntaxe similaire : for, while, do/ while... Avec la JSTL, deux choix vous sont offerts, en fonction du type d'lment que vous souhaitez parcourir avec votre boucle : <c:forEach> pour parcourir une collection, et <c:forTokens> pour parcourir une chane de caractres.

Boucle "classique"
Prenons pour commencer une simple boucle for en scriptlet Java, affichant un rsultat format dans un tableau HTML par exemple : Code : JSP - Une boucle sans la JSTL <%-- Boucle calculant le cube des entiers de 0 7 et les affichant dans un tableau HTML --%> <table> <tr> <th>Valeur</th> <th>Cube</th> </tr> <% int[] cube= new int[8]; /* Boucle calculant et affichant le cube des entiers de 0 7 */ for(int i = 0 ; i < 8 ; i++) { cube[i] = i * i * i; out.println("<tr><td>" + i + "</td> <td>" + cube[i] + "</td></tr>"); } %> </table>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

137/606

Avec la JSTL, si l'on souhaite raliser quelque chose d'quivalent, il faudrait utiliser la syntaxe suivante : Code : JSP - Une boucle avec la JSTL <%-- Boucle calculant le cube des entiers de 0 7 et les affichant dans un tableau HTML --%> <table> <tr> <th>Valeur</th> <th>Cube</th> </tr> <c:forEach var="i" begin="0" end="7" step="1"> <tr> <td><c:out value="${i}"/></td> <td><c:out value="${i * i * i}"/></td> </tr> </c:forEach> </table>

Avant tout, on peut dj remarquer la clart du second code par rapport au premier : les balises JSTL s'intgrent trs bien au formatage HTML englobant les rsultats. On devine rapidement ce que produira cette boucle, ce qui tait bien moins vident avec le code en Java, pourtant tout aussi basique. tudions donc les attributs de cette fameuse boucle : begin : la valeur de dbut de notre compteur (la valeur de i dans la boucle en Java, initialise zro en l'occurrence) ; end : la valeur de fin de notre compteur. V ous remarquez ici que la valeur de fin est 7 et non pas 8, comme c'est le cas dans la boucle Java. La raison est simple : dans la boucle Java en exemple j'ai utilis une comparaison stricte (i strictement infrieur 8), alors que la boucle JSTL ne procde pas par comparaison stricte (i infrieur ou gal 7). J'aurais certes pu crire i <= 7 dans ma boucle Java, mais je n'ai par contre pas le choix dans ma boucle JSTL, c'est uniquement ainsi. Pensez-y, c'est une erreur bte mais facile commettre si l'on oublie ce comportement ; step : c'est le pas d'incrmentation de la boucle. Concrtement, si vous changez cette valeur de 1 3 par exemple, alors le compteur de la boucle ira de 3 en 3 et non plus de 1 en 1. Par dfaut, si vous ne spcifiez pas l'attribut step, la valeur 1 sera utilise ; var : cet attribut est, contrairement ce qu'on pourrait croire a priori , non obligatoire. Si vous ne le spcifiez pas, vous ne pourrez simplement pas accder la valeur du compteur en cours (via la variable i dans notre exemple). V ous pouvez choisir de ne pas prciser cet attribut si vous n'avez pas besoin de la valeur du compteur l'intrieur de votre boucle. Par ailleurs, tout comme en Java lorsqu'on utilise une syntaxe quivalente l'exemple prcdent (dclaration de l'entier i l'intrieur du for), la variable n'est accessible qu' l'intrieur de la boucle, autrement dit dans le corps de la balise <c:forEach>. V ous remarquerez bien videmment que l'utilisation de tags JSTL dans le corps de la balise est autorise : nous utilisons ici dans cet exemple l'affichage via des balises <c:out>. V oici, mais cela doit vous paratre vident, le code HTML produit par cette page JSP : Code : HTML <table> <tr> <th>Valeur</th> <th>Cube</th> </tr> <tr> <td>0</td> <td>0</td> </tr> <tr> <td>1</td> <td>1</td> </tr>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<tr> <td>2</td> <td>8</td> </tr> <tr> <td>3</td> <td>27</td> </tr> <tr> <td>4</td> <td>64</td> </tr> <tr> <td>5</td> <td>125</td> </tr> <tr> <td>6</td> <td>216</td> </tr> <tr> <td>7</td> <td>343</td> </tr> </table>

138/606

Itration sur une collection


Passons maintenant quelque chose de plus intressant et utilis dans la cration de pages web : les itrations sur les collections . Si ce terme ne vous parle pas, c'est que vous avez besoin d'une bonne piqre de rappel en Java ! Et ce n'est pas moi qui vous la donnerai, si vous en sentez le besoin, allez faire un tour sur ce chapitre du tuto de Java. La syntaxe utilise pour parcourir une collection est similaire celle d'une boucle simple, sauf que cette fois, un attribut items est requis. Et pour cause, c'est lui qui indiquera la collection parcourir. Imaginons ici que nous souhaitions raliser l'affichage de news sur une page web. Imaginons pour cela que nous ayons disposition un ArrayList ici nomm maListe, contenant simplement des HashMap. Chaque HashMap ici associera le titre d'une news son contenu. Nous souhaitons alors parcourir cette liste afin d'afficher ces informations dans une page web : Code : JSP <%@ page import="java.util.*" %> <% /* Cration de la liste et des donnes */ List<Map<String, String>> maListe = new ArrayList<Map<String, String>>(); Map<String, String> news = new HashMap<String, String>(); news.put("titre", "Titre de ma premire news"); news.put("contenu", "corps de ma premire news"); maListe.add(news); news = new HashMap<String, String>(); news.put("titre", "Titre de ma seconde news"); news.put("contenu", "corps de ma seconde news"); maListe.add(news); pageContext.setAttribute("maListe", maListe); %> <c:forEach items="${maListe}" var="news"> <div class="news"> <div class="titreNews"> <c:out value="${news['titre']}" /> </div> <div class="corpsNews"> <c:out value="${news['contenu']}" /> </div> </div>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</c:forEach>

139/606

Je sens que certains vont m'attendre au tournant... Eh oui, j'ai utilis du code Java ! Et du code sale en plus ! Mais attention ne pas vous y mprendre : je n'ai recours du code Java ici que pour l'exemple, afin de vous procurer un moyen simple et rapide pour initialiser des donnes de test, et afin de vrifier le bon fonctionnement de notre boucle. Il va de soi que dans une vraie application web, ces donnes seront initialises correctement, et non pas comme je l'ai fait ici. Qu'elles soient rcupres depuis une base de donnes, depuis un fichier, voire codes en dur dans la couche mtier de votre application, ces donnes ne doivent jamais et en aucun cas, je rpte, elles ne doivent jamais et en aucun cas, tre initialises directement depuis vos JSP ! Le rle d'une page JSP, je le rappelle, c'est de prsenter l'information, un point c'est tout. Ce n'est pas pour rien que la couche dans laquelle se trouvent les JSP sappelle la couche de prsentation.

Revenons notre boucle : ici, je n'ai pas encombr la syntaxe, en utilisant les seuls attributs items et var. Le premier indique la collection sur laquelle la boucle porte, en l'occurrence notre List nomme maListe, et le second indique quant lui le nom de la variable qui sera lie llment courant de la collection parcourue par la boucle, que j'ai ici de manire trs originale nomme "news", nos HashMap contenant... des news. Ainsi, pour accder respectivement aux titres et contenus de nos news, il suffit, via la notation avec crochets, de prciser les lments viss dans notre Map, ici aux lignes 19 et 22. Nous aurions trs bien pu utiliser la place des crochets l'oprateur point : ${news.titre} et ${news.contenu}. V oici le rendu HTML de cet exemple : Code : HTML <div class="news"> <div class="titreNews"> Titre de ma premire news </div> <div class="corpsNews"> corps de ma premire news </div> </div> <div class="news"> <div class="titreNews"> Titre de ma seconde news </div> <div class="corpsNews"> corps de ma seconde news </div> </div>

Les attributs prsents prcdemment lors de l'tude d'une boucle simple sont l aussi valables : si vous souhaitez par exemple n'afficher que les dix premires news sur votre page, vous pouvez limiter le parcours de votre liste aux dix premiers lments ainsi : Code : JSP <c:forEach items="${maListe}" var="news" begin="0" end="9"> ... </c:forEach>

Si les attributs begin et end spcifis dpassent le contenu rel de la collection, par exemple si vous voulez afficher les dix premiers lments d'une liste mais qu'elle n'en contient que trois, la boucle s'arrtera automatiquement lorsque le parcours de la liste sera termin, peu importe l'indice end spcifi.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


Simple, n'est-ce pas ?

140/606

titre d'information, voici enfin les diffrentes collections sur lesquelles il est possible d'itrer avec la boucle <c:forEach> de la bibliothque Core : java.util.Collection ; java.util.Map ; java.util.Iterator ; java.util.Enumeration ; Array d'objets ou de types primitifs ; (Chanes de caractres spares par des sparateurs dfinis). Si j'ai mis entre parenthses le dernier lment, c'est parce qu'il est dconseill d'utiliser cette boucle pour parcourir une chane de caractres dont les lments sont spars par des caractres sparateurs dfinis. V oyez le paragraphe suivant pour en savoir plus ce sujet. Enfin, sachez qu'il est galement possible d'itrer directement sur le rsultat d'une requte SQL. Cependant, volontairement, je n'aborderai pas ce cas, pour deux raisons : je ne vous ai pas encore prsent la bibliothque sql de la JSTL, permettant d'effectuer des requtes SQL depuis vos JSP ; je ne vous prsenterai pas la bibliothque sql de la JSTL, ne souhaitant pas vous voir effectuer des requtes SQL depuis vos JSP !

L'attribut varStatus
Il reste un attribut que je n'ai pas encore voqu et qui est, comme les autres, utilisable pour tout type d'itrations, que ce soit sur des entiers ou sur des collections : l'attribut varStatus . Tout comme l'attribut var, il est utilis pour crer une variable de scope, mais prsente une diffrence majeure : alors que var permet de stocker la valeur de l'index courant ou l'lment courant de la collection parcourue, le varStatus permet de stocker un objet LoopTagStatus, qui dfinit un ensemble de proprits dfinissant l'tat courant d'une itration : Proprit begin end step first last count index current Description La valeur de l'attribut begin. La valeur de l'attribut end. La valeur de l'attribut step. Boolen prcisant si l'itration courante est la premire. Boolen prcisant si l'itration courante est la dernire. Compteur d'itrations (commence 1). Index d'itrations (commence 0). lment courant de la collection parcourue.

Reprenons l'exemple utilis prcdemment, mais cette fois-ci en mettant en jeu lattribut varStatus : Code : JSP <c:forEach items="${maListe}" var="news" varStatus="status"> <div class="news"> News n <c:out value="${status.count}"/> : <div class="titreNews"> <c:out value="${news['titre']}" /> </div> <div class="corpsNews"> <c:out value="${news['contenu']}" /> </div>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</div> </c:forEach>

141/606

J'ai utilis ici la proprit count de l'attribut varStatus , affiche simplement en tant que numro de news. Cet exemple est simple, mais suffit vous faire comprendre comment utiliser cet attribut : il suffit d'appeler directement une proprit de l'objet varStatus , que j'ai ici de manire trs originale nomme... status . Pour terminer, sachez enfin que l'objet cr par cet attribut varStatus n'est visible que dans le corps de la boucle, tout comme l'attribut var.

Itration sur une chane de caractres


Une variante de la boucle <c:forEach> existe, spcialement ddie aux chanes de caractres. La syntaxe est presque identique : Code : JSP <p> <%-- Affiche les diffrentes sous-chanes spares par une virgule ou un point-virgule --%> <c:forTokens var="sousChaine" items="salut; je suis un,gros;zro+!" delims=";,+"> ${sousChaine}<br/> </c:forTokens> </p>

Un seul attribut apparat : delims . C'est ici que l'on doit spcifier quels sont les caractres qui serviront de sparateurs dans la chane que la boucle parcourra. Il suffit de les spcifier les uns la suite des autres, comme c'est le cas ici dans notre exemple. Tous les autres attributs vus prcdemment peuvent galement s'appliquer ici (begin, end, step...). Le rendu HTML de ce dernier exemple est donc : Code : HTML <p> salut<br/> je suis un<br/> gros<br/> zero<br/> !<br/> </p>

Ce que la JSTL ne permet pas (encore) de faire


Il est possible en Java d'utiliser les commandes break et continue pour sortir d'une boucle en cours de parcours. Eh bien sachez que ces fonctionnalits ne sont pas implmentes dans la JSTL. Par consquent, il est impossible la plupart du temps de sortir d'une boucle en cours d'itration. Il existe dans certains cas des moyens plus ou moins efficaces de sortir d'une boucle, via l'utilisation de conditions <c:if> notamment. Quant aux cas d'itrations sur des collections, la meilleure solution si le besoin de sortir en cours de boucle se fait ressentir, est de dporter le travail de la boucle dans une classe Java. Pour rsumer, ce genre de situations se rsout au cas par cas, selon vos besoins. Mais n'oubliez pas : votre vue doit se consacrer l'affichage uniquement. Si vous sentez que vous avez besoin de fonctionnalits qui n'existent pas dans la JSTL, il y a de grandes chances pour que vous soyez en train de trop en demander votre vue, et ventuellement de bafouer MVC !

Les liens
www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

142/606

Liens
La balise <c:url> a pour objectif de gnrer des URL. En lisant ceci, j'imagine que vous vous demandez ce qu'il peut bien y avoir de particulier grer dans la cration d'une URL ! Dans une page HTML simple, lorsque l'on cre un lien on se contente en effet d'crire directement l'adresse au sein de la balise <a> : Code : HTML <a href="url">lien</a>

Dans ce cas, qu'est-ce qui peut motiver le dveloppeur utiliser la balise <c:url> ?

Eh bien vous devez savoir qu'en ralit, une adresse n'est pas qu'une simple chane de caractres, elle est soumise plusieurs contraintes. V oici les trois fonctionnalits associes la balise : ajouter le nom du contexte aux URL absolues ; rcrire l'adresse pour la gestion des sessions (si les cookies sont dsactivs ou absents, par exemple) ; encoder les noms et contenus des paramtres de l'URL. L'attribut value contient logiquement l'adresse, et l'attribut var permet comme pour les tags vus auparavant de stocker le rsultat dans une variable. V oici un premier jeu d'exemples : Code : JSP <%-- Gnre une url simple, positionne dans un lien HTML --%> <a href="<c:url value="test.jsp" />">lien</a> <%-- Gnre une url et la stocke dans la variable lien --%> <c:url value="test.jsp" var="lien" />

Reprenons maintenant les trois proprits en dtail, et analysons leur fonctionnement.

1. Ajout du contexte
Lorsqu'une URL est absolue, c'est--dire lorsqu'elle fait rfrence la racine de l'application et commence par le caractre / , le contexte de l'application sera par dfaut ajout en dbut d'adresse. Ceci est principalement d au fait que lors du dveloppement d'une application, le nom du contexte importe peu et on y crit souvent un nom par dfaut, faute de mieux. Il n'est gnralement choisi dfinitivement que lors du dploiement de l'application, qui intervient en fin de cycle. Lors de l'utilisation d'adresses relatives, pas de soucis puisqu'elles ne font pas rfrence au contexte, et pointeront, quoi qu'il arrive, vers le rpertoire courant. Mais pour les adresses absolues, pointant la racine, sans cette fonctionnalit il serait ncessaire d'crire en dur le contexte de l'application dans les URL lors du dveloppement, et de toutes les modifier si le contexte est chang par la suite lors du dploiement. V ous comprenez donc mieux l'intrt d'un tel systme. Code : JSP <%-- L'url absolue ainsi gnre --%> <c:url value="/test.jsp" /> <%-- Sera rendue ainsi dans la page web finale si le contextPath est "Test" --%> /Test/test.jsp <%-- Et une url relative ainsi gnre --%> <c:url value="test.jsp" /> <%-- Ne sera pas modifie lors du rendu --%>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


test.jsp

143/606

2. Gestion des sessions


Si le conteneur JSP dtecte un cookie stockant l'identifiant de session dans le navigateur de l'utilisateur, alors aucune modification ne sera apporte l'URL. Par contre, si ce cookie est absent, les URL gnres via la balise <c:url> seront rcrites pour intgrer l'identifiant de session en question. Regardez ces exemples, afin de bien visualiser la forme de ce paramtre : Code : JSP <%-- L'url ainsi gnre --%> <c:url value="test.jsp" /> <%-- Sera rendue ainsi dans la page web finale, si le cookie est prsent --%> test.jsp <%-- Et sera rendue sous cette forme si le cookie est absent --%> test.jsp;jsessionid=A6B57CE08012FB431D

Ainsi, via ce systme une application Java EE ne dpendra pas de l'activation des cookies du ct utilisateur. Ne vous inquitez pas si vous ne saisissez pas le principe ici, nous reviendrons sur cette histoire de cookies et de sessions plus tard. Pour le moment, essayez simplement de retenir que la balise <c:url> est quipe pour leur gestion automatique !

3. Encodage
En utilisant la balise <c:url>, les paramtres que vous souhaitez passer cette URL seront encods : les caractres spciaux qu'ils contiennent ventuellement vont tre transforms en leurs codes HTML respectifs. Toutefois, il ne faut pas faire de confusion ici : ce sont seulement les paramtres (leur nom et contenu) qui seront encods, le reste de l'URL ne sera pas modifi . La raison de ce comportement est de pouvoir assurer la compatibilit avec l'action standard d'inclusion <jsp:include>, qui ne sait pas grer une URL encode. D'accord, mais comment faire pour passer des paramtres ?

Pour transmettre proprement des paramtres une URL, une balise particulire existe : <c:param>. Elle ne peut exister que dans le corps des balises <c:url>, <c:import> ou <c:redirect>. Elle se prsente sous cette forme assez intuitive : Code : JSP <c:url value="/monSiteWeb/countZeros.jsp"> <c:param name="nbZeros" value="${countZerosBean.nbZeros}"/> <c:param name="date" value="22/06/2010"/> </c:url>

L'attribut name contient donc le nom du paramtre, et value son contenu. C'est en ralit cette balise, ici fille de la balise <c:url>, qui se charge de l'encodage des paramtres, et non directement la balise <c:url>. Retenez enfin qu'une telle balise ne peut exister qu'entre deux balises d'URL, de redirection ou d'import, et qu'il est possible d'en utiliser autant que ncessaire. Code : JSP <%-- Une URL gnre de cette manire --%> <a href="<c:url value="/monSiteWeb/test.jsp"> <c:param name="date" value="22/06/2010"/> <c:param name="donnees" value="des donnes contenant des c@r#ct%res bi&a**es..." </c:url>">Lien HTML</a>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

144/606

<%-- Sera rendue ainsi dans la page web finale --%> <a href="/test/monSiteWeb/test.jsp? date=22%2f06%2f2010&donnees=des+donn%e9es+contenant+des+c%40r%23ct%25res+bi%26a**es..." HTML</a>

V ous voyez bien dans cet exemple que : les caractres spciaux contenus dans les paramtres de l'URL ont t transforms : / est devenu % 2f , est devenu % e9 , etc ; les caractres & sparant les diffrents paramtres, qui font quant eux partie intgrante de l'URL, n'ont pas t modifis en leur code HTML &amp; . Si vous travaillez sur une page XML ou une page XHTML stricte, alors vous devez savoir qu'afin de respecter les normes qui rgissent ces technologies, il est impratif d'encoder proprement l'URL. Cela dit, je viens de vous expliquer que la balise <c:url> n'effectue pas cette opration, elle ne s'occupe que des paramtres... Par consquent, vous devrez transformer vous-mmes les caractres spciaux, comme le & sparant les paramtres d'une URL, en leur code HTML quivalent (en l'occurrence, & doit devenir &amp; pour que la syntaxe soit valide). Si vous avez bien suivi, vous savez qu'il est possible d'effectuer ces transformations l'aide de la balise <c:out> !

Pour rsumer
Rcapitulons tout cela avec un exemple assez complet : Code : JSP <%-- L'url avec paramtres ainsi gnre --%> <c:url value="/monSiteWeb/countZeros.jsp"> <c:param name="nbZeros" value="123"/> <c:param name="date" value="22/06/2010"/> </c:url> <%-- Sera rendue ainsi dans la page web finale, si le cookie est prsent et le contexte est Test --%> /Test/monSiteWeb/countZeros.jsp?nbZeros=123&date=22%2f06%2f2010 <%-- Et sera rendue sous cette forme si le cookie est absent --%> /Test/monSiteWeb/countZeros.jsp;jsessionid=A6B57CE08012FB431D? nbZeros=123&date=22%2f06%2f2010

V ous pouvez ici observer : la mise en place de paramtres via <c:param> ; l'ajout automatique du contexte en dbut de l'URL absolue ; l'encodage automatique des paramtres (ici les caractres / dans la date sont transforms en %2f) ; le non-encodage de l'URL (le caractre & sparant les paramtres n'est pas transform) ; l'ajout automatique de l'identifiant de session. Remarquez d'ailleurs ici sa prsence avant les paramtres de la requte, et non aprs.

Redirection
La balise <c:redirect> est utilise pour envoyer un message de redirection HTTP au navigateur de l'utilisateur. Si elle ressemble l'action <jsp:forward>, il existe toutefois une grosse diffrence, qui rside dans le fait qu'elle va entraner un changement de l'URL dans le navigateur de l'utilisateur final, contrairement <jsp:forward> qui est transparente du point de vue de l'utilisateur (l'URL dans la barre de navigation du navigateur n'est pas modifie). La raison de cette diffrence de comportement est simple : le forwarding se fait ct serveur, contrairement la redirection qui est effectue par le navigateur. Cela limite par consquent la porte de l'action de forwarding qui, puisque excute ct serveur,

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

145/606

est limite aux pages prsentes dans le contexte de la servlet utilise. La redirection tant excute ct client, rien ne vous empche de rediriger l'utilisateur vers n'importe quelle page web. Au final, le forwarding est plus performant, ne ncessitant pas d'aller-retour passant par le navigateur de l'utilisateur final, mais il est moins flexible que la redirection. De plus, utiliser le forwarding impose certaines contraintes : concrtement, l'utilisateur final n'est pas au courant que sa requte a t redirige vers une ou plusieurs servlets ou JSP diffrentes, puisque l'URL qui est affiche dans son navigateur ne change pas. En d'autres termes, cela veut dire que l'utilisateur ne sait pas si le contenu qu'il visualise dans son navigateur a t produit par la page qu'il a appele via l'URL d'origine, ou par une autre page vers laquelle la premire servlet ou JSP appele a effectu un forwarding ! Ceci peut donc poser problme si l'utilisateur rafrachit la page par exemple, puisque cela appellera nouveau la servlet ou JSP d'origine... Sachez enfin que lorsque vous utilisez le forwarding , le code prsent aprs cette instruction dans la page n'est pas excut. Bref, je vous conseille, pour dbuter, d'utiliser la redirection via la balise <c:redirect> plutt que l'action standard JSP, cela vous vitera bien des ennuis. V oyons pour terminer quelques exemples d'utilisation : Code : JSP <%-- Forwarding avec l'action standard JSP --%> <jsp:forward page="/monSiteWeb/erreur.jsp"> <%-- Redirection avec la balise redirect --%> <c:redirect url="http://www.siteduzero.com"/> <%-- Les attributs valables pour <c:url/> le sont aussi pour la redirection. Ici par exemple, l'utilisation de paramtres --%> <c:redirect url="http://www.siteduzero.com"> <c:param name="mascotte" value="zozor"/> <c:param name="langue" value="fr"/> </c:redirect> <%-- Redirigera vers --%> http://www.siteduzero.com?mascotte=zozor&langue=fr

Imports
La balise <c:import> est en quelque sorte un quivalent <jsp:include>, mais qui propose plus d'options, et pallie ainsi les manques de l'inclusion standard. L'attribut url est le seul paramtre obligatoire lors de l'utilisation de cette balise, et il dsigne logiquement le fichier importer : Code : JSP <%-- Copie le contenu du fichier cibl dans la page actuelle --%> <c:import url="exemple.html"/>

Un des avantages majeurs de la fonction d'import est qu'elle permet d'utiliser une variable pour stocker le flux rcupr, et ne propose pas simplement de l'inclure dans votre JSP comme c'est le cas avec <jsp:include>. Cette fonctionnalit est importante, puisqu'elle permet d'effectuer des traitements sur les pages importes. L'attribut utilis pour ce faire est nomm varReader. Nous reverrons cela en dtail lorsque nous dcouvrirons la bibliothque xml de la JSTL, o ce systme prend toute son importance lorsqu'il s'agit de lire et de parser des fichiers XML : Code : JSP <%-- Copie le contenu d'un fichier xml dans une variable (fileReader), puis parse le flux rcupr dans une autre variable (doc). --%> <c:import url="test.xml" varReader="fileReader"> <x:parse var="doc" doc="${fileReader}" /> </c:import>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

146/606

Ne vous inquitez pas si la ligne <x:parse var="doc" doc="${fileReader}" /> vous est inconnue, c'est une balise de la bibliothque xml de la JSTL que je vous prsenterai dans le chapitre suivant. V ous pouvez cependant retenir l'utilisation du <c:import> pour rcuprer un flux xml. Note : le contenu du varReader utilis, autrement dit la variable dans laquelle est stock le contenu de votre fichier, n'est accessible qu' l'intrieur du corps de la balise d'import, entre les tags <c:import> et </c:import>. Il n'est par consquent pas possible de s'en servir en dehors. Dans le chapitre portant sur la bibliothque xml, nous dcouvrirons un autre moyen, permettant de pouvoir travailler sur le contenu du fichier en dehors de l'import, au travers d'une variable de scope.

De la mme manire que la redirection par rapport au forwarding , <c:import> permet d'inclure des pages extrieures au contexte de votre servlet ou votre serveur, contrairement l'action standard JSP. V oyons une nouvelle fois quelques exemples d'utilisation : Code : JSP <%-- Inclusion d'une page avec l'action standard JSP. --%> <jsp:include page="index.html" /> <%-- Importer une page distante dans une variable Le scope par dfaut est ici page si non prcis. --%> <c:import url="http://www.siteduzero.com/zozor/biographie.html" var="bio" scope="page"/> <%-- Les attributs valables pour <c:url/> le sont aussi pour la redirection. Ici par exemple, l'utilisation de paramtres --%> <c:import url="footer.jsp"> <c:param name="design" value="bleu"/> </c:import>

Les autres bibliothques de la JSTL


Au terme de ce chapitre, vous devez tre capables de transformer des scriptlets Java contenant variables, boucles et conditions en une jolie page JSP base sur des tags JSTL. Testez maintenant vos connaissances dans le TP qui suit ce chapitre ! Sachez avant de continuer, que d'autres bibliothques de base existent, la JSTL ne contenant en ralit pas une bibliothque mais cinq ! V oici une brve description des quatre autres : fmt : destine au formatage et au parsage des donnes. Permet notamment la localisation de l'affichage ; fn : destine au traitement de chanes de caractres ; sql : destine l'interaction avec une base de donnes. Celle-ci ne trouve pour moi son sens que dans une petite application standalone, ou une feuille de tests. En effet, le code ayant trait au stockage des donnes dans une application web Java EE suivant le modle MVC doit tre masqu de la vue, et tre encapsul dans le modle, ventuellement dans une couche ddie (voir le modle de conception DAO pour plus d'information ce sujet). Bref, je vous laisse parcourir par vous-mmes les liens de documentation si vous souhaitez en faire usage dans vos projets. En ce qui nous concerne, nous suivons MVC la lettre et je ne souhaite clairement pas vous voir toucher aux donnes de la base directement depuis vos pages JSP... Une fois n'est pas coutume, le premier que je vois coder ainsi, je le pends un arbre ! xml : destine au traitement de fichiers et donnes XML. l'instar de la bibliothque sql , celle-ci trouve difficilement sa place dans une application MVC, ces traitements ayant bien souvent leur place dans des objets du modle, et pas dans la vue. Cela dit, dans certains cas elle peut s'avrer trs utile, ce format tant trs rpandu dans les applications et communications web : c'est pour cela que j'ai dcid d'en faire l'objet du prochain chapitre ! On affiche le contenu d'une variable ou d'un objet avec <c:out>. On effectue un test avec <c:if> ou <c:choose>.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


On ralise une boucle sur une collection avec <c:forEach>. On gnre un lien de manire automatique avec <c:url>. On redirige vers une autre page avec <c:redirect>. On importe une autre page avec <c:import>.

147/606

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

148/606

JSTL core : exercice d'application


La bibliothque Core n'a maintenant plus de secrets pour vous. Mais si vous souhaitez vous familiariser avec toutes ces nouvelles balises et tre l'aise lors du dveloppement de pages JSP, vous devez vous entraner ! Je vous propose ici un petit exercice d'application qui met en jeu des concepts ralisables l'aide des balises que vous venez de dcouvrir. Suivez le guide...

Les bases de l'exercice


Pour mener bien ce petit exercice, commencez par crer un nouveau projet web nomm jstl_exo1 . Rfrez-vous au premier chapitre de cette partie si vous avez encore des hsitations sur la dmarche ncessaire. Configurez bien entendu ce projet en y intgrant la JSTL, afin de pouvoir utiliser nos chres balises dans les pages JSP ! Une fois que c'est fait, crez une premire page JSP la racine de votre application, sous le rpertoire WebContent. Je vous en donne ici le contenu complet : Code : JSP - initForm.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> Initialisation des donnes </title> </head> <body> <form method="post" action="initProcess.jsp"> <p> <label for="nom">Entrez ici votre nom de famille :</label><br /> <input type="text" name="nom" id="nom" tabindex="10" /> </p> <p> <label for="prenom">Entrez ici votre prnom :</label><br /> <input type="text" name="prenom" id="prenom" tabindex="20" /> </p> <p> <label for="pays">Dans quel(s) pays avez-vous dj voyag ?</label><br /> <select name="pays" id="pays" multiple="multiple" tabindex="30"> <option value="France">France</option> <option value="Espagne">Espagne</option> <option value="Italie">Italie</option> <option value="Royaume-uni">Royaume-Uni</option> <option value="Canada">Canada</option> <option value="Etats-unis">Etats-Unis</option> <option value="Chine">Chine</option> <option value="Japon">Japon</option> </select> </p> <p> <label for="autre">Entrez ici les autres pays que vous avez visits, spars par une virgule :</label><br /> <textarea id="autre" name="autre" rows="2" cols="40" tabindex="40" placeholder="Ex: Norvge, Chili, NouvelleZlande"></textarea> </p> <p> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> </p> </form> </body> </html>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

149/606

Rcapitulons rapidement la fonction de cette page : permettre l'utilisateur de saisir son nom ; permettre l'utilisateur de saisir son prnom ; permettre l'utilisateur de choisir les pays qu'il a visits parmi une liste de choix par dfaut ; permettre l'utilisateur de saisir d'autres pays qu'il a visits, en les sparant par une virgule. V oici la figure suivante le rendu, rempli avec des donnes de test.

V otre mission maintenant, c'est d'crire la page initProcess.jsp qui va se charger de traiter les donnes saisies dans la page contenant le formulaire. Nous n'avons pas encore tudi le traitement des formulaires en Java EE, mais ne paniquez pas. Tout ce que vous avez besoin de savoir ici, c'est que les donnes saisies par le client dans les champs du formulaire seront accessibles dans votre JSP travers les paramtres de requtes , autrement dit l'objet implicite param. Avec la JSTL et les expressions EL, vous avez tout en main pour mettre en place ce petit exercice ! Ne vous inquitez pas, nous apprendrons dans la partie suivante de ce cours comment grer proprement les formulaires dans une application Java EE. Le sujet est ici volontairement simple, et son utilit nulle. L'objectif est purement didactique, l'intrt est de vous familiariser avec le dveloppement de pages et la manipulation de donnes en utilisant la JSTL. Ne vous proccupez pas de l'architecture factice mise en place, et ne vous intressez par consquent pas aux dtails de cette premire page initForm.jsp, elle n'est l que pour servir de base notre exercice. Pour en revenir l'exercice, je ne vous demande rien de bien compliqu. La page devra galement tre place la racine du projet, sous le rpertoire WebContent, et sera donc accessible aprs validation du formulaire de la page http://localhost:8080/jstl_exo1/initForm.jsp. Elle devra simplement afficher : 1. une liste rcapitulant le nom de chaque champ du formulaire et les informations qui y ont t saisies ; 2. le nom et le prnom saisis par l'utilisateur ;

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


3. une liste des pays visits par l'utilisateur.

150/606

Il y a plusieurs manires de raliser ces tches basiques, choisissez celle qui vous semble la plus simple et logique. Prenez le temps de chercher et de rflchir, et on se retrouve ensuite pour la correction !

Correction
Ne vous jetez pas sur la correction sans chercher par vous-mmes : cet exercice n'aurait alors plus aucun intrt. Pour ceux d'entre vous qui peinent voir par o partir, ou comment agencer tout cela, voil en exemple le squelette de la page que j'ai ralise, contenant seulement les commentaires expliquant les traitements effectuer : Code : JSP <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Traitement des donnes</title> </head> <body> <p> <b>Vous avez renseign les informations suivantes :</b> </p> <%-- Parcourt chaque paramtre de la requte --%> <%-- Affiche le nom de chaque paramtre. --%> --%> <%-- Parcourt la liste des valeurs de chaque paramtre. <%-- Affiche chacune des valeurs --%>

<p> <b>Vous vous nommez :</b> </p> <p> <%-- Affiche les valeurs des paramtres nom et prenom --%> </p> <p> <b>Vous avez visit les pays suivants :</b> </p> <p> <%-- Teste l'existence du paramtre pays. S'il existe on le traite, sinon on affiche un message par dfaut.--%> <%-- Teste l'existence du paramtre autre. Si des donnes existent on les traite, sinon on affiche un message par dfaut.--%> </p> </body> </html>

Si vous tiez perdus, avec cette bauche vous devriez avoir une meilleure ide de ce que j'attends de vous. Prenez votre temps, et n'hsitez pas relire les chapitres prcdents pour vrifier les points qui vous semblent flous ! V oici finalement la page que j'ai crite. Comme je vous l'ai signal plus tt, ce n'est pas LA solution, c'est simplement une des manires de raliser ce simple traitement : Code : JSP - initProcess.jsp

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Traitement des donnes</title> </head> <body> <p> <b>Vous avez renseign les informations suivantes :</b> </p> <%-- Parcourt l'objet implicite paramValues qui, souvenez-vous, est une Map, pour traiter chaque paramtre de la requte --%> <c:forEach var="parametre" items="${ paramValues }"> <ul> <%-- Affiche la cl de la Map paramValues, qui correspond concrtement au nom du paramtre. --%> <li><b><c:out value="${ parametre.key }"/></b> :</li> traite, <%-- Parcourt le tableau de String[] associ la cl qui correspond la liste de ses valeurs. --%> <c:forEach var="value" items="${ parametre.value }"> <%-- Affiche chacune des valeurs pour la cl donne -

151/606

-%>

<c:out value="${ value }"/> </c:forEach> </ul> </c:forEach>

<p> <b>Vous vous nommez :</b> </p> <p> <%-- Affiche les valeurs des paramtres nom et prenom en y accdant directement via l'objet implicite (une Map) param. On sait en effet qu'il n'y a qu'une valeur associe chacun de ces 2 paramtres, pas besoin d'utiliser paramValues ! --%> <c:out value="${ param.nom }"/> <c:out value="${ param.prenom }"/> </p> <p> <b>Vous avez visit les pays suivants :</b> </p> <p> <%-- Teste l'existence du paramtre pays. S'il existe on le traite, sinon on affiche un message par dfaut.--%> <c:choose> <c:when test="${ !empty paramValues.pays }"> <%-- Parcourt le tableau de valeurs associes au paramtre pays de la requte, en utilisant l'objet implicite paramValues. En effet, c'est ncessaire ici puisque le select permet de renvoyer plusieurs valeurs pour le seul paramtre nomm pays. --%> <c:forEach var="pays" items="${ paramValues.pays }"> <c:out value="${ pays }"/><br/> </c:forEach> </c:when> <c:otherwise> Vous n'avez pas visit de pays parmi la liste propose.<br/> </c:otherwise> </c:choose>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<%-- Teste l'existence du paramtre autre. Si des donnes existent on les traite, sinon on affiche un message par dfaut.--%> <c:choose> <c:when test="${ !empty param.autre }"> <%-- Parcourt les valeurs associes au paramtre autre de la requte, en utilisant l'objet implicite param. En effet, toutes les valeurs sont ici concatnes et transmises dans une seule chane de caractres, qu'on parcourt via la boucle forTokens ! --%> <c:forTokens var="pays" items="${ param.autre }" delims=","> <c:out value="${ pays }"/><br/> </c:forTokens> </c:when> <c:otherwise> Vous n'avez pas visit d'autre pays.<br/> </c:otherwise> </c:choose> </p> </body> </html>

152/606

Et voici la figure suivante le rendu avec les donnes de test.

Rendu du formulaire du TP Core.

J'ai utilis ici des tests conditionnels et diffrentes boucles afin de vous faire pratiquer, et mis en jeu diffrents objets implicites. J'aurais trs bien pu mettre en jeu des variables de scope pour stocker les informations rcupres depuis la requte. Si vous

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

153/606

n'tes pas parvenus raliser cette simple rcupration de donnes, vous devez identifier les points qui vous ont pos problme et revoir le cours plus attentivement ! Je n'irai pour le moment pas plus loin dans la pratique. De nombreuses balises ne sont pas intervenues dans cet exercice. Ne vous inquitez pas : vous aurez bien assez tt l'occasion d'appliquer de manire plus exhaustive ce que vous avez dcouvert, dans les prochaines parties du cours. Soyez patients ! En attendant, n'hsitez pas travailler davantage, tenter de dvelopper d'autres fonctionnalits de votre choix. V ous serez alors prts pour tudier la bibliothque xml de la JSTL, que je vous prsente dans le chapitre suivant !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

154/606

La bibliothque xml
Nous allons ici parcourir les fonctions principales de la bibliothque xml . Les flux ou fichiers XML sont trs souvent utiliss dans les applications web, et la JSTL offrant ici un outil trs simple d'utilisation pour effectuer quelques actions de base sur ce type de format, il serait bte de s'en priver. Toutefois, n'oubliez pas mon avertissement dans la conclusion du chapitre sur la bibliothque Core : seuls certains cas particuliers justifient l'utilisation de la bibliothque xml ; dans la plupart des applications MVC, ces actions ont leur place dans le modle, et pas dans la vue ! Petite remarque avant de commencer : le fonctionnement de certaines balises tant trs similaire celui de balises que nous avons dj abordes dans le chapitre prcdent sur la bibliothque Core, ce chapitre sera par moments un peu plus expditif.

La syntaxe XPath
Pour vous permettre de comprendre simplement les notations que j'utiliserai dans les exemples de ce chapitre, je dois d'abord vous prsenter le langage XML Path Language , ou XPath. Autant vous prvenir tout de suite, je ne vous prsenterai ici que succinctement les bases dont j'ai besoin. Un tuto part entire serait ncessaire afin de faire le tour complet des possibilits offertes par ce langage, et ce n'est pas notre objectif ici. Encore une fois, si vous tes curieux, les documentations et ressources ne manquent pas sur le web ce sujet !

Le langage XPath
Le langage XPath permet d'identifier les nuds dans un document XML. Il fournit une syntaxe permettant de cibler directement un fragment du document trait, comme un ensemble de nuds ou encore un attribut d'un nud en particulier, de manire relativement simple. Comme son nom le suggre, path signifiant chemin en anglais, la syntaxe de ce langage ressemble aux chemins d'accs aux fichiers dans un systme : les lments d'une expression XPath sont en effet spars par des slashs '/'. Je n'introduis ici que les notions qui seront ncessaires dans la suite de ce cours. Pour des notions exhaustives, dirigez-vous vers la page du w3c, ou vers un tuto ddi ce sujet.

Structure XML
V oici la structure du fichier XML de test que j'utiliserai dans les quelques exemples illustrant ce paragraphe : Code : XML - monDocument.xml <news> <article id="1"> <auteur>Pierre</auteur> <titre>Foo...</titre> <contenu>...bar !</contenu> </article> <article id="27"> <auteur>Paul</auteur> <titre>Bientt un LdZ J2EE !</titre> <contenu>Woot ?</contenu> </article> <article id="102"> <auteur>Jacques</auteur> <titre>Coyote court toujours</titre> <contenu>Bip bip !</contenu> </article> </news>

La syntaxe XPath
Plutt que de paraphraser, voyons directement comment slectionner diverses portions de ce document via des expressions XPath, travers des exemples comments : Code : XML

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<!-- Slection du nud racine --> / <!-- Slection des nuds 'article' enfants des nuds 'news' --> /news/article <!-- Slection de tous les nuds inclus dans les nuds 'article' enfants des nuds 'news' --> /news/article/* <!-- Slection de tous les nuds 'auteur' qui ont deux parents quelconques --> /*/*/auteur <!-- Slection de tous les nuds 'auteur' du document via l'oprateur '//' --> //auteur <!-- Slection de tous les nuds 'article' ayant au moins un parent --> /*//article <!-- Slection de l'attribut 'id' des nuds 'article' enfants de 'news' --> /news/article/@id <!-- Slection des nuds 'article' enfants de 'news' dont la valeur du nud 'auteur' est 'Paul' --> /news/article[auteur='Paul'] <!-- Slection des nuds 'article' enfants de 'news' dont l'attribut id vaut '12' --> /news/article[@id='12']

155/606

Je m'arrterai l pour les prsentations. Sachez qu'il existe des commandes plus pousses que ces quelques lments, et je vous laisse le loisir de vous plonger dans les ressources que je vous ai communiques pour plus d'information. Mon objectif ici est simplement de vous donner un premier aperu de ce qu'est la syntaxe XPath, afin que vous compreniez sa logique et ne soyez pas perturbs lorsque vous me verrez utiliser cette syntaxe dans les attributs de certaines balises de la bibliothque xml. J'imagine que cela reste assez flou dans votre esprit, et que vous vous demandez probablement comment diable ces expressions vont pouvoir nous servir, et surtout o nous allons pouvoir les utiliser. Pas d'inquitude : les explications vous seront fournies au fur et mesure que vous dcouvrirez les balises mettant en jeu ce type d'expressions.

Pour ceux d'entre vous qui veulent tester les expressions XPath prcdentes, ou qui veulent pratiquer en manipulant d'autres fichiers XML et/ou en mettant en jeu d'autres expressions XPath, voici un site web qui vous permettra de tester en direct le rsultat de vos expressions sur le document XML de votre choix : XPath Expression Testbed. Amusez-vous et vrifiez ainsi votre bonne comprhension du langage !

Les actions de base


Avant de vous prsenter les diffrentes balises disponibles, je vous donne ici la directive JSP ncessaire pour permettre l'utilisation des balises de la bibliothque xml dans vos pages : Code : JSP <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>

Retenez bien que cette directive devra tre prsente sur chacune des pages de votre projet utilisant les balises JSTL que je vous prsente dans ce chapitre. Dans un prochain chapitre concernant la cration d'une bibliothque personnalise, nous verrons comment il est possible de ne plus avoir se soucier de cette commande. En attendant, ne l'oubliez pas !

Rcuprer et analyser un document


www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

156/606

Je vais, dans ce paragraphe, vous montrer comment procder pour rcuprer et analyser simplement un fichier XML depuis votre page JSP. Je reprends le fichier XML que j'ai utilis prcdemment lorsque je vous ai prsent la syntaxe du langage XPath, et je le nomme monDocument.xml. Commenons par aborder la rcupration du fichier XML. Cette tape correspond simplement un import, ralis avec ce tag de la bibliothque Core que vous devez dj connatre : Code : JSP <c:import url="monDocument.xml" varReader="monReader"> ... </c:import>

Remarquez l'utilisation de l'attribut varReader. C'est en quelque sorte un buffer, une variable qui sera utilise pour une utilisation postrieure du contenu du fichier import. Notez que lorsque vous utiliserez cet attribut, il vous sera impossible d'utiliser conjointement l'attribut var. Rappelez-vous : lorsque l'on utilise cet attribut varReader, le contenu du fichier import n'est pas inclus littralement dans votre page JSP comme c'est le cas lors d'un import simple ; il est copi dans la variable nomme dans l'attribut varReader.

Le document XML tant rcupr et stock dans la variable monReader, nous souhaitons maintenant l'analyser. Nous allons, pour cela, faire intervenir une nouvelle balise, issue de la librairie xml cette fois : Code : JSP <c:import url="monDocument.xml" varReader="monReader"> <%-- Parse le contenu du fichier XML monDocument.xml dans une variable nomme 'doc' --%> <x:parse var="doc" doc="${monReader}" /> ... </c:import>

Deux attributs sont ici utiliss : var : contient le nom de la variable de scope qui contiendra les donnes qui reprsentent notre document XML pars. Comme d'habitude, si l'attribut scope n'est pas explicit, la porte par dfaut de cette variable sera la page ; doc : permet de prciser que l'on souhaite parser le contenu de notre varReader dfini prcdemment lors de l'import. Souvenez-vous : le varReader ici nomm monReader est une variable ; il nous faut donc utiliser une EL pour y faire rfrence, en l'occurrence ${monReader} ! Dans certains codes vieillissants, vous trouverez parfois dans l'utilisation de la balise <x:parse> un attribut nomm xml . Sachez qu'il joue le mme rle que l'attribut doc, et qu'il est dprci : concrtement, il a t remplac par doc, et il ne faut donc plus l'utiliser.

Note : l'import qui stocke le fichier dans le varReader doit rester ouvert pour pouvoir appliquer un <x:parse> sur le contenu de ce varReader ! La porte du varReader dfini est en effet uniquement l'intrieur du corps du <c:import>. Afin de pouvoir accder ce varReader, il ne faut donc pas fermer directement la balise d'import comme c'est le cas ci-dessous : Code : JSP <%-- Mauvaise utilisation du varReader --%> <c:import url="monDocument.xml" varReader="monReader" />

Toutefois, il est possible de ne pas utiliser le varReader, et de simplement utiliser une variable de scope. V ous pourrez ainsi faire

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


votre import, puis traiter le contenu du fichier par la suite, sans devoir travailler dans le corps de la balise d'import : Code : JSP <c:import url="monDocument.xml" var="monReader" />

157/606

Cela dit, je vous conseille de travailler avec le varReader, puisque c'est l'objectif premier de cet attribut. Plusieurs remarques sont d'ores et dj ncessaires. Comprenez bien ici la diffrence entre le varReader de la balise <c:import> et le var de la balise <x:parse>: le premier contient le contenu brut du fichier XML, alors que le second contient le rsultat du parsing du fichier XML. Pour faire simple, la JSTL utilise une structure de donnes qui reprsente notre document XML pars, et c'est cette structure qui est stocke dans la variable dfinie par var. Le type de la variable dfinie via cet attribut var dpendra de l'implmentation choisie par le dveloppeur. Pour information, il est possible de remplacer l'attribut var par l'attribut nomm varDom, qui permet de fixer l'implmentation utilise : la variable ainsi dfinie sera de type org.w3c.dom.Document. De mme, scope sera remplac par scopeDom. Ceci impose donc que votre fichier XML respecte l'interface Document cite prcdemment. Tout cela tant vraiment spcifique, je ne m'talerai pas davantage sur le sujet et je vous renvoie la documentation pour plus d'infos. Importer un fichier n'est pas ncessaire. Il est en effet possible de traiter directement un flux XML depuis la page JSP, en le plaant dans le corps de la balise <x:parse> : Code : JSP <%-- Parse le flux XML contenu dans le corps de la balise --%> <x:parse var="doc"> <news> <article id="1"> <auteur>Pierre</auteur> <titre>Foo...</titre> <contenu>...bar !</contenu> </article> <article id="27"> <auteur>Paul</auteur> <titre>Bientt un LdZ J2EE !</titre> <contenu>Woot ?</contenu> </article> <article id="102"> <auteur>Jacques</auteur> <titre>Coyote court toujours</titre> <contenu>Bip bip !</contenu> </article> </news> </x:parse>

Il reste seulement deux attributs que je n'ai pas encore abords : filter : permet de limiter le contenu trait par l'action de parsing <x:parse> une portion d'un flux XML seulement. Cet attribut peut s'avrer utile lors de l'analyse de documents XML lourds, afin de ne pas dtriorer les performances l'excution de votre page. Pour plus d'information sur ces filtres de type XMLFilter, essayez la documentation. systemId : cet attribut ne vous sera utile que si votre fichier XML contient des rfrences vers des entits externes. V ous devez y saisir l'adresse URI qui permettra de rsoudre les liens relatifs contenus dans votre fichier XML. Bref rappel : une rfrence une entit externe dans un fichier XML est utilise pour y inclure un fichier externe, principalement lorsque des donnes ou textes sont trop longs et qu'il est plus simple de les garder dans un fichier part. Le processus accdera ces fichiers externes lors du parsage du document XML spcifi.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

158/606

Je n'ai, pour ces derniers, pas d'exemple trivial vous proposer. Je fais donc volontairement l'impasse ici ; je pense que ceux parmi vous qui connaissent et ont dj manipul les filtres XML et les entits externes comprendront aisment de quoi il s'agit.

Afficher une expression


Les noms des balises que nous allons maintenant aborder devraient vous tre familiers : ils trouvent leurs quivalents dans la bibliothque Core que vous avez dcouverte dans le chapitre prcdent. Alors que les balises de type Core accdaient des donnes de l'application en utilisant des EL, les balises de la bibliothque xml vont accder des donnes issues de documents XML, via des expressions XPath. Pour afficher un lment, nous allons utiliser la balise <x:out>, pour laquelle seul l'attribut select est ncessaire : Code : JSP <c:import url="monDocument.xml" varReader="monReader"> <%-- Parse le contenu du fichier XML monDocument.xml dans une variable nomme 'doc' --%> <x:parse var="doc" doc="${monReader}" /> <x:out select="$doc/news/article/auteur" /> </c:import>

Le rendu HTML du code ci-dessus est alors le suivant : Code : HTML Pierre

En suivant le paragraphe introduisant XPath, j'avais compris qu'une telle expression renvoyait tous les nuds "auteur" du document ! O sont passs Paul et Jacques ? O est l'erreur ? H h... Eh bien, vrai dire il n'y a aucune erreur ! En effet, l'expression XPath renvoie bel et bien un ensemble de nuds, en l'occurrence les nuds "auteur" ; cet ensemble de nuds est stock dans une structure de type NodeSet, un type propre XPath qui implmente le type Java standard NodeList. Le comportement ici observ provient du fait que la balise d'affichage <x:out> ne gre pas rellement un ensemble de nuds, et n'affiche que le premier nud contenu dans cet ensemble de type NodeSet. Toutefois, le contenu de l'attribut select peut trs bien contenir un NodeSet ou une opration sur un NodeSet. Vrifions par exemple que NodeSet contient bien 3 nuds, puisque nous avons 3 auteurs dans notre document XML : Code : JSP <c:import url="monDocument.xml" varReader="monReader"> <%-- Parse le contenu du fichier XML monDocument.xml dans une variable nomme 'doc' --%> <x:parse var="doc" doc="${monReader}" /> <x:out select="count($doc/news/article/auteur)" /> </c:import>

J'utilise ici la fonction count(), qui renvoie le nombre d'lments que l'expression XPath a slectionns et stocks dans le NodeSet. Et le rendu HTML de cet exemple est bien "3" ; notre ensemble de nuds contient donc bien trois auteurs, Paul et Jacques ne sont pas perdus en cours de route.

L'attribut select de la balise <x:out> est l'quivalent de l'attribut value de la balise <c:out>, sauf qu'il attend ici une expression XPath et non plus une EL ! Rappelez-vous que le rle des expressions XPath est de slectionner des portions de document XML. Expliquons rapidement l'expression <x:out select="$doc/news/article/auteur" /> : elle va

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

159/606

slectionner tous les nuds "auteur" qui sont enfants d'un nud "article" lui-mme enfant du nud racine "news" prsent dans le document $doc. En l'occurrence, $doc se rfre ici au contenu pars de notre variable varReader. Dans une expression XPath, pour faire rfrence une variable nomme nomVar on n'utilise pas ${nomVar} comme c'est le cas dans une EL, mais $nomVar. Essayez de retenir cette syntaxe, cela vous vitera bien des erreurs ou des comportements inattendus !

ce sujet, sachez enfin qu'outre une variable simple, il est possible de faire intervenir les objets implicites dans une expression XPath, de cette manire : Code : JSP <%-- Rcupre le document nomm 'doc' enregistr auparavant en session, via l'objet implicite sessionScope --%> <x:out select="$sessionScope:doc/news/article" /> <%-- Slectionne le nud 'article' dont l'attribut 'id' a pour valeur le contenu de la variable nomme 'idArticle' qui a t passe en paramtre de la requte, via l'objet implicite param --%> <x:out select="$doc/news/article[@id=$param:idArticle]"/>

Ce qu'on peut retenir de cette balise d'affichage, c'est qu'elle fournit, grce un fonctionnement bas sur des expressions XPath, une alternative aux feuilles de style XSL pour la transformation de contenus XML, en particulier lorsque le format d'affichage final est une page web HTML.

Crer une variable


Nous passerons trs rapidement sur cette balise. Sa syntaxe est <x:set>, et comme vous vous en doutez elle est l'quivalent de la balise <c:set> de la bibliothque Core, avec de petites diffrences : l'attribut select remplace l'attribut value, ce qui a la mme consquence que pour la balise d'affichage : une expression XPath est attendue, et non pas une EL ; l'attribut var est obligatoire, ce qui n'tait pas le cas pour la balise <c:set>. Ci-dessous un bref exemple de son utilisation : Code : JSP <%-- Enregistre le rsultat de l'expression XPath, spcifie dans l'attribut select, dans une variable de session nomme 'auteur' --%> <x:set var="auteur" scope="session" select="$doc//auteur" /> <%-- Affiche le contenu de la variable nomme 'auteur' enregistre en session --%> <x:out select="$sessionScope:auteur" />

Le rle de cette balise est donc sensiblement le mme que son homologue de la bibliothque Core : enregistrer le rsultat d'une expression dans une variable de scope. La seule diffrence rside dans la nature de l'expression value, qui est ici une expression XPath et non plus une EL.

Les conditions Les conditions


Les balises permettant la mise en place de conditions sont, l aussi, sensiblement identiques leurs homologues de la bibliothque Core : la seule et unique diffrence rside dans le changement de l'attribut test pour l'attribut select. Par consquent, comme vous le savez maintenant, c'est ici une expression XPath qui est attendue, et non plus une EL !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

160/606

Plutt que de paraphraser le prcdent chapitre, je ne vous donne ici que de simples exemples comments, qui vous permettront de reprer les quelques diffrences de syntaxe.

Une condition simple


Code : JSP <%-- Afficher le titre de la news poste par 'Paul' --%> <x:if select="$doc/news/article[auteur='Paul']"> Paul a dj post une news dont voici le titre : <x:out select="$doc/news/article[auteur='Paul']/titre" /> </x:if>

Le rendu HTML correspondant : Code : JSP Paul a dj post une news dont voici le titre : Bientt un LdZ J2EE !

De mme que pour la balise <c:if>, il est possible de stocker le rsultat du test conditionnel en spcifiant un attribut var.

Des conditions multiples


Code : JSP <%-- Affiche le titre de la news poste par 'Nicolas' si elle existe, et un simple message sinon --%> <x:choose> <x:when select="$doc/news/article[auteur='Nicolas']"> Nicolas a dj post une news dont voici le titre : <x:out select="$doc/news/article[auteur='Nicolas']/titre" /> </x:when> <x:otherwise> Nicolas n'a pas post de news. </x:otherwise> </x:choose>

Le rendu HTML correspondant : Code : JSP Nicolas n'a pas post de news.

Les contraintes d'utilisation de ces balises sont les mmes que celles de la bibliothque Core. Je vous renvoie au chapitre prcdent si vous ne vous en souvenez plus. V oil tout pour les tests conditionnels de la bibliothque xml : leur utilisation est semblable celle des conditions de la bibliothque Core, seule la cible change : on traite ici un flux XML, via des expressions XPath.

Les boucles Les boucles


Il n'existe qu'un seul type de boucles dans la bibliothque xml de la JSTL, la balise <x:forEach> :

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


Code : JSP <!-- Affiche les auteurs et titres de tous les articles --> <p> <x:forEach var="element" select="$doc/news/article"> <strong><x:out select="$element/auteur" /></strong> : <x:out select="$element/titre" />.<br/> </x:forEach> </p>

161/606

Le rendu HTML correspondant : Code : JSP <p>

<strong>Pierre</strong> : Foo....<br/> <strong>Paul</strong> : Bientt un LdZ J2EE !.<br/> <strong>Jacques</strong> : Coyote court toujours.<br/> </p>

De mme que pour la balise <c:forEach>, il est possible de faire intervenir un pas de parcours via l'attribut step, de dfinir les index de dbut et de fin via les attributs begin et end, ou encore d'utiliser l'attribut varStatus pour accder l'tat de chaque itration.

Les transformations Transformations


La bibliothque xml de la JSTL permet d'appliquer des transformations un flux XML via une feuille de style XSL. Je ne reviendrai pas ici sur le langage et les mthodes employer, si vous n'tes pas familiers avec ce concept, je vous conseille de lire cette introduction la mise en forme de documents XML avec XSLT. La balise ddie cette tche est <x:transform>. Commenons par un petit exemple, afin de comprendre comment elle fonctionne. J'utiliserai ici le mme fichier XML que pour les exemples prcdents, ainsi que la feuille de style XSL suivante : Code : XML - test.xsl <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Mise en forme avec XSLT</title> </head> <body> <table width="1000" border="1" cellspacing="0" cellpadding="0"> <tr> <th scope="col">Id</th> <th scope="col">Auteur</th> <th scope="col">Titre</th> <th scope="col">Contenu</th> </tr> <xsl:for-each select="/news/article"> <tr> <td> <xsl:value-of select="@id" /> </td> <td> <xsl:value-of select="auteur" /> </td>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</td> <td> <xsl:value-of select="titre" /> </td> <td> <xsl:value-of select="contenu" /> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>

162/606

Cette feuille affiche simplement les diffrents lments de notre fichier XML dans un tableau HTML. Et voici comment appliquer la transformation base sur cette feuille de style notre document XML : Code : JSP - testTransformXsl.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %> <c:import varReader="xslFile" url="test.xsl"> <c:import varReader="xmlFile" url="monDocument.xml"> <x:transform doc="${xmlFile}" xslt="${xslFile}"/> </c:import> </c:import>

On importe ici simplement nos deux fichiers, puis on appelle la balise <x:transform>. Deux attributs sont utiliss. doc : contient la rfrence au document XML sur lequel la transformation doit tre applique. Attention, ici on parle bien du document XML d'origine, et pas d'un document analys via <x:parse>. On travaille bien directement sur le contenu XML. Il est d'ailleurs possible ici de ne pas utiliser d'import, en dfinissant directement le flux XML traiter dans une variable de scope, voire directement dans le corps de la balise comme dans l'exemple suivant : Code : JSP <x:transform xslt="${xslFile}"> <news> <article id="1"> <auteur>Pierre</auteur> <titre>Foo...</titre> <contenu>...bar !</contenu> </article> <article id="27"> <auteur>Paul</auteur> <titre>Bientt un LdZ J2EE !</titre> <contenu>Woot ?</contenu> </article> <article id="102"> <auteur>Jacques</auteur> <titre>Coyote court toujours</titre> <contenu>Bip bip !</contenu> </article> </news> </x:transform>

xslt : contient logiquement la feuille de style XSL. Il est galement possible de ne pas utiliser d'import, et de simplement dfinir une feuille de style dans une variable de scope.

En l'absence d'attribut var, le contenu transform sera automatiquement gnr dans la page HTML finale. Et lorsque vous

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

163/606

accdez cette page JSP depuis votre navigateur, vous apercevez un tableau contenant les donnes de votre fichier XML : la transformation a bien t applique ! Ceci est particulirement intressant lorsque vous souhaitez formater un contenu XML en HTML, par exemple lors de la lecture de flux RSS. Observez plutt les figures suivantes.

Arborescence sous Eclipse avec XSLT.

Rendu transformation XSLT.

Si par contre vous prcisez un attribut var, le rsultat de cette transformation sera alors stock dans la variable de scope ainsi cre, de type Document. Sachez qu'il existe galement un attribut result qui, en l'absence des attributs var et scope, stocke l'objet cr par la transformation. Pour terminer, il est possible de passer des paramtres une transformation XSLT, en utilisant la balise <x:param>. Cette dernire ne peut exister que dans le corps d'une balise <x:transform>, et s'emploie de la mme manire que son homologue de la bibliothque Core : Code : JSP - testTransformXsl.jsp <c:import var="xslFile" url="test.xsl"/> <c:import var="xmlFile" url="monDocument.xml"/> <x:transform doc="${xmlFile}" xslt="${xslFile}"> <x:param name="couleur" value="orange" /> </x:transform>

Le comportement et l'utilisation sont identiques ceux de <c:param> : deux attributs name et value contiennent simplement le nom et la valeur du paramtre transmettre. Ici dans cet exemple, ma feuille de style ne traite pas de paramtre, et donc ne fait rien de ce paramtre nomm couleur que je lui passe. Si vous souhaitez en savoir plus sur l'utilisation de paramtres dans une feuille XSL, vous savez o chercher ! Il reste deux attributs que je n'ai pas explicits : docSystemId and xsltSystemId. Ils ont tous deux la mme utilit que l'attribut systemId de la balise <x:parse>, et s'utilisent de la mme faon : il suffit d'y renseigner l'URI destine rsoudre les liens relatifs contenus respectivement dans le document XML et dans la feuille de style XSL. Je n'ai pour le moment pas prvu de vous prsenter les autres bibliothques de la JSTL : je pense que vous tes maintenant assez familiers avec la comprhension du fonctionnement des tags JSTL pour voler de vos propres ailes. Mais ne partez pas si vite ! Prenez le temps de faire tous les tests que vous jugez ncessaires. Il n'y a que comme a que a rentrera, et que vous prendrez suffisamment de recul pour comprendre parfaitement ce que vous faites. Dans le chapitre suivant je vous propose un exercice d'application de ce que vous venez de dcouvrir, et ensuite on reprendra le code d'exemple de la partie prcdente en y intgrant la JSTL !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


La bibliothque XML de la JSTL s'appuie sur la technologie XPath. On analyse une source XML avec <x:parse>. On affiche le contenu d'une variable ou d'un noeud avec <x:out>. On ralise un test avec <x:if> ou <x:choose>. On ralise une boucle avec <x:forEach>. On applique une transformation XSLT avec <x:transform>.

164/606

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

165/606

JSTL xml : exercice d'application


La bibliothque xml n'a maintenant plus de secrets pour vous. Mais si vous souhaitez vous familiariser avec toutes ces nouvelles balises et tre l'aise lors du dveloppement de pages JSP, vous devez vous entraner ! Je vous propose ici un petit exercice d'application qui met en jeu des concepts ralisables l'aide des balises que vous venez de dcouvrir. Suivez le guide...

Les bases de l'exercice


On prend les mmes et on recommence... Pour mener bien ce petit exercice, commencez par crer un nouveau projet nomm jstl_exo2 . Configurez bien entendu ce projet en y intgrant la JSTL, afin de pouvoir utiliser nos chres balises dans les pages JSP ! Une fois que c'est fait, crez pour commencer un document XML la racine de votre projet, sous le rpertoire WebContent. Je vous en donne ici le contenu complet : Code : XML - inventaire.xml <?xml version="1.0" encoding="utf-8"?> <inventaire> <livre> <auteur>Pierre</auteur> <titre>D&#233;veloppez vos applications web avec JRuby !</titre> <date>Janvier 2012</date> <prix>22</prix> <stock>127</stock> <minimum>10</minimum> </livre> <livre> <auteur>Paul</auteur> <titre>D&#233;couvrez la puissance du langage Perl</titre> <date>Avril 2017</date> <prix>26</prix> <stock>74</stock> <minimum>10</minimum> </livre> <livre> <auteur>Matthieu</auteur> <titre>Apprenez &#224; programmer en C</titre> <date>Novembre 2009</date> <prix>25</prix> <stock>19</stock> <minimum>20</minimum> </livre> <livre> <auteur>Matthieu</auteur> <titre>Concevez votre site web avec PHP et MySQL</titre> <date>Mars 2010</date> <prix>30</prix> <stock>7</stock> <minimum>20</minimum> </livre> <livre> <auteur>Cysboy</auteur> <titre>La programmation en Java</titre> <date>Septembre 2010</date> <prix>29</prix> <stock>2000</stock> <minimum>20</minimum> </livre> </inventaire>

Ne prtez pas grande attention aux donnes modlises par ce document. Nous avons simplement besoin d'une base simple, contenant de quoi nous amuser un peu avec les balises que nous venons de dcouvrir !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

166/606

Note : toute ressemblance avec des personnages existants ou ayant exist serait fortuite et indpendante de la volont de l'auteur... V otre mission maintenant, c'est d'crire la page rapportInventaire.jsp se chargeant d'analyser ce document XML et de gnrer un rapport qui : listera chacun des livres prsents ; affichera un message d'alerte pour chaque livre dont le stock est en dessous de la quantit minimum spcifie ; listera enfin chacun des livres prsents, regroups par stocks tris du plus grand au plus faible.

Cette page devra galement tre place la racine du projet, sous le rpertoire WebContent, et sera donc accessible via l'URL http://localhost:8080/jstl_exo2/rapportInventaire.jsp. Il y a plusieurs manires de raliser ces tches basiques, choisissez celle qui vous semble la plus simple et logique. Prenez le temps de chercher et de rflchir, et on se retrouve ensuite pour la correction !

Correction
Ne vous jetez pas sur la correction sans chercher par vous-mmes : cet exercice n'aurait alors plus aucun intrt. Je ne vous donne ici pas d'aide supplmentaire. Si vous avez suivi le cours jusqu'ici vous devez tre capables de comprendre comment faire, les balises ncessaires pour cet exercice ressemblant fortement celles utilises dans celui concernant la bibliothque Core ! V oici donc une correction possible : Code : JSP - rapportInventaire.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Rapport d'inventaire</title> </head> <body> <%-- Rcupration du document XML. --%> <c:import url="inventaire.xml" var="documentXML" /> <%-- Analyse du document XML rcupr. --%> <x:parse var="doc" doc="${documentXML}" /> <p><b>Liste de tous les livres :</b></p> <div> <ul> <%-- Parcours du document pars pour y rcuprer chaque nud "livre". --%> <x:forEach var="livre" select="$doc/inventaire/livre"> <%-- Affichage du titre du livre rcupr. --%> <li><x:out select="$livre/titre" /></li> </x:forEach> </ul> </div> <p><b>Liste des livres qu'il faut rapprovisionner :</b></p> <div> <ul> <%-- Parcours du document pars pour y rcuprer chaque nud "livre" dont le contenu du nud "stock" est infrieur au contenu du nud "minimum". --%> <x:forEach var="livre" select="$doc/inventaire/livre[stock < minimum]"> <%-- Affichage des titres, stocks et minimaux du livre rcupr. -%> <li><x:out select="$livre/titre" /> : <x:out select="$livre/stock" /> livres en stock (limite avant alerte :

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<x:out select="$livre/minimum" />)</li> </x:forEach> </ul> </div> <p><b>Liste des livres classs par stock :</b></p> <%-- Il faut rflchir... un peu ! --%> <pre> Le tri d'une liste, d'un tableau, d'une collection... bref de manire gnrale le tri de donnes, ne doit pas se faire depuis votre page JSP ! Que ce soit en utilisant les API relatives aux collections, ou via un bean de votre couche mtier, ou que sais-je encore, il est toujours prfrable que votre tri soit effectu avant d'arriver votre JSP. La JSP ne doit en principe que rcuprer cette collection dj trie, formater les donnes pour une mise en page particulire si ncessaire, et seulement les afficher. C'tait un simple pige ici, j'espre que vous avez rflchi avant de tenter d'implmenter un tri avec la JSTL, et que vous comprenez pourquoi cela ne doit pas intervenir ce niveau ;) </pre> </body> </html>

167/606

Je n'ai fait intervenir ici que des traitements faciles, n'utilisant que des boucles et des expressions XPath. J'aurais pu vous imposer l'utilisation de tests conditionnels, ou encore de variables de scope, mais l'objectif est ici uniquement de vous permettre d'tre l'aise avec l'analyse d'un document XML. Si vous n'tes pas parvenus raliser ce simple traitement de document, vous devez identifier les points qui vous ont pos problme et revoir le cours plus attentivement !

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

168/606

Faisons le point !
Il est temps de mettre en pratique ce que nous avons appris. Nous avons en effet abord toutes les balises et tous les concepts ncessaires, et sommes maintenant capables de rcrire proprement nos premiers exemples en utilisant des tags JSTL ! Je vous propose ensuite, pour vous dtendre un peu, quelques conseils autour de l'criture de code Java en gnral.

Reprenons notre exemple


Dans la partie prcdente, la mise en place de boucles et conditions tait un obstacle que nous tions incapables de franchir sans crire de code Java. Maintenant que nous avons dcouvert les balises de la bibliothque Core de la JSTL, nous avons tout ce qu'il nous faut pour russir. Pour rappel, voici o nous en tions : Code : JSP - tat final de notre vue d'exemple en fin de partie prcdente <%@ page pageEncoding="UTF-8" %> <%@ page import="java.util.List" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Test</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> ${test} ${param.auteur} </p> <p> Rcupration du bean : ${coyote.prenom} ${coyote.nom} </p> <p> Rcupration de la liste : <% List<Integer> liste = (List<Integer>) request.getAttribute( "liste" ); for( Integer i : liste ){ out.println(i + " : "); } %> </p> <p> Rcupration du jour du mois : <% Integer jourDuMois = (Integer) request.getAttribute( "jour" ); if ( jourDuMois % 2 == 0 ){ out.println("Jour pair : " + jourDuMois); } else { out.println("Jour impair : " + jourDuMois); } %> </p> </body> </html>

Et voici les nouvelles balises qui vont nous permettre de faire disparatre le code Java de notre JSP d'exemple : <c:choose> pour la mise en place de conditions ; <c:foreach> pour la mise en place de boucles.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

169/606

Reprise de la boucle
En utilisant la syntaxe JSTL, notre boucle devient simplement : Code : JSP <p>

</p>

Rcupration de la liste : <%-- Boucle sur l'attribut de la requte nomm 'liste' --%> <c:forEach items="${liste}" var="element"> <c:out value="${element}" /> : </c:forEach>

Comme prvu, plus besoin de rcuprer explicitement la variable contenant la liste depuis la requte, et plus besoin d'crire du code Java en dur pour mettre en place la boucle sur la liste.

Reprise de la condition
En utilisant la syntaxe JSTL, notre condition devient simplement : Code : JSP <p>

Rcupration du jour du mois : <c:choose> <%-- Test de parit sur l'attribut de la requte nomm 'jour' --%> <c:when test="${ jour % 2 == 0 }">Jour pair : ${jour}</c:when> <c:otherwise>Jour impair : ${jour}</c:otherwise> </c:choose> </p>

Comme prvu, plus besoin de rcuprer explicitement la variable contenant le jour du mois depuis la requte, et plus besoin d'crire du code Java en dur pour mettre en place le test de parit. Ainsi, notre page finale est bien plus claire et comprhensible : Code : JSP - Page d'exemple sans code Java <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Titre</title> </head> <body> <p>Ceci est une page gnre depuis une JSP.</p> <p> ${test} ${param.auteur} </p> <p> Rcupration du bean : ${coyote.prenom} ${coyote.nom} </p> <p>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


Rcupration de la liste : <c:forEach items="${liste}" var="element"> ${element} : </c:forEach> </p> <p> Rcupration du jour du mois : <c:choose> <c:when test="${ jour % 2 == 0 }">Jour pair : ${jour}</c:when> <c:otherwise>Jour impair : ${jour}</c:otherwise> </c:choose> </p> </body> </html>

170/606

Quelques conseils
Avant d'attaquer la suite du cours, dtendez-vous un instant et dcouvrez ces quelques astuces pour mieux organiser votre code et le rendre plus lisible.

Utilisation de constantes
Afin de faciliter la lecture et la modification du code d'une classe, il est recommand de ne pas crire le contenu des attributs de type primitifs en dur au sein de votre code, et de les regrouper sous forme de constantes en dbut de classe afin d'y centraliser les donnes. Reprenons par exemple notre servlet d'exemple, o vous pouvez voir aux lignes 42 45 et 48 des String initialises directement dans le code : Code : Java - com.sdzee.servlets.Test package com.sdzee.servlets; import java.io.IOException; import java.util.ArrayList; import java.util.List; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import org.joda.time.DateTime; import com.sdzee.beans.CoyoteBean; public class Test extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /** Cration et initialisation du message. */ String message = "Message transmis de la servlet la JSP."; /** Cration du bean et initialisation de ses proprits */ CoyoteBean premierBean = new CoyoteBean(); premierBean.setNom( "Coyote" ); premierBean.setPrenom( "Wile E." ); /** Cration de la liste et insertion de quatre lments */ List<Integer> premiereListe = new ArrayList<Integer>(); premiereListe.add( 27 ); premiereListe.add( 12 ); premiereListe.add( 138 ); premiereListe.add( 6 ); /** On utilise ici la libraire Joda pour manipuler les dates, pour deux raisons : * - c'est tellement plus simple et limpide que de travailler avec

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


les objets Date ou Calendar ! * - c'est (probablement) un futur standard de l'API Java. */ DateTime dt = new DateTime(); Integer jourDuMois = dt.getDayOfMonth(); /** Stockage du message, du bean et de la liste dans l'objet request */ request.setAttribute( "test", message ); request.setAttribute( "coyote", premierBean ); request.setAttribute( "liste", premiereListe ); request.setAttribute( "jour", jourDuMois ); /** Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( "/WEB-INF/test.jsp" ).forward( request, response ); } }

171/606

Les lignes 20, 24 25 et 29 32, bien qu'elles contiennent des String et int en dur, correspondent simplement l'initialisation des donnes d'exemple que nous transmettons notre JSP : ce sont des donnes "externes". Dans le cas d'une application relle, ces donnes seront issues de la base de donnes, du modle ou encore d'une saisie utilisateur, mais bien videmment jamais directement issues de la servlet comme c'est le cas dans cet exemple. En ce qui concerne les String initialises en dur, vous devez remarquer qu'elles ne contiennent que des donnes "internes" : en l'occurrence, un nom de page JSP et quatre noms d'attributs. Il s'agit bien ici de donnes propres au fonctionnement de l'application et non pas de donnes destines tre transmises la vue pour affichage. Eh bien comme je vous l'ai annonc, une bonne pratique est de remplacer ces initialisations directes par des constantes, regroupes en tte de classe. V oici donc le code de notre servlet aprs modification : Code : Java - com.sdzee.servlets.Test package com.sdzee.servlets; import java.io.IOException; import java.util.ArrayList; import java.util.List; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import org.joda.time.DateTime; import com.sdzee.beans.CoyoteBean; public class Test extends HttpServlet { public static final String ATT_MESSAGE = "test"; public static final String ATT_BEAN = "coyote"; public static final String ATT_LISTE = "liste"; public static final String ATT_JOUR = "jour"; public static final String VUE = "/WEB-INF/test.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /** Cration et initialisation du message. */ String message = "Message transmis de la servlet la JSP."; /** Cration du bean et initialisation de ses proprits */ CoyoteBean premierBean = new CoyoteBean(); premierBean.setNom( "Coyote" ); premierBean.setPrenom( "Wile E." );

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


/** Cration de la liste et insertion de quatre lments */ List<Integer> premiereListe = new ArrayList<Integer>(); premiereListe.add( 27 ); premiereListe.add( 12 ); premiereListe.add( 138 ); premiereListe.add( 6 ); /** On utilise ici la libraire Joda pour manipuler les dates, pour deux raisons : * - c'est tellement plus simple et limpide que de travailler avec les objets Date ou Calendar ! * - c'est (probablement) un futur standard de l'API Java. */ DateTime dt = new DateTime(); Integer jourDuMois = dt.getDayOfMonth(); /** Stockage du message, du bean et de la liste dans l'objet request */ request.setAttribute( ATT_MESSAGE, message ); request.setAttribute( ATT_BEAN, premierBean ); request.setAttribute( ATT_LISTE, premiereListe ); request.setAttribute( ATT_JOUR, jourDuMois ); /** Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

172/606

V ous visualisez bien ici l'intrt d'une telle pratique : en dbut de code sont accessibles en un coup dil toutes les donnes utilises en dur au sein de la classe. Si vous nommez intelligemment vos constantes, vous pouvez alors, sans avoir parcourir le code, savoir quelle constante correspond quelle donne. Ici par exemple, j'ai prfix les noms des attributs de requte par "ATT_" et nomm "VUE" la constante contenant le chemin vers notre page JSP. Ainsi, si vous procdez plus tard une modification sur une de ces donnes, il vous suffira de modifier la valeur de la constante correspondante et vous n'aurez pas besoin de parcourir votre code. C'est d'autant plus utile que votre classe est volumineuse : plus long est votre code, plus pnible il sera d'y chercher les donnes initialises en dur. Dornavant, dans tous les exemples de code venir dans la suite du cours, je mettrai en place de telles constantes.

Inclure automatiquement la JSTL Core toutes vos JSP


V ous le savez, pour pouvoir utiliser les balises de la bibliothque Core dans vos pages JSP, il est ncessaire de faire intervenir la directive include en tte de page : Code : JSP <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Admettons-le : dans une application, rares seront les vues qui ne ncessiteront pas l'utilisation de balises issues de la JSTL. Afin d'viter d'avoir dupliquer cette ligne dans l'intgralit de vos vues, il existe un moyen de rendre cette inclusion automatique ! C'est dans le fichier web.xml que vous avez la possibilit de spcifier une telle section : Code : XML <?xml version="1.0" encoding="UTF-8"?> <web-app> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <include-prelude>/WEB-INF/taglibs.jsp</include-prelude>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</jsp-property-group> </jsp-config> ...

173/606

Le fonctionnement est trs simple, la balise <jsp-property-group> ne contenant dans notre cas que deux balises : <url-pattern>, qui permet comme vous vous en doutez de spcifier quels fichiers appliquer l'inclusion automatique. Ici, j'ai choisi de l'appliquer tous les fichiers JSP de l'application ! <include-prelude>, qui permet de prciser l'emplacement du fichier inclure en tte de chacune des pages couvertes par le pattern prcdemment dfini. Ici, j'ai nomm ce fichier taglibs.jsp . Il ne nous reste donc plus qu' crer un fichier taglibs.jsp sous le rpertoire /WEB-INF de notre application, et y placer la directive taglib que nous souhaitons voir apparatre sur chacune de nos pages JSP : Code : JSP - Contenu du fichier taglibs.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Redmarrez Tomcat pour que les modifications apportes au fichier web.xml soient prises en compte, et vous n'aurez dornavant plus besoin de prciser la directive en haut de vos pages JSP : ce sera fait de manire transparente ! Sachez par ailleurs que ce systme est quivalent une inclusion statique, en d'autres termes une directive include <%@ include file="/WEB-INF/taglibs.jsp" %> place en tte de chaque JSP. Nous n'en avons pas l'utilit ici, mais sachez qu'il est possible, avec ce mme systme, d'inclure automatiquement un fichier en fin de page : il faut pour cela prciser le fichier inclure au sein d'une balise <include-coda>, et non plus <include-prelude> comme nous l'avons fait dans notre exemple. Le principe de fonctionnement reste identique, seul le nom de la balise diffre.

Formater proprement et automatiquement votre code avec Eclipse


Produire un code propre et lisible est trs important lorsque vous travaillez sur un projet, et c'est d'autant plus vrai dans le cas d'un projet professionnel et en quipe. Toutefois, harmoniser son style d'criture sur l'ensemble des classes que l'on rdige n'est pas toujours vident ; il est difficile de faire preuve d'une telle rigueur. Pour nous faciliter la tche, Eclipse propose un systme de formatage automatique du code !

Crer un style de formatage


Sous Eclipse, rendez-vous dans le menu Window > Preferences > Java > Code Style > Formatter, comme indiqu la figure suivante.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

174/606

Eclipse

format tool. Le volet de droite de cette fentre est compos de plusieurs blocs : un lien intitul "Configure Project Specific Settings...", qui vous redirige vers la fentre de configuration pour un projet en particulier uniquement ; un formulaire d'dition des profils de formatage existant ; un cadre d'aperu qui vous montre l'apparence de votre code lorsque le profil actuellement en place est utilis.

Pour modifier le style de formatage par dfaut, il suffit de cliquer sur le bouton Edit.... V ous accdez alors une vaste interface vous permettant de personnaliser un grand nombre d'options (voir la figure suivante).

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

175/606

Options de formatage du code. Je laisse aux plus motivs d'entre vous le loisir de parcourir les diffrents onglets et de modifier eux-mmes le style de formatage. V ous devrez, pour que vos modifications soient prises en compte, changer le nom du profil actuel, dans l'encadr en haut de la fentre, puis valider les changements en cliquant sur le bouton Apply en bas de fentre. Pour tous les autres, j'ai cr un modle de formatage prt l'emploi, que je vous propose de mettre en place et d'utiliser pour formater vos fichiers sources : => Tlcharger le fichier format_sdzee.xml (clic droit, puis "Enregistrer sous...") Une fois le fichier tlcharg, il vous suffit de l'importer dans votre Eclipse en cliquant sur le bouton Import... dans le formulaire de la premire fentre, comme indiqu la figure suivante.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

176/606

Import du

modle de formatage. Le nom du profil change alors pour format_sdzee, et il vous reste enfin appliquer les changements en cliquant sur le bouton Apply en bas de fentre.

Utiliser un style de formatage


Maintenant que le profil est en place, vous pouvez formater automatiquement votre code source Java. Pour cela, ouvrez un fichier Java quelconque de votre projet, et rendez-vous dans le menu Source > Format, ou utilisez tout simplement le raccourci clavier Ctrl + Maj + F. Prenons pour exemple le code de notre servlet Test (voir la figure suivante).

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

177/606

Rendu du formatage de la source. gauche la version non formate, et droite la version aprs formatage. La diffrence n'est pas norme sur un code aussi court, d'autant plus que le code d'origine tait dj relativement bien organis et indent. V ous pouvez toutefois remarquer quelques changements arant le code et facilitant sa lecture : l'alignement des valeurs des constantes en tte de classe ; l'ajout d'espaces aprs l'ouverture et avant la fermeture de parenthses.

Automatiser le formatage chaque sauvegarde


Nous voil mieux quips, mais il reste un dtail pnible : il nous faut encore taper Ctrl + Maj + F chaque fois que nous effectuons des modifications dans le code source, afin de conserver un formatage parfait. Comme nous sommes fainants, nous allons demander Eclipse d'y penser pour nous ! Rendez-vous dans le menu Window > Preferences > Java > Editor > Save Actions, comme c'est indiqu sur la figure suivante.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

178/606

Formatage automatique. Comme indiqu dans l'encadr, cochez les cases "Perform the selected actions on save" et "Format source code". Validez les changements, et c'est termin : votre code source Java sera format automatiquement selon les rgles dfinies dans votre profil chaque fois que vous enregistrerez des modifications effectues sur un fichier. V ous n'avez dornavant plus aucune excuse : votre code doit tre correctement format, organis et indent !

Documentation
Les tutoriaux d'auteurs diffrents vous feront profiter de nouveaux points de vue et angles d'attaque, et les documentations officielles vous permettront un accs des informations justes et maintenues jour (en principe).

Liens utiles
JSTL 1.1 Tag Reference (un Javadoc-like bien pratique), sur oracle.com JSP Standard Tag Library, sur java.sun.com

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


propos de la JSTL, sur stackoverflow.com Une introduction la JSTL, sur developer.com Tutoriel sur la JSTL, sur developpez.com Tutoriel sur les TagLib, sur developpez.com FAQ propos des TagLib, sur developpez.com Tutoriel complet sur l'utilisation de la JSTL, sur ibm.com

179/606

V ous l'aurez compris, cette liste ne se veut pas exhaustive, et je vous recommande d'aller chercher par vous-mmes l'information sur les forums et sites du web. En outre, faites bien attention aux dates de cration des documents que vous lisez : les ressources primes sont lgion sur le web, notamment au sujet de la plate-forme Java EE, en constante volution. N'hsitez pas demander la communaut sur le forum Java du Site du Zro, si vous ne parvenez pas trouver l'information que vous cherchez. La JSTL nous ouvre la porte aux fonctionnalits jusque l uniquement ralisables avec des scriptlets. Mettre en place des constantes permet de clarifier le code d'une classe et de simplifier sa maintenance. Eclipse peut prendre en charge pour vous le formatage et l'indentation de votre code, ainsi que la gestion automatique des imports. Cette prise en charge est automatisable, vous permettant ainsi de vous librer de cette contrainte et de vous concentrer sur l'utile. La documentation est indispensable, condition qu'elle soit jour.

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

180/606

TP Fil rouge - tape 2


Dans ce second opus de notre fil rouge, vous allez appliquer ce que vous avez dcouvert propos de la JSTL Core et des bonnes pratiques de dveloppement.

Objectifs
Les prcdents chapitres concernant uniquement la vue, vous allez ici principalement vous consacrer la reprise des pages JSP que vous aviez cres lors du premier TP. Je vous conseille de repartir sur la base de la correction que je vous ai donne pour la premire tape, cela facilitera votre comprhension des tapes et corrections venir.

Utilisation de la JSTL
L'objectif est modeste et le programme lger, mais l'important est que vous compreniez ce que vous faites et que vous soyez l'aise avec le systme des balises de la JSTL. Je vous demande de : scuriser l'affichage des donnes saisies par l'utilisateur contre les failles XSS, dans vos pages afficherClient.jsp et afficherCommande.jsp ; grer dynamiquement les diffrents liens et URL qui interviennent dans le code de vos pages JSP ; crer un menu, qui ne contiendra pour le moment que deux liens respectivement vers creerClient.jsp et creerCommande.jsp, et l'intgrer toutes vos pages existantes ; isoler la partie du formulaire responsable de la cration d'un client dans une page JSP part, et modifier les deux formulaires existants pour qu'ils incluent tous deux ce fichier la place du code actuellement dupliqu ; mettre en place une condition l'affichage du rsultat de la validation : si les donnes ont t correctement saisies, alors afficher le message de succs et la fiche rcapitulative, sinon afficher uniquement le message d'erreur.

Application des bonnes pratiques


Je vous demande, dans le code de vos servlets, de mettre en place des constantes, pour remplacer les chanes de caractres initialises directement au sein du code des mthodes doGet().

Exemples de rendus
Cration d'un client avec erreurs (figure suivante).

Cration d'un client sans erreur (figure suivante).

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

181/606

Cration d'une commande avec erreurs (figure suivante).

Cration d'une commande sans erreur (figure suivante).

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL

182/606

Conseils Utilisation de la JSTL


Je vous donne tout de mme quelques pistes pour vous guider, mais vous devriez tre capables de vous en sortir sans lire ce paragraphe : pour scuriser l'affichage des donnes saisies, pensez la balise <c:out> ; pour la gestion des liens, pensez la balise <c:url> ; pour le menu, vous pouvez crer une page menu.jsp que vous placerez dans le rpertoire /inc et que vous inclurez dans toutes les autres pages grce la balise <c:import> ; pour l'isolement du formulaire de cration d'un client, mme solution : il vous suffit de dplacer le code dans une page JSP que vous pouvez par exemple nommer inc_client_form.jsp et placer dans le rpertoire /inc, et d'utiliser la balise <c:import> pour l'inclure aux deux formulaires existant ; pour la condition, vous pouvez modifier vos servlets pour qu'elles transmettent vos JSP une information supplmentaire - pourquoi pas un boolen - afin que celles-ci puissent dterminer si la validation a t effectue avec succs ou non grce la balise <c:choose> ou <c:if>.

Bien videmment, vous n'oublierez pas d'inclure le jar de la JSTL au rpertoire lib de votre projet, afin de rendre les balises oprationnelles. V oici la figure suivante l'allure de l'arborescence que vous devriez obtenir une fois le TP termin.

V ous pouvez remarquer en encadr les trois nouveaux fichiers intervenant dans votre projet : le fichier jar de la JSTL, la page menu.jsp et la page inc_client_form.jsp.

Application des bonnes pratiques


www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


Il vous suffit ici de remplacer toutes les chanes de caractres utilises directement dans le code de vos servlets par des constantes dfinies en dehors des mthodes doGet(), comme je vous l'ai montr dans l'avant-dernier chapitre. C'est tout ce dont vous avez besoin. Au travail !

183/606

Correction
Faible dose de travail cette fois, j'espre que vous avez bien pris le temps de relire les explications concernant les diffrentes balises mettre en jeu. Encore une fois, ce n'est pas la seule manire de faire, ne vous inquitez pas si vous avez procd diffremment ; le principal est que vous ayez couvert tout ce qu'il fallait couvrir ! Pour que vous puissiez reprer rapidement ce qui a chang, j'ai surlign les modifications apportes aux codes existants. Prenez le temps de rflchir, de chercher et de coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire certains chapitres. La pratique est trs importante, ne vous ruez pas sur la solution !

Code des servlets


Servlet grant le formulaire de cration d'un client : Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.tp.beans.Client; public class CreationClient extends HttpServlet { /* Constantes */ public static final String CHAMP_NOM = "nomClient"; public static final String CHAMP_PRENOM = "prenomClient"; public static final String CHAMP_ADRESSE = "adresseClient"; public static final String CHAMP_TELEPHONE = "telephoneClient"; public static final String CHAMP_EMAIL = "emailClient"; public static final String ATT_CLIENT public static final String ATT_MESSAGE public static final String ATT_ERREUR public static final String VUE "/afficherClient.jsp"; = "client"; = "message"; = "erreur"; =

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Rcupration des donnes saisies, envoyes en tant que paramtres de * la requte GET gnre la validation du formulaire */ String nom = request.getParameter( CHAMP_NOM ); String prenom = request.getParameter( CHAMP_PRENOM ); String adresse = request.getParameter( CHAMP_ADRESSE ); String telephone = request.getParameter( CHAMP_TELEPHONE ); String email = request.getParameter( CHAMP_EMAIL ); String message; boolean erreur; /* * Initialisation du message afficher : si un des champs obligatoires

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


* du formulaire n'est pas renseign, alors on affiche un message * d'erreur, sinon on affiche un message de succs */ if ( nom.trim().isEmpty() || adresse.trim().isEmpty() || telephone.trim().isEmpty() ) { message = "Erreur - Vous n'avez pas rempli tous les champs obligatoires. <br> <a href=\"creerClient.jsp\">Cliquez ici</a> pour accder au formulaire de cration d'un client."; erreur = true; } else { message = "Client cr avec succs !"; erreur = false; } /* * Cration du bean Client et initialisation avec les donnes rcupres */ Client client = new Client(); client.setNom( nom ); client.setPrenom( prenom ); client.setAdresse( adresse ); client.setTelephone( telephone ); client.setEmail( email ); /* Ajout du bean et du message l'objet requte */ request.setAttribute( ATT_CLIENT, client ); request.setAttribute( ATT_MESSAGE, message ); request.setAttribute( ATT_ERREUR, erreur ); /* Transmission la page JSP en charge de l'affichage des donnes */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

184/606

Servlet grant le formulaire de cration d'une commande : Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; public class CreationCommande extends HttpServlet { /* Constantes */ public static final String CHAMP_NOM public static final String CHAMP_PRENOM "prenomClient"; public static final String CHAMP_ADRESSE "adresseClient"; public static final String CHAMP_TELEPHONE "telephoneClient"; public static final String CHAMP_EMAIL

= "nomClient"; = = = =

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


"emailClient"; public static final String "dateCommande"; public static final String "montantCommande"; public static final String "modePaiementCommande"; public static final String "statutPaiementCommande"; public static final String "modeLivraisonCommande"; public static final String "statutLivraisonCommande"; CHAMP_DATE CHAMP_MONTANT CHAMP_MODE_PAIEMENT CHAMP_STATUT_PAIEMENT CHAMP_MODE_LIVRAISON = = = = =

185/606

CHAMP_STATUT_LIVRAISON = = "commande"; = "message"; = "erreur"; = "dd/MM/yyyy =

public static final String ATT_COMMANDE public static final String ATT_MESSAGE public static final String ATT_ERREUR public static final String FORMAT_DATE HH:mm:ss"; public static final String VUE "/afficherCommande.jsp";

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Rcupration des donnes saisies, envoyes en tant que paramtres de * la requte GET gnre la validation du formulaire */ String nom = request.getParameter( CHAMP_NOM ); String prenom = request.getParameter( CHAMP_PRENOM ); String adresse = request.getParameter( CHAMP_ADRESSE ); String telephone = request.getParameter( CHAMP_TELEPHONE ); String email = request.getParameter( CHAMP_EMAIL ); /* Rcupration de la date courante */ DateTime dt = new DateTime(); /* Conversion de la date en String selon le format choisi

*/

DateTimeFormatter formatter = DateTimeFormat.forPattern( FORMAT_DATE ); String date = dt.toString( formatter ); double montant; try { /* Rcupration du montant */ montant = Double.parseDouble( request.getParameter( CHAMP_MONTANT ) ); } catch ( NumberFormatException e ) { /* Initialisation -1 si le montant n'est pas un nombre correct */ montant = -1; } String modePaiement = request.getParameter( CHAMP_MODE_PAIEMENT ); String statutPaiement = request.getParameter( CHAMP_STATUT_PAIEMENT ); String modeLivraison = request.getParameter( CHAMP_MODE_LIVRAISON ); String statutLivraison = request.getParameter( CHAMP_STATUT_LIVRAISON ); String message; boolean erreur; /* * Initialisation du message afficher : si un des champs obligatoires

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


* du formulaire n'est pas renseign, alors on affiche un message * d'erreur, sinon on affiche un message de succs */ if ( nom.trim().isEmpty() || adresse.trim().isEmpty() || telephone.trim().isEmpty() || montant == -1 || modePaiement.isEmpty() || modeLivraison.isEmpty() ) { message = "Erreur - Vous n'avez pas rempli tous les champs obligatoires. <br> <a href=\"creerCommande.jsp\">Cliquez ici</a> pour accder au formulaire de cration d'une commande."; erreur = true; } else { message = "Commande cre avec succs !"; erreur = false; } /* * Cration des beans Client et Commande et initialisation avec les * donnes rcupres */ Client client = new Client(); client.setNom( nom ); client.setPrenom( prenom ); client.setAdresse( adresse ); client.setTelephone( telephone ); client.setEmail( email ); Commande commande = new Commande(); commande.setClient( client ); commande.setDate( date ); commande.setMontant( montant ); commande.setModePaiement( modePaiement ); commande.setStatutPaiement( statutPaiement ); commande.setModeLivraison( modeLivraison ); commande.setStatutLivraison( statutLivraison ); /* Ajout du bean et du message l'objet requte */ request.setAttribute( ATT_COMMANDE, commande ); request.setAttribute( ATT_MESSAGE, message ); request.setAttribute( ATT_ERREUR, erreur ); /* Transmission la page JSP en charge de l'affichage des donnes */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

186/606

Code des JSP


Page de cration d'un client : Code : JSP - /creerClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'un client</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="get" action="<c:url value="/creationClient"/>"> <fieldset> <legend>Informations client</legend> <c:import url="/inc/inc_client_form.jsp" /> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body> </html>

187/606

Page de cration d'une commande : Code : JSP - /creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="get" action="<c:url value="/creationCommande"/>"> <fieldset> <legend>Informations client</legend> <c:import url="/inc/inc_client_form.jsp" /> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="dateCommande" name="dateCommande" value="" size="30" maxlength="30" disabled /> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="" size="30" maxlength="30" /> <br /> <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label> <input type="text" id="modePaiementCommande" name="modePaiementCommande" value="" size="30" maxlength="30" /> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="" size="30" maxlength="30" /> <br />

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="" size="30" maxlength="30" /> <br /> la livraison</label> <label for="statutLivraisonCommande">Statut de

188/606

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="" size="30" maxlength="30" /> <br /> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body> </html>

Page contenant le fragment de formulaire : Code : JSP - /inc/inc_client_form.jsp.jsp <%@ page pageEncoding="UTF-8" %> <label for="nomClient">Nom <span class="requis">*</span></label> <input type="text" id="nomClient" name="nomClient" value="" size="30" maxlength="30" /> <br /> <label for="prenomClient">Prnom </label> <input type="text" id="prenomClient" name="prenomClient" value="" size="30" maxlength="30" /> <br /> <label for="adresseClient">Adresse de livraison <span class="requis">*</span></label> <input type="text" id="adresseClient" name="adresseClient" value="" size="30" maxlength="60" /> <br /> <label for="telephoneClient">Numro de tlphone <span class="requis">*</span></label> <input type="text" id="telephoneClient" name="telephoneClient" value="" size="30" maxlength="30" /> <br /> <label for="emailClient">Adresse email</label> <input type="email" id="emailClient" name="emailClient" value="" size="30" maxlength="60" /> <br />

Page d'affichage d'un client : Code : JSP - /afficherClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" />

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


<title>Affichage d'un client</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ message }</p> <c:if test="${ !erreur }"> <p>Nom : <c:out value="${ client.nom }"/></p> <p>Prnom : <c:out value="${ client.prenom }"/></p> <p>Adresse : <c:out value="${ client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ client.telephone }"/></p> <p>Email : <c:out value="${ client.email }"/></p> </c:if> </div> </body> </html>

189/606

Page d'affichage d'une commande : Code : JSP - /afficherCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ message }</p> <c:if test="${ !erreur }"> <p>Client</p> <p>Nom : <c:out value="${ commande.client.nom }"/></p> <p>Prnom : <c:out value="${ commande.client.prenom }"/></p> <p>Adresse : <c:out value="${ commande.client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ commande.client.telephone }"/></p> <p>Email : <c:out value="${ commande.client.email }"/></p> <p>Commande</p> <p>Date : <c:out value="${ commande.date }"/></p> <p>Montant : <c:out value="${ commande.montant }"/></p> <p>Mode de paiement : <c:out value="${ commande.modePaiement }"/></p> <p>Statut du paiement : <c:out value="${ commande.statutPaiement }"/></p> <p>Mode de livraison : <c:out value="${ commande.modeLivraison }"/></p> <p>Statut de la livraison : <c:out value="${ commande.statutLivraison }"/></p> </c:if> </div> </body>

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


</html>

190/606

Page contenant le nouveau menu : Code : JSP - /inc/menu.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <div id="menu"> <p><a href="<c:url value="/creerClient.jsp"/>">Crer un nouveau client</a></p> <p><a href="<c:url value="/creerCommande.jsp"/>">Crer une nouvelle commande</a></p> </div>

Ajout de styles pour la mise en page du menu : Code : CSS - /inc/style.css /* Gnral ------------------------------------------------------------------------------------ */ body, p, legend, label, input { font: normal 8pt verdana, helvetica, sans-serif; } /* Forms -------------------------------------------------------------------------------------- */ fieldset { padding: 10px; border: 1px #0568CD solid; margin: 10px; } legend { font-weight: bold; color: #0568CD; } form label { float: left; width: 200px; margin: 3px 0px 0px 0px; } form input { margin: 3px 3px 0px 0px; border: 1px #999 solid; } form input.sansLabel { margin-left: 200px; } /* Styles et couleurs ------------------------------------------------------------------------- */ .requis { color: #c00; } .erreur { color: #900; }

www.siteduzero.com

Partie 3 : Une bonne vue grce la JSTL


.succes { color: #090; } .info { font-style: italic; color: #E8A22B; } /* Blocs constituants ------------------------------------------------------------------------- */ div#menu{ border: 1px solid #0568CD; padding: 10px; margin: 10px; } div#corps{ margin: 10px; }

191/606

Nous y voil enfin : nous sommes capables de crer des vues qui suivent le modle MVC . Seulement maintenant que nous sommes au point, nous aimerions bien interagir avec notre client : comment rcuprer et grer les donnes qu'il va nous envoyer ? Rendez-vous dans la partie suivante, nous avons du pain sur la planche !

www.siteduzero.com

Partie 4 : Une application interactive !

192/606

Partie 4 : Une application interactive !


Il est temps de rellement faire entrer en jeu nos servlets, de donner un sens leur existence : nous allons ici apprendre grer les informations envoyes par les clients notre application. Et dans une application web, vous le savez dj, qui dit interaction dit formulaire : cette partie aurait presque pu s'intituler "Le formulaire dans tous ses tats" , car nous allons l'examiner sous toutes les coutures ! Au passage, nous en profiterons pour dcouvrir au travers d'applications pratiques l'utilisation des sessions, des cookies et d'un nouveau composant cousin de la servlet : le filtre.

Formulaires : le b.a.-ba
Dans cette partie, nous allons littralement faire table rase. Laissons tomber nos prcdents exemples, et attaquons l'tude des formulaires par quelque chose de plus concret : un formulaire d'inscription. Cration, mise en place, rcupration des donnes, affichage et vrifications nous attendent ! Bien entendu, nous n'allons pas pouvoir raliser un vrai systme d'inscription de A Z : il nous manque encore pour cela la gestion des donnes, que nous n'allons dcouvrir que dans la prochaine partie de ce cours. Toutefois, nous pouvons d'ores et dj raliser proprement tout ce qui concerne les aspects vue, contrle et traitement d'un tel systme. Allons-y !

Mise en place
Je vous propose de mettre en place une base srieuse qui nous servira d'exemple tout au long de cette partie du cours, ainsi que dans la partie suivante. Plutt que de travailler une nime fois sur un embryon de page sans intrt, je vais tenter ici de vous placer dans un contexte plus proche du monde professionnel : nous allons travailler de manire propre et organise, et la fin de ce cours nous aurons produit un exemple utilisable dans une application relle. Pour commencer, je vous demande de crer un nouveau projet dynamique sous Eclipse. Laissez tomber le bac sable que nous avions nomm test, et repartez de zro : cela aura le double avantage de vous permettre de construire quelque chose de propre, et de vous faire pratiquer l'tape de mise en place d'un projet (cration, build-path, bibliothques, etc.). Je vous propose de nommer ce nouveau projet pro. N'oubliez pas les changements effectuer sur le build-path et le serveur de dploiement, et pensez ajouter le .jar de la JSTL notre projet, ainsi que de crer deux packages vides qui accueilleront par la suite nos servlets et beans. Revenons maintenant notre base. En apparence, elle consistera en une simple page web contenant un formulaire destin l'inscription du visiteur sur le site. Ce formulaire proposera : un champ texte recueillant l'adresse mail de l'utilisateur ; un champ texte recueillant son mot de passe ; un champ texte recueillant la confirmation de son mot de passe ; un champ texte recueillant son nom d'utilisateur (optionnel). V oici la figure suivante un aperu du design que je vous propose de mettre en place.

Formulaire d'inscription

Si vous avez t assidus lors de vos premiers pas, vous devez vous souvenir que, dornavant, nous placerons toujours nos pages JSP sous le rpertoire /WEB-INF de l'application, et qu' chaque JSP cre nous associerons une servlet. Je vous ai ainsi prpar une page JSP charge de l'affichage du formulaire d'inscription, une feuille CSS pour sa mise en forme et une servlet pour l'accompagner.

www.siteduzero.com

Partie 4 : Une application interactive !

193/606

JSP & CSS


V oici le code HTML de base du formulaire d'inscription que nous allons utiliser tout au long de cette partie : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="text" id="email" name="email" value="" size="20" maxlength="60" /> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <br /> <label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="" size="20" maxlength="20" /> <br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

Et voici le code de la feuille de style CSS accompagnant ce formulaire : Code : CSS - /form.css /* Gnral ------------------------------------------------------------------------------------ */ body, p, legend, label, input { font: normal 8pt verdana, helvetica, sans-serif; } fieldset { padding: 10px; border: 1px #0568CD solid;

www.siteduzero.com

Partie 4 : Une application interactive !


} legend { font-weight: bold; color: #0568CD; } /* Forms -------------------------------------------------------------------------------------- */ form label { float: left; width: 200px; margin: 3px 0px 0px 0px; } form input { margin: 3px 3px 0px 0px; border: 1px #999 solid; } form input.sansLabel { margin-left: 200px; } form .requis { color: #c00; }

194/606

Contrairement la page JSP, la feuille de style ne doit pas tre place sous le rpertoire /WEB-INF ! Eh oui, vous devez vous souvenir que ce rpertoire a la particularit de rendre invisible ce qu'il contient pour l'extrieur : dans le cas d'une page JSP c'est pratique, cela rend les pages inaccessibles directement depuis leur URL et nous permet de forcer le passage par une servlet ; dans le cas de notre feuille CSS par contre, c'est une autre histoire ! Car ce que vous ne savez peut-tre pas encore, c'est qu'en ralit lorsque vous accdez une page web sur laquelle est attache une feuille de style, votre navigateur va, dans les coulisses, envoyer une requte GET au serveur pour rcuprer silencieusement cette feuille, en se basant sur l'URL prcise dans la balise <link href="..." />. Et donc fatalement, si vous placez le fichier sous /WEB-INF, la requte va chouer, puisque le fichier sera cach du public et ne sera pas accessible par une URL. De toute manire, dans la trs grande majorit des cas, le contenu d'une feuille CSS est fixe ; il ne dpend pas de codes dynamiques et ne ncessite pas de prtraitements depuis une servlet comme nous le faisons jusqu' prsent pour nos pages JSP. Nous pouvons donc rendre les fichiers CSS accessibles directement aux navigateurs en les plaant dans un rpertoire public de l'application. En l'occurrence, ici j'ai plac cette feuille directement la racine de notre application, dsigne par le rpertoire WebContent dans Eclipse. Retenez donc bien que tous les lments fixes utiliss par vos pages JSP, comme les feuilles de style CSS, les feuilles de scripts Javascript ou encore les images, doivent tre placs dans un rpertoire public, et pas sous /WEB-INF. Avant de mettre en place la servlet, penchons-nous un instant sur les deux attributs de la balise <form>.

La mthode
Il est possible d'envoyer les donnes d'un formulaire par deux mthodes diffrentes : get : les donnes transiteront par l'URL via des paramtres dans une requte HTTP GET. Je vous l'ai dj expliqu, en raison des limitations de la taille d'une URL, cette mthode est peu utilise pour l'envoi de donnes. post : les donnes ne transiteront pas par l'URL mais dans le corps d'une requte HTTP POST, l'utilisateur ne les verra donc pas dans la barre d'adresses de son navigateur. Malgr leur invisibilit apparente, les donnes envoyes via la mthode POST restent aisment accessibles, et ne sont donc pas plus scurises qu'avec la mthode GET : nous devrons donc toujours vrifier la prsence et la validit des

www.siteduzero.com

Partie 4 : Une application interactive !

195/606

paramtres avant de les utiliser. La rgle d'or suivre lorsqu'on dveloppe une application web, c'est de ne jamais faire confiance l'utilisateur. V oil pourquoi nous utiliserons la plupart du temps la mthode POST pour envoyer les donnes de nos formulaires. En l'occurrence, nous avons bien prcis <form method="post" ... > dans le code de notre formulaire.

La cible
L'attribut action de la balise <form> permet de dfinir la page laquelle seront envoyes les donnes du formulaire. Puisque nous suivons le modle MVC, vous devez savoir que l'tape suivant l'envoi de donnes par l'utilisateur est le contrle. Autrement dit, direction la servlet ! C'est l'URL permettant de joindre cette servlet, c'est--dire l'URL que vous allez spcifier dans le fichier web.xml, qui doit tre prcise dans le champ action du formulaire. En l'occurrence, nous avons prcis <form ... action="inscription"> dans le code du formulaire, nous devrons donc associer l'URL /inscription notre servlet dans le mapping du fichier web.xml. Lanons-nous maintenant, et crons une servlet qui s'occupera de rcuprer les donnes envoyes et de les valider.

La servlet
V oici le code de la servlet accompagnant la JSP qui affiche le formulaire : Code : Java - com.sdzee.servlets.Inscription package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class Inscription extends HttpServlet { public static final String VUE = "/WEB-INF/inscription.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Pour le moment, elle se contente d'afficher notre page JSP l'utilisateur lorsqu'elle reoit une requte GET de sa part. Bientt, elle sera galement capable de grer la rception d'une requte POST, lorsque l'utilisateur enverra les donnes de son formulaire ! Avant d'attaquer le traitement des donnes, voici enfin la configuration de notre servlet dans le fichier web.xml : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>Inscription</servlet-name> <servlet-class>com.sdzee.servlets.Inscription</servlet-class> </servlet> <servlet-mapping> <servlet-name>Inscription</servlet-name> <url-pattern>/inscription</url-pattern> </servlet-mapping> </web-app>

www.siteduzero.com

Partie 4 : Une application interactive !

196/606

Souvenez-vous : l'adresse contenue dans le champ <url-pattern> est relative au contexte de l'application. Puisque nous avons nomm le contexte de notre projet pro, pour accder la JSP affichant le formulaire d'inscription il faut appeler l'URL suivante : Code : URL http://localhost:8080/pro/inscription

V ous devez, si tout se passe bien, visualiser le formulaire indiqu la figure suivante dans votre navigateur.

Formulaire d'inscription

Et voici sous forme d'un schma ce que nous venons de raliser (voir la figure suivante).

L'envoi des donnes


Maintenant que nous avons accs notre page d'inscription, nous pouvons saisir des donnes dans le formulaire et les envoyer au serveur. Remplissez les champs du formulaire avec un nom d'utilisateur, un mot de passe et une adresse mail de votre choix, puis cliquez sur le bouton d'inscription. V oici la figure suivante la page que vous obtenez.

Code d'erreur HTTP 405

www.siteduzero.com

Partie 4 : Une application interactive !

197/606

Eh oui, nous avons demand un envoi des donnes du formulaire par la mthode POST, mais nous n'avons pas surcharg la mthode doPost() dans notre servlet, nous avons uniquement crit une mthode doGet(). Par consquent, notre servlet n'est pas encore capable de traiter une requte POST ! Nous savons donc ce qu'il nous reste faire : il faut implmenter la mthode doPost(). V oici le code modifi de notre servlet : Code : Java - com.sdzee.servlets.Inscription package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class Inscription extends HttpServlet { public static final String VUE = "/WEB-INF/inscription.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Traitement des donnes du formulaire */ } }

Maintenant que nous avons ajout une mthode doPost(), nous pouvons envoyer les donnes du formulaire, il n'y aura plus d'erreur HTTP ! Par contre, la mthode doPost() tant vide, nous obtenons bien videmment une page blanche en retour...

Contrle : ct servlet

Maintenant que notre formulaire est accessible l'utilisateur et que la servlet en charge de son contrle est en place, nous pouvons nous attaquer la vrification des donnes envoyes par le client. Que souhaitons-nous vrifier ?

Nous travaillons sur un formulaire d'inscription qui contient quatre champs de type <input>, cela ne va pas tre bien compliqu. V oici ce que je vous propose de vrifier : que le champ obligatoire email n'est pas vide et qu'il contient une adresse mail valide ; que les champs obligatoires mot de passe et confirmation ne sont pas vides, qu'ils contiennent au moins 3 caractres, et qu'ils sont gaux ; que le champ facultatif nom, s'il est rempli, contient au moins 3 caractres. Nous allons confier ces tches trois mthodes distinctes : une mthode validationEmail(), charge de valider l'adresse mail saisie ; une mthode validationMotsDePasse(), charge de valider les mots de passe saisis ; une mthode validationNom(), charge de valider le nom d'utilisateur saisi. V oici donc le code modifi de notre servlet, impliquant la mthode doPost(), des nouvelles constantes et les mthodes de validation cres pour l'occasion, en charge de rcuprer le contenu des champs du formulaire et de les faire valider : Code : Java - com.sdzee.servlets.Inscription

www.siteduzero.com

Partie 4 : Une application interactive !


package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

198/606

public class Inscription extends HttpServlet { public static final String VUE = "/WEB-INF/inscription.jsp"; public static final String CHAMP_EMAIL = "email"; public static final String CHAMP_PASS = "motdepasse"; public static final String CHAMP_CONF = "confirmation"; public static final String CHAMP_NOM = "nom"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Rcupration des champs du formulaire. */ String email = request.getParameter( CHAMP_EMAIL ); String motDePasse = request.getParameter( CHAMP_PASS ); String confirmation = request.getParameter( CHAMP_CONF ); String nom = request.getParameter( CHAMP_NOM ); try { validationEmail( email ); validationMotsDePasse( motDePasse, confirmation ); validationNom( nom ); } catch (Exception e) { /* Grer les erreurs de validation ici. */ }

private void validationEmail( String email ) throws Exception{} private void validationMotsDePasse( String motDePasse, String confirmation ) throws Exception{} private void validationNom( String nom ) throws Exception{} }

La partie en charge de la rcupration des champs du formulaire se situe aux lignes 24 27 : il s'agit tout simplement d'appels la mthode request.getParameter(). Il nous reste maintenant implmenter nos trois dernires mthodes de validation, qui sont vides pour le moment. V oil un premier jet de ce que cela pourrait donner : Code : Java - com.sdzee.servlets.Inscription ... /** * Valide l'adresse mail saisie. */ private void validationEmail( String email ) throws Exception { if ( email != null && email.trim().length() != 0 ) { if ( !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new Exception( "Merci de saisir une adresse mail valide." ); } } else { throw new Exception( "Merci de saisir une adresse mail." ); }

www.siteduzero.com

Partie 4 : Une application interactive !


} /** * Valide les mots de passe saisis. */ private void validationMotsDePasse( String motDePasse, String confirmation ) throws Exception{ if (motDePasse != null && motDePasse.trim().length() != 0 && confirmation != null && confirmation.trim().length() != 0) { if (!motDePasse.equals(confirmation)) { throw new Exception("Les mots de passe entrs sont diffrents, merci de les saisir nouveau."); } else if (motDePasse.trim().length() < 3) { throw new Exception("Les mots de passe doivent contenir au moins 3 caractres."); } } else { throw new Exception("Merci de saisir et confirmer votre mot de passe."); } } /** * Valide le nom d'utilisateur saisi. */ private void validationNom( String nom ) throws Exception { if ( nom != null && nom.trim().length() < 3 ) { throw new Exception( "Le nom d'utilisateur doit contenir au moins 3 caractres." ); } }

199/606

Je ne dtaille pas le code de ces trois courtes mthodes. Si vous ne comprenez pas leur fonctionnement, vous devez imprativement revenir par vous-mmes sur ces notions basiques du langage Java avant de continuer ce tutoriel. J'ai ici fait en sorte que dans chaque mthode, lorsqu'une erreur de validation se produit, le code envoie une exception contenant un message explicitant l'erreur. Ce n'est pas la seule solution envisageable, mais c'est une solution qui a le mrite de tirer parti de la gestion des exceptions en Java. ce niveau, un peu de rflexion sur la conception de notre systme de validation s'impose : Que faire de ces exceptions envoyes ?

En d'autres termes, quelles informations souhaitons-nous renvoyer l'utilisateur en cas d'erreur ? Pour un formulaire d'inscription, a priori nous aimerions bien que l'utilisateur soit au courant du succs ou de l'chec de l'inscription, et en cas d'chec qu'il soit inform des erreurs commises sur les champs posant problme. Comment procder ? L encore, il y a bien des manires de faire. Je vous propose ici le mode de fonctionnement suivant : une chane resultat contenant le statut final de la validation des champs ; une Map erreurs contenant les ventuels messages d'erreur renvoys par nos diffrentes mthodes se chargeant de la validation des champs. Une HashMap convient trs bien dans ce cas d'utilisation : en l'occurrence, la cl sera le nom du champ et la valeur sera le message d'erreur correspondant. Mettons tout cela en musique, toujours dans la mthode doPost() de notre servlet : Code : Java - com.sdzee.servlets.Inscription package com.sdzee.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException;

www.siteduzero.com

Partie 4 : Une application interactive !


import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Inscription extends HttpServlet { public static final String VUE = "/WEBINF/inscription.jsp"; public static final String CHAMP_EMAIL = "email"; public static final String CHAMP_PASS = "motdepasse"; public static final String CHAMP_CONF = "confirmation"; public static final String CHAMP_NOM = "nom"; public static final String ATT_ERREURS = "erreurs"; public static final String ATT_RESULTAT = "resultat"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { String resultat; Map<String, String> erreurs = new HashMap<String, String>(); /* Rcupration des champs du formulaire. */ String email = request.getParameter( CHAMP_EMAIL ); String motDePasse = request.getParameter( CHAMP_PASS ); String confirmation = request.getParameter( CHAMP_CONF ); String nom = request.getParameter( CHAMP_NOM ); /* Validation du champ email. */ try { validationEmail( email ); } catch ( Exception e ) { erreurs.put( CHAMP_EMAIL, e.getMessage() ); } /* Validation des champs mot de passe et confirmation. */ try { validationMotsDePasse( motDePasse, confirmation ); } catch ( Exception e ) { erreurs.put( CHAMP_PASS, e.getMessage() ); } /* Validation du champ nom. */ try { validationNom( nom ); } catch ( Exception e ) { erreurs.put( CHAMP_NOM, e.getMessage() ); } /* Initialisation du rsultat global de la validation. */ if ( erreurs.isEmpty() ) { resultat = "Succs de l'inscription."; } else { resultat = "chec de l'inscription."; } /* Stockage du rsultat et des messages d'erreur dans l'objet request */ request.setAttribute( ATT_ERREURS, erreurs ); request.setAttribute( ATT_RESULTAT, resultat ); /* Transmission de la paire d'objets request/response notre JSP */ this.getServletContext().getRequestDispatcher( VUE

200/606

www.siteduzero.com

Partie 4 : Une application interactive !


).forward( request, response ); } } ...

201/606

Analysez bien les modifications importantes du code, afin de bien comprendre ce qui intervient dans ce processus de gestion des exceptions : chaque appel une mthode de validation d'un champ est entour d'un bloc try/catch ; chaque entre dans un catch, c'est--dire ds lors qu'une mthode de validation envoie une exception, on ajoute la Map erreurs le message de description inclus dans l'exception courante, avec pour cl l'intitul du champ du formulaire concern ; le message resultat contenant le rsultat global de la validation est initialis selon que la Map erreurs contient des messages d'erreurs ou non ; les deux objets erreurs et resultat sont enfin inclus en tant qu'attributs la requte avant l'appel final la vue. Le contrle des donnes dans notre servlet est maintenant fonctionnel : avec les donnes que nous transmettons notre JSP, nous pouvons y dterminer si des erreurs de validation ont eu lieu et sur quels champs.

Affichage : ct JSP
Ce qu'il nous reste maintenant raliser, c'est l'affichage de nos diffrents messages au sein de la page JSP, aprs que l'utilisateur a saisi et envoy ses donnes. V oici ce que je vous propose : 1. en cas d'erreur, affichage du message d'erreur ct de chacun des champs concerns ; 2. r-affichage dans les champs <input> des donnes auparavant saisies par l'utilisateur ; 3. affichage du rsultat de l'inscription en bas du formulaire.

1. Afficher les messages d'erreurs


L'attribut erreurs que nous recevons de la servlet ne contient des messages concernant les diffrents champs de notre formulaire que si des erreurs ont t rencontres lors de la validation de leur contenu, c'est--dire uniquement si des exceptions ont t envoyes. Ainsi, il nous suffit d'afficher les entres de la Map correspondant chacun des champs email , motdepasse, confirmation et nom : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="" size="20" maxlength="60" /> <span class="erreur">${erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['motdepasse']}</span> <br />

www.siteduzero.com

Partie 4 : Une application interactive !


<label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['confirmation']}</span> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['nom']}</span> <br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

202/606

V ous retrouvez aux lignes 17, 22, 27 et 32 l'utilisation des crochets pour accder aux entres de la Map, comme nous l'avions dj fait lors de notre apprentissage de la JSTL. De cette manire, si aucun message ne correspond dans la Map un champ du formulaire donn, c'est qu'il n'y a pas eu d'erreur lors de sa validation ct serveur. Dans ce cas, la balise <span> sera vide et aucun message ne sera affich l'utilisateur. Comme vous pouvez le voir, j'en ai profit pour ajouter un style notre feuille form.css , afin de mettre en avant les erreurs : Code : CSS form .erreur { color: #900; }

V ous pouvez maintenant faire le test : remplissez votre formulaire avec des donnes errones (une adresse mail invalide, un nom d'utilisateur trop court ou des mots de passe diffrents, par exemple) et contemplez le rsultat ! la figure suivante, le rendu attendu lorsque vous entrez un nom d'utilisateur trop court.

Erreur de validation du formulaire

2. Rafficher les donnes saisies par l'utilisateur


Comme vous le constatez sur cette dernire image, les donnes saisies par l'utilisateur avant validation du formulaire disparaissent des champs aprs validation. En ce qui concerne les champs mot de passe et confirmation, c'est trs bien ainsi : aprs une erreur de validation, il est courant de demander l'utilisateur de saisir nouveau cette information sensible. Dans le cas du nom et de l'adresse mail par contre, ce n'est vraiment pas ergonomique et nous allons tcher de les faire rapparatre. Pour cette tape, nous pourrions tre tents de simplement rafficher directement ce qu'a saisi l'utilisateur dans chacun des champs "value" des <input> du formulaire. En effet, nous savons que ces donnes sont directement accessibles via l'objet implicite

www.siteduzero.com

Partie 4 : Une application interactive !

203/606

param, qui donne accs aux paramtres de la requte HTTP. Le problme, et c'est un problme de taille, c'est qu'en procdant ainsi nous nous exposons aux failles XSS. Souvenez-vous : je vous en ai dj parl lorsque nous avons dcouvert la balise <c:out> de la JSTL ! Quel est le problme exactement ?

Bien... puisque vous semblez amnsiques et sceptiques, faisons comme si de rien n'tait, et raffichons le contenu des paramtres de la requte HTTP (c'est--dire le contenu saisi par l'utilisateur dans les champs <input> du formulaire) en y accdant directement via l'objet implicite param, aux lignes 16 et 31 : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="${param.email}" size="20" maxlength="60" /> <span class="erreur">${erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['motdepasse']}</span> <br /> <label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['confirmation']}</span> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="${param.nom}" size="20" maxlength="20" /> <span class="erreur">${erreurs['nom']}</span> <br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

Faites alors nouveau le test en remplissant et validant votre formulaire. Dornavant, les donnes que vous avez entres sont bien prsentes dans les champs du formulaire aprs validatino, ainsi que vous pouvez le constater la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

204/606

Erreur de validation du formulaire avec affichage des donnes saisies En apparence a tient la route, mais je vous ai lourdement avertis : en procdant ainsi, votre code est vulnrable aux failles XSS . V ous voulez un exemple ? Remplissez le champ nom d'utilisateur par le contenu suivant : ">Bip bip ! . Validez ensuite votre formulaire, et contemplez alors ce triste et dsagrable rsultat (voir la figure suivante).

Erreur de validation du formulaire avec faille XSS

Que s'est-il pass ?

Une faille XSS, pardi ! Eh oui, ct serveur, le contenu que vous avez saisi dans le champ du formulaire a t copi tel quel dans le code gnr par notre JSP. Il a ensuite t interprt par le navigateur ct client, qui a alors naturellement considr que le guillemet " et le chevron > contenus en dbut de saisie correspondaient la fermeture de la balise <input> ! Si vous tes encore dans le flou, voyez plutt le code HTML produit sur la ligne posant problme : Code : HTML <input type="text" id="nom" name="nom" value="">Bip bip !" size="20" maxlength="20" />

V ous devez maintenant comprendre le problme : le contenu de notre champ a t copi puis coll tel quel dans la source de notre fichier HTML final, lors de l'interprtation par le serveur de l'expression EL que nous avons mise en place (c'est--dire ${param.nom}). Et logiquement, puisque le navigateur ferme la balise <input> prmaturment, notre joli formulaire s'en retrouve dfigur. Certes, ici ce n'est pas bien grave, je n'ai fait que casser l'affichage de la page. Mais vous devez savoir qu'en utilisant ce type de failles, il est possible de causer bien plus de dommages, notamment en injectant du code Javascript dans la page l'insu du client. Je vous le rpte : la rgle d'or, c'est de ne jamais faire confiance l'utilisateur.

Pour pallier ce problme, il suffit d'utiliser la balise <c:out> de la JSTL Core pour procder l'affichage des donnes.

www.siteduzero.com

Partie 4 : Une application interactive !


V oici ce que donne alors le code modifi de notre JSP, observez bien les lignes 17 et 32 : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${param.email}"/>" size="20" maxlength="60" /> <span class="erreur">${erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['motdepasse']}</span> <br /> <label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['confirmation']}</span> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="<c:out value="${param.nom}"/>" size="20" maxlength="20" /> <span class="erreur">${erreurs['nom']}</span> <br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

205/606

La balise <c:out> se chargeant par dfaut d'chapper les caractres spciaux, le problme est rgl. Notez l'ajout de la directive taglib en haut de page, pour que la JSP puisse utiliser les balises de la JSTL Core. Faites nouveau le test avec le nom d'utilisateur prcdent, et vous obtiendrez bien cette fois le rsultat affich la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

206/606

Erreur de validation du formulaire sans faille XSS Dornavant, l'affichage n'est plus cass, et si nous regardons le code HTML gnr, nous observons bien la transformation du " et du > en leurs codes HTML respectifs par la balise <c:out> : Code : HTML <input type="text" id="nom" name="nom" value="">Bip bip !" size="20" maxlength="20" />

Ainsi, le navigateur de l'utilisateur reconnat que les caractres " et > font bien partie du contenu du champ, et qu'ils ne doivent pas tre interprts en tant qu'lments de fermeture de la balise <input> ! l'avenir, n'oubliez jamais ceci : protgez toujours les donnes que vous affichez l'utilisateur !

3. Afficher le rsultat final de l'inscription


Il ne nous reste maintenant qu' confirmer le statut de l'inscription. Pour ce faire, il suffit d'afficher l'entre resultat de la Map dans laquelle nous avons initialis le message, la ligne 39 dans le code suivant : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${param.email}"/>" size="20" maxlength="60" /> <span class="erreur">${erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label>

www.siteduzero.com

Partie 4 : Une application interactive !


<input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['motdepasse']}</span> <br /> <label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <span class="erreur">${erreurs['confirmation']}</span> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="<c:out value="${param.nom}"/>" size="20" maxlength="20" /> <span class="erreur">${erreurs['nom']}</span> <br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> <p class="${empty erreurs ? 'succes' : 'erreur'}">${resultat}</p> </fieldset> </form> </body> </html>

207/606

V ous remarquez ici l'utilisation d'un test ternaire sur notre Map erreurs au sein de la premire expression EL mise en place, afin de dterminer la classe CSS appliquer au paragraphe. Si la Map erreurs est vide, alors cela signifie qu'aucune erreur n'a eu lieu et on utilise le style nomm succes , sinon on utilise le style erreur. En effet, j'en ai profit pour ajouter un dernier style notre feuille form.css , pour mettre en avant le succs de l'inscription : Code : CSS form .succes { color: #090; }

Et sous vos yeux bahis, voici aux figures suivantes le rsultat final en cas de succs et d'erreur.

Succs de la validation du formulaire

www.siteduzero.com

Partie 4 : Une application interactive !

208/606

Erreur dans la validation du formulaire Les pages places sous /WEB-INF ne sont par dfaut pas accessibles au public par une URL. La mthode d'envoi des donnes d'un formulaire se dfinit dans le code HTML via <form method="...">. Les donnes envoyes via un formulaire sont accessibles ct serveur via des appels request.getParameter( "nom_du_champ" ). La validation des donnes, lorsque ncessaire, peut se grer simplement avec les exceptions et des interceptions via des blocs try / catch. Le renvoi de messages l'utilisateur peut se faire via une simple Map place en tant qu'attribut de requte. L'affichage de ces messages ct JSP se fait alors via de simples et courtes expressions EL. Il ne faut jamais afficher du contenu issu d'un utilisateur sans le scuriser, afin d'liminer le risque de failles XSS. Placer la validation et les traitements sur les donnes dans la servlet n'est pas une bonne solution, il faut trouver mieux.

www.siteduzero.com

Partie 4 : Une application interactive !

209/606

Formulaires : la mode MVC


Le modle MVC est trs clair sur ce point : c'est le modle qui doit s'occuper de traiter les donnes. Le contrleur doit avoir pour unique but d'aiguiller les requtes entrantes et d'appeler les ventuels traitements correspondants. Nous devons donc nous pencher sur la conception que nous venons de mettre en place afin d'en identifier les dfauts, et de la rectifier dans le but de suivre les recommandations MVC.

Analyse de notre conception


La base que nous avons ralise souffre de plusieurs maux : la rcupration et le traitement des donnes sont effectus directement au sein de la servlet. Or nous savons que d'aprs MVC, la servlet est un contrleur, et n'est donc pas cense intervenir directement sur les donnes, elle doit uniquement aiguiller les requtes entrantes vers les traitements correspondants ; aucun modle (bean ou objet mtier) n'intervient dans le systme mis en place ! Pourtant, nous savons que d'aprs MVC, les donnes sont reprsentes dans le modle par des objets...

V oici la figure suivante le schma reprsentant ce quoi nous souhaitons parvenir.

Nous allons donc reprendre notre systme d'inscription pour y mettre en place un modle : 1. cration d'un bean qui enregistre les donnes saisies et valides ; 2. cration d'un objet mtier comportant les mthodes de rcupration/conversion/validation des contenus des champs du formulaire ; 3. modification de la servlet pour qu'elle n'intervienne plus directement sur les donnes de la requte, mais aiguille simplement la requte entrante ; 4. modification de la JSP pour qu'elle s'adapte au modle frachement cr.

Cration du modle
L'utilisateur
Pour reprsenter un utilisateur dans notre modle, nous allons naturellement crer un bean nomm Utilisateur et plac dans le package com.sdzee.beans, contenant trois champs de type String : email , motDePasse et nom. Si vous ne vous souvenez plus des rgles respecter lors de la cration d'un bean, n'hsitez pas relire le chapitre qui y est ddi. V oici le rsultat attendu : Code : Java - com.sdzee.beans.Utilisateur package com.sdzee.beans; public class Utilisateur { private String email; private String motDePasse; private String nom; public void setEmail(String email) {

www.siteduzero.com

Partie 4 : Une application interactive !


this.email = email; } public String getEmail() { return email; } public void setMotDePasse(String motDePasse) { this.motDePasse = motDePasse; } public String getMotDePasse() { return motDePasse; } public void setNom(String nom) { this.nom = nom; } public String getNom() { return nom; }

210/606

C'est tout ce dont nous avons besoin pour reprsenter les donnes d'un utilisateur dans notre application. Dans notre formulaire, il y a un quatrime champ : la confirmation du mot de passe. Pourquoi ne stockons-nous pas cette information dans notre bean ? Tout simplement parce que ce bean ne reprsente pas le formulaire, il reprsente un utilisateur. Un utilisateur final possde un mot de passe et point barre : la confirmation est une information temporaire propre l'tape d'inscription uniquement ; il n'y a par consquent aucun intrt la stocker dans le modle.

Le formulaire
Maintenant, il nous faut crer dans notre modle un objet "mtier", c'est--dire un objet charg de traiter les donnes envoyes par le client via le formulaire. Dans notre cas, cet objet va contenir : 1. 2. 3. 4. les constantes identifiant les champs du formulaire ; la chane resultat et la Map erreurs que nous avions mises en place dans la servlet ; la logique de validation que nous avions utilise dans la mthode doPost() de la servlet ; les trois mthodes de validation que nous avions cres dans la servlet.

Nous allons donc dporter la majorit du code que nous avions crit dans notre servlet dans cet objet mtier, en l'adaptant afin de le faire interagir avec notre bean frachement cr. 1. Pour commencer, nous allons nommer ce nouvel objet InscriptionForm, le placer dans un nouveau package com.sdzee.forms, et y inclure les constantes dont nous allons avoir besoin : Code : Java - com.sdzee.forms.InscriptionForm package com.sdzee.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; public final class private static private static private static private static } ... InscriptionForm { final String CHAMP_EMAIL final String CHAMP_PASS final String CHAMP_CONF final String CHAMP_NOM = = = = "email"; "motdepasse"; "confirmation"; "nom";

www.siteduzero.com

Partie 4 : Une application interactive !

211/606

2. Nous devons ensuite y ajouter la chane resultat et la Map erreurs : Code : Java - com.sdzee.forms.InscriptionForm ... private String resultat; private Map<String, String> erreurs String>(); public String getResultat() { return resultat; } public Map<String, String> getErreurs() { return erreurs; } ... = new HashMap<String,

3. Nous ajoutons alors la mthode principale, contenant la logique de validation : Code : Java - com.sdzee.forms.InscriptionForm ... public Utilisateur inscrireUtilisateur( HttpServletRequest request ) { String email = getValeurChamp( request, CHAMP_EMAIL ); String motDePasse = getValeurChamp( request, CHAMP_PASS ); String confirmation = getValeurChamp( request, CHAMP_CONF ); String nom = getValeurChamp( request, CHAMP_NOM ); Utilisateur utilisateur = new Utilisateur(); try { validationEmail( email ); } catch ( Exception e ) { setErreur( CHAMP_EMAIL, e.getMessage() ); } utilisateur.setEmail( email ); try { validationMotsDePasse( } catch ( Exception e ) { setErreur( CHAMP_PASS, setErreur( CHAMP_CONF, } utilisateur.setMotDePasse( motDePasse, confirmation ); e.getMessage() ); null ); motDePasse );

try { validationNom( nom ); } catch ( Exception e ) { setErreur( CHAMP_NOM, e.getMessage() ); } utilisateur.setNom( nom ); if ( erreurs.isEmpty() ) { resultat = "Succs de l'inscription."; } else { resultat = "chec de l'inscription."; }

www.siteduzero.com

Partie 4 : Une application interactive !


} ... return utilisateur;

212/606

4. Pour terminer, nous mettons en place les mthodes de validation et les deux mthodes utilitaires ncessaires au bon fonctionnement de la logique que nous venons d'crire : Code : Java - com.sdzee.forms.InscriptionForm ... private void validationEmail( String email ) throws Exception { if ( email != null ) { if ( !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new Exception( "Merci de saisir une adresse mail valide." ); } } else { throw new Exception( "Merci de saisir une adresse mail." ); } } private void validationMotsDePasse( String motDePasse, String confirmation ) throws Exception { if ( motDePasse != null && confirmation != null ) { if ( !motDePasse.equals( confirmation ) ) { throw new Exception( "Les mots de passe entrs sont diffrents, merci de les saisir nouveau." ); } else if ( motDePasse.length() < 3 ) { throw new Exception( "Les mots de passe doivent contenir au moins 3 caractres." ); } } else { throw new Exception( "Merci de saisir et confirmer votre mot de passe." ); } } private void validationNom( String nom ) throws Exception { if ( nom != null && nom.length() < 3 ) { throw new Exception( "Le nom d'utilisateur doit contenir au moins 3 caractres." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else {

www.siteduzero.com

Partie 4 : Une application interactive !


} return valeur.trim();

213/606

Encore une fois, prenez bien le temps d'analyser les ajouts qui ont t effectus. V ous remarquerez qu'au final, il y a trs peu de changements : ajout de getters publics pour les attributs privs resultat et erreurs , afin de les rendre accessibles depuis notre JSP via des expressions EL ; la logique de validation a t regroupe dans une mthode inscrireUtilisateur(), qui retourne un bean Utilisateur ; la mthode utilitaire getValeurChamp() se charge dsormais de vrifier si le contenu d'un champ est vide ou non, ce qui nous permet aux lignes 4, 14 et 26 du dernier code de ne plus avoir effectuer la vrification sur la longueur des chanes, et de simplement vrifier si elles sont null ; dans les blocs catch du troisime code, englobant la validation de chaque champ du formulaire, nous utilisons dsormais une mthode setErreur() qui se charge de mettre jour la Map erreurs en cas d'envoi d'une exception ; toujours dans le troisime code, aprs la validation de chaque champ du formulaire, nous procdons dornavant l'initialisation de la proprit correspondante dans le bean Utilisateur, peu importe le rsultat de la validation (lignes 16, 24 et 31). V oil tout ce qu'il est ncessaire de mettre en place dans notre modle. Prochaine tape : il nous faut nettoyer notre servlet ! Le dcoupage en mthodes via setErreur() et getValeurChamp() n'est pas une obligation, mais puisque nous avons dplac notre code dans un objet mtier, autant en profiter pour coder un peu plus proprement.

Reprise de la servlet
Puisque nous avons dport la majorit du code prsent dans notre servlet vers le modle, nous pouvons l'purer grandement ! Il nous suffit d'instancier un objet mtier responsable du traitement du formulaire, et de lui passer la requte courante en appelant sa mthode inscrireUtilisateur() : Code : Java - com.sdzee.servlets.Inscription package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.beans.Utilisateur; import com.sdzee.forms.InscriptionForm ; public class Inscription extends HttpServlet { public static final String ATT_USER = "utilisateur"; public static final String ATT_FORM = "form"; public static final String VUE = "/WEB-INF/inscription.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Prparation de l'objet formulaire */ InscriptionForm form = new InscriptionForm(); /* Appel au traitement et la validation de la requte, et rcupration du bean en rsultant */ Utilisateur utilisateur = form.inscrireUtilisateur( request );

www.siteduzero.com

Partie 4 : Une application interactive !


/* Stockage du formulaire et du bean dans l'objet request request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_USER, utilisateur );

214/606

*/

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Aprs initialisation de notre objet mtier, la seule chose que notre servlet effectue est un appel la mthode inscrireUtilisateur() qui lui retourne alors un bean Utilisateur. Elle stocke finalement ces deux objets dans l'objet requte afin de rendre accessibles la JSP les donnes valides et les messages d'erreur retourns. Dornavant, notre servlet joue bien uniquement un rle d'aiguilleur : elle contrle les donnes, en se contentant d'appeler les traitements prsents dans le modle. Elle ne fait que transmettre la requte un objet mtier : aucun moment elle n'agit directement sur ses donnes.

doGet() n'est pas doPost(), et vice-versa !


Avant de passer la suite, je tiens vous signaler une mauvaise pratique, malheureusement trs courante sur le web. Dans normment d'exemples de servlets, vous pourrez trouver ce genre de code : Code : Java - Exemple de mauvaise pratique dans une servlet import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class ExempleServlet extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Ne fait rien d'autre qu'appeler une JSP */ this.getServletContext().getRequestDispatcher( "/page.jsp" ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Ici ventuellement des traitements divers, puis au lieu * d'appeler tout simplement un forwarding... */ doGet(request, response); } }

V ous comprenez ce qui a t ralis dans cet exemple ? Puisque la mthode doGet() ne fait rien d'autre qu'appeler la vue, le dveloppeur n'a rien trouv de mieux que d'appeler doGet() depuis la mthode doPost() pour raliser le forwarding vers la vue... Eh bien cette manire de faire, dans une application qui respecte MVC, est totalement dnue de sens ! Si vous souhaitez que votre servlet ralise la mme chose, quel que soit le type de la requte HTTP reue, alors : soit vous surchargez directement la mthode service() de la classe mre HttpServlet, afin qu'elle ne redirige plus les requtes entrantes vers les diffrentes mthodes doXXX() de votre servlet. V ous n'aurez ainsi plus implmenter doPost() et doGet() dans votre servlet, et pourrez directement implmenter un traitement unique dans la mthode service() ;

www.siteduzero.com

Partie 4 : Une application interactive !

215/606

soit vous faites en sorte que vos mthodes doGet() et doPost() appellent une troisime et mme mthode, qui effectuera un traitement commun toutes les requtes entrantes. Quel que soit votre choix parmi ces solutions, ce sera toujours mieux que d'crire que doGet() appelle doPost(), ou viceversa ! Pour rsumer, retenez bien que croiser ainsi les appels est une mauvaise pratique qui complique la lisibilit et la maintenance du code de votre application !

Reprise de la JSP
La dernire tape de notre mise niveau est la modification des appels aux diffrents attributs au sein de notre page JSP. En effet, auparavant notre servlet transmettait directement la chane resultat et la Map erreurs notre page, ce qui impliquait que : nous accdions directement ces attributs via nos expressions EL ; nous accdions aux donnes saisies par lutilisateur via l'objet implicite param. Maintenant, la servlet transmet le bean et l'objet mtier notre page, objets qui leur tour contiennent les donnes saisies, le rsultat et les erreurs. Ainsi, nous allons devoir modifier nos expressions EL afin qu'elles accdent aux informations travers nos deux nouveaux objets : Code : JSP - /WEB-INF/inscription.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Inscription</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="inscription"> <fieldset> <legend>Inscription</legend> <p>Vous pouvez vous inscrire via ce formulaire.</p> <label for="email">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${utilisateur.email}"/>" size="20" maxlength="60" /> <span class="erreur">${form.erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['motdepasse']}</span> <br /> <label for="confirmation">Confirmation du mot de passe <span class="requis">*</span></label> <input type="password" id="confirmation" name="confirmation" value="" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['confirmation']}</span> <br /> <label for="nom">Nom d'utilisateur</label> <input type="text" id="nom" name="nom" value="<c:out value="${utilisateur.nom}"/>" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['nom']}</span>

www.siteduzero.com

Partie 4 : Une application interactive !


<br /> <input type="submit" value="Inscription" class="sansLabel" /> <br /> <p class="${empty form.erreurs ? 'succes' : 'erreur'}">${form.resultat}</p> </fieldset> </form> </body> </html>

216/606

Les modifications apportes semblent donc mineures : l'accs aux erreurs et au rsultat se fait travers l'objet form ; l'accs aux donnes se fait travers le bean utilisateur. Mais en ralit, elles refltent un changement fondamental dans le principe : notre JSP lit dsormais directement les donnes depuis le modle ! V oici la figure suivante un schma de ce que nous avons ralis.

Nous avons ainsi avec succs mis en place une architecture MVC pour le traitement de notre formulaire : 1. les donnes saisies et envoyes par le client arrivent la mthode doPost() de la servlet ; 2. celle-ci ordonne alors le contrle des donnes reues en appelant la mthode inscrireUtilisateur() de l'objet mtier InscriptionForm ; 3. l'objet InscriptionForm effectue les traitements de validation de chacune des donnes reues ; 4. il les enregistre par la mme occasion dans le bean Utilisateur ; 5. la mthode doPost() rcupre enfin les deux objets du modle, et les transmet la JSP via la porte requte ; 6. la JSP va piocher les donnes dont elle a besoin grce aux diffrentes expressions EL mises en place, qui lui donnent un accs direct aux objets du modle transmis par la servlet ; 7. pour finir, la JSP met jour l'affichage du formulaire en se basant sur les nouvelles donnes. Il faut utiliser un bean pour stocker les donnes du formulaire. Il faut dplacer la validation et le traitement des donnes dans un objet mtier. La servlet ne fait alors plus qu'aiguiller les donnes : contrle > appels aux divers traitements > renvoi la JSP. La mthode doGet() s'occupe des requtes GET, la mthode doPost() des requtes POST. Tout autre usage est fortement dconseill. La page JSP accde dornavant aux donnes directement travers les objets du modle mis en place, et non plus depuis la requte.

www.siteduzero.com

Partie 4 : Une application interactive !

217/606

TP Fil rouge - tape 3


Avant d'aller plus loin, retour sur le fil rouge travers lequel vous tenez une belle occasion de mettre en pratique tout ce que vous venez de dcouvrir dans ces deux chapitres. V ous allez reprendre le code que vous avez dvelopp au cours des tapes prcdentes pour y ajouter des vrifications sur le contenu des champs, et l'adapter pour qu'il respecte MVC.

Objectifs Fonctionnalits
Pour commencer, vous allez devoir modifier vos pages et servlets afin d'utiliser la mthode POST pour l'envoi des donnes depuis vos formulaires de cration de clients et de commandes, et non plus la mthode GET. Au passage, vous allez en profiter pour appliquer la pratique que je vous avais communique lorsque nous avions dcouvert MVC : vous allez dplacer toutes vos JSP sous le rpertoire /WEB-INF, et grer leur accs entirement depuis vos servlets. Je ne vous l'avais pas fait faire dans les premires tapes pour ne pas vous embrouiller, mais le moment est venu ! Deuximement, je vous demande de mettre en place des vrifications sur les champs des formulaires : chaque champ marqu d'une toile dans les formulaires devra obligatoirement tre renseign ; les champs nom et prenom devront contenir au moins 2 caractres ; le champ adresse devra contenir au moins 10 caractres ; le champ telephone devra tre un nombre et contenir au moins 4 numros ; le champ email devra contenir une adresse dont le format est valide ; le montant devra tre un nombre positif, ventuellement dcimal ; les champs modePaiement, statutPaiement, modeLivraison et statutLivraison devront contenir au moins 2 caractres ; le champ date restera dsactiv. Troisimement, je vous demande de changer le principe de votre petite application : en cas d'erreur lors de la validation (champs manquants ou errons), vous devrez faire retourner l'utilisateur au formulaire de saisie en lui raffichant - sans faille XSS ! - les donnes qu'il a saisies, et en prcisant un message signalant les erreurs sur chaque champ qui pose problme ; en cas de succs, vous devrez envoyer l'utilisateur vers la page qui affiche la fiche rcapitulative.

Enfin bien videmment, tout cela se fera en respectant MVC !

Exemples de rendus
V oici aux figures suivantes quelques exemples de rendu. Cration d'un client avec erreurs :

www.siteduzero.com

Partie 4 : Une application interactive !


Cration d'un client sans erreur :

218/606

Cration d'une commande avec erreurs :

Cration d'une commande sans erreur :

www.siteduzero.com

Partie 4 : Une application interactive !

219/606

Conseils
Concernant le changement de mthode d'envoi de GET vers POST et le dplacement des JSP sous /WEB-INF, il vous suffit de bien penser ce que cela va impliquer dans vos formulaires, dans vos servlets et dans votre menu ! Typiquement, vous allez devoir faire en sorte que vos servlets affichent les formulaires en cas de rception d'une requte GET, et traitent les donnes envoyes en cas de rception d'une requte POST. Ct formulaire, vous allez devoir modifier un attribut de la balise <form>... Et dans votre menu, vous allez devoir remplacer les URL des deux pages JSP par celles de leurs servlets respectives. Concernant les vrifications sur le contenu des champs, vous pouvez bien videmment grandement vous inspirer des mthodes de validation que nous avons mises en place dans le chapitre prcdent dans notre systme d'inscription. Le principe est trs semblable, seules certaines conditions de vrification changent. De mme, afin de respecter MVC, vous pourrez prendre exemple sur la conception utilise dans le chapitre prcdent : beans, objets mtiers et servlets "nettoyes" ! V oici la figure suivante l'arborescence que vous tes censs crer.

www.siteduzero.com

Partie 4 : Une application interactive !

220/606

Enfin, concernant le renvoi vers le formulaire de cration en cas d'erreur(s), avec affichage des erreurs spcifiques chaque champ posant problme, l encore vous pouvez vous inspirer de ce que nous avons dvelopp dans le chapitre prcdent ! Bref, vous l'aurez compris, ce TP est une application pure et simple de ce que vous venez de dcouvrir travers la mise en place de notre systme d'inscription. Je m'arrte donc ici pour les conseils, vous avez toutes les informations et tous les outils en main pour remplir votre mission ! Lancez-vous, ne vous dcouragez pas et surpassez-vous !

Correction
La longueur du sujet est trompeuse : le travail que vous devez fournir est en ralit assez important ! J'espre que vous avez bien pris le temps de rflchir l'architecture que vous mettez en place, la manire dont vos classes et objets s'interconnectent et la logique de validation mettre en place. Comme toujours, ce n'est pas la seule manire de faire, le principal est que votre solution respecte les consignes que je vous ai donnes ! Prenez le temps de rflchir, de chercher et de coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire les deux prcdents chapitres. La pratique est trs importante, ne vous ruez pas sur la solution !

Code des objets mtier


Objet mtier grant le formulaire de cration d'un client : Code : Java - com.sdzee.tp.forms.CreationClientForm

www.siteduzero.com

Partie 4 : Une application interactive !


package com.sdzee.tp.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import com.sdzee.tp.beans.Client; public final class private static private static private static private static private static CreationClientForm { final String CHAMP_NOM final String CHAMP_PRENOM final String CHAMP_ADRESSE final String CHAMP_TELEPHONE final String CHAMP_EMAIL = = = = = "nomClient"; "prenomClient"; "adresseClient"; "telephoneClient"; "emailClient";

221/606

private String resultat; private Map<String, String> erreurs HashMap<String, String>(); public Map<String, String> getErreurs() { return erreurs; } public String getResultat() { return resultat; }

= new

);

public Client creerClient( HttpServletRequest request ) { String nom = getValeurChamp( request, CHAMP_NOM ); String prenom = getValeurChamp( request, CHAMP_PRENOM ); String adresse = getValeurChamp( request, CHAMP_ADRESSE ); String telephone = getValeurChamp( request, CHAMP_TELEPHONE String email = getValeurChamp( request, CHAMP_EMAIL ); Client client = new Client(); try { validationNom( nom ); } catch ( Exception e ) { setErreur( CHAMP_NOM, e.getMessage() ); } client.setNom( nom ); try { validationPrenom( prenom ); } catch ( Exception e ) { setErreur( CHAMP_PRENOM, e.getMessage() ); } client.setPrenom( prenom ); try { validationAdresse( adresse ); } catch ( Exception e ) { setErreur( CHAMP_ADRESSE, e.getMessage() ); } client.setAdresse( adresse ); try { validationTelephone( telephone ); } catch ( Exception e ) { setErreur( CHAMP_TELEPHONE, e.getMessage() ); } client.setTelephone( telephone ); try { validationEmail( email ); } catch ( Exception e ) { setErreur( CHAMP_EMAIL, e.getMessage() );

www.siteduzero.com

Partie 4 : Une application interactive !


} client.setEmail( email ); if ( erreurs.isEmpty() ) { resultat = "Succs de la cration du client."; } else { resultat = "chec de la cration du client."; } } return client;

222/606

private void validationNom( String nom ) throws Exception { if ( nom != null ) { if ( nom.length() < 2 ) { throw new Exception( "Le nom d'utilisateur doit contenir au moins 2 caractres." ); } } else { throw new Exception( "Merci d'entrer un nom d'utilisateur." ); } } { private void validationPrenom( String prenom ) throws Exception

if ( prenom != null && prenom.length() < 2 ) { throw new Exception( "Le prnom d'utilisateur doit contenir au moins 2 caractres." ); } } private void validationAdresse( String adresse ) throws Exception { if ( adresse != null ) { if ( adresse.length() < 10 ) { throw new Exception( "L'adresse de livraison doit contenir au moins 10 caractres." ); } } else { throw new Exception( "Merci d'entrer une adresse de livraison." ); } } private void validationTelephone( String telephone ) throws Exception { if ( telephone != null ) { if ( !telephone.matches( "^\\d+$" ) ) { throw new Exception( "Le numro de tlphone doit uniquement contenir des chiffres." ); } else if ( telephone.length() < 4 ) { throw new Exception( "Le numro de tlphone doit contenir au moins 4 chiffres." ); } } else { throw new Exception( "Merci d'entrer un numro de tlphone." ); } } private void validationEmail( String email ) throws Exception { if ( email != null && !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new Exception( "Merci de saisir une adresse mail valide." ); } } /*

www.siteduzero.com

Partie 4 : Une application interactive !


* Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

223/606

Objet mtier grant le formulaire de cration d'une commande : Code : Java - com.sdzee.tp.forms.CreationCommandeForm package com.sdzee.tp.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; public final class CreationCommandeForm { private static final String CHAMP_DATE "dateCommande"; private static final String CHAMP_MONTANT "montantCommande"; private static final String CHAMP_MODE_PAIEMENT "modePaiementCommande"; private static final String CHAMP_STATUT_PAIEMENT "statutPaiementCommande"; private static final String CHAMP_MODE_LIVRAISON "modeLivraisonCommande"; private static final String CHAMP_STATUT_LIVRAISON "statutLivraisonCommande"; private static final String FORMAT_DATE HH:mm:ss"; private String resultat; private Map<String, String> erreurs HashMap<String, String>(); public Map<String, String> getErreurs() { return erreurs; } = = = = = = = "dd/MM/yyyy

= new

www.siteduzero.com

Partie 4 : Une application interactive !


public String getResultat() { return resultat; } public Commande creerCommande( HttpServletRequest request ) { /* * L'objet mtier pour valider la cration d'un client existe dj, il * est donc dconseill de dupliquer ici son contenu ! la place, il * suffit de passer la requte courante l'objet mtier existant et de * rcuprer l'objet Client cr. */ CreationClientForm clientForm = new CreationClientForm(); Client client = clientForm.creerClient( request ); /* * Et trs important, il ne faut pas oublier de rcuprer le contenu de * la map d'erreurs cre par l'objet mtier CreationClientForm dans la * map d'erreurs courante, actuellement vide. */ erreurs = clientForm.getErreurs(); /* * Ensuite, il suffit de procder normalement avec le reste des champs * spcifiques une commande. */ /* * Rcupration et conversion de la date en String selon le format * choisi. */ DateTime dt = new DateTime(); DateTimeFormatter formatter = DateTimeFormat.forPattern( FORMAT_DATE ); String date = dt.toString( formatter ); String montant = getValeurChamp( request, CHAMP_MONTANT ); String modePaiement = getValeurChamp( request, CHAMP_MODE_PAIEMENT ); String statutPaiement = getValeurChamp( request, CHAMP_STATUT_PAIEMENT ); String modeLivraison = getValeurChamp( request, CHAMP_MODE_LIVRAISON ); String statutLivraison = getValeurChamp( request, CHAMP_STATUT_LIVRAISON ); Commande commande = new Commande(); commande.setClient( client ); commande.setDate( date ); double valeurMontant = -1; try { valeurMontant = validationMontant( montant ); } catch ( Exception e ) { setErreur( CHAMP_MONTANT, e.getMessage() ); } commande.setMontant( valeurMontant ); try { validationModePaiement( modePaiement ); } catch ( Exception e ) { setErreur( CHAMP_MODE_PAIEMENT, e.getMessage() ); }

224/606

www.siteduzero.com

Partie 4 : Une application interactive !


commande.setModePaiement( modePaiement ); try { validationStatutPaiement( statutPaiement ); } catch ( Exception e ) { setErreur( CHAMP_STATUT_PAIEMENT, e.getMessage() ); } commande.setStatutPaiement( statutPaiement ); try { validationModeLivraison( modeLivraison ); } catch ( Exception e ) { setErreur( CHAMP_MODE_LIVRAISON, e.getMessage() ); } commande.setModeLivraison( modeLivraison ); try { validationStatutLivraison( statutLivraison ); } catch ( Exception e ) { setErreur( CHAMP_STATUT_LIVRAISON, e.getMessage() ); } commande.setStatutLivraison( statutLivraison ); if ( erreurs.isEmpty() ) { resultat = "Succs de la cration de la commande."; } else { resultat = "chec de la cration de la commande."; } return commande;

225/606

private double validationMontant( String montant ) throws Exception { double temp; if ( montant != null ) { try { temp = Double.parseDouble( montant ); if ( temp < 0 ) { throw new Exception( "Le montant doit tre un nombre positif." ); } } catch ( NumberFormatException e ) { temp = -1; throw new Exception( "Le montant doit tre un nombre." ); } } else { temp = -1; throw new Exception( "Merci d'entrer un montant." ); } return temp; } private void validationModePaiement( String modePaiement ) throws Exception { if ( modePaiement != null ) { if ( modePaiement.length() < 2 ) { throw new Exception( "Le mode de paiement doit contenir au moins 2 caractres." ); } } else { throw new Exception( "Merci d'entrer un mode de paiement." ); } } private void validationStatutPaiement( String statutPaiement ) throws Exception { if ( statutPaiement != null && statutPaiement.length() < 2 ) {

www.siteduzero.com

Partie 4 : Une application interactive !


throw new Exception( "Le statut de paiement doit contenir au moins 2 caractres." ); } } private void validationModeLivraison( String modeLivraison ) throws Exception { if ( modeLivraison != null ) { if ( modeLivraison.length() < 2 ) { throw new Exception( "Le mode de livraison doit contenir au moins 2 caractres." ); } } else { throw new Exception( "Merci d'entrer un mode de livraison." ); } } private void validationStatutLivraison( String statutLivraison ) throws Exception { if ( statutLivraison != null && statutLivraison.length() < 2 ) { throw new Exception( "Le statut de livraison doit contenir au moins 2 caractres." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

226/606

Code des servlets


Servlet grant la cration d'un client : Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest;

www.siteduzero.com

Partie 4 : Une application interactive !


import javax.servlet.http.HttpServletResponse; import com.sdzee.tp.beans.Client; import com.sdzee.tp.forms.CreationClientForm ; public class CreationClient extends HttpServlet { public static final String ATT_CLIENT = "client"; public static final String ATT_FORM = "form"; public static final String VUE_SUCCES = "/WEBINF/afficherClient.jsp"; public static final String VUE_FORM = "/WEBINF/creerClient.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ CreationClientForm form = new CreationClientForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Client client = form.creerClient( request ); /* Ajout du bean et de l'objet mtier l'objet requte */ request.setAttribute( ATT_CLIENT, client ); request.setAttribute( ATT_FORM, form ); if ( form.getErreurs().isEmpty() ) { /* Si aucune erreur, alors affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

227/606

Servlet grant la cration d'une commande : Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.tp.beans.Commande; import com.sdzee.tp.forms.CreationCommandeForm ;

www.siteduzero.com

Partie 4 : Une application interactive !


public class CreationCommande extends HttpServlet { public static final String ATT_COMMANDE = "commande"; public static final String ATT_FORM = "form"; public static final String VUE_SUCCES INF/afficherCommande.jsp"; public static final String VUE_FORM INF/creerCommande.jsp"; = "/WEB= "/WEB-

228/606

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ CreationCommandeForm form = new CreationCommandeForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Commande commande = form.creerCommande( request ); /* Ajout du bean et de l'objet mtier l'objet requte */ request.setAttribute( ATT_COMMANDE, commande ); request.setAttribute( ATT_FORM, form ); if ( form.getErreurs().isEmpty() ) { /* Si aucune erreur, alors affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Code des JSP


Page contenant le menu : Code : JSP - /inc/menu.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <div id="menu"> <p><a href="<c:url value="/creationClient"/>">Crer un nouveau client</a></p> <p><a href="<c:url value="/creationCommande"/>">Crer une nouvelle commande</a></p> </div>

www.siteduzero.com

Partie 4 : Une application interactive !


Page contenant le fragment de formulaire : Code : JSP - /inc/inc_client_form.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <label for="nomClient">Nom <span class="requis">*</span></label> <input type="text" id="nomClient" name="nomClient" value="<c:out value="${client.nom}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['nomClient']}</span> <br /> <label for="prenomClient">Prnom </label> <input type="text" id="prenomClient" name="prenomClient" value="<c:out value="${client.prenom}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['prenomClient']}</span> <br /> <label for="adresseClient">Adresse de livraison <span class="requis">*</span></label> <input type="text" id="adresseClient" name="adresseClient" value="<c:out value="${client.adresse}"/>" size="30" maxlength="60" /> <span class="erreur">${form.erreurs['adresseClient']}</span> <br /> <label for="telephoneClient">Numro de tlphone <span class="requis">*</span></label> <input type="text" id="telephoneClient" name="telephoneClient" value="<c:out value="${client.telephone}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['telephoneClient']}</span> <br /> <label for="emailClient">Adresse email</label> <input type="email" id="emailClient" name="emailClient" value="<c:out value="${client.email}"/>" size="30" maxlength="60" /> <span class="erreur">${form.erreurs['emailClient']}</span> <br />

229/606

Page de cration d'un client : Code : JSP - /WEB-INF/creerClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'un client</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationClient"/>"> <fieldset> <legend>Informations client</legend> <c:import url="/inc/inc_client_form.jsp" /> </fieldset> <p class="info">${ form.resultat }</p>

www.siteduzero.com

Partie 4 : Une application interactive !


<p class="info">${ form.resultat }</p> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br

230/606

/>

</form> </div> </body> </html>

Page de cration d'une commande : Code : JSP - /WEB-INF/creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationCommande"/>"> <%-- Petite astuce ici : placer le client accessible via l'EL ${ commande.client } dans une variable "client" de porte request, pour que le code du fragment fonctionne la fois depuis le formulaire de cration d'un client et depuis celui de cration d'une commande. --%> <c:set var="client" value="${ commande.client }" scope="request" /> <fieldset> <legend>Informations client</legend> <c:import url="/inc/inc_client_form.jsp" /> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="v" name="dateCommande" value="<c:out value="${commande.date}"/>" size="30" maxlength="30" disabled /> <span class="erreur">${form.erreurs['dateCommande']}</span> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="<c:out value="${commande.montant}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['montantCommande']}</span> <br /> <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label> <input type="text" id="modePaiementCommande"

www.siteduzero.com

Partie 4 : Une application interactive !


name="modePaiementCommande" value="<c:out value="${commande.modePaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modePaiementCommande']}</span> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

231/606

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="<c:out value="${commande.statutPaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutPaiementCommande']}</span> <br /> <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="<c:out value="${commande.modeLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modeLivraisonCommande']}</span> <br /> la livraison</label> <label for="statutLivraisonCommande">Statut de

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="<c:out value="${commande.statutLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutLivraisonCommande']}</span> <br /> </fieldset> <p class="info">${ form.resultat }</p> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body> </html>

Page d'affichage d'un client : Code : JSP - /afficherClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'un client</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ form.resultat }</p> <p>Nom : <c:out value="${ client.nom }"/></p> <p>Prnom : <c:out value="${ client.prenom }"/></p> <p>Adresse : <c:out value="${ client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ client.telephone }"/></p> <p>Email : <c:out value="${ client.email }"/></p>

www.siteduzero.com

Partie 4 : Une application interactive !


</div> </body> </html>

232/606

Page d'affichage d'une commande : Code : JSP - /afficherCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ form.resultat }</p> <p>Client</p> <p>Nom : <c:out value="${ commande.client.nom }"/></p> <p>Prnom : <c:out value="${ commande.client.prenom }"/></p> <p>Adresse : <c:out value="${ commande.client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ commande.client.telephone }"/></p> <p>Email : <c:out value="${ commande.client.email }"/></p> <p>Commande</p> <p>Date : <c:out value="${ commande.date }"/></p> <p>Montant : <c:out value="${ commande.montant }"/></p> <p>Mode de paiement : <c:out value="${ commande.modePaiement }"/></p> <p>Statut du paiement : <c:out value="${ commande.statutPaiement }"/></p> <p>Mode de livraison : <c:out value="${ commande.modeLivraison }"/></p> <p>Statut de la livraison : <c:out value="${ commande.statutLivraison }"/></p> </div> </body> </html>

www.siteduzero.com

Partie 4 : Une application interactive !

233/606

La session : connectez vos clients


Nous allons ici dcouvrir par l'exemple comment utiliser la session. La situation que nous allons mettre en place est un systme de connexion des utilisateurs. Nous allons grandement nous inspirer de ce que nous venons de faire dans le prcdent chapitre avec notre systme d'inscription, et allons directement appliquer les bonnes pratiques dcouvertes. L encore, nous n'allons pas pouvoir mettre en place un systme complet de A Z, puisqu'il nous manque toujours la gestion des donnes. Mais ce n'est pas important : ce qui compte, c'est que vous tenez l une occasion de plus pour pratiquer la gestion des formulaires en suivant MVC !

Le formulaire
La premire chose que nous allons mettre en place est le formulaire de connexion, autrement dit la vue. Cela ne va pas nous demander trop d'efforts : nous allons reprendre l'architecture de la page JSP que nous avons cre dans le chapitre prcdent, et l'adapter nos nouveaux besoins ! V oici le code de notre page connexion.jsp, placer sous le rpertoire /WEB-INF : Code : JSP - /WEB-INF/connexion.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Connexion</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head> <body> <form method="post" action="connexion"> <fieldset> <legend>Connexion</legend> <p>Vous pouvez vous connecter via ce formulaire.</p> <label for="nom">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${utilisateur.email}"/>" size="20" maxlength="60" /> <span class="erreur">${form.erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['motdepasse']}</span> <br /> <input type="submit" value="Connexion" class="sansLabel" /> <br /> <p class="${empty form.erreurs ? 'succes' : 'erreur'}">${form.resultat}</p> </fieldset> </form> </body> </html>

Nous reprenons la mme architecture que pour le systme d'inscription : notre JSP exploite un objet form contenant les ventuels messages d'erreur, et un objet utilisateur contenant les donnes saisies et valides. Par ailleurs, nous rutilisons la mme feuille de style.

Le principe de la session
www.siteduzero.com

Partie 4 : Une application interactive !


Avant d'aller plus loin, nous devons nous attarder un instant sur ce qu'est une session. Pourquoi les sessions existent-elles ?

234/606

Notre application web est base sur le protocole HTTP, qui est un protocole dit "sans tat" : cela signifie que le serveur, une fois qu'il a envoy une rponse la requte d'un client, ne conserve pas les donnes le concernant. Autrement dit, le serveur traite les clients requte par requte et est absolument incapable de faire un rapprochement entre leur origine : pour lui, chaque nouvelle requte mane d'un nouveau client, puisqu'il oublie le client aprs l'envoi de chaque rponse... Oui, le serveur HTTP est un peu gteux ! C'est pour pallier cette lacune que le concept de session a t cr : il permet au serveur de mmoriser des informations relatives au client, d'une requte l'autre. Qu'est-ce qu'une session en Java EE ?

Souvenez-vous : nous en avions dj parl dans ce paragraphe ddi la porte des objets ainsi que dans ce chapitre sur la JSTL Core : la session reprsente un espace mmoire allou pour chaque utilisateur, permettant de sauvegarder des informations tout le long de leur visite ; le contenu d'une session est conserv jusqu' ce que l'utilisateur ferme son navigateur, reste inactif trop longtemps, ou encore lorsqu'il se dconnecte du site ; l'objet Java sur lequel se base une session est l'objet HttpSession ; il existe un objet implicite sessionScope permettant d'accder directement au contenu de la session depuis une expression EL dans une page JSP. Comment manipuler cet objet depuis une servlet ?

Pour commencer, il faut le rcuprer depuis l'objet HttpServletRequest. Cet objet propose en effet une mthode getSession(), qui permet de rcuprer la session associe la requte HTTP en cours si elle existe, ou d'en crer une si elle n'existe pas encore : Code : Java - Rcupration de la session depuis la requte HttpSession session = request.getSession();

Ainsi, tant que cette ligne de code n'a pas t appele, la session n'existe pas !

Ensuite, lorsque nous tudions attentivement la documentation de cet objet, nous remarquons entre autres : qu'il propose un couple de mthodes setAttribute() / getAttribute(), permettant la mise en place d'objets au sein de la session et leur rcupration, tout comme dans l'objet HttpServletRequest ; qu'il propose une mthode getId(), retournant un identifiant unique permettant de dterminer qui appartient telle session. Nous savons donc maintenant qu'il nous suffit d'appeler le code suivant pour enregistrer un objet en session depuis notre servlet, puis le rcuprer : Code : Java - Exemple de manipulations d'objets en session /* Cration ou rcupration de la session */ HttpSession session = request.getSession(); /* Mise en session d'une chane de caractres */ String exemple = "abc";

www.siteduzero.com

Partie 4 : Une application interactive !


session.setAttribute( "chaine", exemple ); /* Rcupration de l'objet depuis la session */ String chaine = (String) session.getAttribute( "chaine" );

235/606

C'est tout ce que nous avons besoin de savoir pour le moment. Observez sur la figure suivante l'enchanement lors de la premire visite d'un utilisateur sur une page ou servlet contenant un appel request.getSession().

1. le navigateur de l'utilisateur envoie une requte au serveur ; 2. la servlet ne trouve aucune session existante lors de l'appel getSession(), et cre donc un nouvel objet HttpSession qui contient un identifiant unique ; 3. le serveur place automatiquement l'identifiant de l'objet session dans la rponse renvoye au navigateur de l'utilisateur ; 4. le navigateur enregistre l'identifiant que le serveur lui a envoy. Observez alors la figure suivante ce qui se passe lors des visites.

1. le navigateur place automatiquement l'identifiant enregistr dans la requte qu'il envoie au serveur ; 2. la servlet retrouve la session associe l'utilisateur lors de l'appel getSession(), grce l'identifiant unique que le navigateur a plac dans la requte ; 3. le serveur sait que le navigateur du client a dj enregistr l'identifiant de la session courante, et renvoie donc une rponse classique l'utilisateur : il sait qu'il n'est pas ncessaire de lui transmettre nouveau l'identifiant ! V ous avez maintenant tout en main pour comprendre comment l'tablissement d'une session fonctionne. En ce qui concerne les

www.siteduzero.com

Partie 4 : Une application interactive !

236/606

rouages du systme, chaque chose en son temps : dans la dernire partie de ce chapitre, nous analyserons comment tout cela s'organise dans les coulisses !

Trs bien, nous avons compris comment a marche. Maintenant dans notre cas, qu'avons-nous besoin d'enregistrer en session ? En effet, c'est une trs bonne question : qu'est-il intressant et utile de stocker en session ? Rappelons-le, notre objectif est de connecter un utilisateur : nous souhaitons donc tre capables de le reconnatre d'une requte l'autre. La premire intuition qui nous vient l'esprit, c'est naturellement de sauvegarder l'adresse mail et le mot de passe de l'utilisateur dans un objet, et de placer cet objet dans la session !

Le modle
D'aprs ce que nous venons de dduire, nous pouvons nous inspirer ce que nous avons cr dans le chapitre prcdent. Il va nous falloir : un bean reprsentant un utilisateur, que nous placerons en session lors de la connexion ; un objet mtier reprsentant le formulaire de connexion, pour traiter et valider les donnes et connecter l'utilisateur. En ce qui concerne l'utilisateur, nous n'avons besoin de rien de nouveau : nous disposons dj du bean cr pour le systme d'inscription ! V ous devez maintenant bien mieux saisir le caractre rutilisable du JavaBean, que je vous vantais dans ce chapitre. En ce qui concerne le formulaire, l par contre nous allons devoir crer un nouvel objet mtier. Eh oui, nous n'y coupons pas : pour chaque nouveau formulaire, nous allons devoir mettre en place un nouvel objet. V ous dcouvrez ici un des inconvnients majeurs de l'application de MVC dans une application Java EE uniquement base sur le trio objets mtier - servlets - pages JSP : il faut rcrire les mthodes de rcupration, conversion et validation des paramtres de la requte HTTP chaque nouvelle requte traite ! Plus loin dans ce cours, lorsque nous aurons acquis un bagage assez important pour nous lancer au-del du Java EE "nu", nous dcouvrirons comment les dveloppeurs ont russi rendre ces tapes entirement automatises en utilisant un framework MVC pour construire leurs applications. En attendant, vous allez devoir faire preuve de patience et tre assidus, nous avons encore du pain sur la planche !

Nous devons donc crer un objet mtier, que nous allons nommer ConnexionForm et qui va grandement s'inspirer de l'objet InscriptionForm : Code : Java - com.sdzee.forms.ConnexionForm package com.sdzee.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import com.sdzee.beans.Utilisateur; public final class ConnexionForm { private static final String CHAMP_EMAIL private static final String CHAMP_PASS private String resultat; private Map<String, String> erreurs String>(); public String getResultat() { return resultat; } public Map<String, String> getErreurs() { return erreurs; } = "email"; = "motdepasse"; = new HashMap<String,

www.siteduzero.com

Partie 4 : Une application interactive !


public Utilisateur connecterUtilisateur( HttpServletRequest request ) { /* Rcupration des champs du formulaire */ String email = getValeurChamp( request, CHAMP_EMAIL ); String motDePasse = getValeurChamp( request, CHAMP_PASS ); Utilisateur utilisateur = new Utilisateur(); /* Validation du champ email. */ try { validationEmail( email ); } catch ( Exception e ) { setErreur( CHAMP_EMAIL, e.getMessage() ); } utilisateur.setEmail( email ); /* Validation du champ mot de passe. */ try { validationMotDePasse( motDePasse ); } catch ( Exception e ) { setErreur( CHAMP_PASS, e.getMessage() ); } utilisateur.setMotDePasse( motDePasse ); /* Initialisation du rsultat global de la validation. */ if ( erreurs.isEmpty() ) { resultat = "Succs de la connexion."; } else { resultat = "chec de la connexion."; } } return utilisateur;

237/606

/** * Valide l'adresse email saisie. */ private void validationEmail( String email ) throws Exception { if ( email != null && !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new Exception( "Merci de saisir une adresse mail valide." ); } } /** * Valide le mot de passe saisi. */ private void validationMotDePasse( String motDePasse ) throws Exception { if ( motDePasse != null ) { if ( motDePasse.length() < 3 ) { throw new Exception( "Le mot de passe doit contenir au moins 3 caractres." ); } } else { throw new Exception( "Merci de saisir votre mot de passe." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); }

www.siteduzero.com

Partie 4 : Une application interactive !


/* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

238/606

Nous retrouvons ici la mme architecture que dans l'objet InscriptionForm : des constantes d'identification des champs du formulaire ; des mthodes de validation des champs ; une mthode de gestion des erreurs ; une mthode centrale, connecterUtilisateur(), qui fait intervenir les mthodes prcdemment cites et renvoie un bean Utilisateur.

La servlet
Afin de rendre tout ce petit monde oprationnel, nous devons mettre en place une servlet ddie la connexion. Une fois n'est pas coutume, nous allons grandement nous inspirer de ce que nous avons cr dans les chapitres prcdents. V oici donc le code de la servlet nomme Connexion, place tout comme sa grande sur dans le package com.sdzee.servlets : Code : Java - com.sdzee.servlets.Connexion package com.sdzee.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.beans.Utilisateur; import com.sdzee.forms.ConnexionForm ; public class Connexion extends public static final String public static final String public static final String "sessionUtilisateur"; public static final String INF/connexion.jsp"; HttpServlet { ATT_USER = "utilisateur"; ATT_FORM = "form"; ATT_SESSION_USER = VUE = "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Affichage de la page de connexion */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ ConnexionForm form = new ConnexionForm(); /* Traitement de la requte et rcupration du bean en

www.siteduzero.com

Partie 4 : Une application interactive !


rsultant */ Utilisateur utilisateur = form.connecterUtilisateur( request ); /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /** * Si aucune erreur de validation n'a eu lieu, alors ajout du bean * Utilisateur la session, sinon suppression du bean de la session. */ if ( form.getErreurs().isEmpty() ) { session.setAttribute( ATT_SESSION_USER, utilisateur ); } else { session.setAttribute( ATT_SESSION_USER, null ); } */ /* Stockage du formulaire et du bean dans l'objet request request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_USER, utilisateur );

239/606

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Au niveau de la structure, rien de nouveau : la servlet joue bien un rle d'aiguilleur, en appelant les traitements prsents dans l'objet ConnexionForm, et en rcuprant le bean utilisateur. Ce qui change cette fois, c'est bien entendu la gestion de la session : la ligne 33, nous appelons la mthode request.getSession() pour crer une session ou rcuprer la session existante ; dans le bloc if lignes 39 43, nous enregistrons le bean utilisateur en tant qu'attribut de session uniquement si aucune erreur de validation n'a t envoye lors de l'excution de la mthode connecterUtilisateur(). partir du moment o une seule erreur est dtecte, c'est--dire si form.getErreurs().isEmpty() renvoie false, alors le bean utilisateur est supprim de la session, via un passage de l'attribut null. Pour terminer, voici la configuration de cette servlet dans le fichier web.xml de l'application : Code : XML - /WEB-INF/web.xml ... <servlet> <servlet-name>Connexion</servlet-name> <servlet-class>com.sdzee.servlets.Connexion</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>Connexion</servlet-name> <url-pattern>/connexion</url-pattern> </servlet-mapping> ...

Une fois tout ce code bien en place et votre serveur redmarr, vous pouvez accder la page de connexion via votre navigateur en vous rendant sur l'URL http://localhost:8080/pro/connexion. la figure suivante, le rsultat attendu.

www.siteduzero.com

Partie 4 : Une application interactive !

240/606

Formulaire de connexion

Oui mais voil, nous n'avons pas encore de moyen de tester le bon fonctionnement de ce semblant de systme de connexion ! Et pour cause, les seuls messages que nous affichons dans notre vue, ce sont les rsultats des vrifications du contenu des champs du formulaire... Aux figures suivantes, le rsultat actuel respectivement lors d'un chec et d'un succs de la validation.

chec de la connexion

Succs de la connexion Ce qu'il serait maintenant intressant de vrifier dans notre vue, c'est le contenu de la session. Et comme le hasard fait trs bien les choses, je vous ai justement rappel en dbut de chapitre qu'il existe un objet implicite nomm sessionScope ddi l'accs au contenu de la session !

Les vrifications Test du formulaire de connexion


Pour commencer, nous allons donc modifier notre page JSP afin d'afficher le contenu de la session : Code : JSP - /WEB-INF/connexion.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Connexion</title> <link type="text/css" rel="stylesheet" href="form.css" /> </head>

www.siteduzero.com

Partie 4 : Une application interactive !


<body> <form method="post" action="connexion"> <fieldset> <legend>Connexion</legend> <p>Vous pouvez vous connecter via ce formulaire.</p> <label for="nom">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${utilisateur.email}"/>" size="20" maxlength="60" /> <span class="erreur">${form.erreurs['email']}</span> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['motdepasse']}</span> <input type="submit" value="Connexion" class="sansLabel" /> <p class="${empty form.erreurs ? 'succes' : 'erreur'}">${form.resultat}</p> <%-- Vrification de la prsence d'un objet utilisateur en session --%> <c:if test="${!empty sessionScope.sessionUtilisateur}"> <%-- Si l'utilisateur existe en session, alors on affiche son adresse email. --%> <p class="succes">Vous tes connect(e) avec l'adresse : ${sessionScope.sessionUtilisateur.email}</p> </c:if> </fieldset> </form> </body> </html>

241/606

Dans cette courte modification, aux lignes 31 35, vous pouvez remarquer : l'utilisation de l'objet implicite sessionScope pour cibler la porte session ; la mise en place d'un test conditionnel via la balise <c:if>. Son contenu (les lignes 33 et 34) s'affichera uniquement si le test est valid ; le test de l'existence d'un objet via l'expression ${!empty ...}. Si aucun objet n'est trouv, ce test renvoie false ; l'accs au bean sessionUtilisateur de la session via l'expression ${sessionScope.sessionUtilisateur} ; l'accs la proprit email du bean sessionUtilisateur via l'expression ${sessionScope.sessionUtilisateur.email}. Rappelez-vous : nous pourrions trs bien accder l'objet en crivant simplement ${sessionUtilisateur}, et l'expression EL chercherait alors d'elle-mme un objet nomm sessionUtilisateur dans chaque porte. Mais je vous ai dj dit que la bonne pratique tait de rserver cette criture l'accs des objets de la porte page, et de toujours accder aux objets des autres portes en prcisant l'objet implicite correspondant (requestScope, sessionScope ou applicationScope).

Accdez maintenant la page http://localhost:8080/pro/connexion, et entrez des donnes valides. V oici la figure suivante le rsultat attendu aprs succs de la connexion.

www.siteduzero.com

Partie 4 : Une application interactive !

242/606

Ensuite, raffichez la page http://localhost:8080/pro/connexion, mais attention pas en appuyant sur F5 ni en actualisant la page : cela renverrait les donnes de votre formulaire ! Non, simplement entrez nouveau l'URL dans le mme onglet de votre navigateur, ou bien ouvrez un nouvel onglet. V oici la figure suivante le rsultat attendu.

V ous pouvez alors constater que la mmorisation de l'utilisateur a fonctionn ! Lorsqu'il a reu la deuxime requte d'affichage du formulaire, le serveur vous a reconnus : il sait que vous tes le client qui a effectu la requte de connexion auparavant, et a conserv vos informations dans la session. En outre, vous voyez galement que les informations qui avaient t saisies dans les champs du formulaire lors de la premire requte sont bien videmment perdues : elles n'avaient t gres que via l'objet request, et ont donc t dtruites aprs envoi de la premire rponse au client. V ous devez maintenant mieux comprendre cette histoire de porte des objets : l'objet qui a t enregistr en session reste accessible sur le serveur au fil des requtes d'un mme client, alors que l'objet qui a t enregistr dans la requte n'est accessible que lors d'une requte donne, et disparat du serveur ds que la rponse est envoye au client.

Pour finir, testons l'effacement de l'objet de la session lorsqu'une erreur de validation survient. Remplissez le formulaire avec des donnes invalides, et regardez la figure suivante le rsultat renvoy.

Le contenu du corps de la balise <c:if> n'est ici pas affich. Cela signifie que le test de prsence de l'objet en session a retourn false, et donc que notre servlet a bien pass l'objet utilisateur null dans la session. En conclusion, jusqu' prsent, tout roule !

Test de la destruction de session


www.siteduzero.com

Partie 4 : Une application interactive !


Je vous l'ai rappel en dbut de chapitre, la session peut tre dtruite dans plusieurs circonstances : l'utilisateur ferme son navigateur ; la session expire aprs une priode d'inactivit de l'utilisateur ; l'utilisateur se dconnecte.

243/606

L'utilisateur ferme son navigateur


Ce paragraphe va tre trs court. Faites le test vous-mmes : 1. 2. 3. 4. ouvrez un navigateur et affichez le formulaire de connexion ; entrez des donnes valides et connectez-vous ; fermez votre navigateur ; rouvrez-le, et rendez-vous nouveau sur le formulaire de connexion.

V ous constaterez alors que le serveur ne vous a pas reconnus : les informations vous concernant n'existent plus, et le serveur considre que vous tes un nouveau client.

La session expire aprs une priode d'inactivit de l'utilisateur


Par dfaut avec Tomcat, la dure maximum de validit impose au-del de laquelle la session est automatiquement dtruite par le serveur est de 30 minutes. V ous vous doutez bien que nous n'allons pas poireauter une demi-heure devant notre cran pour vrifier si cela fonctionne bien : vous avez la possibilit via le fichier web.xml de votre application de personnaliser cette dure. Ouvrez-le dans Eclipse et modifiez-le comme suit : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <session-config> <session-timeout>1</session-timeout> </session-config> <servlet> <servlet-name>Inscription</servlet-name> <servlet-class>com.sdzee.servlets.Inscription</servlet-class> </servlet> <servlet> <servlet-name>Connexion</servlet-name> <servlet-class>com.sdzee.servlets.Connexion</servlet-class> </servlet> <servlet-mapping> <servlet-name>Inscription</servlet-name> <url-pattern>/inscription</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Connexion</servlet-name> <url-pattern>/connexion</url-pattern> </servlet-mapping> </web-app>

Le champ <session-timeout> permet de dfinir en minutes le temps d'inactivit de l'utilisateur aprs lequel sa session est dtruite. Je l'ai ici abaiss une minute, uniquement pour effectuer notre vrification. Redmarrez Tomcat afin que la modification apporte au fichier soit prise en compte, puis : 1. ouvrez un navigateur et affichez le formulaire de connexion ; 2. entrez des donnes valides et connectez-vous ; 3. attendez quelques minutes, puis affichez nouveau le formulaire, dans la mme page ou dans un nouvel onglet. V ous constaterez alors que le serveur vous a oublis : les informations vous concernant n'existent plus, et le serveur considre

www.siteduzero.com

Partie 4 : Une application interactive !


que vous tes un nouveau client.

244/606

Une fois ce test effectu, ditez nouveau votre fichier web.xml et supprimez la section frachement ajoute : dans la suite de nos exemples, nous n'allons pas avoir besoin de cette limitation.

L'utilisateur se dconnecte
Cette dernire vrification va ncessiter un peu de dveloppement. En effet, nous avons cr une servlet de connexion, mais nous n'avons pas encore mis en place de servlet de dconnexion. Par consquent, il est pour le moment impossible pour le client de se dconnecter volontairement du site, il est oblig de fermer son navigateur ou d'attendre que la dure d'inactivit soit dpasse. Comment dtruire manuellement une session ?

Il faut regarder dans la documentation de l'objet HttpSession pour rpondre cette question : nous y trouvons une mthode invalidate(), qui supprime une session et les objets qu'elle contient, et envoie une exception si jamais elle est applique sur une session dj dtruite. Crons sans plus attendre notre nouvelle servlet nomme Deconnexion : Code : Java - com.sdzee.servlets.Deconnexion package com.sdzee.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class Deconnexion extends HttpServlet { public static final String URL_REDIRECTION = "http://www.siteduzero.com"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration et destruction de la session en cours */ HttpSession session = request.getSession(); session.invalidate(); /* Redirection vers le Site du Zro ! */ response.sendRedirect( URL_REDIRECTION );

V ous remarquez ici deux nouveauts : l'appel la mthode invalidate() de l'objet HttpSession ; la redirection vers la page de connexion via la mthode sendRedirect() de l'objet HttpServletResponse, en lieu et place du forwarding que nous utilisions auparavant. Quelle est la diffrence entre la redirection et le forwarding ?

En ralit, vous le savez dj ! Eh oui, vous ne l'avez pas encore appliqu depuis une servlet, mais je vous ai dj expliqu le principe lorsque nous avons dcouvert la balise <c:redirect>, dans cette partie du chapitre portant sur la JSTL Core.

www.siteduzero.com

Partie 4 : Une application interactive !

245/606

Pour rappel donc, une redirection HTTP implique l'envoi d'une rponse au client, alors que le forwarding s'effectue sur le serveur et le client n'en est pas tenu inform. Cela implique notamment que, via un forwarding , il est uniquement possible de cibler des pages internes l'application, alors que via la redirection il est possible d'atteindre n'importe quelle URL publique ! En l'occurrence, dans notre servlet j'ai fait en sorte que lorsque vous vous dconnectez, vous tes redirigs vers votre site web prfr. Fin du rappel, nous allons de toute manire y revenir dans le prochain paragraphe. Pour le moment, concentrons-nous sur la destruction de notre session ! Dclarons notre servlet dans le fichier web.xml de l'application : Code : XML - /WEB-INF/web.xml ... <servlet> <servlet-name>Deconnexion</servlet-name> <servlet-class>com.sdzee.servlets.Deconnexion</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>Deconnexion</servlet-name> <url-pattern>/deconnexion</url-pattern> </servlet-mapping> ...

Redmarrez Tomcat pour que la modification du fichier web.xml soit prise en compte, et testez alors comme suit : 1. 2. 3. 4. ouvrez un navigateur et affichez le formulaire de connexion ; entrez des donnes valides et connectez-vous ; entrez l'URL http://localhost:8080/pro/deconnexion ; affichez nouveau le formulaire de connexion.

V ous constaterez alors que lors de votre retour le serveur ne vous reconnat pas : la session a bien t dtruite.

Diffrence entre forwarding et redirection


Avant de continuer, puisque nous y sommes, testons cette histoire de forwarding et de redirection. Modifiez le code de la servlet comme suit : Code : Java - com.sdzee.servlets.Deconnexion package com.sdzee.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class Deconnexion extends HttpServlet { public static final String VUE = "/connexion"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration et destruction de la session en cours */ HttpSession session = request.getSession();

www.siteduzero.com

Partie 4 : Une application interactive !


session.invalidate(); /* Affichage de la page de connexion */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

246/606

Nous avons ici simplement mis en place un forwarding vers la servlet de connexion : une fois dconnects, vous allez visualiser le formulaire de connexion dans votre navigateur. Oui, mais voyez plutt ce qu'indique la figure suivante !

Forwarding. V ous comprenez ce qu'il s'est pass ? Comme je vous l'ai expliqu dans plusieurs chapitres, le client n'est pas au courant qu'un forwarding a t ralis ct serveur. Pour lui, la page jointe est /pro/deconnexion, et c'est bien elle qui lui a renvoy une rponse HTTP. Par consquent, l'URL dans la barre d'adresses de votre navigateur n'a pas chang ! Pourtant, ct serveur, a t effectu un petit enchanement de forwardings, comme on peut le voir la figure suivante.

Avec un forwarding 1. 2. 3. 4. l'utilisateur accde la page de dconnexion depuis son navigateur ; la servlet de dconnexion transfre la requte vers la servlet de connexion via un forwarding ; la servlet de connexion transfre la requte vers la JSP du formulaire de connexion via un forwarding ; la JSP renvoie le formulaire l'utilisateur.

Ce que vous devez comprendre avec ce schma, c'est que du point de vue du client, pour qui le serveur est comme une grosse bote noire, la rponse envoye par la JSP finale correspond la requte vers la servlet de dconnexion qu'il a effectue. C'est donc pour cette raison que l'utilisateur croit que la rponse est issue de la servlet de dconnexion, et que son navigateur lui affiche toujours l'URL de la page de dconnexion dans la barre d'adresses : il ne voit pas ce qu'il se passe ct serveur, et ne sait pas qu'en ralit sa requte a t balade de servlet en servlet. V oyons maintenant ce qui se passerait si nous utilisions une redirection vers la page de connexion la place du forwarding dans

www.siteduzero.com

Partie 4 : Une application interactive !


la servlet de dconnexion (voir la figure suivante).

247/606

Avec une redirection 1. l'utilisateur accde la page de dconnexion depuis son navigateur ; 2. la servlet de dconnexion envoie une demande de redirection au navigateur vers la servlet de connexion, via un sendRedirect( "/pro/connexion" ) ; 3. le navigateur de l'utilisateur excute alors la redirection et effectue alors une nouvelle requte vers la servlet de connexion ; 4. la servlet de connexion transfre la requte vers la JSP du formulaire de connexion via un forwarding ; 5. la JSP renvoie le formulaire l'utilisateur. Cette fois, vous voyez bien que la rponse envoye par la JSP finale correspond la seconde requte effectue par le navigateur, savoir celle vers la servlet de connexion. Ainsi, l'URL affiche dans la barre d'adresses du navigateur est bien celle de la page de connexion, et l'utilisateur n'est pas drout. Certes, dans le cas de notre page de dconnexion et de notre forwarding , le fait que le client ne soit pas au courant du cheminement de sa requte au sein du serveur n'a rien de troublant, seule l'URL n'est pas en accord avec l'affichage final. En effet, si le client appuie sur F5 et actualise la page, cela va appeler nouveau la servlet de dconnexion, qui va supprimer sa session si elle existe, puis nouveau faire un forwarding , puis finir par afficher le formulaire de connexion nouveau. Seulement imaginez maintenant que nous n'avons plus affaire un systme de dconnexion, mais un systme de gestion de compte en banque, dans lequel la servlet de dconnexion deviendrait une servlet de transfert d'argent, et la servlet de connexion deviendrait une servlet d'affichage du solde du compte. Si nous gardons ce systme de forwarding , aprs que le client effectue un transfert d'argent, il est redirig de manire transparente vers l'affichage du solde de son compte. Et l, a devient problmatique : si le client ne fait pas attention, et qu'il actualise la page en pensant simplement actualiser l'affichage de son solde, il va en ralit nouveau effectuer un transfert d'argent, puisque l'URL de son navigateur est reste fige sur la premire servlet contacte... V ous comprenez mieux maintenant pourquoi je vous avais conseill d'utiliser <c:redirect> plutt que <jsp:forward> dans le chapitre sur la JSTL Core, et pourquoi dans notre exemple j'ai mis en place une redirection HTTP via sendRedirect() plutt qu'un forwarding ? Avant de poursuivre, ditez le code de votre servlet de dconnexion et remettez en place la redirection HTTP vers votre site prfr, comme je vous l'ai montr avant de faire cet apart sur le forwarding .

Derrire les rideaux La thorie : principe de fonctionnement


C'est bien joli tout a, mais nous n'avons toujours pas abord la question fatidique : Comment fonctionnent les sessions ?

www.siteduzero.com

Partie 4 : Une application interactive !

248/606

Jusqu' prsent, nous ne sommes pas inquits de ce qui se passe derrire les rideaux. Et pourtant croyez-moi, il y a de quoi faire ! La chose la plus importante retenir, c'est que c'est vous qui contrlez l'existence d'une session dans votre application. Un objet HttpSession ddi un utilisateur sera cr ou rcupr uniquement lorsque la page qu'il visite implique un appel request.getSession(), en d'autres termes uniquement lorsque vous aurez plac un tel appel dans votre code. En ce qui concerne la gestion de l'objet, c'est le conteneur de servlets qui va s'en charger, en le crant et le stockant en mmoire. Au passage, le serveur dispose d'un moyen pour identifier chaque session qu'il cre : il leur attribue un identifiant unique, que nous pouvons d'ailleurs retrouver via la mthode session.getId(). Ensuite, le conteneur va mettre en place un lment particulier dans la rponse HTTP qu'il va renvoyer au client : un Cookie. Nous reviendrons plus tard sur ce que sont exactement ces cookies, et comment les manipuler. Pour le moment, voyez simplement un cookie comme un simple marqueur, un petit fichier texte qui : contient des informations envoyes par le serveur ; est stock par votre navigateur, directement sur votre poste ; a obligatoirement un nom et une valeur. En l'occurrence, le cookie mis en place lors de la gestion d'une session utilisateur par le serveur se nomme JSESSIONID, et contient l'identifiant de session unique en tant que valeur. Pour rsumer, le serveur va placer directement chez le client son identifiant de session. Donc, chaque fois qu'il cre une session pour un nouveau client, le serveur va envoyer son identifiant au navigateur de celui-ci.

Comment est gr ce cookie ?

Je vous l'ai dj dit, nous allons y revenir plus en dtail dans un prochain chapitre. Toutefois, nous pouvons dj esquisser brivement ce qui se passe dans les coulisses. La spcification du cookie HTTP, qui constitue un contrat auquel tout navigateur web dcent ainsi que tout serveur web doit adhrer, est trs claire : elle demande au navigateur de renvoyer ce cookie dans les requtes suivantes tant que le cookie reste valide. V oil donc la cl du systme : le conteneur de servlets va analyser chaque requte HTTP entrante, y chercher le cookie ayant pour nom JSESSIONID et utiliser sa valeur, c'est--dire l'identifiant de session, afin de rcuprer l'objet HttpSession associ dans la mmoire du serveur.

Quand les donnes ainsi stockes deviennent-elles obsoltes ?

Ct serveur, vous le savez dj : l'objet HttpSesssion existera tant que sa dure de vie n'aura pas dpass le temps qu'il est possible de spcifier dans la section <session-timeout> du fichier web.xml , qui est par dfaut de trente minutes. Donc si le client n'utilise plus l'application pendant plus de trente minutes, le conteneur de servlets dtruira sa session. Aucune des requtes suivantes, y compris celles contenant le cookie, n'aura alors accs la prcdente session : le conteneur de servlets en crera une nouvelle. Ct client, le cookie de session a une dure de vie galement, qui par dfaut est limite au temps durant lequel le navigateur reste ouvert. Lorsque le client ferme son navigateur, le cookie est donc dtruit ct client. Si le client ouvre nouveau son navigateur, le cookie associ la prcdente session ne sera alors plus envoy. Nous revenons alors au principe gnral que je vous ai nonc quelques lignes plus tt : un appel request.getSession() retournerait alors un nouvel objet HttpSession, et mettrait ainsi en place un nouveau cookie contenant un nouvel identifiant de session. Plutt que de vous ressortir les schmas prcdents en modifiant et compltant les lgendes et explications pour y faire apparatre la gestion du cookie, je vais vous faire pratiquer ! Nous allons directement tester notre petit systme de connexion, et analyser ce qui se trame dans les entrailles des changes HTTP...

La pratique : scrutons nos requtes et rponses


Pour commencer, nous allons reprendre notre exemple de connexion et analyser les changes qu'il engendre :

www.siteduzero.com

Partie 4 : Une application interactive !

249/606

1. redmarrez votre serveur Tomcat ; 2. fermez votre navigateur, puis ouvrez-le nouveau ; 3. supprimez toutes les donnes qui y sont automatiquement enregistres. Depuis Firefox ou Chrome, il suffit d'appuyer simultanment sur Ctrl + Maj + Suppr pour qu'un menu de suppression du cache, des cookies et autres donnes diverses apparaisse (voir les figures suivantes).

Suppression des donnes sous Firefox

Suppression des

donnes sous Chrome 4. ouvrez un nouvel onglet vide, et appuyez alors sur F12 pour lancer Firebug depuis Firefox, ou l'outil quivalent intgr depuis Chrome ; 5. cliquez alors sur l'onglet Rseau de Firebug, ou sur l'onglet Network de l'outil intgr Chrome.

Le tout premier accs


Rendez-vous ensuite sur la page http://localhost:8080/pro/connexion. Les donnes enregistres ct client ont t effaces, et le serveur a t redmarr, il s'agit donc ici de notre toute premire visite sur une page du site. En outre, nous savons que la servlet Connexion associe cette page contient un appel request.getSession(). Observez alors ce qui s'affiche dans votre outil (voir les figures suivantes).

www.siteduzero.com

Partie 4 : Une application interactive !

250/606

Cookie dans la rponse avec l'outil Firebug

www.siteduzero.com

Partie 4 : Une application interactive !

251/606

Cookie dans la rponse avec l'outil de Chrome V ous pouvez ici remarquer plusieurs choses importantes : la rponse renvoye par le serveur contient une instruction Set-Cookie, destine mettre en place le cookie de session dans le navigateur du client ; le nom du cookie est bien JSESSIONID, et sa valeur est bien un long identifiant unique ; bien que je sois le seul rel client qui accde au site, le serveur considre mes visites depuis Firefox et Chrome comme tant issues de deux clients distincts, et gnre donc deux sessions diffrentes. Vrifiez les identifiants, ils sont bien diffrents d'un cran l'autre. Dornavant, je vous afficherai uniquement des captures d'cran ralises avec l'outil de Chrome, pour ne pas surcharger d'images ce chapitre. Si vous utilisez Firebug, reportez-vous la capture prcdente si jamais vous ne vous souvenez plus o regarder les informations relatives aux changes HTTP.

L'accs suivant, avec la mme session


Dans la foule, rendez-vous nouveau sur cette mme page de connexion (actualisez la page via un appui sur F5 par exemple). Observez alors la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

252/606

Cookie dans la requte L encore, vous pouvez remarquer plusieurs choses importantes : un cookie est, cette fois, envoy par le navigateur au serveur, dans le paramtre Cookie de l'en-tte de la requte HTTP effectue ; sa valeur correspond celle contenue dans le cookie envoy par le serveur dans la rponse prcdente ; aprs rception de la premire rponse contenant l'instruction Set-Cookie, le navigateur avait donc bien sauvegard le cookie gnr par le serveur, et le renvoie automatiquement lors des requtes suivantes ; dans la deuxime rponse du serveur, il n'y a cette fois plus d'instruction Set-Cookie : le serveur ayant reu un cookie nomm JSESSIONID depuis le client, et ayant trouv dans sa mmoire une session correspondant l'identifiant contenu dans la valeur du cookie, il sait que le client a dj enregistr la session en cours et qu'il n'est pas ncessaire de demander nouveau la mise en place d'un cookie !

L'accs suivant, aprs une dconnexion !


Rendez-vous maintenant sur la page http://localhost:8080/pro/deconnexion, puis retournez ensuite sur http://localhost:8080/pro/connexion. Observez la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

253/606

Cookie dans la requte et la rponse Cette fois encore, vous pouvez remarquer plusieurs choses importantes : deux cookies nomms JSESSIONID interviennent : un dans la requte et un dans la rponse ; la valeur de celui prsent dans la requte contient l'identifiant de notre prcdente session. Puisque nous n'avons pas ferm notre navigateur ni supprim les cookies enregistrs, le navigateur considre que la session est toujours ouverte ct serveur, et envoie donc par dfaut le cookie qu'il avait enregistr lors de l'change prcdent ! la valeur de celui prsent dans la rponse contient un nouvel identifiant de session. Le serveur ayant supprim la session de sa mmoire lors de la dconnexion du client (souvenez-vous du code de notre servlet de dconnexion), il ne trouve aucune session qui correspond l'identifiant envoy par le navigateur dans le cookie de la requte. Il cre donc une nouvelle session, et demande aussitt au navigateur de remplacer le cookie existant par celui contenant le nouveau numro de session, toujours via l'instruction Set-Cookie de la rponse renvoye !

L'accs une page sans session


Nous allons cette fois accder une page qui n'implique aucun appel request.getSession(). Il nous faut donc crer une page JSP pour l'occasion, que nous allons nommer accesPublic.jsp et placer directement la racine de notre application, sous le rpertoire WebContent : Code : JSP - /accesPublic.jsp

www.siteduzero.com

Partie 4 : Une application interactive !


<%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Accs public</title> </head> <body> <p>Bienvenue sur la page d'accs public.</p> </body> </html>

254/606

Redmarrez Tomcat, effacez les donnes de votre navigateur via un Ctrl + Maj + Suppr, et rendez-vous alors sur la page http://localhost:8080/pro/accesPublic.jsp. Observez la figure suivante.

Cration de session par la JSP Pourquoi le serveur demande-t-il la mise en place d'un cookie de session dans le navigateur ?!

En effet, c'est un comportement troublant ! Je vous ai annonc qu'une session n'existait que lorsqu'un appel

www.siteduzero.com

Partie 4 : Une application interactive !

255/606

request.getSession() tait effectu. Or, le contenu de notre page accesPublic.jsp ne fait pas intervenir de session, et aucune servlet ne lui est associe : d'o sort cette session ? Eh bien rassurez-vous, je ne vous ai pas menti : c'est bien vous qui contrlez la cration de la session. Seulement voil, il existe un comportement qui vous est encore inconnu, celui d'une page JSP : par dfaut, une page JSP va toujours tenter de crer ou rcuprer une session . Nous pouvons d'ailleurs le vrifier en jetant un il au code de la servlet auto-gnre par Tomcat. Nous l'avions dj fait lorsque nous avions dcouvert les JSP pour la premire fois, et je vous avais alors fait remarquer que le rpertoire contenant ces fichiers pouvait varier selon votre installation et votre systme. V oici un extrait du code du fichier accesPublic_jsp.java gnr : Code : Java C:\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\pro\org\apache\jsp \accesPublic_jsp.java javax.servlet.http.HttpSession session = null; ... session = pageContext.getSession();

V oil donc l'explication de l'existence d'une session lors de l'accs notre page JSP : dans le code auto-gnr, il existe un appel la mthode getSession() ! Comment viter la cration automatique d'une session depuis une page JSP ?

La solution qui s'offre nous est l'utilisation de la directive page. V oici la ligne ajouter en dbut de page pour empcher la cration d'une session : Code : JSP <%@ page session="false" %>

Toutefois, comprenez bien : cette directive dsactive la session sur toute la page JSP. Autrement dit, en procdant ainsi vous interdisez la manipulation de sessions depuis votre page JSP. Dans ce cas prcis, tout va bien, notre page n'en fait pas intervenir. Mais dans une page qui accde des objets prsents en session, vous ne devez bien videmment pas mettre en place cette directive !

ditez donc votre page accesPublic.jsp et ajoutez-y cette directive en dbut de code. Redmarrez alors Tomcat, effacez les donnes de votre navigateur via un Ctrl + Maj + Suppr, et rendez-vous nouveau sur la page http://localhost:8080/pro/accesPublic.jsp. Observez la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

256/606

Dsactivation de la session dans la JSP V ous pouvez cette fois remarquer qu'aucun cookie n'intervient dans l'change HTTP ! Le serveur ne cherche pas crer ni rcuprer de session, et par consquent il ne demande pas la mise en place d'un cookie dans le navigateur de l'utilisateur. Ce genre d'optimisation n'a absolument aucun impact sur une application de petite envergure, vous pouvez alors trs bien vous en passer. Mais sur une application trs forte frquentation ou simplement sur une page trs fort trafic, en dsactivant l'accs la session lorsqu'elle n'est pas utilise, vous pouvez gagner en performances et en espace mmoire disponible : vous empcherez ainsi la cration d'objets inutiliss par le serveur, qui occuperont de la place jusqu' ce qu'ils soient dtruits aprs la priode d'inactivit dpasse.

L'accs une page sans cookie


Dernier scnario et non des moindres, l'accs une page faisant intervenir un appel request.getSession() depuis un navigateur qui n'accepte pas les cookies ! Eh oui, tous les navigateurs ne gardent pas leurs portes ouvertes, et certains refusent la sauvegarde de donnes sous forme de cookies. Procdez comme suit pour bloquer les cookies depuis votre navigateur. Depuis Firefox : 1. Allez sur la page http://localhost:8080/pro/connexion. 2. Faites un clic droit dans la page et slectionnez Informations sur la page. 3. Slectionnez alors le panneau Permissions .

www.siteduzero.com

Partie 4 : Une application interactive !

257/606

4. Sous Dfinir des cookies , dcochez Permissions par dfaut et cochez Bloquer, comme indiqu la figure suivante.

5. Fermez la fentre Informations sur la page. Depuis Chrome : 1. Cliquez sur l'icne reprsentant une cl molette 2. 3. 4. 5. qui est situe dans la barre d'outils du navigateur.

Slectionnez Paramtres . Cliquez sur Afficher les paramtres avancs . Dans la section "Confidentialit", cliquez sur le bouton Paramtres de contenu. Dans la section "Cookies", modifiez les paramtres comme indiqu la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !

258/606

Redmarrez ensuite Tomcat, effacez les donnes de votre navigateur via un Ctrl + Maj + Suppr, et rendez-vous sur la page http://localhost:8080/pro/connexion. V ous observerez alors que la rponse du serveur contient une instruction Set-Cookie. Actualisez maintenant la page en appuyant sur F5, et vous constaterez cette fois que la requte envoye par votre navigateur ne contient pas de cookie, et que la rponse du serveur contient nouveau une instruction Set-Cookie prsentant un identifiant de session diffrent ! C'est tout fait logique : le navigateur n'accepte plus les cookies, il n'a donc pas enregistr le premier identifiant envoy par le serveur dans la premire rponse. Par consquent, il n'a pas envoy d'identifiant dans la requte suivante ; le serveur ne trouvant aucune information de session dans la seconde requte envoye par le navigateur du client, il le considre comme un nouveau visiteur, cre une nouvelle session et lui demande d'en enregistrer le nouvel identifiant dans un cookie.

Bien, c'est logique. Mais dans ce cas, comment le serveur peut-il associer une session un utilisateur ?

V oil en effet une excellente question : comment le serveur va-t-il tre capable de retrouver des informations en session s'il n'est pas capable de reconnatre un visiteur d'une requte l'autre ? tant donn l'tat actuel de notre code, la rponse est simple : il ne peut pas ! D'ailleurs, vous pouvez vous en rendre compte simplement.

www.siteduzero.com

Partie 4 : Une application interactive !

259/606

1. Rendez-vous sur la page de connexion, saisissez des donnes correctes et validez le formulaire. Observez la figure suivante.

2. Ouvrez alors un nouvel onglet, et rendez-vous nouveau sur la page de connexion. Observez la figure suivante.

Lors de nos prcdents tests, dans la partie sur les vrifications, le formulaire raffichait l'adresse mail avec laquelle vous vous tiez connects auparavant. Cette fois, aucune information n'est raffiche et le formulaire de connexion apparat nouveau vierge. V ous constatez donc bien l'incapacit du serveur vous reconnatre ! Pas de panique, nous allons y remdier trs simplement. Dans notre page connexion.jsp, nous allons modifier une ligne de code : Code : JSP - /WEB-INF/connexion.jsp <!-- Dans la page connexion.jsp, remplacez la ligne suivante : --> <form method="post" action="connexion"> <!-- Par cette ligne : --> <form method="post" action="<c:url value="/connexion" />">

Si vous reconnaissez ici la balise <c:url> de la JSTL Core, vous devez galement vous souvenir qu'elle est quipe pour la gestion automatique des sessions. Je vous avais en effet dj expliqu que cette balise avait l'effet suivant : Code : JSP <%-- L'url ainsi gnre --%> <c:url value="test.jsp" /> <%-- Sera rendue ainsi dans la page web finale, si le cookie est prsent --%> test.jsp <%-- Et sera rendue sous cette forme si le cookie est absent --%> test.jsp;jsessionid=BB569C7F07C5E887A4D

Et a, c'est exactement ce dont nous avons besoin ! Puisque notre navigateur n'accepte plus les cookies, nous n'avons pas d'autre choix que de faire passer l'identifiant de session directement au sein de l'URL.

www.siteduzero.com

Partie 4 : Une application interactive !


Une fois la modification sur la page connexion.jsp effectue, suivez le scnario de tests suivant.

260/606

Rendez-vous nouveau sur la page http://localhost:8080/pro/connexion, et regardez la fois la rponse envoye par le serveur et le code source de la page de connexion. V ous constaterez alors que, puisque le serveur ne dtecte aucun cookie prsent chez le client, il va d'un ct tenter de passer l'identifiant de session via l'instruction Set-Cookie, et de l'autre gnrer une URL prcisant l'identifiant de session. V oyez plutt la figure suivante.

Connectez-vous alors avec des donnes valides. V ous retrouverez alors, dans la barre d'adresses de votre navigateur, l'URL modifie par la balise <c:url>, contenant l'identifiant de session pass par le serveur (voir la figure suivante).

www.siteduzero.com

Partie 4 : Une application interactive !

261/606

Ouvrez un nouvel onglet, et copiez/collez l'URL dans la barre d'adresses pour y ouvrir nouveau la page de connexion en conservant le JSESSIONID. V ous constaterez cette fois que le serveur vous a bien reconnus en se basant sur l'identifiant contenu dans l'URL que vous lui transmettez, et qu'il est capable de retrouver l'adresse mail avec laquelle vous vous tes connects (voir la figure suivante).

Accdez maintenant la page http://localhost:8080/pro/connexion sans prciser le JSESSIONID dans l'URL, et constatez que le serveur est nouveau incapable de vous reconnatre et vous affiche un formulaire vierge ! Nous en avons enfin termin avec notre batterie de tests, et avec tout ce que vous avez dcouvert, les sessions n'ont maintenant presque plus aucun secret pour vous !

En rsum
La session complte un manque du protocole HTTP, en permettant au serveur de reconnatre et tracer un visiteur ; une session est un espace mmoire allou sur le serveur, et dont le contenu n'est accessible que depuis le serveur ; le serveur reprsente une session par l'objet HttpSession, initialis par un simple appel request.getSession() ; afin de savoir quel client est associ telle session cre, le serveur transmet au client l'identifiant de la session qui lui est ddie, le JSESSIONID, dans les en-ttes de la rponse HTTP sous forme d'un cookie ; si le navigateur accepte les cookies, il stocke alors ce cookie contenant l'identifiant de session, et le retransmet au serveur dans les en-ttes de chaque requte HTTP qui va suivre ; si le serveur lui renvoie un nouveau numro, autrement dit si le serveur a ferm l'ancienne session et en a ouvert une nouvelle, alors le navigateur remplace l'ancien numro stock par ce nouveau numro, en crasant l'ancien cookie par le nouveau ; si le navigateur n'accepte pas les cookies, alors le serveur dispose d'un autre moyen pour identifier le client : il est capable de chercher l'identifiant directement dans l'URL de la requte, et pas uniquement dans ses en-ttes ; il suffit au dveloppeur de manipuler correctement les URL qu'il met en place dans son code - avec <c:url> par exemple - pour permettre une gestion continue des sessions, indpendante de l'acceptation ou non des cookies ct client ; une session expire et est dtruite aprs le dpart d'un visiteur (fermeture de son navigateur), aprs son inactivit prolonge ou aprs sa dconnexion manuelle ; pour effectuer une dconnexion manuellement, ct serveur il suffit d'appeler la mthode session.invalidate() ;

www.siteduzero.com

Partie 4 : Une application interactive !

262/606

la dsactivation de la gestion des sessions sur une page en particulier est possible via la directive JSP <%@ page session="false" %>.

www.siteduzero.com

Partie 4 : Une application interactive !

263/606

Le filtre : crez un espace membre


Maintenant que nous savons manipuler les sessions et connecter nos utilisateurs, il serait intressant de pouvoir mettre en place un espace membre dans notre application : c'est un ensemble de pages web qui est uniquement accessible aux utilisateurs connects. Pour ce faire, nous allons commencer par tudier le principe sur une seule page, via une servlet classique. Puis nous allons tendre ce systme tout un ensemble de pages, et dcouvrir un nouveau composant, cousin de la servlet : le filtre !

Restreindre l'accs une page


Ce principe est massivement utilis dans la plupart des applications web : les utilisateurs enregistrs et connects un site ont bien souvent accs plus de contenu et de fonctionnalits que les simples visiteurs. Comment mettre en place une telle restriction d'accs ?

Jusqu' prsent, nous avons pris l'habitude de placer toutes nos JSP sous le rpertoire /WEB-INF, et de les rendre accessibles travers des servlets. Nous savons donc que chaque requte qui leur est adresse passe d'abord par une servlet. Ainsi pour limiter l'accs une page donne, la premire intuition qui nous vient l'esprit, c'est de nous servir de la servlet qui lui est associe pour effectuer un test sur le contenu de la session, afin de vrifier si le client est dj connect ou non.

Les pages d'exemple


Mettons en place pour commencer deux pages JSP : une dont nous allons plus tard restreindre l'accs aux utilisateurs connects uniquement, nomme accesRestreint.jsp et place sous /WEB-INF ; une qui sera accessible tous les visiteurs, nomme accesPublic.jsp et place sous la racine du projet (symbolise par le dossier WebContent sous Eclipse). Le contenu de ces pages importe peu, voici l'exemple basique que je vous propose : Code : JSP - /WEB-INF/accesRestreint.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Accs restreint</title> </head> <body> <p>Vous tes connect(e) avec l'adresse ${sessionScope.sessionUtilisateur.email}, vous avez bien accs l'espace restreint.</p> </body> </html>

Reprenez alors la page accesPublic.jsp cre dans le chapitre prcdent et modifiez son code ainsi : Code : JSP - /accesPublic.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Accs public</title> </head> <body> <p>Vous n'avez pas accs l'espace restreint : vous devez vous <a href="connexion">connecter</a> d'abord. </p>

www.siteduzero.com

Partie 4 : Une application interactive !


</body> </html>

264/606

Rien de particulier signaler ici, si ce n'est l'utilisation d'une expression EL dans la page restreinte, afin d'accder l'adresse mail de l'utilisateur enregistr en session, travers l'objet implicite sessionScope.

La servlet de contrle
Ce qu'il nous faut raliser maintenant, c'est ce fameux contrle sur le contenu de la session avant d'autoriser l'accs la page accesRestreint.jsp. V oyez plutt : Code : Java - com.sdzee.servlets.Restriction package com.sdzee.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class Restriction extends HttpServlet { public static final String ACCES_PUBLIC = "/accesPublic.jsp"; public static final String ACCES_RESTREINT = "/WEBINF/accesRestreint.jsp"; public static final String ATT_SESSION_USER = "sessionUtilisateur"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /** * Si l'objet utilisateur n'existe pas dans la session en cours, alors * l'utilisateur n'est pas connect. */ if ( session.getAttribute( ATT_SESSION_USER ) == null ) { /* Redirection vers la page publique */ response.sendRedirect( request.getContextPath() + ACCES_PUBLIC ); } else { /* Affichage de la page restreinte */ this.getServletContext().getRequestDispatcher( ACCES_RESTREINT ).forward( request, response ); } } }

Comme vous le voyez, le procd est trs simple : il suffit de rcuprer la session, de tester si l'attribut sessionUtilisateur y existe dj, et de rediriger vers la page restreinte ou publique selon le rsultat du test. Remarquez au passage l'emploi d'une redirection dans le cas de la page publique, et d'un forwarding dans le cas de la page prive. V ous devez maintenant tre familiers avec le principe, je vous l'ai expliqu dans le chapitre prcdent. Ici, si j'avais mis en place un forwarding en lieu et place de la redirection HTTP pour la page publique, alors l'URL dans le navigateur de l'utilisateur n'aurait pas t mise jour en cas d'chec de l'accs la page restreinte. Par ailleurs, vous devez galement prter attention la manire dont j'ai construit l'URL utilise pour la redirection, la ligne 26.

www.siteduzero.com

Partie 4 : Une application interactive !

265/606

V ous tes dj au courant que, contrairement au forwarding qui est limit aux pages internes, la redirection HTTP permet d'envoyer la requte n'importe quelle page, y compris des pages provenant d'autres sites. Ce que vous ne savez pas encore, moins d'avoir lu attentivement les documentations des mthodes getRequestDispatcher() et sendRedirect(), c'est que l'URL prise en argument par la mthode de forwarding est relative au contexte de l'application, alors que l'URL prise en argument par la mthode de redirection est relative la racine de l'application ! Concrtement, qu'est-ce que a implique ?

Cette diffrence est trs importante : l'URL passe la mthode getRequestDispatcher() doit tre interne l'application. En loccurrence, dans notre projet cela signifie qu'il est impossible de prciser une URL qui cible une page en dehors du projet pro. Ainsi, un appel getRequestDispatcher( "/accesPublic.jsp" ) ciblera automatiquement la page /pro/accesPublic.jsp, vous n'avez pas prciser vous-mmes le contexte /pro ; l'URL passe la mthode sendRedirect() peut tre externe l'application. Cela veut dire que vous devez manuellement spcifier l'application dans laquelle se trouve votre page, et non pas, faire comme avec la mthode de forwarding , dans laquelle par dfaut toute URL est considre comme tant interne l'application. Cela signifie donc que nous devons prciser le contexte de l'application dans l'URL passe sendRedirect(). En l'occurrence, nous devons lui dire que nous souhaitons joindre une page contenue dans le projet pro : plutt que d'crire en dur /pro/accesPublic.jsp, et risquer de devoir manuellement modifier cette URL si nous changeons le nom du contexte du projet plus tard, nous utilisons ici un appel request.getContextPath(), qui retourne automatiquement le contexte de l'application courante, c'est--dire /pro dans notre cas. Bref, vous l'aurez compris, vous devez tre attentifs aux mthodes que vous employez et la manire dont elles vont grer les URL que vous leur transmettez. Entre les URL absolues, les URL relatives la racine de l'application, les URL relatives au contexte de l'application et les URL relatives au rpertoire courant, il est parfois difficile de ne pas s'emmler les crayons !

Pour terminer, voici sa configuration dans le fichier web.xml de notre application : Code : XML - /WEB-INF/web.xml ... <servlet> <servlet-name>Restriction</servlet-name> <servlet-class>com.sdzee.servlets.Restriction</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>Restriction</servlet-name> <url-pattern>/restriction</url-pattern> </servlet-mapping> ...

N'oubliez pas de redmarrer Tomcat pour que ces modifications soient prises en compte.

Test du systme
Pour vrifier le bon fonctionnement de cet exemple d'accs restreint, suivez le scnario suivant : 1. 2. 3. 4. redmarrez Tomcat, afin de faire disparatre toute session qui serait encore active ; rendez-vous sur la page http://localhost:8080/pro/restriction, et constatez au passage la redirection (changement d'URL) ; cliquez alors sur le lien vers la page de connexion, entrez des informations valides et connectez-vous ; rendez-vous nouveau sur la page http://localhost:8080/pro/restriction, et constatez au passage l'absence de redirection (l'URL ne change pas) ;

www.siteduzero.com

Partie 4 : Une application interactive !


5. allez maintenant sur la page http://localhost:8080/pro/deconnexion ; 6. retournez une dernire fois sur la page http://localhost:8080/pro/restriction.

266/606

Sans grande surprise, le systme fonctionne bien : nous devons tre connects pour accder la page dont l'accs est restreint, sinon nous sommes redirigs vers la page publique.

Le problme
Oui, parce qu'il y a un lger problme ! Dans cet exemple, nous nous sommes occups de deux pages : une page prive, une page publique. C'tait rapide, simple et efficace. Maintenant si je vous demande d'tendre la restriction 100 pages prives, comment comptez-vous vous y prendre ? En l'tat actuel de vos connaissances, vous n'avez pas d'autres moyens que de mettre en place un test sur le contenu de la session dans chacune des 100 servlets contrlant l'accs aux 100 pages prives. V ous vous doutez bien que ce n'est absolument pas viable, et qu'il nous faut apprendre une autre mthode. La rponse nos soucis s'appelle le filtre, et nous allons le dcouvrir dans le paragraphe suivant.

Le principe du filtre Gnralits


Qu'est-ce qu'un filtre ?

Un filtre est un objet Java qui peut modifier les en-ttes et le contenu d'une requte ou d'une rponse. Il se positionne avant la servlet, et intervient donc en amont dans le cycle de traitement d'une requte par le serveur. Il peut tre associ une ou plusieurs servlets. V oici la figure suivante un schma reprsentant le cas o plusieurs filtres seraient associs notre servlet de connexion.

V ous pouvez d'ores et dj remarquer sur cette illustration que les filtres peuvent intervenir la fois sur la requte entrante et sur la rponse mise, et qu'ils s'appliquent dans un ordre prcis, en cascade. Quelle est la diffrence entre un filtre et une servlet ?

Alors qu'un composant web comme la servlet est utilis pour gnrer une rponse HTTP envoyer au client, le filtre ne cre habituellement pas de rponse ; il se contente gnralement d'appliquer d'ventuelles modifications la paire requte / rponse existante. V oici une liste des actions les plus communes ralisables par un filtre : interroger une requte et agir en consquence ; empcher la paire requte / rponse d'tre transmise plus loin, autrement dit bloquer son cheminement dans l'application ; modifier les en-ttes et le contenu de la requte courante ; modifier les en-ttes et le contenu de la rponse courante.

Quel est l'intrt d'un filtre ?

Le filtre offre trois avantages majeurs, qui sont interdpendants :

www.siteduzero.com

Partie 4 : Une application interactive !

267/606

il permet de modifier de manire transparente un change HTTP. En effet, il n'implique pas ncessairement la cration d'une rponse, et peut se contenter de modifier la paire requte / rponse existante ; tout comme la servlet, il est dfini par un mapping , et peut ainsi tre appliqu plusieurs requtes ; plusieurs filtres peuvent tre appliqus en cascade la mme requte. C'est la combinaison de ces trois proprits qui fait du filtre un composant parfaitement adapt tous les traitements de masse, ncessitant d'tre appliqus systmatiquement tout ou partie des pages d'une application. titre d'exemple, on peut citer les usages suivants : l'authentification des visiteurs, la gnration de logs, la conversion d'images, la compression de donnes ou encore le chiffrement de donnes.

Fonctionnement
Regardons maintenant comment est construit un filtre. l'instar de sa cousine la servlet, qui doit obligatoirement implmenter l'interface Servlet, le filtre doit implmenter l'interface Filter. Mais cette fois, contrairement au cas de la servlet qui peut par exemple hriter de HttpServlet, il n'existe ici pas de classe fille. Lorsque nous tudions la documentation de l'interface, nous remarquons qu'elle est plutt succincte, elle ne contient que trois dfinitions de mthodes : init(), doFilter() et destroy(). V ous le savez, lorsqu'une classe Java implmente une interface, elle doit redfinir chaque mthode prsente dans cette interface. Ainsi, voici le code de la structure vide d'un filtre : Code : Java - Exemple d'un filtre import java.io.IOException; import import import import import import javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse;

public class ExempleFilter implements Filter { public void init( FilterConfig config ) throws ServletException { // ... } public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { // ... } public void destroy() { // ... }

Les mthodes init() et destroy() concernent le cycle de vie du filtre dans l'application. Nous allons y revenir en apart dans le paragraphe suivant. La mthode qui va contenir les traitements effectus par le filtre est donc doFilter(). V ous pouvez d'ailleurs le deviner en regardant les arguments qui lui sont transmis : elle reoit en effet la requte et la rponse, ainsi qu'un troisime lment, la chane des filtres . quoi sert cette chane ?

Elle vous est encore inconnue, mais elle est en ralit un objet relativement simple : je vous laisse jeter un il sa courte documentation. Je vous ai annonc un peu plus tt que plusieurs filtres pouvaient tre appliqus la mme requte. Eh bien c'est travers cette chane qu'un ordre va pouvoir tre tabli : chaque filtre qui doit tre appliqu la requte va tre inclus la chane, qui ressemble en fin de compte une file d'invocations.

www.siteduzero.com

Partie 4 : Une application interactive !

268/606

Cette chane est entirement gre par le conteneur, vous n'avez pas vous en soucier. La seule chose que vous allez contrler, c'est le passage d'un filtre l'autre dans cette chane via l'appel de sa seule et unique mthode, elle aussi nomme doFilter(). Comment l'ordre des filtres dans la chane est-il tabli ?

Tout comme une servlet, un filtre doit tre dclar dans le fichier web.xml de l'application pour tre reconnu : Code : XML - Exemple de dclaration de filtres <?xml version="1.0" encoding="UTF-8"?> <web-app> ... <filter> <filter-name>Exemple</filter-name> <filter-class>package.ExempleFilter</filter-class> </filter> <filter> <filter-name>SecondExemple</filter-name> <filter-class>package.SecondExempleFilter</filter-class> </filter> <filter-mapping> <filter-name>Exemple</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>SecondExemple</filter-name> <url-pattern>/page</url-pattern> </filter-mapping> ... </web-app>

V ous reconnaissez ici la structure des blocs utiliss pour dclarer une servlet, la seule diffrence rside dans le nommage des champs : <servlet> devient <filter>, <servlet-name> devient <filter-name>, etc. Eh bien l encore, de la mme manire que pour les servlets, l'ordre des dclarations des mappings des filtres dans le fichier est important : c'est cet ordre qui va tre suivi lors de l'invocation de plusieurs filtres appliqus une mme requte. En d'autres termes, c'est dans cet ordre que la chane des filtres va tre automatiquement initialise par le conteneur. Ainsi, si vous souhaitez qu'un filtre soit appliqu avant un autre, placez son mapping avant le mapping du second dans le fichier web.xml de votre application.

Cycle de vie
Avant de passer l'application pratique et la mise en place d'un filtre, penchons-nous un instant sur la manire dont le conteneur le gre. Une fois n'est pas coutume, il y a l encore de fortes similitudes avec une servlet. Lorsque l'application web dmarre, le conteneur de servlets va crer une instance du filtre et la garder en mmoire durant toute l'existence de l'application. La mme instance va tre rutilise pour chaque requte entrante dont l'URL correspond au contenu du champ <urlpattern> du mapping du filtre. Lors de l'instanciation, la mthode init() est appele par le conteneur : si vous souhaitez passer des paramtres d'initialisation au filtre, vous pouvez alors les rcuprer depuis l'objet FilterConfig pass en argument la mthode. Pour chacune de ces requtes, la mthode doFilter() va tre appele. Ensuite c'est videmment au dveloppeur, vous donc, de dcider quoi faire dans cette mthode : une fois vos traitements appliqus, soit vous appelez la mthode doFilter() de l'objet FilterChain pour passer au filtre suivant dans la liste, soit vous effectuez une redirection ou un forwarding pour changer la destination d'origine de la requte. Enfin, je me rpte mais il est possible de faire en sorte que plusieurs filtres s'appliquent la mme URL. Ils seront alors appels dans le mme ordre que celui de leurs dclarations de mapping dans le fichier web.xml de l'application.

www.siteduzero.com

Partie 4 : Une application interactive !

269/606

Restreindre l'accs un ensemble de pages Restreindre un rpertoire


Aprs cette longue introduction plutt abstraite, lanons-nous et essayons d'utiliser un filtre pour rpondre notre problme : mettre en place une restriction d'accs sur un groupe de pages. C'est probablement l'utilisation la plus classique du filtre dans une application web ! Dans notre cas, nous allons nous en servir pour vrifier la prsence d'un utilisateur dans la session : s'il est prsent, notre filtre laissera la requte poursuivre son cheminement jusqu' la page souhaite ; s'il n'existe pas, notre filtre redirigera l'utilisateur vers la page publique.

Pour cela, nous allons commencer par crer un rpertoire nomm restreint que nous allons placer la racine de notre projet, dans lequel nous allons dplacer le fichier accesRestreint.jsp et y placer les deux fichiers suivants : Code : JSP - /restreint/accesRestreint2.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Accs restreint 2</title> </head> <body> <p>Vous tes connect(e) avec l'adresse ${sessionScope.sessionUtilisateur.email}, vous avez bien accs l'espace restreint numro 2.</p> </body> </html>

Code : JSP - /restreint/accesRestreint3.jsp <%@ page pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Accs restreint 3</title> </head> <body> <p>Vous tes connect(e) avec l'adresse ${sessionScope.sessionUtilisateur.email}, vous avez bien accs l'espace restreint numro 3.</p> </body> </html>

V oici la figure suivante un aperu de larborescence que vous devez alors obtenir.

www.siteduzero.com

Partie 4 : Une application interactive !

270/606

Arborescence sous Eclipse.

C'est de ce rpertoire restreint que nous allons limiter l'accs aux utilisateurs connects. Souvenez-vous bien du point suivant : pour le moment, nos pages JSP n'tant pas situes sous le rpertoire /WEB-INF, elles sont accessibles au public directement depuis leurs URL respectives. Par exemple, vous pouvez vous rendre sur http://localhost:8080/pro/restreint/accesRestreint.jsp mme sans tre connects, le seul problme que vous rencontrerez est l'absence de l'adresse email dans le message affich. Supprimez ensuite la servlet Restriction que nous avions dveloppe en dbut de chapitre, ainsi que sa dclaration dans le fichier web.xml : elle nous est dornavant inutile. Nous pouvons maintenant crer notre filtre. Je vous propose de le placer dans un nouveau package com.sdzee.filters, et de le nommer RestrictionFilter. V oyez la figure suivante comment procder aprs un Ctrl + N sous Eclipse.

www.siteduzero.com

Partie 4 : Une application interactive !

271/606

Cration d'un filtre.

Remplacez alors le code gnr automatiquement par Eclipse par le code suivant : Code : Java - com.sdzee.filters.RestrictionFilter package com.sdzee.filters; import java.io.IOException; import import import import import import javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse;

public class RestrictionFilter implements Filter { public void init( FilterConfig config ) throws ServletException { } public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain ) throws IOException, ServletException { }

www.siteduzero.com

Partie 4 : Une application interactive !


public void destroy() { }

272/606

Rien de fondamental n'a chang par rapport la version gnre par Eclipse, j'ai simplement retir les commentaires et renomm les arguments des mthodes pour que le code de notre filtre soit plus lisible par la suite. Comme vous le savez, c'est dans la mthode doFilter() que nous allons raliser notre vrification. Puisque nous avons dj dvelopp cette fonctionnalit dans une servlet en dbut de chapitre, il nous suffit de reprendre son code et de l'adapter un peu : Code : Java - com.sdzee.filters.RestrictionFilter package com.sdzee.filters; import java.io.IOException; import import import import import import import import import javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class RestrictionFilter implements Filter { public static final String ACCES_PUBLIC = "/accesPublic.jsp"; public static final String ATT_SESSION_USER = "sessionUtilisateur"; { public void init( FilterConfig config ) throws ServletException }

public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException, ServletException { /* Cast des objets request et response */ HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /** * Si l'objet utilisateur n'existe pas dans la session en cours, alors * l'utilisateur n'est pas connect. */ if ( session.getAttribute( ATT_SESSION_USER ) == null ) { /* Redirection vers la page publique */ response.sendRedirect( request.getContextPath() + ACCES_PUBLIC ); } else { /* Affichage de la page restreinte */ chain.doFilter( request, response ); } } public void destroy() { }

Quelques explications s'imposent.

www.siteduzero.com

Partie 4 : Une application interactive !

273/606

Aux lignes 25 et 26, vous constatez que nous convertissons les objets transmis en arguments notre mthode doFilter(). La raison en est simple : comme je vous l'ai dj dit, il n'existe pas de classe fille implmentant l'interface Filter, alors que ct servlet nous avons bien HttpServlet qui implmente Servlet. Ce qui signifie que notre filtre n'est pas spcialis, il implmente uniquement Filter et peut traiter n'importe quel type de requte et pas seulement les requtes HTTP. C'est donc pour cela que nous devons manuellement spcialiser nos objets, en effectuant un cast vers les objets ddis aux requtes et rponses HTTP : c'est seulement en procdant cette conversion que nous aurons accs ensuite la session, qui est propre l'objet HttpServletRequest, et n'existe pas dans l'objet ServletRequest. la ligne 40, nous avons remplac le forwarding auparavant en place dans notre servlet par un appel la mthode doFilter() de l'objet FilterChain. Celle-ci a en effet une particularit intressante : si un autre filtre existe aprs le filtre courant dans la chane, alors c'est vers ce filtre que la requte va tre transmise. Par contre, si aucun autre filtre n'est prsent ou si le filtre courant est le dernier de la chane, alors c'est vers la ressource initialement demande que la requte va tre achemine. En l'occurrence, nous n'avons qu'un seul filtre en place, notre requte sera donc logiquement transmise la page demande. Pour mettre en scne notre filtre, il nous faut enfin le dclarer dans le fichier web.xml de notre application : Code : XML - /WEB-INF/web.xml ... <filter> <filter-name>RestrictionFilter</filter-name> <filter-class>com.sdzee.filters.RestrictionFilter</filter-class> </filter> <filter-mapping> <filter-name>RestrictionFilter</filter-name> <url-pattern>/restreint/*</url-pattern> </filter-mapping> ...

la ligne 9, vous pouvez remarquer l'url-pattern prcis : le caractre * signifie que notre filtre va tre appliqu toutes les pages prsentes sous le rpertoire /restreint. Redmarrez ensuite Tomcat pour que les modifications effectues soient prises en compte, puis suivez ce scnario de tests : 1. essayez d'accder la page http://localhost:8080/pro/restreint/accesRestreint.jsp, et constatez la redirection vers la page publique ; 2. rendez-vous sur la page de connexion et connectez-vous avec des informations valides ; 3. essayez nouveau d'accder la page http://localhost:8080/pro/restreint/accesRestreint.jsp, et constatez le succs de l'opration ; 4. essayez alors d'accder aux pages accesRestreint2.jsp et accesRestreint3.jsp, et constatez l encore le succs de l'opration ; 5. rendez-vous sur la page de dconnexion ; 6. puis tentez alors d'accder la page http://localhost:8080/pro/restreint/accesRestreint.jsp et constatez cette fois l'chec de l'opration.

Notre problme est bel et bien rgl ! Nous sommes maintenant capables de bloquer l'accs un ensemble de pages avec une simple vrification dans un unique filtre : nous n'avons pas besoin de dupliquer le contrle effectu dans des servlets appliques chacune des pages !

Restreindre l'application entire


Avant de nous quitter, regardons brivement comment forcer l'utilisateur se connecter pour accder notre application. Ce principe est par exemple souvent utilis sur les intranets d'entreprise, o la connexion est gnralement obligatoire ds l'entre sur le site.

www.siteduzero.com

Partie 4 : Une application interactive !

274/606

La premire chose faire, c'est de modifier la porte d'application du filtre. Puisque nous souhaitons couvrir l'intgralit des requtes entrantes, il suffit d'utiliser le caractre * appliqu la racine. La dclaration de notre filtre devient donc : Code : XML - /WEB-INF/web.xml <filter>

class> </filter> <filter-mapping> <filter-name>RestrictionFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

<filter-name>RestrictionFilter</filter-name> <filter-class>com.sdzee.filters.RestrictionFilter</filter-

Redmarrez Tomcat pour que la modification soit prise en compte. Maintenant, vous devez rflchir ce que nous venons de mettre en place : nous avons ordonn notre filtre de bloquer toutes les requtes entrantes si l'utilisateur n'est pas connect. Le problme, c'est que si nous ne changeons pas le code de notre filtre, alors l'utilisateur ne pourra jamais accder notre site ! Pourquoi ? Notre filtre le redirigera vers la page accesPublic.jsp comme il le faisait dans le cas de la restriction d'accs au rpertoire restreint, non ? Eh bien non, plus maintenant ! La mthode de redirection que nous avons mise en place va bien tre appele, mais comme vous le savez elle va dclencher un change HTTP, c'est--dire un aller-retour avec le navigateur du client. Le client va donc renvoyer automatiquement une requte, qui va son tour tre intercepte par notre filtre. Le client n'tant toujours pas connect, le mme phnomne va se reproduire, etc. Si vous y tenez, vous pouvez essayer : vous verrez alors votre navigateur vous avertir que la page que vous essayez de contacter pose problme. V oici aux figures suivantes les messages affichs respectivement par Chrome et Firefox.

chec de la restriction

www.siteduzero.com

Partie 4 : Une application interactive !

275/606

chec de la restriction La solution est simple : 1. il faut envoyer l'utilisateur vers la page de connexion, et non plus vers la page accesPublic.jsp ; 2. il faut effectuer non plus une redirection HTTP mais un forwarding , afin qu'aucun nouvel change HTTP n'ait lieu et que la demande aboutisse. V oici ce que devient le code de notre filtre, les changements intervenant aux lignes 16 et 37 : Code : Java - com.sdzee.filters.RestrictionFilter package com.sdzee.filters; import java.io.IOException; import import import import import import import import import javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class RestrictionFilter implements Filter { public static final String ACCES_CONNEXION = "/connexion"; public static final String ATT_SESSION_USER = "sessionUtilisateur"; { public void init( FilterConfig config ) throws ServletException }

public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException, ServletException { /* Cast des objets request et response */ HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /** * Si l'objet utilisateur n'existe pas dans la session en cours, alors * l'utilisateur n'est pas connect.

www.siteduzero.com

Partie 4 : Une application interactive !


*/ if ( session.getAttribute( ATT_SESSION_USER ) == null ) { /* Redirection vers la page publique */ request.getRequestDispatcher( ACCES_CONNEXION ).forward( request, response ); } else { /* Affichage de la page restreinte */ chain.doFilter( request, response ); } } public void destroy() { }

276/606

C'est tout pour le moment. Testez alors d'accder la page http://localhost:8080/pro/restreint/accesRestreint.jsp, vous obtiendrez le formulaire affich la figure suivante.

Ratage du CSS V ous pouvez alors constater que notre solution fonctionne : l'utilisateur est maintenant bien redirig vers la page de connexion. Oui, mais... O est pass le design de notre page ?!

Eh bien la rponse est simple : il a t bloqu ! En ralit lorsque vous accdez une page web sur laquelle est attache une feuille de style CSS, votre navigateur va, dans les coulisses, envoyer une deuxime requte au serveur pour rcuprer silencieusement cette feuille et ensuite appliquer les styles au contenu HTML. Et vous pouvez le deviner, cette seconde requte a bien videmment t bloque par notre superfiltre ! Comment rgler ce problme ?

Il y a plusieurs solutions envisageables. V oici les deux plus courantes : ne plus appliquer le filtre la racine de l'application, mais seulement sur des rpertoires ou pages en particulier, en prenant soin d'viter de restreindre l'accs notre page CSS ; continuer appliquer le filtre sur toute l'application, mais dplacer notre feuille de style dans un rpertoire, et ajouter un passe-droit au sein de la mthode doFilter() du filtre. Je vais vous expliquer cette seconde mthode. Une bonne pratique d'organisation consiste en effet placer sous un rpertoire commun toutes les ressources destines tre incluses, afin de permettre un traitement simplifi. Par "ressources incluses", on entend gnralement les feuilles de style CSS, les feuilles Javascript ou encore les images, bref tout ce qui est susceptible d'tre inclus dans une page HTML ou une page JSP. Pour commencer, crez donc un rpertoire nomm inc sous la racine de votre application et placez-y le fichier CSS, comme

www.siteduzero.com

Partie 4 : Une application interactive !


indiqu la figure suivante.

277/606

Puisque nous venons de dplacer le fichier, nous devons galement modifier son appel dans la page de connexion : Code : JSP - /WEB-INF/connexion.jsp <!-- Dans le fichier connexion.jsp, remplacez l'appel suivant : --> <link type="text/css" rel="stylesheet" href="form.css" /> <!-- Par celui-ci : --> <link type="text/css" rel="stylesheet" href="inc/form.css" />

Pour terminer, nous devons raliser dans la mthode doFilter() de notre filtre ce fameux passe-droit sur le dossier inc : Code : Java - com.sdzee.filters.RestrictionFilter package com.sdzee.filters; import java.io.IOException; import import import import import import import import import javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

public class RestrictionFilter implements Filter { public static final String ACCES_CONNEXION = "/connexion"; public static final String ATT_SESSION_USER = "sessionUtilisateur"; { public void init( FilterConfig config ) throws ServletException } public void doFilter( ServletRequest req, ServletResponse res,

www.siteduzero.com

Partie 4 : Une application interactive !


FilterChain chain ) throws IOException, ServletException { /* Cast des objets request et response */ HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; /* Non-filtrage des ressources statiques */ String chemin = request.getRequestURI().substring( request.getContextPath().length() ); if ( chemin.startsWith( "/inc" ) ) { chain.doFilter( request, response ); return; } /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /** * Si l'objet utilisateur n'existe pas dans la session en cours, alors * l'utilisateur n'est pas connect. */ if ( session.getAttribute( ATT_SESSION_USER ) == null ) { /* Redirection vers la page publique */ request.getRequestDispatcher( ACCES_CONNEXION ).forward( request, response ); } else { /* Affichage de la page restreinte */ chain.doFilter( request, response ); } } public void destroy() { }

278/606

Explications : la ligne 29, nous rcuprons l'URL d'appel de la requte HTTP via la mthode getRequestURI(), puis nous plaons dans la chane chemin sa partie finale, c'est--dire la partie situe aprs le contexte de l'application. Typiquement, dans notre cas si nous nous rendons sur http://localhost:8080/pro/restreint/accesRestreint.jsp, la mthode getRequestURI() va renvoyer /pro/restreint/accesRestreint.jsp et chemin va contenir uniquement /restreint/accesRestreint.jsp ; la ligne 30, nous testons si cette chane chemin commence par /inc : si c'est le cas, cela signifie que la page demande est une des ressources statiques que nous avons places sous le rpertoire inc, et qu'il ne faut donc pas lui appliquer le filtre ! la ligne 31, nous laissons la requte poursuivre son cheminement en appelant la mthode doFilter() de la chane.

Faites les modifications, enregistrez et tentez d'accder la page http://localhost:8080/pro/connexion. Observez la figure suivante.

www.siteduzero.com

Partie 4 : Une application interactive !


Le rsultat est parfait, a fonctionne ! Oui, mais... Quoi encore ? Il n'y a plus de problme, toute l'application fonctionne maintenant !

279/606

Toute ? Non ! Un irrductible souci rsiste encore et toujours... Par exemple, rendez-vous maintenant sur la page http://localhost:8080/pro/restreint/accesRestreint.jsp. Pourquoi la feuille de style n'est-elle pas applique notre formulaire de connexion dans ce cas ?

Eh bien cette fois, c'est cause du forwarding que nous avons mis en place dans notre filtre ! Eh oui, souvenez-vous : le forwarding ne modifie pas l'URL ct client, comme vous pouvez d'ailleurs le voir sur cette dernire illustration. Cela veut dire que le navigateur du client reoit bien le formulaire de connexion, mais ne sait pas que c'est la page /connexion.jsp qui le lui a renvoy, il croit qu'il s'agit tout bonnement du retour de la page demande, c'est--dire /restreint/accesRestreint.jsp. De ce fait, lorsqu'il va silencieusement envoyer une requte au serveur pour rcuprer la feuille CSS associe la page de connexion, le navigateur va navement se baser sur l'URL qu'il a en mmoire pour interprter l'appel suivant : Code : JSP - Extrait de connexion.jsp <link type="text/css" rel="stylesheet" href="inc/form.css" />

En consquence, il va considrer que l'URL relative "inc/form.css" se rapporte au rpertoire qu'il pense tre le rpertoire courant, savoir /restreint (puisque pour lui, le formulaire a t affich par /restreint/accesRestreint.jsp). Ainsi, le navigateur va demander au serveur de lui renvoyer la page /restreint/inc/forms.css , alors que cette page n'existe pas ! V oil pourquoi le design de notre formulaire semble avoir disparu. Pour rgler ce problme, nous n'allons ni toucher au filtre ni au forwarding , mais nous allons tirer parti de la JSTL pour modifier la page connexion.jsp : Code : JSP - /WEB-INF/connexion.jsp <!-- Dans le fichier connexion.jsp, remplacez l'appel suivant : --> <link type="text/css" rel="stylesheet" href="inc/form.css" /> <!-- Par celui-ci : --> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/form.css"/>" />

V ous vous souvenez de ce que je vous avais expliqu propos de la balise <c:url> ? Je vous avais dit qu'elle ajoutait automatiquement le contexte de l'application aux URL absolues qu'elle contenait. C'est exactement ce que nous souhaitons : dans ce cas, le rendu de la balise sera /pro/inc/form.css . Le navigateur reconnatra ici une URL absolue et non plus une URL relative comme c'tait le cas auparavant, et il ralisera correctement l'appel au fichier CSS ! Dans ce cas, pourquoi ne pas avoir directement crit l'URL absolue "/pro/inc/form.css" dans l'appel ? Pourquoi s'embter avec <c:url> ? Pour la mme raison que nous avions utilis request.getContextPath() dans la servlet que nous avions dveloppe en premire partie de ce chapitre. Si demain nous dcidons de changer le nom du contexte, notre page fonctionnera toujours avec la balise <c:url>, alors qu'il faudra diter et modifier l'URL absolue entre la main sinon. J'espre que cette fois, vous avez bien compris ! Une fois la modification effectue, voici le rsultat (voir la figure suivante).

www.siteduzero.com

Partie 4 : Une application interactive !

280/606

Finalement, nous y sommes : tout fonctionne comme prvu !

Dsactiver le filtre
Une fois vos dveloppements et tests termins, pour plus de commodit dans les exemples suivre, vous pouvez dsactiver ce filtre en commentant simplement sa dclaration dans le fichier web.xml de votre application : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> ... <!-<filter> <filter-name>RestrictionFilter</filter-name> <filter-class>com.sdzee.filters.RestrictionFilter</filter-class> </filter> <filter-mapping> <filter-name>RestrictionFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> --> ... </web-app>

Il faudra redmarrer Tomcat pour que la modification soit prise en compte.

Modifier le mode de dclenchement d'un filtre


Je vous ai implicitement fait comprendre travers ces quelques exemples qu'un filtre tait dclench lors de la rception d'une requte HTTP uniquement. Eh bien sachez qu'il s'agit l d'un comportement par dfaut ! En ralit, un filtre est tout fait capable de s'appliquer un forwarding , mais il faut pour cela modifier sa dclaration dans le fichier web.xml : Code : XML - /WEB-INF/web.xml <filter> <filter-name>RestrictionFilter</filter-name> <filter-class>com.sdzee.filters.RestrictionFilter</filter-class> </filter> <filter-mapping> <filter-name>RestrictionFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>

www.siteduzero.com

Partie 4 : Une application interactive !

281/606

Il suffit, comme vous pouvez l'observer, de rajouter un champ <dispatcher> la fin de la section <filter-mapping>. De mme, si dans votre projet vous mettez en place des inclusions et souhaitez leur appliquer un filtre, alors il faudra ajouter cette ligne la dclaration du filtre : Code : XML <dispatcher>INCLUDE</dispatcher>

Nous n'allons pas nous amuser vrifier le bon fonctionnement de ces changements. Retenez simplement qu'il est bel et bien possible de filtrer les forwardings et inclusions en plus des requtes directes entrantes, en modifiant au cas par cas les dclarations des filtres appliquer. Enfin, n'oubliez pas que ces ajouts au fichier web.xml ne sont pris en compte qu'aprs un redmarrage du serveur.

Retour sur l'encodage UTF-8


Avant de passer la suite, je souhaite vous faire dcouvrir un autre exemple d'utilisation d'un filtre. V ous ne l'avez peut-tre pas encore remarqu, mais notre application ne sait toujours pas correctement grer les caractres accentus, ni les caractres spciaux et alphabets non latins... Comment est-ce possible ? Nous avons dj paramtr Eclipse et notre projet pour que tous nos fichiers soient encods en UTF-8, il ne devrait plus y avoir de problme ! Je vous ai dj expliqu, lorsque nous avons associ notre premire servlet une page JSP, que les problmatiques d'encodage intervenaient deux niveaux : ct navigateur et ct serveur. Eh bien en ralit, ce n'est pas si simple. Avec ce que nous avons mis en place, le navigateur est bien capable de dterminer l'encodage des donnes envoyes par le serveur, mais le serveur quant lui est incapable de dterminer l'encodage des donnes envoyes par le client, lors d'une requte GET ou POST. Essayez dans votre formulaire d'inscription d'entrer un nom qui contient un accent, par exemple (voir la figure suivante).

Si vous cliquez alors sur le bouton d'inscription, votre navigateur va envoyer une requte POST au serveur, qui va alors retourner le rsultat affich la figure suivante.

V ous devez ici reconnatre le problme que nous avions rencontrs nos dbuts ! L encore, il s'agit d'une erreur d'interprtation : le serveur considre par dfaut que les donnes qui lui sont transmises suivent l'encodage latin ISO-8859-1, alors qu'en ralit

www.siteduzero.com

Partie 4 : Une application interactive !


ce sont des donnes en UTF-8 qui sont envoyes, d'o les symboles bizarrodes nouveau observs... Trs bien, mais quel est le rapport avec nos filtres ?

282/606

Pour corriger ce comportement, il est ncessaire d'effectuer un appel la mthode setCharacterEncoding() non plus depuis l'objet HttpServletResponse comme nous l'avions fait dans notre toute premire servlet, mais depuis l'objet HttpServletRequest ! En effet, nous cherchons bien ici prciser l'encodage des donnes de nos requtes ; nous l'avons dj appris, l'encodage des donnes de nos rponses est quant lui assur par la ligne place en tte de chacune de nos pages JSP. Ainsi, nous pourrions manuellement ajouter une ligne request.setCharacterEncoding("UTF-8"); dans les mthodes doPost() de chacune de nos servlets, mais nous savons que dupliquer cet appel dans toutes nos servlets n'est pas pratique du tout. En outre, la documentation de la mthode prcise qu'il faut absolument raliser l'appel avant toute lecture de donnes, afin que l'encodage soit bien pris en compte par le serveur. V oil donc deux raisons parfaites pour mettre en place... un filtre ! C'est l'endroit idal pour effectuer simplement cet appel chaque requte reue, et sur l'intgralit de l'application. Et puisque qu'une bonne nouvelle n'arrive jamais seule, il se trouve que nous n'avons mme pas besoin de crer nous mme ce filtre, Tomcat en propose dj un nativement ! Il va donc nous suffire d'ajouter une dclaration dans le fichier web.xml de notre application pour que notre projet soit enfin capable de grer correctement les requtes POST qu'il traite : Code : XML - /WEB-INF/web.xml ... <filter> <filter-name>Set Character Encoding</filter-name> <filterclass>org.apache.catalina.filters.SetCharacterEncodingFilter</filterclass> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>ignore</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ...

V ous retrouvez la dclaration que vous avez apprise un peu plus tt, via la balise <filter>. La seule nouveaut, c'est l'utilisation du filtre natif SetCharacterEncodingFilter issu du package org.apache.catalina.filters. Comme vous pouvez le constater, celui-ci ncessite deux paramtres dinitialisation dont un nomm encoding , qui permet au dveloppeur de spcifier l'encodage utiliser. Je vous laisse parcourir la documentation du filtre pour plus d'informations. De mme, vous retrouvez dans la section <filter-mapping> l'application du filtre au projet entier, grce au caractre * appliqu la racine. Une fois les modifications effectues, il vous suffit alors de redmarrer Tomcat et d'essayer nouveau de saisir un nom accentu. V ous remarquez cette fois la bonne gestion du mot accentu, qui est r-affich correctement. Bien videmment, cela vaut galement pour tout caractre issu d'un alphabet non latin : votre application est dsormais capable de traiter des donnes crites en arabe, en chinois, en russe, etc. Le forwarding s'effectue sur le serveur de manire transparente pour le client, alors que la redirection implique un aller/retour chez le client. Un filtre ressemble une servlet en de nombreux aspects : il agit sur le couple requte / rponse initi par le conteneur de servlets ;

www.siteduzero.com

Partie 4 : Une application interactive !


il se dclare dans le fichier web.xml via deux sections <filter> et <filter-mapping> ; il contient une mthode de traitement, nomme doFilter() ; il peut s'appliquer un pattern d'URL comme une page en particulier.

283/606

Plusieurs filtres sont applicables en cascade sur une mme paire requte / rponse, dans l'ordre dfini par leur dclaration dans le web.xml. La transition d'un filtre vers le maillon suivant de la chane s'effectue via un appel la mthode doFilter() de l'objet FilterChain.

www.siteduzero.com

Partie 4 : Une application interactive !

284/606

Le cookie : le navigateur vous ouvre ses portes


Nous avons dj bien entam la dcouverte du cookie malgr nous, lorsque nous avons tudi le fonctionnement des sessions, mais nous allons tout de mme prendre la peine de rappeler les concepts qui se cachent sous cette technologie. Nous allons ensuite mettre en pratique la thorie dans un exemple concret, et terminer sur une discussion autour de leur scurit.

Le principe du cookie
Le principe gnral est simple : il s'agit un petit fichier plac directement dans le navigateur du client. Il lui est envoy par le serveur travers les en-ttes de la rponse HTTP, et ne contient que du texte. Il est propre un site ou une partie d'un site en particulier, et sera renvoy par le navigateur dans toutes les requtes HTTP adresses ce site ou cette partie du site.

Ct HTTP
Pour commencer, le cookie est une notion qui est lie au protocole HTTP, et qui est dfinie par la RFC 6265 depuis avril 2011. Cette nouvelle version de la norme rend caduque la version 2965, qui elle-mme remplaait la version 2109. Si vous avez du temps perdre, vous pouvez chercher les modifications apportes au fil des volutions, c'est un excellent exercice d'analyse de RFC, ces documents massifs qui font office de rfrence absolue dans bon nombre de domaines ! a, c'tait pour la thorie. Dans la pratique, dans le chapitre portant sur les sessions nous avons dj analys des changes HTTP impliquant des transferts de cookies du serveur vers le navigateur et inversement, et avons dcouvert que : un cookie a obligatoirement un nom et une valeur associe ; un cookie peut se voir attribuer certaines options , comme une date d'expiration ; le serveur demande la mise en place ou le remplacement d'un cookie par le paramtre Set-Cookie dans l'en-tte de la rponse HTTP qu'il envoie au client ; le client transmet au serveur un cookie par le paramtre Cookie dans l'en-tte de la requte HTTP qu'il envoie au serveur. C'est tout ce qui se passe dans les coulisses du protocole HTTP, il s'agit uniquement d'un paramtre dans la requte ou dans la rponse.

Ct Java EE
La plate-forme Java EE permet de manipuler un cookie travers l'objet Java Cookie. Sa documentation claire et concise nous informe notamment que : un cookie doit obligatoirement avoir un nom et une valeur ; il est possible d'attribuer des options un cookie, telles qu'une date d'expiration ou un numro de version. Toutefois, elle nous prcise ici que certains navigateurs prsentent des bugs dans leur gestion de ces options, et qu'il est prfrable d'en limiter l'usage autant que faire se peut afin de rendre notre application aussi multiplateforme que possible ; la mthode addCookie() de l'objet HttpServletResponse est utilise pour ajouter un cookie la rponse qui sera envoye client ; la mthode getCookies() de l'objet HttpServletRequest est utilise pour rcuprer la liste des cookies envoys par le client ; par dfaut, les objets ainsi crs respectent la toute premire norme dcrivant les cookies HTTP, une norme encore plus ancienne que la 2109 dont je vous ai parl dans le paragraphe prcdent, afin d'assurer la meilleure interoprabilit possible. La documentation de la mthode setVersion() nous prcise mme que la version 2109 est considre comme "rcente et exprimentale". Bref, la documentation commence srieusement dater... Peu importe, tout ce dont nous avons besoin pour le moment tait dj dcrit dans le tout premier document, pas de soucis se faire ! V oil tout ce qu'il vous est ncessaire de savoir pour attaquer. Bien videmment, n'hsitez pas parcourir plus en profondeur la Javadoc de l'objet Cookie pour en connatre davantage ! Avant de passer la pratique, comprenez bien que cookies et sessions sont deux concepts totalement distincts ! Mme s'il est vrai que l'tablissement d'une session en Java EE peut s'appuyer sur un cookie, il ne faut pas confondre les deux notions : la session est un espace mmoire allou sur le serveur dans lequel vous pouvez placer n'importe quel type d'objets, alors que le cookie est un espace mmoire allou dans le navigateur du client dans lequel vous ne pouvez placer que du texte.

Souvenez-vous de vos clients !


Pour illustrer la mise en place d'un cookie chez l'utilisateur, nous allons donner notre formulaire de connexion... une mmoire ! Plus prcisment, nous allons donner le choix l'utilisateur d'enregistrer ou non la date de sa dernire connexion, via une case

www.siteduzero.com

Partie 4 : Une application interactive !

285/606

cocher dans notre formulaire. S'il fait ce choix, alors nous allons stocker la date et l'heure de sa connexion dans un cookie et le placer dans son navigateur. Ainsi, son retour aprs dconnexion, nous serons en mesure de lui afficher depuis combien de temps il ne s'est pas connect. Ce systme ne fera donc intervenir qu'un seul cookie, charg de sauvegarder la date de connexion. Alors bien videmment, c'est une simple fonctionnalit que je vous fais mettre en place titre d'application pratique : elle est aisment faillible, par exemple si l'utilisateur supprime les cookies de son navigateur, les bloque, ou encore s'il se connecte depuis un autre poste ou un autre navigateur. Mais peu importe, le principal est que vous travailliez la manipulation de cookies, et au passage cela vous donnera une occasion : de travailler nouveau la manipulation des dates avec la bibliothque JodaTime ; de dcouvrir comment traiter une case cocher, c'est--dire un champ de formulaire HTML de type <input type="checkbox"/>. D'une pierre... trois coups !

Reprise de la servlet
Le plus gros de notre travail va se concentrer sur la servlet de connexion. C'est ici que nous allons devoir manipuler notre unique cookie, et effectuer diffrentes vrifications. En reprenant calmement notre systme, nous pouvons identifier les deux besoins suivants : 1. l'affichage du formulaire de connexion par un visiteur, il nous faut vrifier si le cookie enregistrant la date de la prcdente connexion a t envoy dans la requte HTTP par le navigateur du client. Si oui, alors cela signifie que le visiteur s'est dj connect par le pass avec ce navigateur, et que nous pouvons donc lui afficher depuis combien de temps il ne s'est pas connect. Si non, alors il ne s'est jamais connect et nous lui affichons simplement le formulaire ; 2. la connexion d'un visiteur, il nous faut vrifier s'il a coch la case dans le formulaire, et si oui il nous faut rcuprer la date courante, l'enregistrer dans un cookie et l'envoyer au navigateur du client travers la rponse HTTP.

l'affichage du formulaire
Lors de la rception d'une demande d'accs la page de connexion, la mthode doGet() de notre servlet va devoir : vrifier si un cookie a t envoy par le navigateur dans les en-ttes de la requte ; si oui, alors elle doit calculer la diffrence entre la date courante et la date prsente dans le cookie, et la transmettre la JSP pour affichage. V oici pour commencer la reprise de la mthode doGet(), accompagne des nouvelles constantes et mthodes ncessaires son bon fonctionnement. Je n'ai volontairement pas inclus le code existant de la mthode doPost(), afin de ne pas compliquer la lecture. Lorsque vous reporterez ces modifications sur votre servlet de connexion, ne faites surtout pas un bte copier-coller du code suivant ! Prenez garde modifier correctement le code existant, et ne pas supprimer la mthode doPost() de votre servlet (que j'ai ici remplace par "...") : Code : Java - com.sdzee.servlets.Connexion package com.sdzee.servlets; import java.io.IOException; import javax.servlet.*; import org.joda.time.*; import com.sdzee.beans.Utilisateur; import com.sdzee.forms.ConnexionForm ; public class Connexion extends HttpServlet { public static final String ATT_USER = "utilisateur"; public static final String ATT_FORM = "form"; public static final String ATT_INTERVALLE_CONNEXIONS =

www.siteduzero.com

Partie 4 : Une application interactive !


"intervalleConnexions"; public static final "sessionUtilisateur"; public static final "derniereConnexion"; public static final "dd/MM/yyyy HH:mm:ss"; public static final INF/connexion.jsp"; String String String String ATT_SESSION_USER =

286/606

COOKIE_DERNIERE_CONNEXION = FORMAT_DATE VUE = = "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Tentative de rcupration du cookie depuis la requte */ String derniereConnexion = getCookieValue( request, COOKIE_DERNIERE_CONNEXION ); /* Si le cookie existe, alors calcul de la dure */ if ( derniereConnexion != null ) { /* Rcupration de la date courante */ DateTime dtCourante = new DateTime(); /* Rcupration de la date prsente dans le cookie */ DateTimeFormatter formatter = DateTimeFormat.forPattern( FORMAT_DATE ); DateTime dtDerniereConnexion = formatter.parseDateTime( derniereConnexion ); /* Calcul de la dure de l'intervalle */ Period periode = new Period( dtDerniereConnexion, dtCourante ); /* Formatage de la dure de l'intervalle */ PeriodFormatter periodFormatter = new PeriodFormatterBuilder() .appendYears().appendSuffix( " an ", " ans " ) .appendMonths().appendSuffix( " mois " ) .appendDays().appendSuffix( " jour ", " jours " ) .appendHours().appendSuffix( " heure ", " heures " ) .appendMinutes().appendSuffix( " minute ", " minutes " ) .appendSeparator( "et " ) .appendSeconds().appendSuffix( " seconde", " secondes" ) .toFormatter(); String intervalleConnexions = periodFormatter.print( periode ); /* Ajout de l'intervalle en tant qu'attribut de la requte */ request.setAttribute( ATT_INTERVALLE_CONNEXIONS, intervalleConnexions ); } /* Affichage de la page de connexion */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } ... /** * Mthode utilitaire grant la rcupration de la valeur d'un cookie donn * depuis la requte HTTP. */ private static String getCookieValue( HttpServletRequest request, String nom ) { Cookie[] cookies = request.getCookies(); if ( cookies != null ) { for ( Cookie cookie : cookies ) { if ( cookie != null && nom.equals( cookie.getName() ) ) { return cookie.getValue();

www.siteduzero.com

Partie 4 : Une application interactive !


} }

287/606

} return null;

De plus amples explications : j'ai choisi de nommer le cookie stock chez le client derniereConnexion ; j'ai mis en place une mthode nomme getCookieValue(), ddie la recherche d'un cookie donn dans une requte HTTP : la ligne 56, elle rcupre tous les cookies prsents dans la requte grce la mthode request.getCookies(), que je vous ai prsente un peu plus tt ; la ligne 57, elle vrifie si des cookies existent, c'est--dire si request.getCookies() n'a pas retourn null ; la ligne 58, elle parcourt le tableau de cookies rcupr ; la ligne 59, elle vrifie si un des ventuels cookies prsents dans le tableau a le mme nom que le paramtre nom pass en argument, rcupr par un appel cookie.getName() ; la ligne 60, si un tel cookie est trouv, elle retourne sa valeur via un appel cookie.getValue(). la ligne 23, je teste si ma mthode getCookieValue() a retourn une valeur ou non ; de la ligne 24 la ligne 43, je traite les dates grce aux mthodes de la bibliothque JodaTime. Je vous recommande fortement d'aller vous-mmes parcourir son guide d'utilisation ainsi que sa FAQ. C'est en anglais, mais les codes d'exemples sont trs explicites. V oici quelques dtails en supplment des commentaires dj prsents dans le code de la servlet : j'ai pris pour convention le format "dd/MM/yyyy HH:mm:ss", et considre donc que la date sera stocke sous ce format dans le cookie derniereConnexion plac dans le navigateur le client ; les lignes 27 et 28 permettent de traduire la date prsente au format texte dans le cookie du client en un objet DateTime que nous utiliserons par la suite pour effectuer la diffrence avec la date courante ; la ligne 30, je calcule la diffrence entre la date courante et la date de la dernire visite, c'est--dire l'intervalle de temps coul ; de la ligne 32 40, je cre un format d'affichage de mon choix l'aide de l'objet PeriodFormatterBuilder ; la ligne 41 j'enregistre dans un String, via la mthode print(), l'intervalle mis en forme avec le format que j'ai frachement dfini ; enfin la ligne 43, je transmets l'intervalle mis en forme notre JSP, via un simple attribut de requte nomm intervalleConnexions . En fin de compte, si vous mettez de ct la tambouille que nous ralisons pour manipuler nos dates et calculer l'intervalle entre deux connexions, vous vous rendrez compte que le traitement li au cookie en lui-mme est assez court : il suffit simplement de vrifier le retour de la mthode request.getCookies(), chose que nous faisons ici grce notre mthode getCookieValue().

la connexion du visiteur
La seconde tape est maintenant de grer la connexion d'un visiteur. Il va falloir : vrifier si la case est coche ou non ; si oui, alors l'utilisateur souhaite qu'on se souvienne de lui et il nous faut : rcuprer la date courante ; la convertir au format texte choisi ; l'enregistrer dans un cookie nomm derniereConnexion et l'envoyer au navigateur du client travers la rponse HTTP. si non, alors l'utilisateur ne souhaite pas qu'on se souvienne de lui et il nous faut : demander la suppression du cookie nomm derniereConnexion qui, ventuellement, existe dj dans le navigateur du client. Code : Java - com.sdzee.servlets.Connexion

www.siteduzero.com

Partie 4 : Une application interactive !


public static final String public static final int * 365; // 1 an ... public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ ConnexionForm form = new ConnexionForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Utilisateur utilisateur = form.connecterUtilisateur( request ); /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /* * Si aucune erreur de validation n'a eu lieu, alors ajout du bean * Utilisateur la session, sinon suppression du bean de la session. */ if ( form.getErreurs().isEmpty() ) { session.setAttribute( ATT_SESSION_USER, utilisateur ); } else { session.setAttribute( ATT_SESSION_USER, null ); } /* Si et seulement si la case du formulaire est coche */ if ( request.getParameter( CHAMP_MEMOIRE ) != null ) { /* Rcupration de la date courante */ DateTime dt = new DateTime(); /* Formatage de la date et conversion en texte */ DateTimeFormatter formatter = DateTimeFormat.forPattern( FORMAT_DATE ); String dateDerniereConnexion = dt.toString( formatter ); /* Cration du cookie, et ajout la rponse HTTP */ setCookie( response, COOKIE_DERNIERE_CONNEXION, dateDerniereConnexion, COOKIE_MAX_AGE ); } else { /* Demande de suppression du cookie du navigateur */ setCookie( response, COOKIE_DERNIERE_CONNEXION, "", 0 ); } /* Stockage du formulaire et du bean dans l'objet request */ request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_USER, utilisateur ); this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } /* * Mthode utilitaire grant la cration d'un cookie et son ajout la * rponse HTTP. */ private static void setCookie( HttpServletResponse response, String nom, String valeur, int maxAge ) { Cookie cookie = new Cookie( nom, valeur ); cookie.setMaxAge( maxAge ); response.addCookie( cookie ); } CHAMP_MEMOIRE COOKIE_MAX_AGE = "memoire"; = 60 * 60 * 24

288/606

Quelques explications supplmentaires : la condition du bloc if la ligne 25 permet de dterminer si la case du formulaire, que j'ai choisi de nommer memoire, est coche ;

www.siteduzero.com

Partie 4 : Une application interactive !

289/606

les lignes 29 et 30 se basent sur la convention d'affichage choisie, savoir "dd/MM/yyyy HH:mm:ss", pour mettre en forme la date proprement, partir de l'objet DateTime frachement cr ; j'utilise alors une mthode setCookie(), laquelle je transmets la rponse accompagne de trois paramtres : un nom et une valeur, qui sont alors utiliss pour crer un nouvel objet Cookie la ligne 50 ; un entier maxAge, utilis pour dfinir la dure de vie du cookie grce la mthode cookie.setMaxAge() ; cette mthode se base pour finir sur un appel response.addCookie() dont je vous ai dj parl, pour mettre en place une instruction Set-Cookie dans les en-ttes de la rponse HTTP. la ligne 35, je demande au navigateur du client de supprimer l'ventuel cookie nomm derniereConnexion qu'il aurait dj enregistr par le pass. En effet, si l'utilisateur n'a pas coch la case du formulaire, cela signifie qu'il ne souhaite pas que nous lui affichions un message, et il nous faut donc nous assurer qu'aucun cookie enregistr lors d'une connexion prcdente n'existe. Pour ce faire, il suffit de placer un nouveau cookie derniereConnexion dans la rponse HTTP avec une dure de vie gale zro. Au passage, si vous vous rendez sur la documentation de la mthode setMaxAge(), vous dcouvrirez les trois types de valeurs qu'elle accepte : un entier positif, reprsentant le nombre de secondes avant expiration du cookie sauvegard. En l'occurrence, j'ai donn notre cookie une dure de vie d'un an, soit 60 x 60 x 24 x 365 = 31 536 000 secondes ; un entier ngatif, signifiant que le cookie ne sera stock que de manire temporaire et sera supprim ds que le navigateur sera ferm. Si vous avez bien suivi et compris le chapitre sur les sessions, alors vous en avez probablement dj dduit que c'est de cette manire qu'est stock le cookie JSESSIONID ; zro, qui permet de supprimer simplement le cookie du navigateur.

retenir galement, le seul test valable pour s'assurer qu'un champ de type <input type="checkbox"/> est coch, c'est de vrifier le retour de la mthode request.getParameter() : si c'est null, la case n'est pas coche ; sinon, la case est coche.

Reprise de la JSP
Pour achever notre systme, il nous reste ajouter la case cocher notre formulaire, ainsi qu'un message sur notre page de connexion prcisant depuis combien de temps l'utilisateur ne s'est pas connect. Deux contraintes sont prendre en compte : si l'utilisateur est dj connect, on ne lui affiche pas le message ; si l'utilisateur ne s'est jamais connect, ou s'il n'a pas coch la case lors de sa dernire connexion, on ne lui affiche pas le message. V oici le code, modifi aux lignes 14 16 et 29 30 : Code : JSP - /WEB-INF/connexion.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Connexion</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/form.css"/>" /> </head> <body> <form method="post" action="<c:url value="/connexion" />"> <fieldset> <legend>Connexion</legend> <p>Vous pouvez vous connecter via ce formulaire.</p> <c:if test="${empty sessionScope.sessionUtilisateur && !empty requestScope.intervalleConnexions}"> <p class="info">(Vous ne vous tes pas connect(e) depuis ce navigateur depuis ${requestScope.intervalleConnexions})</p> </c:if>

www.siteduzero.com

Partie 4 : Une application interactive !


<label for="nom">Adresse email <span class="requis">*</span></label> <input type="email" id="email" name="email" value="<c:out value="${utilisateur.email}"/>" size="20" maxlength="60" /> <span class="erreur">${form.erreurs['email']}</span> <br /> <label for="motdepasse">Mot de passe <span class="requis">*</span></label> <input type="password" id="motdepasse" name="motdepasse" value="" size="20" maxlength="20" /> <span class="erreur">${form.erreurs['motdepasse']}</span> <br /> <br /> <label for="memoire">Se souvenir de moi</label> <input type="checkbox" id="memoire" name="memoire" <br />

290/606

/>

<input type="submit" value="Connexion" class="sansLabel" /> <br /> <p class="${empty form.erreurs ? 'succes' : 'erreur'}">${form.resultat}</p> <%-- Vrification de la prsence d'un objet utilisateur en session --%> <c:if test="${!empty sessionScope.sessionUtilisateur}"> <%-- Si l'utilisateur existe en session, alors on affiche son adresse email. --%> <p class="succes">Vous tes connect(e) avec l'adresse : ${sessionScope.sessionUtilisateur.email}</p> </c:if> </fieldset> </form> </body> </html>

la ligne 14, vous remarquerez l'utilisation d'un test conditionnel par le biais de la balise <c:if> de la JSTL Core : la premire moiti du test permet de vrifier que la session ne contient pas d'objet nomm sessionUtilisateur, autrement dit de vrifier que l'utilisateur n'est actuellement pas connect. Souvenez-vous : sessionUtilisateur est l'objet que nous plaons en session lors de la connexion d'un visiteur, et qui n'existe donc que si une connexion a dj eu lieu ; la seconde moiti du test permet de vrifier que la requte contient bien un intervalle de connexions, autrement dit de vrifier si l'utilisateur s'tait dj connect ou non, et si oui s'il avait coch la case. Rappelez-vous de nos mthodes doGet() et doPost() : si le client n'a pas envoy le cookie derniereConnexion, cela signifie qu'il ne s'est jamais connect par le pass, ou bien que lors de sa dernire connexion il n'a pas coch la case, et nous ne transmettons pas d'intervalle la JSP. Le corps de la balise <c:if>, contenant notre message ainsi que l'intervalle transmis pour affichage travers l'attribut de requte intervalleConnexions , est alors uniquement affich si la fois l'utilisateur n'est pas connect l'instant prsent, et s'il a coch la case lors de sa prcdente connexion. Enfin, vous voyez que pour l'occasion j'ai ajout un style info notre feuille CSS, afin de mettre en forme le nouveau message : Code : CSS - /inc/form.css form .info { font-style: italic; color: #E8A22B;

www.siteduzero.com

Partie 4 : Une application interactive !


}

291/606

Vrifications
Le scnario de tests va tre plutt lger. Pour commencer, redmarrez Tomcat, nettoyez les donnes de votre navigateur via Ctrl + Maj + Suppr, et accdez la page de connexion. V ous devez alors visualiser le formulaire vierge. Appuyez sur F12 pour ouvrir l'outil d'analyse de votre navigateur, entrez des donnes valides dans le formulaire et connectezvous en cochant la case "Se souvenir de moi". Examinez alors la figure suivante la rponse renvoye par le serveur.

V ous pouvez constater la prsence de l'instruction Set-Cookie dans l'en-tte de la rponse HTTP, demandant la cration d'un cookie nomm derniereConnexion qui : contient bien la date de connexion, formate selon la convention choisie dans notre servlet ; expire bien dans 31 536 000 secondes, soit un an aprs cration. Ouvrez alors un nouvel onglet et rendez-vous nouveau sur la page de connexion. V ous constaterez que le message contenant l'intervalle ne vous est toujours pas affich, puisque vous tes connects et que l'expression EL dans notre JSP l'a dtect (voir

www.siteduzero.com

Partie 4 : Une application interactive !


la figure suivante).

292/606

Dconnectez-vous alors en vous rendant sur http://localhost:8080/pro/deconnexion, puis rendez-vous nouveau sur la page de connexion et observez la figure suivante.

V ous pouvez constater la prsence, dans l'en-tte Cookie de la requte envoye par le navigateur, du cookie nomm derniereConnexion, sauvegard lors de la prcdente connexion. Bien videmment cette fois, le message vous est affich au sein du formulaire de connexion, et vous prcise depuis combien de temps vous ne vous tes pas connects.

www.siteduzero.com

Partie 4 : Une application interactive !

293/606

Connectez-vous nouveau, mais cette fois sans cocher la case "Se souvenir de moi". Observez alors l'change HTTP qui a lieu (voir la figure suivante).

V ous pouvez constater deux choses : puisque vous vous tiez connects en cochant la case du formulaire auparavant, votre navigateur avait enregistr la date de connexion dans un cookie, et ce cookie existe encore. Cela explique pourquoi le navigateur envoie un cookie derniereConnexion contenant la date de votre premire connexion dans l'en-tte Cookie de la requte ; puisque cette fois vous n'avez pas coch la case du formulaire, la servlet renvoie une demande de suppression du cookie travers la rponse HTTP. Cela explique la prsence de l'instruction Set-Cookie contenant un cookie vide et dont la date d'expiration est le... 1er janvier 1970 !

www.siteduzero.com

Partie 4 : Une application interactive !


Pourquoi le cookie contient-il une date d'expiration ?

294/606

Tout simplement parce qu'il n'existe pas de proprit ni de champ optionnel dans l'instruction Set-Cookie permettant de supprimer un cookie. Ainsi, la seule solution qui s'offre nous est de prciser une date d'expiration antrieure la date courante, afin que le navigateur en dduise que le cookie est expir et qu'il doit le supprimer. Ceci est fait automatiquement lorsque nous donnons notre cookie un maxAge gal zro. Pourquoi le 1er janvier 1970 ? Il s'agit de la date considre comme le temps d'origine par votre systme. Ainsi en ralit, lorsque nous donnons notre cookie un maxAge gal zro, cela se traduit dans l'en-tte HTTP par cette date butoir : de cette manire, le serveur est certain que le cookie sera supprim par le navigateur, peu importe la date courante sur la machine cliente.

propos de la scurit
V ous devez bien comprendre que contrairement aux sessions, bien gardes sur le serveur, le systme des cookies est loin d'tre un coffre-fort. Principalement parce que l'information que vous y stockez est place chez le client, et que par consquent vous n'avez absolument aucun contrle dessus. Par exemple, rien n'indique que ce que vous y placez ne soit pas lu et dtourn par une personne malveillante qui aurait accs la machine du client son insu. L'exemple le plus flagrant est le stockage du nom d'utilisateur et du mot de passe directement dans un cookie. En plaant en clair ces informations dans le navigateur du client, vous les exposez au vol par un tiers malveillant, qui pourra alors voler le compte de votre client... Ainsi, il y a une rgle suivre lorsque vous utilisez des cookies : n'y stockez jamais d'informations sensibles en clair .

Rassurez-vous toutefois, car le systme est loin d'tre une vraie passoire. Simplement, lorsque vous dvelopperez des applications ncessitant un bon niveau de scurit, ce sont des considrations que vous devrez tt ou tard prendre en compte. Autant vous en faire prendre conscience ds maintenant ! Un cookie est un fichier texte de trs petite taille stock dans le navigateur du client, alors identifi par un nom et une valeur. L'objet Cookie permet la gestion des cookies en Java. Pour rcuprer les cookies d'un client, il suffit d'appeler la mthode request.getCookies(), qui renvoie un tableau de cookies. Pour analyser un cookie en particulier, il suffit de vrifier son nom via cookie.getName() et sa valeur via cookie.getValue(). Pour initialiser un cookie sur le serveur, il suffit d'appeler new Cookie( nom, valeur ). Pour l'envoyer au client, il suffit d'appeler la mthode response.addCookie( cookie ). Un cookie ne peut pas tre supprim manuellement depuis le serveur, mais il contient une date d'expiration afin que le navigateur puisse dterminer quand le dtruire.

www.siteduzero.com

Partie 4 : Une application interactive !

295/606

TP Fil rouge - tape 4


Les sessions constituant dj un gros morceau elles seules, dans cette tape du fil rouge vous n'allez pas manipuler de cookies. Et croyez-moi, mme sans a vous avez du pain sur la planche !

Objectifs Fonctionnalits
V ous allez devoir effectuer une modification importante, qui va impacter presque tout votre code existant : je vous demande dans cette tape d'enregistrer en session les clients et commandes crs par un utilisateur. Cela pourrait trs bien tre un jeu d'enfants, si je ne vous demandais rien d'autre derrire... Mais ne rvez pas, vous n'tes pas ici pour vous tourner les pouces !

Enregistrement en session
Pour commencer, comme je viens de vous l'annoncer, vous allez devoir enregistrer en session les clients et commandes crs par un utilisateur. Ainsi, les informations saisies par l'utilisateur ne seront plus perdues aprs validation d'un formulaire de cration !

Liste rcapitulative des clients et commandes crs


Deuximement, vous allez devoir crer deux nouvelles pages : listerClients.jsp, qui listera les clients crs par l'utilisateur ; listerCommandes.jsp, qui listera les commandes cres par l'utilisateur. V ous les placerez bien entendu sous /WEB-INF tout comme leurs consurs, V ous en profiterez pour mettre jour la page menu.jsp en y ajoutant deux liens vers les nouvelles servlets grant ces deux pages frachement cres.

Un formulaire de cration de commande intelligent


Troisimement, et cette fois il va falloir rflchir davantage, je vous demande de modifier le formulaire de cration de commandes. Maintenant que votre application est capable d'enregistrer les clients crs par l'utilisateur, vous allez lui donner un choix lorsqu'il cre une commande : si la commande qu'il veut crer concerne un nouveau client, alors affichez-lui les champs permettant la saisie des informations du nouveau client, comme vous le faisiez dj auparavant ; si la commande qu'il veut crer concerne un client dj existant, alors affichez-lui une liste droulante des clients existants, lui permettant de choisir son client et lui vitant ainsi de saisir nouveau ces informations.

Systme de suppression des clients et commandes


Quatrimement, vous allez devoir mettre en place un systme permettant la suppression d'un client ou d'une commande enregistre dans la session. V ous allez ensuite devoir complter vos pages listerClients.jsp et listerCommandes.jsp pour qu'elles affichent ct de chaque client et commande un lien vers ce systme, permettant la suppression de l'entit correspondante.

Gestion de l'encodage UTF-8 des donnes


Cinquimement, vous allez appliquer le filtre d'encodage de Tomcat votre projet, afin de rendre votre application capable de grer n'importe quelle donne UTF-8. Enfin, vous devrez vous prparer une tasse un thermos de caf ou de th bien fort, parce qu'avec tout ce travail, vous n'tes pas couchs !

Exemples de rendus
V oici aux figures suivantes quelques exemples de rendu. Liste des clients crs au cours de la session, ici avec quatre clients.

www.siteduzero.com

Partie 4 : Une application interactive !

296/606

Liste des commandes lorsqu'aucune commande n'a t cre au cours de la session :

Formulaire de cration d'une commande lorsque la case "Nouveau client : Oui" est coche :

www.siteduzero.com

Partie 4 : Une application interactive !

297/606

Formulaire de cration d'une commande lorsque la case "Nouveau client : Non" est coche :

www.siteduzero.com

Partie 4 : Une application interactive !

298/606

Liste des commandes lorsque deux commandes ont t cres au cours de la session :

Conseils
Enregistrement en session
Concernant l'aspect technique de cette problmatique, vous avez toutes les informations dans le chapitre sur ce sujet : il vous suffit de rcuprer la session en cours depuis l'objet requte, et d'y mettre en place des attributs. Concernant la mise en place dans ce cas prcis par contre, il y a une question que vous allez devoir vous poser : quel type d'attributs enregistrer en session ? Autrement dit, comment stocker les clients et commandes ? C'est une bonne question. Essayez d'y rflchir par vous-mmes avant de lire le conseil qui suit... Ici, ce qui vous intresse, c'est d'enregistrer les clients et les commandes crs. Une liste de clients et une liste de commandes

www.siteduzero.com

Partie 4 : Une application interactive !

299/606

pourraient donc a priori faire l'affaire, mais retrouver quelque chose dans une liste, ce n'est pas simple... Pourtant, vous aimeriez bien pouvoir identifier facilement un client ou une commande dans ces listes. Pour cette raison, une Map semble la solution adapte. Oui, mais comment organiser cette Map ? Les objets Client et Commande seront bien entendu les valeurs, mais qu'estce qui va bien pouvoir servir de cl ? Il y a tout un tas de solutions possibles, mais je vous conseille pour le moment de mettre en place la moins contraignante : considrez que le nom permet d'identifier de manire unique un client, et que la date permet d'identifier de manire unique une commande. Cela implique que votre application ne permettra pas de crer deux commandes au mme instant, ni de crer deux clients portant le mme nom. Ce n'est pas gnial comme comportement, mais pour le moment votre application ne gre toujours pas les donnes, donc a ne vous pose absolument aucun problme ! V ous aurez tout le loisir de rgler ce petit souci dans l'tape 6 du fil rouge ! Bref, je vous conseille donc de placer en session une Map<String, Client> contenant les clients identifis par leur nom, et une Map<String, Commande> contenant les commandes identifies par leur date.

Liste rcapitulative des clients et commandes crs


Deux pages JSP accdant directement aux attributs stocks en session suffisent ici. Puisque vous allez les placer sous /WEBINF, elles ne seront pas accessibles directement par leur URL et vous devrez mettre en place des servlets en amont, qui se chargeront de leur retransmettre les requtes reues. V ous pouvez par exemple les appeler ListeClients et ListeCommandes , et vous n'oublierez pas de les dclarer dans votre fichier web.xml . Dans chacune de ces JSP, le plus simple est de gnrer un tableau HTML qui affichera ligne par ligne les clients ou commandes crs (voir les exemples de rendus). Pour cela, il vous suffit de parcourir les Map enregistres en tant qu'attributs de session l'aide de la balise de boucle JSTL <c:forEach>. Relisez le passage du cours sur la JSTL si vous avez oubli comment itrer sur une collection, et relisez la correction de l'exercice sur la JSTL Core si vous avez oubli comment atteindre les cls et valeurs d'une Map depuis une EL ! Dans le corps de cette boucle, vous placerez les balises HTML ncessaires la cration des lignes du tableau HTML, contenant les entres de la Map des clients ou des commandes. En bonus, pour arer la lecture du tableau final dans le navigateur, vous pouvez utiliser un compteur au sein de votre boucle pour alterner la couleur de fond d'une ligne l'autre. Cela se fait par exemple en testant simplement si l'indice de parcours de la boucle est pair ou impair. Second bonus, vous pouvez mettre en place un test vrifiant si les objets prsents en sessions existent ou non, via par exemple la balise <c:choose> : si non, alors vous pouvez par exemple afficher un message signalant qu'aucun client ou aucune commande n'existe (voir les exemples de rendus).

Un formulaire de cration de commande intelligent


En ce qui concerne cette modification, vous allez devoir rflchir un peu la solution mettre en place : comment donnez un choix l'utilisateur ? Si vous regardez les exemples de rendus que je vous ai donns ci-dessus, vous verrez que j'ai mis en place deux simples boutons de type <input type="radio" /> : au clic sur "Oui", la premire partie du formulaire classique s'affiche ; au clic sur "Non", un nouveau champ <select> listant les clients existants remplace la premire partie du formulaire ! Bien entendu, vous n'tes pas forcs d'utiliser ce moyen ! Si vous souhaitez que votre apprentissage soit efficace, alors vous devez rflchir par vous-mmes cette petite problmatique et lui trouver une solution adapte sans mon aide. Ce petit exercice de conception donnera du fil retordre vos neurones et vous fera prendre encore un peu plus d'aisance avec le Java EE ! En outre, la solution que je propose ici est un peu volue, car elle implique un petit morceau de code Javascript pour permettre la modification du formulaire au clic sur un bouton radio. Si vous ne la sentez pas, prenez le temps et pensez un autre systme plus simple et peut-tre moins volu graphiquement qui permettrait l'utilisateur de choisir entre une liste des clients existants et un formulaire classique comme vous l'affichiez par dfaut auparavant. Ne vous dcouragez pas, testez et russissez ! Note : une telle modification du formulaire va bien videmment impliquer une modification de l'objet mtier qui y est associ, c'est--dire CreationCommandeForm. En effet, puisque vous proposez la rutilisation d'un client existant l'utilisateur dans le formulaire, vous devrez faire en sorte dans votre objet mtier de ne valider les informations clients que si un nouveau client a t cr, et simplement rcuprer l'objet Client existant si l'utilisateur choisit un ancien client dans la liste !

Systme de suppression des clients et commandes

www.siteduzero.com

Partie 4 : Une application interactive !

300/606

Pour terminer, il vous suffit ici de crer deux servlets, ddies chacune la suppression d'un client et d'une commande de la Map prsente en session. V ous contacterez ces servlets via de simples liens HTML depuis vos pages listerClients.jsp ou listerCommandes.jsp ; il faudra donc y implmenter la mthode doGet(). Ces liens contiendront alors, soit un nom de client, soit une date de commande en tant que paramtre d'URL, et les servlets se chargeront alors de retirer l'entre correspondante de la Map des clients ou des commandes, grce la mthode remove(). Dans les exemples de rendus ci-dessus, les croix rouges affiches en fin de chaque ligne des tableaux sont des liens vers les servlets de suppression respectives, que vous pouvez par exemple nommer SuppressionClient et SuppressionCommande.

Gestion de l'encodage UTF-8 des donnes


Rien de particulier vous conseiller ici, il suffit simplement de recopier la dclaration du filtre de Tomcat dans le web.xml du projet, et le tour est jou !

Correction
Cette fois, la longueur du sujet n'est pas trompeuse : le travail que vous devez fournir est bien important ! Posez-vous calmement les bonnes questions, faites l'analogie avec ce que vous avez appris dans les chapitres de cours et n'oubliez pas les bases que vous avez dcouvertes auparavant (cration d'une servlet, modification du fichier web.xml, utilisation de la JSTL, etc.). Comme toujours, ce n'est pas la seule manire de faire, le principal est que votre solution respecte les consignes que je vous ai donnes ! Pour que cette correction soit entirement fonctionnelle, vous devrez inclure la bibliothque jQuery dans le rpertoire /inc de votre projet, ainsi que le fichier image utilis pour illustrer le bouton de suppression.

V oici les deux fichiers tlcharger : jquery.js supprimer.png

Le code des vues


Ajout des deux nouveaux liens dans la page menu.jsp : Code : JSP - /inc/menu.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <div id="menu"> <p><a href="<c:url value="/creationClient"/>">Crer un nouveau client</a></p> <p><a href="<c:url value="/creationCommande"/>">Crer une nouvelle commande</a></p> <p><a href="<c:url value="/listeClients"/>">Voir les clients existants</a></p> <p><a href="<c:url value="/listeCommandes"/>">Voir les commandes existantes</a></p> </div>

Modification du formulaire de cration d'une commande : Code : JSP - /WEB-INF/creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html>

www.siteduzero.com

Partie 4 : Une application interactive !

301/606

<head> <meta charset="utf-8" /> <title>Cration d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationCommande"/>"> <fieldset> <legend>Informations client</legend> <%-- Si et seulement si la Map des clients en session n'est pas vide, alors on propose un choix l'utilisateur --%> <c:if test="${ !empty sessionScope.clients }"> <label for="choixNouveauClient">Nouveau client ? <span class="requis">*</span></label> <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="nouveauClient" checked /> Oui <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="ancienClient" /> Non <br/><br /> </c:if> scope="request" /> <c:set var="client" value="${ commande.client }" <div id="nouveauClient"> <c:import url="/inc/inc_client_form.jsp" /> </div>

<%-- Si et seulement si la Map des clients en session n'est pas vide, alors on cre la liste droulante --%> <c:if test="${ !empty sessionScope.clients }"> <div id="ancienClient"> <select name="listeClients" id="listeClients"> <option value="">Choisissez un client...</option> <%-- Boucle sur la map des clients --%> <c:forEach items="${ sessionScope.clients }" var="mapClients"> <%-- L'expression EL ${mapClients.value} permet de cibler l'objet Client stock en tant que valeur dans la Map, et on cible ensuite simplement ses proprits nom et prenom comme on le ferait avec n'importe quel bean. --%> <option value="${ mapClients.value.nom }">${ mapClients.value.prenom } ${ mapClients.value.nom }</option> </c:forEach> </select> </div> </c:if> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="v" name="dateCommande" value="<c:out value="${commande.date}"/>" size="30" maxlength="30" disabled /> <span class="erreur">${form.erreurs['dateCommande']}</span> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="<c:out value="${commande.montant}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['montantCommande']}</span> <br /> <label for="modePaiementCommande">Mode de paiement <span

www.siteduzero.com

Partie 4 : Une application interactive !

302/606

class="requis">*</span></label> <input type="text" id="modePaiementCommande" name="modePaiementCommande" value="<c:out value="${commande.modePaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modePaiementCommande']}</span> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="<c:out value="${commande.statutPaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutPaiementCommande']}</span> <br /> <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="<c:out value="${commande.modeLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modeLivraisonCommande']}</span> <br /> livraison</label> <label for="statutLivraisonCommande">Statut de la

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="<c:out value="${commande.statutLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutLivraisonCommande']}</span> <br /> <p class="info">${ form.resultat }</p> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> <%-- Inclusion de la bibliothque jQuery. Vous trouverez des cours sur JavaScript et jQuery aux adresses suivantes : - http://www.siteduzero.com/tutoriel-3-309961-dynamisez-vossites-web-avec-javascript.html - http://www.siteduzero.com/tutoriel-3-659477-un-site-webdynamique-avec-jquery.html Si vous ne souhaitez pas tlcharger et ajouter jQuery votre projet, vous pouvez utiliser la version fournie directement en ligne par Google : <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> --%> <script src="<c:url value="/inc/jquery.js"/>"></script> <%-- Petite fonction jQuery permettant le remplacement de la premire partie du formulaire par la liste droulante, au clic sur le bouton radio. --%> <script> jQuery(document).ready(function(){ /* 1 - Au lancement de la page, on cache le bloc d'lments du formulaire correspondant aux clients existants */ $("div#ancienClient").hide(); /* 2 - Au clic sur un des deux boutons radio "choixNouveauClient", on affiche le bloc d'lments correspondant (nouveau ou ancien client) */ jQuery('input[name=choixNouveauClient]:radio').click(function(){ $("div#nouveauClient").hide(); $("div#ancienClient").hide(); var divId = jQuery(this).val();

www.siteduzero.com

Partie 4 : Une application interactive !


}); $("div#"+divId).show();

303/606

}); </script> </body> </html>

Ajout des styles CSS des lments des tableaux : Code : CSS - /inc/style.css ... /* Tableaux ------------------------------------------------------------------------------------*/ table{ border-collapse: collapse; } tr.pair{ background-color: #efefef; } tr.impair{ background-color: #fff; } th{ color: #0568CD; border: 1px solid #0568CD; padding: 5px; } th.action{ border: 1px solid #900; color: #900; } td{ border: 1px solid #ddd; padding: 5px; } td.action{ text-align: center; }

Cration des pages listant les clients et commandes crs : Code : JSP - /WEB-INF/listerClients.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Liste des clients existants</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <c:choose> <%-- Si aucun client n'existe en session, affichage d'un message par dfaut. --%> <c:when test="${ empty sessionScope.clients }">

www.siteduzero.com

Partie 4 : Une application interactive !


<p class="erreur">Aucun client enregistr.</p> </c:when> <%-- Sinon, affichage du tableau. --%> <c:otherwise> <table> <tr> <th>Nom</th> <th>Prnom</th> <th>Adresse</th> <th>Tlphone</th> <th>Email</th> <th class="action">Action</th> </tr> <%-- Parcours de la Map des clients en session, et utilisation de l'objet varStatus. --%> <c:forEach items="${ sessionScope.clients }" var="mapClients" varStatus="boucle"> <%-- Simple test de parit sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%> <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}"> <%-- Affichage des proprits du bean Client, qui est stock en tant que valeur de l'entre courante de la map -%> <td><c:out value="${ mapClients.value.nom }"/></td> <td><c:out value="${ mapClients.value.prenom }"/></td> <td><c:out value="${ mapClients.value.adresse }"/></td> <td><c:out value="${ mapClients.value.telephone }"/></td> <td><c:out value="${ mapClients.value.email }"/></td> <%-- Lien vers la servlet de suppression, avec passage du nom du client - c'est--dire la cl de la Map - en paramtre grce la balise <c:param/>. --%> <td class="action"> <a href="<c:url value="/suppressionClient"><c:param name="nomClient" value="${ mapClients.key }" /></c:url>"> <img src="<c:url value="/inc/supprimer.png"/>" alt="Supprimer" /> </a> </td> </tr> </c:forEach> </table> </c:otherwise> </c:choose> </div> </body> </html>

304/606

Code : JSP - /WEB-INF/listerCommandes.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Liste des commandes existantes</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body>

www.siteduzero.com

Partie 4 : Une application interactive !


<body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <c:choose> <%-- Si aucune commande n'existe en session, affichage d'un message par dfaut. --%> <c:when test="${ empty sessionScope.commandes }"> <p class="erreur">Aucune commande enregistre.</p> </c:when> <%-- Sinon, affichage du tableau. --%> <c:otherwise> <table> <tr> <th>Client</th> <th>Date</th> <th>Montant</th> <th>Mode de paiement</th> <th>Statut de paiement</th> <th>Mode de livraison</th> <th>Statut de livraison</th> <th class="action">Action</th> </tr> <%-- Parcours de la Map des commandes en session, et utilisation de l'objet varStatus. --%> <c:forEach items="${ sessionScope.commandes }" var="mapCommandes" varStatus="boucle"> <%-- Simple test de parit sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%> <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}"> <%-- Affichage des proprits du bean Commande, qui est stock en tant que valeur de l'entre courante de la map -%> <td><c:out value="${ mapCommandes.value.client.prenom } ${ mapCommandes.value.client.nom }"/></td> <td><c:out value="${ mapCommandes.value.date }"/></td> <td><c:out value="${ mapCommandes.value.montant }"/></td> <td><c:out value="${ mapCommandes.value.modePaiement }"/></td> <td><c:out value="${ mapCommandes.value.statutPaiement }"/></td> <td><c:out value="${ mapCommandes.value.modeLivraison }"/></td> <td><c:out value="${ mapCommandes.value.statutLivraison }"/></td> <%-- Lien vers la servlet de suppression, avec passage de la date de la commande - c'est--dire la cl de la Map en paramtre grce la balise <c:param/>. --%> <td class="action"> <a href="<c:url value="/suppressionCommande"><c:param name="dateCommande" value="${ mapCommandes.key }" /></c:url>"> <img src="<c:url value="/inc/supprimer.png"/>" alt="Supprimer" /> </a> </td> </tr> </c:forEach> </table> </c:otherwise> </c:choose> </div> </body> </html>

305/606

www.siteduzero.com

Partie 4 : Une application interactive !

306/606

Le code des servlets


Ajout des quatre servlets de suppression et de listage dans le fichier web.xml : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>Set Character Encoding</filter-name> <filterclass>org.apache.catalina.filters.SetCharacterEncodingFilter</filterclass> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>ignore</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>CreationClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationClient</servletclass> </servlet> <servlet> <servlet-name>ListeClients</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeClients</servlet-class> </servlet> <servlet> <servlet-name>SuppressionClient</servlet-name> <servletclass>com.sdzee.tp.servlets.SuppressionClient</servlet-class> </servlet> <servlet> <servlet-name>CreationCommande</servlet-name> <servletclass>com.sdzee.tp.servlets.CreationCommande</servlet-class> </servlet> <servlet> <servlet-name>ListeCommandes</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeCommandes</servletclass> </servlet> <servlet> <servlet-name>SuppressionCommande</servlet-name> <servletclass>com.sdzee.tp.servlets.SuppressionCommande</servlet-class> </servlet> <servlet-mapping> <servlet-name>CreationClient</servlet-name> <url-pattern>/creationClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeClients</servlet-name> <url-pattern>/listeClients</url-pattern> </servlet-mapping> <servlet-mapping>

www.siteduzero.com

Partie 4 : Une application interactive !


<servlet-name>SuppressionClient</servlet-name> <url-pattern>/suppressionClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>CreationCommande</servlet-name> <url-pattern>/creationCommande</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeCommandes</servlet-name> <url-pattern>/listeCommandes</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SuppressionCommande</servlet-name> <url-pattern>/suppressionCommande</url-pattern> </servlet-mapping> </web-app>

307/606

Cration des servlets de listage : Code : Java - com.sdzee.tp.servlets.ListeClients package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class ListeClients extends HttpServlet { public static final String ATT_CLIENT = "client"; public static final String ATT_FORM = "form"; public static final String VUE INF/listerClients.jsp"; = "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, affichage de la liste des clients */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Code : Java - com.sdzee.tp.servlets.ListeClients package com.sdzee.tp.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class ListeCommandes extends HttpServlet { public static final String ATT_COMMANDE = "commande"; public static final String ATT_FORM = "form"; public static final String VUE = "/WEB-

www.siteduzero.com

Partie 4 : Une application interactive !


INF/listerCommandes.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, affichage de la liste des commandes */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

308/606

Cration des servlets de suppression : Code : Java - com.sdzee.tp.servlets.SuppressionClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client; public class SuppressionClient extends HttpServlet { public static final String PARAM_NOM_CLIENT = "nomClient"; public static final String SESSION_CLIENTS = "clients"; public static final String VUE = "/listeClients";

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String nomClient = getValeurParametre( request, PARAM_NOM_CLIENT ); /* Rcupration de la Map des clients enregistrs en session */ HttpSession session = request.getSession(); Map<String, Client> clients = (HashMap<String, Client>) session.getAttribute( SESSION_CLIENTS ); vides */ /* Si le nom du client et la Map des clients ne sont pas

if ( nomClient != null && clients != null ) { /* Alors suppression du client de la Map */ clients.remove( nomClient ); /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_CLIENTS, clients ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et

www.siteduzero.com

Partie 4 : Une application interactive !


son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

309/606

Code : Java - com.sdzee.tp.servlets.SuppressionCommande package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Commande; public class SuppressionCommande extends HttpServlet { public static final String PARAM_DATE_COMMANDE = "dateCommande"; public static final String SESSION_COMMANDES = "commandes"; public static final String VUE "/listeCommandes"; =

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String dateCommande = getValeurParametre( request, PARAM_DATE_COMMANDE ); /* Rcupration de la Map des commandes enregistres en session */ HttpSession session = request.getSession(); Map<String, Commande> commandes = (HashMap<String, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si la date de la commande et la Map des commandes ne sont pas vides */ if ( dateCommande != null && commandes != null ) { /* Alors suppression de la commande de la Map */ commandes.remove( dateCommande ); /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_COMMANDES, commandes ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et

www.siteduzero.com

Partie 4 : Une application interactive !


son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

310/606

Modification des servlets existantes pour l'enregistrement en session : Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client; import com.sdzee.tp.forms.CreationClientForm ; public class CreationClient extends HttpServlet { public static final String ATT_CLIENT = "client"; public static final String ATT_FORM = "form"; public static final String SESSION_CLIENTS = "clients"; public static final String VUE_SUCCES INF/afficherClient.jsp"; public static final String VUE_FORM INF/creerClient.jsp"; = "/WEB= "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ CreationClientForm form = new CreationClientForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Client client = form.creerClient( request ); /* Ajout du bean et de l'objet mtier l'objet requte */ request.setAttribute( ATT_CLIENT, client ); request.setAttribute( ATT_FORM, form );

www.siteduzero.com

Partie 4 : Une application interactive !


/* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<String, Client> clients = (HashMap<String, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<String, Client>(); } /* Puis ajout du client courant dans la map */ clients.put( client.getNom(), client ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

311/606

Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; import com.sdzee.tp.forms.CreationCommandeForm ; public class CreationCommande extends HttpServlet { public static final String ATT_COMMANDE = "commande"; public static final String ATT_FORM = "form"; public static final String SESSION_CLIENTS = "clients"; public static final String SESSION_COMMANDES = "commandes"; public static final String VUE_SUCCES INF/afficherCommande.jsp"; public static final String VUE_FORM INF/creerCommande.jsp"; = "/WEB= "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); }

www.siteduzero.com

Partie 4 : Une application interactive !


public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ CreationCommandeForm form = new CreationCommandeForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Commande commande = form.creerCommande( request ); /* Ajout du bean et de l'objet mtier l'objet requte */ request.setAttribute( ATT_COMMANDE, commande ); request.setAttribute( ATT_FORM, form ); /* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<String, Client> clients = (HashMap<String, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<String, Client>(); } /* Puis ajout du client de la commande courante dans la map */ clients.put( commande.getClient().getNom(), commande.getClient() ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); session */ /* Ensuite rcupration de la map des commandes dans la

312/606

Map<String, Commande> commandes = (HashMap<String, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( commandes == null ) { commandes = new HashMap<String, Commande>(); } /* Puis ajout de la commande courante dans la map */ commandes.put( commande.getDate(), commande ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_COMMANDES, commandes ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Le code des objets mtiers


Modification de l'objet grant la cration d'une commande :

www.siteduzero.com

Partie 4 : Une application interactive !


Code : Java - com.sdzee.tp.forms.CreationCommandeForm package com.sdzee.tp.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; public final class CreationCommandeForm { private static final String CHAMP_CHOIX_CLIENT "choixNouveauClient"; private static final String CHAMP_LISTE_CLIENTS "listeClients"; private static final String CHAMP_DATE "dateCommande"; private static final String CHAMP_MONTANT "montantCommande"; private static final String CHAMP_MODE_PAIEMENT "modePaiementCommande"; private static final String CHAMP_STATUT_PAIEMENT "statutPaiementCommande"; private static final String CHAMP_MODE_LIVRAISON "modeLivraisonCommande"; private static final String CHAMP_STATUT_LIVRAISON "statutLivraisonCommande"; private static final String ANCIEN_CLIENT "ancienClient"; private static final String SESSION_CLIENTS private static final String FORMAT_DATE HH:mm:ss"; private String resultat; private Map<String, String> erreurs HashMap<String, String>(); public Map<String, String> getErreurs() { return erreurs; } public String getResultat() { return resultat; } public Commande creerCommande( HttpServletRequest request ) { Client client; /* * Si l'utilisateur choisit un client dj existant, pas de validation * effectuer */ String choixNouveauClient = getValeurChamp( request, CHAMP_CHOIX_CLIENT ); if ( ANCIEN_CLIENT.equals( choixNouveauClient ) ) { /* Rcupration du nom du client choisi */ String nomAncienClient = getValeurChamp( request, CHAMP_LISTE_CLIENTS ); /* Rcupration de l'objet client correspondant dans la session */ HttpSession session = request.getSession(); = = = = = = = = = = "clients"; = "dd/MM/yyyy

313/606

= new

www.siteduzero.com

Partie 4 : Une application interactive !


client = ( (Map<String, Client>) session.getAttribute( SESSION_CLIENTS ) ).get( nomAncienClient ); } else { /* * Sinon on garde l'ancien mode, pour la validation des champs. * * L'objet mtier pour valider la cration d'un client existe dj, * il est donc dconseill de dupliquer ici son contenu ! la * place, il suffit de passer la requte courante l'objet mtier * existant et de rcuprer l'objet Client cr. */ CreationClientForm clientForm = new CreationClientForm(); client = clientForm.creerClient( request ); /* * Et trs important, il ne faut pas oublier de rcuprer le contenu * de la map d'erreur cre par l'objet mtier CreationClientForm * dans la map d'erreurs courante, actuellement vide. */ erreurs = clientForm.getErreurs(); } /* * Ensuite, il suffit de procder normalement avec le reste des champs * spcifiques une commande. */ /* * Rcupration et conversion de la date en String selon le format * choisi. */ DateTime dt = new DateTime(); DateTimeFormatter formatter = DateTimeFormat.forPattern( FORMAT_DATE ); String date = dt.toString( formatter ); String montant = getValeurChamp( request, CHAMP_MONTANT ); String modePaiement = getValeurChamp( request, CHAMP_MODE_PAIEMENT ); String statutPaiement = getValeurChamp( request, CHAMP_STATUT_PAIEMENT ); String modeLivraison = getValeurChamp( request, CHAMP_MODE_LIVRAISON ); String statutLivraison = getValeurChamp( request, CHAMP_STATUT_LIVRAISON ); Commande commande = new Commande(); commande.setClient( client ); double valeurMontant = -1; try { valeurMontant = validationMontant( montant ); } catch ( Exception e ) { setErreur( CHAMP_MONTANT, e.getMessage() ); } commande.setMontant( valeurMontant ); commande.setDate( date ); try { validationModePaiement( modePaiement ); } catch ( Exception e ) { setErreur( CHAMP_MODE_PAIEMENT, e.getMessage() ); } commande.setModePaiement( modePaiement ); try {

314/606

www.siteduzero.com

Partie 4 : Une application interactive !


validationStatutPaiement( statutPaiement ); } catch ( Exception e ) { setErreur( CHAMP_STATUT_PAIEMENT, e.getMessage() ); } commande.setStatutPaiement( statutPaiement ); try { validationModeLivraison( modeLivraison ); } catch ( Exception e ) { setErreur( CHAMP_MODE_LIVRAISON, e.getMessage() ); } commande.setModeLivraison( modeLivraison ); try { validationStatutLivraison( statutLivraison ); } catch ( Exception e ) { setErreur( CHAMP_STATUT_LIVRAISON, e.getMessage() ); } commande.setStatutLivraison( statutLivraison ); if ( erreurs.isEmpty() ) { resultat = "Succs de la cration de la commande."; } else { resultat = "chec de la cration de la commande."; } return commande;

315/606

private double validationMontant( String montant ) throws Exception { double temp; if ( montant != null ) { try { temp = Double.parseDouble( montant ); if ( temp < 0 ) { throw new Exception( "Le montant doit tre un nombre positif." ); } } catch ( NumberFormatException e ) { temp = -1; throw new Exception( "Le montant doit tre un nombre." ); } } else { temp = -1; throw new Exception( "Merci d'entrer un montant." ); } return temp; } private void validationModePaiement( String modePaiement ) throws Exception { if ( modePaiement != null ) { if ( modePaiement.length() < 2 ) { throw new Exception( "Le mode de paiement doit contenir au moins 2 caractres." ); } } else { throw new Exception( "Merci d'entrer un mode de paiement." ); } } private void validationStatutPaiement( String statutPaiement ) throws Exception { if ( statutPaiement != null && statutPaiement.length() < 2 ) { throw new Exception( "Le statut de paiement doit contenir au moins 2 caractres." ); }

www.siteduzero.com

Partie 4 : Une application interactive !


} private void validationModeLivraison( String modeLivraison ) throws Exception { if ( modeLivraison != null ) { if ( modeLivraison.length() < 2 ) { throw new Exception( "Le mode de livraison doit contenir au moins 2 caractres." ); } } else { throw new Exception( "Merci d'entrer un mode de livraison." ); } } private void validationStatutLivraison( String statutLivraison ) throws Exception { if ( statutLivraison != null && statutLivraison.length() < 2 ) { throw new Exception( "Le statut de livraison doit contenir au moins 2 caractres." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

316/606

www.siteduzero.com

Partie 4 : Une application interactive !

317/606

Formulaires : l'envoi de fichiers


Nous savons grer toutes sortes de saisies simples - champs de type texte, case cocher, liste droulante, bouton radio, etc. mais il nous reste encore traiter le cas du champ de formulaire permettant l'envoi d'un fichier. C'est un gros chapitre qui vous attend : il y a beaucoup de choses dcouvrir, prenez le temps de bien assimiler toutes les notions prsentes !

Cration du formulaire
Pour permettre au visiteur de naviguer et slectionner un fichier pour envoi via un champ de formulaire, il faut utiliser la balise HTML <input type="file">. Pour rappel, et c'est d'ailleurs explicit dans la spcification HTML, pour envoyer un fichier il faut utiliser la mthode POST lors de l'envoi des donnes du formulaire. En outre, nous y apprenons que l'attribut optionnel enctype doit tre dfini "multipart/form-data". Sans plus tarder, crons sous le rpertoire /WEB-INF une page upload.jsp qui affichera un tel formulaire l'utilisateur : Code : JSP - /WEB-INF/upload.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Envoi de fichier</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/form.css"/>" /> </head> <body> <form action="<c:url value="/upload" />" method="post" enctype="multipart/form-data"> <fieldset> <legend>Envoi de fichier</legend> fichier</label> <label for="description">Description du

<input type="text" id="description" name="description" value="" /> <br /> <label for="fichier">Emplacement du fichier <span class="requis">*</span></label> <input type="file" id="fichier" name="fichier" /> <br /> <input type="submit" value="Envoyer" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

Remarquez bien aux lignes 11 et 20 : l'utilisation de l'attribut optionnel enctype, dont nous n'avions pas besoin dans nos formulaires d'inscription et de connexion puisqu'ils contenaient uniquement des champs classiques ; la mise en place d'un champ <input type="file"/> ddi l'envoi de fichiers. C'est la seule page ncessaire : j'ai rutilis la mme feuille de style CSS que pour nos prcdents formulaires.

Rcupration des donnes Mise en place


V ous commencez tre habitus maintenant : l'tape suivante est la mise en place de la servlet associe cette page. Il nous faut donc crer une servlet, que nous allons nommer Upload et qui est presque vide pour l'instant :

www.siteduzero.com

Partie 4 : Une application interactive !


Code : Java - com.sdzee.servlets.Upload package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

318/606

public class Upload extends HttpServlet { public static final String VUE = "/WEB-INF/upload.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Affichage de la page d'envoi de fichiers */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Mthode vide, pour l'instant... */ } }

Puis l'associer la requte HTTP mise par le formulaire, en la dclarant dans le web.xml de notre application : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> ... <servlet> <servlet-name>Upload</servlet-name> <servlet-class>com.sdzee.servlets.Upload</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>Upload</servlet-name> <url-pattern>/upload</url-pattern> </servlet-mapping> </web-app>

Avec une telle configuration, nous pouvons accder au formulaire en nous rendant depuis notre navigateur sur http://localhost:8080/pro/upload (voir la figure suivante).

L'tape suivante consiste bien videmment complter notre servlet pour traiter les donnes reues !

www.siteduzero.com

Partie 4 : Une application interactive !

319/606

Traitement des donnes


Aprs avoir soumis un tel formulaire, les donnes envoyes sont dans un format binaire "multipart", et sont disponibles dans le corps de la requte POST. ce sujet, une subtilit importante mrite d'tre porte votre attention : ce format de requte n'est pas support par les versions de Tomcat antrieures 7.0.6 . L'explication de ce comportement rside principalement dans : la version de l'API servlet utilise : ce n'est qu' partir de Java EE 6 que la version 3.0 du conteneur de servlets a t mise en place. L'API en version 2.x ne supporte pas de telles requtes, elle ne sait grer que le enctype par dfaut ; un bug dans les premires ditions de Tomcat 7 : aucun problme en ce qui nous concerne, car les versions rcentes ont corrig ce problme.

Avec l'API servlet 2.x


Lorsque des donnes sont envoyes avec le type multipart, les mthodes telles que request.getParameter() retournent toutes null. Il est en thorie possible d'analyser le corps de la requte vous-mmes en vous basant sur la mthode getInputStream() de l'objet HttpServletRequest, mais c'est un vrai travail dorfvre qui requiert une parfaite connaissance de la norme RFC2388 ! Nous n'allons pas tudier en dtail la mthode mettre en place avec cette ancienne version de l'API, mais je vais tout de mme vous donner les lments principaux pour que vous sachiez par o commencer, si jamais vous devez travailler un jour sur une application qui tourne sur une version de l'API antrieure la 3.0.

La coutume est plutt d'utiliser Apache Commons FileUpload pour parser les donnes multipart du formulaire. Cette bibliothque est une implmentation trs robuste de la RFC2388 qui dispose d'excellents guide utilisateur et FAQ (ressources en anglais, mais je vous recommande de parcourir les deux attentivement si vous travaillez avec cette version de l'API servlet). Pour l'utiliser, il est ncessaire de placer les fichiers commons-fileupload.jar et commons-io.jar dans le rpertoire /WEB-INF/lib de votre application. Je vous prsente ici succinctement le principe gnral, mais je ne dtaille volontairement pas la dmarche et ne vous fais pas mettre en place d'exemple pratique : vous allez le dcouvrir un peu plus bas, nous allons utiliser dans notre projet la dmarche spcifique l'API servlet 3.0. V oici cependant un exemple montrant ce quoi devrait ressembler la mthode doPost() de votre servlet d'upload si vous utilisez Apache Commons FileUpload : Code : Java - Exemple d'utilisation de la bibliothque Apache Commons FileUpload ... import import import import ... public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request); for (FileItem item : items) { if (item.isFormField()) { /* Traiter les champs classiques ici (input type="text|radio|checkbox|etc", select, etc). */ String nomChamp = item.getFieldName(); String valeurChamp = item.getString(); /* ... (traitement faire) */ } else { /* Traiter les champs de type fichier (input type="file"). */ String nomChamp = item.getFieldName(); String nomFichier = org.apache.commons.fileupload.FileItem ; org.apache.commons.fileupload.FileUploadException; org.apache.commons.fileupload.disk.DiskFileItemFactory; org.apache.commons.fileupload.servlet.ServletFileUpload;

www.siteduzero.com

Partie 4 : Une application interactive !


FilenameUtils.getName(item.getName()); InputStream contenuFichier = item.getInputStream(); /* ... (traitement faire) */ } } } catch (FileUploadException e) { throw new ServletException("chec de l'analyse de la requte multipart.", e); } } // ...

320/606

Je vous renvoie la documentation de la bibliothque si vous souhaitez en apprendre davantage sur son fonctionnement. En guise d'ouverture pour cette solution, une alternative intressante ce systme serait d'intgrer tout cela dans un Filter qui analyserait le contenu automatiquement et rinsrerait le tout dans la Map des paramtres de la requte, comme s'il s'agissait d'un champ de formulaire classique, rendant ainsi possible de manire transparente : l'utilisation d'un simple request.getParameter() comme lors de la rcupration d'un paramtre quelconque ; l'obtention du fichier upload via request.getAttribute(). V ous pouvez trouver un tel exemple sur cet excellent article.

Avec l'API servlet 3.0


En ce qui nous concerne, notre application se base sur l'API servlet 3.0, la solution prcdente ne nous est donc pas ncessaire ! Dans cette dernire mouture, une nouvelle mthode getParts() est mise disposition dans l'objet HttpServletRequest, et permet de collecter trs simplement les lments de donnes de type multipart ! Auparavant, il tait impossible de parvenir cela simplement sans bibliothque externe. Pour la petite histoire, afin de rendre cette fonctionnalit disponible, la plupart des conteneurs implmentant l'API servlet 3.0 utilisent en ralit le code de la bibliothque Apache Commons FileUpload dans les coulisses ! C'est notamment le cas de Tomcat 7 (Apache) et de GlassFish 3 (Oracle). Pour commencer, nous devons complter la dclaration de notre servlet dans le fichier web.xml avec une section <multipart-config> afin de faire en sorte que la mthode getParts() fonctionne : Code : XML - Extrait du fichier /WEB-INF/web.xml <servlet> <servlet-name>Upload</servlet-name> <servlet-class>com.sdzee.servlets.Upload</servlet-class> <multipart-config> <location>c:/fichiers</location> <max-file-size>10485760</max-file-size> <!-- 10 Mo --> <max-request-size>52428800</max-request-size> <!-- 5 x 10 Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet>

V ous remarquez que cette section s'ajoute au sein de la balise de dclaration <servlet> de notre servlet d'upload. V oici une rapide description des paramtres optionnels existants : <location> contient une URL absolue vers un rpertoire du systme. Un chemin relatif au contexte de l'application n'est pas support dans cette balise, il s'agit bien l d'un chemin absolu vers le systme. Cette URL sera utilise pour stocker temporairement un fichier lors du traitement des fragments d'une requte, lorsque la taille du fichier est plus grande que la taille spcifie dans <file-size-threshold>. Si vous prcisez ici un rpertoire qui n'existe pas sur le disque, alors Tomcat enverra une java.io.IOException lorsque vous tenterez d'envoyer un fichier plus gros que cette limite ; <file-size-threshold> prcise la taille en octets partir de laquelle un fichier reu sera temporairement stock

www.siteduzero.com

Partie 4 : Une application interactive !

321/606

sur le disque ; <max-file-size> prcise la taille maximum en octets autorise pour un fichier envoy. Si la taille d'un fichier envoy dpasse cette limite, le conteneur enverra une exception. En l'occurrence, Tomcat lancera une IllegalStateException ; <max-request-size> prcise la taille maximum en octets autorise pour une requte multipart/form-data. Si la taille totale des donnes envoyes dans une seule requte dpasse cette limite, le conteneur enverra une exception. En paramtrant ainsi notre servlet, toutes les donnes multipart/form-data seront disponibles travers la mthode request.getParts(). Celle-ci retourne une collection d'lments de type Part, et doit tre utilise en lieu et place de l'habituelle mthode request.getParameter() pour rcuprer les contenus des champs de formulaire. l'utilisation, il s'avre que c'est bien plus pratique que d'utiliser directement du pur Apache Commons FileUpload, comme c'tait ncessaire avec les versions antrieures de l'API Servlet ! Par contre, je me rpte, mais je viens de vous annoncer que les contenus des champs du formulaire allaient maintenant tre disponibles en tant que collection d'lments de type Part et a, a va nous poser un petit problme... Car si vous tudiez attentivement l'interface Part, vous vous rendez compte qu'elle est plutt limite en termes d'abstraction : c'est simple, elle ne propose tout bonnement aucune mthode permettant de dterminer si une donne reue renferme un champ classique ou un champ de type fichier ! Dans ce cas, comment savoir si une requte contient des fichiers ?

Heureusement, il va tre facile de nous en sortir par nous-mmes. Afin de dterminer si les donnes transmises dans une requte HTTP contiennent d'ventuels fichiers ou non, il suffit d'analyser ses en-ttes. Regardez plutt ces deux extraits d'en-tte HTTP (comments) : Code : HTTP - Exemples de content-disposition dans l'en-tte d'une requte HTTP // Pour un champ <input type="text"> nomm 'description' Content-Disposition: form-data; name="description" // Pour un champ <input type="file"> nomm 'fichier' Content-Disposition: formdata; name="fichier"; filename="nom_du_fichier.ext"

Comme vous pouvez le constater, la seule diffrence est la prsence d'un attribut nomm filename. Il suffit donc de s'assurer qu'un en-tte contient le mot-cl filename pour tre certain que le fragment trait est un fichier.

Tout cela est magnifique, mais comment allons-nous rcuprer le contenu des en-ttes relatifs un fragment donn ?

Comme d'habitude, il n'y a pas de miracle : tout est dans la documentation ! Encore une fois, si vous tudiez attentivement l'interface Part, vous constatez qu'elle contient une mthode part.getHeader() qui renvoie l'en-tte correspondant un lment. Exactement ce qu'il nous faut ! Ainsi, nous allons pouvoir examiner le contenu des en-ttes relatifs un fragment et y vrifier la prsence du mot-cl filename, afin de savoir si le champ trait est de type fichier ou non. Lanons-nous, et implmentons un dbut de mthode doPost() dans notre servlet d'upload : Code : Java - com.sdzee.servlets.Upload ... public static final String CHAMP_DESCRIPTION = "description"; public static final String CHAMP_FICHIER = "fichier"; ... public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ /* Rcupration du contenu du champ de description */

www.siteduzero.com

Partie 4 : Une application interactive !


String description = request.getParameter( CHAMP_DESCRIPTION ); request.setAttribute( CHAMP_DESCRIPTION, description ); /* * Les donnes reues sont multipart, on doit donc utiliser la mthode * getPart() pour traiter le champ d'envoi de fichiers. */ Part part = request.getPart( CHAMP_FICHIER ); /* * Il faut dterminer s'il s'agit d'un champ classique * ou d'un champ de type fichier : on dlgue cette opration * la mthode utilitaire getNomFichier(). */ String nomFichier = getNomFichier( part ); /* * Si la mthode a renvoy quelque chose, il s'agit donc d'un champ * de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { String nomChamp = part.getName(); request.setAttribute( nomChamp, nomFichier ); } this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } /* * Mthode utilitaire qui a pour unique but d'analyser l'en-tte "content-disposition", * et de vrifier si le paramtre "filename" y est prsent. Si oui, alors le champ trait * est de type File et la mthode retourne son nom, sinon il s'agit d'un champ de formulaire * classique et la mthode retourne null. */ private static String getNomFichier( Part part ) { /* Boucle sur chacun des paramtres de l'en-tte "contentdisposition". */ for ( String contentDisposition : part.getHeader( "contentdisposition" ).split( ";" ) ) { /* Recherche de l'ventuelle prsence du paramtre "filename". */ if ( contentDisposition.trim().startsWith("filename") ) { /* Si "filename" est prsent, alors renvoi de sa valeur, c'est--dire du nom de fichier. */ return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ); } } /* Et pour terminer, si rien n'a t trouv... */ return null; }

322/606

Cette bauche est assez commente pour que vous puissiez comprendre son fonctionnement sans problme. la ligne 16 nous accdons au fragment correspondant au champ fichier du formulaire, puis nous analysons son en-tte pour dterminer s'il s'agit d'un champ de type fichier ou non. Je vous donne pour finir des prcisions au sujet de la mthode utilitaire getNomFichier(). Je vous l'ai annonc un peu plus tt, l'en-tte HTTP lu est de la forme : Code : HTTP - Exemple de content-disposition dans l'en-tte d'une requte HTTP Content-Disposition: form-data; filename="nomdufichier.ext"

www.siteduzero.com

Partie 4 : Une application interactive !

323/606

Afin de slectionner uniquement la valeur du paramtre filename, je ralise dans la mthode utilitaire : un part.getHeader( "content-disposition" ), afin de ne traiter que la ligne de l'en-tte concernant le content-disposition. Par ailleurs, vous pouvez remarquer que la mthode ne prte aucune attention la casse dans l'argument que vous lui passez, c'est--dire aux ventuelles majuscules qu'il contient : que vous criviez "contentdisposition", "Content-disposition" ou encore "Content-Disposition", c'est toujours le mme en-tte HTTP qui sera cibl ; un split( ";" ), afin de distinguer les diffrents lments constituant la ligne, spars comme vous pouvez le constater dans l'exemple d'en-tte ci-dessus par un ";" ; un startsWith( "filename" ), afin de ne slectionner que la chane commenant par filename ; un substring(), afin de ne finalement slectionner que la valeur associe au paramtre filename, contenue aprs le caractre "=" . Avec tous ces dtails, vous devriez comprendre parfaitement comment tout cela s'organise. Modifions alors notre JSP afin d'afficher les valeurs lues et stockes dans les attributs de requtes description et fichier, que j'ai mis en place dans notre servlet : Code : JSP - /WEB-INF/upload.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Envoi de fichier</title> <link type="text/css" rel="stylesheet" href="form.css"> </head> <body> <form action="upload" method="post" enctype="multipart/formdata"> <fieldset> <legend>Envoi de fichier</legend> fichier</label> <label for="description">Description du

<input type="text" id="description" name="description" value="" /> <span class="succes"><c:out value="${description}" /></span> <br /> <label for="fichier">Emplacement du fichier <span class="requis">*</span></label> <input type="file" id="fichier" name="fichier" /> <span class="succes"><c:out value="${fichier}" /></span> <br /> <input type="submit" value="Envoyer" class="sansLabel" /> <br /> </fieldset> </form> </body> </html>

Je ne fais ici que rafficher le contenu des champs, savoir le texte de description et le titre du fichier slectionn par l'utilisateur, aux lignes 17 et 23. Je n'ai volontairement pas ajout d'tapes de validation sur ces deux champs, afin de ne pas surcharger le code de ce chapitre inutilement. Aprs tout, vous savez dj comment procder : il suffit de suivre exactement le mme principe que lorsque nous avions mis en place des validations sur les champs de nos formulaires d'inscription et de connexion dans les

www.siteduzero.com

Partie 4 : Une application interactive !


chapitres prcdents.

324/606

Nous pouvons dornavant tester notre bauche ! J'ai, pour ma part, prcis ces donnes dans mon formulaire, comme vous pouvez le constater la figure suivante ( adapter votre cas selon le fichier que vous allez choisir).

Exemple de donnes dans le formulaire

Et j'ai obtenu ce rsultat (voir la figure suivante).

Rendu aprs envoi

Pour ceux d'entre vous qui utilisent le navigateur Internet Explorer, vous allez obtenir un rendu sensiblement diffrent, comme vous pouvez le voir sur la figure suivante.

Rendu aprs

envoi sous IE V ous constatez que : le nom du fichier a correctement t extrait, mais est entour de guillemets ; avec Internet Explorer, le nom du fichier contient en ralit le chemin complet du fichier sur la machine du client... Nous obtenons ici un bel exemple nous prouvant que la tambouille interne ralise dans certains navigateurs peut imposer certaines spcificits ! La morale de l'histoire, c'est qu'il est primordial de tester le fonctionnement d'une application web sous diffrents navigateurs afin d'viter ce genre de problmes d'une plate-forme une autre...

Afin de pallier les diffrents petits soucis que nous venons de rencontrer, nous devons apporter quelques modifications notre servlet. Dans le bloc if traitant le champ fichier, pour corriger le bug IE : Code : Java - com.sdzee.servlets.Upload /* * Si la mthode a renvoy quelque chose, il s'agit donc d'un champ * de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { String nomChamp = part.getName(); /* * Antibug pour Internet Explorer, qui transmet pour une raison * mystique le chemin du fichier local la machine du client... * * Ex : C:/dossier/sous-dossier/fichier.ext

www.siteduzero.com

Partie 4 : Une application interactive !


* * On doit donc faire en sorte de ne slectionner que le nom et * l'extension du fichier, et de se dbarrasser du superflu. */ nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ) .substring( nomFichier.lastIndexOf( '\\' ) + 1 ); } request.setAttribute( nomChamp, nomFichier );

325/606

Et dans la mthode utilitaire, la ligne 17 pour retirer les guillemets superflus : Code : Java - com.sdzee.servlets.Upload /* * Mthode utilitaire qui a pour unique but d'analyser l'en-tte * "content-disposition", et de vrifier si le paramtre "filename" y est * prsent. Si oui, alors le champ trait est de type File et la mthode * retourne son nom, sinon il s'agit d'un champ de formulaire classique et * la mthode retourne null. */ private static String getNomFichier( Part part ) { /* Boucle sur chacun des paramtres de l'en-tte "contentdisposition". */ for ( String contentDisposition : part.getHeader( "contentdisposition" ).split( ";" ) ) { /* Recherche de l'ventuelle prsence du paramtre "filename". */ if ( contentDisposition.trim().startsWith( "filename" ) ) { /* * Si "filename" est prsent, alors renvoi de sa valeur, * c'est--dire du nom de fichier sans guillemets. */ return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ).trim().replace( "\"", "" ); } } /* Et pour terminer, si rien n'a t trouv... */ return null; }

Je vous laisse analyser ces deux petites corrections par vous-mmes, il s'agit uniquement de bricolages sur les String qui posaient problme ! V ous pouvez maintenant tester, et vrifier que vous obtenez le rsultat indiqu la figure suivante, peu importe le navigateur utilis.

La diffrence entre la thorie et la pratique


Dans le code de notre servlet, j'ai utilis la mthode request.getParameter() pour accder au contenu du champ

www.siteduzero.com

Partie 4 : Une application interactive !


description, qui est un simple champ de type texte. Logique ! vous dites-vous, nous avons toujours fait comme a auparavant et il n'y a pas de raison pour que cela change ! Eh bien dtrompez-vous...

326/606

Comme je vous l'annonais dans le court passage sur les moyens mettre en place avec l'API servlet en version 2.x, avant la version 3.0 un appel la mthode request.getParameter() renvoyait null ds lors que le type des donnes envoyes par un formulaire tait multipart. Depuis la version 3.0, les spcifications ont chang et nous pouvons maintenant y trouver ce passage au sujet de l'envoi de fichiers : Citation : Spcifications de l'API servlet 3.0 For parts with form-data as the Content-Disposition, but without a filename, the string value of the part will also be available via the getParameter / getParameterValues methods on HttpServletRequest, using the name of the part. Pour les non-anglophones, ce passage explicite noir sur blanc que lors de la rception de donnes issues d'un formulaire, envoyes avec le type multipart, la mthode request.getParameter() doit pouvoir tre utilise pour rcuprer le contenu des champs qui ne sont pas des fichiers. Et alors, o est le problme ? C'est bien ce que nous avons fait ici, n'est-ce pas ?

Le problme est ici relativement sournois. En effet, ce que nous avons fait fonctionne, mais uniquement parce que nous utilisons le serveur d'applications Tomcat 7 ! Alors qu'habituellement, Tomcat souffre de carences en comparaison ses confrres comme GlassFish ou JBoss, il se distingue cette fois en respectant la lettre ce passage de la norme. Par contre, le serveur considr comme LA rfrence des serveurs d'applications Java EE - le serveur GlassFish 3 dvelopp par Oracle - ne remplissait toujours pas cette fonctionnalit il y a quelques mois de a ! En ralit, il se comportait toujours comme avec les anciennes versions de l'API, ainsi un appel la mthode request.getParameter() retournait null... C'tait plutt trange, dans la mesure o cette spcification de l'API servlet date tout de mme de 2009 ! Heureusement, plus de deux ans aprs la premire sortie de GlassFish en version 3, ce problme qui avait depuis t report comme un bug, a finalement t corrig dans la dernire version 3.1.2 du serveur. Bref, vous comprenez maintenant mieux le titre de ce paragraphe : en thorie, tout est cens fonctionner correctement, mais dans la pratique ce n'est pas encore le cas partout, et selon le serveur que vous utilisez il vous faudra parfois ruser pour parvenir vos fins. Du coup, comment faire sur un serveur qui ne respecte pas ce passage de la norme ?

Eh bien dans un tel cas, il va falloir que nous rcuprions nous-mmes le contenu binaire de l'lment Part correspondant au champ de type texte, et le reconstruire sous forme d'un String. Apptissant n'est-ce pas ? Les explications qui suivent sont uniquement destines vous donner un moyen de rcuprer les donnes contenues dans les champs classiques d'un formulaire de type multipart dans une application qui tournerait sur un serveur ne respectant pas le point des spcifications que nous venons d'tudier. Si vous n'tes pas concerns, autrement dit si le serveur que vous utilisez ne prsente pas ce bug, vous n'avez pas besoin de mettre en place ces modifications dans votre code, vous pouvez utiliser simplement request.getParameter() comme nous l'avons fait dans l'exemple jusqu' prsent.

Ne vous inquitez pas, nous avons dj presque tout mis en place dans notre servlet, les modifications vont tre minimes ! Dans notre code, nous disposons dj d'une mthode utilitaire getNomfichier() qui nous permet de savoir si un lment Part concerne un champ de type fichier ou un champ classique. Le seul vrai travail va tre de crer la mthode responsable de la reconstruction dont je viens de vous parler. Code : Java - com.sdzee.servlets.Upload ... public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { ... Part part = request.getPart( CHAMP_FICHIER );

www.siteduzero.com

Partie 4 : Une application interactive !


/* * Il faut dterminer s'il s'agit d'un champ classique * ou d'un champ de type fichier : on dlgue cette opration * une mthode utilitaire getNomFichier(). */ String nomFichier = getNomFichier( part ); if ( nomFichier == null ) { /* La mthode a renvoy null, il s'agit donc d'un champ classique ici (input type="text|radio|checkbox|etc", select, etc). */ String nomChamp = part.getName(); /* Rcupration du contenu du champ l'aide de notre nouvelle mthode */ String valeurChamp = getValeur( part ); request.setAttribute( nomChamp, valeurChamp ); } else if ( !nomFichier.isEmpty() ) { /* La mthode a renvoy quelque chose, il s'agit donc d'un champ de type fichier (input type="file"). */ ... } ... } /* * Mthode utilitaire qui a pour unique but de lire l'InputStream contenu * dans l'objet part, et de le convertir en une banale chane de caractres. */ private String getValeur( Part part ) throws IOException { BufferedReader reader = new BufferedReader( new InputStreamReader( part.getInputStream(), "UTF-8" ) ); StringBuilder valeur = new StringBuilder(); char[] buffer = new char[1024]; int longueur = 0; while ( ( longueur = reader.read( buffer ) ) > 0 ) { valeur.append( buffer, 0, longueur ); } return valeur.toString(); }

327/606

La modification importante dans le code de la mthode doPost() est la dcomposition de la vrification du retour de la mthode getNomFichier() en un if / else if. Si la mthode retourne null, alors nous savons que nous avons affaire un champ classique, et nous devons alors rcuprer son contenu avec la nouvelle mthode utilitaire getValeur() que nous venons de mettre en place. En ce qui concerne la mthode utilitaire en elle-mme, encore une fois c'est du bricolage de flux et de String, rien de bien passionnant... du pur Java comme on l'aime chez nous ! Cette nouvelle solution n'apporte aucune nouvelle fonctionnalit, mais offre l'avantage d'tre multiplateforme, alors que le code de notre exemple prcdent ne fonctionnait que sous certains serveurs. Au final, vous voyez que ce n'est pas si tordu, mais c'est quand mme bien moins intuitif et agrable qu'en utilisant directement la mthode request.getParameter() : il est ici ncessaire de faire appel la mthode getValeur() sur chaque champ de type texte dont les donnes sont envoyes travers un formulaire de type multipart. Esprons que les quelques serveurs dapplications qui sont encore la trane comblent ce manque rapidement, afin que les dveloppeurs comme vous et moi puissent se passer de cette tambouille peu ragotante.

Enregistrement du fichier
Maintenant que nous sommes capables de rcuprer les donnes envoyes depuis notre formulaire, nous pouvons nous attaquer la gestion du fichier en elle-mme : la lecture du fichier envoy et son criture sur le disque !

Dfinition du chemin physique


Commenons par dfinir un nom de rpertoire physique dans lequel nous allons crire nos fichiers sur le disque. Rappelez-vous : le chemin que nous avons prcis dans le champ <location> de la section <multipart-config> est uniquement utilis

www.siteduzero.com

Partie 4 : Une application interactive !


par l'API servlet pour stocker les fichiers de manire temporaire. Ce chemin ne sera pas rcuprable ailleurs, et donc pas rutilisable.

328/606

Il y a plusieurs solutions possibles ici : nous pouvons nous contenter d'crire en dur le chemin dans une constante directement au sein de notre servlet, ou encore mettre en place un fichier Properties dans lequel nous prciserons le chemin. Dans notre exemple, nous allons utiliser un autre moyen : nous allons passer le chemin notre servlet via un paramtre dinitialisation. Si vous vous souvenez d'un de nos tout premiers chapitres, celui o nous avons dcouvert la servlet, vous devez galement vous souvenir des options de dclaration d'une servlet dans le fichier web.xml , notamment d'un bloc nomm <init-param>. C'est celui-ci que nous allons mettre en place dans la dclaration de notre servlet d'upload : Code : XML - /WEB-INF/web.xml <servlet> <servlet-name>Upload</servlet-name> <servlet-class>com.sdzee.servlets.Upload</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/</param-value> </init-param> <multipart-config> <location>c:/fichiers</location> <max-file-size>10485760</max-file-size> <!-- 10 Mo --> <max-request-size>52428800</max-request-size> <!-- 5 x 10 Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet>

En procdant ainsi, notre servlet va pouvoir accder un paramtre nomm chemin, disponible travers la mthode getInitParameter() de l'objet ServletConfig !

criture du fichier sur le disque


Reprenons maintenant notre servlet pour y rcuprer ce fameux chemin, et mettre en place proprement l'ouverture des flux dans une mthode ddie l'criture du fichier : Code : Java - com.sdzee.servlets.Upload ... public static final String CHEMIN = "chemin"; public static final int TAILLE_TAMPON = 10240; // 10 ko ... public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Rcupration du contenu du champ de description */ String description = request.getParameter( CHAMP_DESCRIPTION ); request.setAttribute( CHAMP_DESCRIPTION, description ); /* * Les donnes reues sont multipart, on doit donc utiliser la mthode * getPart() pour traiter le champ d'envoi de fichiers. */ Part part = request.getPart( CHAMP_FICHIER );

www.siteduzero.com

Partie 4 : Une application interactive !


/* * Il faut dterminer s'il s'agit d'un champ classique * ou d'un champ de type fichier : on dlgue cette opration * la mthode utilitaire getNomFichier(). */ String nomFichier = getNomFichier( part ); /* * Si la mthode a renvoy quelque chose, il s'agit donc d'un champ * de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { String nomChamp = part.getName(); /* * Antibug pour Internet Explorer, qui transmet pour une raison * mystique le chemin du fichier local la machine du client... * * Ex : C:/dossier/sous-dossier/fichier.ext * * On doit donc faire en sorte de ne slectionner que le nom et * l'extension du fichier, et de se dbarrasser du superflu. */ nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ) .substring( nomFichier.lastIndexOf( '\\' ) + 1 ); /* criture du fichier sur le disque */ ecrireFichier( part, nomFichier, chemin ); } request.setAttribute( nomChamp, nomFichier );

329/606

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } ... /* * Mthode utilitaire qui a pour but d'crire le fichier pass en paramtre * sur le disque, dans le rpertoire donn et avec le nom donn. */ private void ecrireFichier( Part part, String nomFichier, String chemin ) throws IOException { /* Prpare les flux. */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux. */ entree = new BufferedInputStream( part.getInputStream(), TAILLE_TAMPON ); sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), TAILLE_TAMPON ); /* ... */ } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } }

www.siteduzero.com

Partie 4 : Une application interactive !

330/606

Ici, nous pourrions trs bien utiliser directement les flux de type InputStream et FileOutputStream, mais les objets BufferedInputStream et BufferedOutputStream permettent, via l'utilisation d'une mmoire tampon, une gestion plus souple de la mmoire disponible sur le serveur : dans le flux entree, il nous suffit de rcuprer le flux directement depuis la mthode getInputStream() de l'objet Part. Nous dcorons ensuite ce flux avec un BufferedInputStream, avec ici dans l'exemple un tampon de 10 ko ; dans le flux sortie, nous devons mettre en place un fichier sur le disque, en vue d'y crire ensuite le contenu de l'entre. Nous dcorons ensuite ce flux avec un BufferedOutputStream, avec ici dans l'exemple un tampon de 10 ko. Si j'ai pris la peine de vous dtailler l'ouverture des flux, c'est pour que vous remarquiez ici la bonne pratique mise en place, que je vous recommande de suivre ds lors que vous manipulez des flux : toujours ouvrir les flux dans un bloc try, et les fermer dans le bloc finally associ. Ainsi, nous nous assurons, quoi qu'il arrive, que la fermeture de nos flux sera bien effectue ! Note propos du chemin utilis : il reprsente le rpertoire du disque local sur lequel les fichiers vont tre crits. Par exemple, si votre serveur tourne sur le disque C:\ d'une machine sous Windows, alors le chemin /fichiers/ fera rfrence au rpertoire C:\fichiers\ . Ainsi, contrairement au champ <location> abord un peu plus tt dans lequel vous deviez crire un chemin complet, vous pouvez ici spcifier un chemin relatif au systme. Mme remarque toutefois, vous devez vous assurer que ce rpertoire existe, sinon Tomcat enverra une java.io.IOException lors de la tentative d'criture sur le disque.

Ceci fait, il ne nous reste plus qu' mettre en place le tampon et crire notre fichier sur le disque. V oici la mthode complte : Code : Java - com.sdzee.servlets.Upload /* * Mthode utilitaire qui a pour but d'crire le fichier pass en paramtre * sur le disque, dans le rpertoire donn et avec le nom donn. */ private void ecrireFichier( Part part, String nomFichier, String chemin ) throws IOException { /* Prpare les flux. */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux. */ entree = new BufferedInputStream( part.getInputStream(), TAILLE_TAMPON ); sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), TAILLE_TAMPON ); /* * Lit le fichier reu et crit son contenu dans un fichier sur le * disque. */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur; while ( ( longueur = entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur ); } } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } } }

www.siteduzero.com

Partie 4 : Une application interactive !

331/606

l'aide d'un tableau d'octets jouant le rle de tampon, la boucle ici mise en place parcourt le contenu du fichier reu et l'crit morceau par morceau dans le fichier cr sur le disque. Si vous n'tes pas familiers avec la manipulation de fichiers en Java, ou si vous avez oubli comment cela fonctionne, vous pouvez jeter un il ce chapitre du cours de Java.

Test du formulaire d'upload


Il ne nous reste maintenant plus qu' vrifier que tout se droule correctement. Avec la configuration mise en place sur mon poste, fonctionnant sous Windows et Tomcat tournant sur le disque C:\, si je reprends les mmes donnes que dans les exemples prcdents, j'obtiens ce rsultat (voir la figure suivante).

Alors aprs validation, le rpertoire C:\fichiers de mon disque contient bien une copie du fichier eclipse.ini , comme l'indique la figure suivante.

a y est, vous tes maintenant capables de rcuprer des fichiers envoys par vos utilisateurs !

Problmes et limites
Tout cela semble bien joli, mais nous allons un peu vite en besogne. En effet, plusieurs problmatiques importantes se posent avec la solution mise en place : 1. nous ne grons pas les fichiers de mmes noms ; 2. nous ne savons pas viter les doublons ; 3. nous n'avons pas rflchi l'endroit choisi pour le stockage.

Comment grer les fichiers de mmes noms ?


Le plus simple, c'est de toujours renommer les fichiers reus avant de les enregistrer sur le disque. Tout un tas de solutions, que vous pouvez combiner, s'offrent alors vous : ajouter un suffixe votre fichier si un fichier du mme nom existe dj sur le disque ; placer vos fichiers dans des rpertoires diffrents selon leur type ou leur extension ; renommer, prfixer ou suffixer vos fichiers par un timestamp ; vous baser sur un hashCode du contenu binaire du fichier pour gnrer une arborescence et un nom unique ; etc. vous de dfinir quelle(s) solution(s) convien(nen)t le mieux aux contraintes de votre projet.

Comment viter les doublons ?


www.siteduzero.com

Partie 4 : Une application interactive !

332/606

L, il s'agit plus d'une optimisation que d'un rel problme. Mais effectivement, si votre application est voue tre massivement utilise, vous pourriez ventuellement juger intressant d'conomiser un peu d'espace disque sur votre serveur en ne renregistrant pas un fichier qui existe dj sur le disque. Autrement dit, faire en sorte que si un utilisateur envoie un fichier qu'un autre utilisateur a dj envoy par le pass, votre application sache le reconnatre rapidement et agir en consquence. Comme vous devez vous en douter, c'est un travail un peu plus ardu que la simple gestion des fichiers de mmes noms que nous venons d'aborder. Toutefois, une des solutions prcdentes peut convenir : en vous basant sur un hashCode du contenu binaire du fichier pour gnrer une arborescence et un nom unique, vous pouvez ainsi la fois vous affranchir du nom que l'utilisateur a donn son fichier, et vous assurer qu'un contenu identique ne sera pas dupliqu deux endroits diffrents sur votre disque. Exemple : 1. 2. 3. 4. un utilisateur envoie un fichier nomm pastque.jpg ; votre application le reoit, et gnre un hashCode bas sur son contenu, par exemple a8cb45e3d6f1dd5e ; elle stocke alors le fichier dans l'arborescence /a8cb/45e3/d6f1/dd5e.jpg , construite partir du hashCode ; un autre utilisateur envoie plus tard un fichier nomm watermelon.jpg , dont le contenu est exactement identique au prcdent fichier ; 5. votre application le reoit, et gnre alors le mme hashCode que prcdemment, puisque le contenu est identique ; 6. elle se rend alors compte que l'arborescence /a8cb/45e3/d6f1/dd5e.jpg existe dj, et saute l'tape d'criture sur le disque. Bien videmment cela demande un peu de rflexion et d'ajustements. Il faudrait en effet gnrer un hashCode qui soit absolument unique : si deux contenus diffrents peuvent conduire au mme hashCode, alors ce systme ne fonctionne plus ! Mais c'est une piste srieuse qui peut, si elle est bien dveloppe, remplir la mission sans accrocs.

O stocker les fichiers reus ?


C'est en effet une bonne question, qui mrite qu'on s'y attarde un instant. A priori, deux possibilits s'offrent nous : stocker les fichiers au sein de l'application web, dans un sous-rpertoire du dossier WebContent d'Eclipse par exemple ; stocker les fichiers en dehors de l'application, dans un rpertoire du disque local. V ous avez ici aveuglment suivi mes consignes, et ainsi implment la seconde option. En effet, dans l'exemple j'ai bien enregistr le fichier dans un rpertoire plac la racine de mon disque local. Mais vous devez vous rendre compte que cette solution peut poser un problme important : tous les fichiers placs en dehors de l'application, un peu la manire des fichiers placs sous son rpertoire /WEB-INF, sont invisibles au contexte web, c'est--dire qu'ils ne sont pas accessibles directement via une URL. Autrement dit, vous ne pourrez pas proposer aux utilisateurs de tlcharger ces fichiers ! Dans ce cas, pourquoi ne pas avoir opt pour la premire solution ?

Eh bien tout simplement parce que si elle a l'avantage de pouvoir rendre disponibles les fichiers directement aux utilisateurs, puisque tout fichier plac sous la racine d'une application est accessible directement via une URL, elle prsente un autre inconvnient : en stockant les fichiers directement dans le conteneur de votre application, vous les rendez vulnrables un crasement lors d'un prochain redmarrage serveur ou d'un prochain redploiement de l'application. V ous en apprendrez plus ce sujet dans les annexes du cours, contentez-vous pour le moment de retenir la pratique qui dcoule de ces contraintes : il est dconseill de stocker les fichiers uploads par les utilisateurs dans le conteneur. Mais alors, comment faire pour rendre nos fichiers externes disponibles au tlchargement ?

La rponse cette question ncessite un peu de code et d'explications, et vous attend dans le chapitre suivant !

Rendre le tout entirement automatique


Dans le cas d'un petit formulaire comme celui de notre exemple, tout va bien. Nous sommes l pour apprendre, nous avons le temps de perdre notre temps ( ) dvelopper des mthodes utilitaires et rflchir des solutions adaptes. Mais dans une vraie application, des formulaires qui contiennent des champs de type fichier, vous risquez d'en rencontrer plus d'un ! Et vous allez vite dchanter quand vous aurez crer toutes les servlets responsables des traitements...

www.siteduzero.com

Partie 4 : Une application interactive !

333/606

L'idal, a serait de pouvoir continuer utiliser les mthodes request.getParameter() comme si de rien n'tait ! Eh bien pour cela, pas de miracle, il faut mettre en place un filtre qui va se charger d'effectuer les vrifications et conversions ncessaires, et qui va rendre disponible le contenu des fichiers simplement. C'est un travail consquent et plutt difficile. Plutt que de vous faire coder le tout partir de zro, je vais vous laisser admirer la superbe classe prsente dans cet excellent article. C'est en anglais, mais le code est extrmement clair et professionnel, essayez d'y jeter un il et de le comprendre.

Intgration dans MVC


Nous avons dj fourni beaucoup d'efforts, mais il nous reste encore une tape importante franchir : nous devons encore intgrer ce que nous venons de mettre en place dans notre architecture MVC. Nous l'avons dj fait avec le formulaire dinscription et de connexion, vous devez commencer tre habitus ! Il va nous falloir : crer un bean reprsentant les donnes manipules, en l'occurrence il s'agit d'un fichier et de sa description ; crer un objet mtier qui regroupe les traitements jusqu' prsent raliss dans la servlet ; en profiter pour y ajouter des mthodes de validation de la description et du fichier, et ainsi permettre une gestion fine des erreurs et exceptions ; reprendre la servlet pour l'adapter aux objets du modle frachement crs ; modifier la page JSP pour qu'elle rcupre les nouvelles informations qui lui seront transmises. Le plus gros du travail va se situer dans l'objet mtier responsable des traitements et validations. C'est l que nous allons devoir rflchir un petit peu, afin de trouver un moyen efficace pour grer toutes les exceptions possibles lors de la rception de donnes issues de notre formulaire. Sans plus attendre, voici les codes comments et expliqus...

Cration du bean reprsentant un fichier


Commenons par le plus simple. Un fichier tant reprsent par son nom et sa description, nous avons uniquement besoin d'un bean que nous allons nommer Fichier, qui contiendra deux proprits et que nous allons placer comme ses confrres dans le package com.sdzee.beans : Code : Java - com.sdzee.beans.Fichier package com.sdzee.beans; public class Fichier { private String description; private String nom; public String getDescription() { return description; } public void setDescription( String description ) { this.description = description; } public String getNom() { return nom; } public void setNom( String nom ) { this.nom = nom; }

Jusque-l, a va...

Cration de l'objet mtier en charge du traitement du formulaire


Arrivs l, les choses se compliquent un peu pour nous : pour assurer la validation des champs, nous allons simplement vrifier que le champ description est renseign et qu'il

www.siteduzero.com

Partie 4 : Une application interactive !

334/606

contient au moins 15 caractres, et nous allons vrifier que le champ fichier existe bien et contient bien des donnes ; pour assurer la gestion des erreurs, nous allons, comme pour nos systmes d'inscription et de connexion, initialiser une Map d'erreurs et une chane contenant le rsultat final du processus. La difficult va ici trouver sa source dans la gestion des ventuelles erreurs qui peuvent survenir. la diffrence de simples champs texte, la manipulation d'un fichier fait intervenir beaucoup de composants diffrents, chacun pouvant causer des exceptions bien particulires. Je vous donne le code pour commencer, celui de l'objet mtier UploadForm plac dans le package com.sdzee.forms, et nous en reparlons ensuite. Ne paniquez pas s'il vous semble massif, plus de la moiti des lignes sont des commentaires destins vous en faciliter la comprhension ! Code : Java - com.sdzee.forms.UploadForm package com.sdzee.forms; import import import import import import import import java.io.BufferedInputStream ; java.io.BufferedOutputStream ; java.io.File; java.io.FileOutputStream ; java.io.IOException; java.io.InputStream ; java.util.HashMap; java.util.Map;

import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import com.sdzee.beans.Fichier; public final class private static private static private static // 10 ko UploadForm { final String CHAMP_DESCRIPTION = "description"; final String CHAMP_FICHIER = "fichier"; final int TAILLE_TAMPON = 10240;

private String resultat; private Map<String, String> erreurs HashMap<String, String>(); public String getResultat() { return resultat; } public Map<String, String> getErreurs() { return erreurs; }

= new

public Fichier enregistrerFichier( HttpServletRequest request, String chemin ) { /* Initialisation du bean reprsentant un fichier */ Fichier fichier = new Fichier(); /* Rcupration du champ de description du formulaire */ String description = getValeurChamp( request, CHAMP_DESCRIPTION ); /* * Rcupration du contenu du champ fichier du formulaire. Il faut ici * utiliser la mthode getPart(), comme nous l'avions fait dans notre * servlet auparavant. */ String nomFichier = null; InputStream contenuFichier = null; try { Part part = request.getPart( CHAMP_FICHIER );

www.siteduzero.com

Partie 4 : Une application interactive !


/* * Il faut dterminer s'il s'agit bien d'un champ de type fichier : * on dlgue cette opration la mthode utilitaire * getNomFichier(). */ nomFichier = getNomFichier( part ); /* * Si la mthode a renvoy quelque chose, il s'agit donc d'un * champ de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { /* * Antibug pour Internet Explorer, qui transmet pour une * raison mystique le chemin du fichier local la machine * du client... * * Ex : C:/dossier/sous-dossier/fichier.ext * * On doit donc faire en sorte de ne slectionner que le nom * et l'extension du fichier, et de se dbarrasser du * superflu. */ nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ) .substring( nomFichier.lastIndexOf( '\\' ) + 1 ); /* Rcupration du contenu du fichier */ contenuFichier = part.getInputStream(); } } catch ( IllegalStateException e ) { /* * Exception retourne si la taille des donnes dpasse les limites * dfinies dans la section <multipart-config> de la dclaration de * notre servlet d'upload dans le fichier web.xml */ e.printStackTrace(); setErreur( CHAMP_FICHIER, "Les donnes envoyes sont trop volumineuses." ); } catch ( IOException e ) { /* * Exception retourne si une erreur au niveau des rpertoires de * stockage survient (rpertoire inexistant, droits d'accs * insuffisants, etc.) */ e.printStackTrace(); setErreur( CHAMP_FICHIER, "Erreur de configuration du serveur." ); } catch ( ServletException e ) { /* * Exception retourne si la requte n'est pas de type * multipart/form-data. Cela ne peut arriver que si l'utilisateur * essaie de contacter la servlet d'upload par un formulaire * diffrent de celui qu'on lui propose... pirate ! :| */ e.printStackTrace(); setErreur( CHAMP_FICHIER, "Ce type de requte n'est pas support, merci d'utiliser le formulaire prvu pour envoyer votre fichier." ); } /* Si aucune erreur n'est survenue jusqu' prsent */ if ( erreurs.isEmpty() ) { /* Validation du champ de description. */ try { validationDescription( description ); } catch ( Exception e ) { setErreur( CHAMP_DESCRIPTION, e.getMessage() );

335/606

www.siteduzero.com

Partie 4 : Une application interactive !


} fichier.setDescription( description ); /* Validation du champ fichier. */ try { validationFichier( nomFichier, contenuFichier ); } catch ( Exception e ) { setErreur( CHAMP_FICHIER, e.getMessage() ); } fichier.setNom( nomFichier );

336/606

/* Si aucune erreur n'est survenue jusqu' prsent */ if ( erreurs.isEmpty() ) { /* criture du fichier sur le disque */ try { ecrireFichier( contenuFichier, nomFichier, chemin ); } catch ( Exception e ) { setErreur( CHAMP_FICHIER, "Erreur lors de l'criture du fichier sur le disque." ); } } /* Initialisation du rsultat global de la validation. */ if ( erreurs.isEmpty() ) { resultat = "Succs de l'envoi du fichier."; } else { resultat = "chec de l'envoi du fichier."; } } return fichier;

/* * Valide la description saisie. */ private void validationDescription( String description ) throws Exception { if ( description != null ) { if ( description.length() < 15 ) { throw new Exception( "La phrase de description du fichier doit contenir au moins 15 caractres." ); } } else { throw new Exception( "Merci d'entrer une phrase de description du fichier." ); } } /* * Valide le fichier envoy. */ private void validationFichier( String nomFichier, InputStream contenuFichier ) throws Exception { if ( nomFichier == null || contenuFichier == null ) { throw new Exception( "Merci de slectionner un fichier envoyer." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son

www.siteduzero.com

Partie 4 : Une application interactive !


contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } /* * Mthode utilitaire qui a pour unique but d'analyser l'en-tte * "content-disposition", et de vrifier si le paramtre "filename" y est * prsent. Si oui, alors le champ trait est de type File et la mthode * retourne son nom, sinon il s'agit d'un champ de formulaire classique et * la mthode retourne null. */ private static String getNomFichier( Part part ) { /* Boucle sur chacun des paramtres de l'en-tte "contentdisposition". */ for ( String contentDisposition : part.getHeader( "contentdisposition" ).split( ";" ) ) { /* Recherche de l'ventuelle prsence du paramtre "filename". */ if ( contentDisposition.trim().startsWith( "filename" ) ) { /* * Si "filename" est prsent, alors renvoi de sa valeur, * c'est--dire du nom de fichier sans guillemets. */ return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ).trim().replace( "\"", "" ); } } /* Et pour terminer, si rien n'a t trouv... */ return null; } /* * Mthode utilitaire qui a pour but d'crire le fichier pass en paramtre * sur le disque, dans le rpertoire donn et avec le nom donn. */ private void ecrireFichier( InputStream contenu, String nomFichier, String chemin ) throws Exception { /* Prpare les flux. */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux. */ entree = new BufferedInputStream( contenu, TAILLE_TAMPON ); sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), TAILLE_TAMPON ); /* * Lit le fichier reu et crit son contenu dans un fichier sur le * disque. */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur = 0; while ( ( longueur = entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur );

337/606

www.siteduzero.com

Partie 4 : Une application interactive !


} } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } }

338/606

Sans surprise, dans ce code vous retrouvez bien : les mthodes utilitaires ecrireFichier() et getNomFichier(), que nous avions dveloppes dans notre servlet ; l'analyse des lments de type Part que nous avions mis en place l encore dans notre servlet, pour retrouver le champ contenant le fichier ; l'architecture que nous avions mise en place dans nos anciens objets mtiers dans les systmes d'inscription et de connexion. Notamment la Map erreurs , la chane resultat, les mthodes getValeurChamp() et setErreur(), ainsi que la grosse mthode centrale ici nomme enregistrerFichier(), qui correspond la mthode appele depuis la servlet pour effectuer les traitements. Comme vous pouvez le constater, la seule diffrence notoire est le nombre de blocs try / catch qui interviennent, et le nombre de vrifications de la prsence d'erreurs. propos, je ne me suis pas amus vrifier trois fois de suite - aux lignes 109, 128 et 138 - si des erreurs avaient eu lieu au cours du processus ou non. C'est simplement afin d'viter de continuer inutilement le processus de validation si des problmes surviennent lors des traitements prcdents, et galement afin de pouvoir renvoyer un message d'erreur prcis l'utilisateur. Enfin, vous remarquerez pour finir que j'ai modifi lgrement la mthode utilitaire ecrireFichier(), et que je lui passe dsormais le contenu de type InputStream renvoy par la mthode part.getInputStream(), et non plus directement l'lment Part. En procdant ainsi je peux m'assurer, en amont de la demande d'criture sur le disque, si le contenu existe et est bien manipulable par notre mthode utilitaire.

Reprise de la servlet
C'est ici un petit travail de simplification, nous devons nettoyer le code de notre servlet pour qu'elle remplisse uniquement un rle d'aiguilleur : Code : Java - com.sdzee.servlets.Upload package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.beans.Fichier; import com.sdzee.forms.UploadForm ; public class Upload extends HttpServlet { public static final String CHEMIN = "chemin";

public static final String ATT_FICHIER = "fichier"; public static final String ATT_FORM = "form"; public static final String VUE = "/WEB-INF/upload.jsp";

www.siteduzero.com

Partie 4 : Une application interactive !


public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Affichage de la page d'upload */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Prparation de l'objet formulaire */ UploadForm form = new UploadForm(); /* Traitement de la requte et rcupration du bean en rsultant */ Fichier fichier = form.enregistrerFichier( request, chemin ); */ /* Stockage du formulaire et du bean dans l'objet request request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_FICHIER, fichier );

339/606

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

L'unique diffrence avec les anciennes servlets d'inscription et de connexion se situe dans l'appel la mthode centrale de l'objet mtier. Cette fois, nous devons lui passer un argument supplmentaire en plus de l'objet request : le chemin physique de stockage des fichiers sur le disque local, que nous rcuprons - souvenez-vous - via le paramtre dinitialisation dfini dans le bloc <init-param> de la dclaration de la servlet.

Adaptation de la page JSP aux nouvelles informations transmises


Pour terminer, il faut modifier lgrement le formulaire pour qu'il affiche les erreurs que nous grons dornavant grce notre objet mtier. V ous connaissez le principe, cela reprend l encore les mmes concepts que ceux que nous avions mis en place dans nos systmes d'inscription et de connexion : Code : JSP - /WEB-INF/upload.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Envoi de fichier</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/form.css"/>" /> </head> <body> <form action="<c:url value="/upload" />" method="post" enctype="multipart/form-data"> <fieldset>

www.siteduzero.com

Partie 4 : Une application interactive !


<legend>Envoi de fichier</legend> fichier</label> <label for="description">Description du

340/606

<input type="text" id="description" name="description" value="<c:out value="${fichier.description}"/>" /> <span class="erreur">${form.erreurs['description']}</span> <br /> <label for="fichier">Emplacement du fichier <span class="requis">*</span></label> <input type="file" id="fichier" name="fichier" value="<c:out value="${fichier.nom}"/>" /> <span class="erreur">${form.erreurs['fichier']}</span> <br /> <input type="submit" value="Envoyer" class="sansLabel" /> <br /> <p class="${empty form.erreurs ? 'succes' : 'erreur'}">${form.resultat}</p> </fieldset> </form> </body> </html>

Comportement de la solution finale


V ous pouvez maintenant tester votre formulaire sous toutes ses coutures : envoyez des champs vides ou mal renseigns, n'envoyez qu'un champ sur deux, envoyez un fichier trop gros, configurez votre servlet pour qu'elle enregistre les fichiers dans un dossier qui n'existe pas sur votre disque, etc. Dans le dsordre, voici aux figures suivantes un aperu de quelques rendus que vous devriez obtenir dans diffrents cas d'checs.

Envoi sans description ni fichier

Envoi d'un fichier trop lourd

www.siteduzero.com

Partie 4 : Une application interactive !

341/606

Envoi avec une description trop courte et pas de fichier

Ne prenez pas cette dernire tape la lgre : prenez le temps de bien analyser tout ce qui est gr par notre objet mtier, et de bien tester le bon fonctionnement de la gestion du formulaire. Cela vous aidera bien assimiler tout ce qui intervient dans l'application, ainsi que les relations entre ses diffrents composants. Pour envoyer un fichier depuis un formulaire, il faut utiliser une requte de type multipart via <form ... enctype="multipart/form-data">. Pour rcuprer les donnes d'une telle requte depuis le serveur, l'API Servlet 3.0 fournit la mthode request.getParts(). Pour rendre disponible cette mthode dans une servlet, il faut complter sa dclaration dans le fichier web.xml avec une section <multipart-config>. Pour vrifier si une Part contient un fichier, il faut vrifier son type en analysant son en-tte Content-Disposition. Pour crire le contenu d'un tel fichier sur le serveur, il suffit ensuite de rcuprer le flux via la mthode part.getInputStream() et de le manipuler comme on manipulerait un fichier local. Lors de la sauvegarde de fichiers sur un serveur, il faut penser aux contraintes imposes par le web : noms de fichiers identiques, contenus de fichiers identiques, faux fichiers, etc.

www.siteduzero.com

Partie 4 : Une application interactive !

342/606

Le tlchargement de fichiers
Mettons-nous maintenant du ct du client : comment permettre aux utilisateurs de rcuprer un fichier prsent sur le serveur ? Nous pourrions nous contenter de placer nos documents dans un rpertoire du serveur accessible au public, et de leur donner des liens directs vers les fichiers, mais : c'est une mauvaise pratique, pour les raisons voques dans le chapitre prcdent ; nous sommes fidles MVC, et nous aimons bien tout contrler : un seul point d'entre pour les tlchargements, pas cinquante ! C'est dans cette optique que nous allons raliser une servlet qui aura pour unique objectif de permettre aux clients de tlcharger des fichiers.

Une servlet ddie


Les seules ressources auxquelles l'utilisateur peut accder depuis son navigateur sont les fichiers et dossiers placs sous la racine de votre application, c'est--dire sous le dossier WebContent de votre projet Eclipse, l'exception bien entendu du rpertoire priv /WEB-INF. Ainsi, lorsque vous enregistrez vos fichiers en dehors de votre application web (ailleurs sur le disque dur, ou bien sur un FTP distant, dans une base de donnes, etc.), le client ne peut pas y accder directement par une URL. Une des solutions possibles est alors de crer une servlet dont l'unique objectif est de charger ces fichiers depuis le chemin en dehors du conteneur web (ou depuis une base de donnes, mais nous y reviendrons bien plus tard), et de les transmettre en flux continu (en anglais, on parle de streaming ) l'objet HttpServletResponse. Le client va alors visualiser sur son navigateur une fentre de type "Enregistrer sous...". Comment procder ? Regardons tout cela tape par tape...

Cration de la servlet
Pour commencer nous allons crer une bauche de servlet, que nous allons nommer Download et placer dans com.sdzee.servlets : Code : Java - com.sdzee.servlets.Download package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

public class Download extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }

La seule action ralise par le client sera un clic sur un lien pour tlcharger un fichier, notre servlet aura donc uniquement besoin d'implmenter la mthode doGet().

Paramtrage de la servlet
Configurons ensuite l'URL d'accs notre servlet : Code : XML - /WEB-INF/web.xml ... <servlet> <servlet-name>Download</servlet-name> <servlet-class>com.sdzee.servlets.Download</servlet-class>

www.siteduzero.com

Partie 4 : Une application interactive !


</servlet> ... <servlet-mapping> <servlet-name>Download</servlet-name> <url-pattern>/fichiers/*</url-pattern> </servlet-mapping> ...

343/606

Nous faisons ici correspondre notre servlet toute URL commenant par /fichiers/ , travers la balise <url-pattern>. Ainsi, toutes les adresses du type http://localhost:8080/pro/fichiers/fichier.ext ou encore http://localhost:8080/pro/fichiers/dossier/fichier.ext pointeront vers notre servlet de tlchargement. Nous devons maintenant prciser notre servlet o elle va devoir aller chercher les fichiers sur le disque. Comment lui faire connatre ce rpertoire ?

Il y a plusieurs manires de faire, mais puisque nous avions prcis ce chemin dans le fichier web.xml pour la servlet d'upload, nous allons faire de mme avec notre servlet de download ! Si votre mmoire est bonne, vous devez vous souvenir d'une balise optionnelle permettant de prciser une servlet des paramtres d'initialisation... La balise <init-param>, a vous dit quelque chose ? Code : XML - /WEB-INF/web.xml ... <servlet> <servlet-name>Download</servlet-name> <servlet-class>com.sdzee.servlets.Download</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/</param-value> </init-param> </servlet> ... <servlet-mapping> <servlet-name>Download</servlet-name> <url-pattern>/fichiers/*</url-pattern> </servlet-mapping> ...

En procdant ainsi, nous mettons disposition de notre servlet un objet qui contient la valeur spcifie ! Ainsi, ct servlet il nous suffit de lire la valeur associe depuis notre mthode doGet(), vide jusqu' prsent : Code : Java - com.sdzee.servlets.Download public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* Lecture du paramtre 'chemin' pass la servlet via la dclaration dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( "chemin" ); }

V ous retrouvez ici la mthode permettant l'accs aux paramtres d'initialisation getInitParameter(), qui prend en argument le nom du paramtre cibl.

www.siteduzero.com

Partie 4 : Une application interactive !

344/606

Analyse du fichier
Maintenant que tout est prcis ct serveur, il nous faut donner au client un moyen de prciser quel fichier il souhaite tlcharger. La premire ide qui nous vient l'esprit est videmment un paramtre de requte, comme nous avons toujours fait jusqu' prsent notamment avec nos formulaires. Oui, mais nous n'allons pas procder ainsi... Quel est le problme avec les paramtres de requtes ?

Le problme, c'est... Internet Explorer, entre autres. Alors que la plupart des navigateurs sont capables de dtecter proprement un nom de fichier initialis dans les en-ttes HTTP, IE ignore tout simplement ce champ et considre lors de l'affichage de la fentre "Enregistrer sous..." que le nom du fichier enregistrer correspond la fin de l'URL demande, c'est--dire dans notre cas l'URL de notre servlet de tlchargement ! Autrement dit, il va faire tlcharger une page blanche l'utilisateur. Certains navigateurs sont incapables de dtecter correctement le contenu de l'en-tte Content-Type. Bref, afin d'viter tous ces ennuis dus aux diffrentes moutures des navigateurs existant, il nous reste un moyen simple et propre de faire passer notre nom de fichier : l'inclure directement dans l'URL. Autrement dit, faire en sorte qu'il nous suffise d'appeler une URL du type http://localhost:8080/pro/fichiers/test.txt pour tlcharger le fichier nomm test.txt ! Pour ce faire, ct servlet nous allons utiliser une mthode de l'objet HttpServletRequest : getPathInfo(). Elle retourne la fraction de l'URL qui correspond ce qui est situ entre le chemin de base de la servlet et les paramtres de requte. Il faut donc ajouter notre mthode doGet() la ligne suivante : Code : Java - com.sdzee.servlets.Download /* Rcupration du chemin du fichier demand au sein de l'URL de la requte */ String fichierRequis = request.getPathInfo();

Dans la documentation de la mthode getPathInfo(), nous remarquons qu'elle retourne null si aucun chemin n'existe dans l'URL, et qu'un chemin existant commence toujours par /. Nous devons donc vrifier si un chemin vide est transmis en ajoutant cette condition : Code : Java - com.sdzee.servlets.Download /* Vrifie qu'un fichier a bien t fourni */ if ( fichierRequis == null || "/".equals( fichierRequis ) ) { /* Si non, alors on envoie une erreur 404, qui signifie que la ressource demande n'existe pas */ response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }

V ous remarquez ici l'emploi d'une mthode de l'objet HttpServletResponse qui vous tait encore inconnue jusque-l : sendError(). Elle permet de retourner au client les messages et codes d'erreur HTTP souhaits. Je vous laisse parcourir la documentation et dcouvrir par vous-mmes les noms des diffrentes constantes reprsentant les codes d'erreur accessibles ; en l'occurrence celui que j'ai utilis ici correspond la fameuse erreur 404. Bien entendu, vous pouvez opter pour quelque chose de moins abrupt, en initialisant par exemple un message d'erreur quelconque que vous transmettez ensuite une page JSP ddie, pour affichage l'utilisateur. L'tape suivante consiste contrler le nom du fichier transmis et vrifier si un tel fichier existe : Code : Java - com.sdzee.servlets.Download /* Dcode le nom de fichier rcupr, susceptible de contenir des espaces et autres caractres spciaux, et prpare l'objet File */ fichierRequis = URLDecoder.decode( fichierRequis, "UTF-8"); File fichier = new File( chemin, fichierRequis );

www.siteduzero.com

Partie 4 : Une application interactive !


/* Vrifie que le fichier existe bien */ if ( !fichier.exists() ) { /* Si non, alors on envoie une erreur 404, qui signifie que la ressource demande n'existe pas */ response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }

345/606

Avant de crer un objet File bas sur le chemin du fichier rcupr, il est ncessaire de convertir les ventuels caractres spciaux qu'il contient l'aide de la mthode URLDecoder.decode(). Une fois l'objet cr, l encore si le fichier n'existe pas sur le disque, j'utilise la mthode sendError() pour envoyer une erreur 404 au client et ainsi lui signaler que la ressource demande n'a pas t trouve. Une fois ces contrles raliss, il nous faut encore rcuprer le type du fichier transmis, l'aide de la mthode getMimeType() de l'objet ServletContext. Sa documentation nous indique qu'elle retourne le type du contenu d'un fichier en prenant pour argument son nom uniquement. Si le type de contenu est inconnu, alors la mthode renvoie null : Code : Java - com.sdzee.servlets.Download /* Rcupre le type du fichier */ String type = getServletContext().getMimeType( fichier.getName() ); /* Si le type de fichier est inconnu, alors on initialise un type par dfaut */ if ( type == null ) { type = "application/octet-stream"; }

Pour information, les types de fichiers sont dtermins par le conteneur lui-mme. Lorsque le conteneur reoit une requte demandant un fichier et qu'il le trouve, il le renvoie au client. Dans la rponse HTTP retourne, il renseigne alors l'en-tte Content-Type. Pour ce faire, il se base sur les types MIME dont il a connaissance, en fonction de l'extension du fichier retourner. Ces types sont spcifis dans le fichier web.xml global du conteneur, qui est situ dans le rpertoire /conf/ du Tomcat Home. Si vous l'ditez, vous verrez qu'il en contient dj une bonne quantit ! En voici un court extrait : Code : XML - /apache-tomcat-7.0.20/conf/web.xml ... <mime-mapping> <extension>jpeg</extension> <mime-type>image/jpeg</mime-type> </mime-mapping> <mime-mapping> <extension>jpg</extension> <mime-type>image/jpeg</mime-type> </mime-mapping> ...

Ainsi, il est possible d'ajouter un type inconnu au serveur, il suffit pour cela d'ajouter une section <mime-mapping> au fichier. De mme, il est possible d'apporter de telles modifications sur le web.xml de votre projet web, afin de limiter l'impact des changements effectus votre application uniquement, et non pas toute instance de Tomcat lance sur votre poste. Bref, dans notre cas nous n'allons pas nous embter : nous nous contentons de spcifier un type par dfaut si l'extension du fichier demande est inconnue.

Gnration de la rponse HTTP


Aprs tous ces petits traitements, nous avons maintenant tout en main pour initialiser une rponse HTTP et y renseigner les enttes ncessaires, savoir :

www.siteduzero.com

Partie 4 : Une application interactive !


Content-Type ; Content-Length ; Content-Disposition. V oici donc le code en charge de l'initialisation de la rponse : Code : Java - com.sdzee.servlets.Download private static final int DEFAULT_BUFFER_SIZE = 10240; // 10 ko ... /* Initialise la rponse HTTP */ response.reset(); response.setBufferSize( DEFAULT_BUFFER_SIZE ); response.setContentType( type ); response.setHeader( "Content-Length", String.valueOf( fichier.length() ) ); response.setHeader( "Content-Disposition", "attachment; filename=\"" + fichier.getName() + "\"" );

346/606

V oici quelques explications sur l'enchanement ici ralis : reset() : efface littralement l'intgralit du contenu de la rponse initie par le conteneur ; setBufferSize() : mthode appeler imprativement aprs un reset() ; setContentType() : spcifie le type des donnes contenues dans la rponse ; nous retrouvons ensuite les deux en-ttes HTTP, qu'il faut construire " la main" via des appels setHeader().

Lecture et envoi du fichier


Nous arrivons enfin la dernire tape du processus : la lecture du flux et l'envoi au client ! Commenons par mettre en place proprement l'ouverture des flux : Code : Java - com.sdzee.servlets.Download /* Prpare les flux */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux */ entree = new BufferedInputStream( new FileInputStream( fichier ), TAILLE_TAMPON ); sortie = new BufferedOutputStream( response.getOutputStream(), TAILLE_TAMPON ); /* ... */ } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } }

Nous pourrions ici trs bien utiliser directement les flux de type FileInputStream et ServletOutputStream, mais les objets BufferedInputStream et BufferedOutputStream permettent via l'utilisation d'une mmoire tampon une gestion plus souple de la mmoire disponible sur le serveur :

www.siteduzero.com

Partie 4 : Une application interactive !

347/606

dans le flux entree, nous ouvrons un FileInputStream sur le fichier demand. Nous dcorons ensuite ce flux avec un BufferedInputStream, avec ici un tampon de la mme taille que le tampon mis en place sur la rponse HTTP ; dans le flux sortie, nous rcuprons directement le ServletOutpuStream depuis la mthode getOutputStream() de l'objet HttpServletResponse. Nous dcorons ensuite ce flux avec un BufferedOutputStream, avec l encore un tampon de la mme taille que le tampon mis en place sur la rponse HTTP. Encore une fois, je prends la peine de vous dtailler l'ouverture des flux. N'oubliez jamais de toujours ouvrir les flux dans un bloc try, et de les fermer dans le bloc finally associ.

Ceci fait, il ne nous reste plus qu' mettre en place un tampon et envoyer notre fichier au client, le tout depuis notre bloc try : Code : Java - com.sdzee.servlets.Download /* Lit le fichier et crit son contenu dans la rponse HTTP */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur; while ( ( longueur= entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur ); }

l'aide d'un tableau d'octets jouant le rle de tampon, la boucle mise en place parcourt le fichier et l'crit, morceau par morceau, dans la rponse. Nous y voil finalement : notre servlet de tlchargement est oprationnelle ! V ous pouvez tlcharger son code intgral en cliquant sur ce lien.

Vrification de la solution
Avec une telle servlet mise en place, les clients peuvent dornavant tlcharger sur leur poste les fichiers qui sont prsents sous le rpertoire /fichiers du disque du serveur sur lequel tourne votre application. Je vous l'ai dj prcis dans le chapitre prcdent, en ce qui me concerne, avec la configuration en place sur mon poste, ce rpertoire pointe vers c:\fichiers . Ainsi, si je veux qu'un client puisse tlcharger un fichier test.txt depuis mon application, il me suffit de le placer dans c:\fichiers ! Le client devra alors saisir l'URL http://localhost:8080/pro/fichiers/test.txt dans son navigateur et obtiendra le rsultat suivant si le fichier existe bien sur mon serveur (voir la figure suivante).

www.siteduzero.com

Partie 4 : Une application interactive !

348/606

Et il obtiendra logiquement une erreur 404 si le fichier n'existe pas sur le serveur. De mme, vous pouvez vrifier qu'il obtiendra la mme erreur s'il essaie d'accder la servlet de tlchargement sans prciser un nom de fichier, via l'URL http://localhost:8080/pro/fichiers/.

Une solution plus simple


Je vais maintenant vous faire dcouvrir une manire de faire, bien moins chronophage, mais qui n'existe que sur certains serveurs d'applications. En effet, si nous utilisions le serveur GlassFish en lieu et place de Tomcat, nous n'aurions tout bonnement pas besoin d'crire une servlet ddie au tlchargement de fichiers placs l'extrieur du conteneur ! Comment est-ce possible ?

Eh bien il se trouve que le serveur d'Oracle est livr avec une fonctionnalit trs intressante qui permet littralement de "monter" un rpertoire externe au conteneur dans une application web : les Alternate Document Roots. Le principe est relativement simple : il s'agit d'un systme qui permet de mettre en place une correspondance automatique entre des URL qui suivent un format particulier, et un rpertoire du disque local sur lequel le serveur tourne. Par exemple, pour notre exemple il nous suffirait de dfinir que toute URL respectant le pattern /pro/fichiers/* pointe vers son quivalent c:\fichiers\* . La mise en place de ce mcanisme se fait via l'ajout d'une section dans l'quivalent du fichier web.xml sous GlassFish. Si vous utilisez ce serveur et souhaitez faire le test, je vous laisse parcourir la documentation du systme pour plus d'informations, elle est en anglais mais reste trs accessible, mme pour un dbutant.

L'tat d'un tlchargement


Notre prcdente servlet vous parat peut-tre un peu complique, mais elle ne fait en ralit qu'obtenir un InputStream de la ressource dsire, et l'crire dans l'OutputStream de la rponse HTTP, accompagn d'en-ttes modifis. C'est une approche plutt simpliste, car elle ne permet pas de connatre l'tat du tlchargement en cours : autrement dit, en cas de coupure ct client il est impossible de lui proposer la reprise d'un tlchargement en cours. Pourtant, lorsqu'un utilisateur tlcharge un fichier massif et subit un problme de rseau quelconque en cours de route, par exemple 99% du fichier... il aimerait bien que nous ayons sauvegard l'tat de son prcdent tlchargement, et que nous lui proposions son retour de poursuivre l o il s'tait arrt, et de tlcharger uniquement le 1% restant ! Comment proposer la reprise d'un tlchargement ?

www.siteduzero.com

Partie 4 : Une application interactive !

349/606

Le principe se corse un peu ds lors qu'on souhaite proposer ce type de service. Depuis notre servlet, il faudrait manipuler au minimum trois nouveaux en-ttes de la rponse HTTP afin d'activer cette fonctionnalit. Accept-Ranges : cet en-tte de rponse, lorsqu'il contient la valeur "bytes", informe le client que le serveur supporte les requtes demandant une plage dfinie de donnes. Avec cette information, le client peut alors demander une section particulire d'un fichier travers l'en-tte de requte Range. ETag : cet en-tte de rponse doit contenir une valeur unique permettant d'identifier le fichier concern. Il est possible d'utiliser le systme de votre choix, il n'y a aucune contrainte : tout ce qui importe, c'est que chacune des valeurs associes un fichier soit unique. Certains serveurs utilisent par exemple l'algorithme MD5 pour gnrer un hash bas sur le contenu du fichier, d'autres utilisent une combinaison d'informations propos du fichier (son nom, sa taille, sa date de modification, etc.), d'autres gnrent un hash de cette combinaison... Avec cette information, le client peut alors renvoyer l'identifiant obtenu au serveur travers l'en-tte de requte If-Match ou If-Range, et le serveur peut alors dterminer de quel fichier il est question. Last-Modified : cet en-tte de rponse doit contenir un timestamp , qui reprsente la date de la dernire modification du fichier ct serveur. Avec cette information, le client peut alors renvoyer le timestamp obtenu au serveur travers l'entte de requte If-Unmodified-Since, ou bien l encore If-Range. propos de ce dernier en-tte, note importante : un timestamp Java est prcis la milliseconde prs, alors que le timestamp attendu dans l'en-tte n'est prcis qu' la seconde prs. Afin de combler cet cart d'incertitude, il est donc ncessaire d'ajouter une seconde la date de modification retourne par le client dans sa requte, avant de la traiter.

Bref, vous l'aurez compris la manuvre devient vite bien plus complique ds lors que l'on souhaite raliser quelque chose de plus volu et user-friendly ! Il n'est rien d'insurmontable pour vous, mais ce travail requiert de la patience, une bonne lecture du protocole HTTP, une bonne logique de traitement dans votre servlet et enfin une campagne de tests trs pousse, afin de ne laisser passer aucun cas particulier ni aucune erreur. Quoi qu'il en soit, vous avez ici toutes les informations cls pour mettre en place un tel systme ! En ce qui nous concerne, nous allons, dans le cadre de ce cours, nous contenter de notre servlet simpliste, le principe y est pos et l'intrt pdagogique d'un systme plus complexe serait d'autant plus faible.

Raliser des statistiques


Avec notre servlet en place, nous avons centralis la gestion des fichiers demands par les clients, qui passent dornavant tous par cette unique passerelle de sortie. Si vous envisagez dans une application de raliser des statistiques sur les tlchargements effectus par les utilisateurs, il est intuitif d'envisager la modification de cette servlet pour qu'elle relve les donnes souhaites : combien de tlchargements par fichier, combien de fichiers par utilisateur, combien de tlchargements simultans, la proportion d'images tlcharges par rapport aux autres types de fichiers, etc. Bref, n'importe quelle information dont vous auriez besoin. Seulement comme vous le savez, nous essayons de garder nos contrleurs les plus lgers possibles, et de suivre au mieux MVC. V oil pourquoi cette servlet, unique dans l'application, ne va en ralit pas se charger de cette tche. Dans ce cas, o raliser ce type de traitements ?

Si vous rflchissez bien cette problmatique, la solution est vidente : le composant idal pour s'occuper de ce type de traitements, c'est le filtre ! Il suffit en effet d'en appliquer un sur le pattern /fichiers/* pour que celui-ci ait accs toutes les demandes de tlchargement effectues par les clients. Le nombre et la complexit des traitements qu'il devra raliser dpendront bien videmment des informations que vous souhaitez collecter. En ce qui concerne la manire, vous savez dj que tout va passer par la mthode doFilter() du filtre en question... Le tlchargement de fichiers peut tre gr simplement via une servlet ddie, charge de faire la correspondance entre le pattern d'URL choisi ct public et l'emplacement du fichier physique ct serveur. Elle se charge de transmettre les donnes lues sur le serveur au navigateur du client via la rponse HTTP. Pour permettre un tel transfert, il faut rinitialiser une rponse HTTP, puis dfinir ses en-ttes Content-Type, ContentLength et Content-Disposition. Pour envoyer les donnes au client, il suffit de lire le fichier comme n'importe quel fichier local et de recopier son contenu dans le flux de sortie accessible via la mthode response.getOutputStream().

www.siteduzero.com

Partie 4 : Une application interactive !

350/606

TP Fil rouge - tape 5


Dans cette cinquime tape du fil rouge qui clture cette partie du cours, vous allez ajouter un champ au formulaire de cration d'un client, permettant l'utilisateur d'envoyer une image. Servlets d'upload et de download sont au programme !

Objectifs Fonctionnalits
V otre mission cette fois est de permettre l'utilisateur d'envoyer une image lors de la cration d'un client via le formulaire existant. V otre application devra ensuite vrifier que le fichier envoy est bien une image, et vous en profiterez pour vrifier que le poids de l'image ne dpasse pas 1 Mo avant de l'enregistrer dans un dossier externe au conteneur web. Bien entendu, l'objectif suivant va tre d'afficher un lien vers cette image sur la liste des clients existants. Au clic sur ce lien, l'utilisateur pourra alors visualiser l'image associe au client. Enfin, vous devrez veiller ce que l'utilisateur puisse toujours procder la cration d'un client depuis le formulaire de cration d'une commande, comme c'tait dj le cas jusqu' prsent.

Exemples de rendus
Formulaire de cration d'un client, ici rempli avec des donnes de test :

Page rcapitulative aprs le succs de la cration d'un client comportant une image nomme tuto.png :

www.siteduzero.com

Partie 4 : Une application interactive !

351/606

Liste des clients crs, avec un client comportant une image et l'autre non :

Erreur lors de la cration d'un client, ici depuis le formulaire de cration d'une commande, lorsque le fichier envoy n'est pas une image :

www.siteduzero.com

Partie 4 : Une application interactive !

352/606

Erreur lors de la cration d'un client, ici depuis le formulaire de cration d'un client, lorsque le fichier est trop volumineux :

www.siteduzero.com

Partie 4 : Une application interactive !

353/606

propos de cette dernire capture d'cran, vous remarquerez que lorsque la taille maximale dfinie pour un fichier est dpasse, toutes les informations saisies dans le reste du formulaire disparaissent. Ne vous inquitez pas, il s'agit du comportement normal de Tomcat dans ce cas d'utilisation : une IllegalStateException est leve, et les appels aux mthodes getParameter() renvoient tous null. Ce n'est pas trs ergonomique, mais nous nous en contenterons dans le cadre de ce TP.

Conseils Envoi du fichier


Premire tape, la modification du formulaire existant. V ous allez devoir reprendre le code du fragment de JSP contenant les champs dcrivant un client, et y ajouter un champ de type <input type="file"> pour permettre l'utilisateur d'envoyer une image. En consquence, vous allez devoir ajouter un attribut enctype aux balises <form> dans les formulaires des deux JSP responsables de la cration d'un client et d'une commande, afin qu'elles grent correctement les requtes contenant des donnes sous forme de fichiers.

Validation et enregistrement du fichier


Ct serveur, vous allez devoir analyser les donnes reues dans le nouveau paramtre de requte correspondant au champ de type fichier. Pour commencer, faites en sorte que ce champ soit optionnel, autrement dit que l'utilisateur puisse le laisser vide, qu'il ne soit pas oblig d'envoyer un fichier lors de la cration d'un client. Si un fichier est envoy, alors vous allez devoir : vrifier que le poids du fichier envoy ne dpasse pas 1 Mo ; vrifier que le fichier envoy est bien une image ; enregistrer le fichier dans un rpertoire du disque local, en dehors du conteneur ; ajouter le chemin vers l'image dans le bean Client, afin de pouvoir retrouver l'image par la suite.

Le poids du fichier
Comme je vous l'ai appris, avec l'API servlet 3.0 c'est trs simple : les contraintes de taille sont imposes par la dclaration de la servlet dans le fichier web.xml , par l'intermdiaire de la section <multipart-config>. C'est donc ici que vous allez devoir limiter 1 Mo la taille maximale d'un fichier envoy. De mme, vous savez que Tomcat enverra une IllegalStateException en cas de dpassement des limites dfinies. V ous savez donc ce qu'il vous reste faire pour renvoyer un message d'erreur prcis l'utilisateur, en cas d'envoi d'un fichier trop volumineux !

Le type du fichier
Il s'agit de l'tape la plus dlicate raliser. La solution la plus lgre consiste se baser uniquement sur l'extension du fichier envoy par l'utilisateur, mais comme vous vous en doutez, ce n'est pas la solution que je vous demande d'adopter. Souvenezvous : ne faites jamais confiance l'utilisateur ! Qui vous dit qu'un d'entre eux ne va pas envoyer un prtendu fichier image contenant en ralit un excutable, une archive, un script ou que sais-je encore ?... Ainsi, vous allez devoir mettre en place un moyen plus efficace pour dterminer le type rel du fichier transmis. Pas de panique, il existe des bibliothques qui se chargent de tout cela pour vous ! Je vous conseille ici l'utilisation de MimeUtil, car c'est probablement celle qui prsente le moins de dpendances externes. V oici les liens de tlchargement des deux jar ncessaires son bon fonctionnement : MimeUtil SLF4J Il vous suffit de les dposer tous deux dans le rpertoire /WEB-INF/lib de votre projet. Je vous donne ci-dessous un exemple d'utilisation de la bibliothque, vrifiant si un fichier est une image : Code : Java

www.siteduzero.com

Partie 4 : Une application interactive !


/* Extraction du type MIME du fichier depuis l'InputStream nomm "contenu" */ MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" ); Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenu ); /* * Si le fichier est bien une image, alors son en-tte MIME * commence par la chane "image" */ if ( mimeTypes.toString().startsWith( "image" ) ) { /* Appeler ici la mthode d'criture du fichier sur le disque... */ } else { /* Envoyer ici une exception prcisant que le fichier doit tre une image... */ }

354/606

L'enregistrement du fichier
Rien de nouveau ici, cela se passe exactement comme nous l'avons fait dans le cours. V ous allez devoir mettre en place une mthode ddie l'criture du fichier sur le disque, en manipulant proprement les flux (n'oubliez pas la traditionnelle structure try/catch/finally) et en grant les diffrentes erreurs possibles.

Le chemin du fichier
V ous devez, pour terminer, sauvegarder le chemin de l'image dans le bean Client. Il vous faudra donc le modifier pour y ajouter une proprit de type String que vous pouvez par exemple nommer image.

Affichage d'un lien vers l'image


Depuis votre JSP, vous allez devoir rcuprer le chemin vers l'image que vous avez plac dans le bean Client, et en faire un lien vers la servlet de tlchargement que vous allez par la suite mettre en place. Pour obtenir le rendu affich dans le paragraphe prcdent, il vous suffit d'ajouter au tableau gnr par la page listerClients.jsp une colonne, qui contiendra un lien HTML vers l'image si une image existe, et rien sinon. V ous pouvez utiliser pour cela une simple condition <c:if>, testant si la proprit image du bean Client est vide ou non. Si elle n'est pas vide, alors vous afficherez un lien dont l'URL pourra par exemple prendre la forme /pro/images/nomDuFichier.ext, que vous gnrerez bien entendu via la balise <c:url>.

R-affichage de l'image
La dernire tape du TP consiste crer la servlet de tlchargement des images, qui va se charger de faire la correspondance entre l'URL que vous avez cre dans votre JSP - celle de la forme /pro/images/nomDuFichier.ext - et le rpertoire du disque local dans lequel sont stockes les images. Elle va ressembler trs fortement la servlet de download que vous avez mise en place dans le chapitre prcdent, ceci prs qu'elle va cette fois uniquement traiter des images ; vous allez donc pouvoir modifier l'en-tte "Content-Disposition" de "attachment" vers "inline". Ainsi, le navigateur du client va afficher directement l'image aprs un clic sur le lien "V oir" que vous avez mis en place, et ne va plus ouvrir une fentre "Enregistrer sous..." comme c'tait le cas avec la servlet de tlchargement de fichiers.

Correction
Faites attention bien modifier tous les fichiers ncessaires au bon fonctionnement du systme, vous pouvez relire les deux prcdents chapitres pour vous assurer de ne rien oublier. Comme toujours, ce n'est pas la seule manire de faire, le principal est que votre solution respecte les consignes que je vous ai donnes ! Par ailleurs, vous allez probablement devoir adapter cette correction la configuration de votre poste, car comme vous le savez, les dclarations des chemins dans le fichier web.xml dpendent en partie des rpertoires externes que vous utilisez.

www.siteduzero.com

Partie 4 : Une application interactive !

355/606

Prenez le temps de rflchir, de chercher et coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire les prcdents chapitres. La pratique est trs importante, ne vous ruez pas sur la solution !

Le code de l'objet mtier


Secret (cliquez pour afficher) Code : Java - com.sdzee.tp.forms.CreationClientForm package com.sdzee.tp.forms; import import import import import import import import import java.io.BufferedInputStream ; java.io.BufferedOutputStream ; java.io.File; java.io.FileOutputStream ; java.io.IOException; java.io.InputStream ; java.util.Collection; java.util.HashMap; java.util.Map;

import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import com.sdzee.tp.beans.Client; import eu.medsea.mimeutil.MimeUtil; public final class private static private static private static private static "telephoneClient"; private static private static CreationClientForm { final String CHAMP_NOM final String CHAMP_PRENOM final String CHAMP_ADRESSE final String CHAMP_TELEPHONE final String CHAMP_EMAIL final String CHAMP_IMAGE TAILLE_TAMPON = "nomClient"; = "prenomClient"; = "adresseClient"; = = "emailClient"; = "imageClient"; = 10240;

private static final int // 10ko

private String resultat; private Map<String, String> erreurs HashMap<String, String>(); public Map<String, String> getErreurs() { return erreurs; } public String getResultat() { return resultat; }

= new

public Client creerClient( HttpServletRequest request, String chemin ) { String nom = getValeurChamp( request, CHAMP_NOM ); String prenom = getValeurChamp( request, CHAMP_PRENOM ); String adresse = getValeurChamp( request, CHAMP_ADRESSE ); String telephone = getValeurChamp( request, CHAMP_TELEPHONE ); String email = getValeurChamp( request, CHAMP_EMAIL ); String image = null; Client client = new Client(); try { validationNom( nom );

www.siteduzero.com

Partie 4 : Une application interactive !


} catch ( FormValidationException e ) { setErreur( CHAMP_NOM, e.getMessage() ); } client.setNom( nom ); try { validationPrenom( prenom ); } catch ( FormValidationException e ) { setErreur( CHAMP_PRENOM, e.getMessage() ); } client.setPrenom( prenom ); try { validationAdresse( adresse ); } catch ( FormValidationException e ) { setErreur( CHAMP_ADRESSE, e.getMessage() ); } client.setAdresse( adresse ); try { validationTelephone( telephone ); } catch ( FormValidationException e ) { setErreur( CHAMP_TELEPHONE, e.getMessage() ); } client.setTelephone( telephone ); try { validationEmail( email ); } catch ( FormValidationException e ) { setErreur( CHAMP_EMAIL, e.getMessage() ); } client.setEmail( email ); try { image = validationImage( request, chemin ); } catch ( FormValidationException e ) { setErreur( CHAMP_IMAGE, e.getMessage() ); } client.setImage( image ); if ( erreurs.isEmpty() ) { resultat = "Succs de la cration du client."; } else { resultat = "chec de la cration du client."; } } return client;

356/606

private void validationNom( String nom ) throws FormValidationException { if ( nom != null ) { if ( nom.length() < 2 ) { throw new FormValidationException( "Le nom d'utilisateur doit contenir au moins 2 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer un nom d'utilisateur." ); } } private void validationPrenom( String prenom ) throws FormValidationException { if ( prenom != null && prenom.length() < 2 ) { throw new FormValidationException( "Le prnom d'utilisateur doit contenir au moins 2 caractres." ); } }

www.siteduzero.com

Partie 4 : Une application interactive !


private void validationAdresse( String adresse ) throws FormValidationException { if ( adresse != null ) { if ( adresse.length() < 10 ) { throw new FormValidationException( "L'adresse de livraison doit contenir au moins 10 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer une adresse de livraison." ); } } private void validationTelephone( String telephone ) throws FormValidationException { if ( telephone != null ) { if ( !telephone.matches( "^\\d+$" ) ) { throw new FormValidationException( "Le numro de tlphone doit uniquement contenir des chiffres." ); } else if ( telephone.length() < 4 ) { throw new FormValidationException( "Le numro de tlphone doit contenir au moins 4 chiffres." ); } } else { throw new FormValidationException( "Merci d'entrer un numro de tlphone." ); } } private void validationEmail( String email ) throws FormValidationException { if ( email != null && !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new FormValidationException( "Merci de saisir une adresse mail valide." ); } } private String validationImage( HttpServletRequest request, String chemin ) throws FormValidationException { /* * Rcupration du contenu du champ image du formulaire. Il faut ici * utiliser la mthode getPart(). */ String nomFichier = null; InputStream contenuFichier = null; try { Part part = request.getPart( CHAMP_IMAGE ); nomFichier = getNomFichier( part ); /* * Si la mthode getNomFichier() a renvoy quelque chose, il s'agit * donc d'un champ de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { /* * Antibug pour Internet Explorer, qui transmet pour une raison * mystique le chemin du fichier local la machine du client... * * Ex : C:/dossier/sous-dossier/fichier.ext * * On doit donc faire en sorte de ne slectionner que le nom et * l'extension du fichier, et de se dbarrasser du superflu. */ nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ) .substring( nomFichier.lastIndexOf( '\\' ) + 1 );

357/606

www.siteduzero.com

Partie 4 : Une application interactive !


/* Rcupration du contenu du fichier */ contenuFichier = part.getInputStream(); l'InputStream */ /* Extraction du type MIME du fichier depuis

358/606

MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" ); Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenuFichier ); /* * Si le fichier est bien une image, alors son en-tte MIME * commence par la chane "image" */ if ( mimeTypes.toString().startsWith( "image" ) ) { /* Ecriture du fichier sur le disque */ ecrireFichier( contenuFichier, nomFichier, chemin ); } else { throw new FormValidationException( "Le fichier envoy doit tre une image." ); } } } catch ( IllegalStateException e ) { /* * Exception retourne si la taille des donnes dpasse les limites * dfinies dans la section <multipart-config> de la dclaration de * notre servlet d'upload dans le fichier web.xml */ e.printStackTrace(); throw new FormValidationException( "Le fichier envoy ne doit pas dpasser 1Mo." ); } catch ( IOException e ) { /* * Exception retourne si une erreur au niveau des rpertoires de * stockage survient (rpertoire inexistant, droits d'accs * insuffisants, etc.) */ e.printStackTrace(); throw new FormValidationException( "Erreur de configuration du serveur." ); } catch ( ServletException e ) { /* * Exception retourne si la requte n'est pas de type * multipart/form-data. */ e.printStackTrace(); throw new FormValidationException( "Ce type de requte n'est pas support, merci d'utiliser le formulaire prvu pour envoyer votre fichier." ); } } return nomFichier;

/* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu

www.siteduzero.com

Partie 4 : Une application interactive !


* sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } /* * Mthode utilitaire qui a pour unique but d'analyser l'en-tte * "content-disposition", et de vrifier si le paramtre "filename" y est * prsent. Si oui, alors le champ trait est de type File et la mthode * retourne son nom, sinon il s'agit d'un champ de formulaire classique et * la mthode retourne null. */ private static String getNomFichier( Part part ) { /* Boucle sur chacun des paramtres de l'en-tte "content-disposition". */ for ( String contentDisposition : part.getHeader( "content-disposition" ).split( ";" ) ) { /* Recherche de l'ventuelle prsence du paramtre "filename". */ if ( contentDisposition.trim().startsWith( "filename" ) ) { /* * Si "filename" est prsent, alors renvoi de sa valeur, * c'est--dire du nom de fichier sans guillemets. */ return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ).trim().replace( "\"", "" ); } } /* Et pour terminer, si rien n'a t trouv... */ return null; } /* * Mthode utilitaire qui a pour but d'crire le fichier pass en paramtre * sur le disque, dans le rpertoire donn et avec le nom donn. */ private void ecrireFichier( InputStream contenuFichier, String nomFichier, String chemin ) throws FormValidationException { /* Prpare les flux. */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux. */ entree = new BufferedInputStream( contenuFichier, TAILLE_TAMPON ); sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), TAILLE_TAMPON ); /* * Lit le fichier reu et crit son contenu dans un fichier sur le * disque. */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur = 0; while ( ( longueur = entree.read( tampon ) ) > 0 ) {

359/606

www.siteduzero.com

Partie 4 : Une application interactive !


sortie.write( tampon, 0, longueur ); } } catch ( Exception e ) { throw new FormValidationException( "Erreur lors de l'criture du fichier sur le disque." ); } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } } } }

360/606

Le code des servlets


Secret (cliquez pour afficher) Code : XML <?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>Set Character Encoding</filter-name> <filterclass>org.apache.catalina.filters.SetCharacterEncodingFilter</filterclass> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>ignore</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>CreationClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationClient</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> <multipart-config> <location>c:/fichiers/images</location> <max-file-size>2097152</max-file-size> <!-- 2 Mo --> <max-request-size>10485760</max-request-size> <!-- 5 x 2Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet> <servlet> <servlet-name>ListeClients</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeClients</servlet-class> </servlet> <servlet>

www.siteduzero.com

Partie 4 : Une application interactive !


<servlet-name>SuppressionClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.SuppressionClient</servletclass> </servlet> <servlet> <servlet-name>CreationCommande</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationCommande</servletclass> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> <multipart-config> <location>c:/fichiers/images</location> <max-file-size>2097152</max-file-size> <!-- 2 Mo --> <max-request-size>10485760</max-request-size> <!-- 5 x 2Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet> <servlet> <servlet-name>ListeCommandes</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeCommandes</servlet-class> </servlet> <servlet> <servlet-name>SuppressionCommande</servlet-name> <servlet-class>com.sdzee.tp.servlets.SuppressionCommande</servletclass> </servlet> <servlet> <servlet-name>Image</servlet-name> <servlet-class>com.sdzee.tp.servlets.Image</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>CreationClient</servlet-name> <url-pattern>/creationClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeClients</servlet-name> <url-pattern>/listeClients</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SuppressionClient</servlet-name> <url-pattern>/suppressionClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>CreationCommande</servlet-name> <url-pattern>/creationCommande</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeCommandes</servlet-name> <url-pattern>/listeCommandes</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SuppressionCommande</servlet-name> <url-pattern>/suppressionCommande</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Image</servlet-name> <url-pattern>/images/*</url-pattern> </servlet-mapping> </web-app>

361/606

www.siteduzero.com

Partie 4 : Une application interactive !


Code : Java - com.sdzee.tp.servlets.Image package com.sdzee.tp.servlets; import import import import import import import import import import java.io.BufferedInputStream ; java.io.BufferedOutputStream ; java.io.File; java.io.FileInputStream ; java.io.IOException; java.net.URLDecoder; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

362/606

public class Image extends HttpServlet { public static final int TAILLE_TAMPON = 10240; // 10ko public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( "chemin" ); /* * Rcupration du chemin du fichier demand au sein de l'URL de la * requte */ String fichierRequis = request.getPathInfo(); /* Vrifie qu'un fichier a bien t fourni */ if ( fichierRequis == null || "/".equals( fichierRequis )

) {

/* * Si non, alors on envoie une erreur 404, qui signifie que la * ressource demande n'existe pas */ response.sendError( HttpServletResponse.SC_NOT_FOUND ); return; } /* * Dcode le nom de fichier rcupr, susceptible de contenir des * espaces et autres caractres spciaux, et prpare l'objet File */ fichierRequis = URLDecoder.decode( fichierRequis, "UTF-8" ); File fichier = new File( chemin, fichierRequis ); /* Vrifie que le fichier existe bien */ if ( !fichier.exists() ) { /* * Si non, alors on envoie une erreur 404, qui signifie que la * ressource demande n'existe pas */ response.sendError( HttpServletResponse.SC_NOT_FOUND ); return; }

www.siteduzero.com

Partie 4 : Une application interactive !


/* Rcupre le type du fichier */ String type = getServletContext().getMimeType( fichier.getName() ); /* * Si le type de fichier est inconnu, alors on initialise un type par * dfaut */ if ( type == null ) { type = "application/octet-stream"; } /* Initialise la rponse HTTP */ response.reset(); response.setBufferSize( TAILLE_TAMPON ); response.setContentType( type ); response.setHeader( "Content-Length", String.valueOf( fichier.length() ) ); response.setHeader( "Content-Disposition", "inline; filename=\"" + fichier.getName() + "\"" ); /* Prpare les flux */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux */ entree = new BufferedInputStream( new FileInputStream( fichier ), TAILLE_TAMPON ); sortie = new BufferedOutputStream( response.getOutputStream(), TAILLE_TAMPON ); /* Lit le fichier et crit son contenu dans la rponse HTTP */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur; while ( ( longueur = entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur ); } } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } } } }

363/606

Le code des JSP


Secret (cliquez pour afficher) Code : JSP - listerClients.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head>

www.siteduzero.com

Partie 4 : Une application interactive !


<meta charset="utf-8" /> <title>Liste des clients existants</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <c:choose> <%-- Si aucun client n'existe en session, affichage d'un message par dfaut. --%> <c:when test="${ empty sessionScope.clients }"> <p class="erreur">Aucun client enregistr.</p> </c:when> <%-- Sinon, affichage du tableau. --%> <c:otherwise> <table> <tr> <th>Nom</th> <th>Prnom</th> <th>Adresse</th> <th>Tlphone</th> <th>Email</th> <th>Image</th> <th class="action">Action</th> </tr> <%-- Parcours de la Map des clients en session, et utilisation de l'objet varStatus. --%> <c:forEach items="${ sessionScope.clients }" var="mapClients" varStatus="boucle"> <%-- Simple test de parit sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%> <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}"> <%-- Affichage des proprits du bean Client, qui est stock en tant que valeur de l'entre courante de la map -%> <td><c:out value="${ mapClients.value.nom }"/></td> <td><c:out value="${ mapClients.value.prenom }"/></td> <td><c:out value="${ mapClients.value.adresse }"/></td> <td><c:out value="${ mapClients.value.telephone }"/></td> <td><c:out value="${ mapClients.value.email }"/></td> <td> <%-- On ne construit et affiche un lien vers l'image que si elle existe. --%> <c:if test="${ !empty mapClients.value.image }"> <c:set var="image"><c:out value="${ mapClients.value.image }"/></c:set> <a href="<c:url value="/images/${ image }"/>">Voir</a> </c:if> </td> <%-- Lien vers la servlet de suppression, avec passage du nom du client - c'est--dire la cl de la Map - en paramtre grce la balise <c:param/>. --%> <td class="action"> <a href="<c:url value="/suppressionClient"><c:param name="nomClient" value="${ mapClients.key }" /></c:url>"> <img src="<c:url value="/inc/supprimer.png"/>" alt="Supprimer" /> </a> </td>

364/606

www.siteduzero.com

Partie 4 : Une application interactive !


</tr> </c:forEach> </table> </c:otherwise> </c:choose> </div> </body> </html>

365/606

Code : JSP - /inc/inc_client_form.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <label for="nomClient">Nom <span class="requis">*</span></label> <input type="text" id="nomClient" name="nomClient" value="<c:out value="${client.nom}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['nomClient']}</span> <br /> <label for="prenomClient">Prnom </label> <input type="text" id="prenomClient" name="prenomClient" value="<c:out value="${client.prenom}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['prenomClient']}</span> <br /> <label for="adresseClient">Adresse de livraison <span class="requis">*</span></label> <input type="text" id="adresseClient" name="adresseClient" value="<c:out value="${client.adresse}"/>" size="30" maxlength="60" /> <span class="erreur">${form.erreurs['adresseClient']}</span> <br /> <label for="telephoneClient">Numro de tlphone <span class="requis">*</span></label> <input type="text" id="telephoneClient" name="telephoneClient" value="<c:out value="${client.telephone}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['telephoneClient']}</span> <br /> <label for="emailClient">Adresse email</label> <input type="email" id="emailClient" name="emailClient" value="<c:out value="${client.email}"/>" size="30" maxlength="60" /> <span class="erreur">${form.erreurs['emailClient']}</span> <br /> <label for="imageClient">Image</label> <input type="file" id="imageClient" name="imageClient" /> <span class="erreur">${form.erreurs['imageClient']}</span> <br />

Code : JSP - creerClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'un client</title>

www.siteduzero.com

Partie 4 : Une application interactive !


<link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationClient"/>" enctype="multipart/form-data"> <fieldset> <legend>Informations client</legend> <c:import url="/inc/inc_client_form.jsp" /> </fieldset> <p class="info">${ form.resultat }</p> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> </body> </html>

366/606

Code : JSP - creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationCommande"/>" enctype="multipart/form-data"> <fieldset> <legend>Informations client</legend> <%-- Si et seulement si la Map des clients en session n'est pas vide, alors on propose un choix l'utilisateur --%> <c:if test="${ !empty sessionScope.clients }"> <label for="choixNouveauClient">Nouveau client ? <span class="requis">*</span></label> <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="nouveauClient" checked /> Oui <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="ancienClient" /> Non <br/><br /> </c:if> scope="request" /> <c:set var="client" value="${ commande.client }" <div id="nouveauClient"> <c:import url="/inc/inc_client_form.jsp" /> </div>

<%-- Si et seulement si la Map des clients en session n'est pas vide, alors on cre la liste droulante --%> <c:if test="${ !empty sessionScope.clients }"> <div id="ancienClient"> <select name="listeClients" id="listeClients"> <option value="">Choisissez un client...</option> <%-- Boucle sur la map des clients --%> <c:forEach items="${ sessionScope.clients }" var="mapClients">

www.siteduzero.com

Partie 4 : Une application interactive !


var="mapClients">

367/606

<%-- L'expression EL ${mapClients.value} permet de cibler l'objet Client stock en tant que valeur dans la Map, et on cible ensuite simplement ses proprits nom et prenom comme on le ferait avec n'importe quel bean. --%> <option value="${ mapClients.value.nom }">${ mapClients.value.prenom } ${ mapClients.value.nom }</option> </c:forEach> </select> </div> </c:if> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="v" name="dateCommande" value="<c:out value="${commande.date}"/>" size="30" maxlength="30" disabled /> <span class="erreur">${form.erreurs['dateCommande']}</span> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="<c:out value="${commande.montant}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['montantCommande']}</span> <br /> <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label> <input type="text" id="modePaiementCommande" name="modePaiementCommande" value="<c:out value="${commande.modePaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modePaiementCommande']}</span> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="<c:out value="${commande.statutPaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutPaiementCommande']}</span> <br /> <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="<c:out value="${commande.modeLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modeLivraisonCommande']}</span> <br /> livraison</label> <label for="statutLivraisonCommande">Statut de la

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="<c:out value="${commande.statutLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutLivraisonCommande']}</span> <br /> <p class="info">${ form.resultat }</p> </fieldset> <input type="submit" value="Valider" />

www.siteduzero.com

Partie 4 : Une application interactive !


<input type="reset" value="Remettre zro" /> <br /> </form> </div>

368/606

<%-- Inclusion de la bibliothque jQuery. Vous trouverez des cours sur JavaScript et jQuery aux adresses suivantes : - http://www.siteduzero.com/tutoriel-3-309961-dynamisez-vossites-web-avec-javascript.html - http://www.siteduzero.com/tutoriel-3-659477-un-site-webdynamique-avec-jquery.html Si vous ne souhaitez pas tlcharger et ajouter jQuery votre projet, vous pouvez utiliser la version fournie directement en ligne par Google : <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script --%> <script src="<c:url value="/inc/jquery.js"/>"></script> <%-- Petite fonction jQuery permettant le remplacement de la premire partie du formulaire par la liste droulante, au clic sur le bouton radio. --%> <script> jQuery(document).ready(function(){ /* 1 - Au lancement de la page, on cache le bloc d'lments du formulaire correspondant aux clients existants */ $("div#ancienClient").hide(); /* 2 - Au clic sur un des deux boutons radio "choixNouveauClient", on affiche le bloc d'lments correspondant (nouveau ou ancien client) */ jQuery('input[name=choixNouveauClient]:radio').click(function(){ $("div#nouveauClient").hide(); $("div#ancienClient").hide(); var divId = jQuery(this).val(); $("div#"+divId).show(); }); }); </script> </body> </html>

Code : JSP - afficherClient.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'un client</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ form.resultat }</p> <p>Nom : <c:out value="${ client.nom }"/></p> <p>Prnom : <c:out value="${ client.prenom }"/></p> <p>Adresse : <c:out value="${ client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ client.telephone }"/></p> <p>Email : <c:out value="${ client.email }"/></p> <p>Image : <c:out value="${ client.image }"/></p> </div> </body> </html>

www.siteduzero.com

Partie 4 : Une application interactive !

369/606

Code : JSP - afficherCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ form.resultat }</p> <p>Client</p> <p>Nom : <c:out value="${ commande.client.nom }"/></p> <p>Prnom : <c:out value="${ commande.client.prenom }"/></p> <p>Adresse : <c:out value="${ commande.client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ commande.client.telephone }"/></p> <p>Email : <c:out value="${ commande.client.email }"/></p> <p>Image : <c:out value="${ commande.client.image }"/></p> <p>Commande</p> <p>Date : <c:out value="${ commande.date }"/></p> <p>Montant : <c:out value="${ commande.montant }"/></p> <p>Mode de paiement : <c:out value="${ commande.modePaiement }"/></p> <p>Statut du paiement : <c:out value="${ commande.statutPaiement }"/></p> <p>Mode de livraison : <c:out value="${ commande.modeLivraison }"/></p> <p>Statut de la livraison : <c:out value="${ commande.statutLivraison }"/></p> </div> </body> </html>

Nous avons pris les choses par le bon bout, en faisant de gros efforts d'organisation et de dcoupage du code pour suivre les recommandations MVC. C'est un trs gros morceau que nous venons d'engloutir, mais nous sommes loin d'avoir termin et j'espre que vous avez encore de l'apptit ! Ce qu'il nous faut maintenant, c'est un moyen de stocker nos donnes : en route vers les bases de donnes...

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

370/606

Partie 5 : Les bases de donnes avec Java EE


Cette partie est consacre entirement la gestion des donnes dans une application Java EE. Nous commencerons par rappeler de quoi est constitue une base de donnes, comment la faire interagir avec notre application, puis nous tudierons les requtes principales, avant de dcouvrir que notre modle devra tre divis en deux couches distinctes : la couche mtier et la couche donnes.

Introduction MySQL et JDBC


Ce chapitre d'introduction est une entre en matire qui a pour objectifs de vous prsenter rapidement les raisons d'tre et les composants d'une base de donnes, vous guider dans son installation et vous expliquer comment une application Java communique avec elle. L encore, nous allons survoler un sujet extrmement vaste et il faudrait un cours complet pour traiter proprement le sujet. Et puisque le hasard fait trs bien les choses sur le Site du Zro, Taguan a justement rdig un excellent tutoriel de prise en main de la technologie que nous allons utiliser, que je vous encourage bien videmment lire avant de suivre ce chapitre : Administrez vos bases de donnes avec MySQL.

Prsentation des bases de donnes


Une base de donnes... pourquoi ?

V ous connaissez dj plusieurs moyens de stocker des informations depuis votre application : dans des objets et leurs attributs, mais ceux-ci ne restent en mmoire que de manire temporaire, cette dure tant dtermine par leur porte. Au final, lorsque le serveur d'applications est arrt, toutes les donnes sont perdues ; dans des fichiers , dans lesquels vous savez crire en manipulant les flux d'entre et sortie. Les donnes ainsi crites sur le disque ont le mrite d'tre sauvegardes de manire permanente, et sont accessibles peu importe que l'application soit en ligne ou non. Le souci et vous le savez, c'est que cela devient vite trs compliqu ds que vous avez beaucoup de donnes enregistrer et grer. Dans une application web, vous ne pouvez pas y couper, vous devez grer une grande quantit de donnes : pour un site comme le Site du Zro, il faut par exemple enregistrer et grer les informations concernant les membres, les articles et tutoriels crits dans les sections news et cours, les sujets et rponses crits dans le forum, les offres d'emploi, les livres en vente, etc. Toutes ces donnes sont sans arrt lues, crites, modifies ou supprimes, et ce serait mission impossible sans un systme de stockage efficace. Ce systme miracle, c'est la base de donnes : elle permet d'enregistrer des donnes de faon organise et hirarchise.

Structure
La base de donnes (BDD, ou DB en anglais) est un systme qui enregistre des informations, mais pas n'importe comment : ces informations sont toujours classes. Et c'est a qui fait que la BDD est si pratique : c'est un moyen extrmement simple de ranger des informations ! Grossirement, une BDD peut tre vue comme un ensemble de tableaux, des structures contenant donc des lignes et des colonnes et dans lesquelles nos donnes sont ranges. Il existe un vocabulaire spcifique pour dsigner les diffrents lments composant une BDD : la base dsigne le volume englobant l'ensemble, la bote qui contient tous les tableaux ; une table dsigne un tableau de donnes, elle contient des lignes et des colonnes ; une entre dsigne une ligne ; un champ dsigne une colonne. En rsum, une base peut contenir plusieurs tables , qui peuvent contenir plusieurs entres , pouvant leur tour contenir plusieurs champs . V oici par exemple ce quoi pourrait ressembler une table regroupant des informations concernant les membres d'un site : id 1 pseudo Coyote email coyote@bipbip.com age 25

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


2 3 4 Thunderseb jadorejquery@unefois.be 24 Kokotchy Marcel decapsuleur@biere.org marcel@laposte.net ... 27 47 ...

371/606

... ...

V ous voyez bien ici qu'une table se reprsente parfaitement par un simple tableau. Dans cet exemple : les champs sont les ttes de colonne, savoir "id", "pseudo", "email" et "age" ; chaque ligne du tableau est une entre de la table ; il n'y a que quatre entres, mais une table peut trs bien en contenir des millions ! quoi sert le champ "id" ?

Il signifie identifiant, et permet de numroter les entres d'une table : mettre en place un tel champ n'est pas une obligation, mais si vous avez lu le chapitre sur les cls primaires du cours de MySQL que je vous ai conseill en introduction, vous savez dj que cette pratique nous sera trs utile lors de la conception de nos tables.

O sont stockes les donnes ?

En effet, c'est bien gentil de tout planquer dans la grosse bote "base de donnes", mais au final o sont enregistrs les tableaux et les donnes qu'ils contiennent ? Eh bien il n'y a rien de magique, tout cela est sauvegard dans... des fichiers crits sur le disque ! Seulement, ce ne sont pas de simples fichiers texte, ils ne sont en aucun cas destins tre dits la main par le dveloppeur : leur format est bien particulier et dpend du systme de gestion utilis. La reprsentation en tableaux utilise prcdemment pour vous faire comprendre comment fonctionne une table ne doit pas vous induire en erreur : sous la couverture, les donnes sont ordonnes de manire bien plus complexe !

SGBD
Dans le dernier paragraphe, je vous ai parl de systme de gestion, qu'on raccourcit en SGBD. V ous devez savoir qu'il n'existe pas qu'une seule solution pour crer et grer des bases de donnes. V oici une liste des principaux acteurs de ce march : MySQL : solution libre et gratuite, c'est le SGBD le plus rpandu. C'est d'ailleurs celui que nous allons utiliser dans ce cours ! PostgreSQL : solution libre et gratuite, moins connue du grand public mais proposant des fonctionnalits inexistantes dans MySQL ; Oracle : solution propritaire et payante, massivement utilise par les grandes entreprises. C'est un des SGBD les plus complets, mais un des plus chers galement ; SQL Server : la solution propritaire de Microsoft ; DB2 : la solution propritaire d'IBM, utilise principalement dans les trs grandes entreprises sur des Mainframes. Comment choisir un SGBD ?

Comme pour tout choix de technologie, la dcision peut tre influence par plusieurs contraintes : cot, performances, support, etc. Retenez simplement que dans la trs grande majorit des cas, MySQL ou PostgreSQL rpondront parfaitement et gratuitement vos besoins !

Comment fonctionne un SGBD ?

Chaque systme utilise un ensemble d'algorithmes pour trier et stocker les informations et pour y accder, via des requtes crites en langage SQL. Un tel ensemble porte un nom bien particulier : le moteur de stockage. Il y a beaucoup dire sur ce sujet : plutt que de paraphraser, je vais vous demander de lire ce cours crit par un autre membre du Site du Zro, expliquant en dtail

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

372/606

quelles sont les solutions existantes pour le systme MySQL, pourquoi elles existent et en quoi elles diffrent : les moteurs de stockages de MySQL. Dans notre projet, nous allons utiliser le moteur InnoDB , un moteur relationnel : il s'assure que les relations mises en place entre les donnes de plusieurs tables sont cohrentes et que si l'on modifie certaines donnes, ces changements seront rpercuts aux tables lies.

SQL
Je vous ai expliqu que les tables et les donnes contenues dans une BDD taient enregistres dans des fichiers, mais que nous ne pouvions pas diter ces fichiers la main. Dans la pratique, nous n'irons en effet jamais toucher ces fichiers directement : nous allons toujours dlguer cette tche MySQL. Dans les coulisses, c'est lui qui se dbrouillera pour classer nos informations. Et c'est bien l tout l'avantage des bases de donnes : nous n'aurons jamais nous soucier de la manire dont seront organises nos donnes, nous nous contenterons de donner des ordres au SGBD. Comment communiquer avec notre SGBD ?

Pour dialoguer avec lui, nous devons lui envoyer des requtes crites en langage SQL. Encore un nouveau langage apprendre... La bonne nouvelle, c'est que le SQL est un standard, c'est--dire que peu importe le SGBD utilis, vous devrez toujours lui parler en SQL. Le hic, c'est que d'un SGBD l'autre, on observe quelques variantes dans la syntaxe des requtes : rassurez-vous toutefois, cela ne concerne gnralement que certaines commandes qui sont peu utilises. Le langage SQL n'est absolument pas li au langage Java : c'est un langage part entire, uniquement destin aux bases de donnes. Pour vous donner une premire ide de la syntaxe employe, voici un exemple de requte SQL : Code : SQL - Exemple SELECT id, nom, prenom, email FROM utilisateurs ORDER BY id;

Nous sommes ici pour apprendre le Java EE, et malheureusement pour vous si vous ne connaissez pas encore les bases du langage SQL, je ne peux pas me permettre de disserter sur le sujet. Heureusement, si vous souhaitez en savoir plus, comme je vous l'ai dj dit en introduction il existe un cours de MySQL complet sur le Site du Zro, qui vous guidera pas pas dans votre apprentissage du langage. Lorsque vous vous sentirez l'aise avec le sujet, vous serez alors capables de donner n'importe quel ordre votre base de donnes, et serez donc capables de comprendre intgralement les exemples de ce cours sans aucune difficult !

Prparation de la base avec MySQL Installation


Pour commencer, vous devez vous rendre sur la page de tlchargement du site de MySQL. Slectionnez alors le systme d'exploitation sur lequel vous travaillez (Windows, Mac OS ou Linux, 32 bits ou 64 bits), et tlchargez la version de MySQL correspondante.

Sous Windows
Le plus simple est de choisir la version MySQL avec l'installeur MSI, et d'excuter le fichier une fois tlcharg. L'assistant dmarrera alors et vous guidera lors de l'installation. Lorsqu'il vous demandera de choisir entre trois types d'installation, vous choisirez "Typical". Arrivs la fin de l'installation, une fentre vous demandera si vous souhaitez lancer l'assistant de configuration MySQL :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

373/606

V ous veillerez bien cliquer sur la case cocher entoure ci-dessus avant de cliquer sur le bouton Finish. Un assistant vous demandera alors de prciser quelles options vous souhaitez activer. Choisissez la configuration standard, et l'tape suivante, cochez l'option "Include Bin Directory in Windows PATH" :

L'outil vous proposera alors de dfinir un nouveau mot de passe pour l'utilisateur "root". Ne cochez aucune autre option cette tape, et cliquez sur Execute pour lancer la configuration.

Sous Linux
Il vous suffit d'ouvrir un terminal et d'excuter la commande suivante pour installer MySQL, ou son quivalent sous les

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


distributions non debian-like : Code : Console sudo apt-get install mysql-server mysql-client

374/606

Une fois l'installation termine, vous pourrez modifier le mot de passe par dfaut de l'utilisateur "root" avec la commande suivante : Code : Console sudo mysqladmin -u root h localhost password 'entrez ici votre mot de passe'

Sous Mac OS
Le plus simple pour ce systme est de tlcharger depuis le site de MySQL l'archive au format DMG. Ouvrez-la et vous y trouverez un fichier PKG dont le nom doit ressembler mysql-5.5.27-osx10.6-x86.pkg , certains chiffres et extensions pouvant varier ou s'ajouter selon la version courante de MySQL lorsque vous procdez au tlchargement, et selon la configuration de votre poste. Il s'agit l de l'assistant d'installation de MySQL : excutez-le, et suivez simplement les instructions qui vous sont donnes. Une fois l'installation termine, deux possibilits s'offrent vous concernant la configuration du serveur : soit vous installez le package MySQL Startup Item, soit vous effectuez les manipulations la main. V ous trouverez de plus amples dtails concernant ces deux options sur cette page du site officiel ddie l'installation sous Mac OS X.

Cration d'une base


Une fois le serveur MySQL install sur votre poste, vous pouvez crer la base de donnes qui va nous servir dans toute la suite du cours. Nous allons en l'occurrence crer une base nomme bdd_sdzee, et initialiser l'encodage par dfaut UTF-8. Souvenezvous, il s'agit l de l'encodage qui permettra votre application de manipuler la plus grande varit de caractres : Code : SQL - Requte de cration de la base CREATE DATABASE bdd_sdzee DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Et titre d'exemple, le rsultat sous Windows XP sur les copies d'cran ci-dessous :

Cration d'un utilisateur


Par dfaut, MySQL propose un compte root qui donne accs l'intgralit du serveur. C'est une trs mauvaise pratique de travailler via ce compte, nous allons donc crer un utilisateur spcifique notre application, qui n'aura accs qu' la base sur laquelle nous travaillons : Code : SQL - Requte de cration de l'utilisateur CREATE USER 'java'@'localhost' IDENTIFIED BY 'SdZ_eE';

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


GRANT ALL ON bdd_sdzee.* TO 'java'@'localhost' IDENTIFIED BY 'SdZ_eE';

375/606

Ici le compte cr se nomme java, a pour mot de passe SdZ_eE et dispose de tous les droits sur la base bdd_sdzee. V ous pouvez bien entendu changer le mot de passe si celui-l ne vous plat pas. Pour l'utiliser, il faut ensuite vous dconnecter via la commande exit , et vous connecter nouveau via la commande mysql -h localhost -u java -p , et enfin entrer la commande use bdd_sdzee :

Cration d'une table


Maintenant que la base est prte et que nous avons cr et utilisons un compte, nous pouvons mettre en place une table qui va nous servir dans les exemples du chapitre venir. Nous allons crer une table qui reprsente des utilisateurs, comportant un identifiant, une adresse mail, un mot de passe, un nom et une date d'inscription : Code : SQL - Requte de cration de la table CREATE TABLE bdd_sdzee.Utilisateur ( id INT( 11 ) NOT NULL AUTO_INCREMENT , email VARCHAR( 60 ) NOT NULL , mot_de_passe VARCHAR( 32 ) NOT NULL , nom VARCHAR( 20 ) NOT NULL , date_inscription DATETIME NOT NULL , PRIMARY KEY ( id ), UNIQUE ( email ) ) ENGINE = INNODB;

V ous pouvez ensuite vrifier la bonne cration de la table nomme Utilisateur avec les commandes SHOW tables; et DESCRIBE Utilisateur; :

Insertion de donnes d'exemple


Afin de pouvoir commencer en douceur la manipulation d'une base de donnes depuis notre application dans le chapitre suivant, nous allons ici directement mettre en place quelques donnes factices, qui nous serviront d'exemple par la suite : Code : SQL - Requtes d'insertion de donnes INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('coyote@mail.acme', MD5('bipbip'), 'Coyote', NOW()); INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription)

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


VALUES ('jadorejquery@unefois.be', MD5('avecdesfrites'), 'Thunderseb', NOW());

376/606

V ous pouvez ensuite vrifier la bonne insertion des donnes dans la table via la commande SELECT * FROM Utilisateur; :

Mise en place de JDBC dans le projet


Nous sommes capables de crer des bases et des tables dans MySQL, mais notre mission maintenant c'est bien entendu d'effectuer la liaison entre MySQL et notre projet Java EE. Car ce que nous souhaitons, c'est pouvoir interagir avec les tables de notre base bdd_sdzee directement depuis le code de notre application !

JDBC
La solution standard se nomme JDBC : c'est une API qui fait partie intgrante de la plate-forme Java, et qui est constitue de classes permettant l'accs depuis vos applications Java des donnes ranges sous forme de tables. Dans la trs grande majorit des cas, il s'agira bien entendu de bases de donnes stockes dans un SGBD ! Les actions rendues possibles par cette API sont : la connexion avec le SGBD ; l'envoi de requtes SQL au SGBD depuis une application Java ; le traitement des donnes et ventuelles erreurs retournes par le SGBD lors des diffrentes tapes du dialogue (connexion, requte, excution, etc.). Seulement, comme vous le savez, il existe plusieurs SGBD diffrents et bien qu'ils se basent tous sur le langage SQL, chacun a sa manire de grer les donnes. L'avantage de JDBC, c'est qu'il est nativement prvu pour pouvoir s'adapter n'importe quel SGBD ! Ainsi pour faire en sorte que notre application puisse dialoguer avec MySQL, nous aurons simplement besoin d'ajouter notre projet un driver qui est spcifique MySQL.

Si nous utilisions PostgreSQL, il nous faudrait, la place, ajouter un driver propre PostgreSQL, etc. Dans la pratique c'est trs simple, il s'agit tout bonnement d'une archive .jar que nous allons ajouter au build-path de notre projet ! Pour information, sachez que JDBC ne fait pas uniquement partie de la plate-forme Java EE, c'est une brique de base de la plate-forme standard Java SE. Cependant, bien que ce cours se consacre l'apprentissage du Java EE, j'ai jug qu'il tait prfrable de s'y attarder, afin que ceux d'entre vous qui ne sont pas habitus crer des applications Java faisant intervenir une base de donnes puissent dcouvrir et appliquer sereinement le principe par la suite. Aprs tout, cette brique rentre parfaitement dans le cadre de ce que nous tudions dans ce cours, savoir la cration d'une application web !

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

377/606

Mise en place
Pour commencer, il faut rcuprer le driver sur la page de tlchargement du site de MySQL.

Tlchargement du driver MySQL Tlchargez alors l'archive au format que vous prfrez. Dcompressez son contenu dans un dossier sur votre poste, il contiendra alors un fichier mysql-connector-java-xxx-bin.jar (les xxx variant selon la version courante au moment o vous effectuez ce tlchargement). Il suffit ensuite de copier ce fichier dans le rpertoire /lib prsent dans le dossier d'installation de votre Tomcat, et tous les projets dploys sur ce serveur pourront alors communiquer avec une base de donnes MySQL. Au lieu de dposer le driver directement dans le dossier de bibliothques du serveur d'applications, vous pouvez galement passer par Eclipse et le placer uniquement dans le rpertoire /WEB-INF/lib d'un projet en particulier. Eclipse va alors automatiquement ajouter l'archive au classpath de l'application concerne. Toutefois, je vous recommande d'utiliser la premire mthode. Cela vous permettra de ne pas avoir inclure le driver dans chacun de vos projets communicant avec une base de donnes MySQL.

Cration d'un bac sable


Afin de pouvoir dcouvrir tranquillement le fonctionnement de JDBC, nous allons mettre en place un espace de tests dans notre application. Comme je vous l'ai dit, JDBC est une brique de la plate-forme Java SE, nous pourrions donc trs bien nous crer un nouveau projet Java part entire et y faire nos tests, avant de revenir notre projet Java EE. Cependant, je prfre vous faire manipuler directement au sein de notre application, a vous permettra de pratiquer une nouvelle fois la mise en place des diffrents composants habituels. Nous allons en l'occurrence avoir besoin : d'un objet Java, qui contiendra le code de nos essais de manipulation de la base de donnes ; d'une servlet, qui se contentera d'appeler les exemples prsents dans l'objet Java ; d'une page JSP, qui affichera enfin les rsultats de nos diffrentes manipulations.

Cration de l'objet Java


Pour commencer, mettons en place un nouvel objet Java nomm TestJDBC, dans un nouveau package nomm com.sdzee.bdd : Code : Java - com.sdzee.bdd.TestJDBC

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


package com.sdzee.bdd; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; public class TestJDBC { /* La liste qui contiendra tous les rsultats de nos essais */ private List<String> messages = new ArrayList<String>(); { public List<String> executerTests( HttpServletRequest request ) /* Ici, nous placerons le code de nos manipulations */ /* ... */ } return messages;

378/606

Celui-ci contient une seule mthode, vide pour le moment. C'est dans cette mthode que nous allons, par la suite, placer le code de nos manipulations, et c'est cette mthode qui sera appele par notre servlet. La seule chose qu'elle retourne est une liste de messages, qui seront rcuprs par la servlet.

Cration de la servlet
Nous avons besoin d'une servlet pour appeler les tests crits dans notre objet Java. Cette servlet sera dclenche par un accs depuis votre navigateur l'URL http://localhost:8080/pro/testjdbc. V oici donc sa dclaration dans le fichier web.xml de notre application : Code : XML - /WEB-INF/web.xml <servlet> <servlet-name>GestionTestJDBC</servlet-name> <servlet-class>com.sdzee.servlets.GestionTestJDBC</servlet-class> </servlet> <servlet-mapping> <servlet-name>GestionTestJDBC</servlet-name> <url-pattern>/testjdbc</url-pattern> </servlet-mapping>

Et voici son code : Code : Java - com.sdzee.servlets.GestionTestJDBC package com.sdzee.servlets; import java.io.IOException; import java.util.List; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.bdd.TestJDBC; public class GestionTestJDBC extends HttpServlet { public static final String ATT_MESSAGES = "messages"; public static final String VUE = "/WEB-

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


INF/test_jdbc.jsp"; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Initialisation de l'objet Java et rcupration des messages */ TestJDBC test = new TestJDBC(); List<String> messages = test.executerTests( request ); /* Enregistrement de la liste des messages dans l'objet requte */ request.setAttribute( ATT_MESSAGES, messages ); /* Transmission vers la page en charge de l'affichage des rsultats */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

379/606

Elle se contentera d'appeler la mthode de notre nouvel objet Java chaque requte GET reue, autrement dit, chaque fois que nous accderons depuis notre navigateur l'URL que nous avons dfinie prcdemment. Elle rcuprera lors de cet appel la liste des messages crs par le code de nos essais, et la transmettra alors une page JSP pour affichage.

Cration de la page JSP


Enfin, nous avons besoin d'une page qui aura pour unique mission d'afficher les messages retourns par nos diffrents tests, que nous allons nommer test_jdbc.jsp et que nous allons placer sous /WEB-INF : Code : JSP - /WEB-INF/test_jdbc.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Tests JDBC</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/form.css"/>" /> </head> <body> <h1>Tests JDBC</h1> <c:forEach items="${ messages }" var="message" varStatus="boucle"> <p>${ boucle.count }. ${ message }</p> </c:forEach> </body> </html> Maintenant que nous sommes au point et avons cr notre base de donnes, nous allons apprendre l'interroger depuis notre projet Java EE.

En rsum
Une base de donnes permet de stocker des donnes de manire organise et hierarchise. L'utilisation de l'encodage UTF-8 pour stocker les donnes d'une application web est trs recommande. Le driver JDBC permet l'interaction entre une application Java (web ou non) et une base de donnes. Il existe un driver diffrent pour chaque type de base de donnes existant. Il s'agit concrtement d'un simple fichier Jar, placer dans le rpertoire /lib du serveur d'applications.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

380/606

Communiquez avec votre BDD


Nous allons, pour commencer, dcouvrir les nouveaux objets et interfaces qui doivent intervenir dans notre application afin d'tablir une communication avec la base de donnes. Nous apprendrons ensuite lire des donnes depuis la base vers notre application, et enfin crire des donnes depuis notre application vers la base. Une fois la thorie assimile, nous mettrons tout cela en musique dans un exemple pratique, avant d'analyser les manques et risques lis la technique employe, et de dcouvrir une seconde manire de procder qui pallie ces lacunes.

Chargement du driver
Nous avons, dans le chapitre prcdent, rcupr le driver JDBC correspondant MySQL, et nous l'avons ajout au classpath du projet. Il nous est maintenant ncessaire de procder ce que l'on nomme le chargement du driver depuis le code de notre application. V oici le code minimal ncessaire : Code : Java - Chargement du driver JDBC MySQL /* Chargement du driver JDBC pour MySQL */ try { Class.forName( "com.mysql.jdbc.Driver" ); } catch ( ClassNotFoundException e ) { /* Grer les ventuelles erreurs ici. */ }

Intressons-nous la ligne 3 de notre exemple, dont le rle est justement de charger le driver. Le nom de ce dernier, en l'occurrence "com.mysql.jdbc.Driver", est fourni par son constructeur, autrement dit ici par MySQL. Si vous utilisez un autre SGBD, vous devrez vous renseigner sur le site de son distributeur pour trouver son nom exact et ainsi pouvoir le charger depuis votre application. Si cette ligne de code envoie une exception de type ClassNotFoundException, cela signifie que le fichier .jar contenant le driver JDBC pour MySQL n'a pas t correctement plac dans le classpath . V ous pourrez d'ailleurs faire le test vous-mmes lorsque nous passerons la pratique, en retirant le driver que nous avons ajout en tant que bibliothque externe, et constater que cette ligne envoie bien une exception ! Notez bien que dans la pratique, il est absolument inutile de charger le driver avant chaque connexion, une seule fois durant le chargement de l'application suffit ! Toutefois pour des raisons de simplicit, dans les exemples de ce chapitre nous ne nous proccuperons pas de ce dtail et chargerons le driver chaque excution de nos tests.

Nous allons maintenant apprendre communiquer avec une base de donnes. Pour ce faire, nous devons suivre le processus suivant : 1. 2. 3. 4. nous connecter la base ; crer et excuter une requte SQL ; analyser son rsultat ; fermer les diffrentes ressources mises en jeu.

Sans plus attendre, dcouvrons tout cela en dtail !

Connexion la base, cration et excution d'une requte


Le contenu qui va suivre est assez dense, mais il faut bien passer par la thorie pour comprendre. Pas de panique si vous n'assimilez pas tout ce que je vous prsente ici, la partie suivante de ce chapitre est entirement consacre la pratique ! Nous allons commencer par tudier la syntaxe et les objets mettre en uvre, puis nous appliquerons ensuite tout cela travers un petit scnario de tests.

Connexion la base de donnes


Identification de l'URL
Pour nous connecter la base de donnes MySQL depuis notre application Java, nous avons besoin d'une URL spcifique JDBC, qui respecte la syntaxe gnrale suivante :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Code : URL jdbc:mysql://nomhote:port/nombdd

381/606

Dans cette adresse, vous remarquez plusieurs sections : nomhote : le nom de l'hte sur lequel le serveur MySQL est install. S'il est en place sur la mme machine que l'application Java excute, alors vous pouvez simplement spcifier localhost. Cela peut galement tre une adresse IP comme 127.0.0.1. Au passage, si vous rencontrez des problmes de connectivit en spcifiant localhost et que l'utilisation de 127.0.0.1 la place les rsout, alors vous avez un souci de configuration rseau (DNS, hosts, etc.) ; port : le port TCP/IP cout par votre serveur MySQL. Par dfaut, il s'agit du port 3306 ; nombdd : le nom de la base de donnes laquelle vous souhaitez vous connecter. En l'occurrence, il s'agira pour nous de bdd_sdzee. Ainsi, puisque notre serveur MySQL est install sur le mme poste que notre serveur d'applications, l'URL finale sera dans notre cas : Code : URL jdbc:mysql://localhost:3306/bdd_sdzee

tudions maintenant la mise en place de la connexion entre notre application et notre base.

tablissement de la connexion
Aprs le chargement du driver, nous pouvons tenter d'tablir une connexion avec notre base de donnes : Code : Java - Connexion la base de donnes /* Connexion la base de donnes */ String url = "jdbc:mysql://localhost:3306/bdd_sdzee"; String utilisateur = "java"; String motDePasse = "$dZ_E"; Connection connexion = null; try { connexion = DriverManager.getConnection( url, utilisateur, motDePasse ); /* Ici, nous placerons nos requtes vers la BDD */ /* ... */ } catch ( SQLException e ) { /* Grer les ventuelles erreurs ici */ } finally { if ( connexion != null ) try { /* Fermeture de la connexion */ connexion.close(); } catch ( SQLException ignore ) { /* Si une erreur survient lors de la fermeture, il suffit de l'ignorer. */ } }

L'tablissement d'une connexion s'effectue travers l'objet DriverManager. Il suffit d'appeler sa mthode statique getConnection() pour rcuprer un objet de type Connection. Comme vous pouvez le voir ici, celle-ci prend en argument l'adresse de la base de donnes, le nom d'utilisateur et le mot de passe associ.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


L'appel cette mthode peut retourner des erreurs de type SQLException :

382/606

si une erreur SQLException: No suitable driver est envoye, alors cela signifie que le driver JDBC n'a pas t charg ou que l'URL n'a t reconnue par aucun des drivers chargs par votre application ; si une erreur SQLException: Connection refused ou Connection timed out ou encore CommunicationsException: Communications link failure est envoye, alors cela signifie que la base de donnes n'est pas joignable. Si un de ces derniers cas survient, vous trouverez ci-dessous une liste des causes possibles et les pistes de rsolution associes : Cause ventuelle Le serveur MySQL est teint ? Le numro de port dans l'URL est manquant ou incorrect ? Le nom d'hte ou l'adresse IP dans l'URL est incorrect(e) ? Le serveur MySQL n'accepte pas de connexions TCP/IP ? Il n'y a plus aucune connexion disponible sur le serveur MySQL ? Quelque chose entre l'application Java et le serveur MySQL bloque la connexion, comme un pare-feu ou un proxy ? Le nom d'hte dans l'URL n'est pas reconnu par votre serveur DNS local ? Piste de rsolution Dmarrez le serveur MySQL... Ouvrez le fichier de configuration my.cnf de votre serveur MySQL, et vrifiez le port qui y est spcifi. Testez la connectivit en effectuant un simple ping . Vrifiez que MySQL a t lanc sans l'option --skipnetworking . Redmarrez MySQL, et corrigez le code de votre application pour qu'il libre les connexions efficacement. Configurez votre pare-feu et/ou proxy pour qu'il(s) autorise(nt) le port cout par votre serveur MySQL. Utilisez l'adresse IP dans l'URL au lieu du nom d'hte, ou actualisez si possible votre DNS.

Enfin, peu importe que la connexion ait russi ou non, retenez bien que sa fermeture dans un bloc finally est extrmement importante ! Si vous ne fermez pas les connexions que vous ouvrez, et en gardez un grand nombre ouvertes sur une courte priode, le serveur risque d'tre satur et de ne plus accepter aucune nouvelle connexion ; votre application risque alors de planter. La bonne pratique suivre est de toujours ouvrir et fermer la connexion le plus finement possible, c'est--dire au plus prs de l'excution de vos requtes, dans un ensemble try-catch-finally. Cela permet de s'assurer qu'aucune connexion n'est ouverte ni ne reste ouverte inutilement. La mise en place d'un bloc finally permet de s'assurer que la connexion sera ferme quoi qu'il arrive en cours de route.

Nous n'allons pas entrer dans les dtails pour le moment, puisque nous n'en sommes qu'au dbut de notre apprentissage, mais sachez d'ores et dj que l'ouverture d'une connexion a un cot non ngligeable en termes de performances. Dans une application trs frquente, il devient hors de question de procder des ouvertures/fermetures chaque requte effectue, cela reviendrait signer l'arrt de mort de votre serveur ! Nous y reviendrons en temps voulu, pas d'inquitudes.

Cration d'une requte


Avant de pouvoir crer des instructions SQL, vous devez tout d'abord crer un objet de type Statement. Si vous parcourez sa documentation, vous constaterez qu'il s'agit en ralit d'une interface dont le rle est de permettre l'excution de requtes. Pour initialiser cet objet, rien de plus simple, il suffit d'appeler la mthode createStatement() de l'objet Connection prcdemment obtenu ! Donc si nous reprenons notre exemple, juste aprs l'tablissement de la connexion dans notre bloc try, nous devons ajouter le code suivant : Code : Java - Initialisation de l'objet Statement /* Cration de l'objet grant les requtes */

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Statement statement = connexion.createStatement();

383/606

Excution de la requte
Une fois l'objet Statement initialis, il devient alors possible d'excuter une requte. Pour ce faire, celui-ci met votre disposition toute une srie de mthodes, notamment les deux suivantes : executeQuery() : cette mthode est ddie la lecture de donnes via une requte de type SELECT ; executeUpdate() : cette mthode est rserve l'excution de requtes ayant un effet sur la base de donnes (criture ou suppression), typiquement les requtes de type INSERT, UPDATE, DELETE, etc. En outre, il existe des variantes de chaque mthode prenant en compte d'autres arguments, ainsi que deux autres mthodes nommes execute() et executeBatch(). Nous n'allons pas nous attarder sur les subtilits mises en jeu, je vous laisse le soin de lire la documentation de l'objet Statement si vous souhaitez en savoir davantage.

Excution d'une requte de lecture


En parcourant la documentation de la mthode executeQuery(), nous apprenons qu'elle retourne un objet de type ResultSet contenant le rsultat de la requte. V oici donc un exemple effectuant un SELECT sur notre table d'utilisateurs : Code : Java - Excution d'une requte de type SELECT /* Excution d'une requte de lecture */ ResultSet resultat = statement.executeQuery( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" );

Avant d'apprendre rcuprer et analyser le rsultat retourn par cet appel, regardons comment effectuer une requte d'criture dans la base.

Excution d'une requte d'criture


En parcourant cette fois la documentation de la mthode executeUpdate(), nous apprenons qu'elle retourne un entier reprsentant le nombre de lignes affectes par la requte ralise. Si par exemple vous ralisez une insertion de donnes via un INSERT, cette mthode retournera 0 en cas d'chec et 1 en cas de succs. Si vous ralisez une mise jour via un UPDATE, cette mthode retournera le nombre de lignes mises jour. Idem en cas d'une suppression via un DELETE, etc. Je vous propose ici un exemple effectuant un INSERT sur notre table d'utilisateurs : Code : Java - Excution d'une requte de type INSERT /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());" );

Avant de pouvoir tester ces deux mthodes, il nous faut encore dcouvrir comment manipuler les rsultats retourns.

Accs aux rsultats de la requte


Retour d'une requte de lecture
Je vous l'ai dj souffl, l'excution d'une requte de lecture via la mthode statement.executeQuery() retourne un

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

384/606

objet de type ResultSet. V ous pouvez le voir comme un tableau, qui contient les ventuelles donnes retournes par la base de donnes sous forme de lignes. Pour accder ces lignes de donnes, vous avez votre disposition un curseur, que vous pouvez dplacer de ligne en ligne. Notez bien qu'il ne s'agit pas d'un curseur au sens base de donnes du terme, mais bien d'un curseur propre l'objet ResultSet. V oyons cela dans un exemple, puis commentons : Code : Java - Excution d'une requte de type SELECT et rcupration du rsultat /* Excution d'une requte de lecture */ ResultSet resultat = statement.executeQuery( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" ); /* Rcupration des donnes du rsultat de la requte de lecture */ while ( resultat.next() ) { int idUtilisateur = resultat.getInt( "id" ); String emailUtilisateur = resultat.getString( "email" ); String motDePasseUtilisateur = resultat.getString( "mot_de_passe" ); String nomUtilisateur = resultat.getString( "nom" ); } /* Traiter ici les valeurs rcupres. */

Pour commencer, la ligne 2 nous rcuprons le retour de l'appel la mthode statement.executeQuery() dans un objet ResultSet. Ensuite, afin de pouvoir accder aux lignes contenues dans cet objet, nous effectuons un appel la mthode next(), qui permet de dplacer le curseur la ligne suivante. Elle retourne un boolen, initialis true tant qu'il reste des donnes parcourir. Pourquoi ne lisons-nous pas la premire ligne avant de dplacer le curseur la ligne suivante ?

Tout simplement parce que lors de la cration d'un objet ResultSet, son curseur est par dfaut positionn avant la premire ligne de donnes. Ainsi, il est ncessaire de se dplacer d'un cran vers l'avant pour pouvoir commencer lire les donnes contenues dans l'objet. Si vous essayez de lire des donnes avant d'avoir dplac le curseur, votre code enverra une SQLException.

Une fois le curseur positionn correctement, il ne nous reste plus qu' rcuprer les contenus des diffrents champs via une des nombreuses mthodes de rcupration proposes par l'objet ResultSet. Je ne vais pas vous en faire la liste exhaustive, je vous laisse parcourir la documentation pour les dcouvrir en intgralit. Sachez simplement qu'il en existe une par type de donnes rcuprables : une mthode resultat.getInt() pour rcuprer un entier ; une mthode resultat.getString() pour rcuprer une chane de caractres ; une mthode resultat.getBoolean() pour rcuprer un boolen ; etc. Chacune de ces mthodes existe sous deux formes diffrentes : soit elle prend en argument le nom du champ vis dans la table de la base de donnes ; soit elle prend en argument l'index du champ vis dans la table de la base de donnes. En loccurrence, nous avons ici utilis le nom des champs : "id", "email", "mot_de_passe" et "nom". Si nous avions voulu utiliser leurs index, alors nous aurions d remplacer les lignes 6 9 du code prcdent par : Code : Java - Ciblage des champs par leurs index int idUtilisateur = resultat.getInt( 1 ); String emailUtilisateur = resultat.getString( 2 ); String motDePasseUtilisateur = resultat.getString( 3 );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


String nomUtilisateur = resultat.getString( 4 );

385/606

En effet, la colonne nomme "id" est bien la colonne n1 de notre table, "email" la colonne n2, etc. Peu importe que vous utilisiez l'index ou le nom des champs, vous devez vous assurer qu'ils existent bel et bien dans la table. Si vous prcisez un nom de champ qui n'existe pas, ou un index de champ qui dpasse l'index de la dernire colonne, alors la mthode de rcupration vous enverra une SQLException.

Dernire information importante : afin de parcourir toutes les lignes de donnes renvoyes dans le rsultat de la requte, il est ncessaire de boucler sur l'appel la mthode de dplacement du curseur. Lorsque ce curseur atteindra la dernire ligne contenue dans l'objet, la mthode resultat.next() renverra false, et nous sortirons ainsi automatiquement de notre boucle une fois le parcours des donnes achev.

Retour d'une requte d'criture


Lorsque vous effectuez une modification sur une table de votre base de donnes via la mthode statement.executeUpdate(), celle-ci renvoie des informations diffrentes selon le type de la requte effectue : l'excution d'un INSERT renvoie 0 en cas d'chec de la requte d'insertion, et 1 en cas de succs ; l'excution d'un UPDATE ou d'un DELETE renvoie le nombre de lignes respectivement mises jour ou supprimes ; l'excution d'un CREATE, ou de toute autre requte ne retournant rien, renvoie 0. V oici par exemple comment rcuprer le statut d'une requte d'insertion de donnes : Code : Java - Excution d'une requte de type INSERT et rcupration du statut /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());" );

Libration des ressources


Tout comme il est ncessaire de fermer proprement une connexion ouverte, il est extrmement recommand de disposer proprement des objets Statement et ResultSet initialiss au sein d'une connexion : Code : Java - Cycle d'ouverture/fermeture des ressources impliques dans une communication avec la BDD Connection connexion = null; Statement statement = null; ResultSet resultat = null; try { /* * Ouverture de la connexion, initialisation d'un Statement, initialisation d'un ResultSet, etc. */ } catch ( SQLException e ) { /* Traiter les erreurs ventuelles ici. */ } finally { if ( resultat != null ) { try { /* On commence par fermer le ResultSet */ resultat.close(); } catch ( SQLException ignore ) { } } if ( statement != null ) {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


try { /* Puis on ferme le Statement */ statement.close(); } catch ( SQLException ignore ) { }

386/606

} if ( connexion != null ) { try { /* Et enfin on ferme la connexion */ connexion.close(); } catch ( SQLException ignore ) { } }

Ceux-ci doivent obligatoirement tre ferms, du plus rcemment ouvert au plus ancien. Ainsi il faut commencer par fermer le ResultSet, puis le Statement et enfin l'objet Connection. Les exceptions ventuelles, envoyes en cas de ressources dj fermes ou non disponibles, peuvent tre ignores comme c'est le cas dans ce code d'exemple. V ous pouvez bien videmment choisir de les prendre en compte, par exemple en les enregistrant dans un fichier de logs. Tout comme pour la connexion, ces ressources doivent tre fermes au sein d'un bloc finally afin de s'assurer que, quoiqu'il arrive en cours de route, les ressources soient proprement libres.

Mise en pratique
Nous venons d'tudier les tapes principales de la communication avec une base de donnes, qui pour rappel sont : 1. 2. 3. 4. la connexion la base ; la cration et l'excution d'une requte SQL ; la rcupration de son rsultat ; la fermeture des diffrentes ressources mises en jeu.

Il est maintenant temps de pratiquer, en utilisant le bac sable que nous avons mis en place.

Afficher le contenu de la table Utilisateur


En premier lieu, nous allons effectuer une requte de type SELECT afin de rcuprer ce que contient notre table Utilisateur. V oici le code mettre en place dans la mthode executerTests() de notre objet TestJDBC : Code : Java - com.sdzee.bdd.TestJDBC public List<String> executerTests( HttpServletRequest request ) { /* Chargement du driver JDBC pour MySQL */ try { messages.add( "Chargement du driver..." ); Class.forName( "com.mysql.jdbc.Driver" ); messages.add( "Driver charg !" ); } catch ( ClassNotFoundException e ) { messages.add( "Erreur lors du chargement : le driver n'a pas t trouv dans le classpath ! <br/>" + e.getMessage() ); } /* Connexion la base de donnes */ String url = "jdbc:mysql://localhost:3306/bdd_sdzee"; String utilisateur = "java"; String motDePasse = "$dZ_E"; Connection connexion = null; Statement statement = null; ResultSet resultat = null; try { messages.add( "Connexion la base de donnes..." ); connexion = DriverManager.getConnection( url, utilisateur, motDePasse ); messages.add( "Connexion russie !" );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


/* Cration de l'objet grant les requtes */ statement = connexion.createStatement(); messages.add( "Objet requte cr !" ); /* Excution d'une requte de lecture */ resultat = statement.executeQuery( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" ); messages.add( "Requte \"SELECT id, email, mot_de_passe, nom FROM Utilisateur;\" effectue !" ); /* Rcupration des donnes du rsultat de la requte de lecture */ while ( resultat.next() ) { int idUtilisateur = resultat.getInt( "id" ); String emailUtilisateur = resultat.getString( "email" ); String motDePasseUtilisateur = resultat.getString( "mot_de_passe" ); String nomUtilisateur = resultat.getString( "nom" ); /* Formatage des donnes pour affichage dans la JSP finale. */ messages.add( "Donnes retournes par la requte : id = " + idUtilisateur + ", email = " + emailUtilisateur + ", motdepasse = " + motDePasseUtilisateur + ", nom = " + nomUtilisateur + "." ); } } catch ( SQLException e ) { messages.add( "Erreur lors de la connexion : <br/>" + e.getMessage() ); } finally { messages.add( "Fermeture de l'objet ResultSet." ); if ( resultat != null ) { try { resultat.close(); } catch ( SQLException ignore ) { } } messages.add( "Fermeture de l'objet Statement." ); if ( statement != null ) { try { statement.close(); } catch ( SQLException ignore ) { } } messages.add( "Fermeture de l'objet Connection." ); if ( connexion != null ) { try { connexion.close(); } catch ( SQLException ignore ) { } } } } return messages;

387/606

V ous retrouvez ici sans surprise les tapes que nous avons dcouvertes et analyses prcdemment. Les lignes 28 42 correspondent l'excution de la requte SELECT et la rcupration de son rsultat. V ous pouvez remarquer l'utilisation de la liste messages que nous avions mise en place lors de la cration de notre bac sable. Nous y insrons ici, et y insrerons dans la suite de nos exemples, tous les messages indiquant les rsultats et erreurs de nos tests. chaque nouvel essai, il nous suffira alors d'accder l'URL de la servlet, qui je le rappelle est http://localhost:8080/pro/testjdbc, pour que notre JSP finale nous affiche le contenu de cette liste et nous permette ainsi de suivre simplement la progression au sein du code. Une fois ce code mis en place, il ne vous reste plus qu' dmarrer votre serveur Tomcat si ce n'est pas dj fait, et accder l'URL de test. V ous pourrez alors visualiser toutes les informations places dans la liste messages , qui est je vous le rappelle

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

388/606

parcourue par notre page JSP. V ous obtiendrez le rsultat suivant si vous avez correctement prpar votre base de donnes et si vous avez dmarr votre serveur SQL :

Tout fonctionne comme prvu, notre application a russi : charger le driver ; se connecter la base de donnes avec le compte utilisateur que nous y avions cr ; effectuer une requte de slection et rcuprer son rsultat ; fermer les ressources.

Insrer des donnes dans la table Utilisateur


Le principe est sensiblement le mme que pour une requte de lecture ; il suffit d'utiliser un appel statement.executeUpdate() la place d'un appel statement.executeQuery(). Dans notre exemple, nous allons insrer des donnes d'exemple dans la table Utilisateur, puis effectuer une requte de lecture pour visualiser le contenu de la table aprs insertion. Pour ce faire, il faut simplement ajouter le code suivant, juste avant les lignes 28 42 du code prcdent : Code : Java - com.sdzee.bdd.TestJDBC /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());" ); /* Formatage pour affichage dans la JSP finale. */ messages.add( "Rsultat de la requte d'insertion : " + statut + "." );

Nous rcuprons ici le retour de la requte INSERT dans l'entier statut, qui pour rappel vaut 1 en cas de succs de l'insertion et 0 en cas d'chec, et le stockons dans la liste messages pour affichage final dans notre JSP. Accdez nouveau la page de test depuis votre navigateur, et constatez le rsultat suivant :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

389/606

La requte de slection retourne bien une ligne supplmentaire, et il s'agit bien des donnes que nous venons d'insrer. Seulement il nous manque un petit quelque chose... Comment rcuprer l'id auto-gnr par la table lors de l'insertion d'une ligne ?

Dans l'exemple, nous pouvons observer que l'id de la nouvelle ligne insre vaut 3 parce que nous effectuons un SELECT par la suite, mais dans une vraie application nous n'allons pas nous amuser effectuer une requte de lecture aprs chaque insertion simplement pour rcuprer cette information ! Le problme, c'est que la mthode executeUpdate() ne retourne pas cette valeur, elle retourne seulement un entier dont la valeur indique le succs ou non de l'insertion. Eh bien rassurez-vous, car il existe une variante de la mthode executeUpdate() qui remplit exactement cette fonction ! Regardez sa documentation, vous observerez qu'elle prend un argument supplmentaire en plus de la requte SQL effectuer. Il s'agit d'un entier qui peut prendre uniquement deux valeurs, qui sont dfinies par les deux constantes Statement.RETURN_GENERATED_KEYS et Statement.NO_GENERATED_KEYS (qui en ralit valent respectivement 1 et 2). Leurs noms parlent d'eux-mmes : la premire permet la rcupration de l'id gnr, et la seconde ne le permet pas. Mais attention, il ne suffit pas de passer la valeur Statement.RETURN_GENERATED_KEYS en tant que second argument la mthode executeUpdate() pour qu'elle se mette retourner l'id souhait. Non, la mthode continue ne retourner qu'un simple entier indiquant le succs ou non de l'insertion. Ce qui change par contre, c'est qu'il devient alors possible de rcuprer un ResultSet, le mme objet que celui utilis pour rcuprer le retour d'une requte de lecture. V oyez plutt : Code : Java - com.sdzee.bdd.TestJDBC /* Excution d'une requte d'criture avec renvoi de l'id autognr */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc2@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());" , Statement.RETURN_GENERATED_KEYS); /* Formatage pour affichage dans la JSP finale. */ messages.add( "Rsultat de la requte d'insertion : " + statut + "." ); /* Rcupration de l'id auto-gnr par la requte d'insertion. */ resultat = statement.getGeneratedKeys(); /* Parcours du ResultSet et formatage pour affichage de la valeur qu'il contient dans la JSP finale. */ while ( resultat.next() ) { messages.add( "ID retourn lors de la requte d'insertion :" + resultat.getInt( 1 ) ); }

En remplaant la requte d'insertion prcdente par ce nouvel appel, vous obtiendrez alors la ligne suivante parmi les messages affichs :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

390/606

V ous observez que l'id cr par MySQL pour l'entre insre en base est bien prsent dans le ResultSet retourn par statement.getGeneratedKeys() ! Si vous n'obtenez pas ces rsultats et/ou si votre code gnre des exceptions, reportez-vous aux causes ventuelles et aux pistes de rsolution exposes prcdemment.

Les limites du systme


Nous sommes dornavant capables d'interroger notre base de donnes, mais comme toujours, il y a un "mais" ! Dans notre mise en pratique, tout fonctionne merveille, mais nous allons dcouvrir un cas d'utilisation qui peut poser de gros problmes de scurit.

Insrer des donnes saisies par l'utilisateur


Reprenons notre exemple d'insertion de donnes prcdent, mais cette fois au lieu d'insrer des donnes crites en dur dans le code, nous allons confier l'utilisateur la tche de saisir ces donnes. Nous n'allons pas nous embter mettre en place un formulaire pour quelque chose d'aussi basique, nous allons nous contenter de paramtres placs directement dans l'URL de la requte. Concrtement, nous allons modifier lgrement le code de notre objet Java pour que lorsque le client saisit une URL de la forme http://localhost:8080/pro/testjdbc?nom [...] bbb&email=ccc, notre application soit capable de rcuprer et utiliser les donnes ainsi transmises. C'est trs simple, il nous suffit pour cela de remplacer la ligne d'appel la mthode statement.executeUpdate() par les lignes suivantes : Code : Java - com.sdzee.bdd.TestJDBC /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) " + "VALUES ('" + paramEmail + "', MD5('" + paramMotDePasse + "'), '" + paramNom + "', NOW());" );

Avec tout ce que nous avons cod dans les chapitres prcdents, vous devez comprendre ces lignes aisment ! Nous rcuprons ici simplement les paramtres d'URL via la mthode request.getParameter(), et les concatnons directement notre INSERT pour former une requte valide.

Le problme des valeurs nulles


V oyons pour commencer un premier inconvnient majeur avec cette technique. Afin de gnrer une requte valide, nous sommes obligs de prciser des paramtres dans l'URL d'appel de notre page de test. Si nous nous contentons d'appeler btement l'URL http://localhost:8080/pro/testjdbc sans paramtres, les appels aux mthodes request.getParameter() vont retourner des chanes de caractres indfinies, c'est--dire initialises null. Si vous avez prt attention la requte CREATE que nous avions utilise pour mettre en place notre table Utilisateur dans la base de donnes, vous devez vous souvenir que nous avions prcis une contrainte NOT NULL sur les champs nom, motdepasse et email . Ainsi, notre base de donnes doit nous retourner une erreur si nous tentons d'insrer des valeurs indfinies dans la table Utilisateur. Faites btement le test, et appelez votre URL de test sans paramtres. V ous observez alors au sein de la page affiche par votre JSP la ligne suivante :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Que s'est-il pass ? Pourquoi est-ce qu'une ligne a t insre avec des valeurs indfinies ?

391/606

Ne soyez pas leurrs ici, les valeurs que vous avez insres dans votre base ne valent pas null au sens Java du terme ! En ralit, les paramtres que vous rcuprez dans votre application Java sont bien indfinis, mais la construction de la requte par concatnation produit la chane suivante : Code : SQL INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('null',MD5('null'),'null',NOW());

Ainsi, si des valeurs apparaissent comme tant gales null ce n'est pas parce que la contrainte NOT NULL n'a pas t respecte, c'est parce que notre requte a t mal forme ! Alors que les champs email et nom devraient effectivement tre indfinis, ils sont ici initialiss avec le contenu 'null' et la requte d'insertion fonctionne donc comme si ces champs taient correctement renseigns. La requte correctement forme devrait tre la suivante : Code : SQL INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES (null,MD5(null),null,NOW());

Notez bien l'absence des apostrophes autour des null. Si notre base de donnes recevait une telle requte, elle la refuserait, car elle y dtecterait correctement les valeurs indfinies. Par ailleurs, vous remarquerez peut-tre sur l'image que le champ mot_de_passe ne contient pas 'null'. Cela provient tout simplement du fait que nous utilisons la fonction MD5() dans notre requte SQL, qui gnre un hash de la chane contenant 'null'. Si nous n'utilisions pas cette fonction, la valeur de ce champ serait, elle aussi, initialise 'null' !

Comment pallier ce problme d'insertion de valeurs "nulles" ?

Pour viter ce dsagrment, plusieurs solutions s'offrent nous. La plus simple pour le moment, c'est de mettre en place une vrification dans le code de notre objet Java sur le retour de chaque appel la mthode request.getParameter(), juste avant l'excution de la requte. V oici ce que devient alors notre code : Code : Java - com.sdzee.bdd.TestJDBC /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); if ( paramEmail != null && paramMotDePasse != null && paramNom != null ) { /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) " + "VALUES ('" + paramEmail + "', MD5('" + paramMotDePasse + "'), '" + paramNom + "', NOW());" ); /* Formatage pour affichage dans la JSP finale. */ messages.add( "Rsultat de la requte d'insertion : " + statut + "." ); }

Grce ce simple bloc if, si un des appels request.getParameter() retourne une chane indfinie, alors la requte n'est pas effectue. Appelez nouveau la page de test sans paramtres, vous constaterez que la requte n'est plus effectue.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

392/606

Cela dit, c'est trs contraignant de procder de cette manire : si demain la structure de notre table change et qu'il devient par exemple possible de ne pas transmettre le paramtre nom, alors il faudra penser modifier cette condition dans notre code pour autoriser une chane paramNom indfinie...

Le cas idal
Essayons maintenant d'insrer des valeurs correctement, en saisissant une URL qui contient bien les trois paramtres attendus. Appelez par exemple la page de test avec l'URL suivante : Code : URL http://localhost:8080/pro/testjdbc? nom=Marcel&motdepasse=pastque&email=marcel@mail.fr

V ous observerez alors une nouvelle ligne dans la page affiche par votre JSP :

Tout va bien, notre code a bien insr dans la table les donnes rcupres depuis l'URL transmise par l'utilisateur.

Que va-t-il se passer si nous omettons de renseigner un ou plusieurs paramtres ?

Bonne question, il reste effectivement un cas que nous n'avons pas encore trait. Si vous accdez la page de test avec l'URL suivante : Code : URL http://localhost:8080/pro/testjdbc?nom=&motdepasse=&email=

Alors les appels aux mthodes request.getParameter() ne vont cette fois plus retourner null comme c'tait le cas lorsque nous accdions la page sans aucun paramtre, mais des chanes vides. Le rsultat affich sera dans ce cas :

V ous observez ici que les valeurs vides sont insres correctement dans la table. Cela est d au fait que MySQL sait faire la distinction entre un champ vide et un champ nul. Sachez toutefois que ce n'est pas le cas de toutes les solutions existantes sur le march. Certains systmes, comme les bases de donnes Oracle par exemple, considrent par dfaut qu'un champ vide est quivalent un champ nul. Ainsi, notre dernier exemple serait refus par la base si nous travaillions avec le SGBD Oracle.

Les injections SQL


Dans ce dernier exemple tout va bien, car aucune embche ne gne notre parcours, nous voluons dans un monde peupl de bisounours. Mais dans la vraie vie, vous savez trs bien que tout n'est pas aussi merveilleux, et vous devez toujours vous rappeler la rgle d'or que je vous ai dj communique plusieurs reprises : il ne faut jamais faire confiance l'utilisateur . Quel est le problme exactement ?

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Ok... Puisque vous semblez encore une fois sceptiques, changeons l'URL de notre exemple prcdent par celle-ci : Code : URL http://localhost:8080/pro/testjdbc? nom=Marcel'&motdepasse=pastque&email=marcel@mail.fr

393/606

Nous avons simplement ajout une apostrophe au contenu du paramtre nom. Faites le test, et vous observerez alors cette ligne dans la page affiche par votre JSP :

Que s'est-il pass ?

Ce type de failles porte un nom, il s'agit d'une injection SQL. En envoyant une chane contenant le caractre apostrophe, qui pour rappel est le caractre qui sert dlimiter les valeurs dans notre requte d'insertion, l'utilisateur a fait chouer notre requte. En effet, aprs concatnation du contenu des paramtres, notre requte devient : Code : SQL INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('marcel@mail.fr',MD5('pastque'),'Marcel'',NOW());

V ous voyez bien o est le problme ici : dans votre requte, deux apostrophes se suivent, ce qui rend sa syntaxe errone et provoque l'envoi d'une SQLException.

D'accord, l'utilisateur peut provoquer l'envoi d'une exception. Ce n'est pas catastrophique non plus...

En l'occurrence non, a ne nous embte pas plus que a, car a ne pnalise que l'utilisateur, qui se retrouve avec un message d'erreur devant les yeux. Mais rendez-vous bien compte que ce type de failles peut tre trs dangereux pour d'autres types de requtes ! L'objet de ce chapitre n'est pas de faire de vous des pirates en herbe, nous n'allons donc pas nous attarder sur les cas qui posent de gros problmes de scurit, sachez simplement que si vous ne protgez pas efficacement vos requtes, il peut devenir possible pour un utilisateur averti de modifier une requte votre insu ou pire, d'effectuer des requtes sur la base votre insu ! En clair, votre application peut devenir une porte ouverte conduisant tout droit aux donnes de votre base...

Quelle est la parade contre ce type de risques ?

A priori, nous pourrions tenter de crer une mthode de vrification qui se chargerait d'analyser les paramtres saisis par l'utilisateur, et d'y liminer ou chapper les caractres problmatiques (notamment l'apostrophe que nous avons utilise dans notre exemple). Rassurez-vous, il existe bien plus propre, bien plus standard et surtout bien plus efficace : les requtes prpares !

Les requtes prpares Pourquoi prparer ses requtes ?


Nous allons ici dcouvrir un nouvel objet, nomm PreparedStatement. En parcourant sa documentation, vous vous apercevrez qu'il s'agit en ralit d'une nouvelle interface qui implmente l'interface Statement utilise dans nos prcdents exemples. Comme son nom l'indique, cet objet permet de crer des requtes prpares. Pourquoi devrions-nous utiliser de telles requtes ?

En tudiant un peu plus attentivement la documentation de l'objet, vous dcouvrirez alors qu'il prsente trois diffrences majeures avec un Statement classique :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

394/606

l'utilisation de PreparedStatement peut permettre de pr-compiler une requte SQL ; une requte SQL ainsi cre peut tre paramtre, c'est--dire contenir des trous ou jokers qui ne seront combls que lors de son excution ; une requte SQL ainsi cre est protge contre les injections SQL, et contre d'ventuelles erreurs sur les types des paramtres.

1. Des requtes pr-compiles


Le premier avantage se situe au niveau des performances. Ds lors que vous souhaitez excuter une requte d'un mme objet Statement plusieurs fois, il devient intressant d'utiliser un PreparedStatement la place, afin de rduire le temps d'excution. En effet, la principale fonctionnalit de ce nouvel objet est qu' la diffrence d'un classique objet Statement, il prend en argument une requte SQL ds sa cration. Quel est l'intrt de passer une requte l'objet ds sa cration ?

L'avantage, c'est que dans la plupart des cas (*), la requte SQL sera, ds la cration de l'objet, directement envoye au SGBD, o elle sera compile. Pour tre exact, un objet PreparedStatement ne contient donc pas simplement une requte SQL, mais une requte SQL pr-compile. Concrtement, cela signifie que lorsque ce PreparedStatement sera excut, le SGBD n'aura plus qu' excuter la requte sans avoir besoin de la compiler au pralable. (*) Note : tous les drivers JDBC ne procdent pas de cette manire, certains n'effectuent pas l'envoi de la requte vers le serveur SQL pour pr-compilation lors de sa cration via un PreparedStatement. Ces diffrences de comportement d'un driver l'autre existent parce que cette fonctionnalit n'est pas dfinie noir sur blanc dans les spcifications de l'API JDBC.

2. Des requtes paramtres


Le second avantage rside dans le fait qu' travers un tel objet, il est possible de crer des requtes qui prennent en compte des paramtres. Ceux-ci sont reprsents par des points d'interrogation ? dans la requte qui sert de modle, et doivent tre prciss avant l'excution. Si vous n'tes pas familiers avec ce concept, vous en apprendrez davantage dans ce chapitre du cours de MySQL. Le principe est simple, il s'agit de crer une requte modle qui contient un ou plusieurs trous. Exemple d'une requte attendant un seul paramtre : Code : SQL - Exemple de requte paramtre SELECT * FROM Utilisateur WHERE email = ?

V ous observez ici le caractre joker ? dont je viens de vous parler. Lorsque vous passez une telle requte un objet PreparedStatement, celui-ci va la faire pr-compiler et se chargera ensuite de remplacer le paramtre manquant par la valeur que vous souhaitez lui donner au moment de l'excution.

Ainsi, la pr-compilation, couple la prise en compte de paramtres au moment de l'excution, permet d'amliorer considrablement les performances de requtes paramtres destines tre excutes plusieurs fois. Cela dit, ces conditions ne sont pas un prrequis l'utilisation de requtes prpares : il est tout fait possible de prparer des requtes qui n'attendent pas de paramtres ou qui ne seront excutes qu'une seule fois.

3. Des requtes protges !


Le dernier avantage et le plus important de tous , c'est bien videmment celui que je vous ai annonc en conclusion du paragraphe sur les injections SQL : en utilisant des requtes prpares, vous prvenez tout risque de failles de ce type ! Et cette fonctionnalit, contrairement l'tape de pr-compilation, est disponible quel que soit le driver JDBC utilis. En outre, non seulement vos requtes seront protges contre les injections SQL, mais le passage des paramtres s'en retrouvera galement grandement facilit, et ce quel que soit le type du paramtre pass : String, Date, etc., et mme la valeur null ! Nous allons revenir sur ce point dans la mise en pratique venir.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

395/606

Comment prparer ses requtes ?


V ous n'allez pas tre dpayss, le fonctionnement est assez semblable celui d'un Statement classique.

Initialisation de l'objet
La premire diffrence se situe l o, auparavant, nous effectuions cette initialisation : Code : Java - Initialisation de l'objet Statement /* Cration de l'objet grant les requtes */ Statement statement = connexion.createStatement();

Nous allons dsormais directement prciser la requte SQL dans cette nouvelle initialisation : Code : Java - Initialisation de l'objet PreparedStatement /* Cration de l'objet grant la requte prpare dfinie */ PreparedStatement preparedStatement = connexion.prepareStatement( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" );

La seule diffrence est l'appel la mthode prepareStatement() de l'objet Connection qui attend, comme je vous l'ai dj annonc, une requte SQL en argument.

Excution de la requte
Puisque votre requte est dj dclare lors de l'initialisation, la seconde diffrence avec l'utilisation d'un Statement classique se situe au niveau de l'appel la mthode d'excution. Alors qu'auparavant nous effectuions par exemple : Code : Java - Excution d'une requte de type SELECT avec un Statement classique /* Excution d'une requte de lecture */ statement.executeQuery( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" );

Nous allons cette fois simplement appeler : Code : Java - Excution d'une requte de type SELECT avec un PreparedStatement preparedStatement.executeQuery();

V ous l'aurez dj remarqu si vous avez attentivement parcouru la documentation de l'objet PreparedStatement, ses mthodes executeQuery() et executeUpdate() n'attendent logiquement aucun argument.

Et avec des paramtres ?


En effet tout cela est bien joli, mais prparer ses requtes est surtout utile lorsque des paramtres interviennent. Pour rappel, voici comment nous procdions avec un Statement classique : Code : Java - Excution d'une requte paramtre avec un Statement classique

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


/* Cration de l'objet grant les requtes */ statement = connexion.createStatement(); /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); if ( paramEmail != null && paramMotDePasse != null && paramNom != null ) { /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) " + "VALUES ('" + paramEmail + "', MD5('" + paramMotDePasse + "'), '" + paramNom + "', NOW());" ); }

396/606

Et voil comment procder avec une requte prpare. Je vous donne le code, et vous commente le tout ensuite : Code : Java - Excution d'une requte paramtre avec un PreparedStatement /* Cration de l'objet grant les requtes prpares */ preparedStatement = connexion.prepareStatement( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES(?, MD5(?), ?, NOW());" ); /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); /* * Remplissage des paramtres de la requte grce aux mthodes * setXXX() mises disposition par l'objet PreparedStatement. */ preparedStatement.setString( 1, paramEmail ); preparedStatement.setString( 2, paramMotDePasse ); preparedStatement.setString( 3, paramNom ); /* Excution de la requte */ int statut = preparedStatement.executeUpdate();

Pour commencer, la ligne 2 vous observez la dfinition de la requte SQL ds l'initialisation du PreparedStatement. Celleci attend trois paramtres, signals par les trois caractres ?. V ous remarquez ensuite l'tape de remplissage des paramtres de la requte, dans les lignes 13 15. Il s'agit en ralit tout simplement de remplacer les ? par les valeurs des paramtres attendus. Cela se fait par l'intermdiaire des mthodes setXXX() mises disposition par l'objet PreparedStatement, dont je vous laisse le loisir de parcourir la documentation si vous ne l'avez pas dj fait. Celles-ci devraient grandement vous rappeler la flope de mthodes getXXX() que nous avions dcouvertes dans l'objet ResultSet, puisqu'il en existe l encore une pour chaque type gr : une mthode preparedStatement.setInt() pour dfinir un entier ; une mthode preparedStatement.setString() pour dfinir une chane de caractres ; une mthode preparedStatement.setBoolean() pour dfinir un boolen ; ... La plupart de ces mthodes attendent simplement deux arguments : un entier dfinissant le paramtre remplacer ; un objet du type concern destin remplacer le paramtre dans la requte SQL. En l'occurrence, notre requte attend, dans l'ordre, un email, un mot de passe et un nom. Il s'agit l de trois chanes de caractres, voil pourquoi dans notre exemple nous utilisons trois reprises la mthode preparedStatement.setString(),

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

397/606

applique respectivement aux paramtres numrots 1, 2 et 3, reprsentant la position du ? remplacer dans la requte. Ainsi, le contenu de notre chane paramEmail remplace le premier ?, le contenu de paramMotDePasse le second, et le contenu de paramNom le troisime. Facile, n'est-ce pas ? V ous comprenez maintenant mieux pourquoi je vous avais annonc qu'un PreparedStatement facilitait le passage de paramtres : finie la concatnation barbare directement au sein de la requte SQL, tout passe dsormais par des mthodes standard, et c'est bien plus propre ainsi ! Enfin, vous remarquez la ligne 18 l'excution de la requte d'insertion via un simple appel la mthode preparedStatement.executeUpdate().

Mise en pratique
Nous pouvons maintenant utiliser des requtes prpares dans notre classe d'exemple, afin de tester leur bon fonctionnement et leurs diffrences avec les requtes non prpares. V ous devez remplacer le code suivant dans notre objet : Code : Java - Code faisant intervenir un Statement classique /* Cration de l'objet grant les requtes */ statement = connexion.createStatement(); /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); if ( paramEmail != null && paramMotDePasse!= null && paramNom != null ) { /* Excution d'une requte d'criture */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) " + "VALUES ('" + paramEmail + "', MD5('" + paramMotDePasse + "'), '" + paramNom + "', NOW());" ); /* Formatage pour affichage dans la JSP finale. */ messages.add( "Rsultat de la requte d'insertion : " + statut + "." ); }

Par ce nouveau code : Code : Java - Code faisant intervenir un PreparedStatement /* Cration de l'objet grant les requtes prpares */ preparedStatement = connexion.prepareStatement( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES(?, MD5(?), ?, NOW());" ); messages.add( "Requte prpare cre !" ); /* Rcupration des paramtres d'URL saisis par l'utilisateur */ String paramEmail = request.getParameter( "email" ); String paramMotDePasse = request.getParameter( "motdepasse" ); String paramNom = request.getParameter( "nom" ); /* * Remplissage des paramtres de la requte grce aux mthodes * setXXX() mises disposition par l'objet PreparedStatement. */ preparedStatement.setString( 1, paramEmail ); preparedStatement.setString( 2, paramMotDePasse ); preparedStatement.setString( 3, paramNom );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


/* Excution de la requte */ int statut = preparedStatement.executeUpdate(); /* Formatage pour affichage dans la JSP finale. */ messages.add( "Rsultat de la requte d'insertion prpare : " + statut + "." );

398/606

De mme, n'oubliez pas de remplacer la cration de la requte de slection : Code : Java - Ancien mode d'tablissement de la requte de slection /* Excution d'une requte de lecture */ resultat = statement.executeQuery( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" ); messages.add( "Requte \"SELECT id, email, mot_de_passe, nom FROM Utilisateur;\" effectue !" );

Par le code suivant : Code : Java - Nouveau mode d'tablissement de la requte de slection /* Cration de l'objet grant les requtes prpares */ preparedStatement = connexion.prepareStatement( "SELECT id, email, mot_de_passe, nom FROM Utilisateur;" ); messages.add( "Requte prpare cre !" ); /* Excution d'une requte de lecture */ resultat = preparedStatement.executeQuery(); messages.add( "Requte \"SELECT id, email, mot_de_passe, nom FROM Utilisateur;\" effectue !" );

Remarquez au passage que contrairement un Statement classique, qui peut tre rutilis plusieurs fois pour excuter des requtes diffrentes, il est ncessaire avec un PreparedStatement de rinitialiser l'objet via un nouvel appel la mthode connexion.prepareStatement(). Ceci est tout btement d au fait que la requte SQL est passe lors de la cration de l'objet, et non plus lors de l'appel aux mthodes d'excution. Enfin, il faut bien entendu remplacer l'initialisation du Statement par un PreparedStatement ; et la fermeture de la ressource Statement par la fermeture de la ressource PreparedStatement : Code : Java - Fermeture du Statement messages.add( "Fermeture de l'objet Statement." ); if ( statement != null ) { try { statement.close(); } catch ( SQLException ignore ) { } }

Code : Java - Fermeture du PreparedStatement messages.add( "Fermeture de l'objet PreparedStatement." ); if ( preparedStatement != null ) { try { preparedStatement.close(); } catch ( SQLException ignore ) {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} }

399/606

Une fois toutes ces modifications effectues et enregistres, suivez alors ce bref scnario de tests : 1. accdez l'URL sans aucun paramtre http://localhost:8080/pro/testjdbc, et observez le rsultat suivant :

2. accdez l'URL correctement renseigne http://localhost:8080/pro/testjdbc?nom [...] ymond@anpe.fr, et observez le rsultat suivant :

3. accdez l'URL contenant des paramtres vides http://localhost:8080/pro/testjdbc?nom [...] passe=&email=, et observez le rsultat suivant :

4. accdez l'URL contenant une tentative d'injection SQL http://localhost:8080/pro/testjdbc?nom [...] rles@lamer.fr, et observez le rsultat suivant :

V ous remarquez ici deux diffrences majeures avec les requtes non prpares : dans le premier test, le passage des valeurs indfinies est cette fois correctement ralis. La base de donnes refuse alors logiquement la requte, en prcisant ici que le champ email ne peut pas contenir null ; dans le dernier test, le paramtre contenant une apostrophe est cette fois correctement insr dans la table, la requte n'choue plus ! En outre, vous remarquez dans le troisime test qu'une erreur est renvoye lors de la tentative d'insertion des valeurs vides. Cela signifie que la requte prpare autorise logiquement les valeurs vides tout comme le faisait le Statement classique. Mais, puisque dans notre table il y a une contrainte d'unicit sur le champ email et qu'il existe dj une ligne contenant une adresse vide, le SGBD n'accepte pas d'insrer une nouvelle ligne contenant encore une telle adresse. V ous devez maintenant tre convaincus de l'intrt des requtes prpares : elles facilitent le passage de valeurs une requte paramtre, et protgent automatiquement les requtes contre tout type d'injections ! Dornavant, je ne veux plus vous voir utiliser de Statement classique. En trois mots : prparez vos requtes ! Avant de passer la suite, il nous reste encore dcouvrir comment rcuprer l'id auto-gnr aprs excution d'une requte d'insertion. En effet, vous savez qu'avec un Statement classique nous prcisions un paramtre supplmentaire lors de l'excution de la requte : Code : Java /* Excution d'une requte d'criture avec renvoi de l'id autognr */ int statut = statement.executeUpdate( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());" , Statement.RETURN_GENERATED_KEYS);

Eh bien de la mme manire que nous devons prciser la requte SQL, non pas l'excution mais ds la cration d'un

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

400/606

PreparedStatement, nous allons devoir prciser ds la cration si nous souhaitons rcuprer l'id auto-gnr ou non, en utilisant une cousine de la mthode connexion.prepareStatement(), qui attend en second argument les mmes constantes que celles utilises avec le Statement : Code : Java /* Cration d'un PreparedStatement avec renvoi de l'id auto-gnr */ PreparedStatement preparedStatement = connexion.prepareStatement( "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES ('jmarc@mail.fr', MD5('lavieestbelle78'), 'jean-marc', NOW());", Statement.RETURN_GENERATED_KEYS );

Le reste ne change pas : il faut parcourir le ResultSet retourn par la mthode preparedStatement.getGeneratedKeys() et y rcuprer le premier champ, tout comme nous l'avions fait avec un Statement classique.

En rsum

Il est ncessaire de charger le driver JDBC une seule et unique fois, au dmarrage de l'application par exemple. JDBC fonctionne par un systme d'URL. Dans le cas de MySQL : jdbc:mysql://hte:port/nom_de_la_bdd . Il est ncessaire d'tablir une connexion entre l'application et la base de donnes, via un appel DriverManager.getConnection() qui retourne un objet Connection. Un appel connexion.createStatement() retourne un objet Statement, qui permet d'effectuer des requtes via notamment ses mthodes executeQuery() et executeUpdate(). L'emploi de cet objet est viter, celui-ci tant sujet de nombreux problmes dont les dangeureuses injections SQL. Prparer ses requtes permet une protection contre ces injections, une gestion simplifie des paramtres et dans certains cas de meilleures performances. Un appel connexion.prepareStatement() retourne un objet PreparedStatement, qui permet d'effectuer des requtes de manire scurise, via notamment ses mthodes executeQuery() et executeUpdate(). Un ensemble de setters facilite l'utilisation de paramtres dans les requtes effectues. Il est ncessaire de librer les ressources utilises lors d'un change avec la base de donnes, en fermant les diffrents objets intervenant dans un ordre prcis via des appels leur mthode close().

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

401/606

Le modle DAO
Nous avons fait un grand pas en avant en dcouvrant la technologie JDBC, mais il est dj ncessaire de nous poser certaines questions. Dans ce chapitre, nous allons : lister les problmes lis la liaison directe entre nos objets mtier et notre base de donnes ; dcouvrir et mettre en place le design pattern DAO, qui rpond parfaitement cette problmatique ; apprendre l'intgrer proprement dans notre application web.

Objectifs Inconvnients de notre solution


Dans le prcdent chapitre, nous avons uniquement test JDBC dans un bac sable, sans vraiment nous soucier de son intgration dans le cas d'une vraie application. Nous avons certes pris la peine de dcouper un minimum notre exemple, en sparant nettement le trio vue, contrleur et modle, mais ce n'est pas suffisant. V oil une reprsentation globale de ce que nous avons actuellement, avec en rouge les objets de notre couche modle directement en contact avec le systme de stockage :

Pourquoi n'est-ce pas suffisant ? La gestion des donnes est bien effectue dans le modle !

En effet, en thorie MVC est ainsi bien respect. Mais dans la pratique, dans une application qui est constitue de plusieurs centaines voire milliers de classes, cela constitue un rel problme de conception : si nous codons une vraie application de cette manire, nous lions alors trs fortement - pour ne pas dire mlangeons - le code responsable des traitements mtier au code responsable du stockage des donnes. Si bien qu'en fin de compte, il devient impossible d'excuter sparment l'un ou l'autre. Et ceci est fcheux pour plusieurs raisons : il est impossible de mettre en place des tests unitaires : impossible de tester le code mtier de l'application sans faire intervenir le stockage (BDD, etc.) ; impossible de ne tester que le code relatif au stockage des donnes, obligation de lancer le code mtier. il est impossible de changer de mode de stockage. Que ce soit vers un autre SGBD, voire vers un systme compltement diffrent d'une base de donnes, cela impliquerait une rcriture complte de tout le modle, car le code mtier est ml avec et dpendant du code assurant le stockage. V ous trouverez toujours quelqu'un qui vous dira que si chaque composant n'est pas testable sparment ce n'est pas un drame, quelqu'un d'autre qui vous dira que lorsque l'on cre une application on ne change pas de mode de stockage du jour au lendemain, etc. Ces gens-l, je vous conseille de ne les couter que d'une oreille : crire un code orient objet et bien organis est une excellente pratique, et c'est ce que je vais vous enseigner dans cette partie du cours. Je pourrais continuer la liste des inconvnients, en insistant notamment sur le fait qu'un code mlang dans une couche "modle" monolithique est bien plus difficile maintenir et faire voluer qu'un code proprement dcoup et organis, mais je pense que vous avez dj compris et tes dj convaincus. Pour information, ce constat n'est absolument pas limit la plate-forme Java. V ous trouverez des solutions qui rpondent ce besoin dans n'importe quelle technologie web, citons par exemple les fat model avec Ruby on Rails.

Isoler le stockage des donnes


L'ide est qu'au lieu de faire communiquer directement nos objets mtier avec la base de donnes, ou le systme de fichiers, ou

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

402/606

les webservices, ou peu importe ce qui fait office de systme de stockage, ceux-ci vont parler avec la couche DAO. Et c'est cette couche DAO qui va ensuite de son ct communiquer avec le systme de stockage. L'objectif de l'architecture que nous devons mettre en place n'est donc rien d'autre que l'isolement pur et simple du code responsable du stockage des donnes. Nous souhaitons en effet littralement encapsuler ce code dans une couche plus ou moins hermtique, de laquelle aucune information concernant le mode de stockage utilis ne s'chappe. En d'autres termes, notre objectif est de cacher la manire dont sont stockes les donnes au reste de l'application. V oil cette fois une reprsentation de ce que nous souhaitons mettre en place :

V ous visualisez bien que seul le DAO est en contact avec le systme de stockage, et donc que seul lui en a connaissance. Le revers de la mdaille, si c'en est un, c'est qu'afin de raliser le cloisonnement du stockage des donnes, la cration d'une nouvelle couche est ncessaire, c'est--dire l'criture de codes supplmentaires et rptitifs. En rsum, l'objectif du pattern DAO tient en une phrase : il permet de faire la distinction entre les donnes auxquelles vous souhaitez accder, et la manire dont elles sont stockes.

Principe Constitution
Le principe du pattern DAO est de sparer la couche modle d'une application en deux sous-couches distinctes : une couche grant les traitements mtier appliqus aux donnes, souvent nomme couche service ou mtier. Typiquement, tout le travail de validation ralis dans nos objets InscriptionForm et ConnexionForm en fait partie ; une couche grant le stockage des donnes, logiquement nomme couche de donnes. Il s'agit l des oprations classiques de stockage : la cration, la lecture, la modification et la suppression. Ces quatre tches basiques sont souvent raccourcies l'anglaise en CRUD.

Pour raliser efficacement une telle opration, il est ncessaire d'encapsuler les exceptions spcifiques au mode de stockage dans des exceptions personnalises et propres la couche DAO. Dans notre cas par exemple, nous allons devoir faire en sorte que les exceptions propres SQL ou JDBC ne soient pas vues comme telles par nos objets mtier, mais uniquement comme des exceptions manant de la bote noire qu'est notre DAO. De mme, il va falloir masquer le code responsable du stockage au code extrieur , et l'exposer uniquement via des interfaces. Dans notre cas, il s'agira donc de faire en sorte que le code bas sur JDBC soit bien l'abri dans des implmentations de DAO, et que nos objets mtier n'aient connaissance que des interfaces qui les dcrivent. Reprenons le schma prcdent et zoomons sur la couche DAO :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

403/606

La couche modle est constitue de la couche mtier (en rouge) et de la couche donnes (en vert). La couche mtier n'a connaissance que des interfaces dcrivant les objets de la couche donnes. Ainsi, peu importe le systme de stockage final utilis, du point de vue du code mtier les mthodes appeler ne changent pas, elles seront toujours celles dcrites dans l'interface. C'est uniquement l'implmentation qui sera spcifique au mode de stockage.

Intgration
Ne soyez pas leurrs par le schma prcdent, la couche DAO ne va pas seulement contenir les interfaces et implmentations des mthodes CRUD. Elle va galement renfermer quelques classes ddies l'isolement des concepts lis au mode de stockage, comme les exceptions dont nous avons dj brivement parl, mais galement le chargement du driver et l'obtention d'une connexion. Pour ce faire, nous allons crer une Factory (une fabrique) qui sera unique dans l'application, ne sera instancie que si les informations de configuration sont correctes et aura pour rle de fournir les implmentations des diffrents DAO. En ce qui concerne la relation entre la couche mtier et le DAO, c'est trs simple : les objets mtier appellent les mthodes CRUD, qui ont pour rle de communiquer avec le systme de stockage et de peupler les beans reprsentant les donnes. Assez gamberg, du code vaut mieux qu'un long discours !

Cration
Pour commencer, nous allons laisser tomber le bac sable dans lequel nous avons volu dans le chapitre prcdent, et allons reprendre les systmes d'inscription et de connexion que nous avions utiliss jusqu' prsent. Je vous demande donc de supprimer de votre projet les classes GestionTestJDBC.java et TestJDBC.java ainsi que la JSP test_jdbc.jsp, car nous n'allons pas les rutiliser.

Modification de la table Utilisateur


En ce qui concerne la BDD, nous allons conserver la base de donnes bdd_sdzee et le compte java que nous y avons cr, mais allons modifier la table d'exemple Utilisateur. Plus prcisment, nous allons modifier le type du champ mot_de_passe. Pourquoi ? Qu'est-ce qui ne va pas avec le type utilis dans notre bac sable ?

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

404/606

Je ne vous en ai volontairement pas parl jusqu'ici pour ne pas compliquer votre apprentissage de JDBC, mais maintenant que vous tes l'aise, discutons-en ! Dans l'exemple du chapitre prcdent, j'ai choisi de dlguer le chiffrement du mot de passe de l'utilisateur la fonction MD5() de MySQL afin de ne pas encombrer inutilement le code. Seulement c'est une mauvaise pratique, notamment parce que : d'une part, c'est une fonction propre MySQL. Si vous optez pour un autre SGBD, voire pour un autre systme de stockage, vous n'tes pas certains de pouvoir lui trouver un quivalent ; d'autre part, et c'est l le point le plus important, c'est un algorithme rapide et peu sr qu'il est dconseill d'utiliser pour la scurisation de mots de passe. Je ne vais pas vous faire un cours de cryptographie, vous trouverez ce sujet d'excellentes bases sur le Site du Zro et plus largement sur le web. Sachez simplement que pour scuriser de manire efficace les mots de passe de vos utilisateurs, il est prfrable aujourd'hui : d'utiliser un algorithme de hashage relativement fort et lent. Typiquement, une des variantes de SHA-2 (SHA-224, SHA256, SHA-384 ou SHA-512) est un excellent choix ; d'associer au mot de passe un grain de sel alatoire et suffisamment long : 1) pour faire en sorte que deux mots de passe identiques aient une empreinte diffrente ; 2) afin d'empcher un ventuel pirate d'utiliser des structures de donnes comme les fameuses rainbow tables pour dchiffrer rapidement une empreinte ; d'itrer rcursivement la fonction de hashage un trs grand nombre de fois, mais surtout pas uniquement sur l'empreinte rsultant du hashage prcdent afin de ne pas augmenter les risques de collisions, en veillant bien rintroduire chaque itration des donnes comme le sel et le mot de passe d'origine, avec pour objectif de rendre le travail d'un ventuel pirate autant de fois plus lent. Bref, vous l'aurez compris, pour un dbutant c'est presque mission impossible de protger efficacement les mots de passe de ses utilisateurs ! Heureusement, il existe des solutions simplifiant grandement cette opration, et notre choix va se porter sur la bibliothque Jasypt. En quelques mots, il s'agit d'une surcouche aux API de cryptographie existant nativement dans Java, qui fournit des objets et mthodes trs faciles d'accs afin de chiffrer des donnes. Trs bien, mais en quoi cela va-t-il impacter le type de notre champ mot_de_passe dans notre table Utilisateur ?

Eh bien comme je vous l'ai dit, nous n'allons plus utiliser l'algorithme MD5, mais lui prfrer cette fois SHA-256. Le premier gnrait des empreintes longues de 32 caractres, voil pourquoi nous les stockions dans un champ SQL de taille 32. Le second gnre des empreintes longues de 64 caractres, que Jasypt encode pour finir l'aide de Base64 en chanes longues de 56 caractres. Celles-ci ne vont donc pas rentrer dans le champ mot_de_passe que nous avions dfini, et nous devons donc le modifier via la commande suivante excuter depuis l'invite de commandes de votre serveur MySQL : Code : SQL - Modification du type du champ mot_de_passe dans la table des utilisateurs ALTER TABLE Utilisateur CHANGE mot_de_passe mot_de_passe CHAR(56) NOT NULL;

Bien entendu, avant d'effectuer cette modification, vous n'oublierez pas de vous positionner sur la bonne base en excutant la commande USE bdd_sdzee; aprs connexion votre serveur MySQL.

Reprise du bean Utilisateur


Nous disposons dj d'un bean Utilisateur, sur lequel nous avons travaill jusqu' prsent, mais celui-ci ne contient pour l'instant qu'une adresse mail, un nom et un mot de passe. Afin qu'il devienne utilisable pour effectuer la correspondance avec les donnes stockes dans notre table Utilisateur, il nous faut raliser quelques ajouts : Code : Java - com.sdzee.beans.Utilisateur package com.sdzee.beans; import java.sql.Timestamp; public class Utilisateur {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


private Long id; private String email; private String motDePasse; private String nom; private Timestamp dateInscription; public Long getId() { return id; } public void setId( Long id ) { this.id = id; } public void setEmail( String email ) { this.email = email; } public String getEmail() { return email; } public void setMotDePasse( String motDePasse ) { this.motDePasse = motDePasse; } public String getMotDePasse() { return motDePasse; } public void setNom( String nom ) { this.nom = nom; } public String getNom() { return nom; } public Timestamp getDateInscription() { return dateInscription; } public void setDateInscription( Timestamp dateInscription ) { this.dateInscription = dateInscription; } }

405/606

C'est simple, nous avons simplement cr deux nouvelles proprits id et dateInscription stockant logiquement l'id et la date d'inscription, et nous disposons maintenant d'un bean qui reprsente parfaitement une ligne de notre table Utilisateur. Pourquoi avoir utilis un objet Long pour stocker l'id ?

En effet, nous aurions pu a priori nous contenter d'un type primitif long. Seulement dans une base de donnes les valeurs peuvent tre initialises NULL, alors qu'un type primitif en Java ne peut pas valoir null. V oil pourquoi il est dconseill de travailler directement avec les types primitifs, et de leur prfrer les objets enveloppeurs (les fameux Wrapper) : ceux-ci peuvent en effet tre initialiss null. En l'occurrence, les champs de notre table SQL se voient tous appliquer une contrainte NOT NULL, il n'y a donc pas de risque de valeurs nulles. Cependant, c'est une bonne pratique de toujours utiliser les objets Wrapper dans les beans dont les proprits correspondent des champs d'une base de donnes, afin d'viter ce genre d'erreurs !

Maintenant que nous avons cr les diffrentes reprsentations d'un utilisateur dans notre application, nous allons pouvoir nous attaquer au fameux cloisonnement de la couche de donnes, autrement dit la cration proprement parler de notre DAO.

Cration des exceptions du DAO


www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

406/606

Afin de cacher la nature du mode de stockage des donnes au reste de l'application, c'est une bonne pratique de masquer les exceptions spcifiques (celles qui surviennent au runtime, c'est--dire lors de l'excution) derrire des exceptions propres au DAO. Je m'explique. Typiquement, nous allons dans notre application avoir besoin de grer deux types d'exceptions concernant les donnes : celles qui sont lies la configuration du DAO et du driver JDBC ; celles qui sont lies l'interaction avec la base de donnes. Dans la couche modle actuelle de notre systme d'inscription, nous nous apprtons introduire le stockage des donnes. Puisque nous avons dcid de suivre le modle de conception DAO, nous n'allons pas raliser les manipulations sur la base de donnes directement depuis les traitements mtier, nous allons appeler des mthodes de notre DAO, qui leur tour la manipuleront. Nous obtiendrons ainsi un modle divis en deux sous-couches : une couche mtier et une couche de donnes. Seulement vous vous en doutez, lors d'une tentative de lecture ou d'criture dans la base de donnes, il peut survenir de nombreux types d'incidents : des soucis de connexions, des requtes incorrectes, des donnes absentes, la base qui ne rpond plus, etc. Et chacune de ces erreurs correspond une exception SQL ou JDBC particulire. Eh bien notre objectif ici, c'est de faire en sorte que depuis l'extrieur de la couche de donnes, aucune de ces exceptions ne sorte directement sous cette forme. Pour ce faire, c'est extrmement simple, il nous suffit de crer une exception personnalise qui va encapsuler les exceptions lies SQL ou JDBC. V oici donc le code de nos deux nouvelles exceptions : Code : Java - com.sdzee.dao.DAOException package com.sdzee.dao; public class DAOException extends RuntimeException { /* * Constructeurs */ public DAOException( String message ) { super( message ); } public DAOException( String message, Throwable cause ) { super( message, cause ); } public DAOException( Throwable cause ) { super( cause ); }

Code : Java - com.sdzee.dao.DAOConfigurationException package com.sdzee.dao; public class DAOConfigurationException extends RuntimeException { /* * Constructeurs */ public DAOConfigurationException( String message ) { super( message ); } public DAOConfigurationException( String message, Throwable cause ) { super( message, cause ); } public DAOConfigurationException( Throwable cause ) { super( cause ); }

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

407/606

Comme vous pouvez le constater, il s'agit uniquement de classes hritant de RuntimeException, qui se contentent de redfinir les constructeurs. Ne vous inquitez pas si vous ne saisissez pas encore bien pourquoi nous avons besoin de ces deux exceptions, vous allez trs vite vous en rendre compte dans les codes qui suivent !

Cration d'un fichier de configuration


Dans le chapitre prcdent, nous avions directement stock en dur l'adresse et les identifiants de connexion notre base de donnes, dans le code Java de notre objet. C'tait pratique pour l'exemple, mais vous vous doutez bien que nous n'allons pas procder de manire aussi brute dans une vraie application. Afin de sparer les informations de configuration du reste de lapplication, il est recommand de les placer dans un endroit accessible par le code Java et aisment modifiable la main. Une bonne pratique trs courante dans ce genre de cas est la mise en place d'un fichier properties, qui n'est rien d'autre qu'un fichier texte dont les lignes respectent un certain format. Nous allons donc crer un fichier nomm dao.properties , que nous allons placer dans le package com.sdzee.dao : Code : Fichier properties - dao.properties url = jdbc:mysql://localhost:3306/bdd_sdzee driver = com.mysql.jdbc.Driver nomutilisateur = java motdepasse = $dZ_E

V ous retrouvez dans ce fichier les informations de connexion notre base. Chacune d'elles est associe une cl, reprsente par la chane de caractres place gauche du signe gal sur chacune des lignes. Puisque ce fichier contient des informations confidentielles, comme le mot de passe de connexion la base de donnes, il est hors de question de le placer dans un rpertoire public de l'application ; il est impratif de le placer sous /WEBINF ou un de ses sous-rpertoires ! Si vous le mettiez par exemple directement la racine, sous le rpertoire WebContent d'Eclipse, alors ce fichier deviendrait accessible depuis le navigateur du client...

Dans une vraie application, les fichiers de configuration ne sont pas stocks au sein de l'application web, mais en dehors. Il devient ainsi trs facile d'y modifier une valeur sans avoir naviguer dans le contenu de l'application. En outre, mais nous tudierons cela en annexe de ce cours, cela permet de ne pas avoir besoin de redployer l'application chaque modification, et de se contenter de simplement redmarrer le serveur. Toutefois dans notre cas, nous n'en sommes qu'au stade de l'apprentissage et n'allons donc pas nous embter avec ces considrations : placer le fichier sous /WEB-INF nous convient trs bien !

Cration d'une Factory


Nous arrivons maintenant une tape un peu plus dlicate de la mise en place de notre couche de donnes. Il nous faut crer la Factory qui va tre en charge de l'instanciation des diffrents DAO de notre application. Alors certes, pour le moment nous n'avons qu'une seule table en base et donc un seul DAO mettre en place. Mais rappelez-vous : l'important est de crer un code facile maintenir et faire voluer. En outre, maintenant que nous avons plac nos informations de connexion dans un fichier part, cette Factory va tre responsable de : lire les informations de configuration depuis le fichier properties ; charger le driver JDBC du SGBD utilis ; fournir une connexion la base de donnes. Je vous donne le code, relativement bref, et je vous commente le tout ensuite : Code : Java - com.sdzee.dao.DAOFactory package com.sdzee.dao;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import import import import import import import java.io.FileNotFoundException; java.io.IOException; java.io.InputStream ; java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.util.Properties;

408/606

public class DAOFactory { private static final String FICHIER_PROPERTIES "/com/sdzee/dao/dao.properties"; private static final String PROPERTY_URL private static final String PROPERTY_DRIVER private static final String PROPERTY_NOM_UTILISATEUR "nomutilisateur"; private static final String PROPERTY_MOT_DE_PASSE "motdepasse"; private String private String private String url; username; password; = = "url"; = "driver"; = =

DAOFactory( String url, String username, String password ) { this.url = url; this.username = username; this.password = password; } /* * Mthode charge de rcuprer les informations de connexion la base de * donnes, charger le driver JDBC et retourner une instance de la Factory */ public static DAOFactory getInstance() throws DAOConfigurationException { Properties properties = new Properties(); String url; String driver; String nomUtilisateur; String motDePasse; ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream fichierProperties = classLoader.getResourceAsStream( FICHIER_PROPERTIES ); if ( fichierProperties == null ) { throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable." ); } try { properties.load( fichierProperties ); url = properties.getProperty( PROPERTY_URL ); driver = properties.getProperty( PROPERTY_DRIVER ); nomUtilisateur = properties.getProperty( PROPERTY_NOM_UTILISATEUR ); motDePasse = properties.getProperty( PROPERTY_MOT_DE_PASSE ); } catch ( IOException e ) { throw new DAOConfigurationException( "Impossible de charger le fichier properties " + FICHIER_PROPERTIES, e ); } try { Class.forName( driver ); } catch ( ClassNotFoundException e ) {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


throw new DAOConfigurationException( "Le driver est introuvable dans le classpath.", e ); } DAOFactory instance = new DAOFactory( url, nomUtilisateur, motDePasse ); return instance; } /* Mthode charge de fournir une connexion la base de donnes */ /* package */ Connection getConnection() throws SQLException { return DriverManager.getConnection( url, username, password ); } /* * Mthodes de rcupration de l'implmentation des diffrents DAO (un seul * pour le moment) */ public UtilisateurDao getUtilisateurDao() { return new UtilisateurDaoImpl( this ); } }

409/606

Pour commencer, analysons la mthode getInstance() qui, comme son nom l'indique, a pour principal objectif d'instancier la classe DAOFactory . En premier lieu, remarquez qu'il s'agit l d'une mthode statique. Il n'y a ici rien de compliqu, a tient tout simplement du bon sens : si nous n'avions pas dclar cette mthode static, alors nous ne pourrions pas l'appeler avant d'avoir instanci la classe DAOFactory ... Alors que c'est justement l le but de la mthode ! Pourquoi avoir utilis une telle mthode statique, et pas simplement un constructeur public ?

En effet, la plupart du temps lorsque vous souhaitez crer un objet, vous vous contentez d'appeler un de ses constructeurs publics. Typiquement, pour crer un objet de type A, vous crivez A monObjet = new A(); . La limite de cette technique, c'est qu' chaque appel un nouvel objet va tre cr quoi qu'il arrive. Dans notre cas, ce n'est pas ce que nous souhaitons ! Je vous l'ai dj expliqu, nous voulons instancier notre DAOFactory uniquement sous certaines conditions : si le fichier dao.properties est accessible ; si les donnes qu'il contient sont valides ; si le driver JDBC est bien prsent dans l'application. V oil pourquoi nous utilisons une mthode statique et pas un simple constructeur. Pour information, sachez que c'est une pratique courante dans normment de projets et de bibliothques. V oil tout pour cet apart, revenons notre analyse du code. la ligne 34, nous initialisons un objet Properties qui, comme son nom l'indique, va nous permettre de grer notre fichier de configuration. Ensuite, nous procdons l'ouverture du fichier dao.properties aux lignes 40 et 41. Qu'est-ce que c'est que cet objet ClassLoader et cette mthode getResourceAsStream() ? Pourquoi ne pas avoir utilis un traditionnel FileInputStream pour ouvrir notre fichier properties ? En effet, nous aurions pu confier cette tche un simple FileInputStream, seulement il nous aurait alors fallu : donner le chemin complet vers le fichier dans le systme, pour procder l'ouverture du flux ; grer une ventuelle FileNotFoundException dans un bloc catch, et procder la fermeture du flux dans un bloc finally. En lieu et place de cette technique, j'utilise ici une mthode plus sophistique premire vue mais qui en ralit est trs simple : elle consiste appeler la mthode getResourceAsStream() de l'objet ClassLoader, qui se charge pour nous d'ouvrir le flux demand et de retourner null en cas d'erreur. Comme vous pouvez l'observer dans le code, nous rcuprons le

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


ClassLoader depuis le thread courant grce la mthode getContextClassLoader().

410/606

Nous aurions pu crire le tout en une ligne, mais la dcomposition en deux actions distinctes vous permet de comprendre plus facilement de quoi il s'agit. Si vous n'tes pas familiers avec les objets manipuls ici, qui font partie de la plate-forme Java SE, la Javadoc est l pour vous renseigner : n'hsitez pas parcourir les liens que je vous donne !

Une fois l'appel la mthode getResourceAsStream() ralis, nous vrifions la ligne 43 si elle a retourn null, ce qui signifierait alors que le flux n'a pas pu tre ouvert. Si tel est le cas, nous envoyons une exception personnalise DAOConfigurationException stipulant que le fichier n'a pas t trouv. Nous procdons ensuite au chargement des proprits contenues dans le fichier la ligne 48, puis leur lecture aux lignes 49 52. Je vous laisse parcourir les documentations des mthodes Properties.load() et Properties.getProperty() si vous n'tes pas l'aise avec ces manipulations. Aprs tout, ceci n'est pas l'objet de ce cours et vous devriez dj connatre cette fonctionnalit si vous avez correctement tudi Java SE ! Observez alors attentivement ce que nous effectuons au niveau du bloc catch : nous interceptons l'exception ventuellement envoye en cas d'erreur lors du chargement des proprits (format du fichier properties incorrect), et l'encapsulons dans une exception DAOConfigurationException. V ous devez ici commencer mieux comprendre pourquoi nous avons pris la peine de crer des exceptions personnalises, mais ne vous inquitez pas si vous n'avez pas encore bien saisi, nous y reviendrons plus tard. Une fois les informations lues avec succs, nous tentons de charger le driver JDBC dont le nom est prcis dans le fichier dao.properties , en l'occurrence dans notre cas il s'agit du driver pour MySQL. Le principe est le mme que dans le chapitre prcdent, nous effectuons un simple appel Class.forName(). L encore, observez l'encapsulation de l'exception envoye en cas d'erreur (driver introuvable) dans une exception personnalise de type DAOConfigurationException. Pour en terminer avec cette mthode getInstance(), en cas de succs des tapes prcdentes nous instancions la DAOFactory en faisant appel au constructeur dfini aux lignes 23 27. Nous crons ensuite la mthode getConnection() charge de fournir une connexion la base de donnes, aux lignes 68 70. L encore pas de surprise, nous utilisons le mme principe que dans le chapitre prcdent, savoir un appel DriverManager.getConnection(). Par ailleurs, vous avez l un bel exemple de l'utilisation d'une mthode statique la place d'un constructeur public. En effet, nous ne rcuprons pas un objet de type Connection en appelant un constructeur public, mais en faisant appel une mthode statique de l'objet DriverManager !

Enfin, nous devons crire les getters retournant les diffrentes implmentations de DAO contenues dans notre application, ce qui, ne l'oublions pas est le rle d'origine de notre DAOFactory . Comme je vous l'ai dj prcis, pour le moment nous ne travaillons que sur une seule table et n'allons donc crer qu'un seul DAO ; voil pourquoi nous n'avons qu'un seul getter mettre en place, aux lignes 76 78. Ne vous proccupez pas du code de ce getter pour le moment, j'y reviendrai aprs vous avoir expliqu les interfaces et implmentations du DAO Utilisateur. De mme, ne vous inquitez pas si le code ne compile pas encore, il compilera lorsque nous aurons cr les classes mises en jeu !

Cration de l'interface du DAO Utilisateur


Avant de crer une implmentation de notre DAO Utilisateur, il nous faut crire le contrat qui va dfinir toutes les actions qui devront tre effectues sur les donnes, c'est--dire sur le bean Utilisateur. Qui dit contrat dit interface, et c'est donc une interface que nous allons mettre en place. En ce qui concerne le nommage des classes et interfaces, il existe diffrentes bonnes pratiques. Pour ma part, je nomme AbcDao l'interface d'un DAO correspondant la table Abc, et AbcDaoImpl son implmentation. Je vous conseille de suivre cette rgle galement, cela permet de s'y retrouver rapidement dans une application qui contient beaucoup de DAO diffrents ! Nous crons ici un DAO correspondant la table Utilisateur, nous allons donc crer une interface nomme UtilisateurDao :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Code : Java - com.sdzee.dao.UtilisateurDao package com.sdzee.dao; import com.sdzee.beans.Utilisateur; public interface UtilisateurDao { void creer( Utilisateur utilisateur ) throws DAOException; Utilisateur trouver( String email ) throws DAOException; }

411/606

V ous retrouvez ici les deux mthodes mises en jeu par nos formulaires d'inscription et de connexion : la cration d'un utilisateur, lors de son inscription ; la recherche d'un utilisateur, lors de la connexion. Ces mthodes font partie du fameux CRUD dont je vous ai parl auparavant ! Notre systme se limitant ces deux fonctionnalits, nous n'allons volontairement pas implmenter les mthodes de mise jour et de suppression d'un utilisateur. Dans une vraie application toutefois, il va de soi que l'interface de chaque DAO se doit d'tre complte. Petite information au passage : en Java, les mthodes d'une interface sont obligatoirement publiques et abstraites, inutile donc de prciser les mots-cls public et abstract dans leurs signatures. L'criture reste permise, mais elle est dconseille dans les spcifications Java SE publies par Oracle, dans le chapitre concernant les interfaces.

Cration de l'implmentation du DAO


Nous voil arrivs l'tape finale : la cration de l'implmentation de notre DAO Utilisateur. Il s'agit de la classe qui va manipuler la table Utilisateur de notre base de donnes. C'est donc elle qui va contenir le code des mthodes creer() et trouver() dfinies dans le contrat que nous venons tout juste de mettre en place. La premire chose faire, c'est de crer une classe qui implmente l'interface UtilisateurDao. Nous allons bien entendu suivre la convention de nommage que nous avons adopte, et nommer cette implmentation UtilisateurDaoImpl : Code : Java - com.sdzee.dao.UtilisateurDaoImpl public class UtilisateurDaoImpl implements UtilisateurDao { /* Implmentation de la mthode trouver() dfinie dans l'interface UtilisateurDao */ @Override public Utilisateur trouver( String email ) throws DAOException { return null; } /* Implmentation de la mthode creer() dfinie dans l'interface UtilisateurDao */ @Override public void creer( Utilisateur utilisateur ) throws IllegalArgumentException, DAOException { } }

V ous le savez trs bien, en Java lorsqu'une classe implmente une interface, elle doit imprativement dfinir toutes les mthodes qui y sont dcrites. En l'occurrence notre interface UtilisateurDao contient deux mthodes creer() et trouver(), voil pourquoi nous devons crire leur code dans notre implmentation. Nous devons maintenant rflchir un peu. Dans l'architecture que nous sommes en train de construire, nous avons mis en place

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

412/606

une Factory. En plus d'assurer sa fonction principale, c'est--dire crer le DAO via le getter getUtilisateurDao(), elle joue le rle de pierre angulaire : c'est par son intermdiaire que le DAO va pouvoir acqurir une connexion la base de donnes, en appelant sa mthode getConnection(). Pour pouvoir appeler cette mthode et ainsi rcuprer une connexion, le DAO doit donc avoir accs une instance de la DAOFactory. Comment faire ? C'est simple, nous avons dj prpar le terrain la ligne 79 du code de notre DAOFactory ! Si vous regardez bien, nous passons une instance de la classe (via le mot-cl this) l'implmentation du DAO lors de sa construction. Ainsi, il nous suffit de crer un constructeur qui prend en argument un objet de type DAOFactory dans notre DAO : Code : Java - com.sdzee.dao.UtilisateurDaoImpl public class UtilisateurDaoImpl implements UtilisateurDao { private DAOFactory daoFactory; UtilisateurDaoImpl( DAOFactory daoFactory ) { this.daoFactory = daoFactory; } } ...

Jusque-l, rien de bien compliqu. Mais ne vous endormez surtout pas, le gros du travail reste faire ! Nous devons maintenant crire le code des mthodes creer() et trouver(), autrement dit le code travers lequel nous allons communiquer avec notre base de donnes ! V ous connaissez dj le principe : connexion, requte prpare, etc. ! Plutt que d'crire btement du code la chane en recopiant et adaptant ce que nous avions crit dans le chapitre prcdent, nous allons tudier les besoins de nos mthodes et rflchir ce qu'il est possible de factoriser dans des mthodes utilitaires. Mieux encore, nous n'allons pas nous concentrer sur ces deux mthodes en particulier, mais rflchir plus globalement ce que toute mthode communiquant avec une base de donnes doit faire intervenir. Nous pouvons d'ores et dj noter : initialiser une requte prpare avec des paramtres ; rcuprer une ligne d'une table et enregistrer son contenu dans un bean ; fermer proprement les ressources ouvertes (Connection, PreparedStatement, ResultSet). Pour accomplir ces diffrentes tches, a priori nous allons avoir besoin de trois mthodes utilitaires : 1. une qui rcupre une liste de paramtres et les ajoute une requte prpare donne ; 2. une qui rcupre un ResultSet et enregistre ses donnes dans un bean ; 3. une qui ferme toutes les ressources ouvertes. tudions tout cela tape par tape.

1. Initialisation d'une requte prpare


Nous avons besoin d'une mthode qui va prendre en argument une connexion, une requte SQL et une liste d'objets, et s'en servir pour initialiser une requte prpare via un appel connexion.prepareStatement(). ce propos, rappelez-vous de ce que nous avons dcouvert dans le chapitre prcdent : c'est lors de cet appel qu'il faut prciser si la requte doit retourner un champ auto-gnr ou non ! Le problme, c'est que nous ne pouvons pas savoir l'avance si notre requte a besoin de retourner cette information ou non. V oil pourquoi nous allons ajouter un quatrime argument notre mthode, dont voici le code complet : Code : Java /* * Initialise la requte prpare base sur la connexion passe en argument, * avec la requte SQL et les objets donns. */ public static PreparedStatement initialisationRequetePreparee( Connection connexion, String sql, boolean returnGeneratedKeys, Object... objets ) throws SQLException {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


PreparedStatement preparedStatement = connexion.prepareStatement( sql, returnGeneratedKeys ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS ); for ( int i = 0; i < objets.length; i++ ) { preparedStatement.setObject( i + 1, objets[i] ); } return preparedStatement; }

413/606

Observez les quatre arguments passs la mthode : la connexion, dont nous avons besoin pour appeler la mthode connexion.prepareStatement() ; la requte SQL, que nous passons en argument lors de l'appel pour construire l'objet PreparedStatement ; un boolen, indiquant s'il faut ou non retourner d'ventuelles valeurs auto-gnres. Nous l'utilisons alors directement au sein de l'appel connexion.prepareStatement() grce une simple expression ternaire ; une succession d'objets... de tailles variables ! Eh oui, l encore nous n'avons aucun moyen d'anticiper et de savoir l'avance combien de paramtres attend notre requte prpare. Que signifient ces '...' dans le type du dernier argument de la mthode ?

C'est une notation purement Java, dite varargs. V ous pouvez la voir comme un joker : en dclarant ainsi votre mthode, vous pourrez ensuite l'appeler avec autant de paramtres que vous voulez (tant qu'ils respectent le type dclar), et l'appel fonctionnera toujours ! La seule diffrence sera la taille de l'objet pass en tant que quatrime et dernier argument ici. Exemples : Code : Java - Exemples d'appels avec un nombre variable d'arguments initialisationRequetePreparee( connexion, initialisationRequetePreparee( connexion, motDePasse ); initialisationRequetePreparee( connexion, motDePasse, nom ); initialisationRequetePreparee( connexion, motDePasse, nom, dateInscription ); requeteSQL, true, email); requeteSQL, true, email, requeteSQL, true, email, requeteSQL, true, email,

Bien qu'ils ne prsentent pas tous le mme nombre d'arguments, tous ces appels font bien rfrence une seule et mme mthode : celle que nous avons prcdemment dfinie, et dont la signature ne mentionne que quatre arguments ! En ralit sous la couverture, le compilateur regroupera lui-mme tous les arguments supplmentaires dans un simple tableau. En fin de compte, cette notation varargs s'apparente en quelque sorte un tableau implicite. Dans ce cas, pourquoi ne pas utiliser directement un tableau, en dclarant la place de Object... un argument de type Object[] ? Tout simplement parce que cela compliquerait l'utilisation de la mthode. En effet, si nous devions absolument passer un objet de type tableau, alors il faudrait prendre la peine dinitialiser un tableau avant d'appeler la mthode. Nos exemples prcdents deviendraient alors : Code : Java - Exemples d'appels avec un tableau Object[] objets = { email }; initialisationRequetePreparee( connexion, requeteSQL, objets = { email, motDePasse }; initialisationRequetePreparee( connexion, requeteSQL, objets = { email, motDePasse, nom }; initialisationRequetePreparee( connexion, requeteSQL, objets = { email, motDePasse, nom, dateInscription }; initialisationRequetePreparee( connexion, requeteSQL,

true, objets); true, objets); true, objets); true, objets);

Observez la diffrence avec l'exemple prcdent : les appels la mthode sont, cette fois, tous identiques, mais le fait de devoir initialiser des tableaux avant chaque appel est pnible et complique la lecture du code.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

414/606

Au sujet de la nature de ce tableau implicite, pourquoi le dclarer de type Object ?

Eh bien parce que l encore, nous n'avons aucun moyen d'anticiper et de dterminer l'avance ce que notre requte attend en guise de paramtres ! V oil pourquoi nous devons utiliser le type le plus global possible, savoir Object. D'ailleurs, en consquence nous n'allons pas pouvoir faire appel aux mthodes preparedStatement.setString(), preparedStatement.setInt(), etc. que nous avions dcouvertes et utilises dans le chapitre prcdent, puisque nous n'aurons ici aucun moyen simple de savoir de quel type est chaque objet. Heureusement, il existe une mthode preparedStatement.setObject() qui prend en argument un objet de type Object, et qui s'occupe ensuite derrire les rideaux d'effectuer la conversion vers le type SQL du paramtre attendu avant d'envoyer la requte la base de donnes. Sympathique, n'est-ce pas ?

Nous y voil : ce grand apart sur les varargs vous donne toutes les cls pour comprendre ce qui se passe dans la boucle for qui conclut notre mthode. Nous y parcourons le tableau implicite des arguments passs la mthode lors de son appel, et plaons chacun d'eux dans la requte prpare via un appel la mthode preparedStatement.setObject(). En fin de compte, c'est ici une mthode courte que nous avons ralise, mais qui fait intervenir des concepts intressants et qui va nous tre trs utile par la suite. Par ailleurs, notez qu'il s'agit d'une mthode purement utilitaire, que nous pourrons rutiliser telle quelle dans n'importe quel DAO !

2. Mapping d'un ResultSet dans un bean


Abordons maintenant notre seconde mthode utilitaire, qui va nous permettre de faire la correspondance entre une ligne d'un ResultSet et un bean. Contrairement notre prcdente mthode, nous n'allons cette fois pas pouvoir totalement dcoupler la mthode de notre DAO en particulier, puisque nous travaillons directement sur un ResultSet issu de notre table d'utilisateurs et sur un bean de type Utilisateur. V oici le code : Code : Java /* * Simple mthode utilitaire permettant de faire la correspondance (le * mapping) entre une ligne issue de la table des utilisateurs (un * ResultSet) et un bean Utilisateur. */ private static Utilisateur map( ResultSet resultSet ) throws SQLException { Utilisateur utilisateur = new Utilisateur(); utilisateur.setId( resultSet.getLong( "id" ) ); utilisateur.setEmail( resultSet.getString( "email" ) ); utilisateur.setMotDePasse( resultSet.getString( "mot_de_passe" ) ); utilisateur.setNom( resultSet.getString( "nom" ) ); utilisateur.setDateInscription( resultSet.getTimestamp( "date_inscription" ) ); return utilisateur; }

Rien de bien compliqu ici : notre mthode prend en argument un ResultSet dont le curseur a dj t correctement positionn, et place chaque champ lu dans la proprit correspondante du nouveau bean cr.

3. Fermeture des ressources


Pour terminer, nous allons grer proprement la fermeture des diffrentes ressources qui peuvent intervenir dans une communication avec la base de donnes. Plutt que de tout mettre dans une seule et mme mthode, nous allons crire une mthode pour fermer chaque type de ressource : Code : Java

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


/* Fermeture silencieuse du resultset */ public static void fermetureSilencieuse( ResultSet resultSet ) { if ( resultSet != null ) { try { resultSet.close(); } catch ( SQLException e ) { System.out.println( "chec de la fermeture du ResultSet : " + e.getMessage() ); } } } /* Fermeture silencieuse du statement */ public static void fermetureSilencieuse( Statement statement ) { if ( statement != null ) { try { statement.close(); } catch ( SQLException e ) { System.out.println( "chec de la fermeture du Statement : " + e.getMessage() ); } } } /* Fermeture silencieuse de la connexion */ public static void fermetureSilencieuse( Connection connexion ) { if ( connexion != null ) { try { connexion.close(); } catch ( SQLException e ) { System.out.println( "chec de la fermeture de la connexion : " + e.getMessage() ); } } } /* Fermetures silencieuses du statement et de la connexion */ public static void fermeturesSilencieuses( Statement statement, Connection connexion ) { fermetureSilencieuse( statement ); fermetureSilencieuse( connexion ); } /* Fermetures silencieuses du resultset, du statement et de la connexion */ public static void fermeturesSilencieuses( ResultSet resultSet, Statement statement, Connection connexion ) { fermetureSilencieuse( resultSet ); fermetureSilencieuse( statement ); fermetureSilencieuse( connexion ); }

415/606

V ous connaissez dj le principe des trois premires mthodes, puisque nous avons effectu sensiblement la mme chose dans le chapitre prcdent. Nous crons ensuite deux mthodes qui font appel tout ou partie des trois premires, tout bonnement parce que certaines requtes font intervenir un ResultSet, d'autres non ! En outre, notez que tout comme la mthode initialisationRequetePreparee(), ces mthodes sont purement utilitaires et nous pouvons les rutiliser dans n'importe quel DAO.

4. Rcapitulons
Les mthodes purement utilitaires peuvent tre places dans une classe part entire, une classe utilitaire donc, et tre utilises depuis n'importe quel DAO. V oil pourquoi j'ai donc cr une classe finale que j'ai nomme DAOUtilitaire et place dans le package com.sdzee.dao. V ous pouvez la mettre en place vous-mmes, ou bien la tlcharger en cliquant sur ce lien et l'ajouter votre projet.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


En ce qui concerne la mthode map(), celle-ci doit simplement tre place dans la classe UtilisateurDaoImpl .

416/606

La mthode trouver()
Maintenant que nos mthodes utilitaires sont prtes, nous pouvons finalement crire le code de la mthode trouver() : Code : Java - com.sdzee.dao.UtilisateurDaoImpl private static final String SQL_SELECT_PAR_EMAIL = "SELECT id, email, nom, mot_de_passe, date_inscription FROM Utilisateur WHERE email = ?"; /* Implmentation de la mthode dfinie dans l'interface UtilisateurDao */ @Override private Utilisateur trouver( String email ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; Utilisateur utilisateur = null; try { /* Rcupration d'une connexion depuis la Factory */ connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_SELECT_PAR_EMAIL, false, email ); resultSet = preparedStatement.executeQuery(); /* Parcours de la ligne de donnes de l'ventuel ResulSet retourn */ if ( resultSet.next() ) { utilisateur = map( resultSet ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( resultSet, preparedStatement, connexion ); } } return utilisateur;

V ous retrouvez ici les principes dcouverts et appliqus dans le chapitre prcdent : l'obtention d'une connexion, la prparation d'une requte de lecture, son excution, puis la rcupration et l'analyse du ResultSet retourn, et enfin la fermeture des ressources mises en jeu. Les trois lignes surlignes font intervenir chacune des mthodes utilitaires que nous avons dveloppes. ce propos, vous n'oublierez pas d'importer le contenu de la classe DAOUtilitaire afin de rendre disponibles ses mthodes ! Pour ce faire, il vous suffit d'ajouter la ligne suivante dans les imports de votre classe UtilisateurDaoImpl : Code : Java - Import des mthodes utilitaires import static com.sdzee.dao.DAOUtilitaire.*;

En prcisant directement dans l'import le mot-cl static, vous pourrez appeler vos mthodes comme si elles faisaient directement partie de votre classe courante. Par exemple, vous n'aurez pas besoin d'crire DAOUtilitaire.fermeturesSilencieuses( ... ), mais simplement fermeturesSilencieuses( ... ). Pratique, n'est-ce pas ? En outre, vous retrouvez galement dans ce code la bonne pratique que je vous ai enseigne en dbut de cours, savoir la mise en place d'une constante. Elle dtient en l'occurrence l'instruction SQL utilise pour prparer notre requte.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

417/606

La mthode creer()
Dernire pierre notre difice, nous devons crire le code de la mthode creer() : Code : Java - com.sdzee.dao.UtilisateurDaoImpl private static final String SQL_INSERT = "INSERT INTO Utilisateur (email, mot_de_passe, nom, date_inscription) VALUES (?, ?, ?, NOW())"; /* Implmentation de la mthode dfinie dans l'interface UtilisateurDao */ @Override public void creer( Utilisateur utilisateur ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet valeursAutoGenerees = null; try { /* Rcupration d'une connexion depuis la Factory */ connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_INSERT, true, utilisateur.getEmail(), utilisateur.getMotDePasse(), utilisateur.getNom() ); int statut = preparedStatement.executeUpdate(); /* Analyse du statut retourn par la requte d'insertion */ if ( statut == 0 ) { throw new DAOException( "chec de la cration de l'utilisateur, aucune ligne ajoute dans la table." ); } /* Rcupration de l'id auto-gnr par la requte d'insertion */ valeursAutoGenerees = preparedStatement.getGeneratedKeys(); if ( valeursAutoGenerees.next() ) { /* Puis initialisation de la proprit id du bean Utilisateur avec sa valeur */ utilisateur.setId( valeursAutoGenerees.getLong( 1 ) ); } else { throw new DAOException( "chec de la cration de l'utilisateur en base, aucun ID auto-gnr retourn." ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( valeursAutoGenerees, preparedStatement, connexion ); } }

L encore, vous retrouvez les principes dcouverts jusqu' prsent : l'obtention d'une connexion, la prparation d'une requte d'insertion avec demande de renvoi de l'id auto-gnr grce au boolen true pass notre mthode utilitaire DAOUtilitaire.initialisationRequetePreparee(), son excution et la rcupration de son statut, la rcupration de l'id auto-gnr via l'appel la mthode preparedStatement.getGeneratedKeys(), et enfin la fermeture des ressources mises en jeu.

Dans ces deux mthodes, vous observerez bien l'utilisation de l'exception personnalise DAOException que nous avons cre auparavant. Si vous avez encore un peu de mal voir o nous voulons en venir avec nos exceptions, ne vous inquitez pas : vous allez comprendre dans la suite de ce chapitre !

Intgration
Nous allons maintenant tcher d'intgrer proprement cette nouvelle couche dans notre application. Pour l'exemple, nous allons travailler avec notre systme d'inscription et laisser de ct le systme de connexion pour le moment.

Chargement de la DAOFactory
www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

418/606

Notre DAOFactory est un objet que nous ne souhaitons instancier qu'une seule fois, le dmarrage de l'application semble donc l'instant appropri pour procder son initialisation. Dans une application Java classique, il nous suffirait de placer quelques lignes de code en tte de la mthode main(), et le tour serait jou. Mais dans une application Java EE, comment faire ? La solution, c'est l'interface ServletContextListener. Lisez sa trs courte documentation, vous observerez qu'elle fournit une mthode contextInitialized() qui est appele ds le dmarrage de l'application, avant le chargement des servlets et filtres du projet. Il s'agit exactement de ce dont nous avons besoin !

Cration du Listener
Nous allons donc mettre en place un nouveau package com.sdzee.config et y crer une classe nomme InitialisationDaoFactory qui implmente l'interface ServletContextListener : Code : Java - com.sdzee.config.InitialisationDaoFactory package com.sdzee.config; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.sdzee.dao.DAOFactory; public class InitialisationDaoFactory implements ServletContextListener { private static final String ATT_DAO_FACTORY = "daofactory"; private DAOFactory daoFactory;

@Override public void contextInitialized( ServletContextEvent event ) { /* Rcupration du ServletContext lors du chargement de l'application */ ServletContext servletContext = event.getServletContext(); /* Instanciation de notre DAOFactory */ this.daoFactory = DAOFactory.getInstance(); /* Enregistrement dans un attribut ayant pour porte toute l'application */ servletContext.setAttribute( ATT_DAO_FACTORY, this.daoFactory ); } @Override public void contextDestroyed( ServletContextEvent event ) { /* Rien raliser lors de la fermeture de l'application... }

*/ }

Comme d'habitude en Java, puisque nous implmentons une interface nous devons dfinir toutes ses mthodes, en l'occurrence elle en contient deux : une qui est lance au dmarrage de l'application, et une autre sa fermeture. V ous l'aurez devin, seule la mthode appele lors du dmarrage nous intresse ici. Le code est trs simple et trs court, comme vous pouvez l'observer. Si vous avez t trs assidus, vous devez vous souvenir que le ServletContext n'est rien d'autre que l'objet qui agit dans les coulisses de l'objet implicite application ! Sachant cela, vous devez maintenant comprendre sans problme ce que nous faisons ici : nous rcuprons le ServletContext la ligne 16 ; nous obtenons une instance de notre DAOFactory via un appel sa mthode statique DAOFactory.getInstance() ; nous plaons cette instance dans un attribut du ServletContext via sa mthode setAttribute(), qui a donc

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


pour porte l'application entire !

419/606

Il faut tre trs prudent lors de la manipulation d'objets de porte application. En effet, puisque de tels objets sont partags par tous les composants durant toute la dure de vie de l'application, il est recommand de les utiliser uniquement en lecture seule. Autrement dit, il est recommand de les initialiser au dmarrage de l'application et de ne plus jamais les modifier ensuite - que ce soit depuis une servlet, un filtre ou une JSP - afin d'viter les modifications concurrentes et les problmes de cohrence que cela pourrait impliquer. Dans notre cas, il n'y a aucun risque : notre objet est bien cr ds le dmarrage de l'application, et il sera ensuite uniquement lu.

Configuration du Listener
Pour que notre Listener frachement cr soit pris en compte lors du dmarrage de notre application, il nous faut ajouter une section au fichier web.xml : Code : XML - /WEB-INF/web.xml <listener> <listenerclass>com.sdzee.config.InitialisationDaoFactory</listener-class> </listener>

V oil tout ce qu'il est ncessaire d'crire. N'oubliez pas de redmarrer Tomcat pour que la modification soit prise en compte !

Utilisation depuis la servlet


Notre DAOFactory tant prte l'emploi dans notre projet, nous pouvons maintenant rcuprer une instance de notre DAO Utilisateur depuis notre servlet d'inscription. Code : Java - com.sdzee.servlets.Inscription package com.sdzee.servlets; import java.io.IOException; import import import import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; com.sdzee.beans.Utilisateur; com.sdzee.dao.DAOFactory; com.sdzee.dao.UtilisateurDao; com.sdzee.forms.InscriptionForm ;

public class Inscription extends HttpServlet { public static final String CONF_DAO_FACTORY = "daofactory"; public static final String ATT_USER = "utilisateur"; public static final String ATT_FORM = "form"; public static final String VUE = "/WEBINF/inscription.jsp"; private UtilisateurDao utilisateurDao; public void init() throws ServletException { /* Rcupration d'une instance de notre DAO Utilisateur */ this.utilisateurDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getUtilisateurDao(); } public void doGet( HttpServletRequest request,

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


HttpServletResponse response ) throws ServletException, IOException { /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ InscriptionForm form = new InscriptionForm( utilisateurDao ); /* Traitement de la requte et rcupration du bean en rsultant */ Utilisateur utilisateur = form.inscrireUtilisateur( request ); */ /* Stockage du formulaire et du bean dans l'objet request request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_USER, utilisateur );

420/606

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

La premire tape consiste rcuprer une instance de notre DAOFactory . Puisque nous en avons plac une dans un attribut de porte application lors de son chargement, il nous suffit depuis notre servlet de rcuprer le ServletContext et d'appeler sa mthode getAttribute(). En ralit, le principe est exactement le mme que pour un attribut de requte ou de session, seul l'objet de rfrence change. Cette mthode retournant un objet de type Object, nous devons ensuite effectuer un cast vers le type DAOFactory pour pouvoir appeler sa mthode getUtilisateurDao(). Nous enregistrons finalement l'instance de notre DAO Utilisateur dans un attribut de notre servlet. Quelle est cette mthode nomme init() ?

Cette mthode fait partie de celles que je ne vous avais volontairement pas expliques lorsque je vous ai prsent les servlets. Je vous avais d'ailleurs prcis alors, que vous n'tiez pas encore assez l'aise avec le concept de servlet lui-mme, pour avoir besoin d'intervenir sur son cycle de vie. Le temps est venu pour vous d'en savoir un petit peu plus. Pour rappel, une servlet n'est cre qu'une seule et unique fois par le conteneur, lors du dmarrage de l'application ou bien lors de son premier accs (ceci dpendant de la prsence ou non d'une section <load-on-startup> dans la dclaration de la servlet au sein du fichier web.xml ). Eh bien sachez que lorsque la servlet est instancie, cette fameuse mthode init() va tre appele, une seule et unique fois donc. Ainsi, vous avez la possibilit travers cette mthode d'effectuer des tches uniques, qui ne sont pas destines tre lances chaque appel aux mthodes doGet() ou doPost() par exemple. Pourquoi crer notre instance de DAO depuis cette mthode init() ?

Eh bien tout simplement parce que si nous en crions une depuis nos mthodes doXXX(), une nouvelle instance serait cre chaque requte reue ! En l'occurrence, ce n'est pas ncessaire puisque notre DAO est bien crit : il manipule les ressources dont il a besoin au plus prs de la requte SQL effectuer, et les ferme correctement de la mme manire. Par contre, notre DAO pourrait trs bien tre construit d'une manire diffrente et ncessiter alors une nouvelle instance chaque utilisation. Par exemple, imaginez que notre DAO partage une seule et mme connexion la base de donnes pour l'ensemble de ses mthodes CRUD. Eh bien dans ce cas, il serait impensable de ne crer qu'une seule instance du DAO et de la partager tous les clients qui enverraient des requtes vers la servlet, car cela reviendrait partager une seule connexion la BDD pour tous les clients ! Ce type de problmatiques porte un nom : il s'agit de la thread-safety. En quelques mots, il s'agit de la possibilit ou

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

421/606

non de partager des ressources l'ensemble des threads intervenant sur un objet ou une classe. C'est un concept particulirement important dans le cas d'une application web, parce qu'il y a bien souvent un nombre consquent de clients utilisant une seule et mme application centralise.

Qu'est-ce qu'un attribut de servlet a de particulier face un attribut dclar au sein d'une mthode doXXX() ?

La diffrence est majeure : un attribut de servlet est partag par tous les threads utilisant l'instance de la servlet, autrement dit par tous les clients qui y font appel ! Alors qu'un simple attribut dclar par exemple dans une mthode doGet() n'existe que pour un seul thread , et se matrialise ainsi par un objet diffrent d'un thread l'autre. V oil pourquoi dans notre cas, nous enregistrons notre instance de DAO dans un attribut de servlet : il peut tout fait tre partag par l'ensemble des clients faisant appel la servlet, puisque je vous le rappelle il est correctement crit et gre proprement les ressources. L'intrt est avant tout d'conomiser les ressources du serveur : r-instancier un nouveau DAO chaque requte reue serait un beau gchis en termes de mmoire et de performances ! Bref, vous comprenez maintenant mieux pourquoi je n'ai pas souhait vous prsenter le cycle de vie d'une servlet trop tt. Il implique des mcanismes qui ne sont pas triviaux, et il est ncessaire de bien comprendre le contexte d'utilisation d'une servlet avant d'intervenir sur son cycle de vie. L encore, je ne vous dis pas tout et me limite volontairement cette mthode init(). Ne vous inquitez pas, le reste du mystre sera lev en annexe !

Pour finir, il faut transmettre notre instance DAO Utilisateur l'objet mtier InscriptionForm. Eh oui, c'est bien lui qui va grer l'inscription d'un utilisateur, et c'est donc bien lui qui va en avoir besoin pour communiquer avec la base de donnes ! Nous lui passons ici la ligne 35 par l'intermdiaire de son constructeur, qu'il va donc nous falloir modifier par la suite pour que notre code compile...

Reprise de l'objet mtier


Comme je viens de vous le dire, la premire tape consiste ici modifier le constructeur de l'objet InscriptionForm pour qu'il prenne en compte le DAO transmis. En l'occurrence, nous n'avions jusqu' prsent cr aucun constructeur, et nous tions contents de celui par dfaut. Nous allons donc devoir ajouter le constructeur suivant, ainsi que la variable d'instance associe permettant de stocker le DAO pass en argument : Code : Java - com.sdzee.forms.InscriptionForm private UtilisateurDao utilisateurDao;

public InscriptionForm( UtilisateurDao utilisateurDao ) { this.utilisateurDao = utilisateurDao; }

Le DAO tant maintenant disponible au sein de notre objet, nous allons pouvoir l'utiliser dans notre mthode d'inscription pour appeler la mthode creer() dfinie dans l'interface UtilisateurDao. Il faudra alors bien penser intercepter une ventuelle DAOException envoye par le DAO, et agir en consquence ! Code : Java - com.sdzee.forms.InscriptionForm public Utilisateur inscrireUtilisateur( HttpServletRequest request ) { String email = getValeurChamp( request, CHAMP_EMAIL ); String motDePasse = getValeurChamp( request, CHAMP_PASS ); String confirmation = getValeurChamp( request, CHAMP_CONF ); String nom = getValeurChamp( request, CHAMP_NOM ); Utilisateur utilisateur = new Utilisateur(); try { traiterEmail( email, utilisateur ); traiterMotsDePasse( motDePasse, confirmation, utilisateur );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


traiterNom( nom, utilisateur ); if ( erreurs.isEmpty() ) { utilisateurDao.creer( utilisateur ); resultat = "Succs de l'inscription."; } else { resultat = "chec de l'inscription."; } } catch ( DAOException e ) { resultat = "chec de l'inscription : une erreur imprvue est survenue, merci de ressayer dans quelques instants."; e.printStackTrace(); } } return utilisateur;

422/606

V ous pouvez observer plusieurs choses ici : j'appelle la mthode utilisateurDao.creer() la ligne 14, uniquement si aucune erreur de validation n'a eu lieu. En effet, inutile d'aller faire une requte sur la BDD si les critres de validation des champs du formulaire n'ont pas t respects ; il est alors ncessaire de mettre en place un try/catch pour grer une ventuelle DAOException retourne par cet appel ! En l'occurrence, j'initialise la chane resultat avec un message d'chec ; enfin, j'ai regroup le travail de validation des paramtres et d'initialisation des proprits du bean dans des mthodes traiterXXX(). Cela me permet d'arer le code, qui commenait devenir srieusement charg avec tout cet enchevtrement de blocs try/catch , sans oublier les ajouts que nous devons y apporter ! Quels ajouts ? Qu'est-il ncessaire de modifier dans nos mthodes de validation ?

La gestion de l'adresse mail doit subir quelques modifications, car nous savons dornavant qu'une adresse doit tre unique en base, et le mot de passe doit absolument tre chiffr avant d'tre envoy en base. En plus de cela, nous allons mettre en place une exception personnalise pour la validation des champs afin de rendre notre code plus clair. V oici donc le code des mthodes grant ces fonctionnalits : Code : Java - com.sdzee.forms.InscriptionForm private static final String ALGO_CHIFFREMENT = "SHA-256"; ... /* * Appel la validation de l'adresse email reue et initialisation de la * proprit email du bean */ private void traiterEmail( String email, Utilisateur utilisateur ) { try { validationEmail( email ); } catch ( FormValidationException e ) { setErreur( CHAMP_EMAIL, e.getMessage() ); } utilisateur.setEmail( email ); } /* * Appel la validation des mots de passe reus, chiffrement du mot de * passe et initialisation de la proprit motDePasse du bean */ private void traiterMotsDePasse( String motDePasse, String confirmation, Utilisateur utilisateur ) { try { validationMotsDePasse( motDePasse, confirmation );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} catch ( FormValidationException e ) { setErreur( CHAMP_PASS, e.getMessage() ); setErreur( CHAMP_CONF, null ); } /* * Utilisation de la bibliothque Jasypt pour chiffrer le mot de passe * efficacement. * * L'algorithme SHA-256 est ici utilis, avec par dfaut un salage * alatoire et un grand nombre d'itrations de la fonction de hashage. * * La String retourne est de longueur 56 et contient le hash en Base64. */ ConfigurablePasswordEncryptor passwordEncryptor = new ConfigurablePasswordEncryptor(); passwordEncryptor.setAlgorithm( ALGO_CHIFFREMENT ); passwordEncryptor.setPlainDigest( false ); String motDePasseChiffre = passwordEncryptor.encryptPassword( motDePasse ); } utilisateur.setMotDePasse( motDePasseChiffre );

423/606

/* Validation de l'adresse email */ private void validationEmail( String email ) throws FormValidationException { if ( email != null ) { if ( !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new FormValidationException( "Merci de saisir une adresse mail valide." ); } else if ( utilisateurDao.trouver( email ) != null ) { throw new FormValidationException( "Cette adresse email est dj utilise, merci d'en choisir une autre." ); } } else { throw new FormValidationException( "Merci de saisir une adresse mail." ); } }

Pour commencer, remarquez la structure des mthodes traiterXXX() cres : comme je vous l'ai dj annonc, celles-ci renferment simplement l'appel la mthode de validation d'un champ et l'initialisation de la proprit du bean correspondante. Ensuite dans la mthode de traitement des mots de passe, j'ai ajout la procdure de chiffrement l'aide de la bibliothque Jasypt aux lignes 39 42. V ous pouvez la tlcharger directement en cliquant sur ce lien. De la mme manire que pour les bibliothques que nous avons dj manipules dans le pass, il suffit de placer le fichier .jar rcupr sous /WEB-INF/lib. Je vous laisse parcourir sa documentation et dcouvrir les mthodes et objets disponibles, en l'occurrence le code que je vous donne est suffisamment comment pour que vous compreniez comment ce que j'ai mis en place fonctionne. Il est important de raliser cette scurisation du mot de passe en amont, afin d'initialiser la proprit du bean avec l'empreinte ainsi gnre et non pas directement avec le mot de passe en clair. Par la suite, lorsque nous souhaiterons vrifier si un utilisateur entre le bon mot de passe lors de sa connexion, il nous suffira de le comparer directement l'empreinte stocke grce la mthode passwordEncryptor.checkPassword(). De manire gnrale, la rgle veut qu'on ne travaille jamais directement sur les mots de passe en clair.

Enfin, j'ai ajout dans la mthode de validation de l'adresse email un appel la mthode trouver() du DAO, afin de renvoyer une exception en cas d'adresse dj existante. Cela veut dire que nous effectuons deux requtes sur la base lors d'une inscription utilisateur : une pour vrifier si l'adresse n'existe pas dj via l'appel trouver(), et une pour l'insrer via l'appel creer().

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

424/606

Est-ce bien ncessaire d'effectuer deux requtes ? conomiser une requte vers la BDD ne serait pas prfrable ?

Eh bien oui, c'est bien plus pratique pour nous d'effectuer une vrification avant de procder l'insertion. Si nous ne le faisions pas, alors nous devrions nous contenter d'analyser le statut retourn par notre requte INSERT et d'envoyer une exception en cas d'un statut valant zro. Le souci, c'est qu'un chec de l'insertion peut tre d de nombreuses causes diffrentes, et pas seulement au fait qu'une adresse email existe dj en base ! Ainsi en cas d'chec nous ne pourrions pas avertir l'utilisateur que le problme vient de son adresse mail, nous pourrions simplement l'informer que la cration de son compte chou. Il tenterait alors nouveau de s'inscrire avec les mmes coordonnes en pensant que le problme vient de l'application, et recevrait une nouvelle fois la mme erreur, etc. En testant en amont la prsence de l'adresse en base via une bte requte de lecture, nous sommes capables de renvoyer une erreur prcise l'utilisateur. Si l'appel trouver() retourne quelque chose, alors cela signifie que son adresse existe dj : nous envoyons alors une exception contenant un message lui demandant d'en choisir une autre, qui provoque dans traiterEmail() la mise en place du message dans la Map des erreurs, et qui ainsi permet de ne pas appeler la mthode creer() en cas d'adresse dj existante. En outre, chercher conomiser ici une requte SQL relve du dtail. Premirement parce qu'il s'agit ici d'un simple SELECT trs peu gourmand, et deuximement parce qu' moins de travailler sur un site extrmement populaire, l'inscription reste une opration effectue relativement peu frquemment en comparaison du reste des fonctionnalits. Bref, pas de problme !

Cration d'une exception ddie aux erreurs de validation


Enfin, vous remarquez que nos mthodes renvoient dornavant des FormValidationException, et non plus de banales Exception. J'ai en effet pris soin de crer une nouvelle exception personnalise, qui hrite d'Exception et que j'ai place dans com.sdzee.forms : Code : Java - com.sdzee.forms.FormValidationException package com.sdzee.forms; public class FormValidationException extends Exception { /* * Constructeur */ public FormValidationException( String message ) { super( message ); } }

Ce n'est pas une modification ncessaire en soit, mais cela rend le code plus comprhensible la lecture, car nous savons ainsi quel type derreur est manipul par une mthode au premier coup dil.

Vrifications Le code final


Avant tout, je vous propose de vrifier que vous avez bien report toutes les modifications ncessaires au code existant, et ralis tous les ajouts demands. V oici l'arborescence que vous tes censs obtenir :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

425/606

Certaines classes ayant subi beaucoup de retouches, j'ai d fractionner leur code en plusieurs sections, afin de vous l'expliquer le plus clairement possible et ainsi de vous en faciliter la comprhension. Ne souhaitant pas vous voir vous emmler les pinceaux, je vous propose de tlcharger les codes complets des deux classes les plus massives : UtilisateurDaoImpl.java InscriptionForm.java V ous pourrez ainsi vous assurer que vous n'avez omis aucun changement.

Le scnario de tests
Aprs toutes ces nouveauts, tous ces ajouts et toutes ces modifications, il est temps de tester le bon fonctionnement de notre application. Le scnario est relativement lger, il suffit de tester ce qui se passe lorsqu'un utilisateur tente de s'inscrire deux fois avec la mme adresse email. Je vous laisse reprendre les scnarios des chapitres prcdents, afin de vous assurer que tout ce que nous avions mis en place dans les chapitres prcdents fonctionne toujours. Pour information, cela s'appelle un test de rgression. Cela consiste vrifier que les modifications apportes sur le code n'ont pas modifi le fonctionnement de la page ou de l'application, et c'est bien entendu un processus rcurrent et extrmement important dans le dveloppement d'une application !

V oici le formulaire avant soumission des donnes :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

426/606

Le formulaire aprs soumission la premire fois :

Et le formulaire aprs soumission une seconde fois :

Comme prvu, la mthode de validation de l'adresse email a correctement fait son travail. Elle a reu un rsultat lors de l'appel la mthode trouver(), et a par consquent plac un message d'erreur sur le champ email . En outre, vous pouvez regarder le contenu de votre table Utilisateur depuis la console de votre serveur MySQL, via un simple SELECT * FROM Utilisateur;. V ous constaterez alors que les informations que vous avez saisies depuis votre navigateur ont bien t enregistres dans la base de donnes, et que le mot de passe a bien t chiffr. V oil tout ce qu'il est ncessaire de vrifier pour le moment. En apparence, cela parat peu par rapport tout le code que nous avons d mettre en place, je vous l'accorde. Mais ne vous en faites pas, nous n'avons pas uniquement rendu notre premier DAO oprationnel, nous avons prpar le terrain pour tous les futurs DAO de notre application !

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

427/606

En rsum
Le pattern DAO permet d'isoler l'accs aux donnes et leur stockage du reste de l'application. Il existe beaucoup de conceptions diffrentes possibles pour la mise en place d'une couche DAO. Une des solutions possibles est d'crire un couple interface + implmentation par table gre. Mettre en place des exceptions spcifiques au DAO permet de masquer le type du stockage sous-jascent. Mettre en place une Factory initialise au dmarrage de l'application via un ServletContextListener permet de ne charger qu'une seule et unique fois le driver JDBC. Mettre en place des utilitaires pour la prparation des requtes et la libration des ressources permet de limiter la duplication et d'allger grandement le code. Notre DAO librant proprement les ressources qu'il met en jeu, une seule et unique instance de notre DAO peut tre partage par toutes les requtes entrantes. Rcuprer une telle instance de DAO de manire unique depuis une servlet, et non pas chaque requte entrante, peut se faire simplement en utilisant sa mthode init(). Notre servlet transmet alors simplement l'instance du DAO l'objet mtier, qui sera responsable de la gestion des donnes. Notre objet mtier ne connat pas le systme de stockage final utilis : il ne fait qu'appeler les mthodes dfinies dans l'interface de notre DAO.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

428/606

TP Fil rouge - tape 6


Sixime tape du fil rouge, le temps est enfin venu pour vous d'intgrer une base de donnes votre application ! Crations de la base, des tables, des DAO associs et intgration dans l'application existante vous attendent.

Objectifs Fonctionnalits
V ous allez mettre en place une base de donnes dans votre application. Ce changement va impliquer que : lors de la cration d'un client ou d'une commande par l'utilisateur, les donnes ne seront maintenant plus seulement mises en session, mais galement enregistres dans la base ; vous allez rendre possible la cration de clients portant le mme nom, et de commande passes la mme date ; les pages listant les clients et commandes existant continueront se baser uniquement sur les donnes prsentes en session ; la suppression d'un client ou d'une commande ne supprimera pas seulement les donnes de la session, mais galement de la base. C'est tout ce que je vous demande...

Conseils
Ne vous fiez pas la faible longueur du sujet, normment de travail et de rflexion vous attendent !

Cration de la base de donnes


La premire tape consiste crer la base et les tables reprsentant les donnes de votre application. Ceci ne faisant pas directement l'objet de ce cours, je vais vous accompagner en vous dtaillant chaque instruction, suivez le guide !

Connexion au serveur MySQL


Connectez-vous votre serveur via la commande mysql -h localhost -u root -p , puis entrez votre mot de passe le cas chant.

Cration d'une base


Mettez en place une nouvelle base de donnes nomme tp_sdzee l'aide de l'instruction suivante : Code : SQL CREATE DATABASE tp_sdzee DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Mise en place des droits


V ous savez qu'utiliser le compte root est une mauvaise pratique, vous devez donc accorder des droits sur la nouvelle table l'utilisateur java cr dans le cadre du cours, via l'instruction suivante : Code : SQL GRANT ALL ON tp_sdzee.* TO 'java'@'localhost' IDENTIFIED BY 'SdZ_eE';

V ous devez ensuite vous dconnecter de l'utilisateur root via la commande exit , puis vous reconnecter avec votre compte java via la commande mysql -h localhost -u java -p , entrer le mot de passe et enfin entrer la commande use tp_sdzee pour dfinir la base sur laquelle vous allez travailler.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

429/606

Cration des tables


V ous devez alors crer une table Client contenant les champs id, nom, prenom, adresse, telephone, email et image. V oici un exemple d'instruction que vous pouvez utiliser pour cela : Code : SQL CREATE TABLE tp_sdzee.Client ( id INT( 11 ) NOT NULL AUTO_INCREMENT , nom VARCHAR( 20 ) NOT NULL , prenom VARCHAR( 20 ) , adresse VARCHAR( 200 ) NOT NULL , telephone VARCHAR( 10 ) NOT NULL, email VARCHAR( 60 ) , image VARCHAR( 200 ) , PRIMARY KEY ( id ) ) ENGINE = INNODB;

Rien de particulier ici, remarquez simplement la mise en place d'un champ id pour identifier chaque client. De mme, vous devez crer une table Commande contenant les champs id, id_client, date, montant, modePaiement, statutPaiement, modeLivraison et statutLivraison : Code : SQL CREATE TABLE tp_sdzee.Commande ( id INT( 11 ) NOT NULL AUTO_INCREMENT , id_client INT( 11 ) , date DATETIME NOT NULL , montant DEC( 11 ) NOT NULL , mode_paiement VARCHAR( 20 ) NOT NULL , statut_paiement VARCHAR( 20 ) , mode_livraison VARCHAR( 20 ) NOT NULL , statut_livraison VARCHAR( 20 ) , PRIMARY KEY ( id ) , CONSTRAINT fk_id_client -- On donne un nom notre cl FOREIGN KEY (id_client) -- Colonne sur laquelle on cre la cl REFERENCES Client(id) -- Colonne de rfrence (celle de la table Client) ON DELETE SET NULL -- Action effectuer lors de la suppression d'une rfrence ) ENGINE = INNODB;

Premire chose, vous remarquez ici que vous n'allez pas enregistrer les donnes des clients directement dans la table Commande, mais uniquement leur id dans le champ id_client. Les clients existant dj dans la table Client, il serait idiot de dupliquer les donnes ! Deuxime chose, observez en fin d'instruction la mise en place d'une contrainte de cl trangre sur le champ id_client. Grce cette cl, le SGBD sait que le contenu du champ id_client correspond un id existant dans la table Client. Enfin, ne manquez pas l'option de la cl trangre ! Puisqu'une relation est dfinie par le SGBD entre une commande et son client, il devient impossible par dfaut de supprimer un client s'il a dj pass une commande. Dans le cadre de ce TP, ce n'est pas trs ergonomique : vous allez donc devoir changer ce comportement, en prcisant que lors de la suppression d'un client, les champs id_client de la table Commande qui y font rfrence devront passer automatiquement NULL. C'est le rle de la ligne ON DELETE SET NULL. Si vous le souhaitez, vous pouvez changer le comportement adopt ici. V ous pouvez par exemple faire en sorte qu'il soit impossible de supprimer un client s'il a dj pass une commande, ou bien encore faire en sorte que lors de la suppression d'un client, toutes les commandes qu'il a passes soient supprimes. Tout cela est simplement paramtrable via l'option applique sur la cl trangre mise en place !

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

430/606

En construisant ainsi vos tables, vous allez pouvoir vous assurer que les donnes crites dans votre base sont cohrentes.

Mise en place de JDBC


Si ce n'est pas dj fait, vous allez devoir placer le jar du driver JDBC pour MySQL dans le dossier /lib de Tomcat, afin que votre application puisse communiquer avec votre base de donnes.

Rutilisation de la structure DAO dveloppe dans le cadre du cours


V otre base de donnes est en place, vous pouvez maintenant attaquer le dveloppement de votre application. Pour commencer, une bonne nouvelle : vous allez pouvoir rutiliser certaines classes du package com.sdzee.dao que vous avez dveloppes dans le cadre du cours ! Les voici : DAOConfigurationException.java DAOException.java DAOUtilitaire.java En effet, inutile de rinventer la roue pour toutes celles-la : elles fonctionnent, vous avez pass du temps les mettre en place, autant s'en resservir ! V ous pouvez donc crer un package com.sdzee.tp.dao dans votre projet et y copier chacune d'elles. De mme, vous allez pouvoir rutiliser la classe InitialisationDaoFactory.java, et la placer dans un nouveau package com.sdzee.tp.config. Enfin, il reste deux fichiers que vous allez pouvoir rutiliser, moyennant quelques petits ajustements : vous pouvez reprendre le fichier dao.properties , mais il faudra penser bien changer le contenu de son entre url , pour cibler la nouvelle base tp_sdzee que vous avez cre, et non plus bdd_sdzee comme c'tait le cas dans le cadre du cours ; vous pouvez de mme reprendre la classe DAOFactory.java, mais vous allez devoir y changer le chemin vers le fichier dao.properties dfini dans la constante FICHIER_PROPERTIES . Prenez bien le temps de mettre cette structure en place, n'allez pas trop vite et vrifiez que tout est bien au bon endroit avant de poursuivre !

Cration des interfaces et implmentations du DAO


Maintenant que le cadre est en place, vous allez devoir mettre la main la pte et coder les classes du DAO spcifiques votre projet : l'interface ClientDao et son implmentation ClientDaoImpl ; l'interface CommandeDao et son implmentation CommandeDaoImpl .

Les interfaces
Dans le cadre de ce TP, vous n'avez pas dvelopp de formulaires pour la modification des donnes existantes. Ainsi, vous n'avez pas besoin de dfinir toutes les mthodes CRUD dans vos DAO, vous pouvez vous contenter des mthodes creer(), trouver(), lister() et supprimer(). Inspirez-vous de ce que vous avez mis en place dans le chapitre prcdent si vous ne vous souvenez plus comment procder.

Les implmentations
V ous allez devoir dfinir les requtes SQL effectuer sur vos tables, crer et manipuler des requtes prpares depuis vos diffrentes mthodes, tout cela bien entendu en rutilisant les mthodes dveloppes dans la classe DAOUtilitaire. V ous pouvez l encore vous inspirer grandement de ce que vous avez dvelopp dans le chapitre prcdent, mais vous devrez prendre garde modifier les mthodes map(), crer une mthode lister() et bien ajuster les mthodes trouver() et creer(), et enfin supprimer() ! Afin de vous permettre de partir du bon pied, je vous donne ici les requtes utiliser respectivement dans les classes ClientDaoImpl et CommandeDaoImpl :

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Code : Java - Constantes dfinissant les requtes dans ClientDaoImpl private static final String SQL_SELECT = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client ORDER BY id"; private static final String SQL_SELECT_PAR_ID = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client WHERE id = ?"; private static final String SQL_INSERT = "INSERT INTO Client (nom, prenom, adresse, telephone, email, image) VALUES (?, ?, ?, ?, ?, ?)"; private static final String SQL_DELETE_PAR_ID = "DELETE FROM Client WHERE id = ?";

431/606

Code : Java - Constantes dfinissant les requtes dans CommandeDaoImpl private static final String SQL_SELECT = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande ORDER BY id"; private static final String SQL_SELECT_PAR_ID = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande WHERE id = ?"; private static final String SQL_INSERT = "INSERT INTO Commande (id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison) VALUES (?, ?, ?, ?, ?, ?, ?)"; private static final String SQL_DELETE_PAR_ID = "DELETE FROM Commande WHERE id = ?";

Avec ceci en poche, vous n'avez pluss vous soucier de l'aspect SQL du dveloppement, et pouvez vous concentrer sur l'aspect... Java EE !

Intgration dans le code existant


Afin d'intgrer correctement vos DAO dans votre application, vous allez devoir procder de nombreux changements dans le code existant...

Modification des beans


V ous allez devoir apporter deux changements : ajouter un champ id de type Long aux deux beans, pour reflter l'id auto-gnr existant dans vos tables ; modifier le type du champ date dans le bean Commande. Jusqu' prsent vous stockiez cette information sous forme d'une chane de caractres pour ne pas vous compliquer la tche, mais cette fois vous allez devoir faire correspondre ce champ celui cr dans la table Commande. Puisque vous utilisons la bibliothque JodaTime, je vous conseille de changer le type du champ date de String vers DateTime.

Modification des servlets


V ous allez devoir adapter vos servlets CreationClient et CreationCommande, toujours sur le modle de ce que vous avez dvelopp dans le cours : rcuprer la ou les implmentations de DAO depuis la factory, dans la mthode init() de chaque servlet ; passage du ou des DAO l'objet mtier lors de sa construction. En outre, vous allez devoir apporter un autre changement, qui va avoir un impact considrable sur le reste de lapplication. Jusqu' prsent, vous manipuliez des Map indexes sur les noms et dates des clients et commandes, ce qui rendaient impossibles la cration de deux clients portant le mme nom et la cration de deux commandes au mme instant. Maintenant que vos donnes sont identifiables par un id dans vos tables, vous allez pouvoir modifier vos Map pour qu'elles indexent les id des clients et commandes. V os Map vont donc changer de Map<String, Client> et Map<Srting,

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

432/606

Commande> vers Map<Long, Client> et Map<Long, Commande>. Vous allez donc devoir prendre garde, partout o ces maps sont manipules, ne plus travailler sur les noms et dates, mais bien sur les id. Ne prenez pas ce changement la lgre, il va impliquer pas mal de petites modifications a et l... ! V ous allez galement devoir modifier vos servlets de suppression : pour qu'elles se basent sur l'id lors de la suppression d'une entre dans une Map, suite la modification apporte prcdemment ; pour qu'elles suppriment galement les donnes en base, ceci implique donc la rcupration du DAO depuis la mthode init() et un appel la mthode supprimer() du DAO.

Modification des objets mtier


V ous allez devoir reprendre vos deux objets mtier CreationClientForm et CreationCommandeForm pour qu'ils interagissent avec la BDD. L encore, vous pouvez vous inspirer grandement de ce que vous avez dvelopp dans le chapitre prcdent. Dans les grandes lignes, il va vous falloir : rcuprer la ou les instances de DAO ncessaires depuis le constructeur ; effectuer un appel la mthode creer() du DAO une fois la validation des champs passe avec succs, et grer les ventuelles erreurs ou exceptions alors retournes. Attention dans le code de CreationCommandeForm, vous devrez prendre garde bien adapter la manipulation de la Map des clients, suite aux changements que vous avez apports au maps dfinies dans vos servlets !

Modification des JSP


Premirement, il va falloir adapter vos JSP aux changements effectus sur vos maps de clients et commandes. Dans les pages listerClients.jsp et listerCommandes.jsp, vous allez donc devoir modifier les liens de suppression pour qu'ils ne transmettent plus le nom d'un client ou la date d'une commande, mais leur id. Deuximement, vous allez devoir modifier la manire dont vous affichez le champ date du bean Commande ! Eh oui, maintenant que vous l'avez remplac par un objet Joda DateTime, vous ne pouvez plus vous contenter de l'afficher btement via la balise <c:out>... Pas de panique, les dveloppeurs de Joda ont pens ce cas de figure et ont cr une bibliothque de balises destines manipuler les objets Joda ! V ous devez donc tlcharger son jar en cliquant sur ce lien, puis le placer dans le rpertoire /WEB-INF/lib de votre projet. Ensuite, vous allez pouvoir utiliser les balises comme vous utilisez celles de la JSTL. V oici un exemple d'utilisation, qui se charge de formater un objet DateTime pour affichage l'utilisateur selon un pattern dfini : Code : JSP - Exemple d'utilisation d'une balise de la bibliothque Joda <%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> ... <joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"/>

Je vous invite bien entendu parcourir la documentation du projet plus en profondeur pour dcouvrir les diffrentes balises et attributs disponibles.

Cration d'un filtre


Enfin, il reste un aspect important changer dans le mode de fonctionnement de votre application. Je vous ai demand de faire en sorte que vos pages listant les commandes et clients existant continuent se baser sur les infos prsentes en session, il va donc falloir rflchir un petit peu la manire de procder ici. En effet, au tout premier lancement de l'application, aucun problme : aucune donne n'existe, et donc rien n'existe en session. Imaginez maintenant que votre utilisateur cre tout un tas de clients et commandes, puis quitte l'application et ne revient pas

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

433/606

pendant une semaine. son retour, la session n'existera plus depuis belle lurette sur le serveur, qui aura tt fait de la supprimer. Ainsi, les pages listant les clients et commandes n'afficheront rien, alors qu'en ralit il existe des donnes, non pas dans la session mais dans la base de donnes ! Ce qu'il vous faut grer, c'est donc le prchargement des donnes existant en base dans la session de l'utilisateur lors de son arrive, et uniquement lors de son arrive, sur n'importe quelle page de l'application. Comment raliser une telle opration ?

En principe, vous avez dj la rponse cette question ! Rflchissez bien, quel composant est idal pour ce type de fonctionnalit... Eh oui, c'est le filtre ! V ous allez donc crer un filtre appliqu toutes les pages de votre application, qui se chargera de vrifier si une session utilisateur existe, et qui la remplira avec les donnes prsentes en base si elle n'existe pas. En somme, vous allez y rcuprer une implmentation de DAO comme vous l'avez dj fait pour les servlets, effectuer un appel sa mthode lister(), et peupler les maps de clients et de commandes avec les donnes contenues dans les listes rcupres ! N'oubliez pas de bien dclarer le filtre dans le fichier web.xml ! V oil tout dont vous avez besoin. Comme vous le voyez, le sujet tait trs court, mais en ralit le travail est consquent !

Correction
Faites attention bien reprendre les fichiers du cours qui restent inchangs, corriger les quelques classes qui ncessitent des ajustements, et surtout ne baissez pas les bras devant la charge de travail implique par l'introduction d'une base de donnes dans votre programme ! Cet exercice est l'occasion parfaite pour vous familiariser avec ces concepts si importants, ne la gchez pas en abandonnant ds le moindre petit obstacle ! Comme toujours, ce n'est pas la seule manire de faire, le principal est que votre solution respecte les consignes que je vous ai donnes ! Prenez le temps de rflchir, de chercher et coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire les prcdents chapitres. La pratique est trs importante, ne vous ruez pas sur la solution !

Code de la structure DAO


Secret (cliquez pour afficher) V oici les liens directs vers les fichiers que vous devez reprendre tels quels depuis le chapitre prcdent : InitialisationDaoFactory (inchang) DAOConfigurationException (inchang) DAOException (inchang) DAOUtilitaire (inchang) V oil le contenu du nouveau fichier dao.properties , placer dans com.sdzee.tp.dao : Code : Java - dao.properties url = jdbc:mysql://localhost:3306/tp_sdzee driver = com.mysql.jdbc.Driver nomutilisateur = java motdepasse = SdZ_eE

Et voil le code de la classe DAOFactory adapt ce nouveau fichier Properties et au nouveau projet : Code : Java - com.sdzee.tp.dao.DAOFactory package com.sdzee.tp.dao;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import import import import import import import java.io.FileNotFoundException; java.io.IOException; java.io.InputStream ; java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.util.Properties;

434/606

public class DAOFactory { private static final String FICHIER_PROPERTIES "/com/sdzee/tp/dao/dao.properties"; private static final String PROPERTY_URL private static final String PROPERTY_DRIVER "driver"; private static final String PROPERTY_NOM_UTILISATEUR "nomutilisateur"; private static final String PROPERTY_MOT_DE_PASSE "motdepasse"; private String private String private String url; username; password; = = "url"; = = =

/* package */ DAOFactory( String url, String username, String password ) { this.url = url; this.username = username; this.password = password; } /* * Mthode charge de rcuprer les informations de connexion la base de * donnes, charger le driver JDBC et retourner une instance de la Factory */ public static DAOFactory getInstance() throws DAOConfigurationException { Properties properties = new Properties(); String url; String driver; String nomUtilisateur; String motDePasse; ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream fichierProperties = classLoader.getResourceAsStream( FICHIER_PROPERTIES ); if ( fichierProperties == null ) { throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable." ); } try { properties.load( fichierProperties ); url = properties.getProperty( PROPERTY_URL ); driver = properties.getProperty( PROPERTY_DRIVER ); nomUtilisateur = properties.getProperty( PROPERTY_NOM_UTILISATEUR ); motDePasse = properties.getProperty( PROPERTY_MOT_DE_PASSE ); } catch ( FileNotFoundException e ) { throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable.", e ); } catch ( IOException e ) { throw new DAOConfigurationException( "Impossible de charger le fichier properties " + FICHIER_PROPERTIES, e ); }

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


try { Class.forName( driver ); } catch ( ClassNotFoundException e ) { throw new DAOConfigurationException( "Le driver est introuvable dans le classpath.", e ); } DAOFactory instance = new DAOFactory( url, nomUtilisateur, motDePasse ); return instance; } /* Mthode charge de fournir une connexion la base de donnes */ /* package */ Connection getConnection() throws SQLException { return DriverManager.getConnection( url, username, password ); } /* * Mthodes de rcupration de l'implmentation des diffrents DAO * (uniquement deux dans le cadre de ce TP) */ public ClientDao getClientDao() { return new ClientDaoImpl( this ); } public CommandeDao getCommandeDao() { return new CommandeDaoImpl( this ); }

435/606

Code des interfaces DAO


Secret (cliquez pour afficher) L'interface ClientDao : Code : Java - com.sdzee.tp.dao.ClientDao package com.sdzee.tp.dao; import java.util.List; import com.sdzee.tp.beans.Client; public interface ClientDao { void creer( Client client ) throws DAOException; Client trouver( long id ) throws DAOException; List<Client> lister() throws DAOException; } void supprimer( Client client ) throws DAOException;

L'interface CommandeDao : Code : Java - com.sdzee.tp.dao.CommandeDao package com.sdzee.tp.dao;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import java.util.List; import com.sdzee.tp.beans.Commande; public interface CommandeDao { void creer( Commande commande ) throws DAOException; Commande trouver( long id ) throws DAOException; List<Commande> lister() throws DAOException; } void supprimer( Commande commande ) throws DAOException;

436/606

Code des implmentations DAO


Secret (cliquez pour afficher) Code de ClientDaoImpl : Code : Java - com.sdzee.tp.dao.ClientDaoImpl package com.sdzee.tp.dao; import static com.sdzee.tp.dao.DAOUtilitaire.fermeturesSilencieuses; import static com.sdzee.tp.dao.DAOUtilitaire.initialisationRequetePreparee; import import import import import import java.sql.Connection; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.SQLException; java.util.ArrayList; java.util.List;

import com.sdzee.tp.beans.Client; public class ClientDaoImpl implements ClientDao { private static final String SQL_SELECT = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client ORDER BY id"; private static final String SQL_SELECT_PAR_ID = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client WHERE id = ?"; private static final String SQL_INSERT = "INSERT INTO Client (nom, prenom, adresse, telephone, email, image) VALUES (?, ?, ?, ?, ?, ?)"; private static final String SQL_DELETE_PAR_ID = "DELETE FROM Client WHERE id = ?"; private DAOFactory daoFactory;

ClientDaoImpl( DAOFactory daoFactory ) { this.daoFactory = daoFactory; } /* Implmentation de la mthode dfinie dans l'interface ClientDao */ @Override public Client trouver( long id ) throws DAOException { return trouver( SQL_SELECT_PAR_ID, id );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} /* Implmentation de la mthode dfinie dans l'interface ClientDao */ @Override public void creer( Client client ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet valeursAutoGenerees = null; try { connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_INSERT, true, client.getNom(), client.getPrenom(), client.getAdresse(), client.getTelephone(), client.getEmail(), client.getImage() ); int statut = preparedStatement.executeUpdate(); if ( statut == 0 ) { throw new DAOException( "chec de la cration du client, aucune ligne ajoute dans la table." ); } valeursAutoGenerees = preparedStatement.getGeneratedKeys(); if ( valeursAutoGenerees.next() ) { client.setId( valeursAutoGenerees.getLong( 1 ) ); } else { throw new DAOException( "chec de la cration du client en base, aucun ID auto-gnr retourn." ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( valeursAutoGenerees, preparedStatement, connexion ); } } /* Implmentation de la mthode dfinie dans l'interface ClientDao */ @Override public List<Client> lister() throws DAOException { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; List<Client> clients = new ArrayList<Client>(); try { connection = daoFactory.getConnection(); preparedStatement = connection.prepareStatement( SQL_SELECT ); resultSet = preparedStatement.executeQuery(); while ( resultSet.next() ) { clients.add( map( resultSet ) ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( resultSet, preparedStatement, connection ); } } return clients;

437/606

/* Implmentation de la mthode dfinie dans l'interface ClientDao */ @Override public void supprimer( Client client ) throws DAOException { Connection connexion = null;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


PreparedStatement preparedStatement = null; try { connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_DELETE_PAR_ID, true, client.getId() ); int statut = preparedStatement.executeUpdate(); if ( statut == 0 ) { throw new DAOException( "chec de la suppression du client, aucune ligne supprime de la table." ); } else { client.setId( null ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( preparedStatement, connexion ); } } /* * Mthode gnrique utilise pour retourner un client depuis la base de * donnes, correspondant la requte SQL donne prenant en paramtres les * objets passs en argument. */ private Client trouver( String sql, Object... objets ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; Client client = null; try { /* Rcupration d'une connexion depuis la Factory */ connexion = daoFactory.getConnection(); /* * Prparation de la requte avec les objets passs en arguments * (ici, uniquement un id) et excution. */ preparedStatement = initialisationRequetePreparee( connexion, sql, false, objets ); resultSet = preparedStatement.executeQuery(); /* Parcours de la ligne de donnes retourne dans le ResultSet */ if ( resultSet.next() ) { client = map( resultSet ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( resultSet, preparedStatement, connexion ); } } return client;

438/606

/* * Simple mthode utilitaire permettant de faire la correspondance (le * mapping) entre une ligne issue de la table des clients (un ResultSet) et * un bean Client. */ private static Client map( ResultSet resultSet ) throws SQLException { Client client = new Client();

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


client.setId( resultSet.getLong( "id" ) ); client.setNom( resultSet.getString( "nom" ) ); client.setPrenom( resultSet.getString( "prenom" ) ); client.setAdresse( resultSet.getString( "adresse" ) ); client.setTelephone( resultSet.getString( "telephone" ) ); client.setEmail( resultSet.getString( "email" ) ); client.setImage( resultSet.getString( "image" ) ); return client;

439/606

} }

Code de CommandeDaoImpl : Code : Java - com.sdzee.tp.dao.CommandeDaoImpl package com.sdzee.tp.dao; import static com.sdzee.tp.dao.DAOUtilitaire.fermeturesSilencieuses; import static com.sdzee.tp.dao.DAOUtilitaire.initialisationRequetePreparee; import import import import import import import java.sql.Connection; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.SQLException; java.sql.Timestamp; java.util.ArrayList; java.util.List;

import org.joda.time.DateTime; import com.sdzee.tp.beans.Commande; public class CommandeDaoImpl implements CommandeDao { private static final String SQL_SELECT = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande ORDER BY id"; private static final String SQL_SELECT_PAR_ID = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande WHERE id = ?"; private static final String SQL_INSERT = "INSERT INTO Commande (id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison) VALUES (?, ?, ? , ?, ?, ?, ?)"; private static final String SQL_DELETE_PAR_ID = "DELETE FROM Commande WHERE id = ?"; private DAOFactory daoFactory;

CommandeDaoImpl( DAOFactory daoFactory ) { this.daoFactory = daoFactory; } /* Implmentation de la mthode dfinie dans l'interface CommandeDao */ @Override public Commande trouver( long id ) throws DAOException { return trouver( SQL_SELECT_PAR_ID, id ); } /* Implmentation de la mthode dfinie dans l'interface CommandeDao */ @Override

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


public void creer( Commande commande ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet valeursAutoGenerees = null; try { connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_INSERT, true, commande.getClient().getId(), new Timestamp( commande.getDate().getMillis() ), commande.getMontant(), commande.getModePaiement(), commande.getStatutPaiement(), commande.getModeLivraison(), commande.getStatutLivraison() ); int statut = preparedStatement.executeUpdate(); if ( statut == 0 ) { throw new DAOException( "chec de la cration de la commande, aucune ligne ajoute dans la table." ); } valeursAutoGenerees = preparedStatement.getGeneratedKeys(); if ( valeursAutoGenerees.next() ) { commande.setId( valeursAutoGenerees.getLong( 1 ) ); } else { throw new DAOException( "chec de la cration de la commande en base, aucun ID auto-gnr retourn." ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( valeursAutoGenerees, preparedStatement, connexion ); } } /* Implmentation de la mthode dfinie dans l'interface ClientDao */ @Override public List<Commande> lister() throws DAOException { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; List<Commande> commandes = new ArrayList<Commande>(); try { connection = daoFactory.getConnection(); preparedStatement = connection.prepareStatement( SQL_SELECT ); resultSet = preparedStatement.executeQuery(); while ( resultSet.next() ) { commandes.add( map( resultSet ) ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( resultSet, preparedStatement, connection ); } } return commandes;

440/606

/* Implmentation de la mthode dfinie dans l'interface CommandeDao */ @Override public void supprimer( Commande commande ) throws DAOException {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Connection connexion = null; PreparedStatement preparedStatement = null; try { connexion = daoFactory.getConnection(); preparedStatement = initialisationRequetePreparee( connexion, SQL_DELETE_PAR_ID, true, commande.getId() ); int statut = preparedStatement.executeUpdate(); if ( statut == 0 ) { throw new DAOException( "chec de la suppression de la commande, aucune ligne supprime de la table." ); } else { commande.setId( null ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( preparedStatement, connexion ); } } /* * Mthode gnrique utilise pour retourner une commande depuis la base de * donnes, correspondant la requte SQL donne prenant en paramtres les * objets passs en argument. */ private Commande trouver( String sql, Object... objets ) throws DAOException { Connection connexion = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; Commande commande = null; try { /* Rcupration d'une connexion depuis la Factory */ connexion = daoFactory.getConnection(); /* * Prparation de la requte avec les objets passs en arguments * (ici, uniquement un id) et excution. */ preparedStatement = initialisationRequetePreparee( connexion, sql, false, objets ); resultSet = preparedStatement.executeQuery(); /* Parcours de la ligne de donnes retourne dans le ResultSet */ if ( resultSet.next() ) { commande = map( resultSet ); } } catch ( SQLException e ) { throw new DAOException( e ); } finally { fermeturesSilencieuses( resultSet, preparedStatement, connexion ); } } return commande;

441/606

/* * Simple mthode utilitaire permettant de faire la correspondance (le * mapping) entre une ligne issue de la table des commandes (un ResultSet) * et un bean Commande. */ private Commande map( ResultSet resultSet ) throws SQLException {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Commande commande = new Commande(); commande.setId( resultSet.getLong( "id" ) ); /* * Petit changement ici : pour rcuprer un client, il nous faut faire * appel la mthode trouver() du DAO Client, afin de rcuprer un bean * Client partir de l'id prsent dans la table Commande. */ ClientDao clientDao = daoFactory.getClientDao(); commande.setClient( clientDao.trouver( resultSet.getLong( "id_client" ) ) ); commande.setDate( new DateTime( resultSet.getTimestamp( "date" ) ) ); commande.setMontant( resultSet.getDouble( "montant" ) ); commande.setModePaiement( resultSet.getString( "mode_paiement" ) ); commande.setStatutPaiement( resultSet.getString( "statut_paiement" ) ); commande.setModeLivraison( resultSet.getString( "mode_livraison" ) ); commande.setStatutLivraison( resultSet.getString( "statut_livraison" ) ); return commande; } }

442/606

Code des beans


Secret (cliquez pour afficher) Client : Code : Java - com.sdzee.tp.beans.Client package com.sdzee.tp.beans; import java.io.Serializable; public class Client implements Serializable { private private private private private private private Long String String String String String String id; nom; prenom; adresse; telephone; email; image;

public void setId( Long id ) { this.id = id; } public Long getId() { return id; } public void setNom( String nom ) { this.nom = nom; } public String getNom() {

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} return nom;

443/606

public void setPrenom( String prenom ) { this.prenom = prenom; } public String getPrenom() { return prenom; } public void setAdresse( String adresse ) { this.adresse = adresse; } public String getAdresse() { return adresse; } public void setTelephone( String telephone ) { this.telephone = telephone; } public String getTelephone() { return telephone; } public void setEmail( String email ) { this.email = email; } public String getEmail() { return email; } public void setImage( String image ) { this.image = image; } public String getImage() { return image; }

Commande : Code : Java - com.sdzee.tp.beans.Commande package com.sdzee.tp.beans; import java.io.Serializable; import org.joda.time.DateTime; public class Commande implements Serializable { private Long id; private Client client; private DateTime date; private Double montant; private String modePaiement; private String statutPaiement; private String modeLivraison; private String statutLivraison; public Long getId() { return id; }

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


public void setId( Long id ) { this.id = id; } public Client getClient() { return client; } public void setClient( Client client ) { this.client = client; } public DateTime getDate() { return date; } public void setDate( DateTime date ) { this.date = date; } public Double getMontant() { return montant; } public void setMontant( Double montant ) { this.montant = montant; } public String getModePaiement() { return modePaiement; } public void setModePaiement( String modePaiement ) { this.modePaiement = modePaiement; } public String getStatutPaiement() { return statutPaiement; } public void setStatutPaiement( String statutPaiement ) { this.statutPaiement = statutPaiement; } public String getModeLivraison() { return modeLivraison; } public void setModeLivraison( String modeLivraison ) { this.modeLivraison = modeLivraison; } public String getStatutLivraison() { return statutLivraison; } public void setStatutLivraison( String statutLivraison ) { this.statutLivraison = statutLivraison; }

444/606

Code des objets mtier


Secret (cliquez pour afficher)

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Code de CreationClientForm : Code : Java - com.sdzee.tp.forms.CreationClientForm package com.sdzee.tp.forms; import import import import import import import import import java.io.BufferedInputStream ; java.io.BufferedOutputStream ; java.io.File; java.io.FileOutputStream ; java.io.IOException; java.io.InputStream ; java.util.Collection; java.util.HashMap; java.util.Map;

445/606

import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import com.sdzee.tp.beans.Client; import com.sdzee.tp.dao.ClientDao; import com.sdzee.tp.dao.DAOException; import eu.medsea.mimeutil.MimeUtil; public final class private static private static private static private static "telephoneClient"; private static private static CreationClientForm { final String CHAMP_NOM final String CHAMP_PRENOM final String CHAMP_ADRESSE final String CHAMP_TELEPHONE final String CHAMP_EMAIL final String CHAMP_IMAGE TAILLE_TAMPON = "nomClient"; = "prenomClient"; = "adresseClient"; = = "emailClient"; = "imageClient"; = 10240;

private static final int // 10ko

private String resultat; private Map<String, String> erreurs HashMap<String, String>(); private ClientDao clientDao;

= new

public CreationClientForm( ClientDao clientDao ) { this.clientDao = clientDao; } public Map<String, String> getErreurs() { return erreurs; } public String getResultat() { return resultat; } public Client creerClient( HttpServletRequest request, String chemin ) { String nom = getValeurChamp( request, CHAMP_NOM ); String prenom = getValeurChamp( request, CHAMP_PRENOM ); String adresse = getValeurChamp( request, CHAMP_ADRESSE ); String telephone = getValeurChamp( request, CHAMP_TELEPHONE ); String email = getValeurChamp( request, CHAMP_EMAIL ); Client client = new Client(); traiterNom( nom, client ); traiterPrenom( prenom, client ); traiterAdresse( adresse, client );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


traiterTelephone( telephone, client ); traiterEmail( email, client ); traiterImage( client, request, chemin ); try { if ( erreurs.isEmpty() ) { clientDao.creer( client ); resultat = "Succs de la cration du client."; } else { resultat = "chec de la cration du client."; } } catch ( DAOException e ) { setErreur( "imprvu", "Erreur imprvue lors de la cration." ); resultat = "chec de la cration du client : une erreur imprvue est survenue, merci de ressayer dans quelques instants."; e.printStackTrace(); } } return client;

446/606

private void traiterNom( String nom, Client client ) { try { validationNom( nom ); } catch ( FormValidationException e ) { setErreur( CHAMP_NOM, e.getMessage() ); } client.setNom( nom ); } private void traiterPrenom( String prenom, Client client ) { try { validationPrenom( prenom ); } catch ( FormValidationException e ) { setErreur( CHAMP_PRENOM, e.getMessage() ); } client.setPrenom( prenom ); } private void traiterAdresse( String adresse, Client client ) { try { validationAdresse( adresse ); } catch ( FormValidationException e ) { setErreur( CHAMP_ADRESSE, e.getMessage() ); } client.setAdresse( adresse ); } ) { private void traiterTelephone( String telephone, Client client try { validationTelephone( telephone ); } catch ( FormValidationException e ) { setErreur( CHAMP_TELEPHONE, e.getMessage() ); } client.setTelephone( telephone );

private void traiterEmail( String email, Client client ) { try { validationEmail( email ); } catch ( FormValidationException e ) { setErreur( CHAMP_EMAIL, e.getMessage() ); } client.setEmail( email ); } private void traiterImage( Client client, HttpServletRequest

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


request, String chemin ) { String image = null; try { image = validationImage( request, chemin ); } catch ( FormValidationException e ) { setErreur( CHAMP_IMAGE, e.getMessage() ); } client.setImage( image ); } private void validationNom( String nom ) throws FormValidationException { if ( nom != null ) { if ( nom.length() < 2 ) { throw new FormValidationException( "Le nom d'utilisateur doit contenir au moins 2 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer un nom d'utilisateur." ); } } private void validationPrenom( String prenom ) throws FormValidationException { if ( prenom != null && prenom.length() < 2 ) { throw new FormValidationException( "Le prnom d'utilisateur doit contenir au moins 2 caractres." ); } } private void validationAdresse( String adresse ) throws FormValidationException { if ( adresse != null ) { if ( adresse.length() < 10 ) { throw new FormValidationException( "L'adresse de livraison doit contenir au moins 10 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer une adresse de livraison." ); } } private void validationTelephone( String telephone ) throws FormValidationException { if ( telephone != null ) { if ( !telephone.matches( "^\\d+$" ) ) { throw new FormValidationException( "Le numro de tlphone doit uniquement contenir des chiffres." ); } else if ( telephone.length() < 4 ) { throw new FormValidationException( "Le numro de tlphone doit contenir au moins 4 chiffres." ); } } else { throw new FormValidationException( "Merci d'entrer un numro de tlphone." ); } } private void validationEmail( String email ) throws FormValidationException { if ( email != null && !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) { throw new FormValidationException( "Merci de saisir une adresse mail valide." ); } } private String validationImage( HttpServletRequest request,

447/606

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


String chemin ) throws FormValidationException { /* * Rcupration du contenu du champ image du formulaire. Il faut ici * utiliser la mthode getPart(). */ String nomFichier = null; InputStream contenuFichier = null; try { Part part = request.getPart( CHAMP_IMAGE ); nomFichier = getNomFichier( part ); /* * Si la mthode getNomFichier() a renvoy quelque chose, il s'agit * donc d'un champ de type fichier (input type="file"). */ if ( nomFichier != null && !nomFichier.isEmpty() ) { /* * Antibug pour Internet Explorer, qui transmet pour une raison * mystique le chemin du fichier local la machine du client... * * Ex : C:/dossier/sous-dossier/fichier.ext * * On doit donc faire en sorte de ne slectionner que le nom et * l'extension du fichier, et de se dbarrasser du superflu. */ nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ) .substring( nomFichier.lastIndexOf( '\\' ) + 1 ); /* Rcupration du contenu du fichier */ contenuFichier = part.getInputStream(); l'InputStream */ /* Extraction du type MIME du fichier depuis

448/606

MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" ); Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenuFichier ); /* * Si le fichier est bien une image, alors son en-tte MIME * commence par la chane "image" */ if ( mimeTypes.toString().startsWith( "image" ) ) { /* Ecriture du fichier sur le disque */ ecrireFichier( contenuFichier, nomFichier, chemin ); } else { throw new FormValidationException( "Le fichier envoy doit tre une image." ); } } } catch ( IllegalStateException e ) { /* * Exception retourne si la taille des donnes dpasse les limites * dfinies dans la section <multipart-config> de la dclaration de * notre servlet d'upload dans le fichier web.xml */ e.printStackTrace(); throw new FormValidationException( "Le fichier envoy ne doit pas dpasser 1Mo." ); } catch ( IOException e ) { /* * Exception retourne si une erreur au niveau des rpertoires de

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


* stockage survient (rpertoire inexistant, droits d'accs * insuffisants, etc.) */ e.printStackTrace(); throw new FormValidationException( "Erreur de configuration du serveur." ); } catch ( ServletException e ) { /* * Exception retourne si la requte n'est pas de type * multipart/form-data. */ e.printStackTrace(); throw new FormValidationException( "Ce type de requte n'est pas support, merci d'utiliser le formulaire prvu pour envoyer votre fichier." ); } } return nomFichier;

449/606

/* * Ajoute un message correspondant au champ spcifi la map des erreurs. */ private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } /* * Mthode utilitaire qui a pour unique but d'analyser l'en-tte * "content-disposition", et de vrifier si le paramtre "filename" y est * prsent. Si oui, alors le champ trait est de type File et la mthode * retourne son nom, sinon il s'agit d'un champ de formulaire classique et * la mthode retourne null. */ private static String getNomFichier( Part part ) { /* Boucle sur chacun des paramtres de l'en-tte "content-disposition". */ for ( String contentDisposition : part.getHeader( "content-disposition" ).split( ";" ) ) { /* Recherche de l'ventuelle prsence du paramtre "filename". */ if ( contentDisposition.trim().startsWith( "filename" ) ) { /* * Si "filename" est prsent, alors renvoi de sa valeur, * c'est--dire du nom de fichier sans guillemets. */ return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ).trim().replace( "\"", "" ); }

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} /* Et pour terminer, si rien n'a t trouv... */ return null;

450/606

/* * Mthode utilitaire qui a pour but d'crire le fichier pass en paramtre * sur le disque, dans le rpertoire donn et avec le nom donn. */ private void ecrireFichier( InputStream contenuFichier, String nomFichier, String chemin ) throws FormValidationException { /* Prpare les flux. */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux. */ entree = new BufferedInputStream( contenuFichier, TAILLE_TAMPON ); sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), TAILLE_TAMPON ); /* * Lit le fichier reu et crit son contenu dans un fichier sur le * disque. */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur = 0; while ( ( longueur = entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur ); } } catch ( Exception e ) { throw new FormValidationException( "Erreur lors de l'criture du fichier sur le disque." ); } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } } } }

Code de CreationCommandeForm : Code : Java - com.sdzee.tp.forms.CreationCommandeForm package com.sdzee.tp.forms; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import com.sdzee.tp.beans.Client; import com.sdzee.tp.beans.Commande; import com.sdzee.tp.dao.ClientDao;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import com.sdzee.tp.dao.CommandeDao; import com.sdzee.tp.dao.DAOException; public final class CreationCommandeForm { private static final String CHAMP_CHOIX_CLIENT "choixNouveauClient"; private static final String CHAMP_LISTE_CLIENTS "listeClients"; private static final String CHAMP_DATE "dateCommande"; private static final String CHAMP_MONTANT "montantCommande"; private static final String CHAMP_MODE_PAIEMENT "modePaiementCommande"; private static final String CHAMP_STATUT_PAIEMENT "statutPaiementCommande"; private static final String CHAMP_MODE_LIVRAISON "modeLivraisonCommande"; private static final String CHAMP_STATUT_LIVRAISON "statutLivraisonCommande"; private static final String ANCIEN_CLIENT "ancienClient"; private static final String SESSION_CLIENTS "clients"; private static final String FORMAT_DATE "dd/MM/yyyy HH:mm:ss"; private String private Map<String, String> HashMap<String, String>(); private ClientDao private CommandeDao resultat; erreurs clientDao; commandeDao; = = = = = = = = = = =

451/606

= new

public CreationCommandeForm( ClientDao clientDao, CommandeDao commandeDao ) { this.clientDao = clientDao; this.commandeDao = commandeDao; } public Map<String, String> getErreurs() { return erreurs; } public String getResultat() { return resultat; } public Commande creerCommande( HttpServletRequest request, String chemin ) { Client client; /* * Si l'utilisateur choisit un client dj existant, pas de validation * effectuer */ String choixNouveauClient = getValeurChamp( request, CHAMP_CHOIX_CLIENT ); if ( ANCIEN_CLIENT.equals( choixNouveauClient ) ) { /* Rcupration de l'id du client choisi */ String idAncienClient = getValeurChamp( request, CHAMP_LISTE_CLIENTS ); Long id = null; try { id = Long.parseLong( idAncienClient ); } catch ( NumberFormatException e ) { setErreur( CHAMP_CHOIX_CLIENT, "Client inconnu, merci d'utiliser le formulaire prvu cet effet." ); id = 0L; }

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


/* Rcupration de l'objet client correspondant dans la session */ HttpSession session = request.getSession(); client = ( (Map<Long, Client>) session.getAttribute( SESSION_CLIENTS ) ).get( id ); } else { /* * Sinon on garde l'ancien mode, pour la validation des champs. * * L'objet mtier pour valider la cration d'un client existe dj , * il est donc dconseill de dupliquer ici son contenu ! la * place, il suffit de passer la requte courante l'objet mtier * existant et de rcuprer l'objet Client cr. */ CreationClientForm clientForm = new CreationClientForm( clientDao ); client = clientForm.creerClient( request, chemin ); /* * Et trs important, il ne faut pas oublier de rcuprer le contenu * de la map d'erreur cre par l'objet mtier CreationClientForm * dans la map d'erreurs courante, actuellement vide. */ erreurs = clientForm.getErreurs(); } /* * Ensuite, il suffit de procder normalement avec le reste des champs * spcifiques une commande. */ /* Rcupration de la date dans un DateTime Joda. */ DateTime dt = new DateTime(); String montant = getValeurChamp( request, CHAMP_MONTANT ); String modePaiement = getValeurChamp( request, CHAMP_MODE_PAIEMENT ); String statutPaiement = getValeurChamp( request, CHAMP_STATUT_PAIEMENT ); String modeLivraison = getValeurChamp( request, CHAMP_MODE_LIVRAISON ); String statutLivraison = getValeurChamp( request, CHAMP_STATUT_LIVRAISON ); Commande commande = new Commande(); try { traiterClient( client, commande ); commande.setDate( dt ); traiterMontant( montant, commande ); traiterModePaiement( modePaiement, commande ); traiterStatutPaiement( statutPaiement, commande ); traiterModeLivraison( modeLivraison, commande ); traiterStatutLivraison( statutLivraison, commande ); if ( erreurs.isEmpty() ) { commandeDao.creer( commande ); resultat = "Succs de la cration de la

452/606

commande.";

} else { resultat = "chec de la cration de la commande."; } } catch ( DAOException e ) { setErreur( "imprvu", "Erreur imprvue lors de la cration." );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


resultat = "chec de la cration de la commande : une erreur imprvue est survenue, merci de ressayer dans quelques instants."; e.printStackTrace(); } } { return commande;

453/606

private void traiterClient( Client client, Commande commande )

if ( client == null ) { setErreur( CHAMP_CHOIX_CLIENT, "Client inconnu, merci d'utilier le formulaire prvu cet effet." ); } commande.setClient( client ); } ) { private void traiterMontant( String montant, Commande commande double valeurMontant = -1; try { valeurMontant = validationMontant( montant ); } catch ( FormValidationException e ) { setErreur( CHAMP_MONTANT, e.getMessage() ); } commande.setMontant( valeurMontant );

private void traiterModePaiement( String modePaiement, Commande commande ) { try { validationModePaiement( modePaiement ); } catch ( FormValidationException e ) { setErreur( CHAMP_MODE_PAIEMENT, e.getMessage() ); } commande.setModePaiement( modePaiement ); } private void traiterStatutPaiement( String statutPaiement, Commande commande ) { try { validationStatutPaiement( statutPaiement ); } catch ( FormValidationException e ) { setErreur( CHAMP_STATUT_PAIEMENT, e.getMessage() ); } commande.setStatutPaiement( statutPaiement ); } private void traiterModeLivraison( String modeLivraison, Commande commande ) { try { validationModeLivraison( modeLivraison ); } catch ( FormValidationException e ) { setErreur( CHAMP_MODE_LIVRAISON, e.getMessage() ); } commande.setModeLivraison( modeLivraison ); } private void traiterStatutLivraison( String statutLivraison, Commande commande ) { try { validationStatutLivraison( statutLivraison ); } catch ( FormValidationException e ) { setErreur( CHAMP_STATUT_LIVRAISON, e.getMessage() ); } commande.setStatutLivraison( statutLivraison ); } private double validationMontant( String montant ) throws

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


FormValidationException { double temp; if ( montant != null ) { try { temp = Double.parseDouble( montant ); if ( temp < 0 ) { throw new FormValidationException( "Le montant doit tre un nombre positif." ); } } catch ( NumberFormatException e ) { temp = -1; throw new FormValidationException( "Le montant doit tre un nombre." ); } } else { temp = -1; throw new FormValidationException( "Merci d'entrer un montant." ); } return temp; } private void validationModePaiement( String modePaiement ) throws FormValidationException { if ( modePaiement != null ) { if ( modePaiement.length() < 2 ) { throw new FormValidationException( "Le mode de paiement doit contenir au moins 2 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer un mode de paiement." ); } } private void validationStatutPaiement( String statutPaiement ) throws FormValidationException { if ( statutPaiement != null && statutPaiement.length() < 2 ) { throw new FormValidationException( "Le statut de paiement doit contenir au moins 2 caractres." ); } } private void validationModeLivraison( String modeLivraison ) throws FormValidationException { if ( modeLivraison != null ) { if ( modeLivraison.length() < 2 ) { throw new FormValidationException( "Le mode de livraison doit contenir au moins 2 caractres." ); } } else { throw new FormValidationException( "Merci d'entrer un mode de livraison." ); } } private void validationStatutLivraison( String statutLivraison ) throws FormValidationException { if ( statutLivraison != null && statutLivraison.length() < 2 ) { throw new FormValidationException( "Le statut de livraison doit contenir au moins 2 caractres." ); } } /* * Ajoute un message correspondant au champ spcifi la map des erreurs. */

454/606

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


private void setErreur( String champ, String message ) { erreurs.put( champ, message ); } /* * Mthode utilitaire qui retourne null si un champ est vide, et son contenu * sinon. */ private static String getValeurChamp( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

455/606

Code des servlets


Secret (cliquez pour afficher) Contenu du fichier web.xml : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>Set Character Encoding</filter-name> <filterclass>org.apache.catalina.filters.SetCharacterEncodingFilter</filterclass> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>ignore</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>PrechargementFilter</filter-name> <filterclass>com.sdzee.tp.filters.PrechargementFilter</filter-class> </filter> <filter-mapping> <filter-name>PrechargementFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listenerclass>com.sdzee.tp.config.InitialisationDaoFactory</listener-class> </listener>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


<servlet> <servlet-name>CreationClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationClient</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> <multipart-config> <location>c:/fichiers/images</location> <max-file-size>2097152</max-file-size> <!-- 2 Mo --> <max-request-size>10485760</max-request-size> <!-- 5 x 2Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet> <servlet> <servlet-name>ListeClients</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeClients</servlet-class> </servlet> <servlet> <servlet-name>SuppressionClient</servlet-name> <servlet-class>com.sdzee.tp.servlets.SuppressionClient</servletclass> </servlet> <servlet> <servlet-name>CreationCommande</servlet-name> <servlet-class>com.sdzee.tp.servlets.CreationCommande</servletclass> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> <multipart-config> <location>c:/fichiers/images</location> <max-file-size>2097152</max-file-size> <!-- 2 Mo --> <max-request-size>10485760</max-request-size> <!-- 5 x 2Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet> <servlet> <servlet-name>ListeCommandes</servlet-name> <servlet-class>com.sdzee.tp.servlets.ListeCommandes</servlet-class> </servlet> <servlet> <servlet-name>SuppressionCommande</servlet-name> <servlet-class>com.sdzee.tp.servlets.SuppressionCommande</servletclass> </servlet> <servlet> <servlet-name>Image</servlet-name> <servlet-class>com.sdzee.tp.servlets.Image</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/images/</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>CreationClient</servlet-name> <url-pattern>/creationClient</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeClients</servlet-name> <url-pattern>/listeClients</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SuppressionClient</servlet-name> <url-pattern>/suppressionClient</url-pattern> </servlet-mapping> <servlet-mapping>

456/606

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


<servlet-name>CreationCommande</servlet-name> <url-pattern>/creationCommande</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListeCommandes</servlet-name> <url-pattern>/listeCommandes</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SuppressionCommande</servlet-name> <url-pattern>/suppressionCommande</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Image</servlet-name> <url-pattern>/images/*</url-pattern> </servlet-mapping> </web-app>

457/606

Code de la servlet CreationClient : Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; com.sdzee.tp.beans.Client; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.DAOFactory; com.sdzee.tp.forms.CreationClientForm ; { = = = = = "daofactory"; "chemin"; "client"; "form"; "clients";

public class CreationClient extends HttpServlet public static final String CONF_DAO_FACTORY public static final String CHEMIN public static final String ATT_CLIENT public static final String ATT_FORM public static final String SESSION_CLIENTS public static final String VUE_SUCCES INF/afficherClient.jsp"; public static final String VUE_FORM INF/creerClient.jsp"; private ClientDao clientDao;

= "/WEB= "/WEB-

*/

public void init() throws ServletException { /* Rcupration d'une instance de notre DAO Utilisateur

this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao(); } public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Prparation de l'objet formulaire */ CreationClientForm form = new CreationClientForm( clientDao ); /* Traitement de la requte et rcupration du bean en rsultant */ Client client = form.creerClient( request, chemin ); */ /* Ajout du bean et de l'objet mtier l'objet requte request.setAttribute( ATT_CLIENT, client ); request.setAttribute( ATT_FORM, form );

458/606

/* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<Long, Client>(); } /* Puis ajout du client courant dans la map */ clients.put( client.getId(), client ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Code de la servlet CreationCommande : Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import import import import import import import import import import javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; com.sdzee.tp.beans.Client; com.sdzee.tp.beans.Commande; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.CommandeDao; com.sdzee.tp.dao.DAOFactory; com.sdzee.tp.forms.CreationCommandeForm ; = = = = = = = = = "/WEB= "/WEB"chemin"; "commande"; "form"; "clients";

459/606

public class CreationCommande extends HttpServlet { public static final String CONF_DAO_FACTORY "daofactory"; public static final String CHEMIN public static final String ATT_COMMANDE public static final String ATT_FORM public static final String SESSION_CLIENTS public static final String APPLICATION_CLIENTS "initClients"; public static final String SESSION_COMMANDES "commandes"; public static final String APPLICATION_COMMANDES "initCommandes"; public static final String VUE_SUCCES INF/afficherCommande.jsp"; public static final String VUE_FORM INF/creerCommande.jsp"; private ClientDao private CommandeDao clientDao; commandeDao;

public void init() throws ServletException { /* Rcupration d'une instance de nos DAO Client et Commande */ this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao(); this.commandeDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getCommandeDao(); } public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Prparation de l'objet formulaire */ CreationCommandeForm form = new CreationCommandeForm( clientDao, commandeDao ); /* Traitement de la requte et rcupration du bean en

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


rsultant */ Commande commande = form.creerCommande( request, chemin ); */ /* Ajout du bean et de l'objet mtier l'objet requte request.setAttribute( ATT_COMMANDE, commande ); request.setAttribute( ATT_FORM, form );

460/606

/* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<Long, Client>(); } /* Puis ajout du client de la commande courante dans la map */ clients.put( commande.getClient().getId(), commande.getClient() ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); /* Ensuite rcupration de la map des commandes dans la session */ Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( commandes == null ) { commandes = new HashMap<Long, Commande>(); } /* Puis ajout de la commande courante dans la map */ commandes.put( commande.getId(), commande ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_COMMANDES, commandes ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Code de la servlet SuppressionClient : Code : Java - com.sdzee.tp.servlets.SuppressionClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import import import import com.sdzee.tp.beans.Client; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.DAOException; com.sdzee.tp.dao.DAOFactory; extends HttpServlet { CONF_DAO_FACTORY = "daofactory"; PARAM_ID_CLIENT = "idClient"; SESSION_CLIENTS = "clients"; = "/listeClients";

461/606

public class SuppressionClient public static final String public static final String public static final String

public static final String VUE private ClientDao clientDao;

*/

public void init() throws ServletException { /* Rcupration d'une instance de notre DAO Utilisateur

this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao(); } public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String idClient = getValeurParametre( request, PARAM_ID_CLIENT ); Long id = Long.parseLong( idClient ); /* Rcupration de la Map des clients enregistrs en session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); vides */ /* Si l'id du client et la Map des clients ne sont pas

if ( id != null && clients != null ) { try { /* Alors suppression du client de la BDD */ clientDao.supprimer( clients.get( id ) ); /* Puis suppression du client de la Map */ clients.remove( id ); } catch ( DAOException e ) { e.printStackTrace(); } /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_CLIENTS, clients ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} else { return valeur; }

462/606

Code de la servlet SuppressionCommande : Code : Java - com.sdzee.tp.servlets.SuppressionCommande package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; com.sdzee.tp.beans.Commande; com.sdzee.tp.dao.CommandeDao; com.sdzee.tp.dao.DAOException; com.sdzee.tp.dao.DAOFactory;

public class SuppressionCommande extends HttpServlet { public static final String CONF_DAO_FACTORY = "daofactory"; public static final String PARAM_ID_COMMANDE = "idCommande"; public static final String SESSION_COMMANDES = "commandes"; public static final String VUE "/listeCommandes"; private CommandeDao commandeDao; =

*/

public void init() throws ServletException { /* Rcupration d'une instance de notre DAO Utilisateur

this.commandeDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getCommandeDao(); } public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String idCommande = getValeurParametre( request, PARAM_ID_COMMANDE ); Long id = Long.parseLong( idCommande ); /* Rcupration de la Map des commandes enregistres en session */ HttpSession session = request.getSession(); Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si l'id de la commande et la Map des commandes ne sont pas vides */ if ( id != null && commandes != null ) { try { /* Alors suppression de la commande de la BDD */ commandeDao.supprimer( commandes.get( id ) ); /* Puis suppression de la commande de la Map */ commandes.remove( id );

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} catch ( DAOException e ) { e.printStackTrace(); } /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_COMMANDES, commandes ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

463/606

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

Code du filtre
Secret (cliquez pour afficher) Code du filtre de prchargement : Code : Java - com.sdzee.tp.filters.PrechargementFilter package com.sdzee.tp.filters; import import import import import import import import import import import import import import import import import java.io.IOException; java.util.HashMap; java.util.List; java.util.Map; javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpSession; com.sdzee.tp.beans.Client; com.sdzee.tp.beans.Commande; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.CommandeDao; com.sdzee.tp.dao.DAOFactory;

public class PrechargementFilter implements Filter { public static final String CONF_DAO_FACTORY = "daofactory"; public static final String ATT_SESSION_CLIENTS = "clients"; public static final String ATT_SESSION_COMMANDES =

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


"commandes"; private ClientDao private CommandeDao clientDao; commandeDao;

464/606

public void init( FilterConfig config ) throws ServletException { /* Rcupration d'une instance de nos DAO Client et Commande */ this.clientDao = ( (DAOFactory) config.getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao(); this.commandeDao = ( (DAOFactory) config.getServletContext().getAttribute( CONF_DAO_FACTORY ) ) .getCommandeDao(); } public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException, ServletException { /* Cast de l'objet request */ HttpServletRequest request = (HttpServletRequest) req; /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /* * Si la map des clients n'existe pas en session, alors l'utilisateur se * connecte pour la premire fois et nous devons prcharger en session * les infos contenues dans la BDD. */ if ( session.getAttribute( ATT_SESSION_CLIENTS ) == null ) { /* * Rcupration de la liste des clients existant, et enregistrement * en session */ List<Client> listeClients = clientDao.lister(); Map<Long, Client> mapClients = new HashMap<Long, Client>(); for ( Client client : listeClients ) { mapClients.put( client.getId(), client ); } session.setAttribute( ATT_SESSION_CLIENTS, mapClients ); } /* * De mme pour la map des commandes */ if ( session.getAttribute( ATT_SESSION_COMMANDES ) == null ) { /* * Rcupration de la liste des commandes existant, et * enregistrement en session */ List<Commande> listeCommandes = commandeDao.lister(); Map<Long, Commande> mapCommandes = new HashMap<Long, Commande>(); for ( Commande commande : listeCommandes ) { mapCommandes.put( commande.getId(), commande ); } session.setAttribute( ATT_SESSION_COMMANDES, mapCommandes ); } /* Pour terminer, poursuite de la requte en cours */

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


} chain.doFilter( request, res );

465/606

public void destroy() { }

Code des JSP


Secret (cliquez pour afficher) Code de listerClients.jsp : Code : JSP - /WEB-INF/listerClients.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Liste des clients existants</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <c:choose> <%-- Si aucun client n'existe en session, affichage d'un message par dfaut. --%> <c:when test="${ empty sessionScope.clients }"> <p class="erreur">Aucun client enregistr.</p> </c:when> <%-- Sinon, affichage du tableau. --%> <c:otherwise> <table> <tr> <th>Nom</th> <th>Prnom</th> <th>Adresse</th> <th>Tlphone</th> <th>Email</th> <th>Image</th> <th class="action">Action</th> </tr> <%-- Parcours de la Map des clients en session, et utilisation de l'objet varStatus. --%> <c:forEach items="${ sessionScope.clients }" var="mapClients" varStatus="boucle"> <%-- Simple test de parit sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%> <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}"> <%-- Affichage des proprits du bean Client, qui est stock en tant que valeur de l'entre courante de la map -%> <td><c:out value="${ mapClients.value.nom }"/></td> <td><c:out value="${ mapClients.value.prenom }"/></td> <td><c:out value="${ mapClients.value.adresse }"/></td>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


<td><c:out value="${ mapClients.value.telephone }"/></td> <td><c:out value="${ mapClients.value.email }"/></td> <td> <%-- On ne construit et affiche un lien vers l'image que si elle existe. --%> <c:if test="${ !empty mapClients.value.image }"> <c:set var="image"><c:out value="${ mapClients.value.image }"/></c:set> <a href="<c:url value="/images/${ image }"/>">Voir</a> </c:if> </td> <%-- Lien vers la servlet de suppression, avec passage du nom du client - c'est--dire la cl de la Map - en paramtre grce la balise <c:param/>. --%> <td class="action"> <a href="<c:url value="/suppressionClient"><c:param name="idClient" value="${ mapClients.key }" /></c:url>"> <img src="<c:url value="/inc/supprimer.png"/>" alt="Supprimer" /> </a> </td> </tr> </c:forEach> </table> </c:otherwise> </c:choose> </div> </body> </html>

466/606

Code de listerCommandes.jsp : Code : JSP - /WEB-INF/listerCommandes.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Liste des commandes existantes</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <c:choose> <%-- Si aucune commande n'existe en session, affichage d'un message par dfaut. --%> <c:when test="${ empty sessionScope.commandes }"> <p class="erreur">Aucune commande enregistre.</p> </c:when> <%-- Sinon, affichage du tableau. --%> <c:otherwise> <table> <tr> <th>Client</th> <th>Date</th>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


<th>Montant</th> <th>Mode de paiement</th> <th>Statut de paiement</th> <th>Mode de livraison</th> <th>Statut de livraison</th> <th class="action">Action</th> </tr> <%-- Parcours de la Map des commandes en session, et utilisation de l'objet varStatus. --%> <c:forEach items="${ sessionScope.commandes }" var="mapCommandes" varStatus="boucle"> <%-- Simple test de parit sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%> <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}"> <%-- Affichage des proprits du bean Commande, qui est stock en tant que valeur de l'entre courante de la map --%> <td><c:out value="${ mapCommandes.value.client.prenom } ${ mapCommandes.value.client.nom }"/></td> <td><joda:format value="${ mapCommandes.value.date }" pattern="dd/MM/yyyy HH:mm:ss"/></td> <td><c:out value="${ mapCommandes.value.montant }"/></td> <td><c:out value="${ mapCommandes.value.modePaiement }"/></td> <td><c:out value="${ mapCommandes.value.statutPaiement }"/></td> <td><c:out value="${ mapCommandes.value.modeLivraison }"/></td> <td><c:out value="${ mapCommandes.value.statutLivraison }"/></td> <%-- Lien vers la servlet de suppression, avec passage de la date de la commande - c'est--dire la cl de la Map - en paramtre grce la balise <c:param/>. --%> <td class="action"> <a href="<c:url value="/suppressionCommande"><c:param name="idCommande" value="${ mapCommandes.key }" /></c:url>"> <img src="<c:url value="/inc/supprimer.png"/>" alt="Supprimer" /> </a> </td> </tr> </c:forEach> </table> </c:otherwise> </c:choose> </div> </body> </html>

467/606

Code de creerCommande.jsp : Code : JSP - /WEB-INF/creerCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cration d'une commande</title>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

468/606

<link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div> <form method="post" action="<c:url value="/creationCommande"/>" enctype="multipart/form-data"> <fieldset> <legend>Informations client</legend> <%-- Si et seulement si la Map des clients en session n'est pas vide, alors on propose un choix l'utilisateur --%> <c:if test="${ !empty sessionScope.clients }"> <label for="choixNouveauClient">Nouveau client ? <span class="requis">*</span></label> <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="nouveauClient" checked /> Oui <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="ancienClient" /> Non <br/><br /> </c:if> scope="request" /> <c:set var="client" value="${ commande.client }" <div id="nouveauClient"> <c:import url="/inc/inc_client_form.jsp" /> </div>

<%-- Si et seulement si la Map des clients en session n'est pas vide, alors on cre la liste droulante --%> <c:if test="${ !empty sessionScope.clients }"> <div id="ancienClient"> <select name="listeClients" id="listeClients"> <option value="">Choisissez un client...</option> <%-- Boucle sur la map des clients --%> <c:forEach items="${ sessionScope.clients }" var="mapClients"> <%-- L'expression EL ${mapClients.value} permet de cibler l'objet Client stock en tant que valeur dans la Map, et on cible ensuite simplement ses proprits nom et prenom comme on le ferait avec n'importe quel bean. --%> <option value="${ mapClients.key }">${ mapClients.value.prenom } ${ mapClients.value.nom }</option> </c:forEach> </select> </div> </c:if> </fieldset> <fieldset> <legend>Informations commande</legend> <label for="dateCommande">Date <span class="requis">*</span></label> <input type="text" id="v" name="dateCommande" value="<joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"/>" size="30" maxlength="30" disabled /> <span class="erreur">${form.erreurs['dateCommande']}</span> <br /> <label for="montantCommande">Montant <span class="requis">*</span></label> <input type="text" id="montantCommande" name="montantCommande" value="<c:out value="${commande.montant}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['montantCommande']}</span> <br /> <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

469/606

<input type="text" id="modePaiementCommande" name="modePaiementCommande" value="<c:out value="${commande.modePaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modePaiementCommande']}</span> <br /> paiement</label> <label for="statutPaiementCommande">Statut du

<input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="<c:out value="${commande.statutPaiement}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutPaiementCommande']}</span> <br /> <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label> <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="<c:out value="${commande.modeLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['modeLivraisonCommande']}</span> <br /> livraison</label> <label for="statutLivraisonCommande">Statut de la

<input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="<c:out value="${commande.statutLivraison}"/>" size="30" maxlength="30" /> <span class="erreur">${form.erreurs['statutLivraisonCommande']}</span> <br /> <p class="info">${ form.resultat }</p> </fieldset> <input type="submit" value="Valider" /> <input type="reset" value="Remettre zro" /> <br /> </form> </div> <%-- Inclusion de la bibliothque jQuery. Vous trouverez des cours sur JavaScript et jQuery aux adresses suivantes : - http://www.siteduzero.com/tutoriel-3-309961-dynamisez-vossites-web-avec-javascript.html - http://www.siteduzero.com/tutoriel-3-659477-un-site-webdynamique-avec-jquery.html Si vous ne souhaitez pas tlcharger et ajouter jQuery votre projet, vous pouvez utiliser la version fournie directement en ligne par Google : <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script --%> <script src="<c:url value="/inc/jquery.js"/>"></script> <%-- Petite fonction jQuery permettant le remplacement de la premire partie du formulaire par la liste droulante, au clic sur le bouton radio. --%> <script> jQuery(document).ready(function(){ /* 1 - Au lancement de la page, on cache le bloc d'lments du formulaire correspondant aux clients existants */ $("div#ancienClient").hide(); /* 2 - Au clic sur un des deux boutons radio "choixNouveauClient", on affiche le bloc d'lments correspondant (nouveau ou ancien client) */ jQuery('input[name=choixNouveauClient]:radio').click(function(){ $("div#nouveauClient").hide(); $("div#ancienClient").hide(); var divId = jQuery(this).val(); $("div#"+divId).show();

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


}); }); </script> </body> </html>

470/606

Code de afficherCommande.jsp : Code : JSP - /WEB-INF/afficherCommande.jsp <%@ page pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Affichage d'une commande</title> <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"/>" /> </head> <body> <c:import url="/inc/menu.jsp" /> <div id="corps"> <p class="info">${ form.resultat }</p> <p>Client</p> <p>Nom : <c:out value="${ commande.client.nom }"/></p> <p>Prnom : <c:out value="${ commande.client.prenom }"/></p> <p>Adresse : <c:out value="${ commande.client.adresse }"/></p> <p>Numro de tlphone : <c:out value="${ commande.client.telephone }"/></p> <p>Email : <c:out value="${ commande.client.email }"/></p> <p>Image : <c:out value="${ commande.client.image }"/></p> <p>Commande</p> <p>Date : <joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"/></p> <p>Montant : <c:out value="${ commande.montant }"/></p> <p>Mode de paiement : <c:out value="${ commande.modePaiement }"/></p> <p>Statut du paiement : <c:out value="${ commande.statutPaiement }"/></p> <p>Mode de livraison : <c:out value="${ commande.modeLivraison }"/></p> <p>Statut de la livraison : <c:out value="${ commande.statutLivraison }"/></p> </div> </body> </html>

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

471/606

Grer un pool de connexions avec BoneCP


La solution que nous avons mise en place souffre d'une carence importante : elle manque cruellement d'ambition ! Je n'ai fait qu'effleurer la question lors de notre apprentissage de JDBC, il est maintenant temps de nous y attarder et d'y apporter une solution simple et efficace.

Contexte
Dans le second chapitre de cette partie, je vous ai averti que l'ouverture d'une connexion avec la BDD avait un cot non ngligeable en termes de performances, et que dans une application trs frquente, il tait hors de question de procder des ouvertures/fermetures chaque requte effectue sans signer l'arrt de mort de votre serveur ! quoi faisais-je allusion ? Penchons-nous un instant sur le contexte du problme.

Une application multi-utilisateurs


Une application web est une application centralise laquelle tous les utilisateurs accdent distance depuis leurs navigateurs. Partant de cette dfinition simpliste, il est ais de deviner les limites du systme : si un nombre consquent d'utilisateurs diffrents ralise des actions sur le site dans un intervalle de temps restreint, voire de manire simultane, le serveur va devoir traiter l'ensemble des requtes reues une vitesse folle pour assurer un certain confort de navigation au visiteur d'une part, et pour ne pas s'crouler sous la charge d'autre part. Ceci tant dit, il ne faut pas s'alarmer pour autant : tous les composants d'un serveur d'applications ne sont pas critiques. Sans plus attendre, voyons celui qui pche le premier...

Le cot d'une connexion la BDD


Le maillon faible de la chane est sans surprise la liaison entre l'application (pour gnraliser, disons le conteneur web) et la base de donnes. C'est ici que la dgradation des performances sera la plus importante, tant par sa rapidit que par sa lourdeur de consquences. De quel problme exactement sommes-nous en train de parler ?

L'tablissement d'une connexion entre notre application et notre base de donnes MySQL un cot en temps non ngligeable, qui peut monter jusqu' plusieurs centaines de millisecondes. Les principales raisons de cette latence sont la ncessit d'tablir une connexion TCP/IP entre le conteneur et le SGBD d'une part, avec les contraintes naturelles que cela pose (lenteur du rseau, pare-feu, filtrages, etc.), et le fait que le SGBD va devoir prparer des informations relatives l'utilisateur entrant chaque nouvelle connexion. Alors certes, l'chelle d'une connexion cela reste tout fait ngligeable : quelques diximes de seconde de ralentissement sont presque anecdotiques... Oui, mais imaginez maintenant que cinquante utilisateurs effectuent simultanment des actions faisant intervenir une communication avec la base de donnes sur notre site : notre SGBD doit alors crer autant de nouvelles connexions, et nous passons ainsi de quelques diximes de secondes plusieurs secondes de latence ! Et a bien videmment, c'est inacceptable : sans parler de l'attente ct utilisateur, votre application va de son ct continuer recevoir des requtes et tenter d'tablir des connexions avec votre base, jusqu'au moment o la limite sera atteinte et votre SGBD cessera de rpondre, causant tout bonnement le plantage lamentable de votre application. En revanche, le reste des oprations effectues sur une base de donnes est trs rapide ! Typiquement, la plupart de celles bases sur une connexion dj ouverte s'effectuent en quelques millisecondes seulement.

La structure actuelle de notre solution


Pour couronner le tout, la solution que nous avons mise en place fonctionne trs bien pour effectuer des tests sur notre poste de dveloppement, mais n'est pas du tout adapte une utilisation en production ! Eh oui, rflchissez bien : alors que nous aurions pu nous contenter d'ouvrir une seule connexion et de la partager l'ensemble des mthodes d'un DAO, nous procdons l'ouverture/fermeture d'une connexion chaque requte effectue ! Pour vous rafrachir la mmoire, voici mis plat le squelette basique que nous avons appliqu : Code : Java - Structure de base des mthodes de nos DAO Connection connexion = null;

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


PreparedStatement preparedStatement = null; ResultSet resultat = null; try { connexion = daoFactory.getConnection(); preparedStatement = connexion.prepareStatement( REQUETE_SQL ); ... resultat = preparedStatement.executeQuery(); ... } finally { if (resultat != null) try { resultat.close(); } catch (SQLException ignore) {} if (preparedStatement != null) try { preparedStatement.close(); } catch (SQLException ignore) {} if (connexion != null) try { connexion.close(); } catch (SQLException ignore) {} }

472/606

Dans ce cas, pourquoi vous avoir fait dvelopper une structure si peu adapte une utilisation en conditions relles ?

En ralit, le tableau n'est pas aussi sombre qu'il n'y parat. Certes, l'obtention d'une nouvelle connexion chaque requte est une catastrophe en termes de performances. Mais le bon point, c'est que notre code est correctement organis et dcoup ! Regardez de plus prs, nos DAO ne font pas directement appel la mthode JDBC DriverManager.getConnection() pour tablir une connexion, ils le font par l'intermdiaire de la mthode getConnection() de notre DAOFactory : Code : Java - DAOFactory.getConnection() /* Mthode charge de fournir une connexion la base de donnes */ /* package */ Connection getConnection() throws SQLException { return DriverManager.getConnection( url, username, password ); }

Ainsi notre solution est clairement mauvaise pour le moment, mais nous sentons bien qu'en modifiant la manire dont notre DAOFactory obtient une connexion, nous pouvons amliorer grandement la situation, et ce sans rien avoir changer dans nos DAO ! Bref, je ne vous ai pas fait coder n'importe quoi...

Principe Rutilisation des connexions


Afin d'allger la charge que le SGBD doit actuellement supporter chaque requte, nous allons utiliser une technique trs simple : nous allons prcharger un certains nombre de connexions la base de donnes, et les rutiliser. L'expression employe pour nommer cette pratique est le connection pooling , souvent trs sauvagement francis pool de connexions . Comment fonctionne ce mcanisme de rutilisation ?

Pour faire simple, le pool de connexions va pr-initialiser un nombre donn de connexions au SGBD lorsque l'application dmarre. Autrement dit, il va crer plusieurs objets Connection et les garder ouverts et bien au chaud. Ensuite, le pool de connexions va se charger de distribuer ses objets Connection aux mthodes de l'application qui en ont besoin. Concrtement, cela signifie que ces mthodes ne vont plus faire appel DriverManager.getConnection(), mais plutt quelque chose comme pool.getConnection(). Enfin, puisque l'objectif du systme est de partager un nombre pr-dfini de ressources, un appel la mthode Connection.close() ne devra bien entendu pas provoquer la fermeture relle d'une connexion ! En lieu et place, c'est tout simplement un renvoi dans le pool de l'objet Connection qui va avoir lieu. De cette manire, et seulement de cette manire, la boucle est boucle : l'objet Connection inutilis retourne la source, et est alors prt tre nouveau distribu.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

473/606

De manire image, vous pouvez voir le pool comme un systme de location de vhicules. Il dispose d'une flotte de vhicules dont il est le seul pouvoir autoriser la sortie, sortie qu'il autorise chaque demande d'un client. Cette sortie est par nature prvue pour tre de courte dure : si un ou plusieurs clients ne rend(ent) pas le vhicule rapidement, voire ne le rend(ent) pas du tout, alors petit petit la flotte de vhicules disponibles va se rduire comme peau de chagrin, et le pool n'aura bientt plus aucun vhicule disponible... Bref, la libration des ressources dont je vous ai parl avec insistance dans les prcdents chapitres prend ici tout son intrt : elle est la cl de vote du systme. Si une ressource inutilise ne retourne pas au pool, les performances de l'application vont invitablement se dgrader.

Remplacement du DriverManager par une DataSource


Maintenant que nous savons comment tout cela fonctionne en thorie, intressons-nous la pratique. Jusqu' prsent, nous avons utilis le DriverManager du package java.sql pour obtenir une connexion notre base de donnes. Or nous venons de dcouvrir qu'afin de mettre en place un pool de connexions, il ne faut plus passer par cet objet... Quel est le problme avec le DriverManager ?

Sur une application Java classique, avec un utilisateur unique et donc une seule connexion vers une base de donnes, il convient trs bien. Mais une application Java EE est multi-threads et multi-utilisateurs, et sous ces contraintes cet objet ne convient plus. Dans une application Java EE en conditions relles, plusieurs connexions parallles sont ouvertes avec la base de donnes et pour les raisons voques un peu plus tt, il n'est pas envisageable d'ouvrir des connexions la vole pour chaque objet ou mthode agissant sur la base. Nous avons besoin d'une gestion des ressources plus efficace, et notre choix va se porter sur l'objet DataSource du nouveau package javax.sql. Il s'agit en ralit d'une interface, qu'Oracle recommande d'utiliser en lieu de place du DriverManager, et ce peu importe le cas d'utilisation. Dans ce cas, pourquoi ne pas avoir directement appris manipuler une DataSource ?

Tout simplement parce qu'encore aujourd'hui, l'objet DriverManager est trs rpandu dans beaucoup de projets de faible ou moyenne envergure, et il est prfrable que vous sachiez comment il fonctionne. En outre, vous allez bientt dcouvrir que la manipulation d'une DataSource n'est pas si diffrente !

Choix d'une implmentation


Une DataSource n'est qu'une interface, il est donc ncessaire d'en crire une implmentation. Rassurez-vous, nous n'allons pas nous occuper de cette tche : il existe plusieurs bibliothques, libres et gratuites, qui ont t cres par des quipes de dveloppeurs expriments et valides par des annes d'utilisations. Sans tre exhaustif, voici une liste des solutions les plus couramment rencontres : Apache DBCP BoneCP c3p0 DBPool Le seul petit souci, c'est que toutes ces bibliothques ne disposent pas d'une communaut active, et toutes n'offrent pas les mmes performances. Je vous pargne la recherche d'informations sur chacune des solutions et leurs benchmarks respectifs, et vous annonce que nous allons utiliser BoneCP ! Quoi qu'il en soit, peu importe la solution que vous choisissez dans vos projets, le principe de base ne change pas et le processus de configuration que nous allons apprendre dans le paragraphe suivant reste sensiblement le mme.

Mise en place Ajout des jar au projet


Comme pour toute bibliothque, il va nous falloir placer l'archive jar de la solution dans notre environnement. De la mme

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

474/606

manire que nous l'avions fait pour le driver JDBC, nous allons dposer le jar de BoneCP, tlchargeable en cliquant ici, dans le rpertoire /lib de Tomcat. En outre, la page listant les conditions requises un bon fonctionnement de la solution nous signale qu'il faut galement inclure les bibliothques SLF4J et Google Guava notre projet, car la solution BoneCP en dpend. V ous devez donc tlcharger les deux archives en cliquant sur ce lien pour SLF4J et sur celui-ci pour Guava, et les placer sous le rpertoire /WEB-INF/lib aux cts de l'archive de BoneCP.

Prise en main de la bibliothque


Nous devons ensuite tudier la documentation de BoneCP pour comprendre comment tout cela fonctionne. Il existe plusieurs mthodes de configuration chacune propre un usage particulier. Ainsi, il est possible de paramtrer l'outil la main, via un DataSource, pour une utilisation avec le framework Spring ou encore pour une utilisation avec le duo de frameworks Spring et Hibernate. Quoi qu'il en soit peu importe le contexte, les proprits accessibles sont sensiblement les mmes et nous allons toujours retrouver : jdbcUrl , contenant l'URL de connexion JDBC ; username, contenant le nom d'utilisateur du compte utiliser sur la BDD ; password, contenant le mot de passe du compte utiliser sur la BDD ; partitionCount, contenant un entier de 1 N symbolisant le nombre de partitions du pool. C'est un paramtre spcifique BoneCP, qui n'existe pas dans des solutions comme c3p0 ou DBCP ; maxConnectionsPerPartition, contenant le nombre maximum de connexions pouvant tre cres par partition. Concrtement, cela signifie que si cette valeur vaut 5 et qu'il y a 3 partitions, alors il y aura en tout 15 connexions uniques disponibles et partages via le pool. noter que BoneCP ne va pas initialiser l'ensemble de ces connexions d'une traite, mais va commencer par en crer autant que spcifi dans la proprit minConnectionsPerPartition, puis il va graduellement augmenter le nombre de connexions disponibles au fur et mesure que la charge va monter ; minConnectionsPerPartition, contenant le nombre minimum de connexions par partitions. En ce qui nous concerne, nous allons dans le cadre de ce cours mettre en place un pool de connexions la main. Toujours en suivant la documentation de BoneCP, nous apprenons qu'il faut passer par l'objet BoneCPConfig pour initialiser les diffrentes proprits que nous venons de dcouvrir. Il suffit ensuite de crer un pool via l'objet BoneCP. V oici le code d'exemple : Code : Java - Exemple de mise en place d'un pool de connexions Class.forName( "com.mysql.jdbc.Driver" ); // chargement du driver JDBC (ici MySQL) BoneCPConfig config = new BoneCPConfig(); // cration d'un objet BoneCPConfig config.setJdbcUrl( url ); // dfinition de l'URL JDBC config.setUsername( nomUtilisateur ); // dfinition du nom d'utilisateur config.setPassword( motDePasse ); // dfinition du mot de passe config.setMinConnectionsPerPartition( 5 ); // dfinition du nombre min de connexions par partition config.setMaxConnectionsPerPartition( 10 ); // dfinition du nombre max de connexions par partition config.setPartitionCount( 2 ); // dfinition du nombre de partitions BoneCP connectionPool = new BoneCP( config ); partir de l'objet BoneCPConfig ... // cration du pool

V oil tout ce que nous avons besoin de savoir. Pour le reste, la documentation est plutt exhaustive.

Modification de la DAOFactory
www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

475/606

Maintenant que nous savons comment procder, nous devons modifier le code de notre DAOFactory pour qu'elle travaille non plus en se basant sur l'objet DriverManager, mais sur un pool de connexions. Je vous donne le code pour commencer, et vous explique le tout ensuite : Code : Java - com.sdzee.dao.DAOFactory package com.sdzee.dao; import import import import import import java.io.FileNotFoundException; java.io.IOException; java.io.InputStream ; java.sql.Connection; java.sql.SQLException; java.util.Properties;

import com.jolbox.bonecp.BoneCP; import com.jolbox.bonecp.BoneCPConfig; public class DAOFactory { private static final String FICHIER_PROPERTIES "/com/sdzee/dao/dao.properties"; private static final String PROPERTY_URL private static final String PROPERTY_DRIVER private static final String PROPERTY_NOM_UTILISATEUR "nomutilisateur"; private static final String PROPERTY_MOT_DE_PASSE "motdepasse"; /* package */ BoneCP = null; = = "url"; = "driver"; = =

connectionPool

/* package */ DAOFactory( BoneCP connectionPool ) { this.connectionPool = connectionPool; } /* * Mthode charge de rcuprer les informations de connexion la base de * donnes, charger le driver JDBC et retourner une instance de la Factory */ public static DAOFactory getInstance() throws DAOConfigurationException { Properties properties = new Properties(); String url; String driver; String nomUtilisateur; String motDePasse; BoneCP connectionPool = null; ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream fichierProperties = classLoader.getResourceAsStream( FICHIER_PROPERTIES ); if ( fichierProperties == null ) { throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable." ); } try { properties.load( fichierProperties ); url = properties.getProperty( PROPERTY_URL ); driver = properties.getProperty( PROPERTY_DRIVER ); nomUtilisateur = properties.getProperty( PROPERTY_NOM_UTILISATEUR ); motDePasse = properties.getProperty(

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


PROPERTY_MOT_DE_PASSE ); } catch ( FileNotFoundException e ) { throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable.", e ); } catch ( IOException e ) { throw new DAOConfigurationException( "Impossible de charger le fichier properties " + FICHIER_PROPERTIES, e ); } try { Class.forName( driver ); } catch ( ClassNotFoundException e ) { throw new DAOConfigurationException( "Le driver est introuvable dans le classpath.", e ); } try { /* * Cration d'une configuration de pool de connexions via l'objet * BoneCPConfig et les diffrents setters associs. */ BoneCPConfig config = new BoneCPConfig(); /* Mise en place de l'URL, du nom et du mot de passe */ config.setJdbcUrl( url ); config.setUsername( nomUtilisateur ); config.setPassword( motDePasse ); /* Paramtrage de la taille du pool */ config.setMinConnectionsPerPartition( 5 ); config.setMaxConnectionsPerPartition( 10 ); config.setPartitionCount( 2 ); /* Cration du pool partir de la configuration, via l'objet BoneCP */ connectionPool = new BoneCP( config ); } catch ( SQLException e ) { e.printStackTrace(); throw new DAOConfigurationException( "Erreur de configuration du pool de connexions.", e ); } /* * Enregistrement du pool cr dans une variable d'instance via un appel * au constructeur de DAOFactory */ DAOFactory instance = new DAOFactory( connectionPool ); return instance; } /* Mthode charge de fournir une connexion la base de donnes */ /* package */ Connection getConnection() throws SQLException { return connectionPool.getConnection(); } /* * Mthodes de rcupration de l'implmentation des diffrents DAO (un seul * pour le moment) */ public UtilisateurDao getUtilisateurDao() { return new UtilisateurDaoImpl( this ); } }

476/606

Comme vous pouvez le constater, le principe est trs lgrement diffrent. Auparavant lors d'un appel getInstance(), nous enregistrions les informations de connexion la base de donnes dans des variables d'instance et nous les rutilisions chaque appel getConnection(). Dornavant, lors d'un appel getInstance() nous procdons directement l'initialisation du pool de connexions, et nous enregistrons uniquement le pool ainsi obtenu dans une variable d'instance, qui est alors rutilise chaque appel getConnection().

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE


Ainsi, seuls quelques lments changent dans le code : le constructeur se base maintenant sur l'objet BoneCP, ici aux lignes 23 25 ; la lecture des informations depuis le fichier Properties ne change pas ; le chargement du driver ne change pas ; avant d'appeler le constructeur, nous procdons aux lignes 64 83 la cration du pool de connexions ; enfin, la mthode getConnection() se base maintenant sur le pool, et non plus dur le DriverManager !

477/606

V oil tout ce qu'il est ncessaire de modifier pour mettre en place un pool de connexions dans notre application ! Simple et rapide, n'est-ce pas ? V ous voil devant un autre exemple de l'intrt de bien organiser et dcouper le code d'une application. Dans cet exemple, la transparence est totale lors de l'utilisation des connexions, il nous suffit uniquement de modifier la mthode getConnection() de la classe DAOFactory ! Si nous n'avions pas mis en place cette Factory et avions procd directement l'ouverture d'une connexion depuis nos servlets ou objets mtiers, nous aurions d reprendre l'intgralit du code concern pour mettre en place notre pool...

noter galement l'importance dans le code existant d'avoir correctement ferm/libr les ressources utilises (et notamment les objets Connection) lors des requtes dans nos DAO, car si les connexions utilises n'taient pas systmatiquement renvoyes au pool, alors nous nous heurterions trs rapidement un problme d'puisement de stock, menant inluctablement au plantage du serveur !

Vrifications
Une fois toutes les modifications effectues, il ne vous reste plus qu' vrifier que votre code compile correctement et redmarrer Tomcat ! Effectuez alors quelques tests de routine sur la page http://localhost:8080/pro/inscription pour vous assurer du bon fonctionnement de votre application : essayez de vous inscrire en oubliant de confirmer votre mot de passe ; essayez de vous inscrire avec une adresse mail dj utilise ; enfin, inscrivez-vous avec des informations valides.

Configuration fine du pool


Ce dernier paragraphe va peut-tre vous dcevoir, mais il n'existe pas de rgles empiriques appliquer pour bien configurer un pool de connexions. C'est un processus qui est fortement li la fois au nombre moyen d'utilisateurs simultans sur le site, la complexit des requtes effectues sur la base, aux capacits matrielles du serveur, au SGBD utilis, la qualit de service souhaite, etc. En somme, la configuration fine d'un pool s'effectue au cas par cas. Cela passe par une srie de tests grandeur nature, durant lesquels un certain nombre de connexions et utilisations simultanes est simul en accord avec les spcifications du projet, afin de vrifier comment ragit la base de donnes, comment ragit le pool et comment sont impactes les performances globales de l'application en priode de charge faible, moyenne et forte, le tout pour une configuration donne. Si les tests sont convaincants, alors la configuration est valide. Sinon, elle est modifie - changement du nombre de connexions minimum et maximum, et du nombre de partitions - et une autre srie de tests est lance. Bref, aller plus loin ici n'a aucun intrt pdagogique pour nous : nous sommes ici pour apprendre dvelopper une application, pas pour devenir des experts en dploiement !

En rsum
La connexion une base de donnes est une tape coteuse en termes de temps et de performances. Il est ncessaire d'initialiser un nombre prdfini de connexions, et de le partager/distribuer/rutiliser pour chaque requte entrante : c'est le principe du pool de connexions . Lorsqu'un pool de connexions est en place, un appel la mthode connexion.close() ne ferme pas littralement une connexion, mais la renvoie simplement au pool. La mthode getConnection() tant centralise et dfinie dans notre Factory, il nous est trs ais de modifier son comportement.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

478/606

Un pool de connexions se base sur le principe d'une DataSource, objet qu'il est vivement recommand d'utiliser en lieu et place du DriverManager. BoneCP est une solution de pooling trs efficace, aisment configurable et intgrable n'importe quelle application Java EE. Nous voil enfin aptes crer une application de A Z ! Nous n'avons pas encore pris connaissance des autres moyens existant pour crer et grer plus simplement une application, mais avec notre bagage nous sommes d'ores et dj capables de produire une application complte et fonctionnelle.

www.siteduzero.com

Partie 5 : Les bases de donnes avec Java EE

479/606

Partie 6 : Aller plus loin avec JPA et JSF


Dans cette ultime partie, nous allons dcouvrir deux concepts trs utiliss dans les projets en entreprise : la persistance de donnes, intervenant comme son nom l'indique au niveau de la gestion des donnes. EJB et JPA sont au programme ! les frameworks MVC, qui imposent une organisation du code en couches bien dfinie. JSF 2 est celui que nous allons tudier.

Les annotations
Dans ce chapitre, nous allons dcouvrir les annotations, un systme introduit avec Java EE 5 qui va nous permettre de nous dbarrasser de notre fichier web.xml !

Prsentation
Pour commencer, parce qu'il est toujours utile de s'intresser aux raisons d'tre d'une technologie, nous allons tudier le sujet de manire globale et nous attarder quelques instants sur la thorie. Si vous connaissez dj cette fonctionnalit, qui comme nous allons le voir existe depuis quelques temps dj sur la plate-forme Java, vous pouvez survoler cette introduction et passer directement au paragraphe qui traite spcifiquement des annotations sous Java EE. Une annotation est tout simplement une description d'un lment. Elle peut ainsi s'appliquer un package, une classe, une interface, un constructeur, une mthode, un champ, un argument de mthode, une variable locale ou encore une autre annotation. Quel est l'intrt de dcrire ces diffrents lments ?

Pour rpondre entirement cette question, nous allons aborder diffrents aspects.

crire des mta-donnes


V ous avez peut-tre dj entendu parler de mta-donnes ou de mta-programmation. Il s'agit d'un concept trs simple : une mta-donne dsigne une donne qui donne une information sur une autre donne. V oil pour la dfinition littrale, intressonsnous maintenant l'intrt de ces mta-donnes dans le cadre de notre projet ! Premirement, parlons Java. En ralit Java a toujours propos une forme de mta-programmation, que vous connaissez tous mais que vous n'avez probablement jamais appele ainsi : il s'agit de la Javadoc ! Eh oui, la Javadoc n'est plus ni moins qu'un outil permettant de dcrire certaines portions de votre code... Dans ce cas, les mta-donnes sont utilises par le dveloppeur, qui va tout simplement lire les informations contenues dans la documentation. Deuximement, abordons le cas de Java EE. L encore, mme constat : la plate-forme a toujours propos une forme de mtaprogrammation par le biais du fameux fichier web.xml, dans lequel nous avons saisi les descriptions de certains composants de notre application. Dans ce cas, les mta-donnes sont utilises par le conteneur, qui analyse le contenu du fichier au dmarrage du serveur afin d'identifier quels composants web agissent sur quelles requtes. Ainsi, voici la dfinition que nous pouvons retenir : les mta-donnes sont de simples informations accompagnant le code d'une application, et qui peuvent tre utilises des fins diverses.

Pallier certaines carences


Il y a quelques annes, Java ne permettait pas encore de faire de mta-programmation de manire simple. La seule solution qui s'en approchait tait la Javadoc et son systme de tags, et permettait uniquement de rdiger de la documentation destine au dveloppeur. l'poque, certaines solutions avaient alors vu le jour afin d'en dtourner l'usage, notamment XDoclet qui permettait de dfinir des tags Javadoc personnaliss et de gnrer du code partir des mta-donnes saisies dans ce semblant de documentation. Reconnaissant les manques de la plate-forme ce niveau et surtout le besoin d'un systme plus robuste, plus flexible et surtout homogne, l'quipe en charge de l'volution de Java a introduit le concept des annotations avec lavnement du JDK 1.5, en

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

480/606

2004. Par ailleurs, sachez que cette version a probablement t la plus riche de toutes en termes d'apport de nouveauts : les annotations comme nous venons de le voir, mais galement la programmation gnrique, la syntaxe for adapte au parcours de collections, les imports statiques, l'autoboxing, la notation varargs... Bref, les dveloppeurs n'ont pas chm ! C'est par ailleurs aprs cette mise jour majeure du JDK que J2EE est devenu Java EE 5 en 2006, et c'est donc uniquement partir de cette version que les annotations ont t rendues disponibles pour le dveloppement d'applications web. Dans toutes les versions antrieures de la plate-forme (c'est--dire l'ensemble des versions estampilles J2EE), cette fonctionnalit n'existait pas. Depuis Java EE 6, lanc fin 2009, le support des annotations est total et leur gestion fait partie intgrante du compilateur Java.

Simplifier le dveloppement
La raison d'tre des annotations est sans quivoque : elles permettent de simplifier considrablement le processus de dveloppement et de maintenance d'une application. Pourquoi ? Principalement parce qu'une annotation est crite directement dans le code, au sein d'une classe ou d'une mthode. Ainsi, l'information associe un lment du code est accessible et visible directement, il n'est pas ncessaire de se rfrer des donnes crites dans des fichiers externes. De mme, lors de la phase de dveloppement il n'est pas ncessaire de crer et maintenir ces fichiers externes.

Principe
Maintenant que nous savons de quoi il retourne, dcouvrons comment sont construites les annotations.

Syntaxe sans paramtres


Une annotation sous sa forme la plus simple est uniquement constitue d'un mot-cl prcd du signe @. Nous allons prendre pour exemple l'annotation @Override, que vous avez dj pu observer dans le code de notre classe com.sdzee.dao.UtilisateurDaoImpl : Code : Java - Exemple d'annotation Java public class UtilisateurDaoImpl implements UtilisateurDao { /* Implmentation de la mthode trouver() dfinie dans l'interface UtilisateurDao */ @Override public Utilisateur trouver( String email ) throws DAOException { ... } } ...

Il s'agit l d'une annotation Java qui sert dcrire une mthode, en l'occurrence la mthode trouver(). Elle est constitue du caractre @ suivi du mot-cl Override, et elle est place juste avant le dbut de la mthode. Pour la petite histoire, sachez que cette annotation est doublement utile : elle permet de prciser au compilateur qu'une mthode redfinit une mthode d'une interface. En cas d'erreur - c'est--dire si le nom de la mthode ne correspond aucune mthode d'aucune interface - alors le compilateur doit prvenir le dveloppeur et ventuellement faire chouer la compilation. elle rend le code plus lisible, en diffrenciant les mthodes redfinies des autres. Ceci est renforc par les comportements intuitifs introduits dans la plupart des IDE, qui marquent visuellement de telles mthodes. Je vous montre ici cette annotation en particulier pour illustrer la syntaxe utilise, mais ne vous y trompez pas il en existe bien d'autres. Il est d'ailleurs possible de crer ses propres annotations si besoin, mais ceci a uniquement trait au Java et sort par consquent du cadre de ce cours. Nous ce qui nous intresse, c'est le Java EE !

Syntaxe avec paramtres


Il existe une seconde forme d'annotation, qui attend en argument des paramtres. Elle se construit de la manire suivante :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


Code : Java - Exemple d'annotation avec paramtres @WebServlet( name="TestServlet", urlPatterns = {"/test", "/ok"} )

481/606

Explications : une annotation peut attendre un ou plusieurs paramtres, spars par une virgule et placs entre parenthses juste aprs l'annotation. Dans cet exemple, les paramtres sont nomms name et urlPatterns . un paramtre peut attendre une ou plusieurs valeurs : si une seule valeur est attendue, celle-ci est place entre guillemets et lie au paramtre par le symbole =. Ici, le paramtre name attend une unique valeur dfinie "TestServlet". si plusieurs valeurs sont attendues, celles-ci sont places entre guillemets, spares par une virgule et l'ensemble ainsi form est plac entre accolades, puis li au paramtre par le symbole = (en somme, la syntaxe d'initialisation d'un tableau). Ici, le paramtre urlPatterns reoit deux valeurs dfinies respectivement "/test" et "/ok".

V oil tout ce qu'il est ncessaire de retenir concernant la syntaxe des annotations.

Avec l'API Servlet 3.0


Nous sommes enfin prts pour aborder quelques annotations spcifiques Java EE 6, introduites par l'API Servlet en version 3.0. Celles-ci font toutes partie du package javax.servlet.annotation.

WebServlet
La premire laquelle nous allons nous intresser est celle qui permet de dclarer une servlet : @WebServlet. Comme vous pouvez le voir dans sa documentation, elle peut accepter tous les paramtres qu'il est possible de dfinir dans une section <servlet> ou <servlet-mapping> du fichier web.xml. Prenons pour exemple notre servlet Inscription. Pour rappel, sa dclaration dans le fichier web.xml tait la suivante : Code : XML - /WEB-INF/web.xml <servlet> <servlet-name>Inscription</servlet-name> <servlet-class>com.sdzee.servlets.Inscription</servlet-class> </servlet> <servlet-mapping> <servlet-name>Inscription</servlet-name> <url-pattern>/inscription</url-pattern> </servlet-mapping>

L'annotation qui remplace littralement cette description est : Code : Java - com.sdzee.servlets.Inscription ... @WebServlet( name="Inscription", urlPatterns = "/inscription" ) public class Inscription extends HttpServlet { ...

Remarquez bien les points suivants : il faut placer l'annotation juste avant la dclaration de la classe dans le code de votre servlet ; il n'est pas ncessaire de prciser le contenu de <servlet-class> dans l'annotation : puisque celle-ci se trouve directement dans le code de la servlet, le compilateur sait dj quelle classe elle s'applique. Ajoutez l'annotation votre servlet, supprimez la dclaration de votre web.xml et redmarrez Tomcat. V ous pourrez alors vrifier

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


par vous-mmes que votre application fonctionne exactement comme avant, en vous rendant sur la page http://localhost:8080/pro/inscription.

482/606

En outre, certains d'entre vous auront peut-tre galement remarqu que la proprit name de la servlet n'est plus utile. Dans le fichier web.xml , le champ <servlet-name> servait tablir un lien entre les sections <servlet> et <servletmapping>, mais maintenant que nous prcisons directement l'url-pattern dans l'annotation de la servlet, son nom ne nous sert plus rien. Par consquent, il est possible d'crire l'annotation sous sa forme la plus simple : Code : Java - com.sdzee.servlets.Inscription ... @WebServlet( "/inscription" ) public class Inscription extends HttpServlet { ...

Ainsi, seul l'url-pattern sur lequel est mappe notre servlet est ncessaire son bon fonctionnement. L encore, vous pouvez confirmer ce comportement en modifiant l'annotation dans votre servlet Inscription et en vrifiant que l'application fonctionne toujours. Par ailleurs, puisque les meta-donnes sont maintenant prsentes directement dans le code de votre servlet, il n'est plus ncessaire de redmarrer Tomcat chaque modification du nom de la servlet ou encore de son url-pattern : les modifications sont prises en compte presque instantanment !

WebFilter
Trs similaire la prcdente, il existe une annotation qui permet de dclarer un filtre : @WebFilter. Comme pour sa cousine ddie la servlet, vous constaterez dans sa documentation qu'elle accepte toutes les proprits dfinissables depuis une section <filter> ou <filter-mapping> du fichier web.xml. Prenons pour exemple le filtre RestrictionFilter que nous avions mis en place pour tester la restriction d'accs sur un groupe de pages. Pour rappel, sa dclaration dans notre fichier web.xml tait la suivante : Code : XML - /WEB-INF/web.xml <filter> <filter-name>RestrictionFilter</filter-name> <filter-class>com.sdzee.filters.RestrictionFilter</filter-class> </filter> <filter-mapping> <filter-name>RestrictionFilter</filter-name> <url-pattern>/restreint/*</url-pattern> </filter-mapping>

De la mme manire que pour notre servlet Inscription, l'annotation qui remplace littralement cette description peut s'crire : Code : Java ... @WebFilter( urlPatterns = "/restreint/*" ) public class RestrictionFilter implements Filter { ...

Dans notre application, nous avions dsactiv le filtre pour ne pas tre embts dans les autres exemples du cours. Si vous le souhaitez, vous pouvez remettre en place le filtre de restriction dans votre projet en ajoutant simplement cette annotation la classe RestrictionFilter, et ainsi vrifier que le seul ajout de cette annotation implique bel et bien une activation du filtre, et ce mme sans redmarrage de Tomcat ! Pour le dsactiver, il vous suffira ensuite de supprimer ou de commenter l'annotation.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

483/606

WebInitParam
Il existe galement une annotation qui ne peut tre utilise qu'au sein d'une annotation @WebServlet ou @WebFilter : @WebInitParam. Comme son nom l'indique, elle est destine remplacer la section <init-param> qu'il est possible d'inclure aux dclarations d'une servlet ou d'un filtre dans le fichier web.xml . Sa documentation nous confirme que seuls deux attributs sont requis : un attribut name et un attribut value, remplaant respectivement <param-name> et <param-value>. Prenons pour exemple notre servlet Download. Pour rappel, sa dclaration dans le fichier web.xml tait la suivante : Code : XML - /WEB-INF/web.xml <servlet> <servlet-name>Download</servlet-name> <servlet-class>com.sdzee.servlets.Download</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Download</servlet-name> <url-pattern>/fichiers/*</url-pattern> </servlet-mapping>

L'annotation qui remplace cette description devient : Code : Java - com.sdzee.servlets.Download ... @WebServlet( urlPatterns = "/fichiers/*", initParams = @WebInitParam( name = "chemin", value = "/fichiers/" ) ) public class Download extends HttpServlet { ...

Remarquez bien l'utilisation de l'annotation fille @WebInitParam au sein de l'annotation mre @WebServlet. L encore, si vous souhaitez vrifier le bon fonctionnement de l'annotation il vous suffit de l'ajouter votre servlet Download, de supprimer la dclaration de votre web.xml , puis de redmarrer Tomcat et de tenter de tlcharger un des fichiers prsents sur votre disque en vous rendant sur la page http://localhost:8080/pro/fichiers/... (o les ... correspondent au nom ou chemin du fichier) !

WebListener
Reposons-nous un instant avec l'annotation ddie aux Listener : @WebListener. Comme vous pouvez le constater en parcourant sa documentation, elle ne requiert aucun paramtre. D'ailleurs, vous devez vous souvenir de la courte dclaration de notre Listener InitialisationDaoFactory : Code : XML - /WEB-INF/web.xml <listener> <listenerclass>com.sdzee.config.InitialisationDaoFactory</listener-class> </listener>

L'annotation qui remplace cette description est tout bonnement :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


Code : Java - com.sdzee.config.InitialisationDaoFactory ... @WebListener public class InitialisationDaoFactory implements ServletContextListener { ...

484/606

Comme annonc, elle n'attend aucun paramtre et suffit dclarer une classe en tant que Listener au sein d'une application web. Pratique, n'est-ce pas ?

MultipartConfig
Pour terminer, nous allons nous intresser l'annotation ddie au traitement des requtes de type Multipart : @MultipartConfig. Sa documentation nous confirme que, tout comme nous l'avions fait dans la section <multipartconfig> de notre web.xml , nous pouvons prciser quatre paramtres : location, fileSizeThreshold, maxFileSize et maxRequestSize. Je ne reviens pas sur leur signification, nous les avons dj dcouverts dans le chapitre expliquant l'envoi de fichier. Pour rappel, voici comment nous avions dclar notre servlet d'upload : Code : XML - /WEB-INF/web.xml <servlet> <servlet-name>Upload</servlet-name> <servlet-class>com.sdzee.servlets.Upload</servlet-class> <init-param> <param-name>chemin</param-name> <param-value>/fichiers/</param-value> </init-param> <multipart-config> <location>c:/fichiers</location> <max-file-size>10485760</max-file-size> <!-- 10 Mo --> <max-request-size>52428800</max-request-size> <!-- 5 x 10Mo --> <file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo --> </multipart-config> </servlet> <servlet-mapping> <servlet-name>Upload</servlet-name> <url-pattern>/upload</url-pattern> </servlet-mapping>

C'est un cas d'tude intressant, puisque la description contient la fois un paramtre d'initialisation et une section Multipart. L'annotation correspondante est : Code : Java - com.sdzee.servlets.Upload ... @WebServlet( urlPatterns = "/upload", initParams = @WebInitParam( name = "chemin", value = "/fichiers/" ) ) @MultipartConfig( location = "c:/fichiers", maxFileSize = 10 * 1024 * 1024, maxRequestSize = 5 * 10 * 1024 * 1024, fileSizeThreshold = 1024 * 1024 ) public class Upload extends HttpServlet { ...

Eh oui, nous avons besoin de deux annotations diffrentes appliques sur la mme classe pour remplacer intgralement la prcdente dclaration de la servlet ! En effet, alors que @WebInitParam est incluse dans le corps de l'annotation

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


@WebServlet, l'annotation @MultipartConfig est quant elle indpendante et doit donc tre spcifie part. Ct syntaxe, vous pouvez relever deux points intressants :

485/606

lorsqu'un paramtre attendu est de type numrique, il ne faut pas l'entourer de guillemets comme nous le faisions jusqu'alors pour tous nos paramtres de type String ; lorsqu'un paramtre attendu est de type numrique, il est possible d'utiliser des oprateurs mathmatiques simples dans sa valeur. Ici, j'ai utilis l'oprateur de multiplication * afin de rendre visible au premier coup dil le fait que notre servlet limite les tailles respectivement 10Mo, 5x10Mo et 1Mo. Pour tester, supprimez la dclaration du fichier web.xml et ajoutez ces deux annotations votre servlet Upload. Redmarrez alors Tomcat, puis rendez-vous sur http://localhost:8080/pro/upload et tentez d'envoyer un fichier trop volumineux, un fichier sans description et enfin un fichier correspondant aux critres de validation.

Et le web.xml dans tout a ?


Avec ces quelques simples annotations, vous allez pouvoir mettre au rgime votre fichier web.xml de manire drastique ! Toutefois, si vous faites vous mme l'exprience et tentez de remplacer chacune des dclarations par leurs annotations quivalentes, tt ou tard vous vous rendrez compte que tout ne va pas disparatre. En ralit, le fichier web.xml est encore utile pour plusieurs raisons, que nous allons rapidement dcouvrir.

1. Dclarer la version de l'API servlet


La premire utilisation, vidente pour vous je l'espre, est la dclaration de la version de l'API Servlet requise par votre application. Autrement dit, vous avez toujours besoin de la balise <web-app> : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> ...

J'en profite pour vous rappeler que les annotations ne sont disponibles qu' partir de Java EE 6, et qu'il vous faut donc imprativement dclarer l'API Servlet 3.0 pour pouvoir les utiliser dans votre code.

2. Spcifier un ordre de filtrage


Souvenez-vous du chapitre sur les filtres, je vous y avais annonc que l'ordre de dclaration des filtres dans le fichier web.xml tait important car il dterminait l'ordre dans lequel vos filtres allaient tre appliqus aux requtes entrantes. Eh bien sachez que ce niveau de configuration n'est pas atteignable avec de simples annotations. En effet, en utilisant des annotations il est impossible de prciser dans quel ordre vous souhaitez appliquer diffrents filtres mapps sur une mme requte. Ainsi, si vous souhaitez organiser vos filtres et dfinir un ordre d'excution prcis, vous devrez continuer les dclarer dans votre fichier web.xml . Toutefois, si vous souhaitez limiter l'encombrement de votre web.xml vous pourrez toujours utiliser des annotations pour remplacer les sections <filter>, et vous contenter d'y crire les sections <filter-mapping>. C'est tout ce dont le conteneur a besoin pour dfinir l'ordre d'excution des filtres ! Notez cependant que ce dcoupage, savoir les annotations d'un ct pour remplacer les sections <filter>, et le fichier web.xml de l'autre pour crire les sections <filter-mapping>, ne fonctionne avec Tomcat que depuis la version 7.0.28 ! Auparavant, le bug 53354 empchait le bon fonctionnement. Autrement dit, si vous utilisez une version antrieure de Tomcat et que vous souhaitez donner un ordre vos filtres, alors vous devrez les dclarer intgralement

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


dans votre web.xml , et ne pourrez pas utiliser les annotations @WebFilter.

486/606

3. Excuter un filtre externe


Toujours propos des filtres, il existe un cas particulier dans lequel vous ne pourrez pas utiliser l'annotation @WebFilter : lorsque le filtre que vous appliquez ne fait pas partie de votre application ! Sans accs au code source, vous n'avez pas vraiment le choix... Par exemple, dans notre projet nous utilisons le filtre natif de Tomcat nomm Set Character Encoding , afin de complter la gestion de l'UTF-8. Eh bien pour ce cas prcis, nous n'avons pas d'autre choix que de dclarer le filtre la main dans le web.xml de notre application.

4. Surcharger les valeurs dfinies dans une annotation


Pour conclure, sachez que les valeurs prcises dans toutes vos annotations sont en ralit considres par le conteneur comme des valeurs par dfaut. En d'autres termes, si vous dclarez la fois une annotation et une section dans le fichier web.xml , alors les donnes de la section seront prises en compte quoi qu'il arrive. Concrtement, cela signifie que par exemple si vous mappez en mme temps une servlet sur une URL depuis une section dans le web.xml , et sur une autre URL depuis une annotation, alors la servlet sera en fin de compte mappe sur les deux URL. Par ailleurs, dans le cas de paramtres valeur unique comme les @WebInitParam, en cas de double dfinition c'est la valeur contenue dans le web.xml qui sera prise en compte, et celle contenue dans l'annotation sera ignore.

En rsum
Une annotation est une mta-donne, une indication lie l'lement qu'elle cible. Les fichiers de configuration externes sont pnibles utiliser et difficilement maintenables. Il existe des annotations propres Java EE, apparues avec la version 6 de la plate-forme : @WebServlet permet de dclarer une servlet ; @WebFilter permet de dclarer un filtre ; @WebInitParam permet de prciser un paramtre d'initialisation ; @WebListener permet de dclarer un listener ; @MultipartConfig permet d'activer la gestion des requtes de type multipart depuis une servlet. Le fichier web.xml devient presque inutile ! La lisibilit et la comprhension d'une application est facilite, les informations de configuration tant maintenant directement prsentes au sein du code !

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

487/606

La persistance des donnes avec JPA


Incontournables pour beaucoup, dcries par certains, les solutions de persistance ont depuis quelques annes le vent en poupe, dans les applications Java SE comme dans les applications Java EE. Nous allons dans ce chapitre nous frayer un chemin dans cette jungle de nouveauts, et dcouvrir comment appliquer correctement une telle solution notre projet.

Gnralits
Quest-ce quon appelle la persistance de donnes ?

Si vous ne connaissez pas ce mot, ne vous inquitez surtout pas : au sens gnral, il sagit simplement du terme utilis pour dcrire le fait de stocker des donnes dune application de manire persistante ! Autrement dit, de les sauvegarder afin quelles ne disparaissent pas lorsque le programme se termine. Rien de bien compliqu, en somme. En Java, lorsquon parle dune solution de persistance des donnes , on voque couramment un systme permettant la sauvegarde des donnes contenues dans des objets . En ralit, vous connaissez donc dj tous un moyen de persistance : le stockage dans une base de donnes relationnelle via JDBC !

Dans ce cas, pourquoi ne pas nous contenter de notre systme actuel ?

En effet, notre tandem JDBC & DAO fonctionne bien, et persiste correctement nos donnes dans notre base de donnes MySQL. Cest pourquoi dans labsolu, ce que vous avez appris jusque l est suffisant pour dvelopper une application web Java EE de petite moyenne envergure. En outre, cest une solution lgre, portable et pratique puisqu'elle ne ncessite qu'un simple conteneur de servlets pour fonctionner ! Toutefois, il faut bien vous rendre compte dun aspect trs important. Dans notre exemple - une trs petite application - nous ne nous sommes pas inquits de devoir rcrire un DAO pour chaque table manipule. Pourtant, cest une belle perte de temps : dans chacune de nos classes, nous utilisons un code source trs similaire pour rcuprer les donnes des tables et les transformer en objets. Concrtement, mis part le SQL, l'affectation des paramtres sur l'objet PreparedStatement et la rcupration des rsultats et leur affectation sur le bean, rien ne change : la structure gnrale du code reste toujours la mme ! Maintenant imaginez-vous devoir travailler sur un projet impliquant plusieurs centaines de tables V ous tes bons pour crire autant de DAO ! Parce que mme si leur structure globale est identique, chacun deux contient des requtes et attributs spcifiques. Et comme vous le savez tous, non seulement le dveloppeur est fainant mais par-dessus tout, la duplication de code dans un projet, cest le mal incarn. Pour allger cette tche, vous pourriez bien videmment commencer par rajouter de labstraction et de la gnricit dans votre couche daccs aux donnes (souvent raccourcie DAL, pour Data Access Layer) et ses interfaces, afin dobtenir une interface unique pour tous vos DAO, et ne plus devoir crire que les implmentations spcifiques chaque table. Pour allger davantage la tche, vous pourriez ensuite raliser un gnrateur de DAO, un petit programme qui se chargerait dcrire le code de base des implmentations pour chacune des tables votre place, et vous nauriez alors plus qu reprendre chaque mthode incomplte. Avec un tel systme en place, il deviendrait alors envisageable dattaquer le dveloppement dune application de grande chelle plus sereinement. Seulement, il resterait encore des choses trs pnibles raliser, et surtout trs chronophages : il faudrait encore sassurer que la correspondance entre les tables et les objets est correctement ralise, et ce pour chaque action effectue : lecture de donnes, mises jour, insertions, suppressions chaque fois il faudrait crire une requte SQL spcifique, chaque fois il faudrait rcuprer son rsultat et ventuellement en extraire les donnes pour crer ou mettre jour les attributs dun bean, chaque fois V ous voyez o je veux en venir ? V ous devez bien sentir que si un systme global grait pour vous ces actions rcurrentes, votre travail serait bien plus agrable, nest-ce pas ? Eh bien voil pourquoi nous sommes en droit de ne pas nous contenter dutiliser JDBC & DAO, et voil pourquoi je vous prsente dans ce chapitre la solution fournie par Java EE : JPA.

Quest-ce que JPA ?

Littralement Java Persistence API , il sagit dun standard faisant partie intgrante de la plate-forme Java EE, une

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

488/606

spcification qui dfinit un ensemble de rgles permettant la gestion de la correspondance entre des objets Java et une base de donnes, ou autrement formul la gestion de la persistance. Ce mcanisme qui gre la correspondance entre des objets dune application et les tables dune base de donnes se nomme ORM, pour Object-Relational Mapping . Ainsi dans le sens le plus simpliste du terme, ce que nous avons ralis dans les chapitres prcdents travers nos DAO nest rien dautre quun ORM manuel !

Et Hibernate, TopLink, EclipseLink, OpenJPA ?

Peut-tre avez-vous dj entendu parler de ces solutions, les bien nomms frameworks ORM . Comprenez-bien que lorsquon parle de JPA, il sagit uniquement dune API, cest--dire une description d'un comportement suivre, en l'occurrence pour respecter un standard en place. Cest le mme principe que la fameuse DataSource que nous avions dcouverte dans le dernier chapitre de la partie prcdente : linterface nous expliquait comment faire, mais pour mettre en place concrtement un pool de connexions, il nous a fallu utiliser une implmentation (en loccurrence BoneCP). Eh bien l cest exactement pareil : les interfaces de JPA dcrivent comment respecter le standard, mais nous devons utiliser une implmentation pour en tirer parti ! V oil donc ce que sont Hibernate, EclipseLink & consorts : des implmentations du standard JPA. En dautres termes JPA constitue la thorie, et ces frameworks en sont la pratique. ce propos comme toujours, il y a des diffrences entre la thorie et la pratique. En effet, la plupart de ces solutions sortent du cadre dfini par JPA, et proposent des fonctionnalits annexes qui leur sont propres. Ainsi, le dveloppeur doit tre conscient de ce quil souhaite raliser : sil utilise des fonctionnalits spcifiques un framework en particulier, cest--dire une des fonctionnalits qui ne sont pas dcrites dans le standard JPA, alors il sexpose au risque ne de plus pouvoir faire machine arrire (choisir une autre solution de persistance) sans devoir modifier le code de son application. V oil donc lavantage de disposer dun standard bien dfini : partir du moment o lon fait du JPA, peu importe limplmentation utilise, le code applicatif sera identique, et donc portable. Pour la petite histoire, chronologiquement parlant certains de ces frameworks sont apparus avant JPA, cest le cas notamment de Hibernate et TopLink. Le standard a en ralit t cr lpoque dans le but dharmoniser les solutions existantes, afin de coller la devise du Java : write once, run everywhere . Ainsi, vous pouvez voir ce standard comme le dnominateur commun entre toutes les solutions reconnues existantes.

Principe
Maintenant que nous savons de quoi il retourne, dcouvrons comment tout cela sorganise. Je vous prviens ds maintenant, dans ce chapitre ainsi que dans les chapitres venir, je ne vais pas dtailler mes explications aussi finement que dans les parties prcdentes. La principale raison tant que pour travailler sur de tels concepts avancs, il est ncessaire que vous soyez curieux et un minimum autonomes. Des pans entiers de Java EE vont entrer en jeu, et vous allez devoir lire les documentations et liens que je vous fournis au fur et mesure de votre dcouverte si vous souhaitez assimiler correctement toutes les informations que je vous transmets. En outre, une autre excellente raison qui me pousse faire ce choix est que vous devez petit petit vous librer du format "tutoriel", et prendre de l'aise avec l'utilisation de ressources en tous genres (documentations, forums, extraits de code, etc.), car plus vous irez loin dans une technologie, moins vous trouverez de personnes pour vous renseigner, et plus il vous faudra compter sur vos capacits chercher les rponses par vous-mmes !

Des EJB dans un conteneur


Le concept mre qui se cache derrire JPA, cest le fameux EJB ou Enterprise JavaBean . V oil encore un terme dont vous avez dj d entendre parler maintes reprises, et pas forcment en bien Si je vous dis a, cest parce que cette technologie a connu une volution plutt chaotique. leur apparition en 1998, les objets EJB ont suscit lenthousiasme des dveloppeurs, enthousiasme qui a rapidement laiss place de vives critiques de tous bords : Trop compliqu ! Trop lourd ! Trop de configuration ! Trop de fichiers XML ! , etc. Il aura fallu attendre jusquen 2006 pour que la troisime version des EJB gagne finalement ses gallons et conquire les dveloppeurs, grce un fonctionnement ultra-simplifi. Bien videmment, nous nallons pas nous intresser aux mandres qui ont conduit ce que sont les EJB aujourdhui. Tout ce dont nous avons besoin, cest de comprendre comment ils fonctionnent dans leur version actuelle.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

489/606

Notez par ailleurs que la toile regorge dinformations dpasses sur ce sujet, et quil est parfois difficile de trouver des informations jour ailleurs que dans les documentations et cours officiels, bien souvent disponibles en anglais uniquement. Gardez bien en tte que les EJB aujourd'hui, ce sont les EJB 3, et que les EJB 1 et 2 sont de l'histoire ancienne.

Principe gnral
Les EJB sont des objets prsentant une caractristique bien particulire : ils sont grs par le conteneur. Attention, quand on parle ici de conteneur il nest plus question du simple conteneur de servlets que nous avons utilis jusqu prsent ! Non, il sagit bien ici de conteneur EJB , un lment dont le travail est de grer entirement le cycle de vie des EJB quil contient. Quest-ce que cela signifie concrtement ?

Eh bien tout simplement quune grande partie de ce travail pnible, auparavant ralis par le dveloppeur, est dornavant dlgue au conteneur, laissant ainsi au bon fainant qu'il est le loisir de se concentrer sur le code mtier de son application. Ne vous inquitez pas si cest encore flou dans votre esprit, vous comprendrez lorsque nous passerons la pratique. Par dfaut, tout serveur dapplications Java EE au sens strict du terme contient un conteneur EJB. Pour ce qui est des serveurs lgers comme Tomcat en revanche, ce nest pas le cas ! Ainsi, vous ne pouvez pas manipuler dEJB depuis Tomcat sans y ajouter un tel conteneur.

JPA, ou les EJB Entity


Il existe deux types dEJB : les EJB Entity, et les EJB Session. Celui qui sert de pilier JPA est le premier, et le plus simple apprhender : lEJB Entity. Cest lui qui dfinit quelles donnes doivent tre sauvegardes, et cest via lui quest effectue la correspondance entre un objet et une table dune base de donnes. En apparence, cest un objet qui ressemble extrmement un simple Javabean, dans lequel on ajoute simplement quelques annotations. Souvenez-vous, je vous avais expliqu dans le chapitre prcdent que celles-ci ntaient rien dautre que des mtadonnes Eh bien en loccurrence, ces informations sont ici utilises pour informer le conteneur dEJB de la manire dont lobjet devra tre gr.

Un gestionnaire dentits
Dfinir quelles donnes doivent tre sauvegardes en construisant des entits est une premire tape, mais elle ne servirait rien sil nexistait pas derrire un systme dfinissant comment ces donnes doivent tre sauvegardes ! Les mthodes permettant dtablir une connexion avec la base de donnes et de grer la persistance se trouvent dans un objet particulier nomm EntityManager, ou gestionnaire d'entits. Il sagit l encore dun objet dont le cycle de vie est gr par le conteneur. Toutefois, qui dit connexion une base de donnes dit configuration manuelle, voil pourquoi cet objet se base sur des informations que le dveloppeur doit saisir dans un fichier de configuration externe, que nous allons dcouvrir prochainement et qui nest rien dautre quun simple fichier XML. Pour rsumer, le fonctionnement de JPA est bas sur deux briques principales : des EJB Entity : ce sont des objets ressemblant trs fortement des JavaBeans, dans lesquels des annotations dfinissent des correspondances entre les objets et leurs attributs d'un ct, et les tables relationnelles de la base de donnes et leurs champs de l'autre (on parle alors de mapping relationnel/objet) ; un EntityManager : c'est une classe qui est charge de mettre en musique les correspondances dfinies dans les entits, et qui ralise donc toutes les oprations CRUD (Create, Read, Update, Delete) sur la base de donnes.

Mise en place Le serveur dapplications GlassFish


www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

490/606

Raisons de ce choix
Comme je vous lai annonc un peu plus tt, Tomcat ne gre pas les EJB de manire native. Et comme vous le savez maintenant, sans EJB pas de JPA. Nous pourrions certes ajouter les Jar des diffrentes solutions dont nous allons avoir besoin pour continuer travailler sous Tomcat, mais ce nest pas ce que nous allons faire. Je souhaite en effet profiter de cette occasion pour vous faire travailler sur un autre serveur, un vrai serveur dapplications Java EE cette fois : GlassFish. Pas nimporte quel serveur dailleurs : il sagit du serveur de rfrence, celui qui implmente la lettre les spcifications Java EE 6. Rien d'tonnant me direzvous, puisque c'est Oracle - la maison mre de tout l'cosystme Java - qui dite ce serveur ! Si vous vous souvenez bien, je vous en avais dj brivement parl lorsque nous avions dcouvert la JSTL : je vous avais alors expliqu que GlassFish, contrairement Tomcat pour lequel il est ncessaire de fournir un Jar, embarquait par dfaut la bibliothque. Eh bien je pourrais vous faire exactement la mme rflexion au sujet de JPA : GlassFish embarque par dfaut son implmentation de rfrence, le framework de persistance nomm EclipseLink, alors quil est ncessaire de fournir un Jar externe Tomcat pour quil permette de travailler avec JPA. En outre, sachez galement quil est possible avec GlassFish de redployer automatiquement une application aprs toute modification sur son code ou sa configuration, alors quil fallait souvent redmarrer Tomcat pour quil prenne en compte des modifications.

Mise en place
V ous laurez compris, avec GlassFish, tout est inclus par dfaut. Actuellement, la dernire version en date est estampille GlassFish 3.1.2.2 . Pour la rcuprer, rendez-vous sur cette page de tlchargement, choisissez ensuite la version intitule WebProfile, et enfin la version correspondant votre systme (dans l'encadr sur l'image suivante, premire colonne pour Windows, seconde pour Mac & Linux) :

Si une version plus rcente est disponible lorsque vous lisez ce cours, vous pouvez opter pour cette dernire en lieu et place de la 3.1.2.2, mais ce n'est pas une obligation. Sachez par ailleurs que nous allons trs bientt intgrer notre serveur Eclipse, par consquent si vous tenez utiliser une version plus rcente, assurez-vous d'abord que son intgration Eclipse est possible.

Une fois tlcharge, installez-la en suivant simplement les consignes qui vous sont donnes par lassistant. Quelques conseils pour la route : prfrez un rpertoire proche de la racine de votre disque. Amis windowsiens, nallez surtout pas installer le serveur dans les traditionnels "Program Files" ou autres "Documents And Settings" En ce qui me concerne, je lai install dans un dossier intitul glassfish3 directement la racine de mon disque C:\, et c'est d'ailleurs ce que propose l'installeur par dfaut ; n'installez pas l'outil de mise jour automatique (" Update tool "), cela vous vitera un dlai supplmentaire lors de l'installation et quelques ennuis ventuels ; faites attention ne pas donner au serveur les mmes ports que ceux utiliss par Tomcat : si vous avez gard par dfaut

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

491/606

le port 8080 avec Tomcat, alors utilisez par exemple le port 8088 pour GlassFish, afin dviter les conflits en cas dutilisation simultane. De mme pour le port de la console dadministration, pour lequel vous pouvez en principe laisser la valeur par dfaut 4848, Tomcat utilisant par dfaut un port diffrent :

si vous spcifiez un mot de passe pour la gestion du serveur, notez le bien quelque part, pour pouvoir le retrouver en cas doubli.

Intgration Eclipse
L'installation du serveur termine, ouvrez (ou redmarrez) Eclipse. Dans le volet intitul Servers , en bas de votre espace de travail Eclipse, faites un clic droit dans le vide et choisissez New > Server. Dans la fentre qui souvre alors saffiche une liste des diffrents serveurs actuellement grs par votre version dEclipse. Si dans cette liste figure un dossier nomm GlassFish, alors votre Eclipse est dj capable de prendre en charge votre serveur frachement install. Si toutefois aucun dossier portant ce nom napparat, vous devrez cliquer sur le lien intitul Download additional servers adapters . Eclipse va alors scanner les diffrents outils disponibles, et une fois la recherche termine vous devrez choisir lentre nomme Oracle GlassFish et la tlcharger. Une fois la rcupration termine, vous pourrez alors poursuivre. Une fois prts intgrer votre serveur Eclipse, droulez le dossier nomm GlassFish et cliquez sur lentre intitule GlassFish 3.1.2.2 (ou suprieure si une version plus rcente est disponible lorsque vous lisez ce cours). Sur les crans suivants, ne changez rien aux rglages par dfaut et terminez en cliquant sur Finish. V otre serveur apparat alors dans le volet infrieur de votre espace de travail, aux cts de votre serveur Tomcat utilis jusqu prsent dans le cours :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

492/606

Configuration
Commencez par vrifier que votre serveur se lance correctement en cliquant sur le bouton de dmarrage intgr au volet serveur dEclipse. Si des erreurs surviennent, redmarrez Eclipse. Si des erreurs surviennent encore, redmarrez votre poste. Si aprs cela des erreurs surviennent toujours, alors vous devrez dsinstaller proprement GlassFish et reprendre calmement les tapes prcdentes. Une fois le serveur lanc avec succs, vous allez pouvoir procder sa configuration. De quelle configuration est-il question exactement ? De celle du pool de connexions que vous allez mettre en place ! Eh oui, maintenant que vous savez mettre en place un pool, vous navez plus dexcuse

Quand nous avons dcouvert les pools, nous navions rien configur avec Tomcat ! Nous avions simplement ajout quelques Jar et cod quelques lignes dans linitialisation de notre DAOFactory C'est vrai, seulement puisque nos allons travailler avec JPA, cest dornavant notre conteneur qui va soccuper de manipuler la base de donnes pour nous ! Il va donc bien falloir que nous lui prcisions quelque part comment faire. Il existe plusieurs manires de procder : celle que je vais vous prsenter consiste paramtrer le pool directement au niveau du serveur, et non plus au niveau de lapplication. Pour commencer, on prend les mmes et on recommence ! Il faut rcuprer les Jar de BoneCP et les placer dans le rpertoire /lib de GlassFish. O se trouve ce rpertoire exactement ?

V ous remarquerez rapidement que l'organisation en interne d'un serveur GlassFish est sensiblement diffrente de celle d'un serveur Tomcat. Nous n'allons pas nous amuser parcourir chaque dossier de l'arborescence, je vais simplement vous guider lorsque cela sera ncessaire. En l'occurrence, vous devez placer vos Jar dans le dossier /glassfish3/glassfish/domains/domain1/lib/ext/ . Une fois ceci fait, il faut ensuite procder au paramtrage du pool et de la connexion la base de donnes. Plutt que de vous perdre dans les formulaires de configuration de la console dadministration de GlassFish (qui, pour les curieux, est accessible l'adresse http://localhost:4848), je vous ai prpar un fichier XML prt lemploi, contenant toutes les informations dont nous avons besoin : cliquez-ici pour le tlcharger (clic-droit > Enregistrer sous...). Merci qui ? :- Pour information, ce fichier contient la dclaration dune connexion MySQL via JDBC classique, rfrence par le nom jdbc/bonecp_resource , et son association un pool de connexion bas sur BoneCP. Une fois le fichier rcupr, vous allez devoir lappliquer votre serveur. Pour ce faire, rendez-vous dans le rpertoire /glassfish3/bin, et copiez-y le fichier. Excutez alors le fichier nomm asadmin (le fichier .bat si vous travaillez sous Windows, l'autre sinon). Une console de commandes souvre alors. Sous Windows, vous allez devoir y taper la commande suivante : Code : Console add-resources bonecp-datasource.xml

Sous Mac ou Linux, vous devrez probablement prciser le chemin complet vers le fichier XML pour que la commande localise correctement votre fichier XML : Code : Console

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


add-resources /chemin/complet/vers/glassfish3/bin/bonecp-datasource.xml

493/606

Si lopration se droule sans accrocs, vous verrez alors safficher un message confirmant le succs de lopration. V ous pourrez finalement fermer la fentre. V oil tout ce quil est ncessaire de configurer ct serveur. Dornavant pour tout projet que vous dvelopperez et dploierez sur ce serveur, vous pourrez profiter trs simplement dun pool de connexions BoneCP vers une base de donnes MySQL prt lusage !

Cration du projet
Nous sommes maintenant prts crer un nouveau projet sous Eclipse. Pour commencer, rendez-vous dans New > Dynamic Web Project, et nommez par exemple votre projet pro_jpa. Copiez-y ensuite depuis lancien projet pro la JSP inscription.jsp, le fichiers CSS et son rpertoire /inc, le bean Utilisateur, le DAO UtilisateurDao, les classes DAOException et FormValidationException, lobjet mtier InscriptionForm et enfin la servlet Inscription. En clair, tout ce qui va nous tre utile pour mettre en place un systme dinscription bas cette fois sur JPA ! Pour le moment, ne vous proccupez pas des ventuels erreurs ou avertissements affichs par Eclipse dans votre nouveau projet, nous allons transformer cet embryon tape par tape.

Ensuite, nous allons devoir crer un fichier nomm glassfish-web.xml dans le rpertoire /WEB-INF de notre application. Sans grande surprise, il s'agit d'un fichier ressemblant fortement au fichier web.xml, que nous allons ici utiliser pour dfinir le contexte de dploiement de notre application, ainsi qu'une option qui peut toujours servir. Regardons d'abord le code, et parlons-en ensuite : Code : XML - /WEB-INF/glassfish-web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app> <context-root>/pro_jpa</context-root> <class-loader delegate="true"/> <jsp-config> <property name="keepgenerated" value="true"> <description>Conserve une copie du code des servlets autognres.</description> </property> </jsp-config> </glassfish-web-app>

Observez la syntaxe des quelques sections ici mises en place. Concernant la proprit keepgenerated, il s'agit d'une option permettant de demander au serveur de garder une copie du code Java des servlets auto-gnres depuis vos JSP. Nous n'allons pas nous en servir, mais cela pourra toujours vous tre utile dans la suite de votre apprentissage. Crez ensuite un dossier nomm META-INF dans le rpertoire src du projet, et crez-y un fichier nomm persistence.xml . V oici l'arborescence que vous tes alors censs obtenir :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

494/606

Et voici son code de base : Code : XML - src/META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> </persistence>

Dans ce fichier ncessaire au bon fonctionnement de JPA, nous allons dfinir deux choses : les informations de connexion la base de donnes dont auront besoin les EntityManager ; une unit de persistance pour chacune de nos entits. Quest-ce que cest que cette histoire dunit ?

Une unit de persistance est un contexte, une section qui permet de dfinir quelles classes va sappliquer un EntityManager, sur quelle connexion il va se baser et comment il va dialoguer avec la BDD pour persister les donnes. Ne venons-nous pas de configurer la connexion directement depuis le serveur ?

Oui, tout fait. Mais il est tout de mme ncessaire de prciser lapplication quelle connexion elle doit utiliser ! V oil dailleurs pourquoi la connexion porte un nom dans notre serveur : cest pour lui permettre dtre retrouve trs simplement dans un annuaire, que lon nomme JNDI. Nous nallons pas nous attarder sur ce concept, qui fait partie de Java au sens large. Nous allons donc mettre en place une unit que nous allons nommer "bdd_sdzee_PU" (PU pour Persistence Unit ), qui va s'appliquer notre entit Utilisateur et qui va se baser sur la connection nomme jdbc/bonecp_resource. V oici le code ncessaire : Code : XML <persistence-unit name="bdd_sdzee_PU" transaction-type="JTA"> <jta-data-source>jdbc/bonecp_resource</jta-data-source>

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


<class>com.sdzee.entities.Utilisateur</class> </persistence-unit>

495/606

Je vous laisse observer la syntaxe employer. Concernant le type de transaction, c'est un concept avanc que nous n'allons pas aborder dans ce cours, je vous demande de me faire aveuglment confiance sur cet aspect ! ;-) Pour terminer, nous avons la possibilit de dfinir des proprits supplmentaires, concernant la connexion tablir par exemple. Je vous voquais un peu plus tt plusieurs manires de configurer une connexion, en voil une seconde : si nous navions pas configur le pool sur notre serveur, nous aurions d prciser tout cela directement dans ce fichier. Dans notre cas, nous navons aucune proprit ajouter, et voici le fichier complet : Code : XML - src/META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="bdd_sdzee_PU" transaction-type="JTA"> <jta-data-source>jdbc/bonecp_resource</jta-data-source> <class>com.sdzee.entities.Utilisateur</class> <properties/> </persistence-unit> </persistence>

Cration dune entit Utilisateur


Nous pouvons maintenant attaquer la transformation de notre projet. Notre premire cible est le bean Utilisateur, auquel nous allons inclure quelques annotations. Attention, il nest pas ici question des annotations Java EE que je vous ai prsentes dans le chapitre prcdent, mais bien dannotations propres JPA. Gardez ce lien vers la Javadoc officielle sous le coude, car vous allez en avoir besoin pour comprendre en dtails chacune des annotations que nous allons mettre en jeu. Pour commencer, nous allons indiquer notre serveur, plus prcisment notre conteneur, que notre bean Utilisateur va devenir un EJB Entity. Pour cela, nous devons l'annoter avec @Entity : Code : Java - Extrait de l'entit com.sdzee.tp.beans.Utilisateur @Entity public class Utilisateur { ... }

En thorie, il faudrait galement indiquer via l'annotation @Table(name = "Utilisateur") que lentit est lie la table nomme Utilisateur dans notre base. Toutefois, le comportement par dfaut du conteneur, en labsence de cette prcision, est de considrer que le nom de la table est identique celui de la classe. Dans notre cas, notre bean et notre table sappellent tous deux Utilisateur, et nous pouvons donc nous passer de cette annotation superflue. Il est ensuite ncessaire de dfinir la correspondance entre les donnes de lentit Utilisateur et les donnes qui se trouvent dans la table. Nous allons donc devoir annoter les attributs de notre entit. L encore, par dfaut le conteneur sait identifier luimme que tel attribut correspond tel champ, partir du moment o les champs portent le mme nom que les attributs. Lorsque ce nest pas le cas par contre, il est ncessaire dajouter une annotation sur lattribut pour prciser avec quel champ doit tre tablie une correspondance. Dans notre cas, nous avons deux attributs qui portent un nom diffrent de leur quivalent dans la table, et nous devons donc ajouter les annotations @Column(name = "") sur chacun deux : Code : Java - Extrait de l'entit com.sdzee.tp.beans.Utilisateur

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


@Column( name = "mot_de_passe" ) private String motDePasse; ... @Column( name = "date_inscription" ) private Timestamp dateInscription;

496/606

Enfin, pour prciser qu'un attribut est associ la cl primaire de la table, il est ncessaire de lannoter avec @Id. En outre, pour que le conteneur puisse correctement grer lid auto-gnr lors de la cration dun utilisateur en base, il faut ajouter lannotation @GeneratedValue sur cet attribut (voyez sa documentation pour plus de dtails) : Code : Java - Extrait de l'entit com.sdzee.tp.beans.Utilisateur @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private Long id;

Et cest tout ! Eh oui, voil tout ce quil est ncessaire de modifier pour transformer notre simple JavaBean en un EJB Entity, prt tre gr par notre conteneur ! Par ailleurs, puisque nous venons de transformer notre bean en entit, nous allons en profiter pour renommer le package qui la contient par com.sdzee.tp.entities. V oici le code final de notre nouvelle entit, les getters/setters exclus : Code : Java - com.sdzee.tp.entities.Utilisateur package com.sdzee.entities; import java.sql.Timestamp; import import import import import javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id;

@Entity public class Utilisateur { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private Long id; private String email; @Column( name = "mot_de_passe" ) private String motDePasse; private String nom; @Column( name = "date_inscription" ) private Timestamp dateInscription; } ... // couples de getters/setters pour chaque attribut dclar

Remarquez la simplicit avec laquelle nous avons opr les changements : cinq petites annotations et le tour est jou ! Si nos attributs portaient tous le mme nom que leur quivalent dans la table, seules trois annotations auraient suffi !

Cration dun EJB Session


Le moment est pour nous venu de dcouvrir le second type dEJB : lEJB Session. V ous ne savez pas encore de quoi il retourne, mais ne vous inquitez pas, il ny a rien de sorcier. Il s'agit simplement d'un objet qui donne accs aux services & mthodes qu'il contient. Il existe deux types dEJB Session : ceux qui sont Stateless , et ceux qui sont Stateful . Pour vous aider comprendre la diffrence entre ces deux types d'EJB Session, prenons deux exemples :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

497/606

Stateless : authentification sur un site marchand


Premier exemple, un systme d'authentification (connexion) un site marchand. Dans sa version la plus simple, pour raliser cette tche il suffit d'un objet contenant une mthode qui compare un couple d'identifiants passs en paramtres ceux qui sont stocks dans la table des clients en base, et qui retourne un code de succs ou d'erreur en retour. Cet objet ne prsente donc pas de risques lis aux multiples Threads (en anglais, on parle de thread-safety), et une seule instance peut trs bien tre partage par de multiples requtes issues de clients diffrents. De manire concise, voici les proprits d'un EJB Stateless : aucune donne n'est retenue ni enregistre, c'est--dire qu'aucun tat n'est retenu. On dit alors que l'objet est sans tat, ou Stateless ; aucun mcanisme ne garantit que deux appels conscutifs une mthode d'un tel EJB visent une seule et mme instance ** ; les accs concurrents sont impossibles, mais le systme est threadsafe tout de mme puisque le conteneur envoie les requtes simultanes vers des instances diffrentes du mme EJB ** . ( ** : nous allons y revenir lorsque nous modifierons notre servlet)

Stateful : panier sur un site marchand


Second exemple, un systme de panier sur un site marchand. Pour raliser une telle tche, nous avons besoin d'un objet qui soit capable de retenir les commandes effectues par un client, et qui puisse tre rutilis par ce mme client pendant sa session d'utilisation, sans risque qu'un autre client puisse y accder. Cette objet prsente donc un risque li aux multiples Threads : une mme instance ne doit pas surtout pas tre partage par plusieurs requtes issues de clients diffrents, sans quoi le panier perd tout son intrt. De manire concise, voici les proprits d'un EJB Stateful : des donnes sont retenues dans l'objet aprs un appel, c'est--dire qu'il conserve un tat. On dit alors que l'objet est tat, ou Stateful ; laccs une instance de lEJB est rserv un seul client la fois ; les accs concurrents sont impossibles, le conteneur gre une liste dattente en cas de tentatives simultanes.

Maintenant que nous connaissons le principe, rflchissons un peu : quoi va bien pouvoir nous servir un EJB Session dans notre application ? Je vous donne un indice : nous allons utiliser un EJB Session de type Stateless. Regardez rapidement le code de nos diffrents objets, et essayez de reprer lequel parmi eux correspond parfaitement la dfinition d'un objet sans tat. V ous avez trouv ? Eh bien oui, nous allons tout bonnement remplacer notre prcdent DAO ! Aprs tout, il sagit bien l dune classe ne contenant que des mthodes dinteraction avec JDBC. Bien entendu cette fois, nous nallons plus converser avec JDBC. Car cela, cest le conteneur qui va sen occuper pour nous grce JPA ! Dornavant, nous allons simplement demander notre EntityManager de donner des ordres notre base de donnes, via ses mthodes persist(), find(), remove(), etc. N'hsitez pas parcourir en dtail la Javadoc de cet objet afin de dcouvrir tous les trsors qu'il recle. V oici donc ce que va devenir notre classe UtilisateurDao. Je vous donne d'abord le code, les explications viennent aprs : Code : Java - com.sdzee.dao.UtilisateurDao package com.sdzee.dao; import import import import import javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.NoResultException; javax.persistence.PersistenceContext; javax.persistence.Query;

import com.sdzee.entities.Utilisateur; @Stateless public class UtilisateurDao { private static final String JPQL_SELECT_PAR_EMAIL = "SELECT u FROM Utilisateur u WHERE u.email=:email"; private static final String PARAM_EMAIL = "email";

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


// Injection du manager, qui s'occupe de la connexion avec la @PersistenceContext( unitName = "bdd_sdzee_PU" ) private EntityManager em; // Enregistrement d'un nouvel utilisateur public void creer( Utilisateur utilisateur ) throws DAOException try { em.persist( utilisateur ); } catch ( Exception e ) { throw new DAOException( e ); }

498/606

BDD

// Recherche d'un utilisateur partir de son adresse email public Utilisateur trouver( String email ) throws DAOException { Utilisateur utilisateur = null; Query requete = em.createQuery( JPQL_SELECT_PAR_EMAIL ); requete.setParameter( PARAM_EMAIL, email ); try { utilisateur = (Utilisateur) requete.getSingleResult(); } catch ( NoResultException e ) { return null; } catch ( Exception e ) { throw new DAOException( e ); } return utilisateur; }

Quarante lignes, imports et sauts de ligne compris. On est bien loin des 100+ lignes de notre ancien UtilisateurDaoImpl ! Premire remarque, la structure globale de l'objet n'a pas chang : il est toujours constitu de deux mthodes, charges respectivement de crer et de trouver un utilisateur en base. Afin de ne pas avoir modifier les appels ces deux mthodes depuis notre objet mtier, j'ai pris soin de conserver leur nom et paramtres l'identique : creer( Utilisateur utilisateur ) et trouver( String email ). Deuximement, remarquez avec quelle simplicit nous prcisons notre conteneur que l'objet est un EJB de type Stateless. Il suffit pour cela d'une annotation @Stateless place avant la dclaration de la classe. De mme, remarquez avec quelle simplicit nous pouvons injecter dans notre EJB une instance d'un EntityManager dpendant d'une unit de persistance, via l'annotation @PersistenceContext( unitName = "..." ). V ous reconnatrez ici le nom de l'unit que nous avons dclare dans le fichier persistence.xml : bdd_sdzee_PU. Injecter ?

Cest le terme utilis pour dcrire le fait que le cycle de vie de lobjet annot est gr par le conteneur. En dautres termes, cela signifie que nous navons plus besoin de nous occuper de la cration ni de linitialisation de lobjet, cest le conteneur qui va le faire pour nous ! En effet comme vous pouvez le voir dans ce code, nous faisons appel des mthodes de l'objet em mais aucun moment nous ne crons ni n'initialisons une instance d'objet, nous nous contentons uniquement de sa dclaration et de son annotation ! Analysons ensuite la mthode de cration, la plus simple des deux : alors qu'elle occupait auparavant une vingtaine de lignes et faisait appel des mthodes utilitaires pour la cration d'une requte prpare et la libration des ressources utilises, elle est dsormais rduite peau de chagrin grce une seule et unique mthode : persist(). C'est cette mthode de l'EntityManager qui va se charger de tout pour nous, nous avons uniquement besoin de lui transmettre notre entit Utilisateur et tout le reste s'effectue derrire les rideaux ! Et si nous n'avions pas mis en place une exception spcifique de type DAOException, notre mthode tiendrait sur une seule et unique ligne ! En ce qui concerne la mthode de rcupration d'un utilisateur en se basant sur son adresse email, ce n'est pas aussi magique. En effet, comment notre EntityManager pourrait-il deviner que nous souhaitons utiliser l'argument pass en paramtre dans une

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

499/606

clause WHERE applique une requte sur la table Utilisateur ? La seule requte de lecture qu'il est possible de faire par dfaut, c'est la plus basique, savoir la recherche dun lment en se basant sur sa cl primaire. Ce cas mis part, lEntityManager ne peut pas deviner ce que vous souhaitez rcuprer : la clause WHERE permet elle seule deffectuer de nombreuses requtes diffrentes sur une seule et mme table. Nous n'y coupons pas, nous devons crire un minimum de SQL pour parvenir nos fins. Toutefois, il nest plus ncessaire dcrire du SQL directement via JDBC comme nous le faisions dans nos DAO. Dornavant, JPA nous facilite la tche en encadrant notre travail, et nous propose pour cela deux mthodes diffrentes : le langage JPQL, et le systme Criteria. Le plus simple prendre en mains est le JPQL, car il ressemble trs fortement au langage SQL. Cependant, il prsente une diffrence majeure : il n'est pas utilis pour interagir avec la base de donnes, mais avec les entits de notre application. Et c'est bien l l'objectif de JPA : dcoupler l'application du systme de stockage final. Pour crer une telle requte, il suffit d'appeler la mthode createQuery() de l'EntityManager. Analysons brivement la syntaxe de notre requte : Code : JPQL SELECT u FROM Utilisateur u WHERE u.email=:email

Tout d'abord, observez la ressemblance frappante avec le langage SQL. V oici les subtilits noter : 1. dans la section FROM, nous dfinissons le type de l'objet cibl par la requte, Utilisateur, et son alias dans la requte courante, ici u ; 2. dans la section SELECT, nous prcisons simplement que nous souhaitons rcuprer l'entit Utilisateur en notant son alias u ; 3. dans la clause WHERE, nous ciblons l'attribut email de l'entit Utilisateur en concatnant simplement l'alias et le nom du champ, ici u.email . Enfin, nous spcifions que nous allons fournir un paramtre la requte et le nommons email via la notation :email . Ainsi, aprs la construction de cette requte la ligne 32, nous fournissons un paramtre nomm email et contenant l'adresse utilise pour effectuer la recherche parmi les entits, grce la trs explicite mthode setParameter(). Ceci devrait vous rappeler le principe des requtes prpares que nous avions auparavant mises en place dans nos DAO. Enfin, nous ralisons l'appel qui va dclencher la requte JPQL via la mthode getSingleResult(). Comme vous pouvez le lire dans sa documentation, celle-ci retourne un unique rsultat. Nous aurions galement pu utiliser la mthode getResultList(), mais ce n'est pas ncessaire : nous savons qu'une adresse email est forcment unique dans notre base de donnes, et nous pouvons donc tre certain qu'il n'existera jamais plus d'un rsultat. Par contre, puisque nous utilisons getSingleResult() nous devons faire attention au cas o aucun rsultat ne serait retourn, c'est--dire lorsqu'une adresse n'existe pas encore en base. En effet, la documentation de la mthode nous prvient qu'elle envoie un exception de type NoResultException lorsque rien n'est trouv. V oil pourquoi nous entourons l'appel d'un bloc try / catch, dans lequel nous retournons null en cas d'absence de rsultat. Les autres exceptions possibles tant peu probables ou lies des erreurs de configuration, nous nous contentons pour finir de les encapsuler dans notre exception spcifique DAOException. Nous en avons termin avec notre EJB. Ne vous laissez pas mprendre par la longueur de mes explications : j'ai pris le temps de dtailler chaque point pour que vous compreniez aisment comment s'utilisent les nouveaux lments que vous avez dcouverts, mais si vous regardez maintenant nouveau le code, vous vous rendrez compte qu'il est trs simple et surtout trs court !

Modification de la servlet
Auparavant, nous rcuprions une instance de DAO lors de la cration de la servlet, depuis une Factory qui tait quant elle initialise au lancement de lapplication. Avec JPA, nous allons pouvoir nous dbarrasser de tout cet arsenal, et par consquent nous n'allons plus avoir besoin de la mthode init() dans notre servlet ! Dans ce cas, comment allons-nous transmettre une unique instance d'objet qui sera partage par toutes les requtes entrantes, comme l'tait notre DAO auparavant ?

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

500/606

Eh bien en ralit, nous n'allons plus nous contenter d'une seule instance, mais de plusieurs instances que le conteneur va grer pour nous. Il va littralement crer un pool d'EJB, similaire sur le principe notre pool de connexions : lorsque plusieurs requtes quasi-simultanes manant de clients diffrents seront rediriges vers notre servlet, le conteneur va transmettre une instance diffrente de notre EJB chacune d'elles tant qu'il en restera dans son pool, et quand le pool sera vide il grera une file d'attente et attendra le retour des instances dj distribues pour servir les requtes en attente. Sur le papier, tout cela parat bien compliqu, n'est-ce pas ? Eh bien en pratique, figurez-vous qu'il n'y a rien de plus simple ! D'aprs vous, qu'est-ce qui permet de donner des informations au conteneur, et qui ncessite insolemment peu d'efforts ?... Bingo ! C'est l'aide dune simple mais puissante annotation que nous allons injecter notre EJB directement dans notre servlet : @EJB. Injecter ?

Eh oui, l encore nous allons faire appel un mcanisme d'injection similaire celui que nous avons dcouvert un peu plus tt dans notre EJB Stateless. Pour rappel, cela signifie que nous navons plus besoin de nous occuper de la cration ni de linitialisation de lobjet, cest le conteneur qui va le faire pour nous ! V oyez plutt : Code : Java package com.sdzee.servlets; import java.io.IOException; import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

import com.sdzee.dao.UtilisateurDao; import com.sdzee.entities.Utilisateur; import com.sdzee.forms.InscriptionForm ; @WebServlet( urlPatterns = { "/inscription" } ) public class Inscription extends HttpServlet { public static final String ATT_USER = "utilisateur"; public static final String ATT_FORM = "form"; public static final String VUE = "/WEBINF/inscription.jsp"; // Injection de notre EJB (Session Bean Stateless) @EJB private UtilisateurDao utilisateurDao; public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Affichage de la page d'inscription */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Prparation de l'objet formulaire */ InscriptionForm form = new InscriptionForm( utilisateurDao ); /* Traitement de la requte et rcupration du bean en rsultant */ Utilisateur utilisateur = form.inscrireUtilisateur( request ); /* Stockage du formulaire et du bean dans l'objet request

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


*/ request.setAttribute( ATT_FORM, form ); request.setAttribute( ATT_USER, utilisateur );

501/606

this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Comme vous pouvez le voir la ligne 33, nous transmettons notre EJB - annot la ligne 23 - notre objet mtier, de la mme manire que nous lui passions linstance de DAO auparavant. Mais alors que nous devions initialiser manuellement le contenu de notre objet utilisateurDao dans notre version prcdente, cette fois nous nous reposons entirement sur le conteneur : aucun moment nous ne crons ni n'initialisons une instance d'objet, nous nous contentons uniquement de sa dclaration et de son annotation ! Comprenez et retenez bien cet aspect fondamental de l'EJB : nous ne nous occupons plus de son cycle de vie, nous dlguons entirement ce travail au conteneur.

Avant de passer la suite, il y a quelque chose de trs important que vous devez comprendre au sujet de l'injection d'EJB dans une servlet : elle est exclusivement rserve aux EJB de type Stateless . Souvenez-vous : il n'existe qu'une seule et unique instance de votre servlet, que le conteneur initialise au chargement de votre application. Autrement dit, les variables d'instances sont partages entre toutes les requtes qui sont rediriges vers votre servlet ! Ainsi dclarer et utiliser un objet localement dans une des mthodes doXXX() ne pose aucun problme, car chaque requte entrante - c'est--dire chaque Thread - va crer localement sa propre instance de l'objet. Par contre, dclarer un objet en dehors des mthodes doXXX() est proscrire si l'objet en question conserve un tat (en d'autres termes, s'il est Stateful), car il pourra alors tre partag par plusieurs requtes issues de clients diffrents. Si vous ne comprenez pas le problme, souvenez-vous de l'exemple du panier d'achats sur un site marchand... Bref, voil pourquoi il ne faut jamais procder l'injection d'objets Stateful dans une servlet : cela causerait trs probablement des comportements non souhaits, que vous ne pourrez dceler qu'aprs des tests pousss. Dans ce cas, pourquoi ne nous sommes pas poss cette question lorsque nous partagions notre DAO entre toutes nos requtes auparavant ? Remarque pertinente : notre DAO tait effectivement dclar en tant que variable d'instance dans notre servlet. Eh bien l'explication, c'est que notre DAO tait... sans tat ! Eh oui, il ne conservait aucune donne et pouvait trs bien tre rendu accessible diffrents threads. Bien entendu, je m'tais bien gard de vous parler de tout ca si tt, vous aviez dj bien assez de grain moudre avec le reste. Disons que je vous avais conduit me faire inconsciemment et aveuglment confiance ! Dans un chapitre annexe la fin du cours, vous en apprendrez davantage sur le cycle de vie d'une servlet et sur son caractres multi-threads, via des exemples simples et un droulement pas pas du cheminement des requtes traites.

Enfin, mais vous l'avez probablement dj remarque dans le code, vous n'oublierez pas d'insrer l'annotation Java EE @WebServlet que nous avons dcouverte dans le chapitre prcdent, afin de dclarer votre servlet et ainsi ne plus avoir maintenir un pnible fichier web.xml !

Modification de lobjet mtier


Nous apercevons le bout du tunnel ! Dans notre objet mtier, il nous reste une lgre modification apporter, afin de prendre en charge linitialisation de la date dinscription au sein du code. Eh oui, rflchissez bien : puisque cest maintenant un EntityManager qui va soccuper pour nous de linsertion en base, et que cette EntityManager fait tout derrire les rideaux de manire automatise via la mthode persist(), nous navons plus la possibilit de prciser manuellement MySQL dinitialiser le champ date_inscription via la fonction NOW() comme nous le faisions auparavant. Nous pourrions mettre en place une valeur par dfaut pour le champ, en allant directement modifier la table SQL, mais nous sommes l pour faire du Java et un simple ajout suffit pour pallier ce manque : Code : Java - Extraits de com.sdzee.forms.InscriptionForm

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


public Utilisateur inscrireUtilisateur( HttpServletRequest request ) { String email = getValeurChamp( request, CHAMP_EMAIL ); String motDePasse = getValeurChamp( request, CHAMP_PASS ); String confirmation = getValeurChamp( request, CHAMP_CONF ); String nom = getValeurChamp( request, CHAMP_NOM ); Timestamp date = new Timestamp( System.currentTimeMillis() ); Utilisateur utilisateur = new Utilisateur(); try { traiterEmail( email, utilisateur ); traiterMotsDePasse( motDePasse, confirmation, utilisateur ); traiterNom( nom, utilisateur ); traiterDate( date, utilisateur ); ... }

502/606

... }

/* * Simple initialisation de la proprit dateInscription du bean avec la * date courante. */ private void traiterDate( Timestamp date, Utilisateur utilisateur ) { utilisateur.setDateInscription( date ); }

la ligne 6, nous initialisons un objet Timestamp avec la date et l'heure courantes. Ligne 13, nous ralisons un appel une mthode traiterDate(), que nous dfinissons ensuite aux lignes 23 25 et qui se contente d'appeler le setter de notre entit. Le reste du code est inchang, je me suis donc permis de tronquer les blocs pour rester concis. V ous prendrez bien soin d'intgrer correctement ces lgers ajouts au code existant de votre objet mtier !

Nous y voil ! En prenant un peu de recul, vous vous apercevrez que le code grant l'accs aux donnes est autrement plus lger quil ne ltait lorsque nous faisions tout la main, et que le reste de l'application profite par la mme occasion de ce rgime. Paradoxalement, vous vous rendrez compte que cette nouvelle architecture est trs proche de lancienne ! Eh oui, le code que je vous avais fait mettre en place tait dj plutt bien organis, nest-ce pas ? ;-)

Tests et vrifications Vrification du bon fonctionnement dune inscription


Depuis Eclipse, dployez votre projet sur l'instance de GlassFish que vous y avez intgr prcdemment, et dmarrez le serveur. Rendez-vous ensuite sur la page http://localhost:8088/pro_jpa/inscription depuis votre navigateur, puis faites de simples tests : avec erreurs dans certains champs, puis sans erreur, et enfin avec une adresse dj enregistre en base. Si tout est correctement en place, absolument rien ne doit changer dans le comportement de votre application par rapport notre dernier projet ! Si vous obtenez une erreur quelconque, cela signifie que vous avez oubli quelque chose en cours de route. Assurezvous que : votre serveur MySQL est bien dmarr ; votre serveur GlassFish est bien configur (le pool, notamment) et dmarr ; vous avez bien copi et modifi toutes les classes et tous les fichiers ncessaires ; votre projet est bien dploy sur votre serveur. Dans les coulisses par contre, vous savez que beaucoup de choses ont chang, et que vous avez presque compltement abandonn MySQL et JDBC. D'ailleurs ce sujet, si nous allions jeter un oeil aux requtes effectues par EclipseLink - notre solution JPA - lors d'un appel aux mthodes prsentes dans notre DAO, c'est--dire notre EJB Stateless ?

Analyse des requtes SQL gnres lors dune inscription


www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

503/606

V ous pouvez analyser les requtes SQL effectues par un EntityManager en activant l'criture de logs depuis son unit de persistance dans le fichier persistence.xml . V ous vous souvenez de ces proprits dont je vous ai parl ? Eh bien le moment est venu de nous en servir : Code : XML - src/META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="bdd_sdzee_PU" transaction-type="JTA"> <jta-data-source>jdbc/bonecp_resource</jta-data-source> <class>com.sdzee.entities.Utilisateur</class> <properties> <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.parameters" value="true"/> </properties> </persistence-unit> </persistence>

En ajoutant cette section aux lignes 9 12 dans notre fichier persistence.xml , nous demandons explicitement notre solution JPA - EclipseLink - d'crire dans le fichier de logs du serveur les requtes SQL qu'elle envoie la base de donnes. O se trouve ce fichier de logs ?

Selon votre configuration, vous verrez peut-tre s'afficher les requtes effectues dans l'onglet intitul Console en bas de votre espace de travail Eclipse, aux cts de l'onglet Servers dans lequel vous grez le dploiement de votre application sur GlassFish. V ous avez en principe la possibilit de visualiser le contenu des logs du serveur en effectuant un clic droit sur son nom, et en suivant GlassFish > View Log file :

Si cela ne fonctionne pas sur votre poste, alors il vous suffit d'aller regarder manuellement le fichier nomm server.log situ dans le rpertoire /glassfish3/glassfish/domains/domain1/logs , avec votre diteur de texte prfr. Si par exemple vous essayez de vous connecter avec une adresse email dj enregistre, vous devriez visualiser une requte de cette forme, qui correspond bien la requte que nous avons crite en JPQL dans notre EJB : Code : Console

[#|2012-12-03T23:27:09.358+0800|FINE|glassfish3.1.2|org.eclipse.persistence.session.file: .../glassfish3/glassfish/domains/domain1/eclipseApps/pro_jpa/WEBINF/classes/_bdd_sdzee_PU.sql|_ThreadID=18;_ThreadName=Thread-3; ClassName=null;MethodName=null;|SELECT ID, date_inscription, EMAIL, mot_de_passe, NOM FRO

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


) bind => [coyote@gmail.com]|#]

504/606

Dans cet exemple, j'ai essay de m'inscrire avec l'adresse coyote@gmail.com alors qu'elle existe dj en base. Ne vous proccupez pas des informations affiches en amont : ce sont simplement d'autres marqueurs qui ne nous intressent pas pour notre tude. Si maintenant vous vous inscrivez avec succs, vous allez pouvoir observer les requtes suivantes : Code : Console

[#| .... |INSERT INTO UTILISATEUR (date_inscription, EMAIL, mot_de_passe, NOM) VALUES (?, bind => [2012-1203 23:37:31.582, coyote@test.com, wZdEPOFZ3KpA7qCanXtfeHtb0GDe66qEmh0FvLKnp00dP0PGKK70jQ= [#| .... |SELECT LAST_INSERT_ID()|#]

Dans cet exemple, j'ai utilis pour m'inscrire l'adresse coyote@test.com, et j'ai renseign mon nom avec le mot "test". Il est intressant de remarquer deux choses : la mthode persist() de l'EntityManager - que, pour rappel, nous appelons la ligne 20 de notre EJB Stateless s'occupe pour nous de gnrer automatiquement une requte d'insertion et d'y assigner les diffrentes valeurs des paramtres attendus. Nul besoin de le prciser, la requte gnre est bien entendu une requte prpare ; afin de rcuprer l'id auto-gnr, vous pouvez voir ici la seconde requte SQL effectue : SELECT LAST_INSERT_ID(). Je vous laisse analyser davantage par vous-mmes les changes ayant lieu dans les diffrents cas d'usage de notre petite application.

Aller plus loin


V ous devez tre conscient dune chose : je ne peux ici que guider vos premiers pas. Les frameworks ORM, mme limits strictement au standard JPA, couvrent un panel dapplications si vaste qu'il est presque impossible dtre exhaustif sur le sujet. Par ailleurs, bien que vous soyez arrivs jusquici, il va encore vous falloir acqurir de lexprience avant de pouvoir saisir pleinement toutes les possibilits offertes par ces solutions. En apart, voil pourquoi il est extrmement important mes yeux que vous assimiliez les concepts dans lordre, en dcouvrant et appliquant les bonnes pratiques au fur et mesure de votre apprentissage. V oil donc pourquoi je vous ai fait apprendre tape par tape depuis le dbut de ce cours : pour que vous ayez une exprience concrte de ce qui se passe en coulisses. Et voil pourquoi mme si je nai fait ici que vous mettre dans la bonne direction, je vous ai donn les cls ncessaires la bonne comprhension des principes mis en jeu.

V ous aurez trs vite loccasion den dcouvrir davantage avec la prochaine et dernire tape du fil rouge, mais il reste un nombre consquent dinformations importantes et doutils trs pratiques que nous nallons pas aborder ensemble. En voici quelques uns, pour vous mettre leau la bouche : avec un framework ORM, il est possible de gnrer automatiquement le code des entits depuis les tables dune base de donnes ! Encore moins de code crire, et donc moins de travail pour le dveloppeur. avec un framework ORM, il est possible de gnrer automatiquement les tables dune base de donnes depuis les entits dune application ! Eh oui, la rciproque est galement possible : si un dveloppeur prfre raliser un modle objet plutt quun modle de donnes, il lui est possible de ne pas avoir soccuper lui-mme de la cration des tables ! avec un framework ORM, la gestion des transactions est automatise. C'est un aspect que nous n'avons pas abord dans ce cours, mais qui entre trs vite en jeu dans une application un petit peu plus volue. avec un framework ORM,

ORM, ou ne pas ORM ?


Beaucoup dinterrogations et de dbats fusent sur la toile concernant le pour et le contre. Beaucoup de clichs, dinformations dpasses voire errones, et surtout un manque dinformation flagrant. Tentons de mettre au clair quand utiliser un ORM, et quand sen passer

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

505/606

Gnration des requtes


Si les requtes cres par une solution de persistance sont parfaites pour les actions de type CUD (Create, Update, Delete), elles sont plus discutables pour les requtes de type R (les SELECT et toutes leurs clauses et jointures ventuelles).

Perte de contrle sensible sur le SQL utilis pour communiquer avec la BDD
C'est un fait indiscutable : de par sa nature, un framework ORM masque les communications avec la BDD. S'il est possible de paramtrer finement une solution de persistance afin qu'elle produise des requtes SQL au plus proche de ce que l'on aurait fait la main, le jeu n'en vaut pas toujours la chandelle : il faut parfois passer plus de temps paramtrer le tout qu' le faire soi-mme. Certes, cette abstraction peut tre acceptable dans certains cas, mais elle peut faire rflchir sur de gros projets o les performances sont cruciales et dans le cadre desquels il faut optimiser au maximum les requtes effectues.

Fonctionnalits annexes, en dehors des bases de JPA


La plupart des frameworks existant fournissent des fonctionnalits intressantes n'existant pas (encore) dans le standard JPA, et permettent ainsi au dveloppeur de s'affranchir de certaines contraintes, souvent chronophages lorsqu'elles doivent tre prises en compte manuellement.

Envergure du projet
Indubitablement, sur un projet de grande taille, l'utilisation d'un framework ORM entrane un gain en temps de dveloppement important.

Contexte du projet
En rgle gnrale, si le modle de donnes d'un projet est prexistant, alors il est probablement plus sage de partir sans ORM. l'oppos, si le modle de donnes d'un projet change frquemment durant son dveloppement, alors partir sur un ORM offre un avantage considrable, avec la possibilit de re-gnrer entirement et automatiquement la couche daccs aux donnes.

Performances attendues
Dans des projets o la performance est la proccupation principale, il est plus sage de ne pas utiliser d'ORM. Je me permets ici un gros raccourci, mais dans ce contexte il est important de noter qu'un ORM nest pas rapide, et son tuning est complexe.

Possibilit de mixer avec et sans ORM


Dans l'absolu, il ne faut pas perdre de vue qu'il est possible d'utiliser JPA pour tout ce qui est simple et barbant - les actions de type CUD et certaines actions de lecture - et du SQL fait main pour le reste. Il suffit pour cela d'une couche d'abstraction, un DAO par exemple, et il devient alors ais de ne conserver que les avantages des deux solutions.

Solutions alternatives
Il ne faut pas perde de vue que d'autres moyens existent. C'est bien souvent une fausse bonne ide, mais il arrive que certaines grandes botes sur certains grand projets crent leur propre framework de persistance. En outre, des solutions plus lgres et transparentes comme MyBatis reprennent ce que les dveloppeurs apprcient dans un ORM, tout en conservant une transparence au niveau du SQL recherche par bon nombre d'entre eux. V oil les principales pistes que vous serez amens explorer si vous tes un jour amens vous poser la question "ORM, ou pas ORM ?". Si je ne devais utiliser qu'un seul argument pour aiguiller votre choix, je vous conseillerais de vous poser la question suivante : est-ce que vous souhaitez passer une grande partie de votre temps sur la couche daccs aux donnes, ou est-ce que vous prfrez consacrer votre temps et votre nergie dvelopper une couche mtier efficace ? Enfin pour terminer et faire un apart sur votre apprentissage, sachez que des trs petits projets comme nous en crons dans le cadre de ce cours sont des opportunits en or pour apprendre calmement et efficacement comment fonctionne une solution de persistance. N'attendez surtout pas d'tre parachuts sur des projets immenses ou mme simplement

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

506/606

dj bien entams, sans quoi vous allez passer un temps fou rellement saisir tout ce qui mrite d'tre assimil.

Nous allons nous arrter ici en ce qui concerne la persistance de donnes. Cest la fois trop et pas assez : trop, parce que beaucoup de concepts intervenant dans la bonne apprhension dun tel systme sont inconnus des novices, et pas assez parce que mme dbutants, je suis certains que vous souhaitez en apprendre avantage sur cet aspect du dveloppement ! Rassurez-vous, je ne vous abandonne pas en cours de route : comme je vous lai dj expliqu, avec ce que vous avez appris dans ce chapitre - que vous allez par ailleurs mettre en pratique et complter dans un cas plus complexe dans ltape suivante du TP Fil rouge - vous avez les notions ncessaires pour voler de vos propres ailes ! Sans aucun doute, pour tre complet sur le sujet, il faudrait un cours part entire.

En rsum
JPA est un standard appartenant Java EE, dfinissant un systme de persistance de donnes. Hibernate, EclipseLink et consorts sont des frameworks ORM, des solutions qui implmentent ce standard. JPA masque intgralement le moyen de stockage au dveloppeur, en lui permettant de travailler uniquement sur un modle objet. C'est le framework qui se charge dans les coulisses de dialoguer avec la BDD pour assurer la correspondance avec le modle. L' objet qui reprsente une table de la BDD est un EJB de type Entity . C'est un JavaBean dclar auprs du conteneur par @Entity, et contenant un ensemble d'annotations sur ses proprits, permettant de dfinir les contraintes et relations existantes. L'objet qui fournit des mthode d'interaction avec la BDD est l'EntityManager, et il est entirement gr par le conteneur. Le fichier de configuration de JPA se nomme persistance.xml , et doit tre plac dans le rpertoire src/META-INF. C'est un bonne pratique d'utiliser un EJB Stateless en guise de DAO. Il suffit alors d'y injecter l'EntityManager via @PersistenceContext, et d'appeler ses diffrentes mthodes d'interaction. Contrairement un EJB Stateful , un EJB Stateless peut tre inject sans risques dans une servlet via @EJB. Les objets et EJB injects sont entirement grs par le conteneur, le dveloppeur ne fait que les utiliser. Le dtail des requtes SQL effectues sur la BDD par le framework ORM peut tre enregistr dans le fichier de logs du serveur, en activant le mode logging dans le fichier persistence.xml de l'application. Un framework ORM n'est ni une solution magique et rponse tout, ni une machinerie lourde et inutilisable : une analyse rapide du contexte d'un projet permet de dfinir s'il est intressant ou non d'y faire intervenir une telle solution.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

507/606

TP Fil rouge - tape 7


Septime et dernire tape du fil rouge : vous allez pouvoir pratiquer JPA dans un contexte un peu plus compliqu que celui du cours, car faisant intervenir un modle de donnes plus complexe apprhender. V ous en profiterez galement pour travailler avec vos nouveaux outils : GlassFish et BoneCP !

Objectifs Fonctionnalits
V ous tes quips pour refondre votre TP en utilisant JPA. V ous allez devoir : migrer votre projet sur GlassFish ; mettre en place un pool de connexions pour votre projet avec BoneCP ; reprendre le code existant pour y mettre en place les annotations, configurations et classes ncessaires au bon fonctionnement de JPA supprimer les classes devenues obsoltes.

En apparence, tout cela parat bien lger, mais vous allez faire face quelques petits obstacles qui vous obligeront chercher, et assimiler un peu mieux encore comment fonctionne JPA. Bon courage, et ne flanchez pas dans cette dernire ligne droite !

Conseils Environnement de dveloppement


Contexte du projet
V ous allez pouvoir rutiliser le serveur GlassFish mis en place dans le cadre du cours pour dployer votre projet. Faites une duplication de la correction du projet telle qu'elle tait l'issue de l'tape 6, afin de conserver des sources "propres". Pour migrer le projet vers votre nouveau serveur, vous devrez ensuite vous rendre dans le build-path du projet dupliqu, et modifier les points suivants : dans l'onglet "Libraries", remplacez celle qui mentionne Tomcat dans la liste affiche par celle de GlassFish en suivant Add Library > Server Runtime, puis en choisissant votre instance de GlassFish ; l'entre intitule "Targeted runtimes" dans les proprits de votre projet ; et enfin l'entre intitule "Server" toujours dans les proprits de votre projet.

Configuration du pool
V ous profiterez de l'occasion pour vous exercer mettre en place un pool de connexions BoneCP sous GlassFish et l'utiliser. Pour cela, vous pouvez essayer de modifier le fichier XML que je vous avais prpar dans le cadre du cours, afin qu'il cible cette fois non plus la base bdd_sdzee, mais votre base tp_sdzee. Sinon, je vous ai prpar un fichier prt l'emploi que vous pouvez tlcharger en cliquant ici. Pour le reste de la manipulation, vous pouvez vous reporter au cours si vous ne vous souvenez plus.

Configuration de l'application
Une fois le contexte en place, vous devrez ensuite crer un fichier WEB-INF/glassfish-web.xml , et un fichier src/METAINF/persistence.xml , comme nous l'avons fait dans le cours. Reportez vous au chapitre prcdent si vous avez des doutes.

Reprise du code existant


Suppression des objets devenus obsoltes
V ous allez pouvoir ds prsent vous dbarrasser de la DAOFactory, du Listener l'initalisant et des utilitaires DAO.

Transformation des JavaBeans en EJB Entity


V ous allez ensuite devoir insrer des annotations JPA dans vos beans, comme nous l'avons fait dans le cours.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

508/606

Toutefois, vous allez vite vous rendre compte qu'un champ est diffrent des autres : celui qui correspond une cl trangre dans la table Commande. Pour le grer correctement, il y a une particularit prendre en compte : il va falloir prciser au conteneur quel type de relation existe entre le champ et la table cible par la cl. V ous pouvez chercher par vous-mmes si vous vous en sentez capables, ou suivre les indications suivantes autrement. Il existe plusieurs types de relation entre des champs de tables relationnelles : un un, un plusieurs, ou plusieurs un. Ceci relevant du design de la BDD et clairement pas du dveloppement Java EE, je ne vous demande pas de comprendre exactement de quoi il est question ici. En l'occurrence, dans notre TP nous avons affaire une relation de type plusieurs un, car plusieurs entres de la table Commande peuvent pointer vers un mme Client. Il faut donc utiliser une annotation JPA prvue cet effet, nomme @ManyToOne. En complment, il faut galement expliciter le champ de la table vis par la cl trangre, via l'annotation nomme @JoinColumn( name = "..." ). Autre petite difficult, le fait que nous utilisions un champ de type DateTime dans notre entit Commande. Les dveloppeurs de la bibliothque JodaTime ont pris la peine d'crire une classe pour convertir une tel objet vers une date utilisable par le framework Hibernate, une solution ORM trs rpandue, mais ils n'ont pas fait le travail pour EclipseLink , qui est pourtant le framework utilis par dfaut par GlassFish. Ainsi, vous allez devoir vous occuper vous-mme de la conversion... Pas d'inquitude cependant, car ce petit dveloppement n'est pas l'objectif de ce TP. Ainsi, je vous propose une classe prte l'emploi, ralise par Xandacona et fournie sur cette page de son blog : Code : Java - Conversion d'un objet DateTime pour EclipseLink package com.sdzee.tp.tools; import java.sql.Timestamp; import import import import org.eclipse.persistence.mappings.DatabaseMapping; org.eclipse.persistence.mappings.converters.Converter; org.eclipse.persistence.sessions.Session; org.joda.time.DateTime;

public class JodaDateTimeConverter implements Converter { private static final long serialVersionUID = 1L; @Override public Object convertDataValueToObjectValue( Object dataValue, Session session ) { return dataValue == null ? null : new DateTime( (Timestamp) dataValue ); } @Override public Object convertObjectValueToDataValue( Object objectValue, Session session ) { return objectValue == null ? null : new Timestamp( ( (DateTime) objectValue ).getMillis() ); } @Override public void initialize( DatabaseMapping mapping, Session session } @Override public boolean isMutable() { return false; } }

) {

Dposez cette classe nomme JodaDateTimeConverter dans un nouveau package intitul com.sdzee.tp.tools, et vous pourrez ensuite l'aide des annotations @Converter et @Convert prciser votre conteneur comment effectuer la conversion de manire automatise. L encore pas de panique, je vous montre comment annoter l'attribut date dans votre entit Commande : Code : Java

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

509/606

@Column( columnDefinition = "TIMESTAMP" ) @Converter( name = "dateTimeConverter", converterClass = JodaDateTimeConverter.class ) @Convert( "dateTimeConverter" ) private DateTime date;

Transformation des DAO en EJB Session Stateless


Annotations, EntityManager et mthodes d'accs la BDD sont au programme. Cette fois par contre, la diffrence du cours, puisqu'aucune requte de slection complexe n'est ralise, nous avons uniquement besoin de recherches bases sur les cl primaire de nos tables (leurs id). Ainsi, il ne vous sera pas ncessaire d'crire de requtes JPQL la main, vous pourrez utiliser simplement la mthode find() de votre EntityManager. V ous voil enfin dbarrasss du SQL !

Modification des servlets


Dans les servlets qui manipulaient un DAO auparavant (pour la cration de clients et de commandes), vous allez devoir supprimer les rfrences aux anciens composants (DAOFactory), injecter vos EJB Stateless et les transmettre aux objets mtier en lieu et place des DAO.

Annotation des servlets et des filtres


V ous allez pouvoir reprendre toutes les servlets et les annoter avec @WebServlet. De mme pour le filtre de prchargement avec @WebFilter. Enfin, n'oubliez pas les servlets ncessitant une configuration multipart ou l'ajout de paramtre d'initialisation, avec les annotations @MultipartConfig et @WebInitParam. Une fois toutes ces modifications apportes, vous pourrez vous dbarrasser du fichier web.xml !

Modification des objets mtier


Lorsque nous avons utilis pour la premire fois une requte de type MultiPart, je vous avais averti que GlassFish ne respectait pas correctement la spcification Java EE, et ne permettait pas d'appeler directement la mthode request.getParameter() pour obtenir un paramtre classique contenu dans une requte de type MultiPart. Heureusement pour nous, la dernire version du serveur - la 3.1.2.2 que je vous ai fait tlcharger dans le chapitre prcdent - a corrig ce lger souci, et il est dsormais possible de raliser ce genre d'appels. Autrement dit, vous n'avez rien changer dans vos objets mtiers !

Correction
Faites attention bien reprendre les fichiers du cours qui restent inchangs, corriger les classes qui ncessitent des ajustements, et supprimer celles qui ne vous servent plus rien. Et surtout ne baissez pas les bras devant la charge de travail implique par l'intgration de JPA dans votre application ! Cet exercice est l'occasion parfaite pour vous familiariser avec les bases de ces concepts avancs ! Une fois n'est pas coutume, ce n'est pas la seule manire de faire, le principal est que votre solution respecte les consignes que je vous ai donnes ! Prenez le temps de rflchir, de chercher et coder par vous-mmes. Si besoin, n'hsitez pas relire le sujet ou retourner lire les prcdents chapitres. La pratique est trs importante, ne vous ruez pas sur la solution !

Le code de configuration
Secret (cliquez pour afficher) Code : XML - WEB-INF/glassfish-web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app> <context-root>/tp7</context-root> <class-loader delegate="true"/> <jsp-config> <property name="keepgenerated" value="true"> <description>Keep a copy of the generated servlet class java code.</description> </property> </jsp-config> </glassfish-web-app>

510/606

Code : XML - src/META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="tp_sdzee_PU" transaction-type="JTA"> <jta-data-source>jdbc/bonecp_resource_tp</jta-data-source> <class>com.sdzee.entities.Client</class> <class>com.sdzee.entities.Commande</class> <properties/> </persistence-unit> </persistence>

Le code des EJB Entity


Secret (cliquez pour afficher) Code : Java - com.sdzee.tp.entities.Client package com.sdzee.tp.entities; import java.io.Serializable; import import import import javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id;

@Entity public class Client implements Serializable { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private Long id; private String nom; private String prenom; private String adresse; private String telephone; private String email; private String image; public void setId( Long id ) { this.id = id;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


} public Long getId() { return id; } public void setNom( String nom ) { this.nom = nom; } public String getNom() { return nom; } public void setPrenom( String prenom ) { this.prenom = prenom; } public String getPrenom() { return prenom; } public void setAdresse( String adresse ) { this.adresse = adresse; } public String getAdresse() { return adresse; } public void setTelephone( String telephone ) { this.telephone = telephone; } public String getTelephone() { return telephone; } public void setEmail( String email ) { this.email = email; } public String getEmail() { return email; } public void setImage( String image ) { this.image = image; } public String getImage() { return image; }

511/606

Code : Java - com.sdzee.tp.entities.Commande package com.sdzee.tp.entities; import java.io.Serializable; import import import import import import javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.JoinColumn;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import org.eclipse.persistence.annotations.Convert; import org.eclipse.persistence.annotations.Converter; import org.joda.time.DateTime; import com.sdzee.tp.tools.JodaDateTimeConverter; @Entity public class Commande implements Serializable { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private Long id; @ManyToOne @JoinColumn( name = "id_client" ) private Client client; @Column( columnDefinition = "TIMESTAMP" ) @Converter( name = "dateTimeConverter", converterClass = JodaDateTimeConverter.class ) @Convert( "dateTimeConverter" ) private DateTime date; private Double montant; @Column( name = "mode_paiement" ) private String modePaiement; @Column( name = "statut_paiement" ) private String statutPaiement; @Column( name = "mode_livraison" ) private String modeLivraison; @Column( name = "statut_livraison" ) private String statutLivraison; public Long getId() { return id; } public void setId( Long id ) { this.id = id; } public Client getClient() { return client; } public void setClient( Client client ) { this.client = client; } public DateTime getDate() { return date; } public void setDate( DateTime date ) { this.date = date; } public Double getMontant() { return montant; } public void setMontant( Double montant ) { this.montant = montant; } public String getModePaiement() { return modePaiement; } public void setModePaiement( String modePaiement ) { this.modePaiement = modePaiement;

512/606

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


} public String getStatutPaiement() { return statutPaiement; } public void setStatutPaiement( String statutPaiement ) { this.statutPaiement = statutPaiement; } public String getModeLivraison() { return modeLivraison; } public void setModeLivraison( String modeLivraison ) { this.modeLivraison = modeLivraison; } public String getStatutLivraison() { return statutLivraison; } public void setStatutLivraison( String statutLivraison ) { this.statutLivraison = statutLivraison; }

513/606

Le code des EJB Session


Secret (cliquez pour afficher) Code : Java - com.sdzee.tp.dao.ClientDao package com.sdzee.tp.dao; import java.util.List; import import import import javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.PersistenceContext; javax.persistence.TypedQuery;

import com.sdzee.tp.entities.Client; @Stateless public class ClientDao { BDD // Injection du manager, qui s'occupe de la connexion avec la @PersistenceContext( unitName = "tp_sdzee_PU" ) private EntityManager em; public Client trouver( long id ) throws DAOException { try { return em.find( Client.class, id ); } catch ( Exception e ) { throw new DAOException( e ); } } public void creer( Client client ) throws DAOException {

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


try { em.persist( client ); } catch ( Exception e ) { throw new DAOException( e ); }

514/606

public List<Client> lister() throws DAOException { try { TypedQuery<Client> query = em.createQuery( "SELECT c FROM Client c ORDER BY c.id", Client.class ); return query.getResultList(); } catch ( Exception e ) { throw new DAOException( e ); } } public void supprimer( Client client ) throws DAOException { try { em.remove( em.merge( client ) ); } catch ( Exception e ) { throw new DAOException( e ); } }

Code : Java - com.sdzee.tp.dao.CommandeDao package com.sdzee.tp.dao; import java.util.List; import import import import javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.PersistenceContext; javax.persistence.TypedQuery;

import com.sdzee.tp.entities.Commande; @Stateless public class CommandeDao { BDD // Injection du manager, qui s'occupe de la connexion avec la @PersistenceContext( unitName = "tp_sdzee_PU" ) private EntityManager em; public Commande trouver( long id ) throws DAOException { try { return em.find( Commande.class, id ); } catch ( Exception e ) { throw new DAOException( e ); } } public void creer( Commande commande ) throws DAOException { try { em.persist( commande ); } catch ( Exception e ) { throw new DAOException( e ); } } public List<Commande> lister() throws DAOException { try { TypedQuery<Commande> query = em.createQuery( "SELECT c FROM Commande c ORDER BY c.id", Commande.class );

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


FROM Commande c ORDER BY c.id", Commande.class ); return query.getResultList(); } catch ( Exception e ) { throw new DAOException( e ); } } { public void supprimer( Commande commande ) throws DAOException try { em.remove( em.merge( commande ) ); } catch ( Exception e ) { throw new DAOException( e ); }

515/606

Le code des servlets


Secret (cliquez pour afficher) Code : Java - com.sdzee.tp.servlets.CreationClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.MultipartConfig; javax.servlet.annotation.WebInitParam ; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.dao.ClientDao; import com.sdzee.tp.entities.Client; import com.sdzee.tp.forms.CreationClientForm ; @WebServlet( urlPatterns = { "/creationClient" }, initParams = @WebInitParam( name = "chemin", value = "/fichiers/images/" ) ) @MultipartConfig( location = "/tmp", maxFileSize = 2 * 1024 * 1024, maxRequestSize = 10 * 1024 * 1024, fileSizeThreshold = 1024 * 1024 ) public class CreationClient extends HttpServlet { public static final String CHEMIN = "chemin"; public static final String ATT_CLIENT = "client"; public static final String ATT_FORM = "form"; public static final String SESSION_CLIENTS = "clients"; public static final String VUE_SUCCES INF/afficherClient.jsp"; public static final String VUE_FORM INF/creerClient.jsp"; @EJB private ClientDao clientDao; = "/WEB= "/WEB-

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Prparation de l'objet formulaire */ CreationClientForm form = new CreationClientForm( clientDao ); /* Traitement de la requte et rcupration du bean en rsultant */ Client client = form.creerClient( request, chemin ); */ /* Ajout du bean et de l'objet mtier l'objet requte request.setAttribute( ATT_CLIENT, client ); request.setAttribute( ATT_FORM, form );

516/606

/* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<Long, Client>(); } /* Puis ajout du client courant dans la map */ clients.put( client.getId(), client ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Code : Java - com.sdzee.tp.servlets.CreationCommande package com.sdzee.tp.servlets;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import import import import import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.MultipartConfig; javax.servlet.annotation.WebInitParam ; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.CommandeDao; com.sdzee.tp.entities.Client; com.sdzee.tp.entities.Commande; com.sdzee.tp.forms.CreationCommandeForm ;

517/606

@WebServlet( urlPatterns = { "/creationCommande" }, initParams = @WebInitParam( name = "chemin", value = "/fichiers/images/" ) ) @MultipartConfig( location = "/tmp", maxFileSize = 2 * 1024 * 1024, maxRequestSize = 10 * 1024 * 1024, fileSizeThreshold = 1024 * 1024 ) public class CreationCommande extends HttpServlet { public static final String CHEMIN = "chemin"; public static final String ATT_COMMANDE = "commande"; public static final String ATT_FORM = "form"; public static final String SESSION_CLIENTS = "clients"; public static final String APPLICATION_CLIENTS = "initClients"; public static final String SESSION_COMMANDES = "commandes"; public static final String APPLICATION_COMMANDES = "initCommandes"; public static final String VUE_SUCCES INF/afficherCommande.jsp"; public static final String VUE_FORM INF/creerCommande.jsp"; @EJB private ClientDao @EJB private CommandeDao clientDao; commandeDao; = "/WEB= "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, simple affichage du formulaire */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( CHEMIN ); /* Prparation de l'objet formulaire */ CreationCommandeForm form = new CreationCommandeForm( clientDao, commandeDao );

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


/* Traitement de la requte et rcupration du bean en rsultant */ Commande commande = form.creerCommande( request, chemin ); */ /* Ajout du bean et de l'objet mtier l'objet requte request.setAttribute( ATT_COMMANDE, commande ); request.setAttribute( ATT_FORM, form );

518/606

/* Si aucune erreur */ if ( form.getErreurs().isEmpty() ) { /* Alors rcupration de la map des clients dans la session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( clients == null ) { clients = new HashMap<Long, Client>(); } /* Puis ajout du client de la commande courante dans la map */ clients.put( commande.getClient().getId(), commande.getClient() ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_CLIENTS, clients ); /* Ensuite rcupration de la map des commandes dans la session */ Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si aucune map n'existe, alors initialisation d'une nouvelle map */ if ( commandes == null ) { commandes = new HashMap<Long, Commande>(); } /* Puis ajout de la commande courante dans la map */ commandes.put( commande.getId(), commande ); /* Et enfin (r)enregistrement de la map en session */ session.setAttribute( SESSION_COMMANDES, commandes ); /* Affichage de la fiche rcapitulative */ this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response ); } else { /* Sinon, r-affichage du formulaire de cration avec les erreurs */ this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response ); } } }

Code : Java - com.sdzee.tp.servlets.Image package com.sdzee.tp.servlets; import import import import import import java.io.BufferedInputStream ; java.io.BufferedOutputStream ; java.io.File; java.io.FileInputStream ; java.io.IOException; java.net.URLDecoder;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import import import import import import javax.servlet.ServletException; javax.servlet.annotation.WebInitParam ; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

519/606

@WebServlet( urlPatterns = { "/images/*" }, initParams = @WebInitParam( name = "chemin", value = "/fichiers/images/" ) ) public class Image extends HttpServlet { public static final int TAILLE_TAMPON = 10240; // 10ko public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* * Lecture du paramtre 'chemin' pass la servlet via la dclaration * dans le web.xml */ String chemin = this.getServletConfig().getInitParameter( "chemin" ); /* * Rcupration du chemin du fichier demand au sein de l'URL de la * requte */ String fichierRequis = request.getPathInfo(); /* Vrifie qu'un fichier a bien t fourni */ if ( fichierRequis == null || "/".equals( fichierRequis )

) {

/* * Si non, alors on envoie une erreur 404, qui signifie que la * ressource demande n'existe pas */ response.sendError( HttpServletResponse.SC_NOT_FOUND ); return; } /* * Dcode le nom de fichier rcupr, susceptible de contenir des * espaces et autres caractres spciaux, et prpare l'objet File */ fichierRequis = URLDecoder.decode( fichierRequis, "UTF-8" ); File fichier = new File( chemin, fichierRequis ); /* Vrifie que le fichier existe bien */ if ( !fichier.exists() ) { /* * Si non, alors on envoie une erreur 404, qui signifie que la * ressource demande n'existe pas */ response.sendError( HttpServletResponse.SC_NOT_FOUND ); return; } /* Rcupre le type du fichier */ String type = getServletContext().getMimeType( fichier.getName() ); /* * Si le type de fichier est inconnu, alors on initialise un type par * dfaut

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


*/ if ( type == null ) { type = "application/octet-stream"; }

520/606

/* Initialise la rponse HTTP */ response.reset(); response.setBufferSize( TAILLE_TAMPON ); response.setContentType( type ); response.setHeader( "Content-Length", String.valueOf( fichier.length() ) ); response.setHeader( "Content-Disposition", "inline; filename=\"" + fichier.getName() + "\"" ); /* Prpare les flux */ BufferedInputStream entree = null; BufferedOutputStream sortie = null; try { /* Ouvre les flux */ entree = new BufferedInputStream( new FileInputStream( fichier ), TAILLE_TAMPON ); sortie = new BufferedOutputStream( response.getOutputStream(), TAILLE_TAMPON ); /* Lit le fichier et crit son contenu dans la rponse HTTP */ byte[] tampon = new byte[TAILLE_TAMPON]; int longueur; while ( ( longueur = entree.read( tampon ) ) > 0 ) { sortie.write( tampon, 0, longueur ); } } finally { try { sortie.close(); } catch ( IOException ignore ) { } try { entree.close(); } catch ( IOException ignore ) { } } } }

Code : Java - com.sdzee.tp.servlets.ListeClients package com.sdzee.tp.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

@WebServlet( urlPatterns = { "/listeClients" } ) public class ListeClients extends HttpServlet { public static final String ATT_CLIENT = "client"; public static final String ATT_FORM = "form"; public static final String VUE INF/listerClients.jsp"; = "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


IOException { /* la rception d'une requte GET, affichage de la liste des clients */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

521/606

Code : Java - com.sdzee.tp.servlets.ListeCommandes package com.sdzee.tp.servlets; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

@WebServlet( urlPatterns = { "/listeCommandes" } ) public class ListeCommandes extends HttpServlet { public static final String ATT_COMMANDE = "commande"; public static final String ATT_FORM = "form"; public static final String VUE INF/listerCommandes.jsp"; = "/WEB-

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* la rception d'une requte GET, affichage de la liste des commandes */ this.getServletContext().getRequestDispatcher( VUE ).forward( request, response ); } }

Code : Java - com.sdzee.tp.servlets.SuppressionClient package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession;

import com.sdzee.tp.dao.ClientDao; import com.sdzee.tp.dao.DAOException; import com.sdzee.tp.entities.Client; @WebServlet( urlPatterns = { "/suppressionClient" } ) public class SuppressionClient extends HttpServlet { public static final String PARAM_ID_CLIENT = "idClient"; public static final String SESSION_CLIENTS = "clients"; public static final String VUE = "/listeClients";

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


@EJB private ClientDao

522/606

clientDao;

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String idClient = getValeurParametre( request, PARAM_ID_CLIENT ); Long id = Long.parseLong( idClient ); /* Rcupration de la Map des clients enregistrs en session */ HttpSession session = request.getSession(); Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS ); vides */ /* Si l'id du client et la Map des clients ne sont pas

if ( id != null && clients != null ) { try { /* Alors suppression du client de la BDD */ clientDao.supprimer( clients.get( id ) ); /* Puis suppression du client de la Map */ clients.remove( id ); } catch ( DAOException e ) { e.printStackTrace(); } /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_CLIENTS, clients ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

Code : Java - com.sdzee.tp.servlets.SuppressionCommande package com.sdzee.tp.servlets; import java.io.IOException; import java.util.HashMap; import java.util.Map; import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.sdzee.tp.dao.CommandeDao; import com.sdzee.tp.dao.DAOException; import com.sdzee.tp.entities.Commande; @WebServlet( urlPatterns = { "/suppressionCommande" } ) public class SuppressionCommande extends HttpServlet { public static final String PARAM_ID_COMMANDE = "idCommande"; public static final String SESSION_COMMANDES = "commandes"; public static final String VUE "/listeCommandes"; @EJB private CommandeDao commandeDao; =

523/606

public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { /* Rcupration du paramtre */ String idCommande = getValeurParametre( request, PARAM_ID_COMMANDE ); Long id = Long.parseLong( idCommande ); /* Rcupration de la Map des commandes enregistres en session */ HttpSession session = request.getSession(); Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES ); /* Si l'id de la commande et la Map des commandes ne sont pas vides */ if ( id != null && commandes != null ) { try { /* Alors suppression de la commande de la BDD */ commandeDao.supprimer( commandes.get( id ) ); /* Puis suppression de la commande de la Map */ commandes.remove( id ); } catch ( DAOException e ) { e.printStackTrace(); } /* Et remplacement de l'ancienne Map en session par la nouvelle */ session.setAttribute( SESSION_COMMANDES, commandes ); } /* Redirection vers la fiche rcapitulative */ response.sendRedirect( request.getContextPath() + VUE );

/* * Mthode utilitaire qui retourne null si un paramtre est vide, et son * contenu sinon. */ private static String getValeurParametre( HttpServletRequest request, String nomChamp ) { String valeur = request.getParameter( nomChamp ); if ( valeur == null || valeur.trim().length() == 0 ) { return null; } else { return valeur; } } }

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

524/606

Le code du filtre
Secret (cliquez pour afficher) Code : Java - com.sdzee.tp.filters.PrechargementFilter package com.sdzee.tp.filters; import import import import import import import import import import import import import import import import import import java.io.IOException; java.util.HashMap; java.util.List; java.util.Map; javax.ejb.EJB; javax.servlet.Filter; javax.servlet.FilterChain; javax.servlet.FilterConfig; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.annotation.WebFilter; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpSession; com.sdzee.tp.dao.ClientDao; com.sdzee.tp.dao.CommandeDao; com.sdzee.tp.entities.Client; com.sdzee.tp.entities.Commande;

@WebFilter( urlPatterns = { "/*" } ) public class PrechargementFilter implements Filter { public static final String ATT_SESSION_CLIENTS = "clients"; public static final String ATT_SESSION_COMMANDES = "commandes"; @EJB private ClientDao @EJB private CommandeDao clientDao; commandeDao;

public void init( FilterConfig filterConfig ) throws ServletException { } public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException, ServletException { /* Cast de l'objet request */ HttpServletRequest request = (HttpServletRequest) req; /* Rcupration de la session depuis la requte */ HttpSession session = request.getSession(); /* * Si la map des clients n'existe pas en session, alors l'utilisateur se * connecte pour la premire fois et nous devons prcharger en session * les infos contenues dans la BDD. */ if ( session.getAttribute( ATT_SESSION_CLIENTS ) == null ) { /* * Rcupration de la liste des clients existant, et enregistrement

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


* en session */ Client>();

525/606

List<Client> listeClients = clientDao.lister(); Map<Long, Client> mapClients = new HashMap<Long, for ( Client client : listeClients ) { mapClients.put( client.getId(), client ); } session.setAttribute( ATT_SESSION_CLIENTS, mapClients

);

/* * De mme pour la map des commandes */ if ( session.getAttribute( ATT_SESSION_COMMANDES ) == null ) { /* * Rcupration de la liste des commandes existant, et * enregistrement en session */ List<Commande> listeCommandes = commandeDao.lister(); Map<Long, Commande> mapCommandes = new HashMap<Long, Commande>(); for ( Commande commande : listeCommandes ) { mapCommandes.put( commande.getId(), commande ); } session.setAttribute( ATT_SESSION_COMMANDES, mapCommandes ); } /* Pour terminer, poursuite de la requte en cours */ chain.doFilter( request, res );

public void destroy() { }

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

526/606

Introduction aux frameworks MVC


Nous avons appris limiter nos efforts sur la couche d'accs aux donnes avec JPA, nous allons maintenant dcouvrir comment allger notre charge de travail sur les couches vue et contrleur grce aux frameworks MVC ! Au programme de ce chapitre d'introduction, des rappels sur le pattern MVC, des gnralits sur le concept de frameworks et sur leur intrt, et le point sur le march actuel des solutions existantes.

Gnralits Rappel concernant MVC


Revenons l'espace d'un court instant sur les objectifs du pattern MVC. Il s'agit comme vous le savez tout maintenant d'un modle de conception, une bonne pratique qui dcrit comment le code d'une application doit tre organis. Dans une application Java EE, il dcoupe littralement le code en plusieurs couches, chacune tant charge d'assurer des tches bien dfinies : nous n'allons pas disserter davantage sur ce sujet, nous avons mis tout cela en pratique depuis le dbut du cours et vous connaissez dj tous les composants constituant le modle, le contrleur et la vue. Les principaux avantages d'un tel pattern sont vidents : la clart introduite par un dcoupage clair et surtout standard des diffrentes sections d'une application permet une maintenance du code bien plus aise que si le code ne respectait aucune rgle prtablie. C'est cette quasi-universalit du mode de dveloppement qui confre MVC son intrt le plus consquent ; le dcoupage, et donc l'isolement des diffrentes tches au sein d'une application, permet une meilleure rpartition du travail entre les diffrents profils de dveloppeurs. Le cas le plus souvent mis en avant est celui du designer web, qui peut ainsi ne s'occuper que de la vue sans avoir se soucier de ce qui se passe derrire, ni mme de comment cela se passe. ces avantages s'oppose toutefois un inconvnient majeur : puisqu'il y a ncessit de dlimiter clairement les couches d'une application, il faut fatalement crire davantage de code, et la structure ainsi dfinie impose des rptitions de code frquentes. Est-il besoin de le prciser, qui dit plus de code dit dveloppement plus lent, risque d'erreurs plus grand, etc.

Qu'est-ce qu'un framework MVC ?


Comme vous le savez tous, la brique de base de la plate-forme Java EE est la servlet. Tout passe par elle, et mme nos pages JSP sont transformes en servlets derrire les rideaux. Eh bien un framework MVC n'est rien d'autre qu'une surcouche cette technologie de base. En masquant ces rouages les plus basiques, une telle solution facilite le dcoupage et la gestion du code d'une application. Elle intervient sur le cycle de vie d'une requte dans l'application, et prend en quelque sorte la main sur son cheminement : voil pourquoi on qualifie souvent les frameworks MVC de frameworks d'inversion de contrle, parfois abrg IoC.

quels besoins rpond-il ?


Nous avons d'ores et dj rpondu cette question en listant les avantages et inconvnients principaux du pattern MVC : l'objectif premier d'un framework MVC et de simplifier le flot d'excution (ou workflow ) d'une application, c'est--dire de : limiter les rptitions de code impliques par la mise en place d'un dcoupage du code ; raliser une partie du travail redondant la place du dveloppeur, en effectuant des tches gnriques derrire les rideaux, de manire transparente ; contraindre le dveloppeur respecter une organisation clairement dfinie. Alors qu'il reste libre d'implmenter comme bon lui semble le pattern MVC lorsqu'il travaille la main, il doit se conformer un format plus ou moins strict lorsqu'il fait usage d'un framework ; par corollaire du premier point, rendre le dveloppement de certains aspects plus rapide, permettant ainsi au dveloppeur de se concentrer sur le coeur de l'application.

Quand utiliser un framework MVC, et quand s'en passer ?


Avant de nous pencher sur les diffrents types de frameworks MVC existant, rflchissons la question suivante : qu'est-ce qui justifie l'utilisation d'un framework MVC dans une application ? Nous venons certes de dcouvrir les besoins auxquels il rpond, mais ce n'est pas pour autant qu'il est toujours judicieux d'en utiliser un. Ainsi mme si l'intuition nous incite considrer qu'un framework ne peut apporter que du bon dans un projet, voici les principaux axes de rflexion explorer :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

527/606

l'envergure du projet : si l'application dveloppe est relativement petite, il n'est probablement pas ncessaire de mettre en place un framework , et le dveloppement MVC " la main" peut convenir ; le temps d'apprentissage : si vous ou votre quipe n'avez que peu de connaissances sur un framework MVC, alors il faut peser le pour et le contre entre le temps gagn durant le dveloppement grce son utilisation, et le temps perdu en amont pour apprendre et assimiler la technologie ; le contexte du projet : si des contraintes sont formules au niveau du serveur et des composants utilisables par un projet, et ventuellement au niveau des performances ou du niveau de scalabilit attendus, alors il faut dterminer si l'utilisation d'un framework rentre dans ce cadre ou non, et vrifier qu'il respecte bien les contraintes nonces.

Framework MVC bas sur les requtes Dfinition


La premire grande catgorie de frameworks MVC qualifie ceux qui se basent sur les requtes , on parle galement de frameworks bas sur les actions . C'est le type de frameworks MVC le plus simple comprendre et assimiler par les dveloppeurs qui ont dj de l'exprience avec le dveloppement Java EE en suivant MVC, car il reprend dans les grandes lignes sensiblement les mmes principes. Ce sont des solutions qui interviennent directement sur le cycle de vie d'une requte au sein de l'application (on rejoint ici cette histoire de "prise de contrle"). Il serait ncessaire de nous y attarder plus longuement si nous n'avions jamais touch MVC, mais puisque nous en avons fait le fer de lance de notre parcours, nous savons dj trs bien de quoi il est question ici !

Principe
L encore, le principe est extrmement simple comprendre pour quiconque ayant dj travaill avec MVC : il s'agit d'un framework web qui prend en entre une requte issue d'un client, qui dtermine ce que le serveur doit en faire et qui renvoie enfin au client une rponse en consquence. Le flot d'excution peut donc tre qualifi de linaire, et reprend ce que nous avons jusqu' prsent mis en place la main dans nos exemples. Ainsi, le dveloppeur doit penser en termes d'actions, et associe naturellement ce que demande l'utilisateur (sa requte) ce que l'utilisateur reoit (la rponse). Concrtement, le dveloppeur crit des classes qui reprsentent des actions effectues par l'utilisateur, comme par exemple "Passer une commande" ou "V oir les dtails du client". Ces classes sont charges de rcuprer les donnes transmises via la requte HTTP, et de travailler dessus. En somme, avec un tel framework le dveloppeur travaille toujours de prs ou de loin sur la paire requte/rponse, comme nous l'avons fait jusqu' prsent. Le gros changement noter, mais nous y reviendrons plus tard, est la mise en place d'une servlet unique jouant le rle d'aiguilleur gant (ou Front Controller), qui se charge de dlguer au modle mtier en se basant sur l'URL de la requte et de ses paramtres. Le dveloppeur peut alors travailler directement sur les objets HttpServletRequest et HttpServletResponse bruts dans le modle, ou bien utiliser le systme de mapping fourni par le framework . Ce systme permet au dveloppeur de confier au framework les tches de regroupement, conversion et validation des paramtres de requte, et si ncessaire de mise jour des valeurs du modle donnes, avant d'invoquer les actions mtier. Enfin, il doit toujours crire lui-mme les pages - bien souvent des pages JSP - en charge de crer les rponses renvoyer au client, et il jouit donc d'une libert totale sur le rendu HTML/CSS/JS de chaque vue.

Solutions existantes
Trois grans acteurs se partagent le devant de la scne : Spring, solution trs rpandue dont le spectre s'tend de la simple gestion des requtes l'ensemble du cycle de vie de l'application, bien souvent couple Hibernate pour la persistance des donnes ; Struts, solution dite par Apache dont la gloire appartient au pass. Le framework a chang du tout au tout avec la sortie d'une seconde mouture qui se nomme Struts 2, mais qui part son titre n'a rien de similaire Struts, premier du nom. Par ailleurs, la communaut maintenant le projet semble moins active que les concurrentes ; Stripes, un challenger intressant et montant, trs lger et rput pour tre facile prendre en mains.

Framework MVC bas sur les composants Dfinition


La seconde grande catgorie de frameworks MVC qualifie ceux qui se basent non pas sur les requtes mais sur les composants.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

528/606

la diffrence de ceux bass sur les requtes, les frameworks bass sur les composants dcoupent logiquement le code en "composants", masquant ainsi le chemin d'une requte au sein de l'application. Ils essaient en quelque sorte d'abstraire les concepts de requte et rponse, et de traiter une application comme une simple collection de composants qui prsentent leur propre mthode de rendu et des actions pour effectuer des tches. Les ressources traitant de ce sujet dans la langue de Molire sont sur la toile tonnamment lgres. Si vous tes anglophones vous trouverez cependant de quoi faire, de nombreuses discussions intressantes et fournies tant disponibles dans la langue de Shakespeare. Pour information, sachez qu'on parle en anglais d'action-based frameworks et de component-based frameworks .

l'origine, de telles solutions ont t dveloppes pour faciliter aux dveloppeurs Java, peu familiers avec le dveloppement web et plus proches du dveloppement d'applications de bureau traditionnelles, la cration d'applications web, sans devoir connatre les rouages de l'API Servlet. En outre, de telles solutions ont pour objectif secondaire de rendre la matrise des technologies de prsentation web habituellement requises superflues, notamment HTML, CSS et JS.

Principe
Dans le MVC bas sur les composants, une unique servlet jouant le rle de Front Controller va elle-mme regrouper, convertir et valider les paramtres de requte, et mettre jour les valeurs du modle, et le dveloppeur n'a ainsi se soucier que des actions mtier. La faon dont le contrleur regroupe/convertit/valide/met jour les valeurs est dfinie dans un unique endroit, la vue. Puisque c'est impossible raliser l'aide de simple HTML "pur", un langage similaire spcifique est requis pour y parvenir. Dans le cas de JSF, c'est un langage bas sur du XML (XHTML). V ous utilisez du XML pour dfinir les composants de l'interface utilisateur, qui eux contiennent des informations sur la manire dont le contrleur doit regrouper/convertir/valider/mettre jour les valeurs et gnrer lui-mme le rendu HTML. Les avantages et inconvnients doivent maintenant tre clairs pour vous : avec un framework MVC bas sur les requtes vous devez crire plus de code vous-mmes pour parvenir vos fins. Cependant, vous avez un meilleur contrle sur le processus, c'est--dire sur le cheminement d'une requte au sein de l'application, et sur le rendu HTML/CSS/JS ; avec un framework MVC bas sur les composants, vous n'avez pas besoin d'crire autant de code vous-mmes. Cependant, vous avez moins de possibilits de contrle sur le processus et le rendu HTML/CSS/JS. Donc si vous souhaitez raliser des choses qui s'cartent un peu de ce que dcrit le standard, vous perdrez beaucoup plus de temps si vous utilisez un framework MVC bas sur les composants.

Solutions existantes
Une fois de plus, trois grands acteurs se partagent le devant de la scne : JSF, ou "Java Server Faces" : il s'agit de la solution standard intgre Java EE 6, et les trois prochains chapitres du cours lui sont ddis ; Wicket : solution dite par Apache, elle est trs en vogue grce sa relative simplicit et la communaut en charge de sa maintenance est trs active ; Tapestry : solution dite par Apache, ayant connu une volution assez chaotique. Durant les premires annes de son existence, chaque nouvelle version du framework cassait la compatibilit avec les versions prcdentes, forant les dveloppeurs en faisant usage migrer et/ou rcrire le code de leurs applications pour rester jour... A priori cette sombre poque est maintenant rvolue, et la version actuelle a t construite pour tre volutive.

Les "dissidents"
Sachez pour conclure qu'il existe plusieurs solutions dj trs populaires qui ne se classent dans aucune de ces deux catgories, mais qui sont tout de mme utilises pour dvelopper des applications web, et qui peuvent tre utilises en suivant le pattern MVC. On retiendra notamment : GWT, solution dite par Google pour la cration d'applications web de type client-serveur ; Play!, un framework plus global qui se place au mme niveau que Java EE lui-mme, et qui permet de dvelopper avec les

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


langages Java ou Scala.

529/606

En rsum
Un framework MVC est destin simplifier et acclrer le dveloppement d'une application web, et isoler clairement le travail effectuer par les diffrents profils de dveloppeurs. Utiliser un framework n'est pas toujours la solution optimale, il est parfois judicieux voire ncessaire de s'en passer. Un framework MVC bas sur les requtes (ou sur les actions) reprend l'organisation et les principes de ce que nous avons dvelopp la main jusqu' prsent. Il simplifie l'aspect contrleur via l'intervention d'une servlet unique jouant le rle d'aiguilleur gant. La vue est quant elle la plupart du temps ralise avec la technologie JSP. Un framework MVC bas sur les composants masque les changes de requtes et rponses derrire des composants autonomes, symboliss par l'intermdiaire de balises dans la vue. Une servlet unique joue le rle d'aiguilleur gant. Le standard adopt par Java EE 6 est un framework MVC bas sur les composants : la solution se nomme JSF, ou Java Server Faces. D'autres modes de dveloppement web existent sur la plate-forme Java, et sont activement dvelopps par de grands acteurs du web et de l'internet.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

530/606

Premiers pas avec JSF


Dans cette introduction au framework JSF, nous allons dcouvrir en quoi il consiste dans les grandes lignes, tudier ses composants de base, puis mettre en place un exemple trs simple et enfin le comparer son quivalent ralis avec la technologie JSP.

Qu'est-ce que JSF ? Prsentation


Nous l'avons voqu dans le chapitre prcdent, JSF (JavaServer Faces) est un framework MVC bas sur les composants . Il est construit sur l'API Servlet et fournit des composants sous forme de bibliothques de balises ressemblant trs fortement la JSTL. Celles-ci peuvent tre utilises dans des pages JSP comme dans toute autre technologie de vue base sur le Java, car le framework JSF ne limite pas le dveloppeur une technologie particulire pour la vue. Cependant, il existe une technologie relativement rcente baptise la Facelet, qui fait partie du standard et qu'il est recommand d'utiliser ds lors que l'on travaille avec JSF, celle-ci tant bien plus adapte que les pages JSP : ces-dernires sont d'ailleurs considres comme une technologie de prsentation dprcie pour JSF depuis la sortie de Java EE 6 en 2009. Hormis les avantages inhrents tout framework MVC bas sur les composants, que nous avons lists dans le chapitre prcdent, JSF offre notamment de grandes capacits de cration de templates (ou gabarits), telles que les composants composites. Qu'est-ce que la cration de templates ?

Il s'agit tout simplement de la possibilit de dcouper une page en plusieurs composants indpendants, assembls ensuite pour former une page finale. cet gard, la technologie JSP fournit seulement le tag <jsp:include/>, que nous avons dj rencontr dans notre TP pour une tche trs simple, savoir inclure automatiquement un menu sur chacune de nos pages. Pour rester concis, JSF permet comme la plupart des frameworks MVC de grer les vnements et la validation des donnes saisies par l'utilisateur, de contrler la navigation entre les pages, de crer des vues accessibles, de faciliter l'internationalisation des pages, etc. En outre, puisqu'il est bas sur les composants et non sur les requtes, il permet de grer un tat pour une vue donne. Pas de panique, nous allons revenir sur ces aspects importants en temps voulu et par la pratique.

Principe
JSF est un framework MVC, et propose en guise de contrleur unique du cycle de vie du traitement des requtes la FacesServlet. Un contrleur unique ?

Eh oui vous avez bien lu, il n'y a qu'une seule servlet charge d'aiguiller l'intgralit des requtes entrantes vers les bons composants, et ce pour l'application toute entire ! Cet architecture porte un nom, il s'agit du pattern Front Controller, qui luimme est une spcialisation du pattern Mdiateur. JSF vous vite d'avoir crire le code - standard, passe-partout et pnible - responsable du regroupement des saisies utilisateurs (paramtres de requtes HTTP), de leur conversion & validation, de la mise jour des donnes du modle, de l'invocation d'actions mtiers et de la gnration de la rponse. Ainsi vous vous retrouvez uniquement avec une page JSP ou une page XHTML (une Facelet) en guise de vue, et un JavaBean en tant que modle. Cela acclre le dveloppement de manire significative ! Les composants JSF sont utiliss pour lier la vue avec le modle, et la FacesServlet utilise l'arbre des composants JSF pour effectuer tout le travail. Tout cela semble bien compliqu, mais en ralit si vous devez n'en retenir qu'une chose, c'est celle-ci : avec JSF, vous n'avez plus besoin d'crire de servlets ! Ne soyez pas inquiets face toutes ces nouveauts, nous allons y revenir calmement et en dtails un peu plus loin dans ce chapitre.

Historique
Avant de passer la suite, penchons-nous un instant sur le pass de la solution. Certains d'entre vous le savent peut-tre dj, JSF jouit d'une certaine rputation auprs des dveloppeurs... une mauvaise rputation ! Pourtant, si l'on y regarde d'un peu plus prs, on se rend trs vite compte que la plupart des reproches faits JSF sont sans fondements.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

531/606

Afin de casser cette mauvaise rputation que trane injustement JSF, hormis lorsqu'il est question de la relative difficult d'apprentissage quand on ne dispose pas de solides connaissances en Java EE (servlets notamment), nous allons faire un rapide tat des lieux de l'volution du framework , de sa naissance aujourd'hui. Bien qu'actuellement dans sa version 2.1, il a toujours besoin de se dbarrasser de l'image ngative qu'il dgageait lorsqu'il n'tait pas encore mature. Et c'est vrai que si l'on se penche sur son histoire, on relve des inconvnients majeurs... Cet historique contient certaines informations et certains dtails qui vous chapperont certainement la premire lecture, si vous n'avez jamais travaill avec JSF par le pass. Ne vous y attardez pas pour le moment, tchez simplement de saisir l'volution gnrale du framework . Si vous rencontrez un jour un projet bas sur une ancienne version de JSF, vous trouverez ici les diffrences majeures avec la version actuelle et les inconvnients importants.

JSF 1.0 (mars 2004)


Ce ft la version initiale. Bien qu'estampille 1.0, elle tait pleine de bugs la fois au niveau des performances et du noyau, vous n'imaginez pas. Les applications ne fonctionnaient pas toujours comme attendu. En tant que dveloppeur, vous auriez fuit cette solution en hurlant !

JSF 1.1 (mai 2004)


Il s'agissait ici d'une mise jour destine corriger les bugs de la version initiale... Et les performances n'taient toujours pas au rendez-vous. En outre, norme inconvnient : il tait impossible d'insrer du HTML dans une page JSF sans dfauts ! trange dcision dans un framework destin la cration d'applications web que de laisser de ct les designer web... Pour faire court, l'intgralit du code HTML brut tait rendu avant les balises JSF ! Il fallait alors entourer chaque portion de code HTML par une balise JSF (<f:verbatim>) pour qu'elle soit incluse correctement dans l'arbre des composants (l'enchanement des balises JSF) et rendue proprement. Bien que cela respecte les spcifications crites l'poque, cela a valu au framework un flot de critiques ! Imaginez vous devoir entourer chaque balise HTML intervenant dans vos pages JSP par une autre balise, juste pour qu'elle soit correctement prise en compte... Bref, c'tait une vraie horreur.

JSF 1.2 (mai 2006)


Ce ft la premire version prpare par la nouvelle quipe de dveloppement de JSF, mene par Ryan Lubke. L'quipe a fourni un travail norme, et les spcifications ont beaucoup volu. L'amlioration de la gestion de la vue a t le principal axe de changement. Elle n'a pas seulement rendu la vue JSF indpendante des JSP, mais elle a galement permis aux dveloppeurs d'insrer du HTML dans une page JSF sans avoir dupliquer sans cesse ces satanes balises <f:verbatim> ncessaires dans les versions prcdentes. Un autre centre d'intrt de l'quipe l'poque ft l'amlioration des performances. Presque chaque version corrective mineure tait alors accompagne d'amliorations notables des performances globales du framework. Le seul rel inconvnient commun toutes les versions JSF 1.x (1.2 inclus) tait l'absence d'une porte se plaant entre la requte et la session (celle qui est souvent nomme "scope de conversation"). Cela forait les dveloppeurs jouer avec des champs de formulaires cachs, des requtes sur la BDD non ncessaires et/ou abuser des sessions chaque fois que quelqu'un voulait retenir le modle donnes initial pour les requtes suivantes afin de procder aux validation/conversion/mise jour du modle et aux invocations d'actions dans des applications web plus complexes. En somme, le dveloppeur devait faire comme nous lorsque nous travaillons la main sans framework, il n'avait aucun moyen standard pour donner un tat une vue en particulier. Ce problme pouvait l'poque tre partiellement palli en utilisant une bibliothque tierce qui sauvegardait l'tat des donnes ncessaires pour les requtes suivantes, notamment la balise <t:saveState> de la bibliothque MyFaces Tomahawk, ou encore le framework de conversation MyFaces Orchestra. Un autre point trs pnalisant pour les webdesigners tait le fait que JSF utilisait le caractre : en tant que sparateur d'identifiant afin d'assurer l'unicit des id des lments HTML gnrs lors du rendu des balises, notamment lorsqu'un composant tait utilis plus d'une fois dans la vue (cration de templates, boucles sur les composants, etc.). Si vous connaissez un petit peu CSS, vous devez savoir que ce caractre n'est pas autoris dans les identifiants CSS, et les webdesigners devaient alors utiliser le caractre \ pour chapper les : dans les slecteurs CSS qu'ils mettaient en place, ce qui produisait des slecteurs tranges et sales tels que #formulaireId\:champId { ... } dans les feuilles CSS. En outre, JSF 1.x n'tait livr avec aucune fonctionnalit Ajax prte l'emploi. Ce n'tait pas vraiment un inconvnient technique, mais l'explosion du web 2.0 en a fait un inconvnient fonctionnel. Exadel introduisait alors rapidement la bibliothque Ajax4jsf , qui a t activement dveloppe durant les annes suivantes puis intgre au noyau de la bibliothques de composants JBoss RichFaces. D'autres bibliothques offrent depuis des composants ajaxiss, IceFaces tant probablement la plus connue. Quand JSF 1.2 a atteint la moiti de sa vie, une nouvelle technologie pour la vue base sur le XML a t introduite : les Facelets .

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


Cela a offert d'normes avantages sur les JSP, particulirement pour la cration de templates et pour les performances.

532/606

JSF 2.0 (juin 2009)


Ce ft la seconde version majeure. Il y et normment de changements la fois techniques et fonctionnels. La technologie JSP a t remplace par les Facelets en tant que technologie de vue par dfaut, et aux Facelets ont t greffes des fonctionnalits permettant la cration de composants purement XML (appels composants composite). Ajax a t introduit notamment, via un composant qui montre des similarits avec Ajax4jsf. Les annotations et amliorations favorisant le concept convention-pluttque-configuration ont t introduites pour liminer les volumineux et verbeux fichiers de configuration autant que possible. En outre, le caractre sparateur d'identifiant : est devenu configurable. Dernire volution mais non des moindres, une nouvelle porte a t introduite : le scope view, se plaant entre la requte et la session. Cela a palli un autre inconvnient majeur de JSF 1.x, comme nous l'avons vu prcdemment. Nous allons y revenir en dtail dans un prochain exemple pratique. Jusque l, tout parat idyllique, et cette version estampille 2.0 semble salvatrice. Cependant, bien que la plupart des inconvnients de JSF 1.x aient disparu avec cette version, il subsiste des bugs spcifiques JSF 2.0 qui peuvent tre un facteur bloquant dans le dveloppement d'une application. Si le coeur vous en dit, vous pouvez jeter un il aux bugs JSF 2.x en cours pour vous faire une ide, mais ils concernent une utilisation avance du framework , et la plupart de ces bugs sont de toute manire contournables. Enfin pour terminer sur une note plus gaie, c'est avec JSF 2.0 que sont apparues de nouvelles bibliothques de composants plus graphiques, on peut notamment citer l'impressionnant PrimeFaces et OpenFaces.

MVC bas sur les composants vs MVC bas sur les requtes
Certains avancent que le plus gros inconvnient de JSF est qu'il n'autorise que peu de contrle sur le code HTML/CSS/JS gnr. Cela ne tient en ralit pas JSF en lui-mme, mais simplement au fait que c'est un framework MVC bas sur les composants, et pas sur les requtes (actions). Si vous tes perdus, je vous renvoie au chapitre prcdent pour les dfinitions. En ralit, c'est trs simple : si un haut niveau de contrle sur le rendu HTML/CSS/JS est votre principale exigence lorsque vous choisissez un framework MVC, alors vous devez regarder du ct des framework bass sur les requtes comme Spring MVC.

Aprs ce long apart sur l'histoire de JSF, dont vous pouvez trouver la version originale anglaise sur cette page, rsumons la situation en quelques lignes : JSF 2.0 est un framework web MVC qui se focalise sur la simplification de la construction d'interfaces utilisateur, autrement dit la vue. Il propose nativement plus d'une centaine de balises cet gard, et facilite la rutilisation des composants d'une interface utilisateur l'autre ; avec JSF 1, tout devait tre dclar dans un fichier de configuration, et le dveloppeur devait donc maintenir un nime fichier XML... Avec JSF 2, c'en est termin de ces fichiers (inter)minables : les annotations remplacent les fichiers externes, et le dveloppement s'en retrouve donc grandement acclr et simplifi ! dans ce cours, je ne ferai dornavant plus jamais la distinction entre JSF 1 et JSF 2, tout bonnement parce que JSF aujourd'hui, c'est JSF 2 et rien d'autre. Dans votre tte, cela doit tre tout aussi clair : JSF 1 est de l'histoire ancienne, et je n'y ferai allusion que ponctuellement pour vous informer des diffrences importantes avec la version actuelle, afin que vous ne soyez pas trop surpris si jamais vous devez un jour travailler sur une application dveloppe avec JSF 1 (ce que je ne vous souhaite bien videmment pas !).

Les ressources primes concernant JSF font lgion sur le web ! normment de cours, tutos, sujets de forums et documentations traitent de JSF 1, alors que JSF 2 a chang normment de choses. Faites bien attention aux liens que vous parcourez, et basez-vous sur les dates cls de l'histoire de JSF pour dterminer si les informations que vous lisez sont d'actualit ou non. En rgle gnrale, vous devez donc vous mfier de tout ce qui a t publi avant 2009 !

Structure d'une application JSF


tudions brivement comment se construit une application base sur JSF. Cela ne devrait pas bouleverser vos habitudes, la structure globale est trs similaire celle d'une application web Java EE MVC traditionnelle :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

533/606

la vue est gnralement assure par des pages JSP ou par des pages XHTML (on parle alors de Facelets) ; le modle est assur par des entits ou des JavaBeans ; le contrleur auparavant incarn par nos servlets est dornavant dcompos en deux lments : une unique servlet mre servant de point d'entre toute requte, la FacesServlet ; un JavaBean particulier, dclar via une annotation et dsign par le terme managed-bean . le tout est mis en musique par des fichiers de configuration : le classique web.xml, mais galement un nouveau fichier nomm faces-config.xml. Nous observons que la diffrence majeure qui ressort jusqu' prsent est l'absence de servlets spcifiques chaque page, comme nous devions en crire dans nos applications MVC faites maison.

Facelets et composants La page JSP mise au placard ?


Comme je vous l'ai annonc plusieurs reprises dj, la page JSP n'est plus la technologie de rfrence pour la cration de vues dans une application Java EE. Avec JSF, ce sont maintenant de simples pages XHTML qui sont utilises, pages que l'on nomme des Facelets . Partant de ce constat, si vous avez bien suivi vous tes en droit de vous poser la question suivante : Nous avons dj dcouvert que les servlets n'allaient plus tre ncessaires dans notre application, voil maintenant que les JSP non plus ne vont plus nous servir... Pourquoi avoir pass des chapitres entiers apprendre crer et manier des servlets et des JSP, si c'est pour les laisser tomber ensuite ? Premirement, ces technologies sont loin d'tre la retraite, car comme nous l'avons dj abord les frameworks MVC ne sont pas utiliss partout. En outre, il existe dans le monde de l'entreprise normment d'applications bases sur les servlets et les JSP, applications qui ne sont pas prtes d'tre migres ni rcrites vers un framework MVC, bien souvent pour des raisons de cot. Plus important encore, il existe normment d'applications bases sur des frameworks MVC diffrents de JSF, notamment le trs populaire Spring MVC, qui utilisent encore massivement les pages JSP en guise de vue. Deuximement, l'apprentissage des frameworks et de leur fonctionnement est bien plus facile lorsque vous disposez dj des bases. Bien entendu, dans l'absolu il est possible d'attaquer directement par les solutions de haut niveau - c'est--dire celles qui masquent les technologies de base comme les servlets, les JSP ou encore JDBC - mais tt ou tard vous devrez regarder sous la couverture... Et quand ce jour arrivera, vous serez alors bien plus apte comprendre les rouages de tout cette machinerie en connaissant les fondements que si vous dcouvriez le tout pour la premire fois ! Enfin, dans les cas d'applications de faible envergure, d'applications ncessitant d'excellentes performances et d'applications devant tre aisment dployables trs large chelle, il est encore relativement courant de ne pas utiliser de frameworks. Bref, rassurez-vous, vous n'avez pas perdu votre temps en arrivant jusqu'ici !

Structure et syntaxe
Nous allons aborder la structure d'une Facelet en la comparant avec la structure d'une technologie que nous connaissons dj : la page JSP.

Gnralits
La diffrence la plus importante se situe au niveau du contenu. Alors qu'une page JSP est un objet complexe, pouvant contenir la fois des balises JSP ou JSTL et du code Java par l'intermdiaire de scriptlets, une Facelet est quant elle un fichier XML pur ! Elle ne peut par consquent contenir que des balises, et il est impossible d'y inclure des scriplets : le Java est donc dfinitivement banni de la vue avec cette technologie. Au niveau du format et du rendu, la situation n'a pas chang : tout comme les servlets et les pages JSP sont le plus souvent utilises pour gnrer des pages HTML, c'est le langage XHTML qui est le plus utilis pour crer des Facelets (XHTML tant une version de la syntaxe HTML conforme au standard XML). Toutefois, sachez que le framework JSF est markup -agnostique : cela signifie qu'il peut s'adapter n'importe quel langage, partir du moment o ce langage respecte la structure dcrite par le standard XML. Ainsi, il est galement possible de construire une Facelet avec du langage XML pur, par exemple pour crer un flux RSS, ou avec le langage XUL pour n'en citer qu'un. Au niveau de l'aspect extrieur du fichier, une Facelet porte en gnral une des ces trois extensions : .jsf , .xhtml ou .faces . Il n'existe pas une unique extension pour la simple raison que nous venons de dcouvrir, savoir le fait qu'une Facelet n'est rien d'autre qu'un document XML. Dans l'absolu, c'est par dfaut l'extension .xml qui pourrait tre utilise... Cela dit, il est utile de pouvoir reconnatre facilement une Facelet parmi d'autre fichiers du mme type, pour la dmarquer du reste : voil pourquoi ces trois extensions ont de facto t retenues par les dveloppeurs. Ne vous inquitez pas, nous dcouvrirons trs bientt comment

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


le framework est capable de savoir quelles extensions de fichiers lui sont destines. Ainsi, voici la structure vide d'une Facelet : Code : HTML - exemple.jsf <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> ... </html>

534/606

V ous retrouvez bien le doctype HTML, suivi de la balise <html> conforme au standard XHTML. Bref, en apparence ce n'est qu'une page web toute simple !

Bibliothques et balises
Une Facelet est donc constitue de balises, dont la syntaxe est trs similaire celles des balises JSTL utilises dans une page JSP. Avant de poursuivre sur la forme des balises JSF, intressons-nous aux bibliothques. Tout comme il existe des directives permettant d'importer des bibliothques de balises JSTL dans une page JSP, il existe un moyen pour inclure les bibliothques de balises JSF dans une Facelet. Toutefois, il vous faut oublier le concept mme de la directive JSP : il s'agissait l littralement d'un ordre donn au conteneur, lui prcisant comment il devait grer une page JSP et gnrer sa servlet Java associe. V oil pourquoi il tait non seulement possible via une directive d'inclure des bibliothques, mais galement d'importer des classes Java, d'activer ou non les sessions HTTP, les expressions EL, etc. Dans une Facelet, une bibliothque de balises est incluse via l'ajout d'un attribut xmlns la balise <html> qui ouvre le corps de la page. Il s'agit l d'un namespace XML, je vous invite vous renseigner sur cette notion si vous souhaitez comprendre comment cela fonctionne en dtail. En ce qui nous concerne, nous allons simplement retenir qu'un tel attribut permet de dclarer une bibliothque dans une Facelet ! Le framework JSF intgre nativement trois bibliothques standard : HTML, Core et UI. V oici comment les inclure dans une Facelet : Code : HTML - exemple.jsf <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> ... </html>

V ous reconnaissez ici le systme de liens utilis galement par la JSTL. De mme, vous retrouvez le systme de prfixe identifiant une bibliothque : traditionnellement, h: dsigne la bibliothque HTML, ui: la bibliothque de templating et de composition, et f: la bibliothque Core. Au passage, ne confondez pas cette dernire avec la bibliothque Core de la JSTL, nous parlons bien ici de balises JSF ! Pour terminer et revenir sur les balises JSF proprement parler, sur la forme elles ressemblent comme deux gouttes d'eau aux balises JSTL. Par exemple, voici une balise issue de la bibliothque HTML : Code : HTML - Exemple de balise JSF <h:outputLabel for="confirmation">Confirmation du mot de passe <span class="requis">*</span></h:outputLabel>

V ous pouvez d'ores et dj remarquer les similitudes avec les balises JSTL : il est possible d'y prciser des attributs, une balise possde un corps, il est possible d'y inclure d'autres balises, etc. Dans nos exemples venir, nous allons utiliser de nombreuses balises qui vous sont encore inconnues. Si j'ai pris le temps de vous dcrire les balises JSP et JSTL les plus couramment utilises lorsque de nos premiers pas avec Java EE, je ne vais cette fois

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


pas rdiger d'inventaire des balises JSF, et ce pour trois raisons :

535/606

1. vous tes l'aise avec le concept de balises, vous savez comment elles se construisent ; 2. vous tes l'aise avec la recherche d'informations, vous tes capables de trouver et parcourir les ressources et documentations par vous-mmes ; 3. les bibliothques standard de JSF contiennent beaucoup de balises, certaines ddies des tches bien diffrentes de ce que vous connaissez et avez manipul jusqu' prsent, et en faire l'inventaire dtaill et expliqu par l'exemple serait chronophage et en fin de compte peu efficace. Pour vous faciliter la tche, je vous communique quelques ressources extrmement utiles : la documentation officielle des balises JSF, par Oracle ; une documentation similaire, qui date un peu mais qui est prsente de manire un peu plus conviviale que le format Javadoc, par JSFToolbox ; la page JSF de Stackoverflow, contenant des gnralits, des exemples basiques et une listes de liens fournie et jour sur le sujet. Gardez ces liens accessibles dans les favoris de votre navigateur, vous allez en avoir besoin pour dcouvrir toutes les balises proposes par JSF !

Expressions EL
Une Facelet peut tout comme une page JSP contenir des expressions EL. Toutefois, celles-ci sont un peu diffrentes de celles que nous avons utilises jusqu' prsent. V oyez dans cet exemple la forme d'une expression EL avec JSF : Code : HTML - Exemple d'expression EL avec JSF #{inscrireBean.utilisateur.motDePasse}

V ous allez me dire, part le symbole # qui remplace le symbole $ auparavant utilis, a priori le reste ne change pas : les accolades, l'accs aux beans, ses proprits, l'oprateur . en guise de sparateur... Eh bien en ralit, c'est un peu plus compliqu que a. Pour mieux comprendre, faisons un rapide historique de la technologie EL : en juin 2002, la premire version de la JSTL parat et introduit le concept des expressions EL pour la toute premire fois. Celles-ci taient alors construites pour appeler les mthodes getter de JavaBeans, uniquement via la syntaxe ${...} et uniquement depuis les balises JSTL ; en novembre 2003, la seconde mouture de la technologie JSP fait son apparition et les expressions EL deviennent partie intgrante du standard J2EE 1.4. La JSTL en version 1.1 ne contient alors plus la technologie EL, et la syntaxe ${...} fonctionne dsormais en dehors des balises JSTL, dans le corps des pages JSP ; en mars 2004, la premire version de JSF est publie, et introduit le concept des expressions EL dites "diffres". Il s'agissait alors d'expressions sous la forme #{...} et ne fonctionnant que dans des balises JSF. La principale diffrence avec les expressions de la forme ${...} dcrites par le standard JSP est qu'elles permettent non seulement d'appeler les mthodes getter des beans, mais galement leurs mthodes setter ; en mai 2005, les deux technologies EL coexistantes sont combines en une seule spcification : les expressions EL sont alors dites "unifies", et font partie intgrante du standard Java EE 5. La syntaxe #{...} devient de fait utilisable galement depuis une page JSP, mais y est toujours limite l'accs aux mthodes getter, seul l'usage depuis JSF permet d'appeler les mthodes setter ; en dcembre 2009, une volution des expressions EL est introduite avec le standard Java EE 6. Celles-ci permettent dsormais, depuis la syntaxe #{...}, d'appeler n'importe quelle mthode d'un bean, et non plus seulement ses getters/ setters. C'est par ailleurs cette occasion que les Facelets sont devenues partie intgrante du standard Java EE 6. De cette longue pope, vous devez retenir deux choses importantes : 1. il est possible d'utiliser la syntaxe #{...} depuis vos pages JSP ! Je ne vous l'ai pas prsente plus tt pour ne pas vous embrouiller, et surtout pour ne pas vous voir appeler des mthodes Java dans tous les sens sans comprendre ce que MVC implique et impose, ni comprendre comment doit tre construit un bean...

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

536/606

2. nous allons dornavant utiliser la syntaxe #{...} avec JSF, car contrairement ce que nous faisions avec la syntaxe ${...} depuis nos pages JSP, nous allons avoir besoin d'initialiser des valeurs et non plus seulement d'accder en lecture des valeurs.

V ous voil au point sur les aspects superficiels de la technologie Facelet. Il est grand temps maintenant de passer derrire les rideaux, et de dcouvrir comment tout cela fonctionne dans les coulisses...

Comment a marche ?
Avant tout, et parce que vous en aurez forcment un jour besoin si vous travaillez avec JSF, voici l'outil le plus utile pour comprendre les rouages du systme : la documentation officielle du framework JSF dans sa version actuelle (2.1). V ous savez dj comment fonctionne une page JSP, mais un petit rappel ne fait jamais de mal. La technologie JSP fournit un langage de templating , permettant de crer des pages qui sont - par un procd que vous connaissez - traduites en servlets Java, puis compiles. Pour faire court, le corps d'une page JSP devient l'quivalent de la mthode service(), la mthode mre des mthodes doGet() et doPost(). Les balises JSP et JSTL qui y sont utilises sont directement transformes en code Java et intgres dans la servlet gnre, vous pouvez d'ailleurs vous en rendre compte par vous-mmes en allant regarder le code des servlets auto-gnres par Tomcat dans nos prcdents exemples. Dans une Facelet par contre, qui comme nous venons de le voir n'est qu'un fichier XML, les balises JSF ne sont que des appels des composants JSF qui sont entirement autonomes. Autrement formul, lorsqu'un composant JSF est appel, il gnre son propre rendu dans son tat courant. Le cycle de vie des composants JSF n'a par consquent aucune relation avec le cycle de vie d'une page JSP et de sa servlet auto-gnre. Une Facelet est donc une page constitue d'une suite d'appels des composants JSF (raliss par l'intermdiaires de balises), et ceux-ci forment ce que l'on appelle un arbre de composants , ou component tree en anglais. Ainsi, ne vous laissez pas tromper par les apparences : mme si JSF vous permet de travailler avec des balises JSF qui ressemblent comme deux gouttes d'eau des balises JSTL, retenez bien que JSF ne fonctionne pas comme fonctionnaient nos exemples MVC bass sur des servlets et des JSP. Ces similitudes ont par contre un avantage certain : puisque vous connaissez dj MVC avec les JSP, vous pouvez attaquer le dveloppement avec JSF trs rapidement !

En prenant un peu de recul, JSF s'apparente dans son fonctionnement Swing ou AWT : c'est un framework qui fournit une collection de composants standards et rutilisables, permettant la cration d'interfaces utilisateur (web, en l'occurrence). la diffrence des JSP, les Facelets JSF conservent un tat (on dit alors que la vue est stateful ) : ce sont les composants autonomes appels par l'intermdiaires des balises contenues dans les Facelets qui permettent de maintenir cet tat. De la mme manire que Swing et AWT, les composants JSF suivent le pattern de l'objet composite pour grer un arbre de composants : en clair, cela signifie la fois qu'un objet conteneur contient un composant, et qu'un objet conteneur est lui-mme un composant. La vue lie ces composants graphiques la page XHTML, et permet ainsi au dveloppeur de directement lier des champs HTML d'interaction utilisateur (saisie de donnes, listes, etc.) des proprits de beans, et des boutons leurs mthodes d'actions.

Un processus en 6 tapes
Arrtons les comparaisons avec d'autres solutions, et tudions concrtement le fonctionnement du framework . Avec JSF, le traitement d'une requte entrant sur le serveur est dcoup en six tapes, que nous allons parcourir sommairement : 1. La restauration de la vue La requte entrante est redirige vers l'unique servlet jouant le rle de super-contrleur, la FacesServlet. Celle-ci examine son contenu, en extrait le nom de la page cible et dtermine s'il existe dj une vue associe cette page (eh oui, rappelez-vous bien qu'avec JSF la vue conserve un tat). V oil pourquoi cette tape s'intitule "restauration de la vue" : il s'agit en ralit de restaurer les ventuels composants dj chargs si l'utilisateur a dj accd la page par le pass. La FacesServlet va donc chercher les composants utiliss par la vue courante. Si la vue n'existe pas dj, elle va la crer. Si elle existe dj, elle la rutilise. La vue contient tous les composants de l'interface utilisateur intervenant dans la page. La vue (c'est- -dire l'ensemble des composants qui y interviennent, et donc son tat) est sauvegarde dans l'objet FacesContext. 2. L'application des valeurs contenues dans la requte Arrivs cette tape, les composants de la vue courante ont tout juste t rcuprs ou crs depuis l'objet FacesContext. Chacun d'eux va maintenant rcuprer la valeur qui lui est assigne depuis les paramtres de la requte, ou ventuellement depuis des cookies ou headers.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

537/606

Ces valeurs vont alors tre converties. Ainsi si un champ est li une proprit de type Integer, alors son contenu va tre converti en Integer. Si cette conversion choue, un message d'erreur va tre plac dans le FacesContext, et sera utilis lors du futur rendu de la rponse. noter qu' cette tape peut intervenir la "prise en charge immdiate des vnements" : cela veut dire que si un composant est marqu comme tel, sa valeur va directement tre convertie puis valide dans la foule. Si aucun composant n'arbore cette proprit, alors les valeurs de tous les composants sont d'abord converties, puis intervient ensuite l'tape de validation sur l'ensemble des valeurs. 3. La validation des donnes Les valeurs tout juste converties vont ensuite tre valides, en suivant les rgles de validation dfinies par le dveloppeur. Si la validation d'une valeur choue, un message d'erreur est ajout au FacesContext, et le composant concern est marqu comme "invalide" par JSF. La prochaine tape est alors directement le rendu de la rponse, il n'y aura aucune autre tape intermdiaire. Si les valeurs sont correctes vis vis des rgles de validation en place, alors la prochaine tape est la mise jour des valeurs du modle. 4. La mise jour des valeurs du modle Les composants peuvent tre directement lis, par l'intermdiaire des balises prsentes dans la vue, des proprits de beans. Ces beans sont qualifis de managed-beans ou backing-beans, car ils sont grs par JSF et la vue s'appuie sur eux. Si de tels liens existent, alors les proprits de ces beans sont mises jour avec les nouvelles valeurs des composants correspondant, frachement valides. Puisque la validation a eu lieu en premier lieu, le dveloppeur est certain que les donnes enregistres dans le modle sont valides, au sens format du champ du formulaire. Il n'est par contre pas exclus que les donnes ne soient pas valides d'un point de vue de ce qu'attend le code mtier de l'application, mais c'est tout fait normal, puisque cette tape est la suivante dans le processus... 5. L'appel aux actions, le code mtier de l'application Les actions associes la soumission du formulaire sont alors appeles par JSF. Il s'agit enfin de l'entre en jeu du code mtier : maintenant que les donnes ont t converties, valides et enregistres dans le modle, elle peuvent tre utilises par l'application. La fin de cette tape se concrtise par la redirection vers la vue correspondante, qui peut dpendre ou non du rsultat produit par le code mtier. Il s'agit donc de dfinir la navigation au sein des vues existantes, ce qui est ralis directement depuis le bouton de validation dans la page, ou depuis un fichier de configuration XML externe nomm faces-config.xml. 6. Le rendu de la rponse La dernire et ultime tape est le rendu de la rponse. La vue dfinie dans la navigation est finalement affiche l'utilisateur : tous les composants qui la composent effectuent alors leur propre rendu, dans leur tat courant. La page HTML ainsi gnre est finalement envoye au client, mais a, vous vous en doutiez ! V oil comment se droule le traitement d'une requte avec JSF. Comme vous pouvez le voir, a change du mode de traitement linaire que nous avions adopt dans nos exemples MVC faits maison, notamment au niveau de la possibilit de prise en charge immdiate d'un vnement qui permet le court-circuitage du processus global pour un composant en particulier ! Si tout ceci est encore trs flou dans votre tte, c'est normal : beaucoup de choses vous semblent encore bien abstraites. Ne vous dcouragez surtout pas, car comprendre le fonctionnement de JSF est l'effort le plus intense qu'il vous faudra fournir. Ds lors que vous aurez assimil comment se goupille toute cette mcanique, vous aurez fait plus de la moiti du chemin vers l'adoption de JSF !

Pour vous aider bien comprendre, je vous propose de dcouvrir ce processus dans un exemple pratique trs simple. Mais avant cela, je vous propose de vous dtendre un peu en dcouvrant une petit astuce sous Eclipse, permettant de prparer rapidement votre espace de travail au dveloppement de Facelets, les fameuses pages que nous allons crer la place de nos pages JSP.

Crer un template de Facelet par dfaut avec Eclipse


Avec Eclipse, il est possible de dfinir quel sera le contenu gnr par dfaut lors de la cration d'un nouveau fichier. Pour prparer facilement nos vues JSF, il nous suffit donc de crer un nouveau type de fichier nomm "Facelet" et de personnaliser son contenu par dfaut. Pour ce faire, rendez vous dans les prfrences d'Eclipse, puis suivez Web > HTML Files > Editor > Templates et cliquez enfin sur New :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

538/606

Entrez alors comme nom "New Facelet Page", puis slectionnez le contexte "New HTML", entrez comme description "Creates a new Facelet page", puis copiez le code suivant dans le champ pattern et validez enfin en cliquant sur OK : Code : HTML - Modle de nouvelle Facelet <!DOCTYPE html> <html lang="fr" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Insrer le titre ici</title> </h:head> <h:body> ${cursor} </h:body> </html>

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

539/606

Une fois cette configuration en place, vous pourrez simplement crer une nouvelle Facelet prte tre code ! Il vous suffira de faire un clic droit dans un projet, puis de suivre New > HTML File :

Puis de donner un nom votre fichier et de cliquer sur Next dans la fentre qui s'affiche alors, avant de choisir votre template frachement cr dans la liste qui s'affiche enfin et de valider en cliquant sur Finish :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

540/606

Le nouveau fichier que vous venez de crer contient alors automatiquement le code de base que vous avez dfini plus tt dans les options d'Eclipse.

Premier projet De quoi avons-nous besoin ?


Puisque nous travaillons avec GlassFish, nous n'avons en apparence besoin de rien : tout y est dj inclus ! En ralit, c'est encore une fois le mme refrain : JSF n'est qu'une spcification, et pour utiliser JSF il faut donc disposer d'une implmentation. Souvenez-vous de JPA, c'tait exactement pareil : l'implmentation de rfrence de JPA utilise par dfaut par GlassFish tait EclipseLink, mais il existait d'autres implmentations trs utilises comme Hibernate notamment. En ce qui concerne JSF, il existe deux principales implmentations : Oracle Mojarra, l'implmentation de rfrence, utilise par dfaut par GlassFish ; Apache MyFaces, l'implmentation dite par Apache. Quelle implmentation choisir ?

Les diffrences entre ces deux solutions sont minimes aux premiers abords. Seule une utilisation trs pousse d'une solution ou de l'autre vous fera prendre conscience des carts existant entre ces deux implmentations, et de l'intrt de prfrer l'une l'autre. Ainsi, il n'y a pas de rponse empirique la question : selon le contexte de votre projet, vous serez peut-tre amens changer d'une implmentation vers l'autre, en raison d'un comportement qui pose problme chez l'une mais pas chez l'autre. Qui plus est, puisque ces deux implmentations respectent la spcification JSF, elles sont interchangeables trs simplement.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


L'ventuel besoin de changer d'implmentation en cours de route dans un projet n'est donc pas un rel problme.

541/606

Bref, en ce qui nous concerne, nous n'en sommes qu'aux balbutiements et allons partir par dfaut sur l'implmentation Mojarra, puisque c'est celle que GlassFish embarque nativement. Et si nous n'utilisions pas GlassFish ?

Si le serveur utilis est un serveur d'applications Java EE au sens strict du terme, alors une implmentation JSF doit tre fournie par dfaut. Si par contre c'est un serveur lger comme Tomcat qui est utilis, alors il est ncessaire d'ajouter au projet les Jar de l'implmentation JSF pour pouvoir l'utiliser dans votre application. Ces archives Jar sont bien entendu disponibles au tlchargement sur les sites respectifs des deux solutions. Et si nous souhaitions utiliser MyFaces sur un serveur GlassFish ?

Si nous voulions changer d'implmentation JSF et utiliser MyFaces au lieu de Mojarra, il nous suffirait alors d'importer les archives Jar de l'implmentation dans notre projet, de la mme manire que nous devons les importer sur un serveur lger. Au final, retenez bien que GlassFish est livr par dfaut avec une implmentation de JSF, et donc que nous pouvons travailler avec JSF sans aucun ajout.

Cration du projet
Nous pouvons maintenant attaquer la cration de notre premier exemple : nous allons trs modestement crer une page qui demande l'utilisateur de saisir son nom dans un champ de formulaire, et une seconde page qui se chargera d'afficher le nom saisi l'utilisateur. Rien de transcendant je vous l'accorde, mais c'est dj assez pour que vous puissiez dcouvrir en douceur le fonctionnement de JSF. Avant tout, nous devons mettre en place un projet web sous Eclipse. La dmarche est la mme que pour nos prcdents travaux : crez un projet web dynamique ; nommez-le pour cet exemple test_jsf ; validez, et le projet est prt !

Cration du bean
Pour commencer, nous allons crer un simple bean pour stocker le nom saisi par l'utilisateur. Je vous donne le code, et vous explique les quelques nouveauts ensuite : Code : Java - com.sdzee.exemple.BonjourBean package com.sdzee.exemple; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; @ManagedBean @RequestScoped public class BonjourBean implements Serializable { private static final long serialVersionUID = 1L; private String public String getNom() { return nom; } nom;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


public void setNom( String nom ) { this.nom = nom; }

542/606

V ous pouvez observer deux choses qui diffrent des simples beans que nous utilisions dans nos exemples MVC traditionnels : le bean implmente l'interface Serializable. En ralit, je vous avais dj prvenu que nos beans pouvaient tirer parti de cette interface lorsque nous avons dcouvert le JavaBean pour la premire fois, mais je ne vous avais alors pas expliqu quoi cela pouvait bien servir. En ralit c'est trs simple : en rendant un bean srialisable, vous lui donnez la capacit de survivre un redmarrage du serveur. Cela ne va pas plus loin que cela, c'est un dtail qui n'a aucune importance dans notre exemple, et j'ai simplement fait intervenir cette interface pour vous expliquer son rle. Au passage, votre IDE Eclipse vous fera remarquer la ncessit de prciser un attribut serialVersionUID, qu'il peut gnrer automatiquement pour vous. le bean contient deux annotations spcifiques JSF : @ManagedBean : permet de prciser au serveur que ce bean est dornavant gr par JSF. Cela signifie simplement que JSF va utiliser ce bean en tant que modle associ une ou plusieurs vues. Par dfaut, le nom du bean correspond au nom de la classe, la majuscule en moins : en l'occurrence le nom de notre bean est donc bonjourBean. Si nous voulions dsigner ce bean par un autre nom, par exemple direBonjour, alors il nous faudrait annoter le bean en prcisant le nom souhait, via @ManagedBean(name="direBonjour") ; @RequestScoped : permet de prciser au serveur que ce bean a pour porte la requte. Il s'agit en l'ocurrence de la porte utilise par dfaut en cas d'absence d'annotation. Ainsi si vous omettez de l'crire, le bean sera de toute manire plac dans la porte requte. C'est toutefois une bonne pratique de toujours crire cette annotation, afin de clarifier le code. Il existe autant d'annotations que de portes disponibles dans JSF : @NoneScoped, @RequestScoped, @ViewScoped, @SessionScoped, @ApplicationScoped, et @CustomScope. Ne vous inquitez pas, nous y reviendrons en dtail trs prochainement. Pour information, avec JSF 1.x ces annotations n'existaient pas, il fallait dclarer chaque bean dans un fichier de configuration externe nomm faces-config.xml . Grce aux annotations, ce n'est dsormais plus ncessaire avec JSF 2.x !

V ous savez maintenant ce qu'est le fameux managed-bean ou backing-bean dont je vous avais parl un peu plus tt : un simple bean annot pour le dclarer comme tel auprs de JSF. Un pas de plus vers la comprhension de JSF...

Cration des facelets


La seconde tape consiste crer les vues de notre petite application. Nous allons donc crer deux Facelets, qui pour rappel ne sont rien d'autre que des pages XHTML et contenant des balises propres JSF, charges respectivement d'afficher un champ de formulaire l'utilisateur, et de lui afficher les donnes saisies. Nous allons nommer la page contenant le formulaire bonjour.xhtml , et la page charge de l'affichage bienvenue.xhtml . Toutes deux doivent tre places directement la racine de votre application, symbolise par le dossier /WebContent dans Eclipse : Code : HTML - /bonjour.xhtml <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Premier exemple JSF 2.0</title> </h:head> <h:body> <h1>Premier exemple JSF 2.0 - bonjour.xhtml</h1> <h:form> <h:inputText value="#{bonjourBean.nom}" /> <h:commandButton value="Souhaiter la bienvenue" action="bienvenue" /> </h:form> </h:body> </html>

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


Cette page gnre : un champ de texte JSF, et le lie avec la proprit nom du bean bonjourBean (notre managed-bean , gr par JSF) ; un bouton de formulaire, charg d'afficher la page bienvenue.xhtml lorsqu'il est cliqu.

543/606

Observons la constitution de cette page. En premier lieu, vous retrouvez logiquement l'en-tte html particulier dont je vous ai parl lorsque nous avons abord la constitution d'une Facelet, qui contient le type de la page ainsi que les ventuelles dclarations de bibliothques de balises JSF. En l'occurrence, nous avons ici simplement dclar la bibliothque HTML la ligne 3, puisque nous n'utilisons dans la page que des balises de la bibliothque HTML. V ous observez ensuite des balises JSF, prfixes par h: comme dclar dans l'en-tte html. Etudions-les dans leur ordre d'apparition :

Le header
La section header est cre par la balise <h:head>. Il s'agit d'un composant JSF qui permet notamment d'inclure des ressources JS ou CSS dans le contenu gnr entre les balises HTML <head> et </head>, de manire automatise depuis votre code Java. Dans notre exemple, nous n'avons rien de particulier y inclure, nous nous contentons d'y dfinir un titre pour notre page via la balise HTML <title>. V ous pouvez galement y dclarer un charset via la balise HTML <meta>, etc., comme vous le faisiez dans vos pages JSP.

Le formulaire
Le formulaire est cr par la balise <h:form>. Il s'agit du composant JSF permettant de gnrer un formulaire, contenant d'ventuels champs de saisies. La mthode HTTP utilise pour l'envoi des donnes est toujours POST. Si vous souhaitez utiliser la mthode GET, alors le plus simple est de ne pas utiliser le composant JSF et d'crire directement votre formulaire en HTML brut et en y spcifiant l'attribut <form ... method="get">. Les donnes sont par dfaut renvoyes la page contenant le formulaire.

Le champ de saisie
Le champ de type texte est cr par la balise <h:inputText>. Son attribut value permet de dfinir deux choses : la valeur contenue dans cet attribut sera par dfaut affiche dans le champ texte, il s'agit ici du mme principe que pour un champ de texte HTML classique <input type="text" value="..." /> ; la valeur contenue dans cet attribut sera utilise pour initialiser la proprit nom du bean BonjourBean, par l'intermdiaire de l'expression EL #{bonjourBean.nom}. En l'occurrence, JSF va valuer l'expression lorsque le formulaire sera valid, c'est--dire lorsque le bouton sera cliqu, et va alors chercher l'objet nomm bonjourBean, puis utiliser sa mthode setter setNom() pour enregistrer dans la proprit nom la valeur contenue dans le champ texte.

Le bouton de validation
Le bouton d'envoi des donnes du formulaire est cr par la balise <h:commandButton> : son attribut value contient la valeur affiche l'utilisateur en guise de texte du bouton, tout comme un bouton HTML classique ; son attribut action permet quant lui de dfinir o rediriger l'utilisateur : en l'occurrence, nous allons le rediriger vers la page qui affiche le texte saisi, c'est--dire bienvenue.xhtml . Il nous suffit pour cela d'crire bienvenue dans le champ action de la balise, et le composant se chargera automatiquement derrire les rideaux d'appeler la page nomme bienvenue.xhtml .

Sous JSF 1.x, il tait ncessaire de dclarer une navigation-rule dans le fichier faces-config.xml , afin de dfinir quelle page serait affiche une fois le bouton cliqu. Avec JSF 2.x, vous constatez qu'il est dornavant possible de directement placer le nom de la page dans l'attribut action du bouton. Pour une navigation simple, c'est trs pratique et plus que suffisant ! Toutefois, sachez que pour mettre en place une navigation un peu plus complexe, il nous faudra toujours utiliser une section navigation-rule dans le faces-config.xml .

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


V oil ensuite le code de la page de bienvenue, extrmement simple : Code : HTML - /bienvenue.xhtml <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Premier exemple JSF 2.0</title> </h:head> <h:body> <h1>Premier exemple JSF 2.0 - bienvenue.xhtml</h1> <p>Bienvenue #{bonjourBean.nom} !</p> </h:body> </html>

544/606

Lors de l'affichage de la page, JSF va valuer l'expression EL #{bonjourBean.nom} situe la ligne 9, et chercher l'objet intitul bonjourBean pour afficher sa proprit nom, rcupre automatiquement via la mthode getter getNom().

Configuration de l'application
Afin de mettre ce petit monde en musique, il nous faut pour finir crire un peu de configuration dans le fichier web.xml : Code : XML - /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app> <!-- Changer cette valeur "Production" lors du dploiement final de l'application --> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> > <!-- Dclaration du contrleur central de JSF : la FacesServlet -<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>

<!-- Mapping : association des requtes dont le fichier porte l'extension .xhtml la FacesServlet --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> </web-app>

Aux lignes 10 14, nous dclarons tout simplement la servlet mre de JSF, celle qui joue le rle du Front Controller : la FacesServlet. C'est exactement le mme principe que lorsque nous dfinissions nos propres servlets la main auparavant : il suffit de prciser un nom, en l'occurrence je l'ai nomme " Faces Servlet", et sa localisation dans l'application, en l'occurrence javax.faces.webapp.FacesServlet. Aux lignes 17 20, nous procdons ensuite au mapping d'un pattern d'URL sur cette seule et unique FacesServlet. L'objectif est de rediriger toutes les requtes entrantes vers elle. Dans notre exemple, nous nous contentons d'associer les vues portant l'extension .xhtml la FacesServlet, puisque toutes nos vues sont ainsi constitues. ce sujet, sachez que tous les dveloppeurs n'utilisent pas l'extension .xhtml pour leurs vues, et il est courant dans les projets

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

545/606

JSF existants de rencontrer quatre types d'URL diffrentes : /faces/*, *.jsf, *.xhtml et *.faces. Si vous devez un jour manipuler des vues portant une de ces extensions, il vous faudra simplement ajouter des mappings dans votre web.xml : Code : XML - Mappings pour les diffrents types d'URL <!-- Mapping des diffrents patterns d'URL devant tre associs la FacesServlet --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping>

Ainsi avec une telle configuration, dans notre projet les quatre URLs suivantes pointeraient toutes vers la mme page /bonjour.xhtml : http://localhost:8088/test_jsf/bonjour.jsf http://localhost:8088/test_jsf/bonjour.faces http://localhost:8088/test_jsf/bonjour.xhtml http://localhost:8088/test_jsf/faces/bonjour.jsf Je me rpte, mais en ce qui nous concerne le seul mapping des vues portant l'extension .xhtml est suffisant, nous n'avons pas besoin de mettre en place tous ces autres mappings. Enfin, vous remarquerez la dclaration d'un paramtre particulier aux lignes 4 7. Il s'agit d'une fonctionnalit utile propose par JSF : lors du dveloppement d'une application, il est recommand d'initialiser le paramtre nomm javax.faces.PROJECT_STAGE avec la valeur Development. Ceci va rendre disponibles de nombreuses informations de debugging et les afficher directement au sein de vos pages en cas de problme, permettant ainsi de tracer les erreurs rapidement. Lors du dploiement final, une fois l'application acheve, il suffit alors de changer la valeur du paramtre Production, et toutes ces informations non destines au public ne seront alors plus affiches. Le fichier web.xml que je vous donne ici en exemple est donc gnrique, il ne contient rien de spcifique ce projet en particulier et se contente d'tablir les proprits ncessaires au bon fonctionnement de JSF. En d'autres termes, vous pourrez rutiliser ce fichier tel quel pour tous vos projets JSF !

Tests & observations


Notre projet est maintenant prt pour utilisation : seuls ces deux Facelets et ce bean suffisent ! Vrifions pour commencer le bon fonctionnement de l'application en nous rendant sur l'URL http://localhost:8088/test_jsf/bonjour.xhtml depuis notre navigateur. V ous devez observer un champ de formulaire et un bouton de validation :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

546/606

Aprs un clic sur le bouton, vous devez alors tre redirigs vers la page de bienvenue affichant ce que vous avez tap dans le champ texte :

Essayons maintenant de changer la cible prcise dans l'attribut action du bouton par une page qui n'existe pas dans notre application. Modifiez par exemple la ligne 12 de la page bonjour.xhtml par : Code : HTML - Modification de l'attribut action <h:commandButton value="Souhaiter la bienvenue" action="connexion" />

Rendez vous alors nouveau sur la page http://localhost:8088/test_jsf/bonjour.xhtml, cliquez sur le bouton et observez l'erreur affiche :

V ous voil face au type d'informations dont je vous ai parl lorsque je vous ai expliqu l'intrt du paramtre PROJECT_STAGE, que nous avons mis en place dans notre web.xml : le contrleur JSF - la FacesServlet - est incapable de trouver une Facelet nomme connexion.xhtml , et JSF vous affiche donc automatiquement l'erreur, directement au sein de votre page ! Pratique pour identifier ce qui pose problme au premier coup d'oeil, n'est-ce pas ? Faisons maintenant le mme essai, mais cette fois en passant le paramtre javax.faces.PROJECT_STAGE "Production". Effectuez la modification dans le fichier web.xml, puis rendez vous une nouvelle fois sur la page depuis votre navigateur. Dornavant lorsque vous cliquez sur le bouton, aucune erreur ne s'affiche ! Et c'est normal, puisque le mode de production est destin une utilisation publique de l'application.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

547/606

V ous comprenez maintenant mieux l'intrt de ce paramtre : il permet de dfinir le mode de fonctionnement de l'application, et donc de dfinir si les messages de debug seront affichs ou non sur les pages affiches par l'utilisateur.

Nous allons maintenant annuler nos prcdentes modifications : remettons "Development" le paramtre dans le fichier web.xml, et remettons "bienvenue" dans le champ action de notre formulaire. Nous allons ensuite modifier l'expression EL contenue dans notre champ texte, et y prcisant un bean qui n'existe pas dans notre application. Modifiez par exemple la ligne 11 de la page bonjour.xhtml par : Code : HTML - Modification de l'expression EL <h:inputText value="#{inscriptionBean.nom}" />

Rendez vous alors nouveau sur la page http://localhost:8088/test_jsf/bonjour.xhtml, cliquez sur le bouton et observez l'erreur affiche :

Lors de l'valuation de l'expression EL, JSF ne trouve aucun bean nomm inscriptionBean et affiche donc une page d'erreur dtaillant le problme rencontr. Pour terminer, annulons cette dernire modification en mettant nouveau le bean bonjourBean dans l'expression EL, puis essayons de changer le nom de la mthode setter dans le bean. Modifiez par exemple les lignes 20 22 de la classe BonjourBean par : Code : Java - Modification de la mthode setter public void setPrenom( String nom ) { this.nom = nom; }

Rendez vous alors nouveau sur la page http://localhost:8088/test_jsf/bonjour.xhtml, cliquez sur le bouton et observez l'erreur affiche :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

548/606

Lors de l'valuation de l'expression EL, JSF ne trouve aucune mthode setter pour la proprit nom du bean bonjourBean : en effet, la rgle pour un JavaBean impose que la mthode soit correctement nomme, et puisque nous avons chang sa dnomination, JSF considre alors qu'il n'existe pas de mthode setter. Il nous prvient donc logiquement qu'une exception javax.el.PropertyNotWritableException est leve, et que la proprit nom est considr comme tant en lecture seule. Nous y voil, nous avons observ le fonctionnement correct de notre application et fait le tour des erreurs les plus courantes. Avant de passer aux bonnes pratiques, analysons brivement ce qui a chang par rapport notre ancienne mthode, sans JSF : auparavant, nous aurions d crire deux pages JSP, en lieu de place de nos deux simples Facelets ; auparavant, nous aurions d crire une servlet, charge : d'afficher la page bonjour.jsp lors de la rception d'une requte GET ; de rcuprer le contenu du champ texte du formulaire lors de la rception d'une requte POST, puis de s'en servir pour initialiser la proprit du bean BonjourBean, que nous aurions d'ailleurs d crer, avant de transmettre le tout sous forme d'attributs la page bienvenue.jsp pour affichage. a parat peu a priori, mais rendez vous bien compte de ce dont nous n'avons plus nous soucier avec JSF : 1. la manipulation des objets requte et rponse : avec JSF, nous n'avons pas conscience de ces objets, leur existence nous est masque par le framework . 2. l'extraction des paramtres contenus dans une requte HTTP : avec JSF, il nous suffit d'crire une EL au sein d'une Facelet, et le tour est jou. 3. l'initialisation manuelle des beans : avec JSF, le cycle de vie des beans annots est entirement gr par le framework ! aucun moment nous n'avons initialis un bean, et vrai dire, aucun moment nous n'avons cr une classe Java susceptible de pouvoir procder cette initialisation ! Tout ce que nous avons mis en place, ce sont deux Facelets et un bean... 4. la mise en place d'attributs dans un objet HttpServletRequest pour transmission une page JSP : avec JSF, il suffit d'crire une EL dans un composant de notre Facelet, et le framework s'occupe du reste. 5. la redirection manuelle vers une page JSP pour affichage : avec JSF, il suffit de prciser la page cible dans l'attribut action du composant <h:commandButton>. Ainsi, vous pouvez dj vous rendre compte de la simplification opre par JSF : rien que sur ce petit exemple extrmement simple, le code produire est bien plus lger que si nous avions fait du MVC la main !

Enfin, je vous invite examiner le code source HTML de la page bonjour.xhtml que vous visualisez depuis votre navigateur. V ous y trouverez le code HTML produit par le rendu des diffrents composants de votre Facelet, c'est-dire le rendu des balises <h:form>, <h:inputText>, etc. V ous comprendrez alors mieux ce que je vous annonais dans le chapitre prcdent : avec un framework bas sur les composants, vous n'tes plus aussi matre du HTML final envoy l'utilisateur que vous l'tiez lorsque vous criez vos vues entirement la main par l'intermdiaire de pages JSP.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

549/606

Les ressources
Pour terminer ce chapitre difficile et relativement abstrait sur une note plus lgre, nous allons dcouvrir le systme des ressources mis en place dans les projets JSF. V ous avez d vous en rendre compte, nous n'avons pas utilis de styles CSS dans notre prcdent exemple. Je vous rassure tout de suite, nous aurions pu faire comme nous en avions l'habitude, et dclarer une feuille CSS dans la section header de notre Facelet bonjour.xhtml : Code : HTML - Dclaration d'une feuille CSS <h:head> <title>Premier exemple JSF 2.0</title> <link type="text/css" rel="stylesheet" href="inc/form.css" /> </h:head>

Nous aurions alors bien videmment d prendre soin de recopier la feuille form.css et le dossier /inc que nous utilisions dans nos prcdents exemples, et de les placer la racine du projet :

Faites ces modifications, rendez vous une nouvelle fois sur la page http://localhost:8088/test_jsf/bonjour.xhtml depuis votre navigateur, et constatez les lgers changements intervenant dans l'affichage des lments HTML. Tout est donc normal, cependant nous n'allons pas procder de cette manire dans la suite du cours . Avec JSF 2.x, une bonne pratique est de placer toutes les ressources web telles que les fichiers CSS, les images ou les fichiers JavaScript, dans un rpertoire intitul resources et plac directement la racine de votre application, c'est--dire au mme niveau que le dossier /WEB-INF. Cette bonne pratique veut que nous mettions en place ce dossier resources , et que nous y crions des sous-dossiers pour dlimiter les diffrentes ressources web que nous souhaitons utiliser. Si par exemple nous avions des images, des feuilles CSS et des scripts JS, pour respecter la bonne pratique nous pourrions par exemple crer l'arborescence suivante et y placer nos diffrentes ressources :

Pour le moment, nous n'avons que le fichier form.css y mettre.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

550/606

Une fois ceci en place, nous pouvons maintenant utiliser un composant ddi l'inclusion de ressources depuis nos Facelets. Par exemple, pour utiliser la feuille CSS form.css place dans le dossier /resources/default/css , il suffit de remplacer l'inclusion traditionnelle via <link type="text/css" ... > par : Code : HTML <h:outputStylesheet library="default" name="css/form.css" />

Ce composant JSF permet de cibler directement le sous-dossier du rpertoire resources via le contenu de son attribut library , que nous avons donc renseign par "default" afin d'accder au sous-dossier que nous avons mis en place. Il cible enfin le fichier souhait via le contenu de son attribut name. Effectuez les modifications (cration du dossier /resources/default/css , dplacement du fichier form.css et suppression de l'ancien dossier /inc, puis modification de l'appel au fichier CSS depuis votre Facelet) et rendez vous nouveau sur la page depuis votre navigateur. V ous n'observerez aucun changement, preuve que le composant a bien fonctionn et a correctement inclus votre feuille CSS ! Le composant JSF ici utilis est ddi l'inclusion de feuilles CSS. Il en existe d'autres similaires, pour l'inclusion des fichiers JavaScript et des images. Nous y reviendrons le moment venu dans la suite du cours.

JSF, ou ne pas JSF ?


Faut-il opter pour JSF, ou du MVC avec des servlets/JSP faites maison ?

Dans le cas d'une application web destine votre apprentissage personnel, crer votre propre framework n'est pas une mauvaise ide, c'est un trs bon exercice. Cela dit, si le long terme est envisag pour votre application, ce n'est clairement pas judicieux. La plupart des frameworks MVC existant sont bien penss, et la majorit des imprvus y sont pris en compte. En outre, l'API d'un framework public reconnu est bien documente et maintenue par une communaut tierce. Dans un autre registre, si jamais votre application devient populaire et que vous devez intgrer des dveloppeurs supplmentaires votre projet, afin par exemple de satisfaire aux divers besoins du client, il est bien plus ais de trouver quelqu'un qui est dj familier avec un framework existant. Avec un framework fait maison et probablement bugg, vous trouverez peu de dveloppeurs prts se former sur une telle technologie et prendre en charge la maintenance sachant pertinemment qu'ils ne rutiliseront probablement jamais cette technologie dans leurs projets futurs... Enfin, sachez que tout cela ne s'applique pas uniquement JSF, mais galement tous les autres frameworks populaires existant, comme Spring MVC par exemple.

Quelle est la diffrence entre JSF et Servlets/JSP/HTML/CSS/JS ?

Pour faire une analogie avec une technologie d'actualit dans le domaine du web, comparer JSF au pur combo Servlets/JSP/HTML/CSS/JS revient comparer jQuery du pur JavaScript : faire plus avec moins de code. Pour prendre PrimeFaces comme exemple, explorez sa vitrine et dcouvrez des exemples de codes complets. RichFaces propose lui aussi une vitrine avec des exemples de codes complets. Si vous tudiez avec attention ces exemples, vous comprendrez alors que vous n'avez bien souvent pas vous soucier de la qualit du rendu HTML/CSS/JS d'une part, et d'autre part que vous n'avez besoin que d'un simple bean pour raliser le modle et d'une simple page XHTML pour la vue. Remarquez toutefois que vous ne devez pas voir JSF comme une alternative HTML/CSS/JS uniquement, mais bien prendre en compte galement la partie serveur (typiquement JSP/Servlets). JSF permet d'viter tout le code passe-partout en charge du regroupement des paramtres de requtes HTTP, de leur conversion/validation, de la mise jour des donnes du modle et de l'excution de la bonne mthode Java pour raliser les traitements mtiers. Avec JSF, vous n'avez plus qu'une page XHTML en guise de vue, et un JavaBean en tant que modle. Cela acclre le dveloppement de manire significative ! Bien entendu, comme c'est le cas avec tous les frameworks MVC bass sur les composants, vous disposez avec JSF d'une faible marge de contrle sur le rendu HTML/CSS/JS. Ajouter du code JS personnalis n'est par exemple pas chose aise. Si c'est un obstacle pour votre projet, regardez plutt du ct des frameworks MVC bass sur les actions comme Spring MVC. V ous devez toutefois savoir que vous allez devoir crire tout ce code HTML/CSS/JS vous-mme et par le biais de pages JSP, l o les Facelets de JSF vous offriraient des templates avancs.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

551/606

En rsum
JSF est un framework MVC bas sur les composants. C'est un standard, car il fait partie intgrante de la plate-forme Java EE 6. Avec JSF, une unique servlet joue entre autres le rle d'aiguilleur gant : la FacesServlet. Avec JSF, il n'est plus ncessaire d'crire de servlets : seuls des Facelets et des backing-beans sont ncessaires. Dans sa version actuelle, JSF tire partie des annotations Java pour simplifier grandement le dveloppement et la configuration. Une Facelet est une simple page XHTML, contenant des balises qui lient littralement la vue aux composants JSF, et leur associe d'ventuelles valeurs via des expressions EL. L'ensemble des balises d'une vue, qui peut tre constitue d'une ou plusieurs Facelets, constitue l'arbre des composants associ cette vue. Avec JSF, le processus de traitement d'une requte rassemble les tapes de rcupration, conversion, validation et sauvegarde des donnes. Lors du dveloppement d'une application JSF, il est possible d'activer un mode de debug pour faciliter le pistage des erreurs rencontres.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

552/606

La gestion d'un formulaire avec JSF


Maintenant que nous sommes familiers avec JSF, nous allons mettre en pratique et apprendre grer proprement un formulaire, en ajoutant de la complexit au fur et mesure de notre progression. Une fois n'est pas coutume, nous allons rcrire notre fonction d'inscription d'utilisateur. Comme d'habitude, nous allons ajouter un petit plus notre systme : aprs la cration initiale du systme en suivant MVC avec des JSP et des servlets, nous y avions ajout une base de donnes pour commencer, puis nous y avions intgr JPA. Eh bien cette fois nous allons bien videmment employer JSF, mais galement rendre la validation du formulaire... ajaxise ! Apptissant, n'est-ce pas ?

Une inscription classique Prparation du projet


Nous allons partir sur une base propre. Pour ce faire, crez un nouveau projet web dynamique sous Eclipse, et nommez-le pro_jsf . Crez-y alors un fichier de configuration /WEB-INF/glassfish-web.xml : Code : XML - /WEB-INF/glassfish-web.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app> <context-root>/pro_jsf</context-root> <class-loader delegate="true"/> <jsp-config> <property name="keepgenerated" value="true"> <description>Conserve une copie du code des servlets autognres.</description> </property> </jsp-config> </glassfish-web-app>

Copiez-y ensuite le fichier web.xml que nous avions mis en place dans le projet test_jsf , fichier que nous pouvons comme je vous l'avais expliqu rutiliser tel quel. Avant de poursuivre, je vous conseille de mettre en place un petite configuration particulire, afin d'viter de futurs ennuis. Par dfaut, le contenu d'un champ laiss vide dans vos formulaires sera considr comme une chane vide. Et vous devez le savoir, les chanes vides sont l'ennemi du dveloppeur ! Heureusement, il est possible de forcer le conteneur considrer un tel contenu comme une valeur nulle plutt que comme une chane vide, en ajoutant cette section dans votre fichier web.xml : Code : XML - Configuration de la gestion des chanes vides dans /WEB-INF/web.xml ... <context-param> <paramname>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</paramname> <param-value>true</param-value> </context-param> ...

V oil tout pour le moment, nous reviendrons sur l'intrt pratique de cette manipulation plus loin dans ce chapitre.

Cration de la couche d'accs aux donnes


La premire tape du dveloppement, si c'en est une, est la "cration" du modle. En ralit, nous n'avons rien faire ici : nous allons simplement rutiliser notre entit Utilisateur telle que nous l'avions dveloppe dans notre projet pro_jpa, ainsi que notre

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


EJB Stateless ! Copiez donc simplement dans votre projet : la classe com.sdzee.entities.Utilisateur, en conservant le mme package ; la classe com.sdzee.dao.DAOException, en conservant le mme package ; la classe com.sdzee.dao.UtilisateurDao, en conservant le mme package ; le fichier de configuration de JPA src/META-INF/persistence.xml , en conservant le mme rpertoire. V oici l'arborescence que vous devez obtenir une fois arrivs cette tape :

553/606

Cration du backing bean


Nous l'avons dcouvert dans le chapitre prcdent, un nouvel objet propre JSF fait son apparition : le backing bean. Il s'agit en ralit d'une sorte de mini-contrleur MVC, une sorte de glue qui relie la vue (la page JSF) au modle de donnes (l'entit). Cet objet est littralement li la vue, et tous les attributs de l'entit sont exposs la vue travers lui . Pour faire l'analogie avec ce que nous avions dvelopp dans nos prcdents exemples, cet objet va remplacer notre ancien InscriptionForm. Toutefois, nous n'allons pour le moment pas nous encombrer avec les mthodes de validation des diffrents champs du formulaire, et nous contenterons de mettre en place une inscription sans vrifications. Nous complterons ensuite notre systme, lorsque nous aurons construit une base fonctionnelle.

Sur la forme, ce backing-bean se prsente comme un bean classique, aux annotations JSF prs. Puisqu'il est associ une action, il est courant de le nommer par un verbe reprsentant l'action effectue. Nous allons donc logiquement dans le cadre de notre exemple crer un bean intitul InscrireBean : Code : Java - com.sdzee.beans.InscrireBean package com.sdzee.beans; import java.io.Serializable; import java.sql.Timestamp; import import import import import javax.ejb.EJB; javax.faces.application.FacesMessage; javax.faces.bean.ManagedBean; javax.faces.bean.RequestScoped; javax.faces.context.FacesContext;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import com.sdzee.dao.UtilisateurDao; import com.sdzee.entities.Utilisateur; @ManagedBean @RequestScoped public class InscrireBean implements Serializable { private static final long serialVersionUID = 1L; private Utilisateur utilisateur;

554/606

// Injection de notre EJB (Session Bean Stateless) @EJB private UtilisateurDao utilisateurDao; // Initialisation de l'entit utilisateur public InscrireBean() { utilisateur = new Utilisateur(); } // Mthode d'action appele lors du clic sur le bouton du formulaire // d'inscription public void inscrire() { initialiserDateInscription(); utilisateurDao.creer( utilisateur ); FacesMessage message = new FacesMessage( "Succs de l'inscription !" ); FacesContext.getCurrentInstance().addMessage( null, message ); } public Utilisateur getUtilisateur() { return utilisateur; } private void initialiserDateInscription() { Timestamp date = new Timestamp( System.currentTimeMillis() } utilisateur.setDateInscription( date );

); }

Notre objet contient tout d'abord une rfrence notre entit Utilisateur la ligne 20, laquelle est associe une mthode getter lignes 40 42. Cette entit Utilisateur est initialise depuis un simple constructeur public et sans argument aux lignes 27 29. Notre DAO Utilisateur, qui pour rappel est depuis l'introduction de JPA dans notre projet un simple EJB Stateless, est inject automatiquement via l'annotation @EJB la ligne 24, exactement comme nous l'avions fait depuis notre servlet dans le projet pro_jpa. Enfin, une mthode d'action nomme inscrire() est charge : d'initialiser la proprit dateInscription de l'entit Utilisateur avec la date courante, via la mthode initialiserDateInscription() que nous avons cre aux lignes 44 47 pour l'occasion ; d'enregistrer l'utilisateur en base, via un appel la mthode creer() du DAO Utilisateur ; d'initialiser un message de succs de la validation. Dans cette dernire tape, deux nouveaux objets apparaissent : FaceMessage : cet objet permet simplement de dfinir un message de validation, que nous prcisons ici en dur directement dans son constructeur. Il existe d'autres constructeurs, notamment un qui permet d'associer un message un niveau de criticit, en prcisant une catgorie qui est dfinie par FacesMessage.Severity. Les niveaux existants sont reprsents par des constantes que vous pouvez retrouver sur la documentation de l'objet FacesMessage. En ce qui nous concerne, nous ne spcifions qu'un message dans le constructeur, et c'est par consquent la criticit Severity.INFO qui est applique par dfaut notre message par JSF ; FaceContext : vous retrouvez l l'objet dont je vous ai annonc l'existence dans le chapitre prcdent, celui qui contient l'arbre des composants d'une vue ainsi que les ventuels messages d'erreur qui leurs sont associs. Eh bien ici,

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

555/606

nous nous en servons pour mettre en place un FacesMessage dans le contexte courant via la mthode addMessage(), pour que la rponse puisse ensuite l'afficher. Je vous laisse parcourir sa documentation, et nous reviendrons ensemble sur l'intrt de passer null en tant que premier argument de cette mthode lorsque que nous dvelopperons la vue. Par dfaut, nous avons annot notre bean avec @RequestScoped pour le placer dans la porte requte : en effet, notre objet ne va intervenir qu' chaque demande d'inscription et n'a donc pas vocation tre stock plus longtemps que le temps d'une requte. Au passage, vous remarquez ici pourquoi il est trs important d'avoir dcouvert comment fonctionne une application Java EE MVC sans framework : si vous n'aviez pas conscience de ce qui se passe derrire les rideaux, notamment des diffrentes portes existantes dans une application et des allers/retours de paires requte/rponse qui ont lieu chaque intervention de l'utilisateur depuis son navigateur, vous auriez beaucoup plus de mal saisir comment manipuler vos objets avec JSF !

Si vous avez bien observ le code de ce backing-bean, et si vous vous souvenez de celui de notre ancien objet mtier InscriptionForm, vous devrez instantanment vous poser la question suivante : O sont les mthodes de rcupration et conversion des valeurs envoyes depuis le formulaire ?

Eh oui, dans notre bean nous nous contentons ici simplement d'initialiser la date d'inscription dans notre entit Utilisateur, car ce n'est pas une information saisie par l'utilisateur. Mais en ce qui concerne toutes les autres proprits de notre entit, nous ne faisons strictement rien. V ous retrouvez ici ce que je vous ai expliqu dans la description du processus du traitement d'une requte avec JSF, le fameux parcours en six tapes : les tapes de rcupration, conversion, validation et enregistrement dans le modle sont entirement automatises ! Et c'est depuis la vue que nous allons directement effectuer les associations entre les champs du formulaire et les proprits de notre entit.

Cration de la vue
Nous devons ensuite crer la Facelet gnrant le formulaire d'inscription. Pour rappel, une Facelet n'est qu'une simple page XHTML contenant des balises JSF. Je vous donne ds maintenant le code de la vue dans son intgralit, prenez le temps de bien regarder les composants qui interviennent et nous en reparlons en dtails ensuite : Code : HTML - /inscription.xhtml <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <meta charset="utf-8" /> <title>Inscription</title> <h:outputStylesheet library="default" name="css/form.css" /> </h:head> <h:body> <h:form> <fieldset> <legend>Inscription</legend> <h:outputLabel for="email">Adresse email <span class="requis">*</span></h:outputLabel> <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" required="true" size="20" maxlength="60" /> <h:message id="emailMessage" for="email" errorClass="erreur" /> <br /> <h:outputLabel for="motdepasse">Mot de passe <span class="requis">*</span></h:outputLabel> <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" required="true" size="20" maxlength="20" />

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


<h:message id="motDePasseMessage" for="motdepasse" errorClass="erreur" /> <br /> <h:outputLabel for="confirmation">Confirmation du mot de passe <span class="requis">*</span></h:outputLabel> <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" required="true" size="20" maxlength="20" /> <h:message id="confirmationMessage" for="confirmation" errorClass="erreur" /> <br /> <h:outputLabel for="nom">Nom d'utilisateur <span class="requis">*</span></h:outputLabel> <h:inputText id="nom" value="#{inscrireBean.utilisateur.nom}" required="true" size="20" maxlength="20" /> <h:message id="nomMessage" for="nom" errorClass="erreur" /> <br /> <h:messages globalOnly="true" infoClass="info" /> <h:commandButton value="Inscription" action="#{inscrireBean.inscrire}" styleClass="sansLabel" /> <br /> </fieldset> </h:form> </h:body> </html>

556/606

V ous devez reconnatre la structure globale de la page, identiques aux premiers exemples de Facelets que nous avons mis en place dans le chapitre prcdent : un en-tte HTML particulier contenant la dclarations de la bibliothque de composants, prfixs par h: ; une section header contenant l'inclusion de la feuille de style CSS form.css , que vous prendrez soin de recopier depuis le projet test_jsf en recrant l'arborescence /resources/default/css dans votre projet ; puis le coprs de la page contenant un formulaire gnr par le composant <h:form>. Nous allons maintenant dtailler comment nous procdons la cration des diffrents lments du formulaire, en analysant les composants qui interviennent. Comme vous pourrez le constater en parcourant la documentation de chacune des balises, elles supportent toutes un nombre consquent d'attributs, nous allons par consquent limiter notre analyse ceux qui nous sont ici utiles. Si vous souhaitez connatre en dtails toutes les possibilits offertes par chaque balise, je vous invite parcourir leurs documentations en intgralit et faire vos propres tests pour vrifier que vous avez bien compris.

Les labels
Pour gnrer un label associ un champ de formulaire, concrtis par la balise HTML <label>, il faut utiliser le composant <h:outputLabel>. Le comportement de l'attribut for est identique celui de la balise HTML, il suffit d'y prciser l'id du champ de saisie auquel le label fait rfrence.

Les champs de saisie


Pour gnrer un champ de saisie de type texte, concrtis par la balise HTML <input type="text" ...>, il faut utiliser le composant <h:inputText>. Tout comme la balise HTML, il accepte les attributs id, value, size et maxlength. Pour gnrer un champ de saisie de type mot de passe, concrtis par la balise HTML <input type="password" ...>, il faut utiliser le composant <h:inputSecret>. Il accepte lui aussi les attributs id, value, size et maxlength. Petite nouveaut, nous utilisons un attribut nomm required, qui peut prendre comme valeur true ou false, et qui va dterminer si l'utilisateur doit obligatoirement saisir des donnes dans ce champ ou non. En ralit, il s'agit l d'un marqueur qui va tre appliqu au composant, et qui va permettre de gnrer une erreur lors de la validation de la valeur du champ associ : si un champ est marqu comme requis et qu'aucune valeur n'est entre par l'utilisateur, alors un message d'erreur sera

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

557/606

automatiquement plac dans le FacesContext par JSF, et nous pourrons ensuite l'afficher l'utilisateur dans la rponse. Ce qu'il faut bien observer ici, c'est l'emploi d'expressions EL pour cibler la proprit utilisateur de notre backing-bean, par exemple #{inscrireBean.utilisateur.email} la ligne 13. Comme je vous l'ai dj dit, tous les attributs de l'entit sont exposs la vue travers le backing-bean. V oil pourquoi nous devons d'abord cibler l'entit utilisateur, qui est elle-mme une proprit du backing-bean inscrireBean, puis cibler la proprit de l'entit dsire. V ous comprenez mieux maintenant pourquoi je vous ai dit que ce bean tait la glue qui reliait la vue au modle ! En outre, comprenez bien que la liaison cre par l'intermdiaire de cette expression EL place dans l'attribut value d'un composant de saisie est bi-directionnelle : lors du rendu de la page HTML, la valeur contenue dans le modle qui est retourne par cette expression EL sera affiche dans le champ de saisie ; lors de l'envoi des donnes du formulaire par l'utilisateur, la valeur contenue dans le champ de saisie sera utilise pour mettre jour la valeur contenue dans le modle.

Les messages
Depuis le temps que je vous parle de ces fameux messages d'erreurs, nous y voil. La diffrence la plus marquante entre la page JSP que nous avions utilise jusqu' prsent et notre Facelet frachement cre est l'utilisation d'un composant JSF pour gnrer un message associ un champ de saisie en particulier. Il s'agit du composant <h:message>. Entre autres, celui-ci accepte deux attributs qui nous sont ici utiles : for, pour cibler l'id du champ concern ; errorClass , pour permettre de donner une classe CSS particulire lors de l'affichage du message gnr s'il s'agit d'une erreur. Ce composant va donc afficher, lors du rendu de la rponse, l'ventuel message associ au champ cibl par l'attribut for. S'il s'agit d'un message d'erreur, que JSF sait diffrencier des messages d'informations qui peuvent ventuellement tre placs dans le FacesContext grce au niveau de criticit associ un message dont je vous ai parl un peu plus tt, alors le style erreur dfini dans notre feuille CSS sera appliqu. Nous utilisons en fin de page un autre lment responsable de l'affichage de messages l'utilisateur : le composant <h:messages>. Par dfaut, celui-ci provoque l'affichage de tous les messages disponibles dans la vue, y compris ceux qui sont dj affichs via un <h:message> ailleurs dans la page. Toutefois, il est possible de n'afficher que les messages qui ne sont attachs aucun composant dfini, c'est--dire les messages dont l'id est null, en utilisant l'attribut optionnel globalOnly="true" : Code : HTML <h:messages globalOnly="true" />

V ous comprenez maintenant pourquoi dans la mthode inscrire() de notre backing-bean, nous avons pass null en paramtre de la mthode FacesContext.addMessage() : c'est pour pouvoir distinguer notre message caractre gnral (nous nous en servons pour stocker le rsultat final de l'inscription) des messages lis aux composants de la vue. Comprenez donc bien que le code suivant dans notre backing-bean : Code : Java facesContext.addMessage("clientId", facesMessage);

Attacherait le message donn au composant <h:message for="clientId">, et que nous passons null pour n'attacher notre message aucun composant existant. Notre message a ainsi un caractre global , voil d'ailleurs pourquoi l'attribut de la balise <h:messages> permettant de cibler uniquement ce type de messages s'intitule... globalOnly ! Enfin, nous utilisons l'attribut infoClass pour donner notre message global le style info qui est dfini dans notre feuille CSS. Nous pourrions utiliser galement l'attribut styleClass , mais puisque JSF permet de diffrencier les messages selon leur gravit, autant en profiter !

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

558/606

Le bouton d'envoi
Pour gnrer un bouton de soumission de formulaire, concrtis par la balise HTML <input type="submit" ...>, il faut utiliser le composant <h:commandButton>. Nous l'avons dj tudi dans notre premier exemple : le contenu de son attribut value est affich en tant que texte du bouton HTML gnr, et son attribut action permet de dfinir la navigation. la diffrence de notre premier exemple cependant, o nous redirigions l'utilisateur vers une autre page, nous utilisons ici une expression EL pour appeler une mthode de notre backing-bean, en l'occurrence notre mthode d'action inscrire(). V ous retrouvez ici ce que je vous ai expliqu en vous prsentant la technologie EL : les expressions se basant sur la syntaxe #{...} permetter d'appeler n'impote quel mthode d'un bean, et pas seulement une mthode getter comme c'tait le cas avec ${...}. Nous utilisons enfin l'attribut styleClass , pour appliquer au bouton HTML gnr la classe sansLabel dfinie dans notre feuille CSS.

Nous avons fait le tour de tout ce qu'il faut savoir sur notre lger systme d'inscription. Pour le moment, aucun contrle de validation n'est effectu hormis les simples required="true" sur les champs du formulaire. De mme, aucune information n'est affiche hormis un message d'erreur sur chaque champ laiss vide, et notre message de succs lorsque l'inscription fonctionne.

Tests & observations


Notre projet est maintenant prt pour utilisation : en fin de compte, seuls une Facelet et un backing bean sont suffisant, le reste tant rcupr depuis notre projet JPA. Vrifions pour commencer le bon fonctionnement de l'application en nous rendant sur l'URL http://localhost:8088/pro_jsf/bonjour.xhtml depuis notre navigateur. V ous devez observer le formulaire d'inscription tel qu'il existait dans nos prcdents exemples :

Si ce n'est pas le cas, c'est que vous avez oubli quelque chose en cours de route. Vrifiez bien que vous avez : copi les classes Utilisateur, UtilisateurDao et DAOException en conservant leurs packages respectifs, depuis le projet pro_jpa ; copi le fichier META-INF/persistence.xml depuis le projet pro_jpa ; copi le fichier form.css depuis le projet test_jsf , en le plaant dans l'arborescence /resources/default/css/ ; copi le fichier web.xml depuis le projet test_jsf ; dmarr votre serveur MySQL ; dmarr votre serveur GlassFish ; dploy votre projet pro_jsf sur le serveur GlassFish.

Le formulaire s'affichant correctement, nous pouvons alors tester une inscription. Pour ce premier cas, nous allons essayer avec des informations valides, et qui n'existent pas dj en base. Par exemple, avec une adresse jamais utilise auparavant, deux mots de passes corrects et identiques et un nom d'utilisateur suffisamment long :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

559/606

Aprs un clic sur le bouton d'inscription, l'inscription fonctionne et le message de succs est affich :

Nous constatons alors que : c'est la page courante qui est recharge. Comme je vous l'ai dj expliqu, par dfaut et sans rgle de navigation particulire prcise par le dveloppeur, c'est la page courante qui est automatiquement utilise comme action d'un formulaire JSF ; le contenu des champs de saisie des mots de passe n'est pas r-affich, alors que l'expression EL est bien prsente dans l'attribut value des champs. Ce comportement est voulu, car il ne faut jamais retransmettre un mot de passe aprs validation d'un formulaire. Nous avions d'ailleurs pris garde ne pas le faire dans notre ancienne page JSP d'inscription, si vous vous souvenez bien. Avec JSF, le composant <h:inputSecret> est programm pour ne pas renvoyer son contenu, il n'y a donc plus d'erreur d'inattention possible ; le message de succs est bien dcor avec le style dcrit par la classe info de notre feuille CSS. JSF a donc bien utilis la classe prcise dans l'attribut infoClass de la balise <h:messages>, ce qui est une preuve que le framework a bien attribu par dfaut le niveau Severity.INFO au message que nous avons construit depuis notre backing-bean.

Essayons maintenant de nous inscrire en entrant des informations invalides, comme par exemple une adresse mail dj utilise (celle que vous venez de saisir pour raliser l'inscription prcdente ira trs bien) :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

560/606

Nous constatons alors l'chec de notre systme, qui plante et affiche un joli message de debug JSF. Toutefois, pas d'inquitude, c'tait prvu : puisque nous n'avons encore mis en place aucun contrle, l'inscription a t tente sans vrifier auparavant si l'adresse email existait dj en base, et MySQL a retourn une exception lors de cette tentative car il a trouv une entre contenant cette adresse dans la table Utilisateur. C'est trs fcheux, mais nous n'allons pas nous occuper de ce problme tout de suite. Poursuivons nos tests, et essayons cette fois-ci de nous inscrire en laissant plusieurs champs du formulaire vides. Par exemple, retournons sur le formulaire, saisissons uniquement une adresse email et cliquons sur le bouton d'inscription :

Nous constatons alors l'affichage de messages d'erreurs ct de chacun des champs laisss vides : c'est le fruit du composant <h:message> ! En outre, ces messages tant spcifis comme tant des erreurs par JSF, le framework utilise l'attribut errorClass et les dcore avec la classe erreur de notre feuille CSS : voil pourquoi ces messages apparaissent en rouge. Par contre, nous observons que ces messages automatiquement gnrs par JSF sont vraiment bruts de dcoffrage... Il nous faut trouver un moyen de les rendre plus user-friendly , et c'est ce quoi nous allons nous atteler ds maintenant.

Amliorations des messages affichs lors de la validation


Les messages automatiques gnrs par JSF sur chaque champ de notre formulaire sont vraiment laids, et cela s'explique trs simplement : par dfaut, JSF fait prcder ses messages d'erreurs des identifiants des objets concerns. En l'occurrence, il a concatn l'identifiant de notre formulaire (j_idt7 ) et celui de chaque champ (motdepasse, confirmation et nom) en les sparants par le caractre :. Quand avons-nous donn cet id barbare notre formulaire ?

Eh bien en ralit, nous ne lui avons jamais donn d'identifiant, et JSF en a donc gnr un par dfaut, voil pourquoi il est si laid. Ainsi pour rendre ces messages moins repoussants, nous pouvons donc commencer par donner un id notre formulaire. Nous allons par exemple l'appeler "formulaire", en changeant sa dclaration dans notre Facelet de <h:form> <h:form

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


id="formulaire">. Appliquez cette modification au code, puis tentez nouveau le prcdent test :

561/606

C'est dj mieux, mais a reste encore brut. Si nous regardons la documentation du composant <h:inputText>, nous remarquons qu'il prsente un attribut intitul label , qui est utilis pour reprsenter un champ de manire littrale. Nous allons donc ajouter un attribut label chacune des balises dclarant un composant <h:inputText> ou <h:inputSecret> dans notre Facelet : Code : HTML - Ajout d'un label sur chaque composant <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" ... label="Email" /> <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" ... label="Mot de passe" /> <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" ... label="Confirmation" /> <h:inputText id="nom" value="#{inscrireBean.utilisateur.nom}"... label="Nom" />

Effectuez ces modifications, puis faites nouveau le test :

C'est un peu mieux, mais c'est encore brut, et c'est toujours en anglais... Pour rgler ce problme une fois pour toutes, nous allons utiliser l'attribut requiredMessage des composants de saisie JSF, qui d'aprs leur documentation permet de dfinir le message utilis lors de la vrification de la rgle dfinie par l'attribut required. En d'autres termes, nous allons y spcifier directement le message d'erreur afficher lorsque le champ est laiss vide ! Nous allons donc laisser tomber nos attributs label , et les remplacer par ces nouveaux attributs requiredMessage : Code : HTML - Ajout d'un requiredMessage sur chaque composant <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" ... requiredMessage="Veuillez saisir une adresse email" /> <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" ... requiredMessage="Veuillez saisir un mot de passe" /> <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" ...

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


requiredMessage="Veuillez saisir la confirmation du mot de passe" /> <h:inputText id="nom" value="#{inscrireBean.utilisateur.nom}" ... requiredMessage="Veuillez saisir un nom d'utilisateur" />

562/606

Effectuez ces modifications, puis faites nouveau le test :

Nous y voil, les messages sont finalement propres et comprhensibles par les utilisateurs. Petit bmol toutefois, point de vue qualit du code, c'est un peu sale de dfinir directement en dur dans la vue les messages d'erreurs afficher... Comme le hasard fait trs bien les choses, il existe justement une fonctionnalit dans JSF qui permet de dfinir des messages dans un fichier externe, et d'y faire des rfrences depuis les Facelets. Ce systme s'appelle un bundle, et nous allons en mettre un en place dans notre exemple.

Mise en place d'un bundle


Un bundle n'est rien d'autre qu'un fichier de type Properties, contenant une liste de messages. La premire tape dans la cration d'un bundle consiste donc crer un fichier Properties et y placer nos diffrents messages de validation. Nous allons par exemple nommer notre fichier messages.properties . Il faut le placer dans les sources du projet, aux cts du code de l'application. En l'occurrence nous allons le placer dans un package nomm com.sdzee.bundle : Code : Properties - com.sdzee.bundle.messages inscription.email = Veuillez saisir une adresse email inscription.motdepasse = Veuillez saisir un mot de passe inscription.confirmation = Veuillez saisir la confirmation du mot de passe inscription.nom = Veuillez saisir un nom d'utilisateur

Nous avons ici plac nos quatre messages, identifis par le nom de la Facelet qui en fait usage (inscription) suivi d'un point et du nom du champ concern. La syntaxe respecter est celle d'un fichier de type Properties Java classique. Une fois ce dossier en place, il faut maintenant le charger depuis notre Facelet pour qu'elle puisse faire rfrence son contenu. Pour ce faire, nous allons utiliser le composant <f:loadBundle>. Celui-ci doit tre plac dans la page avant les balises qui en feront usage, typiquement nous pouvons le mettre dans le header de notre page : Code : HTML - Ajout du chargement du bundle dans le header de la Facelet inscription.xhtml <h:head> ... <f:loadBundle basename="com.sdzee.bundle.messages" var="msg"/> </h:head>

Cette balise attend uniquement deux attributs : basename, qui contient le chemin complet dans lequel est plac le fichier (le package suivi du nom du fichier) ; var, qui permet de dfinir par quel nom le bundle sera dsign dans le reste de la page.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

563/606

Nous avons donc prcis le chemin com.sdzee.bundle.messages, et nous utiliserons le nom msg pour faire rfrence notre bundle. Effectuez cet ajout dans le header de votre Facelet. Il ne nous reste maintenant plus qu' remplacer nos messages, actuellement en dur dans les attributs requiredMessage, par une rfrence vers les messages prsents dans le bundle. V ous vous en doutez peut-tre dj, iI suffit pour cela d'utiliser des expressions EL ! V oil comment nous allons procder : Code : HTML - Remplacement des messages en dur dans les attributs requiredMessage <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" ... requiredMessage="#{msg['inscription.email']}" /> <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" ... requiredMessage="#{msg['inscription.motdepasse']}" /> <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" ... requiredMessage="#{msg['inscription.confirmation']}" /> <h:inputText id="nom" value="#{inscrireBean.utilisateur.nom}" ... requiredMessage="#{msg['inscription.nom']}" />

La forme de l'expression EL utilise est simple : nous ciblons le bundle via son nom msg , puis nous utilisons la notation avec les crochets pour cibler le nom souhait. Effectuez ces dernires modifications dans le code de votre Facelet, puis testez nouveau votre formulaire. Si vous n'avez rien oubli et si vous avez correctement positionn votre fichier bundle, vous devriez observer exactement le mme comportement que lors du test effectu avec les messages crits en dur. L'intrt de cette technique, c'est bien videmment d'crire un code plus propre dans vos Facelets, mais surtout de regrouper tous vos messages de validation dans un seul et unique fichier. Pratique, notamment pour l'internationalisation ! Par exemple, si vous souhaitez traduire votre application dans une langue diffrente, vous n'aurez alors qu' changer de bundle : vous n'aurez pas besoin de repasser sur chacune de vos Facelets pour traduire les messages un par un !

Nous allons nous arrter l pour les tests et amliorations. Passons maintenant au paragraphe suivant, afin de rendre tout ce mcanisme... ajaxis !

Une inscription ajaxise Prsentation


Qu'est-ce que c'est, une inscription "ajaxise" ?

AJAX est l'acronyme d'Asynchronous Javascript and XML, ce qui en franais signifie littralement Javascript et XML asynchrones . Derrire cette appellation se cache un ensemble de technologies qui permettent la mise jour d'un fragment d'une page web sans que le rechargement complet de la page web visite par l'utilisateur ne soit ncessaire. C'est ce type de technologie qui permet certains sites de proposer des fonctionnalits avances et intuitives leurs utilisateurs, citons par exemple le site du zro qui propose l'auto-compltion lors de la saisie dans le champ de recherche d'un membre, ou encore le site Stackoverflow avec son systme de vote en direct sur les rponses poses et questions apportes. Ainsi lorsque je parle d'inscription ajaxise, je dsigne en ralit le fait de pouvoir valider le contenu de chacun des champs de notre formulaire d'inscription sans ncessiter un clic sur le bouton d'envoi, ni ncessiter un rechargement de la page entire.

L'AJAX avec JSF


Si nous travaillions toujours la main, il nous faudrait mettre les mains dans le cambouis et mettre en place du JavaScript, des traitements spciaux dans nos servlets pour ne dclencher l'actualisation que d'un morceau de la page visite par l'utilisateur, etc. Heureusement, avec JSF nous allons pouvoir garder nos mains propres : le framework nous propose un moyen ultra-simple pour court-circuiter le processus classique de traitement d'une requte, et permettre un composant de s'actualiser de manire indpendante, et non pas dans le flot complet de l'arbre des composants prsents dans la vue courante comme c'est le cas traditionnellement.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

564/606

La solution offerte se matrialise sous la forme... d'un composant ! C'est cette fois une balise de la bibliothque Core que nous allons utiliser : la bien nomme <f:ajax>. Sans plus attendre, je vous propose le nouveau code de notre Facelet, et nous en discutons ensuite : Code : HTML - /inscription.xhtml <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <meta charset="utf-8" /> <title>Inscription</title> <h:outputStylesheet library="default" name="css/form.css" /> <f:loadBundle basename="com.sdzee.bundle.messages" var="msg"/> </h:head> <h:body> <h:form id="formulaire"> <fieldset> <legend>Inscription</legend> <h:outputLabel for="email">Adresse email <span class="requis">*</span></h:outputLabel> <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" required="true" size="20" maxlength="60" requiredMessage="#{msg['inscription.email']}"> <f:ajax event="blur" render="emailMessage" /> </h:inputText> <h:message id="emailMessage" for="email" errorClass="erreur" /> <br /> <h:outputLabel for="motdepasse">Mot de passe <span class="requis">*</span></h:outputLabel> <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" required="true" size="20" maxlength="20" requiredMessage="#{msg['inscription.motdepasse']}"> <f:ajax event="blur" render="motDePasseMessage" /> </h:inputSecret> <h:message id="motDePasseMessage" for="motdepasse" errorClass="erreur" /> <br /> <h:outputLabel for="confirmation">Confirmation du mot de passe <span class="requis">*</span></h:outputLabel> <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" required="true" size="20" maxlength="20" requiredMessage="#{msg['inscription.confirmation']}"> <f:ajax event="blur" render="confirmationMessage" /> </h:inputSecret> <h:message id="confirmationMessage" for="confirmation" errorClass="erreur" /> <br /> <h:outputLabel for="nom">Nom d'utilisateur <span class="requis">*</span></h:outputLabel> <h:inputText id="nom" value="#{inscrireBean.utilisateur.nom}" required="true" size="20" maxlength="20" requiredMessage="#{msg['inscription.nom']}"> <f:ajax event="blur" render="nomMessage" /> </h:inputText> <h:message id="nomMessage" for="nom" errorClass="erreur" /> <br /> <h:messages globalOnly="true" infoClass="info" />

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


<h:commandButton value="Inscription" action="#{inscrireBean.inscrire}" styleClass="sansLabel"> <f:ajax execute="@form" render="@form" /> </h:commandButton> <br /> </fieldset> </h:form> </h:body> </html>

565/606

Les diffrences apparentes avec la version prcdente de la Facelet sont minimes, seules quelques lignes font leur apparition : chaque balise <h:inputText> et <h:inputSecret> contient dornavant un corps, et dans se corps est placs la fameuse balise <f:ajax> ; de mme, la balise <h:inputCommand> contient dornavant elle aussi un corps, dans lequel est loge une balise <f:ajax>. tudions la construction de cette nouvelle balise. Seuls deux de ses attributs nous sont utiles dans les champs de saisie : event, qui nous permet de dfinir l'action dclenchant l'envoi de la requte AJAX au serveur ; render, qui nous permet de dfinir le ou les composant(s) dont le rendu doit tre effectu, une fois la requte traite par le serveur. Autrement dit, c'est extrmement simple : il suffit de placer la balise <f:ajax> dans le corps du champ qui est concern par l'vnement dclencheur, et de prciser dans l'attribut render le(s) composant(s) pour le(s)quel(s) nous souhaitons relancer un rendu graphique. Dans notre cas, ce qui nous intresse c'est de faire valider le contenu d'un champ ds que l'utilisateur a termin la saisie, et d'actualiser le message associ au champ en consquence : l'event que nous utilisons pour considrer que l'utilisateur a termin la saisie du contenu d'un champ s'intitule blur. Cette proprit JavaScript va permettre de dclencher une requte ds que le champ courant perd le focus, c'est--dire ds que l'utilisateur clique dans un autre champ, ou ailleurs sur la page, ou bien lorsqu'il navigue en dehors du champ via la touche tabulation de son clavier par exemple ; pour dsigner quel composant actualiser, nous devons simplement prciser son id dans l'attribut render.

La balise <f:ajax> prsente sur chaque champ de saisie va soumettre (et donc valider !) seulement le contenu du champ courant, et dclencher un raffichage du message associ lorsque le champ va perdre le focus. La balise <f:ajax> place quant elle sur le bouton de validation est un peu diffrente des autres : elle permet d'effectuer un envoi complet, mais l encore ajaxis et non pas simplement un envoi classique avec rechargement de la page. Pour ce faire, vous constatez l'emploi d'un motcl un peu spcial : @form . Il existe quatre marqueurs de la sorte : @this : dsigne le composant englobant la balise <f:ajax> ; @form : dsigne le formulaire entier ; @all : dsigne l'arbre des composants de la page entire ; @none : ne dsigne aucun composant. En prcisant @form dans l'attribut render, nous nous assurons ainsi que tout le formulaire va tre actualis lors d'un clic sur le bouton d'envoi. Par ailleurs, nous n'utilisons plus l'attribut event comme nous le faisions sur les champs de saisie, car nous savons trs bien que c'est un clic sur le bouton qui va dclencher l'action. Nous utilisons cette fois l'attribut execute, qui permet de dfinir la porte de l'action effectuer : en l'occurrence, nous souhaitons bien traiter le formulaire complet. Effectuez ces modifications dans votre Facelet, puis testez nouveau votre formulaire d'inscription. chaque fois que vous allez cliquer dans un champ de saisie, puis en dehors de ce champ, vous pourrez observer l'actualisation des ventuels messages d'erreurs associs chaque champ, et ce presque en temps rel ! En fin de compte, JSF offre un masquage total de ce qui se passe sous la couverture ! Il suffit d'inclure une simple balise dans le corps d'un composant, et le tour est jou. C'est d'une facilit la fois admirable et dconcertante !

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

566/606

L'importance de la porte d'un objet


Avant de passer la suite, je tiens vous faire remarquer quelque chose de trs inquitant : nous avons oubli de nous soucier de la porte de notre backing-bean ! Souvenez, celle que nous avions dclare via l'annotation @RequestScoped. Nous ne nous en tions pas inquits plus que a sur le moment, mais maintenant que nous avons ajaxis notre formulaire, cette notion de porte va prendre de l'importance. Actuellement, notre objet est dans la porte requte. Cela signifie que notre bean ne vit que le temps d'une requte : il est cr par JSF l'arrive d'une requte sur le serveur, puis dtruit une fois la rponse renvoye. Autrement dit notre formulaire provoque la cration et la destruction d'un bean sur le serveur chaque fois que l'utilisateur change de champ ! C'est du gchis de performances. Nous pourrions envisager de mettre notre objet en session plutt que dans la porte requte. Oui, mais la session est un objet qu'il est coteux de maintenir pour le serveur, car il faut conserver un unique objet pour chaque visiteur utilisant l'application. V oil pourquoi en pratique, il ne faut utiliser la session que lorsque c'est imprativement ncessaire, comme pour raliser un panier d'achats par exemple, o les items commands doivent absolument tre conservs tout au long de la navigation du visiteur. Dans notre simple systme d'inscription, garder nos informations serait du pur gchis de mmoire. Nous devons donc imprativement rflchir ce type de problmatique lorsque nous dveloppons une application : car plus il y a d'utilisateurs, plus le gaspillage de ressources va tre susceptible de poser des problmes que vous ne pouvez pas identifier par de simples tests momo-utilisateur. En d'autres termes, votre application peut trs bien donner l'impression de fonctionner au poil et d'tre parfaitement code, jusqu'au jour o un nombre critique d'utilisateurs simultans va faire surgir des problmes imprvus. Pour revenir notre cas, il existe une porte qui se place entre la requte et la session, et qui semble donc parfaite pour ce que nous faisons ici : le scope de conversation. Il permet de conserver un objet tant qu'une mme vue est utilise par un mme utilisateur. Cela signifie que tant qu'un utilisateur effectue des requtes depuis une seule et mme page, alors l'objet est conserv sur le serveur et rutilis. Ds que l'utilisateur effectue une requte vers une autre vue, alors l'objet est finalement dtruit. Dans notre cas, c'est parfait : notre formulaire provoque l'envoi de plusieurs requtes vers le serveur pour la validation de chacun des champs, et toutes constituent un change continu entre le serveur et une unique vue. Nous allons donc placer notre objet dans cette porte, pour limiter le gchis de ressource sur le serveur. Pour dclarer un backing-bean dans cette porte, il faut l'annoter avec @ViewScoped, et non plus avec @RequestScoped. V oil tout ce qu'il nous faut changer pour optimiser notre application ! titre d'exercice, et si vous avez dj parcouru l'annexe sur le dbuggage de projet avec Eclipse, je vous encourage utiliser le mode debug pour vrifier de vos propres yeux la conservation d'une instance du backing-bean d'une requte l'autre avec le scope de conversation, et sa destruction avec le scope de requte.

Une inscription contrle Dporter la validation de la vue vers l'entit


Nous nous sommes jusqu' prsent contents d'effectuer une simple vrification sur les champs du formulaire, directement dans la vue grce aux attributs required et requiredMessage. Nous allons dplacer ces conditions de la vue vers notre modle, savoir notre entit, grce de simples annotations ! Le serveur GlassFish fournit par dfaut un moyen de validation, identifi sous le nom de JSR 303 : il contient diffrentes contraintes de validations sous forme d'annotations, par exemple @NotNull qui permet d'indiquer qu'une proprit ne peut tre laisse vide. Premire tape, nous allons donc supprimer de notre Facelet les quelques required et requiredMessage prsents sur les champs de notre formulaire, et que nous utilisions justement pour indiquer que nos champs ne pouvaient pas tre laisss vides. Il nous suffit ensuite d'diter notre entit Utilisateur et d'y ajouter les annotations sur les attributs correspondants pour remettre en place ces contraintes. V oici le code de notre entit reprise et complte (j'omets ici les mthodes getters/ setters pour ne pas encombrer le code inutilement) : Code : Java - com.sdzee.entities.Utilisateur package com.sdzee.entities; import java.sql.Timestamp; import javax.persistence.Column; import javax.persistence.Entity;

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


import import import import javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.validation.constraints.NotNull;

567/606

@Entity public class Utilisateur { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private Long id; @NotNull private String email; @Column( name = "mot_de_passe" ) @NotNull private String motDePasse; @NotNull private String nom; @Column( name = "date_inscription" ) private Timestamp dateInscription; } ....

Les seuls ajouts effectus sont les trois annotations @NotNull, issues du package javax.validation.constraints. Par contre, comprenez bien que ceci ne fonctionnera que si vous avez suivi mon conseil en dbut de chapitre, et ajout javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL en paramtre de contexte. Sans cette configuration, les champs du formulaire laisss vides seraient traits comme des chanes vides par votre serveur, et par consquent ne seraient pas dtectes comme nulles par ces annotations frachement mises en place dans l'entit. Le seul cas o vous pourriez vous passer de cette configuration se prsente si vous utilisiez Hibernate en guise de framework ORM, et que vous utilisiez l'annotation spcifique Hibernate intitule @NotEmpty. Cepedant comme je vous l'ai dj expliqu, si vous commencez mettre du code spcifique Hibernate dans votre application, vous ne pourrez plus revenir en arrire, c'est--dire changer d'implmentation de JPA, sans modifier votre code...

Une fois ces lgres modifications effectues, ouvrez nouveau la page d'inscription dans votre navigateur, et tentez une nouvelle fois de vous inscrire en laissant des champs vides. V ous constaterez alors que les contraintes fonctionnent bien, mais que les messages de validation sont une nouvelle fois trop gnriques :

Pas d'inquitude, nous allons pouvoir les personnaliser directement depuis notre entit en compltant nos annotations : Code : Java - com.sdzee.entities.Utilisateur ... @NotNull( message = "Veuillez saisir une adresse email" ) private String email; @Column( name = "mot_de_passe" )

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


@NotNull( message private String @NotNull( message private String ... = "Veuillez saisir un mot de passe" ) motDePasse; = "Veuillez saisir un nom d'utilisateur" ) nom;

568/606

Il suffit comme vous pouvez l'observer dans cet exemple de prciser entre parenthse un attribut message l'annotation @NotNull. Nous voil de retour une situation similaire celle que nous observions lorsque nous effectuions la validation directement depuis notre vue :

Quelle est la mthode de validation prfrer : depuis la vue, ou depuis l'entit ?

Cela dpend principalement des contraintes du projet. Par exemple, si l'application doit pouvoir fonctionner sur un serveur lger comme Tomcat sans support des EJB ni de JPA, alors il ne faudra pas utiliser la validation depuis l'entit mais lui prfrer la validation depuis la vue JSF.

Affiner les contrles effectus


Allons un petit peu plus loin, et essayons de nous rapprocher davantage du fonctionnement de notre formulaire lorsque nous utilisions une page JSP et notre objet mtier fait maison. Nous souhaitons affiner la validation des diffrents champs de notre formulaire. Dans notre ancien objet metier, nous procdions aux vrifications suivantes : que l'adresse respecte bien le format standard d'une adresse email ; que le mot de passe soit long de 3 caractres ou plus ; que le nom soit long de 3 caractres ou plus. Le hasard fait encore une fois bien les choses :l'annotation @Pattern fournie par la JSR 303 est parfaite pour vrifier le format de l'adresse grce l'utilisation d'expressions rgulires, et l'annotation @Size est parfaite pour vrifier la taille des champs ! V oici le code complt : Code : Java - com.sdzee.entities.Utilisateur ... @NotNull( message = "Veuillez saisir une adresse email" ) @Pattern( regexp = "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)", message = "Merci de saisir une adresse mail valide" ) private String email; @Column( name = "mot_de_passe" ) @NotNull( message = "Veuillez saisir un mot de passe" ) @Size( min = 3, message = "Le mot de passe doit contenir au moins 3

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


caractres" ) private String motDePasse; @NotNull( message = "Veuillez saisir un nom d'utilisateur" ) @Size( min = 3, message = "Le nom d'utilisateur doit contenir au moins 3 caractres" ) private String nom; ...

569/606

Les documentations respectives des deux annotations expliquent que l'attribut regexp permet de dfinir une expression rgulire qui sera applique au champ cibl par l'annotation @Pattern, et que l'attribut min permet de dfinir la taille minimale autorise pour le champ cibl par l'annotation @Size. Effectuez ces ajouts, puis testez nouveau de vous inscrire en saisissant un nom ou un mot de passe trop court, et une adresse email dont le format est incorrect :

Rendez-vous bien compte de la simplicit avec laquelle nous avons mis en place ces quelques vrifications : de simples annotations places sur les champs de l'entit suffisent ! Alors qu'auparavant, nous avions d crire pas loin d'une centaine de ligne de code dans notre objet mtier, rien que pour mettre en place ces vrifications et grer les exceptions proprement... Par ailleurs, nous n'avons pas mis en place ce type de contrle dans notre application, mais nous pourrions tout fait vrifier que le mot de passe fourni par l'utilisateur prsente un niveau de scurit suffisamment lev. Pour ce faire, l'annotation @Pattern se rvle une nouvelle fois trs utile. V oici par exemple le code mettre en place afin de s'assurer que le mot de passe entr par l'utilisateur contient au moins 8 caractres, dont au moins un chiffre, une lettre minuscule et une lettre majuscule : Code : Java - Vrification du niveau de scurit du mot de passe saisi @NotNull( message = "Veuillez saisir un mot de passe" ) @Pattern(regexp = ".*(?=.{8,})(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).*", message = "Le mot de passe saisi n'est pas assez scuris") private String motDePasse;

Simple et rapide, n'est-ce pas ? La puissance des expressions rgulires associe la simplicit des annotations, c'est tout simplement magique ! Alors qu'il nous aurait fallu crire une barbante mthode de traitement supplmentaire et l'exception associe dans notre ancien objet mtier, nous pouvons dsormais nous contenter d'une courte annotation sur le champ valider, directement dans notre entit !

Ajouter des contrles "mtier"


Jusqu' prsent, nous avons russi appliquer des contrles sur le format des donnes de manire tonnamment simple. Cela dit, nous avons lchement vit deux contrles qui taient pourtant en place dans notre ancien objet mtier, et qui sont ncessaires pour raliser un inscription valide : le mot de passe et sa confirmation doivent tre gaux ; l'adresse mail ne doit pas exister dans la base de donnes.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

570/606

Ces contraintes se distinguent des simples vrifications de format que nous avons mises en place jusqu' prsent, car elles ont trait l'aspect mtier de notre application. Nous devons donc imprativement trouver un moyen propre de mettre en place ces contrles dans notre nouvelle architecture. Comme toujours, JSF propose un outil ddi notre besoin. Le framework fournit une interface nomme javax.faces.validator.Validator, qui permet de crer une classe contenant une mthode de validation, qui pourra alors tre lie un composant trs simplement depuis la vue, via un attribut plac dans une balise. Nous allons pour commencer mettre en place la vrification de l'existence de l'adresse email dans la base de donnes. Pour ce faire, nous devons crer un nouvel objet implmentant cette interface Validator, que nous allons nommer ExistenceEmailValidator et placer dans un nouveau package com.sdzee.validators : Code : Java - com.sdzee.validators.ExistenceEmailValidator package com.sdzee.validators; import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;

public class ExistenceEmailValidator implements Validator { @Override public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { ... } }

La seule mthode qu'il est ncessaire de surcharger est la mthode validate() : c'est elle qui va contenir le code mtier charg d'effectuer le contrle de l'existence de l'adresse dans la base. Nous savons dj comment raliser cette tche, nous l'avions dj fait dans notre ancien objet mtier. Il nous suffit donc d'adapter le code que nous avions alors crit. V oici un exemple de solution : Code : Java - com.sdzee.validators.ExistenceEmailValidator package com.sdzee.validators; import import import import import import javax.ejb.EJB; javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;

import com.sdzee.dao.DAOException; import com.sdzee.dao.UtilisateurDao; public class ExistenceEmailValidator implements Validator { private static final String EMAIL_EXISTE_DEJA = "Cette addresse email est dj utilise"; @EJB private UtilisateurDao utilisateurDao;

@Override public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { /* Rcupration de la valeur traiter depuis le paramtre value */ String email = (String) value; try { if ( utilisateurDao.trouver( email ) != null ) {

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


/* * Si une adresse est retourne, alors on envoie une exception * propre JSF, qu'on initialise avec un FacesMessage de * gravit "Erreur" et contenant le message d'explication. Le * framework va alors grer lui-mme cette exception et s'en * servir pour afficher le message d'erreur l'utilisateur. */ throw new ValidatorException( new FacesMessage( FacesMessage.SEVERITY_ERROR, EMAIL_EXISTE_DEJA, null ) ); } } catch ( DAOException e ) { /* * En cas d'erreur imprvue manant de la BDD, on prpare un message * d'erreur contenant l'exception retourne, pour l'afficher * l'utilisateur ensuite. */ FacesMessage message = new FacesMessage( FacesMessage.SEVERITY_ERROR, e.getMessage(), null ); FacesContext facesContext = FacesContext.getCurrentInstance(); facesContext.addMessage( component.getClientId( facesContext ), message ); } } }

571/606

V ous devriez comprendre sans problmes avec les commentaires prsents dans le code. V ous dcouvrez ici de nouveaux objets du framework JSF, comme UIComponent ou ValidatorException, et en retrouvez d'autres que vous connaissez dj comme FacesContext et FacesMessage. N'hsitez pas parcourir leur documentation, via Eclipse ou dans la Javadoc en ligne directement depuis votre navigateur, si vous souhaitez en apprendre davantage sur les constructeurs et mthodes ici utiliss. V ous retrouvez en outre l'injection de notre EJB Stateless, le DAO Utilisateur, dans notre objet via l'annotation JPA @EJB. Maintenant que notre objet est prt, il nous faut le dclarer auprs de JSF afin qu'il le rendre accessible notre vue. Oui, parce que pour le moment c'est bien gentil d'avoir cod un objet implmentant l'interface Validator, mais encore faut-il que notre vue puisse s'en servir pour l'appliquer au champ de saisie de l'adresse email ! Comme d'habitude, les annotations sont l pour nous sauver, et JSF propose l'annotation @FacesValidator : celle-ci permet de dclarer auprs du framework un objet comme tant un Validator, et permet ainsi de rendre cet objet accessible depuis une balise dans nos Facelets. Malheureusement, nous n'allons pas pouvoir utiliser cette annotation... Pourquoi ? Eh bien il s'agit l d'un comportement trange de JSF : les objets annots avec @FacesValidator ne sont pas pris en charge par le conteneur d'injection. Qu'est-ce que cela veut dire, concrtement ?

Formul autrement, cela signifie qu'il n'est pas possible d'injecter un EJB avec l'annotation @EJB dans un Validator JSF annot avec @FacesValidator. C'est un problme, car nous faisons usage de notre DAO Utilisateur dans notre validateur, et puisque nous travaillons avec JPA nous avons besoin de pouvoir y injecter cet EJB. D'aprs les informations qui circulent sur la toile, il semblerait que la communaut des dveloppeurs charge du maintien de JSF et de JPA travaille sur ce point, et qu'une correction soit prvue pour la prochaine version venir de JSF (JSF 2.2, pas encore sorti lors de la rdaction de ce cours).

Il nous faut donc contourner cette limitation nous-mmes. Il existe plusieurs moyens : nous pouvons conserver l'annotation @FacesValidator et rcuprer manuellement notre EJB depuis notre validateur, ou encore laisser tomber l'annotation et nous dbrouiller autrement. Nous allons opter pour la seconde mthode, et en ce qui concerne la premire je vous renvoie vers cet excellent article pour plus de dtails. Nous n'allons donc pas utiliser cette annotation. la place, nous allons dclarer notre objet comme un simple backing-bean ! Ainsi, notre vue pourra y accder, c'est le principe mme du backing-bean, et nous pourrons y injecter notre EJB sans problme. Nous devons donc ajouter les annotations suivantes notre objet :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


Code : Java - Annotations de notre validateur ... @ManagedBean @RequestScoped public class ExistenceEmailValidator implements Validator { ...

572/606

Au sujet de la porte requte ici utilise, puisque notre validateur ne sera utilis que pour valider un champ, nous pouvons utiliser la porte par dfaut sans souci. Maintenant que notre objet est dclar et donc accessible depuis la vue, nous devons ajouter une balise notre Facelet pour qu'elle fasse appel ce validateur pour le champ de saisie de l'adresse email. Pour ce faire, nous allons utiliser la balise <f:validator>. En regardant sa documentation, nous dcouvrons qu'elle possde un attribut validatorId permettant de prciser quel objet utilis en tant que validateur. Oui mais voil, cet attribut ne fonctionne que si notre objet est annot avec @FacesValidator, et pour des raisons techniques voques un peu plus tt nous avons dcid de ne pas utiliser cette annotation... Heureusement, la balise propose un autre attribut intitul binding , qui permet de prciser un backing-bean utiliser en tant que validateur, la condition que cet objet implmente l'interface Validator. Bingo ! C'est exactement notre cas, et il ne nous reste donc plus qu' ajouter cette balise dans notre Facelet. Le code responsable du champ de saisie de l'adresse email va donc devenir : Code : HTML - Ajout du validateur sur le champ de saisie de l'adresse email <h:inputText id="email" value="#{inscrireBean.utilisateur.email}" size="20" maxlength="60"> <f:ajax event="blur" render="emailMessage" /> <f:validator binding="#{existenceEmailValidator}" /> </h:inputText>

Sans surprise, nous utilisons une expression EL pour cibler notre backing-bean, comme nous l'avons dj fait pour inscriptionBean dans les autres balises. Sa dnomination est par dfaut, je vous le rappelle, le nom de la classe de l'objet dbutant par une minuscule, savoir existenceEmailValidator dans notre cas. Une fois cette modification effectue, rendez-vous nouveau sur la page d'inscription depuis votre navigateur, et essayez de vous inscrire avec une adresse email qui existe dj dans votre base de donnes :

V ous constatez alors que la validation est effectue comme prvu : en plus des contrles sur le format de l'adresse email, votre application vrifie maintenant que l'adresse saisie n'existe pas dj dans la base de donnes, et affiche un message d'erreur le cas chant ! Par ailleurs, vous remarquerez que puisque nous avons donn le niveau FacesMessage.SEVERITY_ERROR notre message d'erreur depuis la mthode de validation, le message est bien considr comme tel par JSF et est color en rouge (la classe CSS erreur lui est applique). Il nous reste encore un contrle effectuer avant d'en avoir termin avec notre formulaire : vrifier que le mot de passe et la confirmation saisis sont gaux. De la mme manire que prcdemment, nous allons crer un validateur ddi cette tche. Seulement cette fois-ci, nous n'allons pas avoir besoin d'injecter un EJB dans notre objet. En effet, pour vrifier que les deux

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

573/606

champs sont gaux, nous n'avons absolument pas besoin de faire appel notre DAO Utilisateur. Nous allons donc pouvoir utiliser l'annotation @FacesValidator pour lier notre validateur la vue : Code : Java - com.sdzee.validators.ConfirmationMotDePasseValidator package com.sdzee.validators; import import import import import import javax.faces.component.UIComponent; javax.faces.component.UIInput; javax.faces.context.FacesContext; javax.faces.validator.FacesValidator; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;

@FacesValidator( value = "confirmationMotDePasseValidator" ) public class ConfirmationMotDePasseValidator implements Validator { @Override public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { ... } }

Celle-ci attend en paramtre le nom qui sera utilis comme identifiant depuis la vue pour dsigner ce validateur : nous lui donnons ici le mme nom que la classe, avec la premire lettre en minuscule pour coller avec la rgle suivre pour les backingbean et avoir des dnominations homognes dans notre Facelet. V oil tout pour la forme. Le plus important maintenant, c'est de trouver un moyen de comparer les contenus de deux champs diffrents. Car c'est bien cela que nous cherchons faire : comparer le contenu du champ de saisie du mot de passe avec celui de la confirmation. Pour ce faire, nous allons commencer par regarder comment procder depuis notre Facelet, avant de revenir sur notre objet et de coder la mthode de validation. Commenons tout simplement par lier notre validateur, mme s'il n'est pas encore termin, au composant en charge de la confirmation : Code : HTML <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" size="20" maxlength="20"> <f:ajax event="blur" render="confirmationMessage" /> <f:validator validatorId="confirmationMotDePasseValidator" /> </h:inputSecret>

Puisque nous avons cette fois pu nous servir l'annotation @FacesValidator, nous pouvons donc utiliser l'attribut validatorId de la balise <f:validator>. Celle-ci, la diffrence de l'attribut binding , n'attend pas une expression EL mais directement le nom du validateur. Il s'agit de celui que nous avons dfini en tant que paramtre de l'annotation dans notre validateur, en l'occurrence confirmationMotDePasseValidator. En mettant en place cette balise, nous nous assurons ainsi que notre validateur est associ au composant en charge du champ de confirmation. La prochaine tape va consister dclarer le composant en charge du champ de mot de passe en tant qu'attribut du composant en charge de la confirmation. L'intrt de raliser cette association est de rendre disponible le contenu du champ de mot de passe depuis le composant en charge du champ de confirmation. Ne vous embrouillez pas trop pour le moment, regardez simplement comment procder : Code : HTML <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" size="20" maxlength="20"> <f:ajax event="blur" render="confirmationMessage" /> <f:attribute name="composantMotDePasse"

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


value="#{composantMotDePasse}" /> <f:validator validatorId="confirmationMotDePasseValidator" /> </h:inputSecret>

574/606

Nous utilisons la balise <f:attribute> pour mettre en place l'association. Sa proprite name permet de dfinir le nom de l'objet qui va tre cr en tant qu'attribut au composant courant, et sa proprit value permet de dfinir son contenu. Nous avons donc ici nomm l'objet composantMotDePasse, et avons li la proprit value directement avec sa valeur en utilisant l'expression EL #{composantMotDePasse}. Cette dclaration permet donc de crer un objet reprsentant le champ mot de passe en tant qu'attribut du composant de confirmation, mais il faut encore faire en sorte que la valeur du champ mot de passe soit bien affecte la valeur de cet objet. Pour ce faire, nous allons modifier la dclaration du composant en charge du mot de passe de cette manire : Code : HTML <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" binding="#{composantMotDePasse}" size="20" maxlength="20"> <f:ajax event="blur" render="motDePasseMessage" /> </h:inputSecret>

En ajoutant un attribut binding et en le liant la valeur de l'objet via l'expression EL #{composantMotDePasse}, nous nous assurons alors que la valeur saisie dans le champ de mot de passe sera affecte l'objet dclar en tant qu'attribut du composant de confirmation ! La dernire tape consiste maintenant accorder nos violons. Actuellement, nos validations AJAX dclenchent le rendu de leur champ respectif uniquement. Maintenant que nous vrifions l'galit entre les deux champs, pour rendre l'exprience utilisateur plus intuitive et logique, il faut faire en sorte que la validation AJAX de l'un entrane la validation de l'autre, et vice-versa. Pour ce faire, nous devons modifier les balises <f:ajax> que nous avions mises en place, et le code final devient alors : Code : HTML - Vrification de l'galit des mots de passe ... <h:inputSecret id="motdepasse" value="#{inscrireBean.utilisateur.motDePasse}" binding="#{composantMotDePasse}" size="20" maxlength="20"> <f:ajax event="blur" execute="motdepasse confirmation" render="motDePasseMessage confirmationMessage" /> </h:inputSecret> ... <h:inputSecret id="confirmation" value="#{inscrireBean.utilisateur.motDePasse}" size="20" maxlength="20"> <f:validator validatorId="confirmationMotDePasseValidator" /> <f:attribute name="composantMotDePasse" value="#{composantMotDePasse}" /> <f:ajax event="blur" execute="motdepasse confirmation" render="motDePasseMessage confirmationMessage" /> </h:inputSecret> ...

Seules les balises <f:ajax> ont chang. Deux choses importantes remarquer : l'ajout effectu dans les attributs render. Avec cette configuration, l'affichage du message associ au mot de passe ET de celui associ la confirmation sera actualis quoi qu'il arrive. Pour information, mais vous pouvez trouver cela vousmmes dans la documentation de la balise <f:ajax>, il suffit de sparer les composants actualiser par un espace. l'utilisation de l'attribut execute. Je vous ai dj expliqu qu'il permet de dfinir la porte de l'action ralise. C'est exactement ce dont nous avons besoin ici : nous voulons que lorsque l'vnement blur est dclench, la fois le

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

575/606

composant mot de passe et le composant confirmation soient traits. V oil pourquoi nous y prcisons les identifiants de nos deux champs de saisie. Notre Facelet est enfin termine, il ne nous reste maintenant plus qu' coder la mthode de validation, dans notre Validator associ au champ de confirmation. Le petit challenge qui nous attend, c'est de trouver un moyen pour rcuprer le contenu du champ mot de passe depuis le composant trait par la mthode, c'est--dire le composant de confirmation. En regardant la documentation de l'objet UIComponent, nous y trouvons la mthode getAttributes() qui permet de rcuprer une liste des attributs du composant. C'est justement ce que nous cherchons faire ! Eh oui, rappelez-vous : nous avons dclar le composant mot de passe comme attribut du composant confirmation dans notre Facelet, par le biais de la balise <f:attribute>. Nous apprenons que les lments de cette liste sont accessibles par leur nom, il va donc nous suffire de cibler l'lment nomm composantMotDePasse et le tour est jou ! V oici le code final de notre validateur : Code : Java - com.sdzee.validators.ConfirmationMotDePasseValidator package com.sdzee.validators; import import import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.component.UIInput; javax.faces.context.FacesContext; javax.faces.validator.FacesValidator; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;

@FacesValidator( value = "confirmationMotDePasseValidator" ) public class ConfirmationMotDePasseValidator implements Validator { private static final String CHAMP_MOT_DE_PASSE = "composantMotDePasse"; private static final String MOTS_DE_PASSE_DIFFERENTS = "Le mot de passe et la confirmation doivent tre identiques."; @Override public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { /* * Rcupration de l'attribut mot de passe parmi la liste des attributs * du composant confirmation */ UIInput composantMotDePasse = (UIInput) component.getAttributes().get( CHAMP_MOT_DE_PASSE ); /* * Rcupration de la valeur du champ, c'est--dire le mot de passe * saisi */ String motDePasse = (String) composantMotDePasse.getValue(); /* Rcupration de la valeur du champ confirmation */ String confirmation = (String) value; if ( confirmation != null && !confirmation.equals( motDePasse ) ) { /* * Envoi d'une exception contenant une erreur de validation JSF * initialise avec le message destin l'utilisateur, si les mots * de passe sont diffrents */ throw new ValidatorException( new FacesMessage( FacesMessage.SEVERITY_ERROR, MOTS_DE_PASSE_DIFFERENTS, null ) ); } } }

La seule petite difficult ici est de penser convertir l'objet rcupr depuis la liste des attributs du composant confirmation, la

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

576/606

ligne 23, en tant que UIInput, afin de pouvoir rcuprer ensuite simplement sa valeur avec la mthode getValue(). Quant au reste de la mthode, c'est le mme principe que dans le validateur de l'adresse email que nous avons cod un peu plus tt. Une fois ces modifications effectues dans le code de votre projet, rendez vous une ultime fois sur la page d'inscription depuis votre navigateur. Testez alors de rentrer des mots de passe diffrents, gaux, de rentrer la confirmation avant le mot de passe, de rentrer des mots de passe gaux puis d'en modifier un des deux, etc. V ous observerez alors, si vous n'avez rien oubli, une actualisation en direct des messages d'erreur associs aux champs mot de passe et confirmation ! Nous en avons enfin termin avec notre formulaire d'inscription ! Tout cela a d vous paratre bien long, mais c'est avant tout parce que nous avons pris le temps de dcortiquer toutes les nouveauts qui interviennent. Si vous prenez un peu de recul, et que vous regardez le code final de votre application, vous remarquerez alors qu'il est bien moins volumineux et bien plus organis que lorsque nous travaillions sur MVC la main. Mission russie pour le tandem JSF + JPA : la gestion des formulaires est maintenant une partie de plaisir, et seules les vrifications mtier ncessitent encore l'criture de mthodes de validation !

En rsum
Grce notre travail effectu avec JPA, la couche de donnes est totalement indpendante du reste de l'application. Pour grer correctement les champs de formulaires laisss vides par l'utilisateur, il est recommand de configurer le paramtre javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL dans le web.xml du projet. Pour grer un formulaire simple, il suffit d'une Facelet, d'un backing-bean et d'une entit : Le backing-bean contient une rfrence l'entit, et une ou plusieurs mthodes d'actions appeles lors de la soumission du formulaire associ. La porte du backing-bean ne doit pas tre dfinie la lgre, il est important d'utiliser la plus petit porte possible pour limiter le gchis de ressources sur le serveur. La mthode FacesContext.addMessage() permet de dfinir un message gr par JSF, et de lui attribuer un niveau de gravit. La Facelet contient des balises qui reprsentent les composants JSF, et qui sont lies aux proprits de l'entit par l'intermdiaire du backing-bean, travers des expressions EL. La validation du format des donnes saisies est entirement prise en charge par JSF, et il est possible de l'effectuer : depuis la vue, via des attributs et balises ddies. depuis le modle, via des annotations sur les proprits des entits. Les contrles mtier se font par le biais d'un objet Validator, dont la cration est simple et guide par le framework . L'ajaxisation de la validation des champs d'un formulaire avec JSF est incroyablement simple. Avec le tandem JPA et JSF, nous pouvons constuire un formulaire efficace sans SQL et sans JavaScript.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

577/606

L'envoi de fichiers avec JSF


Dans cet ultime chapitre, nous allons dcouvrir comment grer l'envoi de fichiers depuis un formulaire avec JSF. Ce sujet va en ralit nous servir de prtexte pour dcouvrir les fameuses bibliothques de composants JSF ! En guise de clture, je vous donnerai ensuite des indications pour raliser la huitime et dernire tape du fil rouge, et nous ferons enfin un rapide point sur ce qui vous attend dans le monde du dveloppement web avec Java EE !

Le problme
J'ai beau chercher dans la documentation, je ne trouve aucune balise qui ressemble de prs ou de loin quelque chose qui s'occupe des fichiers ! Ne perdez pas davantage votre temps, aucun composant standard n'existe actuellement dans JSF pour gnrer une balise <input type="file">, ni pour grer correctement les requtes de types multipart impliques par les transferts de donnes occasionns. Cependant, c'est au programme de la version JSF 2.2, prvue pour fin mars 2013 au moment de l'criture de ce cours. Une balise standard <h:inputFile> va faire son apparition, et apporter notamment un support AJAX pour l'envoi de fichiers ! Mais en attendant cette nouveaut, nous devons trouver un autre moyen... Au passage, si vous parcourez ce programme vous apprendrez que cette nouvelle mouture de JSF apportera enfin le support de l'injection via l'annotation @EJB dans tous les objets JSF, problme auquel nous nous tions heurts dans le chapitre prcdent. V ous pouvez par ailleurs noter que le support des requtes de type GET et des lments HTML5 est galement annonc.

Avec JSF, il est possible de dfinir ses propres composants. Ne peut-on pas en crer un pour l'occasion ?

C'est une trs bonne ide sur le papier, et effectivement JSF permet la cration de composants personnaliss. Cependant rien que pour un simple et unique composant, c'est une charge de travail assez consquente. Afin de mener bien un tel objectif, il est notamment ncessaire de connatre les concepts de FacesRenderer et de FacesComponents. Autrement dit, il faut mettre les mains dans le code d'une bibliothque ou d'une balise existante pour comprendre comment c'est fait, et en dduire comment l'adapter pour notre besoin. C'est un exercice trs formateur je vous l'accorde, et je vous encourage vous y atteler (voir la bulle d'information ci-aprs), mais dans le cadre de notre cours c'est trop d'efforts pour ce que nous souhaitons faire. En outre, un autre obstacle se dresse sur notre chemin : par dfaut, JSF et sa FacesServlet n'embarquent rien qui permette de grer les requtes de type mutlipart. Qu'est-ce que cela signifie exactement ? Si vous vous souvenez bien, dans notre servlet d'upload faite maison , nous avions fait intervenir une annotation particulire, nomme @MultipartConfig, afin de prciser au conteneur que notre servlet tait quipe pour traiter des requtes de ce type. Ensuite seulement, nous pouvions y utiliser les mthodes request.getParts(), etc. Eh bien voil ce qu'il manque actuellement JSF : la FacesServlet n'est pas annote avec @MultipartConfig, elle n'est capable dans les coulisses que d'utiliser les mthodes de rcupration de paramtres traditionnelles, comme request.getParameter(). Par consquent, il n'est pas possible de se baser directement sur elle pour raliser un systme d'envoi de fichiers. Il existe bien entendu une parade. Il faudrait pour commencer raliser tout le travail de conversion et de gestion des Parts la main, comme nous l'avions fait dans notre servlet d'upload auparavant. Seulement cela n'est pas suffisant : pour intgrer a proprement avec JSF, il faudrait crer un filtre, qui s'occuperait de prcharger les traitements ncessaires en amont de la FacesServlet, de manire rendre disponible les donnes d'une requte mulipart travers les mthodes traditionnelles que la FacesServlet sait utiliser.. Elle pourrait ainsi, comme si de rien n'tait et peu importe le type de requte entrante (normale ou multipart), permettre de manipuler les donnes envoyes. Bref, vous devez vous rendre compte que c'est un travail massif qui nous attend, difficle faire d'une part, et encore plus difficile faire proprement. Autant de raisons pour nous pousser choisir une autre solution : utiliser un composant permettant l'envoi de fichier, prt l'emploi et fourni... dans une bibliothque de composants externe ! Pour information, un bienfaiteur trs actif dans le dveloppement et la promotion de JSF surnomm BalusC, a dj : cod des filtres prts l'emploi pour la gestion des requtes multipart, et les a rendu disponibles dans ce premier article de blog ; cr un composant personnalis pour permettre la gestion de l'envoi de fichiers avec JSF, et expliqu la dmarche sur ce second article de blog.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

578/606

C'est en anglais, mais je vous encourage jeter un oeil ses codes et explications, c'est clair, propre et trs formateur !

Les bibliothques de composants


De manire gnrale, quand vous avez besoin de quelque chose qui semble assez commun, et que vous vous rendez compte que a n'existe pas dans le standard JSF, vous devez prendre le rflexe de vous dire que quelqu'un a dj d se poser la question avant vous, que quelqu'un y a probablement dj apport une rponse, et qu'il existe srement une ou plusieurs manires de rsoudre votre problme. a parat bte premire vue, mais nombreux sont les dveloppeurs qui ont tt fait de l'oublier et qui s'puisent rinventer la roue, avant bien souvent de se rendre compte du temps qu'ils ont perdu !

D'ailleurs si vous vous souvenez du charabia que je vous avais racont en introduisant le framework JSF, vous savez que nous avons dj parl de PrimeFaces , RichFaces , IceFaces , etc. Ces fameuses bibliothques de composants volus et arborant des sites vitrines parfois poustouflants. V ous pensez vraiment que des solutions capables de proposer des fonctionnalits avances comme du drag & drop, de la gnration de graphiques ou du dessin vectoriel en temps rel cl en mains, ne permettent pas de grer l'envoi de fichier ? V ous pensez bien, c'est un des premiers travaux qu'elles ont pour la plupart entrepris ! Ainsi, si vous cherchez un peu sur la toile vous tomberez notamment sur : PrimeFaces, qui offre la balise <p:fileUpload> ; RichFaces, qui offre la balise <rich:fileUpload> ; IceFaces, qui offre la balise <ice:inputFile>. Ce sont l les trois mastodontes, les trois bibliothques de composants JSF les plus fournies, les plus reconnues, les plus utilises et les plus maintenues. Seulement ils sont un peu massifs, ces mastodontes. Et sortir une telle machinerie (qui se matrialise dans votre projet par une flope d'archive Jar placer dans le dossier /WEB-INF/lib, et qui alourdit donc votre application) simplement pour raliser de l'envoi de fichiers, c'est un peu sortir un tank pour chasser une mouche. ct de a, il existe des projets un peu moins ambitieux, mais tout aussi utiles comme le clbre TomaHawk , qui offre la balise <t:inputFileUpload>. Nous allons donc nous pencher sur cette solution "lgre", si tant est que l'on puisse vraiment la qualifier ainsi, et nous en servir pour mettre en place l'envoi de fichier via un formulaire.

L'envoi de fichier avec Tomahawk Prparation du projet


Rcuprez Tomahawk en choisissant sur cette page de tlchargement la dernire version disponible, dans le format qui vous convient (archive .zip ou .tar.gz). Le fichier se nomme MyFaces Tomahawk 1.1.14 for JSF 2.0 , le numro de version pouvant changer si une nouvelle version existe lorsque vous lisez ce cours. Dcompressez ensuite le contenu de l'archive sur votre poste, vous obtiendrez alors un dossier portant le mme nom que l'archive. Dans mon cas, ce dossier s'intitule /tomahawk20-1.1.14 . Dans ce dossier se trouve un rpertoire intitul /lib, qui contient son tour 18 archives Jar ! Ce sont tous ces fichiers que vous allez devoir copier dans le rpertoire /WEB-INF/lib de votre projet afin d'y intgrer Tomahawk. Comme je vous l'ai dj expliqu, il est ncessaire de mettre en place un filtre afin de rendre la gestion des requtes multipart possible. Pour notre plus grand bonheur, Tomahawk en fournit un prt l'emploi, qu'il nous suffit de dclarer dans le fichier web.xml de notre application : Code : XML - Dclaration du filtre multipart de Tomahawk dans le fichier web.xml <filter> <filter-name>MyFacesExtensionsFilter</filter-name> <filterclass>org.apache.myfaces.webapp.filter.ExtensionsFilter</filterclass> </filter> <filter-mapping> <filter-name>MyFacesExtensionsFilter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping>

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

579/606

Notez-bien que le nom prcis dans le champ <servlet-name> doit absolument tre celui que vous avez donn votre FacesServlet. Si vous aviez repris la lettre le fichier web.xml que je vous avais propos lors de notre premier projet JSF, alors vous pouvez galement reprendre cette configuration telle quelle.

Cration de la Facelet
Crez ensuite une Facelet d'upload, en prenant exemple sur la page JSP que nous avions cre cet gard lorsque nous avions dcouvert l'envoi de fichiers via une servlet : Code : HTML - Facelet d'upload <!DOCTYPE html> <html lang="fr" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:t="http://myfaces.apache.org/tomahawk"> <h:head> <title>Tomahawk - Envoi de fichier</title> <h:outputStylesheet library="default" name="css/form.css" /> </h:head> <h:body> <h:form enctype="multipart/form-data"> <fieldset> <legend>Envoi de fichier</legend> <h:outputLabel for="description">Description du fichier</h:outputLabel> <h:inputText id="description" value="#{uploadBean.fichier.description}"/> <h:message id="descriptionMessage" for="description" errorClass="erreur" /> <br /> <h:outputLabel for="fichier">Emplacement du fichier <span class="requis">*</span></h:outputLabel> <t:inputFileUpload id="fichier" value="#{uploadBean.fichier.contenu}" /> <h:message id="fichierMessage" for="fichier" errorClass="erreur" /> <br /> <h:messages globalOnly="true" infoClass="info" /> <h:commandButton value="Envoyer" action="#{uploadBean.envoyer}" styleClass="sansLabel"/> <br /> </fieldset> </h:form> </h:body> </html>

La seule nouveaut ici est l'utilisation du composant <t:inputFileUpload> de la bibliothque Tomahawk la ligne 22, que nous dclarons dans l'en-tte <html> la ligne 6 de la mme manire que les bibliothques de composants standards de JSF. En ce qui concerne les trois expressions EL employes aux lignes 17, 22 et 28, elles vous donnent une ide de l'architecture que nous allons mettre en place derrire cette Facelet : un backing-bean nomm UploadBean, qui contient un JavaBean intitul fichier en tant que proprit, qui son tour contient deux proprits nommes description et contenu. Le backing-bean contient galement une mthode envoyer(), appele lors du clic sur le bouton de validation du formulaire. V ous n'oublierez pas de remarquer la ligne 12 l'ajout de l'attribut enctype="multipart/form-data" la balise <h:form>, c'est indispensable pour que le formulaire envoie correctement le fichier au serveur.

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

580/606

Cration du JavaBean
Commenons donc par crer un objet intitul Fichier, contenant deux proprits : la description du fichier, stocke sous forme d'une simple String ; le contenu du fichier, stockant les donnes envoyes par l'utilisateur. Un rapide parcours de la documentation de la balise <t:inputFileUpload> nous apprend que l'objet utilis pour stocker le fichier envoy par l'utilisateur est de type UploadedFile. Nous allons galement reprendre les vrifications que nous effectuions dans notre systme d'upload bas sur les servlets, savoir : un fichier doit obligatoirement tre transmis ; une description, si elle est renseigne, doit contenir au moins 15 caractres. Pour les mettre en place, nous allons nouveau pouvoir utiliser de simples annotations comme nous l'avons dcouvert dans le chapitre prcdent. V oici le code du bean en rsultant : Code : Java - com.sdzee.entities.Fichier package com.sdzee.entities; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.apache.myfaces.custom.fileupload.UploadedFile; public class Fichier { @Size( min = 15, message = "La phrase de description du fichier doit contenir au moins 15 caractres" ) private String description; @NotNull( message = "Merci de slectionner un fichier envoyer" ) private UploadedFile contenu; public String getDescription() { return description; } public void setDescription( String description ) { this.description = description; } public UploadedFile getContenu() { return contenu; } public void setContenu( UploadedFile contenu ) { this.contenu = contenu; }

V ous retrouvez sans surprise les annotations @Size et @NotNull appliques aux proprits, accompagnes de leur message d'erreur respectif.

Cration du backing-bean
www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

581/606

La dernire tape consiste crer le backing-bean qui va mettre tout ce petit monde en musique. Celui-ci doit donc s'intituler UploadBean, contenir et initialiser une instance d'un bean de type Fichier, ainsi qu'une mthode intitule envoyer() : Code : Java - com.sdzee.beans.UploadBean package com.sdzee.beans; import java.io.IOException; import java.io.Serializable; import import import import javax.faces.application.FacesMessage; javax.faces.bean.ManagedBean; javax.faces.bean.RequestScoped; javax.faces.context.FacesContext;

import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import com.sdzee.entities.Fichier; @ManagedBean @RequestScoped public class UploadBean implements Serializable { private static final long serialVersionUID = 1L; private Fichier fichier;

// Initialisation du bean fichier public UploadBean() { fichier = new Fichier(); } public void envoyer() throws IOException { String nomFichier = FilenameUtils.getName( fichier.getContenu().getName() ); String tailleFichier = FileUtils.byteCountToDisplaySize( fichier.getContenu().getSize() ); String typeFichier = fichier.getContenu().getContentType(); byte[] contenuFichier = fichier.getContenu().getBytes(); /* * Effectuer ici l'enregistrement du contenu du fichier sur le disque, * ou dans la BDD (accompagn du type du contenu, ventuellement), ou * tout autre traitement souhait... */ FacesContext.getCurrentInstance().addMessage( null, new FacesMessage( String.format( "Fichier '%s', de taille '%s' et de type '%s' envoy avec succs !", nomFichier, tailleFichier, typeFichier ) ) ); } public Fichier getFichier() { return fichier; } public void setFichier( Fichier fichier ) { this.fichier = fichier; }

V ous reconnaissez dans cet exemple la structure d'un backing-bean, trs semblable celui que nous avons cr dans le chapitre prcdent : les annotations @ManagedBean et @RequestScoped, le constructeur pour initialiser le bean de type Fichier et

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


la paire de getter/ setter associe.

582/606

C'est dans la mthode envoyer() que vous devrez appeler les traitements que vous souhaitez effectuer sur votre fichier : l'crire sur le disque comme nous l'avions fait dans notre systme bas sur les servlets, ou pourquoi pas l'enregistrer dans une table dans votre base de donnes, etc. J'ai pour ma part choisi d'effectuer de simples rcuprations d'informations concernant le fichier dans cet exemple, afin de ne pas encombrer le code inutilement. Cela me permet tout de mme de vous montrer l'utilisation des mthodes de l'objet UploadedFile : la ligne 29, je rcupre le nom du fichier via UploadedFile.getName() et le convertit dans un format propre grce la mthode utilitaire FilenameUtils.getName() ; la ligne 30, je rcupre la taille du fichier via UploadedFile.getSize() et la convertit dans un format lisible grce la mthode utilitaire FileUtils.byteCountToDisplaySize() ; la ligne 31, je rcupre directement le type du fichier via UploadedFile.getContentType(). Je gnre finalement un simple FacesMessage, initialis avec un identifiant null et un message rcapitulant le nom, la taille et le type du fichier envoy, que j'ajoute au FacesContext. Tout ceci est fait, comme vous devez vous en douter, en prvision d'un affichage dans notre Facelet via la balise <h:messages globalOnly="true">.

Tests et vrifications
Tout est prt, nous pouvons tester notre formulaire d'envoi. Rendez-vous sur la page http://localhost:8088/pro_jsf/upload.xhtml, vous devrez alors observer le mme formulaire vierge que celui mis en place dans notre ancien systme d'upload bas sur les servlets :

L'affichage du champ de slection du fichier peut varier selon le navigateur que vous utilisez et la langue de votre systme. Ici, il s'agit de l'affichage sous le navigateur Chrome sur un systme en langue anglaise, ne vous inquitez donc pas si vous obtenez un rendu sensiblement diffrent. Essayons pour commencer de valider sans rien saisir ni slectionner :

Nous observons alors un message d'erreur sur le champ du fichier, et rien sur la description. C'est bien le comportement attendu, puisque nous n'avons mis une annotation @NotNull que sur la proprit contenu, et pas sur la description. Essayons ensuite de valider en saisissant une description de moins de 15 caractres, sans slectionner de fichier :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

583/606

Cette fois, l'annotation @Size entre en jeu, et nous sommes prvenus par un message d'erreur sur le champ description que nous devons saisir au moins 15 caractres. Essayons enfin de valider en saisissant une description assez longue, et en slectionnant un fichier lger quelconque depuis notre disque :

J'ai pour ma part slectionn un fichier de type PDF et nomm trs sobrement 1.pdf . Cette fois, l'envoi est valid et nous observons le message d'information que nous avons construit dans la mthode envoyer() de notre backing-bean. En l'occurrence, le message me prcise bien le titre de mon fichier, sa taille et son type MIME. Nous avons donc russi mettre en place un systme d'envoi de fichier trs simplement, grce au composant fourni par la bibliothque Tomahawk. Si vous souhaitez reproduire fidlement le comportement de notre ancien systme d'upload, il ne vous reste plus qu' intgrer une mthode d'criture du fichier sur le disque directement dans votre backing-bean, ou bien dans une classe utilitaire que vous appellerez depuis la mthode envoyer() !

Limitation de la taille maximale autorise


Avant d'en terminer avec ce formulaire, je tiens vous prciser une information importante au sujet du composant que nous utilisons pour l'envoi de fichiers : il ne sait pas trs bien grer une limitation de la taille maximale autorise. V oyons cela ensemble en dtails. Dans la documentation du filtre de Tomahawk, nous apprenons que dans la section <filter> de la dclaration du filtre dans le web.xml, nous avons la possibilit d'insrer un paramtre d'initialisation afin de dfinir la taille maximale autorise par fichier : Code : XML - Ajout d'une limite de taille maximale pour les fichiers <filter> <filter-name>MyFacesExtensionsFilter</filter-name> <filterclass>org.apache.myfaces.webapp.filter.ExtensionsFilter</filterclass> <init-param> <param-name>uploadMaxFileSize</param-name> <param-value>10m</param-value> </init-param> </filter>

Le format respecter dans le champ <param-value> est le suivant :

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


10 -> 10 octets de donnes ; 10k -> 10 Ko de donnes ; 10m -> 10 Mo de donnes ; 1g -> 1 Go de donnes.

584/606

Le problme est que lorsque cette limite est dpasse, Tomahawk ne prpare pas un joli FacesMessage prt tre affich dans notre Facelet ct du composant <t:inputFileUpload>, il envoie la place une exception de type SizeLimitExceededException, qui sort du cadre normal d'excution de JSF. Je ne rentre pas dans les dtails, mais pour faire simple il n'est tout bonnement pas possible de grer proprement cette exception ! V ous pouvez faire le test si vous le souhaitez : mettez en place cet ajout dans la dclaration du filtre dans votre web.xml, par exemple avec une taille maximale de 1Mo, et essayez ensuite d'envoyer via votre formulaire un fichier dont la taille dpasse cette limite. V ous ne visualiserez en retour aucun message d'erreur sur votre formulaire, ni aucun message de validation. La seule erreur que vous pourrez trouver sera affiche dans le fichier /glassfish/domains/domain1/logs/server.log de GlassFish, qui contiendra une ligne mentionnant l'exception dont je viens de vous parler... Comment faire pour grer cette exception ?

Je vous l'ai dj dit, ce n'est pas possible proprement. Ce qu'il est possible de faire par contre, c'est de mettre en place une parade : nous pouvons mettre en place une limite de taille trs grande, par exemple 100Mo de donnes, et nous occuper de la vrification de la taille du fichier envoy grce un Validator JSF. Alors bien videmment, cela ne rgle pas entirement le problme : dans l'absolu, un utilisateur pourra toujours tenter d'envoyer un fichier trop volumineux (pesant plus de 100Mo), et se retrouver face un formulaire sans erreur apparente. Mais nous pouvons considrer que si un utilisateur s'amuse envoyer un fichier aussi gros, alors a sera bien fait pour lui, il ne mrite pas d'tre proprement inform de l'erreur ! Mettons donc en place une limitation large dans le web.xml : Code : XML - Ajout d'une limite de taille de 100Mo <filter> <filter-name>MyFacesExtensionsFilter</filter-name> <filterclass>org.apache.myfaces.webapp.filter.ExtensionsFilter</filterclass> <init-param> <param-name>uploadMaxFileSize</param-name> <param-value>100m</param-value> </init-param> </filter>

Il nous faut alors crer un Validator, comme nous l'avons fait deux reprises dans le chapitre prcdent. Nous allons le nommer TailleMaxFichierValidator et le placer comme ses confrres dans le package com.sdzee.validators : Code : Java - com.sdzee.validators.TailleMaxFichierValidator package com.sdzee.validators; import import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.validator.FacesValidator; javax.faces.validator.Validator; javax.faces.validator.ValidatorException;

import org.apache.myfaces.custom.fileupload.UploadedFile; @FacesValidator( "tailleMaxFichierValidator" ) public class TailleMaxFichierValidator implements Validator {

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF


private static final long TAILLE_MAX_FICHIER = 1 * 1024 * 1024; // 1Mo private static final String MESSAGE_ERREUR = "La taille maximale autorise est de 1Mo"; public void validate( FacesContext context, UIComponent component, Object value ) throws ValidatorException { if ( value != null && ( (UploadedFile) value ).getSize() > TAILLE_MAX_FICHIER ) { throw new ValidatorException( new FacesMessage( FacesMessage.SEVERITY_ERROR, MESSAGE_ERREUR, null ) ); } } }

585/606

V ous retrouvez l'annotation @FacesValidator, permettant de dclarer l'objet comme tel auprs de JSF. Le code de la mthode validate() est ensuite trs simple : nous nous contentons de vrifier que la taille du fichier ne dpasse pas la limite dfinie, en l'occurrence j'ai fix cette limite 1Mo, et envoyons le cas chant une exception initialise avec un FacesMessage de gravit erreur et contenant un message d'avertissement destin l'utilisateur. Pour terminer, nous devons lier notre Validator au composant d'envoi de fichier dans notre Facelet, par l'intermdiaire de la balise <f:validator> que vous connaissez dj. Remplacez tout btement la dclaration de la balise <t:inputFileUpload> par : Code : HTML <t:inputFileUpload id="fichier" value="#{uploadBean.fichier.contenu}"> <f:validator validatorId="tailleMaxFichierValidator" /> </t:inputFileUpload>

Pour rappel, le nom pass dans l'attribut validatorId est le nom dfini dans l'annotation @FacesValidator de notre objet TailleMaxFichierValidator. Nous pouvons maintenant vrifier le bon fonctionnement de notre systme de contrle de la taille d'un fichier. Rendez vous nouveau sur le formulaire d'envoi depuis votre navigateur, et essayez d'envoyer un fichier dont le poids est suprieur 1Mo (et bien videmment, infrieur 100Mo) :

V ous observez alors que le fichier est correctement envoy au serveur, ce qui est normal puisque la limite de taille autorise est fixe 100Mo. Une fois le fichier rcupr, JSF applique alors le Validator que nous avons cr et constate que le fichier est trop volumineux : il affiche donc le message d'erreur l'utilisateur, celui que nous avons prpar dans l'exception envoye depuis la mthode validate(), sur le champ de slection du fichier. V ous savez maintenant vrifier la taille d'un fichier, malgr l'incapacit du composant grer proprement la taille maximale qu'il est permis de dfinir dans le filtre.

Et plus si affinits... TP Fil rouge - tape 8


www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

586/606

Il vous reste encore un bout de chemin arpenter seuls, et vous serez prts pour la huitime et dernire tape du fil rouge ! Eh oui, je vous avais moiti menti en vous disant que la septime tape du fil rouge tait la dernire. Pourquoi moiti ? Parce qu'il y a bel et bien une huitime tape, mais cette fois, je ne vous fournirai pas de correction ! V ous allez devoir vous dbrouiller tous seuls pour intgrer JSF dans votre application, et vous devrez notamment apprendre utiliser les composants standards suivants : la balise <h:selectOneRadio>, pour gnrer le bouton radio de choix entre l'utilisation d'un client existant et la saisie d'un nouveau client ; la balise <h:selectOneMenu>, pour gnrer la liste droulante de slection d'un client existant ; la balise <h:link>, pour gnrer les liens HTML prsents dans les diffrentes vues ; la balise <h:dataTable>, pour gnrer le tableau rcapitulatif des clients existants et des commandes passes ; la balise <ui:include>, pour remplacer l'inclusion du menu jusqu' prsent ralise via une balise de la JSTL. Nous avons dj appris les rudiments de JSF ensemble, ce petit exercice en solitaire ne devrait pas vous poser de problme particulier : vous allez simplement devoir chercher par vous-mmes dans la documentation et sur le web pour trouver les informations qui vous manquent. Pourquoi est-ce que nous n'apprenons pas ensemble utiliser ces quelques composants ?

La premire raison de ce choix est simple : je ne souhaite pas vous donner l'impression d'avoir termin l'apprentissage de JSF ! Ce que j'entends par l, c'est qu'il ne faut surtout pas vous imaginer qu'une fois capables de reproduire le fil rouge avec JSF, vous n'aurez plus rien apprendre. Alors certes, j'aurais pu vous tenir la main jusqu' la fin du TP, et vous avertir ensuite comme je suis en train de le faire maintenant, mais je suis persuad que cela n'aurait pas eu le mme effet. En ne vous donnant que des pistes pour cet ultime tape du fil rouge, je vous prpare en douceur ce qui vous attend ensuite : l'apprentissage par vousmmes. La seconde raison de ce choix est que pour couvrir l'intgralit de JSF, tout en gardant ce rythme lent, pdagogique et adapt aux novices, il faudrait une collection de livres entire ! Il y a bien trop d'aspects aborder, de fonctionnalits dcouvrir, de ressources apprendre manipuler, etc. De toute manire, matriser le framework de A Z n'est ncessaire que si vous envisagez de travailler son amlioration ! Pour un usage commun, c'est--dire l'utilisation de JSF dans vos projets, il vous suffit d'en connatre suffisamment pour pouvoir vous dbrouiller seuls lorsqu'un problme survient : cela inclut la connaissance de l'API Servlet, du processus de traitement des requtes par JSF, des Facelets, des principaux composants, etc. Bref, tout ce que nous avons dcouvert ensemble dans ce cours ! Enfin, vous ne devez jamais oublier que l'exprience, les tests, les erreurs et la recherche de solutions sont les outils les plus pdagogiques qui puissent tre. Lorsque vous rencontrez un problme, posez-vous les bonnes questions et interrogez-vous avant tout sur son origine, avant ne serait-ce que de songer y trouver une solution.

Ce que l'avenir vous rserve


Ce qu'il vous manque pour pouvoir voler de vos propres ailes
Les bibliothques de composants JSF sont richissimes, et vous devez absolument vous y intresser si vous comptez vous lancer dans le dveloppement avec JSF. Nous les avons cites plusieurs reprises dans le cours : PrimeFaces, RichFaces, IceFaces, OpenFaces, Tomahawk... Il existe mme des solutions intermdiaires comme OmniFaces, qui se focalise sur la simplification du dveloppement avec JSF et qui gagne en maturit et en popularit. Sans parler de ces bibliothques ni mme des composants standards restant que nous n'avons pas tudis, il y a encore bon nombre de concepts JSF que nous n'avons pas eu la chance de pouvoir aborder ensemble dans ce cours, notamment : la cration de composants personnaliss ave JSF, la cration de FacesConverters, la comprhension des portes @Custom et @None sur un backing-bean, le dcoupage d'une page en plusieurs templates... Plus important encore, il y a surtout bon nombre de concepts Java/Java EE au sens large que nous n'avons pas eu la chance de pouvoir aborder ensemble dans ce cours, notamment : l'envoi de mail depuis une application web, la rcriture d'URL (ou urlrewriting ), la mise en place de webservices, l'criture de tests unitaires, la gnration d'une base de donnes directement depuis un modle d'entits JPA... Toutes ces mentions ne sont que des exemples, pour vous montrer que mme lorsque vous savez cr une application web, il existe toujours des domaines que vous ne connaissez ou ne matrisez pas encore, et qui n'attendent que vous !

Bientt dans les bacs...

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

587/606

Pour clore ce triste paragraphe aux allures de testament, n'oubliez pas de rester attentifs aux mises jour apportes rgulirement aux technologies Java et Java EE. La sortie de Java EE 7 est proche (prvue pour mars 2013 lors de la rdaction de ce cours), et par consquent celles de JSF 2.2, de JPA 2.1, de GlassFish 4, etc. Un lot de nouveauts fera comme toujours son apparition, et vous devrez vous y frayer un chemin si vous souhaitez rester efficace !

V ous devez maintenant en avoir pleinement conscience : le monde Java EE est immensment vaste ! V ous avez acquis en suivant ce cours les bases, les bonnes pratiques et les rflexes ncessaires pour tre autonomes dans votre progression future. Par dessus tout, j'espre que ce cours vous a donn l'envie de continuer et d'en apprendre plus, d'aller plus loin, que ce soit pour simplifier le dveloppement de vos applications, ou pour dvelopper des fonctionnalits toujours plus avances.

En rsum
Dans sa version courante (JSF 2.1), le framework ne fournit pas de composant pour l'envoi de fichiers, et ne sait pas grer les requtes de type multipart. La prochaine version du framework (JSF 2.2) est prvue ce jour pour mars 2013 et devrait pallier ce manque. La plupart des bibliothques de composants proposent une balise ddie l'envoi de fichiers, et un filtre pour la gestion des requtes multipart. Avec la bibliothque Tomhawak, il suffit de : dclarer le filtre ExtensionsFilter dans le web.xml du projet. mettre en place la balise <t:inputFileUpload> dans le formulaire. utiliser un objet de type UploadedFile pour rcuprer le fichier envoy. Tomahawk ne permet pas de grer proprement la limitation de la taille maximale autorise pour l'envoi de fichiers. Un contournement simple et rapide consiste relever la limite autorise, et mettre en place un Validator pour vrifier manuellement la taille du fichier envoy. Les univers JSF, JPA et Java EE en gnral sont immenses : faire preuve d'autonomie, de curiosit et d'initiative vous permettra de progresser efficacement ! Avec ce bagage technique supplmentaire, nous voil maintenant quips pour nous lancer dans le dveloppements d'applications web bases sur des standards conus pour faciliter et acclrer notre travail. Nous avons toutes les cls en mains pour construire nos projets de manire efficace et professionnelle !

www.siteduzero.com

Partie 6 : Aller plus loin avec JPA et JSF

588/606

Partie 7 : Annexes
Dans ces deux chapitres indpendants, vous retrouverez des informations pratiques concernant le dploiement d'une application web Java EE d'une part, et le dbuggage et l'analyse d'une application d'autre part.

Dbugger un projet
Dans cette lgre annexe, je vous prsente les principaux outils connatre pour tester et dbugger votre application Java EE.

Les fichiers de logs Quels fichiers ?


Les premiers outils connatre, si tant est que l'on puisse les qualifier d'outils, sont les fichiers de logs des diffrents composants de votre application. Ils contiennent les messages, avertissements et erreurs enregistrs lors du dmarrage des composants et lors de leur fonctionnement. Pour des applications basiques, comme celles que vous dveloppons dans ce cours, trois fichiers diffrents peuvent vous tre utiles : les logs du serveur d'applications ; les logs du SGBD ; les logs de la JVM.

Fichier de logs du serveur d'applications


Le fichier dont vous aurez le plus souvent besoin est le fichier de logs de votre serveur d'applications. Il contient des informations enregistres au dmarrage du serveur, lors du dploiement de votre application et lors de l'utilisation de votre application. C'est ici par exemple qu'atterrisent toutes les exceptions imprvues, c'est--dire celles qui ne sont pas interceptes dans votre code, et qui peuvent donc tre synonymes de problmes. Selon le serveur que vous utilisez et le systme d'exploitation sur lequel il s'excute, le fichier se situera dans un rpertoire diffrent. V ous pourrez trouver cette information sur le web ou tout simplement dans la documentation de votre serveur. titre d'exemple, le fichier de logs d'une application web sous GlassFish se somme server.log et se situe dans le rpertoire /glassfish/domains/ nom-du-domaine/logs/ , o nom-du-domaine dpend de la manire dont vous avez configur votre serveur lors de son installation. Par dfaut, ce rpertoire se nomme domain1 . V oici un exemple de contenu possible : Code : Console - Extrait de logs de GlassFish

Dec 3, 2012 2:16:40 PM com.sun.enterprise.admin.launcher.GFLauncherLogger info INFO: Successfully launched in 16 msec. [#|2012-12-03T14:16:43.331+0800|INFO|glassfish3.1.2|com.sun.enterprise.server.logging.GFF Running GlassFish Version: GlassFish Server Open Source Edition 3.1.2.2 (build 5)|#]

[#|2012-12-03T14:16:43.629+0800|INFO|glassfish3.1.2|javax.enterprise.system.core.com.sun. Grizzly Framework 1.9.50 started in: 61ms - bound to [0.0.0.0:4848]|#] ...

[#|2012-12-09T21:55:54.774+0800|SEVERE|glassfish3.1.2|javax.enterprise.system.tools.admin Exception while preparing the app : Exception [EclipseLink-4002] (Eclipse Persistence Ser Internal Exception: java.sql.SQLException: Error in allocating a connection. Cause: Conne JDBC url = jdbc:mysql://localhost:3306/bdd_sdzee, username = java. Terminating connection pool. Original Exception: -----com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has no at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) .....

V ous pouvez dfinir le niveau de dtail des informations enregistres via la console d'administration asadmin que je vous fait

www.siteduzero.com

Partie 7 : Annexes
dcouvrir dans le chapitre du cours portant sur JPA. En ce qui concerne le framework de persistance EclipseLink, il suffit d'ajouter une section dans le fichier de configuration persistence.xml de votre application : Code : XML - Niveau de dtails de logs d'EclipseLink <persistence-unit ...> ... <properties> <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.parameters" value="true"/> </properties> </persistence-unit>

589/606

En procdant cet ajout, vous pourrez visualiser les requtes SQL gnres par votre application au sein du fichier de logs de GlassFish.

Fichier de logs du SGBD


V otre gestionnaire de base de donnes peut galement gnrer des fichiers de logs. Il existe gnralement diffrents niveaux de dtails : l'enregistrement des erreurs uniquement, l'enregistrement de toutes les connexions tablies et requtes effectues, l'enregistrement des requtes longues (on parle alors de Slow Query), etc. L encore, selon la BDD utilise et le systme d'exploitation sur lequel elle s'excute, les fichiers se trouveront dans un rpertoire diffrent. titre d'exemple toujours, MySQL n'enregistre par dfaut aucune information ( l'exception des erreurs sous Windows uniquement). Il est alors possible d'activer les diffrents modes de logging en ajoutant des paramtres lors du lancement du serveur SQL : pour activer le mode Error Log, il suffit d'ajouter --log-error[=nom_du_fichier] la commande de lancement de MySQL, o nom_du_fichier est optionnel et permet de spcifier dans quel fichier enregistrer les informations. pour activer le mode General Query Log, c'est--dire l'enregistrement des connexions et requtes effectues, alors il suffit d'ajouter --log[=nom_du_fichier] la commande de lancement de MySQL, o nom_du_fichier est optionnel et permet de spcifier dans quel fichier enregistrer les informations.

V oici un exemple de contenu d'un fichier Error Log : Code : Console - Extrait de logs de MySQL InnoDB: Setting log file ./ib_logfile1 size to 5 MB InnoDB: Database physically writes the file full: wait... InnoDB: Doublewrite buffer not found: creating new InnoDB: Doublewrite buffer created InnoDB: 127 rollback segment(s) active. InnoDB: Creating foreign key constraint system tables InnoDB: Foreign key constraint system tables created 121114 0:29:59 InnoDB: Waiting for the background threads to start 121114 0:30:00 InnoDB: 1.1.8 started; log sequence number 0 121114 0:30:00 [Note] Server hostname (bindaddress): '0.0.0.0'; port: 3306

Par dfaut sous les systmes de type Unix, le fichier de logs d'erreurs se trouve dans /usr/local/mysql/data/ .

Fichier de logs de la JVM


Dans certains cas, vous pouvez tes confronts des problmes lis aux fondations de votre application, savoir la JVM utilise pour faire tourner Java sur vore machine. Elle aussi peut enregistrer des logs d'erreurs, et vous pouvez spcifier dans quel rpertoire et quel fichier en ajoutant -XX:ErrorFile=nom_du_fichier lors de son lancement. Par exemple, si vous souhaitez que votre JVM cre un fichier nomm jvm_error_xxx.log dans le rpertoire /var/log/java/ , o xxx est l'identifiant du processus courant, il faut lancer Java ainsi :

www.siteduzero.com

Partie 7 : Annexes
Code : Console java -XX:ErrorFile=/var/log/java/java_error_%p.log

590/606

Si l'option -XX:ErrorFile= n'est pas prcise, alors le nom de fichier par dfaut sera hs_err_pidxxx.log , o xxx est l'identifiant du processus courant. En outre, le systme essaiera de crer ce fichier dans le rpertoire de travail du processus. Si jamais le fichier ne peut tre cr pour une raison quelconque (espace disque insuffisant, problme de droits ou autre), alor le fichier sera cr dans le rpertoire temporaire du systme d'exploitation (/tmp sous Linux, par exemple).

Comment les utiliser ?


Si vous avez intgr votre serveur d'applications Eclipse, alors son fichier de logs sera accessible depuis le volet infrieur de l'espace de travail Eclipse, ce qui vous permettra de ne pas avoir quitter votre IDE pour rgler les problmes lis au code de votre application. Par exemple, si vous utilisez le serveur GlassFish depuis Eclipse il vous suffit de faire un clic droit sur le nom de votre serveur, et de suivre GlassFish > View Log File :

En ce qui concerne les logs des autres composants, il vous faudra le plus souvent aller les chercher et les parcourir par vousmmes, manuellement. Pour ne rien vous cacher, ce travail est bien plus ais sous les systmes bass sur Unix (Mac OS, Linux, etc.), qui proposent via leur puissant terminal quelques commandes trs pratiques pour la recherche et l'analyse de donnes, notamment tail -f et grep . Cela dit, quelque soit le systme d'exploitation utilis il est souvent intressant de pouvoir colorer le contenu d'un fichier de logs, afin de pouvoir plus facilement reprer les blocs d'informations intressantes parmi le flot de messages qu'il contient. Pour ce faire, vous pouvez par exemple utiliser un logiciel comme SublimeText. La coloration syntaxique vous aidera grandement au dchiffrage des sections qui vous intressent, observez par exemple un extrait du fichier server.log de GlassFish ouvert avec SublimeText, coloration syntaxique active :

www.siteduzero.com

Partie 7 : Annexes
C'est dj bien moins austre ainsi, n'est-ce pas ?

591/606

Le mode debug d'Eclipse Principe


Le second outil le plus utile pour le dveloppement d'une application avec Eclipse est le mode Debug que ce dernier fournit. Il s'agit d'un mode d'excution pas pas , que vous avez la possibilit de contrler manuellement et de manire aussi fine que souhaite. Le principe est simple : Eclipse prend la main sur votre serveur, et vous offre la possibilit de littralement mettre en pause l'excution du code de votre application. Le systme se base sur des breakpoints , de simple marqueurs que vous avez la possibilit de poser sur les lignes de code que vous souhaitez analyser.

Interface
Pour lancer le serveur en mode debug, il ne faut plus utiliser le bouton classique mais cette fois celui qui ressemble un petit insecte, ou bug en anglais. Le serveur va alors dmarrer, et vous pourrez commencer utiliser l'application que vous y avez dploye sans diffrence apparente avec le mode classique :

Pour pouvoir mettre en pause l'excution de votre application et tudier son fonctionnement, il faut au minimum ajouter un breakpoint dans la portion de code que vous souhaitez analyser. Cela se fait tout simplement en double-cliquant sur l'espace vide situ gauche d'une ligne de code :

Un petit rond bleu apparat alors dans l'espace libre, et au survol de ce marqueur une infobulle vous informe de la ligne de code et de la mthode cible par le breakpoint. Rendez-vous nouveau dans le navigateur depuis lequel vous testez l'application, et effectuez nouveau la ou les actions faisant intervenir la portion de code sur laquelle vous avez pos un marqueur. Cette fois, votre navigateur va rester en attente d'une rponse de votre serveur, et une fentre d'avertissement va alors apparatre dans Eclipse :

Eclipse vous informe que vous vous apprtez ouvrir un espace visuel ddi au mode debug. V ous devrez alors cliquer sur Yes

www.siteduzero.com

Partie 7 : Annexes
pour accepter, et Eclipse vous affichera alors une nouvelle interface depuis laquelle vous allez pouvoir contrler votre application.

592/606

Pendant ce temps, le navigateur reste logiquement en attente d'une rponse du serveur, tant que vous n'avez pas fait avancer le droulement jusqu'au bout du cycle en cours, c'est--dire jusqu'au renvoi d'une rponse au client.

Depuis l'espace de debug, vous avez pour commencer la possibilit de revenir vers la vue de travail classique, sobrement intitule Java EE, en cliquant sur le bouton situ dans le volet en haut droite de l'cran :

Rciproquement, vous pourrez passer de la vue d'dition vers la vue de debug en cliquant sur le bouton intitul Debug depuis la vue de travail classique. Deuxime volet important, celui situ en haut gauche de l'cran de debug :

C'est via ces quelques boutons que vous allez pouvoir commander le droulement de l'excution de votre code. Une autre fonctionnalit importante du mode debug est la possibilit de surveiller des variables ou des objets, d'inspecter leur contenu en temps rel afin de vrifier qu'elles se comportent bien comme prvu. Il faut pour cela slectionner l'objet observer dans le code affich dans le volet gauche central de la fentre de debug, puis effectuer un clic droit et choisir Watch :

Le volet suprieur droit de l'cran de debug va alors se mettre jour et afficher les informations concernant l'objet que vous avez slectionn :

V ous pouvez bien entendu surveiller plusieurs objets simultanment. C'est dans cette petite fentre que vous pourrez vrifier que les donnes que vous crez et manipulez dans votre code sont bien celles que vous attendez, au moment o vous l'attendez !

www.siteduzero.com

Partie 7 : Annexes

593/606

Exemple pratique
Assez dissert, passons l'action. Nous allons mettre en place un projet constitu d'une seule et unique servlet, dans le but de faire nos premiers pas avec le mode debug d'Eclipse, mais pas seulement : nous allons galement en profiter pour observer le cycle de vie d'une servlet, et son caractre multithreads. D'une pierre deux coups ! Pour commencer, nous allons crer un nouveau projet web dynamique depuis Eclipse, et le nommer test_debug . Nous allons ensuite y crer une nouvelle servlet nomme Test et place dans un package intitul com.test, dont voici le code : Code : Java - Servlet de test package com.test; import java.io.IOException; import import import import import javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;

@WebServlet( "/test" ) public class Test extends HttpServlet { private int compteur = 0; @Override public void init() throws ServletException { System.out.println( ">> Servlet initalise." ); } public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { ++compteur; int compteurLocal = 0; ++compteurLocal; System.out.println( ">> Compteurs incrments." );

@Override public void destroy() { System.out.println( ">> Servlet dtruite." ); }

Avant de passer aux tests, attardons nous un instant sur le code ici mis en place. V ous devez vous souvenir des mthodes init() et destroy(), nous les avons dj rencontres dans nos exemples, que ce soit dans des servlets ou dans des filtres. Pour rappel, elles sont prsentes par dfaut dans la classe mre de toute servlet, HttpServlet (qui les hrite elle-mme de sa classe mre GenericServlet), et ne doivent pas ncessairement tre surcharges dans une servlet, contrairement aux mthodes doXXX() dont une au moins doit tre prsente dans chaque servlet. Dans cet exemple, nous surchargeons donc ces mthodes init() et destroy()aux lignes 17 19 et 30 32, dans lesquelles nous ne faisons rien d'autre qu'afficher un simple message dans la sortie standard, savoir la console du serveur. Ne vous inquitez pas, vous allez trs vite comprendre pourquoi nous nous embtons surcharger ces deux mthodes ! Reste alors le code de la mthode doGet(). L encore, rien de bien compliqu : nous incrmentons deux compteurs, l'un tant dclar localement dans la mthode doGet(), et l'autre tant dclar en tant que variable d'instance. V oil tout ce dont nous allons avoir besoin. Pas besoin de JSP, ni de fichier web.xml d'ailleurs puisque nous avons utilis l'annotation @WebServlet pour dclarer notre servlet auprs de Tomcat ! Nous pouvons donc dmarrer notre projet en mode debug, en cliquant sur le bouton ressemblant un petit insecte dont je vous ai parl prcdemment. Avant d'accder notre servlet depuis notre navigateur, nous allons placer quelques breakpoints dans notre le code, afin de pouvoir mettre en pause son excution aux endroits souhaits. Nous allons choisir les lignes 18, 22, 26 et 31, et nous allons donc effectuer un clic droit

www.siteduzero.com

Partie 7 : Annexes
dans l'espace vide sur la gauche de chacune d'elles et choisir "Toggle breakpoint" :

594/606

Le mme effet est ralisable en double-cliquant simplement dans cet espace vide. Une fois les breakpoints positionns, nous visualisons alors quatre petits ronds bleu tels que celui-ci :

Notre environnement est maintenant prt, nous pouvons commencer tester. Pour ce faire, il nous suffit de nous rendre depuis notre navigateur sur la page http://localhost:8080/test_debug/test. Ds lors, Eclipse va prendre le dessus et va nous avertir que nous allons changer de vue de travail, nous cliquons alors sur Yes. Dans la vue de debug qui s'affiche alors, nous pouvons remarquer que l'excution du code s'est mise en pause sur la ligne 18 du code. C'est bien le comportement que nous attendions, car comme nous accdons notre servlet pour la premire fois, sa mthode init() est appele et notre premier breakpoint est activ. Pour poursuivre le droulement, nous appuyons alors sur la touche F8 de notre clavier, ou sur le bouton correspondant dans le volet de contrle en haut gauche de la vue de debug. L'excution continue alors et se met aussitt en pause sur la ligne 22. Logique, puisque qu'il s'agit l de notre second breakpoint, et qu'il est activ chaque passage d'une requte dans la mthode doGet() ! En d'autres termes, ce moment prcis la requte GET mise par notre navigateur lorsque nous avons appel l'URL de notre servlet a dj t achemine dans notre mthode doGet(). Au passage, vous en profiterez pour observer que la mthode init() a bien t appele en vrifiant le contenu du volet infrieur de la vue de debug. Il s'agit de la console, et si tout s'est bien pass vous devriez y trouver le message crit par la ligne 18 de notre code :

Nous allons alors slectionner la variable compteur dans le code affich dans le volet central, faire un clic-droit puis choisir Watch :

Dans le volet suprieur droit apparat alors le contenu de notre variable :

www.siteduzero.com

Partie 7 : Annexes

595/606

Puisque la ligne n'a pas encore t excute, la valeur est encore zro. Nous appuyons alors nouveau sur F8, et le cycle se poursuit jusqu'au prochain breakpoint la ligne 26. La valeur du compteur a alors chang et est maintenant logiquement 1. Nous allons galement surveiller la variable compteurLocal , en la slectionnant, en effectuant un clic-droit et en choisissant Watch. Le volet suprieur droit nous affiche alors les contenus des deux variables :

Un nouvel appui sur F8 permet de terminer le cycle : le navigateur affiche une page blanche, ce qui est logique puisque nous n'avons pas envoy d'informations au client travers la rponse HTTP. Aucune tche n'tant en attente de traitement, la vue de travail de debug cesse alors d'afficher le contenu des variables. Nous allons maintenant changer de navigateur, et accder une nouvelle fois notre servlet via son URL http://localhost:8080/test_debug/test. Du point de vue du serveur, tout se passera donc comme si la visite tait issue d'un utilisateur diffrent. La vue de debug va alors s'activer nouveau, l'excution du code va se bloquer sur la ligne 22 et nous allons devoir appuyer sur F8 pour passer la ligne 26. cet instant, vous allez observer deux valeurs de compteurs diffrentes dans le volet des variables surveilles :

Que s'est-il pass ?

Eh oui, premire vue nous sommes en droit de nous poser la question ! Dans notre code, nous initialisons bien nos deux compteurs zro, pourquoi le compteur dclar en tant que variable d'instance n'a-t-il pas t remis zro comme l'a t notre compteur local ? Eh bien tout simplement parce que comme je vous l'ai dj expliqu plusieurs reprises dans le cours, une servlet n'est instancie qu'une seule et unique fois par votre serveur, et cette unique instance va ensuite tre partage par toutes les requtes entrantes. Concrtement, cela signifie que toutes les variables d'instance ne seront initialises qu'une seule fois, lors de l'instanciation de la servlet. Les varialbes locales quant elles sembleront rinitialises chaque nouvel appel. V oil pourquoi la variable compteur a conserv sa valeur entre deux appels, alors que la variable compteurLocal a quant elle bien t rinitialise. "Sembleront ?"

Oui, sembleront. Car en ralit, c'est un petit peu plus compliqu que cela. V oil comment tout cela s'organise : chaque requte entrante conduit la cration d'un Thread, qui accde alors l'instance de la servlet ; une variable d'instance n'est cre, comme son nom l'indique, que lors de l'instanciation de la servlet, et un unique espace mmoire est allou pour cette variable dans la heap ; tous les threads utilisant cette instance, autrement dit toutes les requtes entrantes diriges vers cette servlet, partagent et utilisent cette mme variable, et donc ce mme espace mmoire ; une variable locale est initialise chaque utilisation par un Thread, autrement dit par chaque nouvelle requte

www.siteduzero.com

Partie 7 : Annexes

596/606

entrante, dans sa stack ; un espace mmoire diffrent est allou pour chaque initialisation dans la stack, mme comportement d'ailleurs pour les paramtres de mthodes.

Stack ? Heap ?

V ous devriez dj connatre ces concepts si vous avez dj programm en Java, mais nous allons tout de mme nous y pencher un instant. Dans une JVM, la mmoire est dcoupe en plusieurs sections : code, stack, heap et static. Pour faire simple, voici comment sont utilises les fractions qui nous intressent ici : la stack contient les mthodes, variables locales et variables rfrences ; la heap contient les objets. V oil pourquoi notre variable d'instance est dclare dans la heap : elle fait partie d'une classe classe, donc d'un objet. Ce qu'il est trs important de retenir ici, ce n'est pas uniquement le fait qu'une variable d'instance conserve son tat d'un appel l'autre. Ce qui est extrmement important, c'est de bien comprendre qu'une variable d'instance est partage par tous les clients ! Eh oui, vous avez bien constat dans notre exemple que mme en utilisant un autre navigateur, la variable compteur utilise tait la mme que celle utilise pour le premier appel.

Alors bien entendu, dans notre petit exemple ca ne vous parat pas important. Mais imaginez maintenant que dans cette variable d'instance vous stockiez non plus un banal compteur mais un nom d'utilisateur, une adresse mail ou un mot de passe... De telles informations ainsi stockes seront alors partages par tous les clients ! Et selon l'usage que vous faites de cette variable dans votre code, vous vous exposerez de graves ennuis. Nous voil maintenant au point sur deux aspects importants : nous avons pris en mains le mode de debug d'Eclipse, et nous avons illustr le caractre multithread d'une servlet.

Conseils au sujet de la thread-safety


Avant de passer la suite, revenons brivement sur la problmatique des Threads et des servlets. Avec ce que nous avons pos, nous avons compris que la principale inquitude se situe au niveau de la thread-safety. Nous savons maintenant que les servlets et les filtres sont partags par toutes les requtes. C'est un avantage du Java, c'est multithread et des threads diffrents (comprendre ici "des requtes HTTP") peuvent utiliser la mme instance d'une classe. Cela serait par ailleurs bien trop coteux (en termes de mmoire et de performances) d'en recrer une chaque requte. Rappelons que lorsque le conteneur de servlets dmarre, il lit le fichier web.xml de chaque application web, et/ou scanne les annotations existantes, la recherche des url-pattern associs aux servlets dclares. Il place alors les informations trouves dans ce qui s'apparente grossirement une Map de servlets. ce sujet, sachez qu'en ralit une servlet peut tre instancie plusieurs fois par un mme conteneur. En effet, si une mme servlet est mappe sur plusieurs URL diffrentes, alors autant d'instances de la servlet seront cres et mises en mmoire. Mais le mme principe de base tient toujours : une seule instance est partage par toutes les requtes adressant la mme URL.

Ces servlets sont donc stockes dans la mmoire du serveur, et rutilises chaque fois qu'une URL appele correspond la servlet associ l'url-pattern dfini dans le web.xml ou l'annotation. Le conteneur de servlets droule grossirement ce processus pour chaque tape : Code : Java for (Entry<String, HttpServlet> entry : servlets.entrySet()) { String urlPattern = entry.getKey(); HttpServlet servlet = entry.getValue(); if (request.getRequestURL().matches(urlPattern)) { servlet.service(request, response);

www.siteduzero.com

Partie 7 : Annexes
} break;

597/606

La mthode HttpServlet#service() dcide alors quelle mthode parmi doGet(), doPost(), etc. appeler en fonction de HttpServletRequest#getMethod(). Nous voyons bien travers ce code que le conteneur de servlets rutilise pour chaque requte la mme instance de servlet (ici, elles sont reprsentes grossirement stockes dans une Map). En d'autres termes : les servlets sont partages par toutes les requtes . C'est pourquoi il est trs important d'crire le code d'une servlet de manire threadsafe, ce qui signifie concrtement : ne jamais assigner de donnes issues des portes request ou session dans des variables d'instance d'une servlet, mais uniquement dans des variables dclares localement dans ses mthodes. Comprenez-bien de quoi il est ici question. Si vous assignez des donnes issues de la session ou de la requte dans une variable d'instance d'une servlet ou d'un filtre, alors un tel attribut se retrouverait partag par toutes les requtes de toutes les sessions... Il s'agit l en quelque sorte d'un dbordement : alors que votre variable devrait rester confine sa porte, elle se retrouve accessible depuis un primtre bien plus large. Ce qui crase bien videmment les principes rsums prcdemment ! Ce n'est absolument pas threadsafe ! L'exemple ci-dessous illustre clairement cette situation : Code : Java public class MaServlet extends HttpServlet { private Object objetNonThreadSafe; // Objet dclar en tant que variable d'instance protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object objetThreadSafe; // Objet dclar localement dans la mthode doGet() objetNonThreadSafe = request.getParameter("foo"); // MAUVAIS !!! Cet objet est partag par toutes les requtes... objetThreadSafe = request.getParameter("foo"); // OK, c'est threadsafe : l'objet reste bien dans la bonne porte. } }

Quelques outils de tests Les tests unitaires


Un autre outil indispensable au bon dveloppement d'une application est le test unitaire. La solution propose par la plate-forme Java s'appelle JUnit : c'est un framework destins aux tests. Qu'est-ce qu'un test unitaire ?

Un test unitaire est une portion de code, crite par un dveloppeur, qui se charge d'excuter une fonctionnalit en particulier. Le dveloppeur doit donc en crire autant qu'il existe de fonctionnalits dans son application, afin de tester l'intgralit du code. On parle alors de couverture du code, et dans un projet plus le taux de couverture est lev, plus le pourcentage de fonctionnalits testes est important. Gnralement dans les projets en entreprises, un niveau de couverture minimum est demand : par exemple, 80%. Cela signifie qu'au moins 80% des fonctionnalits du code doivent tre couvertes par des tests unitaires. Quel est l'intrt de mettre en place de tels tests ?

En effet, la question est lgitime. A priori, partir du moment o le dveloppeur teste correctement (et corrige ventuellement) ses classes et mthodes aprs criture de son code, aucune erreur ne doit subsister. Oui, mais cela n'est vrai que pour une fraction de l'application : celle qui a t teste par le dveloppeur pour vrifier que son code fonctionnait correctement. Mais rien ne garantit qu'aprs ajout d'une nouvelle fonctionnalit, la prcdente fonctionnera toujours !

www.siteduzero.com

Partie 7 : Annexes

598/606

V oil donc le principal objectif de la couverture du code : pouvoir s'assurer que le comportement d'une application ne change pas, aprs corrections, ajouts ou modifications. En effet, puisque chacun des tests unitaires s'applique une seule fonctionnalit de manire indpendante, il suffit de lancer tous les tests un par un pour confirmer que tout se comporte comme prvu. Pour information, c'est ce que l'on appelle des "tests de non rgression" : s'assurer qu'aprs l'ajout de code nouveau ou la modification de code existant, l'application fonctionne toujours comme espr et aucun nouveau problme - ou aucune rgression - ne survient. La couverture du code par une batterie de tests unitaires permet d'automatiser la vrification, alors qu'il faudrait procder la main sans, et donc tre minutieux et bien penser tester tous les cas de figure possibles.

Quelle partie du code d'une application doit tre couvert ?

Seules les classes mtiers et d'accs aux donnes ont besoin d'tre testes, car c'est ici que les fonctionnalits et rsultats sont contenus. Les servlets n'tant que des aiguilleurs, elles ne sont pas concernes. Doivent donc tre tests unitairement les JavaBeans, les objets mtiers, les EJB, les DAO, les ventuels WebServices, etc. Au sein de ces classes, seules les mthodes publiques doivent tre testes : le fonctionnement des mthodes prives ne nous intresse pas, tout ce qui importe est que les mthodes publiques qui font appel ces mthodes prives en interne retournent le bon rsultat ou respectent le bon comportement. quoi ressemble un test unitaire ?

Les tests unitaires sont en principe cods dans un projet ou package part, afin de ne pas tre directement lis au code de l'application. Un test unitaire consiste en une mthode au sein d'une classe faisant intervenir des annotations spcifiques JUnit. Imaginez par exemple que nous ayons crit une classe nomme MaClasse, contenant des mthodes de calculs basiques nommes multiplication() et addition(). V oici alors quoi pourrait ressembler la classe de tests unitaires charge de vrifier son bon fonctionnement : Code : Java - Exemple de test unitaire JUnit import org.junit.*; public class Test{ @Test public void testMultiplication() { // MaClasse est cible par le test MaClasse test = new MaClasse(); // Vrifie que multiplication( 6, 7 ) retourne bien 42 assertEquals( "Erreur", 42, test.multiplication( 6, 7 ) );

@Test public void testAddition() { // MaClasse est cible par le test MaClasse test = new MaClasse(); // Vrifie que addition( 43, 115 ) retourne bien 158 assertEquals( "Erreur", 158, test.addition( 43, 115 ) );

Chaque test unitaire suit de prs ou de loin ce format, et les tests doivent pouvoir tre excuts dans un ordre arbitraire : aucun test ne doit dpendre d'un autre test. Comme vous pouvez l'observer, pour crire un test JUnit il suffit d'crire une mthode et de l'annoter avec @Test. La mthode utilise pour vrifier le rsultat de l'excution, assertEquals(), est fournie par JUnit et se charge de comparer le rsultat attendu avec le rsultat obtenu. Pour le lancement des tests, Eclipse permet d'intgrer intuitivement l'excution de tests JUnit depuis l'interface utilisateur via un simple clic-droit sur la classe de tests, puis Run As > JUnit Test. En ce qui concerne la couverture du code, il existe un excellent plugin qui permet de mesurer le taux de couverture d'une application, et de surligner en vert et rouge les portions de codes qui sont respectivement couvertes et non couvertes : EclEmma. Pour vous donner une meilleure ide du principe, voici une capture

www.siteduzero.com

Partie 7 : Annexes
de la solution en action :

599/606

Nous allons nous arrter l pour la dcouverte des tests unitaires, vous avez en mains toutes les informations ncessaires pour savoir quoi et chercher et comment mettre le tout en place dans vos projets !

Les tests de charge


Pour conclure cette annexe, je vais vous prsenter trs brivement deux solutions que vous serez un jour ou l'autre amens utiliser si vous dvelopper des applications web. La premire se nomme JMeter, et est dite par la fondation Apache. Elle permet d'effectuer des tests de charge et des mesures de performances. Elle est particulirement utile pour le tests d'application web : vrifier que le code d'une application se comporte de manire correcte est une chose, mais il faut galement vrifier qu'une application fonctionne toujours de manire convenable lorsque des dizaines, centaines ou milliers de visiteurs l'utilisent simultanment. C'est l qu'intervient JMeter : la solution permet d'automatiser et de simuler des charges importantes sur un serveur, un rseau ou un composant en particulier, ou pour analyser la performance et la ractivit globale d'une application sous diffrents niveaux de charge. Bref, elle vous permettra de contrler la prsence de problmes trs importants, que vous ne pouvez pas dceler manuellement. Elle permet galement de gnrer des graphiques et rapports afin de vous faciliter l'analyse des comportements observs par la suite. La seconde se nomme JProfiler. Plus complexe matriser, elle n'est gnralement mise en oeuvre que lorsque des

www.siteduzero.com

Partie 7 : Annexes
problmes sont observs sur une application. Ses atouts les plus classiques sont l'analyse pousse de la mmoire utilise lors de l'utilisation d'une application, l'analyse de l'utilisation du CPU, l'analyse du temps pass dans chaque mthode ou portion de code, etc. Les rapports qu'elle gnre permettent alors de dcouvrir qu'est ce qui cause des problmes, de comprendre pourquoi cela ne fonctionne pas comme prvu, et surtout sous quelles conditions.

600/606

www.siteduzero.com

Partie 7 : Annexes

601/606

Empaquetage et dploiement d'un projet


V ous trouverez dans cette annexe les informations connatre pour packager et dployer simplement votre projet sur un serveur d'applications.

Mise en bote du projet JAR, WAR ou EAR ?


Pour faciltier l'tape de dploiement d'un projet, il est coutume de mettre en bote le code dans un fichier ressemblant en tout point une archive, l'exception de la compression. Une application web Java EE peut tre empaquete dans une archive Java (JAR), dans une archive Web (WAR) ou dans une archive dite "d'entreprise" (EAR). En ralit, sur le principe ces trois formats ne diffrent que par leur extension : ce sont tous des fichiers JAR standards ! C'est uniquement par l'usage que les diffrences apparaissent. Quand utiliser un format d'archive en particulier ?

L'utilisation de formats diffrents rend possible l'assemblage de diffrentes applications Java EE partageant des composants identiques. Aucune tape de codage supplmentaire n'est alors requise, seul l'assemblage (ou packaging ) des diffrents lments constituant une application dans des fichiers JAR, WAR ou EAR joue un rle. V oil comment se dfinit habituellement l'usage des trois formats : les modules EJB, qui contiennent les EJB (des classes Java) et leurs ventuels fichiers de description, sont packags dans une archive JAR classique, qui contient un fichier de configuration nomm ejb-jar.xml dans son rpertoire /METAINF ; les servlets, les pages JSP, les lments de prsentation et de design et les classes mtier Java sont packags dans une archive WAR, qui n'est rien d'autre qu'une archive JAR classique contenant un fichier web.xml dans son rpertoire /WEB-INF ; les archives JAR et WAR sont leur tour packages dans une archive globale EAR, qui n'est rien d'autre qu'une archive JAR classique contenant un fichier application.xml dans son rpertoire /META-INF, et qui sera finalement dploye sur le serveur. L'intrt majeur d'isoler les EJB dans une archive spare est de pouvoir sparer convenablement les donnes et le code mtier du reste de l'application. Et c'est exactement ce que l'archive EAR permet : les EJB vont dans un JAR, le contenu li de prs ou de loin au web va dans un WAR (qui au passage, n'est pas forcment un fichier, mais plutt une structure reprsentant l'arborescence du projet). La consquence de cette sparation, c'est que les deux modules JAR et WAR n'ont pas accs aux mmes ressources : le module web peut accder aux EJB (typiquement, des beans), le module EJB peut accder aux ventuelles ressources dfinies globalement dans le module englobant EAR (typiquement, des bibliothques), mais le module EJB ne peut quand lui pas accder aux ressources dfinies dans le module web. Cette contrainte est voulue, et permet de rendre indpendant le module EJB qui doit tre totalement dcoupl d'une quelconque information lie la vue. Cette isolation force permet ainsi de d'assurer qu'un dveloppeur ne puisse pas mlanger les concepts, que ce soit par mgarde ou par ignorance. En outre, grce cette nette sparation il devient trs simple de rutiliser le module EJB depuis d'autres applications web, Java SE ou autres. Si celui-ci contenait des rfrences aux servlets ou Facelets JSF, par exemple, il serait alors compliqu de les utiliser dans d'autres contextes, en particulier dans un contexte Java SE. Pour faire une analogie avec des concepts que vous maitrisez dj, vous pouvez comparer ce systme avec le fait que je vous interdise d'crire des scriptlets dans vos JSP, ou avec le principe de la programmation par contrat (interfaces et implmentations). En somme, placer vos EJB dans un module spar est une bonne pratique. Cependant, dans le cadre de projets de faible envergure, il est frquent de ne pas se soucier de cette pratique. C'est encore plus courant avec des dveloppeurs dbutants, pour lesquels il est souvent difficile de comprendre et de se souvenir de la rpartition des diffrents lments. Ainsi, passer outre la contrainte de dcoupage et tout ranger dans un unique module WAR rend le dploiement plus ais aux dveloppeurs inexpriments, et leur facilite donc l'entre dans le monde Java EE. Tt ou tard, ils finiront quoi qu'il arrive par assimiler la notions de couches, et placeront leurs EJB dans un module spar. Par ailleurs, ce mode d'intgration simplifi fait partie d'un concept plus global intitul EJB Lite, qui est destin rendre possible l'utilisation d'EJB dans une application sans devoir charger l'intgralit les services que peut fournir un conteneur EJB. Cela sort du cadre de cette annexe, mais pour information sachez que c'est une des principales diffrences entre la version complte de GlassFish, et la version "web profile" que je vous ai fait installer.

www.siteduzero.com

Partie 7 : Annexes

602/606

Et donc pour dployer une application, quand utiliser un WAR, et quand utiliser un EAR ?

En rsum, deux possibilits s'offrent vous : si votre projet utilise des EJB et est dploy sur un serveur d'application Java EE, alors vous pouvez suivre la recommandation et utiliser une archive EAR, qui sera elle-mme constitue d'une ou plusieurs archives WAR et JAR. V ous pouvez galement ignorer la bonne pratique courante et utiliser un unique fichier WAR, mais sachez que derrire les rideaux, votre serveur va en ralit automatiquement englober votre archive... dans une archive EAR ! si votre projet web ne fait pas intervenir d'EJB, alors seule une archive WAR est requise. En outre, si votre serveur n'est pas un serveur d'applications Java EE au sens strict du terme, comme Tomcat ou Jetty par exemple, alors vous ne pourrez pas utiliser le format EAR, vous devrez obligatoirement packager vos applications sous forme d'achives WAR.

Mise en pratique
Nos projets sont de trs faible envergure, ne font que trs peu intervenir les EJB et nous dbutons le Java EE : nous allons donc apprendre dployer un projet sous forme d'une unique archive WAR. Prenons pour exemple le projet intitul pro que nous avons dvelopp dans le cadre du cours. Pour l'exporter sous forme d'archive WAR depuis Eclipse, rien de plus simple ! Faites un clic-droit sur votre projet dans le volet de gauche de votre espace de travail Eclipse, puis choisissez Export > WAR File :

Une fentre s'ouvre alors, dans laquelle vous devez choisir un emplacement pour enregistrer l'archive qui va tre cre :

www.siteduzero.com

Partie 7 : Annexes

603/606

V ous pouvez par ailleurs choisir d'optimiser votre WAR pour un type de serveur en particulier, et d'inclure les fichiers source de votre code dans l'archive gnre. Cliquez-alors sur le bouton Finish, et votre WAR apparatra alors dans le dossier que vous avez spcifi sur votre disque :

V oil tout ce qu'il est ncessaire de faire pour exporter votre projet dans une archive WAR !

Dploiement du projet Contexte


Habituellement, le dveloppement d'une application et la premire phase de tests (les tests unitaires, et ventuellement d'autres tests plus spcifiques) sont raliss depuis un serveur local, trs souvent intgr Eclipse (ou Netbeans, ou autre IDE similaire). Ensuite, il est courant qu'un premier dploiement ait lieu sur une seconde plate-forme, dite "de qualification". Celle-ci correspond en gnral une rplique plus ou moins fidle de l'environnement de production final. Enfin a lieu le dploiement sur la plate-forme de production, la plate-forme finale sur laquelle va tourner l'application ouverte aux utilisateurs. Par ailleurs, il est important de noter que parfois le dveloppement et la qualification sont effectus sur un type de serveur d'applications, par exemple Tomcat ou GlassFish installs sur une distribution Debian, puis l'application est finalement dploye sur un serveur dont la configuration est diffrente en production (bien souvent pour des raisons de cots ou de simplicit), par exemple un serveur WebLogic install sur une distribution OpenSuse. Bref, vous avez compris o je veux en venir : une mme application est amene tre utilise dans diffrents contextes et sur diffrentes configurations, et il est donc ncessaire de pouvoir simplement installer et mettre jour une application sur les plate-formes de qualification et de production sans contraintes.

www.siteduzero.com

Partie 7 : Annexes

604/606

V oil pourquoi le systme d'archives a t mis en place : en exportant votre application dans une archive WAR ou EAR, vous vous assurez de pouvoir la transporter et manipuler de manire extrmement simple !

Mise en pratique
Pour commencer, si ce n'est pas dj fait retirez le projet pro de votre serveur Tomcat intgr Eclipse. Pour ce faire, il vous suffit de faire un clic-droit sur le projet dans le volet Servers et de choisir Remove :

Dans la fentre d'avertissement qui s'ouvre alors, confirmez en appuyant sur Ok. Ainsi, nous sommes maintenant certains que notre application n'existe plus dans Tomcat. Fermez ensuite Eclipse. Nous allons maintenant lancer Tomcat manuellement. La manipulation est diffrente selon le systme d'exploitation que vous utilisez : depuis Windows, rendez-vous dans le dossier /bin de votre installation de Tomcat, et lancez le fichier intitul startup.bat ; depuis un systme Unix (Linux ou Mac OS), ouvrez un Terminal, dplacez-vous dans le dossier de votre installation de Tomcat, et excutez la commande bin/startup.sh . V otre serveur correctement lanc, vous pouvez dornavant accder l'URL http://localhost:8080 depuis votre navigateur pour vrifier que votre serveur fonctionne, et vous pouvez galement vrifier que vous ne pouvez pas accder l'URL http://localhost:8080/pro/inscription, autrement dit que l'application pro n'est pas encore disponible. Il ne nous reste plus qu' dployer notre application sur notre server Tomcat. Pour cela, rien de plus simple : nous devons dposer notre archive WAR dans le rpertoire webapps de Tomcat. Copiez-collez donc votre archive pro.war dans ce dossier, puis rendez-vous nouveau sur l'URL http://localhost:8080/pro/inscription depuis votre navigateur. V ous devez alors constater que votre application fonctionne : Tomcat a automatiquement analys et dploy votre archive WAR ! Par ailleurs, vous pourrez galement vrifier que dans le rpertoire webapps existe dornavant un dossier intitul pro : c'est Tomcat qui l'a cr partir du contenu de votre fichier WAR. V ous y trouverez toutes vos classes compiles dans le rpertoire WEB-INF/classes , vos JSP et autres lments de design. Si vous aviez choisi d'intgrer les fichiers source de l'application l'archive lors de l'export depuis Eclipse, alors vous y trouverez galement vos fichiers sources Java. Sous GlassFish, l'opration est sensiblement la mme : lancez votre serveur en vous rendant dans le dossier /glassfish/bin, et en excutant le fichier startserv ou startserv.bat selon que vous utilisiez Unix ou Windows ; dployez alors automatiquement votre archive en la dposant dans le rpertoire autodeploy prsent dans /glassfish/domains/nom-de-votre-domaine/ .

Avancement du cours
Ce cours est termin ! J'apporterai ventuellement de fines corrections par endroits lorsque ncessaire, mais le contenu et la structure du cours ne changeront plus.

www.siteduzero.com

Partie 7 : Annexes

605/606

Et aprs ?
J'espre que ce modeste cours vous ouvrira les portes vers des technologies non abordes ici, notamment : d'autres frameworks MVC, comme Spring ou Stripes ; d'autres frameworks de persistance, comme Hibernate ou MyBatis. Autour du dveloppement web en Java, les sujets sont vastes et les outils nombreux, mais tous vous sont accessibles : n'hsitez pas vous y plonger, et pourquoi pas vous lancer dans la rdaction d'un tutoriel leur sujet !

www.siteduzero.com

You might also like