You are on page 1of 572

Rfrence

Java EE 6 et GlassFish 3
Antonio Goncalves

Rseaux et tlcom Programmation Dveloppement web Scurit Systme dexploitation

Java EE 6 et GlassFish 3
Antonio Goncalves

Traduit par ric Jacoboni, avec la contribution technique de ric Hbert

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient rsulter de cette utilisation. Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle. Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices oudommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes. Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs propritaires respectifs.
Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 www.pearson.fr Mise en pages : TyPAO ISBN : 978-2-7440-4157-0 Copyright 2010 Pearson Education France Tous droits rservs ISBN original: 978-1-4302-1954-5 Copyright 2009 by Antonio Goncalves Tous droits rservs dition originale publie par Apress 2855 Telegraph Avenue Berkeley, CA 94705 www.apress.com
Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L. 122-10 dudit code. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

Titre original: Beginning Java EE6 Platform with GlassFish 3

Traduction: ric Jacoboni, avec la contribution de ric Hbert

Table des matires


Avant-propos ...................................................................................................................... propos de lauteur ......................................................................................................... Remerciements ................................................................................................................... Introduction ........................................................................................................................ 1 Tour dhorizon de Java EE 6 ............................................................................................ Prsentation de Java EE ........................................................................................................ XIII XIV XV 1 5 6 6 9 9 10 11 12 14 15 16 16 18 19 19 20 21 22 24 24 26 26 27 34

Un peu dhistoire............................................................................................................ Standards .......................................................................................................................... Architecture ..................................................................................................................... Composants ..................................................................................................................... Conteneurs ....................................................................................................................... Services ............................................................................................................................ Protocoles rseau ........................................................................................................... Paquetages ....................................................................................................................... Java Standard Edition ...................................................................................................
Spcifications de Java EE 6 ................................................................................................ Nouveauts de Java EE6......................................................................................................

Plus lger .......................................................................................................................... lagage ............................................................................................................................. Profils ................................................................................................................................ Plus simple dutilisation .............................................................................................. Plus riche.......................................................................................................................... Plus portable....................................................................................................................
Lapplication CD-Bookstore ............................................................................................... Configuration de lenvironnement de travail ...................................................................

JDK 1.6............................................................................................................................. Maven 2 ............................................................................................................................ JUnit 4...............................................................................................................................

IV

Java EE 6 et GlassFish 3

Derby 10.5 ....................................................................................................................... GlassFish v3 .................................................................................................................... Installation de GlassFish ..............................................................................................


Rsum .................................................................................................................................... 2 Persistance en Java ............................................................................................................. Rsum de la spcification JPA ...........................................................................................

39 42 48 50 51 52 53 54 54 55 55 57 59 60 61 62 63 64 66 67 69 70 71 73 73 76 77 78 80 85 92 95 97 99

Historique de la spcification...................................................................................... Nouveauts de JPA 2.0 ................................................................................................. Implmentation de rfrence .....................................................................................


Comprendre les entits .........................................................................................................

ORM = Object-Relational Mapping ......................................................................... Interrogation des entits ............................................................................................... Mthodes de rappel et couteurs ...............................................................................
Rcapitulatif............................................................................................................................

criture de lentit Book .............................................................................................. criture de la classe Main ............................................................................................ Unit de persistance pour la classe Main ................................................................. Compilation avec Maven ............................................................................................. Excution de la classe Main avec Derby ................................................................. criture de la classe BookTest..................................................................................... Unit de persistance pour la classe BookTest ........................................................ Excution de la classe BookTest avec Derby intgr ...........................................
Rsum .................................................................................................................................... 3 ORM : Object-Relational Mapping................................................................................. Association dune entit .......................................................................................................

Configuration par exception ........................................................................................


Associations lmentaires ....................................................................................................

Tables ................................................................................................................................ Cls primaires ................................................................................................................. Attributs............................................................................................................................ Types daccs .................................................................................................................. Collections de types de base ....................................................................................... Association des types de base .....................................................................................
Associations avec XML........................................................................................................

Table des matires

Objets intgrables ..................................................................................................................

Types daccs dune classe intgrable ......................................................................


Correspondance des relations ..............................................................................................

102 104 106 107 109 121 122 126 127 135 139 141 142 145 147 149 150 162 163 165 167 167 169 169 170 171 171 174 175 178 179 182 183 185 186

Relations dans les bases de donnes relationnelles ............................................... Relations entre entits .................................................................................................. Chargement des relations ............................................................................................. Tri des relations ..............................................................................................................
Traduction de lhritage........................................................................................................

Stratgies dhritage ...................................................................................................... Type de classes dans une hirarchie dhritage ......................................................
Rsum .................................................................................................................................... 4 Gestion des objets persistants............................................................................................ Interrogation dune entit ..................................................................................................... Le gestionnaire dentits.......................................................................................................

Obtenir un gestionnaire dentits ............................................................................... Contexte de persistance ................................................................................................ Manipulation des entits .............................................................................................. LAPI de cache ...............................................................................................................
JPQL ........................................................................................................................................

Select ................................................................................................................................. From .................................................................................................................................. Where ................................................................................................................................ Order By ........................................................................................................................... Group By et Having....................................................................................................... Suppressions multiples ................................................................................................ Mises jour multiples...................................................................................................
Requtes ..................................................................................................................................

Requtes dynamiques ................................................................................................... Requtes nommes ........................................................................................................ Requtes natives .............................................................................................................


Concurrence ............................................................................................................................

Gestion de version ......................................................................................................... Verrouillage optimiste................................................................................................... Verrouillage pessimiste ................................................................................................


Rsum ....................................................................................................................................

VI

Java EE 6 et GlassFish 3

Mthodes de rappel et couteurs ...................................................................................... Cycle de vie dune entit ...................................................................................................... Mthodes de rappel ............................................................................................................... couteurs (listeners) .............................................................................................................. Rsum ....................................................................................................................................

187 187 189 192 198 199 200 201 202 204 205 206 207 209 209 210 211 213 214 215 216 217 218 220 221 221 223 225 226 226 229 232 239 252

Enterprise Java Beans ......................................................................................................... Introduction aux EJB ............................................................................................................

Types dEJB .................................................................................................................... Anatomie dun EJB ...................................................................................................... Conteneur dEJB ............................................................................................................ Conteneur intgr........................................................................................................... Injection de dpendances et JNDI ............................................................................. Mthodes de rappel et intercepteurs..........................................................................
Tour dhorizon de la spcification EJB ..............................................................................

Historique ........................................................................................................................ Nouveauts dEJB 3.1 .................................................................................................. EJB Lite ............................................................................................................................


Rcapitulatif............................................................................................................................

Lentit Book ................................................................................................................... Le bean de session sans tat BookEJB ..................................................................... Unit de persistance pour le BookEJB...................................................................... La classe Main ................................................................................................................ Compilation et assemblage avec Maven .................................................................. Dploiement sur GlassFish.......................................................................................... Excution de la classe Main avec Derby ................................................................. La classe BookEJBTest .................................................................................................
Rsum .................................................................................................................................... 7 Beans de session et service timer ...................................................................................... Beans de session ....................................................................................................................

Beans sans tat................................................................................................................ Beans avec tat ............................................................................................................... Singletons ........................................................................................................................ Modle des beans de session....................................................................................... Appels asynchrones .......................................................................................................

Table des matires

VII

Conteneurs intgrs ......................................................................................................


Le service timer......................................................................................................................

254 256 257 260 261 263 265 266 266 267 269 272 274 277 278 280 282 283 283 284 285 286 288 289 296 298 299 300 301 301 305 307 309 310

Expressions calendaires ............................................................................................... Cration automatique dun timer ............................................................................... Cration dun timer par programme ........................................................................
Rsum .................................................................................................................................... 8 Mthodes de rappel et intercepteurs ............................................................................... Cycles de vie des beans de session .....................................................................................

Beans sans tat et singletons ....................................................................................... Beans avec tat ............................................................................................................... Mthodes de rappel .......................................................................................................
Intercepteurs ...........................................................................................................................

Intercepteurs autour des appels .................................................................................. Intercepteurs de mthode ............................................................................................. Intercepteur du cycle de vie ........................................................................................ Chanage et exclusion dintercepteurs .....................................................................
Rsum .................................................................................................................................... 9 Transactions et scurit....................................................................................................... Transactions ............................................................................................................................

ACID ................................................................................................................................. Transactions locales ...................................................................................................... XA et transactions distribues ....................................................................................


Support des transactions avec les EJB ...............................................................................

Transactions gres par le conteneur ....................................................................... Transactions gres par le bean ..................................................................................
Scurit ....................................................................................................................................

Principal et rle .............................................................................................................. Authentification et habilitation ...................................................................................


Gestion de la scurit dans EJB ..........................................................................................

Scurit dclarative ....................................................................................................... Scurit par programmation ........................................................................................


Rsum .................................................................................................................................... 10 JavaServer Faces .................................................................................................................. Introduction JSF..................................................................................................................

VIII

Java EE 6 et GlassFish 3

FacesServlet et faces-config.xml ................................................................................ Pages et composants...................................................................................................... Moteurs de rendu ........................................................................................................... Convertisseurs et validateurs....................................................................................... Beans grs et navigation ............................................................................................ Support dAjax ...............................................................................................................
Rsum des spcifications de linterface web ..................................................................

311 311 313 313 314 315 316 316 317 317 318 318 318 320 320 321 322 325 327 328 329 329 330 331 332 332 334 335 338 338 341 342 343 344 345

Bref historique des interfaces web ............................................................................ JSP 2.2, EL 2.2 et JSTL 1.2 ........................................................................................ JSF 2.0 .............................................................................................................................. Nouveauts de JSF 2.0 ................................................................................................. Implmentation de rfrence.......................................................................................
Rcapitulatif............................................................................................................................

Lentit Book ................................................................................................................... LEJB BookEJB .............................................................................................................. Le bean gr BookController ...................................................................................... La page newBook.xhtml................................................................................................ La page listBooks.xhtml ............................................................................................... Configuration avec web.xml ........................................................................................ Compilation et assemblage avec Maven .................................................................. Dploiement dans GlassFish ....................................................................................... Excution de lapplication ...........................................................................................
Rsum .................................................................................................................................... 11 Pages et composants ............................................................................................................. Pages web................................................................................................................................

HTML ............................................................................................................................... XHTML............................................................................................................................ CSS .................................................................................................................................... DOM ................................................................................................................................. JavaScript .........................................................................................................................


Java Server Pages ...................................................................................................................

Directives ......................................................................................................................... Scripts ............................................................................................................................... Actions.............................................................................................................................. Rcapitulatif ....................................................................................................................

Table des matires

IX

Langage dexpressions (EL) ................................................................................................ La bibliothque de marqueurs standard de JSP (JSTL) ..................................................

347 349 350 351 353 355 357 358 360 361 363 374 375 381 383 385 385 387 389 390 390 391 391 396 400 402 404 406 407 408 410 410 412 413 418

Actions fondamentales ................................................................................................. Actions de formatage .................................................................................................... Actions SQL.................................................................................................................... Actions XML .................................................................................................................. Fonctions ..........................................................................................................................
Facelets .................................................................................................................................... JavaServer Faces ....................................................................................................................

Cycle de vie ..................................................................................................................... Composants HTML standard ...................................................................................... Gestion des ressources.................................................................................................. Composants composites ............................................................................................... Objets implicites ............................................................................................................
Rsum .................................................................................................................................... 12 Traitement et navigation..................................................................................................... Le modle MVC ....................................................................................................................

FacesServlet .................................................................................................................... FacesContext ................................................................................................................... Configuration de Faces .................................................................................................


Beans grs .............................................................................................................................

criture dun bean gr ................................................................................................ Modle dun bean gr ................................................................................................. Navigation........................................................................................................................ Gestion des messages ...................................................................................................
Conversion et validation .......................................................................................................

Convertisseurs................................................................................................................. Convertisseurs personnaliss ...................................................................................... Validateurs ....................................................................................................................... Validateurs personnaliss .............................................................................................


Ajax ..........................................................................................................................................

Concepts gnraux......................................................................................................... Ajax et JSF ...................................................................................................................... Rcapitulatif ....................................................................................................................


Rsum ....................................................................................................................................

Java EE 6 et GlassFish 3

13

Envoi de messages ................................................................................................................ Prsentation des messages....................................................................................................

419 420 420 421 421 422 422 422 423 423 425 427 428 429 442 443 446 447 447 452 453 455 456 457 457 458 459 459 461 461 462 462 465 466 467

JMS.................................................................................................................................... MDB..................................................................................................................................
Rsum de la spcification des messages .........................................................................

Bref historique des messages ...................................................................................... JMS 1.1............................................................................................................................. EJB3.1 ............................................................................................................................. Implmentation de rfrence.......................................................................................
Envoi et rception dun message ......................................................................................... Java Messaging Service ........................................................................................................

Point point .................................................................................................................... Publication-abonnement............................................................................................... API JMS ........................................................................................................................... Slecteurs ......................................................................................................................... Mcanismes de fiabilit ...............................................................................................
MDB: Message-Driven Beans ............................................................................................

Cration dun MDB....................................................................................................... Le modle des MDB ..................................................................................................... MDB comme consommateur ...................................................................................... MDB comme producteur ............................................................................................. Transactions .................................................................................................................... Gestion des exceptions .................................................................................................
Rcapitulatif............................................................................................................................

OrderDTO........................................................................................................................ OrderSender .................................................................................................................... OrderMDB ....................................................................................................................... Compilation et assemblage avec Maven .................................................................. Cration des objets administrs .................................................................................. Dploiement du MDB dans GlassFish ..................................................................... Excution de lexemple ................................................................................................
Rsum .................................................................................................................................... 14 Services web SOAP .............................................................................................................. Prsentation des services web..............................................................................................

UDDI.................................................................................................................................

Table des matires

XI

WSDL ............................................................................................................................... SOAP ................................................................................................................................ Protocole de transport ................................................................................................... XML ..................................................................................................................................


Rsum de la spcification des services web ...................................................................

467 468 468 468 468 469 469 471 471 473 476 478 481 481 484 485 486 494 496 497 497 498 499 501 502 505 505 507 507 508 508 509 510 510 516

Bref historique des services web................................................................................ Spcifications Java EE .................................................................................................. Implmentation de rfrence.......................................................................................
Appel dun service web ....................................................................................................... JAXB: Java Architecture for XML Binding.....................................................................

Liaison .............................................................................................................................. Annotations .....................................................................................................................


La partie immerge de liceberg..........................................................................................

WSDL ............................................................................................................................... SOAP ................................................................................................................................


JAX-WS: Java API for XML-Based Web Services ........................................................

Le modle JAX-WS ...................................................................................................... Appel dun service web ...............................................................................................


Rcapitulatif............................................................................................................................

La classe CreditCard..................................................................................................... Le service web CardValidator .................................................................................... Compilation et assemblage avec Maven .................................................................. Dploiement dans GlassFish ....................................................................................... Le consommateur du service web ............................................................................ Cration des artefacts du consommateur et assemblage avec Maven............... Excution de la classe Main ........................................................................................
Rsum .................................................................................................................................... 15 Services web REST .............................................................................................................. Prsentation des services web REST..................................................................................

Ressources ....................................................................................................................... URI .................................................................................................................................... Reprsentations .............................................................................................................. WADL ............................................................................................................................... HTTP ................................................................................................................................
Spcification des services web REST ................................................................................

XII

Java EE 6 et GlassFish 3

Historique rapide de REST ......................................................................................... JAX-RS 1.1...................................................................................................................... Nouveauts de JAX-RS 1.1 ......................................................................................... Implmentation de rfrence.......................................................................................
Lapproche REST ..................................................................................................................

516 517 517 517 518 518 519 519 520 521 522 523 523 524 525 526 528 530 532 534 536 537 537 538 539 542 543 544 545 546 547

Du Web aux services web ........................................................................................... Pratique de la navigation sur le Web ......................................................................... Interface uniforme ......................................................................................................... Accessibilit .................................................................................................................... Connectivit .................................................................................................................... Sans tat ...........................................................................................................................
JAX-RS: Java API for RESTful Web Services ................................................................

Le modle JAX-RS........................................................................................................ criture dun service REST ........................................................................................ Dfinition des URI......................................................................................................... Extraction des paramtres ............................................................................................ Consommation et production des types de contenus ........................................... Fournisseurs dentits ................................................................................................... Mthodes ou interface uniforme ................................................................................ Informations contextuelles .......................................................................................... Gestion des exceptions ................................................................................................. Cycle de vie .....................................................................................................................
Rcapitulatif............................................................................................................................

Lentit Book ................................................................................................................... Le service REST BookResource ................................................................................ Configuration avec web.xml ........................................................................................ Compilation et assemblage avec Maven .................................................................. Dploiement dans GlassFish ....................................................................................... Excution de lexemple ................................................................................................
Rsum .................................................................................................................................... Index ...................................................................................................................................................

Avant-propos

Bien que Java EE5 soit unanimement considre comme la version la plus importante de Java EE, Java EE 6 fournit de nombreuses fonctionnalits supplmentaires. La cration dapplications dentreprise est, en effet, encore amliore grce EJB3.1, JPA2.0, JAX-RS et JSF2.0. La plate-forme Java EE est dsormais arrive un niveau de maturit permettant de faire ctoyer lgret et puissance. Vous pourriez videmment utiliser votre navigateur favori pour parcourir les nombreux blogs, wikis et articles consacrs Java EE6, mais je vous conseille plutt de commencer par lire ce livre: il est concis, pragmatique et il distille lexprience complte de lauteur sur le sujet. Cet ouvrage utilise GlassFish comme serveur dapplications sous-jacent pour plusieurs raisons: GlassFishV3 est limplmentation de rfrence et est donc en phase avec Java EE6. Par ailleurs, lexprience acquise en utilisant une implmentation de rfrence et les technologies les plus rcentes est adaptable au monde de lentreprise. Ce que vous apprendrez sera directement exploitable en production. Antonio Goncalves est un exemple rare de dveloppeur o se mlent passion pour Java et exprience professionnelle de Java EE. Son travail de consultant, combin sa participation au Java User Group de Paris ainsi, bien sr, que son rle dans les diffrents groupes dexperts Java EE6 en font lauteur idal de Java EE6 et GlassFish3. Aprs la lecture de ce livre, vous aurez compris que la plus grande richesse de Java EE nest pas la somme de ses fonctionnalits mais la communaut qui l'a cr, ainsi que sa nature mme de standard qui vous permet de choisir ou de modifier l'implmentation en fonction de vos besoins. La libert n'est pas simplement reprsente par l'open-source, mais galement par les standards ouverts. Alexis Moussine-Pouchkine quipe GlassFish, Sun Microsystems.

propos de lauteur

Antonio Goncalves est un architecte logiciel; il vit Paris. Sintressant au dveloppement en Java depuis la fin des annes 1990, il a travaill dans diverses socits de diffrents pays, pour lesquelles il intervient dsormais en tant que consultant en architecture logicielle JavaEE. Son exprience lui a permis dacqurir une grande connaissance des serveurs dapplications comme WebLogic, JBoss et, bien sr, GlassFish. Cest un ardent dfenseur des logiciels open-source il est membre de lOSSGTP (Open Source Get Together Paris). Il est galement lun des initiateurs et des animateurs du Paris Java User Group. Antonio a crit son premier livre sur Java EE5 en 2007. Depuis, il a rejoint le JCP et est lun des experts de plusieurs JSR (Java EE6, JPA2.0 et EJB3.1). Au cours de ces dernires annes, Antonio est intervenu dans plusieurs confrences internationales consacres essentiellement JavaEE notamment JavaOne, The Server Side Symposium, Devoxx et Jazoon. Il a galement publi de nombreux articles techniques, aussi bien pour des sites web (DevX) que pour des magazines spcialiss (Programmez, Linux Magazine). Antonio possde le diplme dingnieur en informatique du CNAM (Conservatoire national des arts et mtiers) de Paris et une matrise en conception oriente objet de luniversit de Brighton.

Remerciements

crire un livre sur une nouvelle spcification comme Java EE 6 est une tche norme qui mobilise les talents de plusieurs personnes. Avant tout, je remercie Steve Anglin, dApress, de mavoir donn lopportunit de participer la collection "Beginning Series" dApress, que japprcie beaucoup en tant que lecteur. Tout au long de mon travail de rdaction, jai t en contact avec Candace English et Tom Welsh, qui ont relu louvrage et mont rassur lorsque javais des doutes sur le fait de pouvoir le terminer en temps voulu. Je remercie galement les relecteurs techniques, Jim Farley et Sumit Pal, qui ont fait un excellent travail en me suggrant de nombreuses amliorations. Enfin, jai admir le travail remarquable dAmi Knox, qui a produit la dernire version de ldition. Merci galement Alexis Midon et Sebastien Auvray, les coauteurs du Chapitre15, consacr aux services web REST: Alex est un informaticien passionn, fan de REST, et Sbastien est un dveloppeur talentueux qui utilise REST de faon pragmatique. Merci eux pour leur aide prcieuse. Je remercie tout spcialement Alexis Moussine-Pouchkine, qui a gentiment accept dcrire la prface et la section sur GlassFish. Il ma galement t dun grand secours pour contacter les bonnes personnes pour des sujets particuliers: je pense Ryan Lubke pour JSF2.0, Paul Sandoz pour JAX-RS1.1 et Franois Orsini pour Derby. Merci Damien Gouyette pour son aide sur JSF2.0. Damien a une grande exprience en dveloppement web et en JSF en particulier (au fait, merci celui qui gre le dpt SVN). Merci galement Arnaud Hritier pour avoir crit la section sur Maven et pour mavoir aid rsoudre certains problmes avec Maven, ainsi qu Nicolas de Loof pour sa relecture technique sur le sujet. Sbastien Moreno ma aid pour JUnit et a relu le manuscrit complet avec David Dewalle et Pascal Graffion tout cela dans un dlai imparti trs serr. Je les remercie beaucoup pour leur travail. Je remercie les correcteurs, Denise Green et Stefano Costa, pour avoir tent de donner une touche shakespearienne ce livre.

XVI

Java EE 6 et GlassFish 3

Les diagrammes de cet ouvrage ont t raliss avec lextension Visual Paradigm dIntelliJ IDEA. Je remercie dailleurs Visual Paradigm et JetBrains pour mavoir offert une licence gratuite de leurs excellents produits. Je naurais pas pu crire ce livre sans laide de la communaut Java: tous ceux qui ont pris sur leur temps libre pour maider dans leurs courriers lectroniques, dans les listes de diffusion ou sur les forums de discussion. La liste de diffusion des groupes dexperts JCP est, videmment, la premire qui me vient lesprit: merci Roberto Chinnici, Bill Shannon, Kenneth Saks, Linda DeMichiel, Michael Keith, Reza Rahman, Adam Bien, etc. Un gros bisou ma fille, lose: les interruptions dues son espiglerie mont aid alors que je passais mes week-ends crire. Un livre est le produit dun nombre incalculable de personnes que je voudrais remercier pour leur contribution, que ce soit pour un avis technique, une bire dans un bar, un extrait de code... Merci donc Jean-Louis Dewez, Frdric Drouet, les geeks du JUG de Paris, T.Express, les membres dOSSGTP, les Cast Codeurs, FIP, Marion, Les Connards, Vitalizen, La Fontaine, Ago, Laure, La Grille, les Eeckman, Yaya, Rita, os Navalhas, La Commune Libre dAligre, etc. Merci tous!

Introduction

Aujourdhui, les applications doivent accder des donnes, appliquer une logique mtier, ajouter des couches de prsentation et communiquer avec des systmes externes. Les entreprises tentent de raliser toutes ces oprations moindre cot, en se servant de technologies standard et robustes supportant des charges importantes. Si vous tes dans cette situation, ce livre est fait pour vous. Java Enterprise Edition est apparu la fin des annes 1990 et a dot le langage Java dune plate-forme logicielle fiable, prte pour les besoins des entreprises. Critiqu chaque nouvelle version, mal compris ou mal utilis, en comptition avec les frameworks open-source, J2EE a t considr comme une technologie lourde. JavaEE a bnfici de toutes ces critiques pour samliorer: son but actuel est la simplicit. Si vous faites partie de ceux qui pensent "que les EJB sont nuls et quil faut sen dbarrasser", lisez ce livre et vous changerez davis. Les EJB (Enterprise Java Beans), comme toutes les technologies de JavaEE, sont des composants puissants. Si, au contraire, vous tes un fan de Java EE, cet ouvrage vous montrera que la plate-forme a trouv son quilibre grce une simplification du dveloppement, de nouvelles spcifications, un modle de composants EJB plus lgers, des profils et llagage. Si vous dbutez avec JavaEE, ce livre vous guidera dans votre apprentissage des spcifications les plus importantes au moyen dexemples et de diagrammes trs simples comprendre. Les standards ouverts sont lune des forces principales de Java EE. Plus que jamais, les applications crites avec JPA, EJB, JSF, JMS, les services web SOAP ou REST sont portables entre les diffrents serveurs dapplications. Comme vous pourrez le constater, la plupart des implmentations de rfrence de Java EE 6 (GlassFish, EclipseLink, Mojarra, OpenMQ, Metro et Jersey) sont distribues sous les termes dune licence open-source. Ce livre explore les innovations de cette nouvelle version et examine les diffrentes spcifications et la faon de les assembler pour dvelopper des applications. Java

Java EE 6 et GlassFish 3

EE6 est form denviron 30spcifications et cest une version importante pour la couche mtier (EJB3.1, JPA2.0), la couche web (Servlet3.0, JSF2.0) et linteroprabilit (services web SOAP et REST). Ce livre prsente une grande partie de toutes ces spcifications, utilise le JDK1.6 et certains patrons de conception bien connus, ainsi que le serveur dapplications GlassFish, la base de donnes Derby, JUnit et Maven. Il est abondamment illustr de diagrammes UML, de code Java et de copies dcran.

Structure du livre
Ce livre ne se veut pas une rfrence exhaustive de Java EE6. Il sintresse essentiellement aux spcifications les plus importantes et aux nouvelles fonctionnalits de cette version. Sa structure respecte le dcoupage de larchitecture dune application.
Prsentation Chapitre 10 : JavaServer Faces Chapitre 11 : Pages et composants Chapitre 12 : Traitements et navigation

Logique mtier Chapitre 6 : Enterprise Java Beans Chapitre 7 : Beans de session et service timer Chapitre 8 : Mthodes de rappel et intercepteurs Chapitre 9 : Transactions et scurit Interoprabilit Chapitre 13 : Envoi de messages Chapitre 14 : Services web SOAP Chapitre 15 : Services web REST

Persistance Chapitre 2 : Persistance en Java Chapitre 3 : ORM : ObjectRelational Mapping Chapitre 4 : Gestion des objets persistants Chapitre 5 : Mthodes de rappel et couteurs

Le Chapitre1 prsente brivement lessentiel de Java EE6 et les outils que nous utiliserons dans ce livre (JDK, Maven, JUnit, Derby et GlassFish). La couche de persistance est dcrite du Chapitre2 au Chapitre5 et sintresse principalement JPA2.0. Aprs un survol gnral et quelques exemples au Chapitre2, le Chapitre 3 sintresse lassociation objet-relationnel (ORM). Le Chapitre 4 montre comment grer et interroger les entits, tandis que le5 prsente leur cycle de vie, les mthodes de rappel et les couteurs.

Introduction

Pour dvelopper une couche mtier transactionnelle avec Java EE6, on utilise naturellement les EJB, qui seront dcrits du Chapitre 6 au Chapitre 9. Le Chapitre 6 passe en revue la spcification, son histoire et donne des exemples de code, tandis que le7 sintresse plus particulirement aux beans de session et leur modle de programmation, ainsi quau nouveau service timer. Le Chapitre8 est consacr au cycle de vie des EJB et aux intercepteurs, tandis que le9 explique tout ce qui a trait aux transactions et la scurit. Du Chapitre10 au Chapitre12, vous apprendrez dvelopper une couche de prsentation avec JSF2.0. Aprs une prsentation de la spcification au Chapitre10, le Chapitre11 sintresse la construction dune page web avec JSF et Facelets. Le12, quant lui, explique comment interagir avec un "backend" EJB et comment naviguer entre les pages. Enfin, les derniers chapitres vous prsenteront diffrents moyens dchanger des informations avec dautres systmes. Le Chapitre13 montre comment changer des messages asynchrones avec JMS (Java Message Service) et les MDB (Message-Driven Beans); le Chapitre14 sintresse aux services web SOAP tandis que le15 est consacr aux services web REST.

Tlchargement et excution du code


Les exemples de ce livre sont conus pour tre compils avec le JDK1.6, dploys sur le serveur dapplications GlassFish V3 et stocks dans la base de donnes Derby. Le Chapitre1 explique comment installer tous ces logiciels et chaque chapitre montre comment compiler, dployer, excuter et tester les composants selon la technologie employe. Tous les codes des exemples ont t tests sur la plate-forme Windows, mais ni sur Linux ni sur OS X. Les codes sources de ces exemples sont disponibles sur le site des ditions Pearson (www.pearson.fr), sur la page consacre cet ouvrage.

Contacter lauteur
Pour toute question sur le contenu de ce livre, sur le code ou tout autre sujet, contactez-moi ladresse antonio.goncalves@gmail.com. Vous pouvez galement visiter mon site web, http://www. antoniogoncalves.org.

1
Tour dhorizon de Java EE 6
De nos jours, les entreprises voluent dans une comptition lchelle mondiale. Elles ont besoin pour rsoudre leurs besoins mtiers dapplications qui deviennent de plus en plus complexes. notre poque de mondialisation, les socits sont prsentes sur les diffrents continents, fonctionnent 24heures sur24, 7jours sur 7via Internet et dans de nombreux pays ; leurs systmes doivent tre internationaliss et savoir traiter plusieurs monnaies et des fuseaux horaires diffrents tout ceci en rduisant les cots, en amliorant le temps de rponse des services, en stockant les donnes sur des supports fiables et scuriss et en offrant diffrentes interfaces graphiques leurs clients, employs et fournisseurs. La plupart des socits doivent combiner ces dfis innovants avec leur systme dinformation existant tout en dveloppant en mme temps des applications B2B (business to business) pour communiquer avec leurs partenaires. Il nest pas rare non plus quune socit doive coordonner des donnes stockes divers endroits, traites par plusieurs langages de programmation et achemines via des protocoles diffrents. videmment, ceci ne doit pas faire perdre dargent, ce qui signifie quil faut empcher les pannes du systme et toujours rester disponible, scuris et volutif. Les applications dentreprise doivent faire face aux modifications et la complexit tout en tant robustes. Cest prcisment pour relever ces dfis qua t cr Java Enterprise Edition (Java EE). La premire version de Java EE (connue sous le nom de J2EE) se concentrait sur les problmes que devaient rsoudre les socits en 1999: les composants distribus. Depuis, les logiciels ont d sadapter de nouvelles solutions techniques comme les services web SOAP ou REST. La plate-forme a donc volu pour tenir compte de ces besoins en proposant plusieurs mcanismes standard sous forme de spcifications. Au cours des annes, Java EE a volu pour devenir plus riche, plus lger, plus simple dutilisation et plus portable.

Java EE 6 et GlassFish 3

Ce chapitre fait un tour dhorizon de Java EE. Aprs une prsentation rapide de son architecture interne, il prsentera les nouveauts de Java EE6. La seconde partie du chapitre est consacre la mise en place de votre environnement de dveloppement pour que vous puissiez vous-mme mettre en uvre les extraits de code prsents dans ce livre.

Prsentation de Java EE
Lorsque lon veut traiter une collection dobjets, on ne commence pas par dvelopper sa propre table de hachage: on utilise lAPI des collections. De mme, lorsque lon a besoin dune application transactionnelle, scurise, interoprable et distribue, on ne dveloppe pas des API de bas niveau: on utilise la version entreprise de Java (JavaEE). Tout comme ldition standard de Java (JavaSE, Standard Edition) permet de traiter les collections, JavaEE fournit des moyens standard pour traiter les transactions via Java Transaction API (JTA), les messages via Java Message Service (JMS) ou la persistance via Java Persistence API (JPA). JavaEE est un ensemble de spcifications pour les applications dentreprise; il peut donc tre considr comme une extension de Java SE destine faciliter le dveloppement dapplications distribues, robustes, puissantes et haute disponibilit. Java EE6 est une version importante. Non seulement elle marche dans les pas de Java EE 5 pour fournir un modle de dveloppement simplifi, mais elle ajoute galement de nouvelles spcifications et apporte des profils et de llagage pour allger ce modle. La sortie de Java EE6 concide avec le dixime anniversaire de la plate-forme entreprise: elle combine donc les avantages du langage Java avec lexprience accumule au cours de ces dix dernires annes. En outre, elle tire profit du dynamisme de la communaut open-source et de la rigueur du JCP. Dsormais, Java EE est une plate-forme bien documente, avec des dveloppeurs expriments, une communaut dutilisateurs importante et de nombreuses applications dployes sur les serveurs dentreprises. Cest un ensemble dAPI permettant de construire des applications multi-tier reposant sur des composants logiciels standard; ces composants sont dploys dans diffrents conteneurs offrant un ensemble de services.
Un peu dhistoire

Dix ans permettent de se faire une ide de lvolution de JavaEE (voir Figure1.1), qui sest dabord appel J2EE. La premire version, J2EE1.2, a t initialement dveloppe par Sun et est apparue en 1999 sous la forme dune spcification contenant dix

Chapitre 1

Tour dhorizon de Java EE 6

Java Specification Requests (JSR). cette poque, on parlait beaucoup de CORBA: cest la raison pour laquelle J2EE1.2 a t cr en ayant lesprit la cration de systmes distribus. Les Enterprise Java Beans (EJB) introduits alors permettaient de manipuler des objets service distants avec ou sans tat et, ventuellement, des objets persistants (beans entits). Ils taient construits selon un modle distribu et transactionnel utilisant le protocole sous-jacent RMI-IIOP (Remote Method InvocationInternet Inter-ORB Protocol). La couche web utilisait les servlets et les JavaServer Pages (JSP) et les messages taient envoys via JMS.
Figure1.1 Historique de J2EE/Java EE.
Services web Robuste, volutif J2EE 1.3 J2EE 1.4 Facilit de dveloppement Java EE 5 Profil Web EoD Java EE 6

Application d'entreprise J2EE 1.2


Servlet JSP EJB JMS RMI/IIOP

lagage Conteneur intgrable JAX-RS Validation des beans Annotations Injection JPA WS-* JSF

Project JPE

EJB CMP JCA

Services web Gestion Dploiement

Profil web

Mai 1998

Dec 1999 10 specs

Sept 2001 13 specs

Nov 2003 20 specs

Mai 2006 23 specs

Q3 2009 28 specs

INFO CORBA est apparu vers 1988, prcisment parce que les systmes dentreprise commenaient tre distribus (Tuxedo, CICS, par exemple). Les EJB puis J2EE lui ont embot le pas, mais dix ans plus tard. Lorsque J2EE est apparu pour la premire fois, CORBA avait dj gagn son statut industriel, mais les socits commenaient utiliser des solutions "tout-Java" et lapproche neutre de CORBA vis--vis des langages de programmation est donc devenu redondante.

partir de J2EE 1.3, la spcification a t dveloppe par le Java Community Process (JCP) en rponse la JSR58. Le support des beans entit est devenu obligatoire et les EJB ont introduit les descripteurs de dploiement XML pour stocker les mtadonnes (qui taient jusqualors srialises dans un fichier avec EJB1.0). Cette version a galement rgl le problme du surcot induit par le passage des paramtres par valeur avec les interfaces distantes en introduisant les interfaces locales

Java EE 6 et GlassFish 3

et en passant les paramtres par rfrence. J2EE Connector Architecture (JCA) a t ajoute afin de connecter J2EE aux EIS (Enterprise Information Systems).
INFO JCP est une organisation ouverte cre en 1998 afin de dfinir les volutions de la plateforme Java. Lorsquil identifie le besoin dun nouveau composant ou dune nouvelle API, linitiateur (appel "leader de la spcification") cre une JSR et forme un groupe dexperts. Ce groupe, form de reprsentants de diverses socits ou organisations, ainsi que de personnes prives, est responsable du dveloppement de la JSR et doit dlivrer: 1) une spcification qui explique les dtails et dfinit les bases de la JSR; 2) une implmentation de rfrence (RI, Reference Implementation); et 3) un kit de test de compatibilit (TCK, Technology Compatibility Kit), cest--dire un ensemble de tests que devront satisfaire toutes les implmentations avant de pouvoir prtendre quelles sont conformes la spcification. Une fois quelle a t accepte par le comit excutif (EC, Executive Committee), la spcification est fournie la communaut pour tre implmente. En ralit, Java EE est une JSR qui chapeaute dautres JSR et cest la raison pour laquelle on parle souvent de JSR umbrella.

J2EE1.4 (JSR151), qui est sorti en 2003, a ajout vingt spcifications et le support des services web. EJB2.1 permettait ainsi dinvoquer des beans de session partir de SOAP/HTTP. Un service de temporisation a galement t ajout pour permettre aux EJB dtre appels des moments prcis ou intervalles donns. Cette version fournissait un meilleur support pour lassemblage et le dploiement des applications. Bien que ses supporters lui aient prdit un grand avenir, toutes les promesses de J2EE ne se sont pas ralises. Les systmes crs grce lui taient trop complexes et les temps de dveloppement, souvent sans commune mesure avec les exigences de lutilisateur. J2EE tait donc considr comme un modle lourd, difficile tester, dployer et excuter. Cest cette poque que des frameworks comme Struts, Spring ou Hibernate ont commenc merger et proposer une nouvelle approche dans le dveloppement des applications. Heureusement, Java EE5 (JSR244) fit son apparition au deuxime trimestre de 2006 et amliora considrablement la situation. Il sinspirait des frameworks open-source en revenant un bon vieil objet Java (POJO, Plain Old Java Object). Les mtadonnes pouvaient dsormais tre dfinies grce des annotations et les descripteurs XML devenaient facultatifs. Du point de vue des dveloppeurs, EJB3 et la nouvelle spcification JPA reprsentrent donc plus un bond prodigieux quune volution de la plate-forme. JSF (JavaServer Faces) fit galement son apparition comme framework standard de la couche prsentation et JAX-WS2.0 remplaa JAX-RPC comme API pour les services web SOAP.

Chapitre 1

Tour dhorizon de Java EE 6

Aujourdhui, Java EE6 (JSR316) poursuit sur cette voie en appliquant les concepts dannotation, de programmation POJO et la politique "convention plutt que configuration" toute la plate-forme, y compris la couche web. Il fournit galement un grand nombre dinnovations comme la toute nouvelle API JAX-RS1.1, simplifie des API matures comme EJB3.1 et en enrichit dautres comme JPA2.0 ou le service de temporisation. Les thmes principaux de Java EE6 sont la portabilit (en standardisant le nommage JNDI, par exemple), la dprciation de certaines spcifications (via llagage) et la cration de sous-ensembles de la plate-forme au moyen de profils. Dans ce livre, nous prsenterons toutes ces amliorations et montrerons comment Java Enterprise Edition est devenu la fois bien plus simple et bien plus riche.
Standards

Java EE repose sur des standards. Cest une spcification centrale qui chapeaute un certain nombre dautres JSR. Vous pourriez vous demander pourquoi les standards sont si importants puisque certains des frameworks Java les plus utiliss (Struts, Spring, etc.) ne sont pas standardiss. La raison est que les standards, depuis laube des temps, facilitent la communication et les changes des exemples de standards bien connus concernent les langues, la monnaie, le temps, les outils, les trains, les units de mesure, llectricit, le tlphone, les protocoles rseau et les langages de programmation. Quand Java est apparu, le dveloppement dune application web ou dentreprise passait gnralement par lutilisation doutils propritaires: on crait son propre framework ou lon senfermait en choisissant un framework commercial propritaire. Puis vint lpoque des frameworks open-source, qui ne reposent pas toujours sur des standards ouverts. Vous pouvez donc utiliser une solution open-source qui vous enferme dans une seule implmentation ou en choisir une qui implmente les standards et qui sera alors portable. Java EE fournit des standards ouverts implments par plusieurs frameworks commerciaux (WebLogic, Websphere, MQSeries, etc.) ou open-source (GlassFish, JBoss, Hibernate, Open JPA, Jersey, etc.) pour grer les transactions, la scurit, les objets tat, la persistance des objets, etc. Aujourdhui plus que jamais dans lhistoire de Java EE, votre application peut tre dploye sur nimporte quel serveur dapplications conforme, moyennant quelques modifications mineures.
Architecture

Java EE est un ensemble de spcifications implmentes par diffrents conteneurs. Ces conteneurs sont des environnements dexcution Java EE qui fournissent cer-

10

Java EE 6 et GlassFish 3

tains services aux composants quils hbergent: gestion du cycle de vie, injection de dpendances, etc. Les composants doivent respecter des contrats bien dfinis pour communiquer avec linfrastructure de JavaEE et avec les autres composants, et ils doivent tre assembls en respectant un certain standard (fichiers archives) avant dtre dploys. Java EE tant un surensemble de la plate-forme JavaSE, les API de cette dernire peuvent donc tre utilises par nimporte quel composant de JavaEE. La Figure1.2 prsente les relations logiques qui relient les conteneurs. Les flches reprsentent les protocoles utiliss par un conteneur pour accder un autre. Le conteneur web, par exemple, hberge les servlets qui peuvent accder aux EJB via le protocole RMI-IIOP.
Figure1.2 Conteneurs standard de Java EE.
<<executionEnvironment>> Conteneur web
<<component>> Servlet <<component>> JSF HTTP SSL RMI / IIOP

<<executionEnvironment>> Conteneur EJB


<<component>> EJB

HTTP SSL

RMI / IIOP RMI / IIOP

<<executionEnvironment>> Conteneur Applets


<<component>> Applet

Conteneur d'applications client


<<component>> Application

Composants

Lenvironnement dexcution de Java EE dfinit quatre types de composants que doivent supporter toutes les implmentations:

Les applets sont des applications graphiques excutes dans un navigateur web. Elles utilisent lAPI Swing pour fournir des interfaces utilisateurs puissantes. Les applications sont des programmes excuts sur un client. Il sagit le plus souvent dinterfaces graphiques ou de programmes non interactifs qui ont accs toutes les fonctionnalits de la couche mtier de JavaEE.

Chapitre 1

Tour dhorizon de Java EE 6

11

Les applications web (composes de servlets, de filtres de servlet, dcouteurs dvnements web, de pages JSP et de JSF) sexcutent dans un conteneur web et rpondent aux requtes HTTP envoyes par les clients web. Les servlets permettent galement de mettre en place des services web SOAP et REST. Les EJB (Enterprise Java Beans) sont des composants permettant de traiter la logique mtier en modle transactionnel. On peut y accder localement et distance via RMI (ou HTTP pour les services web SOAP et REST).

Conteneurs

Linfrastructure de Java EE est dcoupe en domaines logiques appels conteneurs (voir Figure1.2). Chacun deux joue un rle spcifique, supporte un ensemble dAPI et offre des services ses composants (scurit, accs aux bases de donnes, gestion des transactions, injection de ressources, etc.). Les conteneurs cachent les aspects techniques et amliorent la portabilit. Selon le type dapplication que vous voudrez construire, vous devrez comprendre les possibilits et les contraintes de chaque conteneur. Si, par exemple, vous devez dvelopper une couche de prsentation web, vous crirez une application JSF et la dploierez dans un conteneur web, non dans un conteneur EJB. Si, en revanche, vous voulez quune application web appelle une couche mtier, vous devrez srement utiliser la fois un conteneur web et un conteneur EJB. La plupart des navigateurs web fournissent des conteneurs dapplets pour excuter les composants applets. Lorsque vous dveloppez des applets, vous pouvez donc vous concentrer sur laspect visuel de lapplication puisque le conteneur vous fournit un environnement scuris grce un modle de protection appel "bac sable": le code qui sexcute dans ce bac sable nest pas autoris en sortir, ce qui signifie que le conteneur empchera un code tlcharg sur votre machine locale daccder aux ressources de votre systme, comme les processus ou les fichiers. Le conteneur dapplications client (ACC, application client container) contient un ensemble de classes et de bibliothques Java ainsi que dautres fichiers afin dajouter linjection, la gestion de la scurit et le service de nommage aux applications JavaSE (applications Swing, traitements non interactifs ou, simplement, une classe avec une mthode main()). ACC communique avec le conteneur EJB en utilisant le protocole RMI-IIOP et avec le conteneur web via le protocole HTTP (pour les services web, notamment).

12

Java EE 6 et GlassFish 3

Le conteneur web (ou conteneur de servlets) fournit les services sous-jacents permettant de grer et dexcuter les composants web (servlets, JSP, filtres, couteurs, pages JSF et services web). Il est responsable de linstanciation, de linitialisation et de lappel des servlets et du support des protocoles HTTP et HTTPS. Cest lui quon utilise pour servir les pages web aux navigateurs des clients. Le conteneur EJB est responsable de la gestion de lexcution des beans entreprise contenant la couche mtier de votre application JavaEE. Il cre de nouvelles instances des EJB, gre leur cycle de vie et fournit des services comme les transactions, la scurit, la concurrence, la distribution, le nommage ou les appels asynchrones.
Services

Les conteneurs fournissent les services sous-jacents leurs composants. En tant que dveloppeur, vous pouvez donc vous concentrer sur limplmentation de la logique mtier au lieu de rsoudre les problmes techniques auxquels sont exposes les applications dentreprise. La Figure1.3 montre les services fournis par chaque conteneur.
Conteneur web JSP JSF Servlet JSF JPA JTA Connecteurs JMS Gestion Mtadonnes WS Services web RMI/IIOP Conteneur EJB EJB JavaMail JPA JTA Connecteurs JMS Gestion Mtadonnes WS Services web

JACC JASP C JAXR JAX-RS JAX-WS JAX-RPC

Figure1.3

Services fournis par les conteneurs.

JACC JASP C JAXR JAX-RS JAX-WS JAX-RPC SAAJ HTTP SSL Conteneur Applets Applet Java SE

JavaMail JSTL

SAAJ

Java SE HTTP SSL Conteneur d'applications client Application cliente JPA Gestion Mtadonnes WS Services web JMS JAXR JAX-WS JAX-RPC SAAJ Java SE

Java SE RMI/IIOP

JDBC JDBC Base de donnes

Chapitre 1

Tour dhorizon de Java EE 6

13

Les conteneurs web et EJB, par exemple, fournissent des connecteurs pour accder EIS, alors que le conteneur dapplet ou ACC ne le font pas. JavaEE offre les services suivants:

JTA. Ce service offre une API de dmarcation des transactions utilise par le conteneur et lapplication. Il sert galement dinterface entre le gestionnaire de transactions et un gestionnaire de ressource au niveau SPI (Service Provider Interface). JPA. Fournit lAPI pour la correspondance modle objet-modle relationnel (ORM, Object-Relational Mapping). JPQL (Java Persistence Query Language) permet dinterroger les objets stocks dans la base de donnes sous-jacente. JMS. Permet aux composants de communiquer de faon asynchrone par passage de messages. Ce service fournit un systme denvoi de message fiable, point point (P2P) ainsi que le modle publication-abonnement. JNDI (Java Naming and Directory Interface). Cette API, incluse dans JavaSE, permet daccder aux systmes dannuaires et de nommage. Votre application peut lutiliser pour associer des noms des objets puis les retrouver dans un annuaire. Avec elle, vous pouvez parcourir des sources de donnes, des fabriques JMS, des EJB et dautres ressources. Omniprsente dans le code jusqu J2EE1.4, JNDI est dsormais utilise de faon plus transparente grce linjection. JavaMail. Le but de cette API consiste simplifier lenvoi de courrier lectronique par les applications. JAF (JavaBeans Activation Framework). Cette API, incluse dans JavaSE, est un framework pour la gestion des donnes des diffrents types MIME. Elle est utilise par JavaMail. XML. La plupart des composants Java EE peuvent ventuellement tre dploys laide de descripteurs de dploiements XML, et les applications doivent souvent manipuler des documents XML. JAXP (Java API for XML Processing) permet danalyser des documents XML laide des API SAX et DOM, ainsi que pour XSLT. StAX (Streaming API for XML) est une API danalyse XML par flux. JCA. Les connecteurs permettent daccder EIS partir dun composant JavaEE, que ce soient des bases de donnes, des mainframes ou des programmes ERP (Enterprise Resource Planning). Scurit. JAAS (Java Authentication and Authorization Service) fournit les services permettant dauthentifier les utilisateurs et dassurer le contrle de leurs

14

Java EE 6 et GlassFish 3

droits daccs. JACC (Java Authorization Service Provider Contract for Containers) dfinit un contrat entre un serveur dapplication Java EE et un fournisseur de service dautorisation, ce qui permet de greffer des fournisseurs de services dautorisation personnaliss dans nimporte quel produit JavaEE.

Services web. Java EE reconnat les services web SOAP et REST. JAX-WS (Java API for XML Web Services) remplace JAX-RPC (Java API for XML-based RPC) et fournit le support des protocoles SOAP et HTTP. JAX-RS (Java API for RESTful Web Services) fournit le support des services web reposant sur REST. Gestion. Java EE dfinit des API pour grer les conteneurs et les serveurs laide dun bean dentreprise spcialis. LAPI JMX (Java Management Extensions) fournit galement une aide la gestion. Dploiement. La spcification de dploiement Java EE dfinit un contrat entre les outils de dploiement et les produits JavaEE afin de standardiser le dploiement des applications.

Protocoles rseau

Comme le montre la Figure1.3 (voir la section "Platform Overview" de la spcification Java EE6), les composants dploys dans les conteneurs peuvent tre invoqus via diffrents protocoles. Une servlet dploye dans un conteneur web, par exemple, peut tre appele avec HTTP ou comme un service web avec un point terminal EJB dploy dans un conteneur EJB. Voici la liste des protocoles reconnus par JavaEE:

HTTP. HTTP est le protocole du Web, omniprsent dans les applications modernes. LAPI ct client est dfinie par le paquetage java.net de JavaSE. LAPI ct serveur de HTTP est dfinie par les servlets, les JSP et les interfaces JSF, ainsi que par les services web SOAP et REST. HTTPS est une combinaison de HTTP et du protocole SSL (Secure Sockets Layer). RMI-IIOP. RMI (Remote Method Invocation) permet dappeler des objets distants indpendamment du protocole sous-jacent avec JavaSE, le protocole natif est JRMP (Java Remote Method Protocol). RMI-IIOP est une extension de RMI permettant de lintgrer CORBA. IDL (Java interface description language) permet aux composants des applications JavaEE dinvoquer des objets CORBA externes laide du protocole IIOP. Les objets CORBA peuvent avoir t crits dans diffrents langages (Ada, C, C++, Cobol, etc.) et, bien sr, en Java.

Chapitre 1

Tour dhorizon de Java EE 6

15

Paquetages

Pour tre dploys dans un conteneur, les composants doivent dabord tre empaquets dans une archive au format standard. JavaSE dfinit les fichiers jar (Java Archive), qui permettent de regrouper plusieurs fichiers (classes Java, descripteurs de dploiement ou bibliothques externes) dans un seul fichier compress (reposant sur le format ZIP). JavaEE dfinit diffrents types de modules ayant leur propre format de paquetage reposant sur ce format jar commun. Un module dapplication client contient des classes Java et dautres fichiers de ressources empaquets dans un fichier jar. Ce fichier peut tre excut dans un environnement JavaSE ou dans un conteneur dapplication client. Comme tous les autres formats darchive, le fichier jar contient ventuellement un rpertoire META-INF dcrivant larchive. Le fichier META-INF/MANIFEST.MF, notamment, permet de dcrire les donnes du paquetage. Sil est dploy dans un ACC, le descripteur de dploiement peut ventuellement se trouver dans le fichier META-INF/application-client.xml. Un module EJB contient un ou plusieurs beans de session et/ou des beans pilots par des messages (MDB, Message Driven Bean) assembls dans un fichier jar (souvent appel fichier jar EJB). Ce fichier peut ventuellement contenir un descripteur de dploiement META-INF/ejb-jar.xml et ne peut tre dploy que dans un conteneur EJB. Un module dapplication web contient des servlets, des JSP, des pages JSF, des services web ainsi que tout autre fichier web associ (pages HTML et XHTML, feuilles de style CSS, scripts JavaScript, images, vidos, etc.). Depuis Java EE6, un module dapplication web peut galement contenir des beans EJB Lite (un sousensemble de lAPI des EJB que nous dcrirons au Chapitre6). Tous ces composants sont assembls dans un fichier jar portant lextension .war (souvent dsign sous le terme de fichier war, ou Web Archive). Lventuel descripteur de dploiement est dfini dans le fichier WEB-INF/web.xml. Si le fichier war contient des beans EJB Lite, larchive peut galement contenir un descripteur de dploiement dcrit par WEBINF/ejb-jar.xml. Les fichiers .class Java se trouvent dans le rpertoire WEB-INF/ classes et les fichiers jar dpendants sont dans le rpertoire WEB-INF/lib. Un module entreprise peut contenir zro ou plusieurs modules dapplications web, zro ou plusieurs modules EJB et dautres bibliothques classiques ou externes. Toutes ces composantes sont assembles dans une archive entreprise (un fichier jar portant lextension .ear) afin que le dploiement de ces diffrents modules se fasse simultanment et de faon cohrente. Le descripteur de dploiement facultatif dun module entreprise est dfini dans le fichier META-INF/application.xml. Le rpertoire spcial lib sert partager les bibliothques entre les modules.

16

Java EE 6 et GlassFish 3

Java Standard Edition

Il est important de bien comprendre que Java EE est un surensemble de JavaSE (Java Standard Edition). Ceci signifie donc que toutes les fonctionnalits du langage Java et toutes ses API sont galement disponibles dans JavaEE. Java SE6 est apparu officiellement le 11dcembre 2006. Cette version a t dveloppe sous le contrle de la JSR270; elle apporte de nombreuses fonctionnalits supplmentaires et poursuit leffort de simplification de la programmation initi par Java SE5 (autoboxing, annotations, gnricit, numrations, etc.). Java SE6 fournit de nouveaux outils de diagnostic, de gestion et de surveillance des applications; il amliore lAPI JMX et simplifie lexcution des langages de scripts dans la machine virtuelle Java (JVM, Java Virtual Machine). Le but de cet ouvrage nest pas de prsenter Java SE6: consultez labondante documentation disponible sur le langage si vous pensez ne pas assez le matriser. Un bon point de dpart est le livre de Dirk Louis et Peter Mller, Java SE6 (Pearson, 2007).

Spcifications de Java EE 6
Java EE 6 est une spcification centrale dfinie par la JSR316 qui contient vingthuit autres spcifications. Un serveur dapplication souhaitant tre compatible avec Java EE6 doit donc implmenter toutes ces spcifications. Les Tableaux1.1 1.5 numrent toutes leurs versions et leurs numros de JSR. Certaines spcifications ont t lagues, ce qui signifie quelles seront peut-tre supprimes de Java EE7.
Tableau1.1: Spcification de Java Enterprise Edition

Spcification Java EE

Version 6.0

JSR 316

URL http://jcp.org/en/jsr/detail?id=316

Tableau1.2: Spcifications des services web

Spcification JAX-RPC JAX-WS

Version 1.1 2.2

JSR 101 224

URL http://jcp.org/en/jsr/detail?id=101 http://jcp.org/en/jsr/detail?id=224

lague X

Chapitre 1

Tour dhorizon de Java EE 6

17

Tableau1.2: Spcifications des services web (suite)

Spcification JAXB JAXM StAX Services web Mtadonnes des services web JAX-RS JAXR

Version 2.2 1.0 1.0 1.2 1.1 1.0 1.1

JSR 222 67 173 109 181 311 93

URL http://jcp.org/en/jsr/detail?id=222 http://jcp.org/en/jsr/detail?id=67 http://jcp.org/en/jsr/detail?id=173 http://jcp.org/en/jsr/detail?id=109 http://jcp.org/en/jsr/detail?id=181 http://jcp.org/en/jsr/detail?id=311 http://jcp.org/en/jsr/detail?id=93

lague

Tableau1.3: Spcifications web

Spcification JSF JSP

Version JSR 2.0 2.2 314 245 52

URL http://jcp.org/en/jsr/detail?id=314 http://jcp.org/en/jsr/detail?id=245 http://jcp.org/en/jsr/detail?id=52

lague

JSTL (JavaServer 1.2 Pages Standard Tag Library) Servlet 3.0 Expression Language 1.2

315 245

http://jcp.org/en/jsr/detail?id=315 http://jcp.org/en/jsr/detail?id=245

Tableau1.4: Spcification Entreprise

Spcification EJB JAF JavaMail JCA JMS JPA JTA

Version JSR 3.1 1.1 1.4 1.6 1.1 2.0 1.1 318 925 919 322 914 317 907

URL http://jcp.org/en/jsr/detail?id=318 http://jcp.org/en/jsr/detail?id=925 http://jcp.org/en/jsr/detail?id=919 http://jcp.org/en/jsr/detail?id=322 http://jcp.org/en/jsr/detail?id=914 http://jcp.org/en/jsr/detail?id=317 http://jcp.org/en/jsr/detail?id=907

lague

18

Java EE 6 et GlassFish 3

Tableau1.5: Gestion, scurit et autres spcifications

Spcification JACC Validation Bean Annotations communes Dploiement dapplications Java EE Gestion Java EE

Version JSR 1.1 1.0 1.0 1.2 115 303 250 88

URL http://jcp.org/en/jsr/detail?id=115 http://jcp.org/en/jsr/detail?id=303 http://jcp.org/en/jsr/detail?id=250 http://jcp.org/en/jsr/detail?id=88

lague

1.1

77 196

http://jcp.org/en/jsr/detail?id=77 http://jcp.org/en/jsr/detail?id=196

Interface de 1.0 fournisseur de service dauthentification pour les conteneurs Support du dbogage 1.0 pour les autres langages

45

http://jcp.org/en/jsr/detail?id=45

Nouveauts de Java EE6


Maintenant que vous connaissez larchitecture interne de Java EE, vous pourriez vous demander ce quapporte Java EE6. Le but principal de cette version est de poursuivre la simplification de la programmation introduite par Java EE 5. Avec Java EE5, les EJB, les entits persistantes et les services web ont t remodels afin dutiliser une approche plus oriente objet (via des classes Java implmentant des interfaces Java) et pour se servir des annotations pour dfinir les mtadonnes (les descripteurs de dploiement XML sont donc devenus facultatifs). Java EE6 poursuit dans cette voie et applique les mmes paradigmes la couche web. Aujourdhui, un bean gr par JSF est une classe Java annote avec un descripteur XML. Java EE 6 sapplique galement simplifier la plate-forme laide de profils et en supprimant certaines technologies obsoltes. Il ajoute des fonctionnalits supplmentaires aux spcifications existantes (en standardisant, par exemple, les beans de session singletons) et en ajoute de nouvelles (comme JAX-RS). Plus que jamais, les applications Java EE6 sont portables entre les conteneurs grce aux noms JNDI standard et un conteneur EJB intgr.

Chapitre 1

Tour dhorizon de Java EE 6

19

Plus lger

Le groupe dexperts pour Java EE 6 a relev un dfi intressant: comment allger la plate-forme tout en lui ajoutant des spcifications supplmentaires? Aujourdhui, un serveur dapplications doit implmenter vingt-huit spcifications pour tre conforme Java EE 6. Un dveloppeur doit donc connatre des milliers dAPI, certaines ntant mme plus pertinentes puisquelles ont t marques comme lagues. Pour rendre la plate-forme plus lgre, le groupe dexperts a donc introduit les profils, llagage et EJB Lite (un sous-ensemble des fonctionnalits compltes dEJB uniquement destin aux interfaces locales, aux transactions et la scurit). Nous tudierons EJB Lite plus en dtail au Chapitre6.
lagage

La premire version de Java EE est apparue en 1999 et, depuis, chaque nouvelle version a ajout son lot de nouvelles spcifications (comme on la vu la Figure1.1). Cette inflation est devenue un problme en termes de taille, dimplmentation et dapprentissage. Certaines fonctionnalits ntaient pas trs bien supportes ou peu dployes parce quelles taient techniquement dpasses ou que dautres solutions avaient vu le jour entre-temps. Le groupe dexperts a donc dcid de proposer la suppression de certaines fonctionnalits via llagage (pruning). Java EE 6 a adopt ce mcanisme dlagage (galement appel "marquage pour suppression"), suivant en cela le groupe JavaSE. Ce mcanisme consiste proposer une liste de fonctionnalits qui pourraient ne plus tre reconduites dans Java EE7. Aucun de ces lments nest supprim dans la version courante. Certaines fonctionnalits seront remplaces par des spcifications plus rcentes (les beans entits, par exemple, sont remplacs par JPA) et dautres quitteront simplement la spcification Java EE7 pour continuer dvoluer comme des JSR indpendantes (les JSR88 et77, par exemple). Ceci dit, les fonctionnalits lagues suivantes sont toujours prsentes dans Java EE6:

EJB 2.x Entity Beans CMP (partie de la JSR 318). Ce modle de composants persistants complexe et lourd des beans entits dEJB 2.x a t remplac par JPA. JAX-RPC (JSR 101). Il sagissait de la premire tentative de modliser les services web SOAP comme des appels RPC. Cette spcification a dsormais t remplace par JAX-WS, qui est bien plus simple utiliser et plus robuste. JAXR (JSR 93). JAXR est lAPI ddie aux communications avec les registres UDDI. Ce dernier tant peu utilis, JAXR devrait quitter JavaEE et continuer dvoluer comme une JSR distincte.

20

Java EE 6 et GlassFish 3

Java EE Application Deployment (JSR 88). La JSR 88 est une spcification que les dveloppeurs doutils peuvent utiliser pour le dploiement sur les serveurs dapplications. Cette API nayant pas reu beaucoup de soutien de la part des diteurs, elle devrait quitter JavaEE et continuer dvoluer comme une JSR distincte. Java EE Management (JSR 77). Comme la JSR88, la JSR77 tait une tentative de crer des outils de gestion des serveurs dapplications.

Profils

Les profils sont une innovation majeure de lenvironnement Java EE 6. Leur but principal consiste rduire la taille de la plate-forme pour quelle convienne mieux aux besoins du dveloppeur. Quelles que soient la taille et la complexit de lapplication que vous dveloppez aujourdhui, vous la dploierez sur un serveur dapplications qui vous offre les API et les services de vingt-huit spcifications. Lune des principales critiques adresses JavaEE est quil tait trop lourd: les profils ont donc t conus pour rgler ce problme. Comme le montre la Figure1.4, les profils sont des sous-ensembles ou des surensembles de la plate-forme et peuvent chevaucher cette dernire ou dautres profils.
Figure1.4 Profils de la plate-forme Java EE.
Java EE 6 complet Profil X Profil web

Profil Y

Java EE 6 dfinit un seul profil: le profil web. Son but consiste permettre au dveloppeur de crer des applications web avec lensemble de technologies appropri. Web Profile 1.0 est spcifi dans une JSR distincte ; cest le premier profil de la plate-forme Java EE6. Dautres seront crs dans le futur (on pourrait penser un profil minimal ou un profil de portail). Le profil web, quant lui, voluera son rythme et nous pourrions disposer dune version1.1 ou1.2 avant la sortie de Java EE7. Nous verrons galement apparatre des serveurs dapplications compatibles Web Profile1.0 et non plus compatibles Java EE6. Le Tableau1.6 numre les spcifications contenues dans ce profil web.

Chapitre 1

Tour dhorizon de Java EE 6

21

Tableau1.6: Spcifications de Web Profile 1.0

Spcification JSF JSP JSTL Servlet Expression Language EJB Lite JPA JTA Annotations communes

Version 2.0 2.2 1.2 3.0 1.2 3.1 2.0 1.1 1.0

JSR 314 245 52 315

URL http://jcp.org/en/jsr/detail?id=314 http://jcp.org/en/jsr/detail?id=245 http://jcp.org/en/jsr/detail?id=52 http://jcp.org/en/jsr/detail?id=315

318 317 907 250

http://jcp.org/en/jsr/detail?id=318 http://jcp.org/en/jsr/detail?id=317 http://jcp.org/en/jsr/detail?id=907 http://jcp.org/en/jsr/detail?id=250

Plus simple dutilisation

Outre lallgement de la plate-forme, un autre but de Java EE6 tait galement de le rendre plus simple dutilisation. Le choix de cette version a t dappliquer ce paradigme la couche web. Les composants de Java EE ont besoin de mtadonnes pour informer le conteneur de leur comportement avant Java EE5, la seule solution tait dutiliser un fichier descripteur de dploiement XML. Avec lapparition des annotations dans les EJB, les entits et les services web, il est devenu plus simple dassembler et de dployer les composants puisquil y a moins de XML crire. Java EE5 a donc modifi larchitecture de la couche entreprise et les composants sont passs un modle POJO et aux interfaces; la couche web, en revanche, ne bnficiait pas encore de ces amliorations. Avec Java EE 6, les servlets, les beans grs par JSF, les convertisseurs JSF, les validateurs et les moteurs de rendus sont galement des classes annotes pouvant ventuellement tre assorties de descripteurs de dploiement en XML. Le Listing 1.1 montre le code dun bean gr par JSF : vous constaterez que ce nest, en fait, quune classe Java avec une seule annotation. Si vous connaissez dj JSF, vous apprendrez avec plaisir que, dans la plupart des cas, le fichier faces-config.xml est devenu facultatif (si vous ne connaissez pas JSF, vous le dcouvrirez aux Chapitres10, 11 et 12).

22

Java EE 6 et GlassFish 3

Listing1.1: Un bean gr par JSF


@ManagedBean public class BookController { @EJB private BookEJB bookEJB; private Book book = new Book(); private List<Book> bookList = new ArrayList<Book>(); public String doCreateBook() { book = bookEJB.createBook(book); bookList = bookEJB.findBooks(); return "listBooks.xhtml"; } } // Getters, setters

Les EJB sont galement plus simples dvelopper en Java EE6. Comme le montre le Listing1.2, une simple classe annote sans interface suffit dsormais pour accder localement un EJB. Les EJB peuvent galement tre dploys directement dans un fichier war sans avoir t au pralable assembls dans un fichier jar. Toutes ces amliorations font des EJB les composants transactionnels les plus simples, qui peuvent servir aussi bien des applications web minimales qu des applications dentreprise complexes.
Listing1.2: EJB sans tat
@Stateless public class bookEJB { @PersistenceContext(unitName = "chapter01PU") private EntityManager em; public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; }

Plus riche

Dun ct, Java EE 6 sest allg en introduisant les profils ; de lautre, il sest enrichi en ajoutant de nouvelles spcifications et en amliorant celles qui existaient

Chapitre 1

Tour dhorizon de Java EE 6

23

dj. Les services web REST ont fait leur chemin dans les applications modernes et Java EE6 suit donc les besoins des entreprises en ajoutant la nouvelle spcification JAX-RS. Comme le montre le Listing1.3, un service web REST est une classe annote qui rpond des actions HTTP. Vous en apprendrez plus sur JAX-RS au Chapitre15.
Listing1.3: Service web REST
@Path("books") public class BookResource { @PersistenceContext(unitName = "chapter01PU") private EntityManager em; @GET @Produces({"application/xml", "application/json"}) public List<Book> getAllBooks() { Query query = em.createNamedQuery("findAllBooks"); List<Book> books = query.getResultList(); return books; } }

La nouvelle version de lAPI de persistance (JPA 2.0) a t amliore par lajout de collections de types de donnes simples (String, Integer, etc.), dun verrouillage pessimiste, dune syntaxe JPQL plus riche, dune toute nouvelle API de dfinition de requtes et par le support de la mise en cache. JPA est dcrite aux Chapitres2 5 de cet ouvrage. Les EJB sont plus faciles crire (avec des interfaces ventuelles) et assembler (dans un fichier war) et disposent galement de nouvelles fonctionnalits, comme les appels asynchrones ou un service de temporisation plus labor pour planifier les tches. Un nouveau composant bean de session singleton fait galement son apparition. Comme le montre le Listing1.4, une simple annotation suffit transformer une classe Java en singleton gr par un conteneur (une seule instance du composant par application). Les Chapitres 6 9 vous en apprendront plus sur ces nouvelles fonctionnalits.
Listing1.4: Bean de session singleton
@Singleton public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); public void addToCache(Long id, Object object) { if (!cache.containsKey(id))

24

Java EE 6 et GlassFish 3

cache.put(id, object); } public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

La couche prsentation sest galement enrichie. JSF 2.0 ajoute en effet le support dAjax et des Facelets (voir Chapitres10 12).
Plus portable

Depuis sa cration, le but de Java EE est de permettre le dveloppement et le dploiement des applications sur nimporte quel serveur dapplications, sans modifier son code ou les fichiers de configuration. En ralit, ce nest pas aussi simple que cela puisse paratre: les spcifications ne couvrent pas tous les dtails et les implmentations finissent par offrir des solutions non portables. Cest ce qui sest pass pour les noms JNDI, par exemple: lorsque lon dployait un EJB sur GlassFish, JBoss ou WebLogic, le nom JNDI tait diffrent parce quil ne faisait pas partie de la spcification et il fallait donc modifier le code en fonction du serveur dapplications utilis. Ce problme prcis est dsormais corrig car Java EE6 spcifie une syntaxe prcise des noms JNDI qui est la mme sur tous les serveurs dapplications (voir Chapitre7). Une autre difficult avec les EJB consiste pouvoir les tester ou les utiliser dans un environnement JavaSE. Certains serveurs dapplication (comme JBoss) utilisent pour cela leurs propres implmentations. EJB3.1 fournit dsormais une API standard pour lexcution des EJB dans un environnement JavaSE (voir Chapitre7).

Lapplication CD-Bookstore
Tout au long de ce livre, nous prsenterons des extraits de code qui manipulent des entits, des EJB, des pages JSF, des couteurs JMS et des services web SOAP ou REST. Tous ces extraits proviennent de lapplication CD-Bookstore, un site web de commerce en ligne permettant de parcourir un catalogue de livres et de CD pour les acheter. Lapplication interagit avec un systme bancaire pour valider les cartes de

Chapitre 1

Tour dhorizon de Java EE 6

25

crdit. Le diagramme des cas dutilisation prsent la Figure1.5 dcrit les acteurs et les fonctionnalits de ce systme.
Figure1.5 Diagramme des cas dutilisation de lapplication CD-Bookstore.
CD BookStore Cration d'un compte Parcours du catalogue Recherche d'un article Connexion et dconnexion Parcours des commandes Modification du compte
<<Extend>>

Gestion du catalogue des articles

Utilisateur

Gestion des clients

Employ

Client

Cration de commande Validation de carte bancaire

Achat d'articles

<<Include>>

Banque

Les acteurs qui interagissent avec le systme dcrit la Figure1.5 sont les suivants:

Les employs de la socit, qui doivent grer la fois le catalogue des articles et les informations sur les clients. Ils peuvent galement parcourir les commandes. Les utilisateurs, qui sont les visiteurs anonymes du site qui consultent le catalogue des livres et des CD. Pour acheter un article, ils doivent crer un compte afin de devenir clients. Les clients, qui peuvent parcourir le catalogue, modifier les informations de leur compte et acheter des articles en ligne. La banque externe, laquelle le systme dlgue la validation des cartes de crdit.
INFO

Le code des exemples de ce livre est disponible sur le site web des ditions Pearson (http:// www.pearson.fr), sur la page consacre cet ouvrage.

26

Java EE 6 et GlassFish 3

Configuration de lenvironnement de travail


Ce livre contient de nombreux extraits de code et la plupart des chapitres se terminent par une section "Rcapitulatif". Ces sections expliquent pas pas comment dvelopper, compiler, dployer, excuter et tester unitairement un composant. Pour cela, vous aurez besoin des logiciels suivants:

JDK 1.6; Maven 2; Junit 4; la base de donnes Derby 10.5 (alias JavaDB); le serveur dapplication GlassFish v3.

JDK 1.6

Le JDK (Java Development Kit) est essentiel au dveloppement et lexcution des exemples de ce livre. Il comprend un certain nombre doutils, notamment un compilateur (javac), une machine virtuelle (java), un gnrateur de documentation (javadoc), des outils de gestion (Visual VM), etc. Pour linstaller, rendez-vous sur le site officiel de Sun (http://java.sun.com/javase/downloads), choisissez votre plate-forme et votre langue, puis tlchargez la distribution approprie. Si vous travaillez sous Windows (ce livre ne traite pas des systmes Linux et OSX), double-cliquez sur le fichier jdk-6u18-windows-i586-p.exe. Le premier cran vous demandera daccepter la licence du logiciel et le second, prsent la Figure1.6, numrera les modules du JDK que vous pouvez installer (JDK, JRE, base de donnes Derby, sources). Une fois linstallation termine, il faut initialiser la variable JAVA_HOME avec le rpertoire o vous avez choisi dinstaller le JDK (C:\Program Files\Java\jdk1.6.0_18\ par dfaut) puis ajouter le rpertoire %JAVA_HOME%\bin la variable PATH. Pour vrifier que Java est bien reconnu par votre systme, tapez la commande java -version (voir Figure1.7).

Chapitre 1

Tour dhorizon de Java EE 6

27

Figure1.6 Configuration de linstallation du JDK.

Figure1.7 Affichage de la version du JDK.

Maven 2

Afin de reflter ce que vous trouverez sur le terrain, nous avons dcid dutiliser Maven (http://maven.apache.org) pour construire les exemples de ce livre, bien que sa description complte sorte du cadre de cet ouvrage (vous trouverez de trs nombreuses ressources consacres cet outil sur Internet ou dans les librairies). Cependant, nous introduirons quelques lments que vous devez connatre pour comprendre et utiliser nos exemples.
Historique

La construction dune application Java EE exige plusieurs oprations:


gnration du code et des ressources; compilation des classes Java et des classes de test; assemblage du code dans une archive (jar, ear, war, etc.) avec, ventuellement, des bibliothques jar externes.

28

Java EE 6 et GlassFish 3

Effectuer ces tches manuellement prend du temps et risque de produire des erreurs. Les quipes de dveloppement ont donc recherch des moyens dautomatiser tout ce processus. En 2000, les dveloppeurs Java commencrent utiliser Ant (http://ant.apache. org), qui leur permettait dcrire des scripts de construction de leurs applications. Ant est lui-mme crit en Java et offre un grand nombre de commandes qui, linstar de loutil make dUnix, sont portables entre les diffrentes plates-formes. Les quipes de dveloppement commencrent donc crer leurs propres scripts en fonction de leurs besoins. Cependant, Ant atteignit ses limites lorsque les projets commencrent impliquer des systmes htrognes complexes. Les socits avaient du mal industrialiser leur systme de construction de leurs applications. Il nexistait pas vritablement doutil permettant de rutiliser simplement un script de construction dun projet lautre (le copier/coller tait la seule mthode pour y parvenir). En 2002, la fondation Apache a mis disposition Maven, qui non seulement rsolvait tous ces problmes mais allait galement bien au-del dun simple outil de construction. Maven offre aux projets une solution pour les construire, des bibliothques partages et une plate-forme volutive au moyen dextensions, permettant ainsi dassurer la qualit, de produire la documentation, de grer les quipes de travail, etc. Fond sur le principe de "convention plutt que configuration", Maven introduit une description de projet standard et un certain nombre de conventions, notamment une structure de rpertoires standardise (voir Figure 1.8). Avec son architecture extensible reposant sur des extensions (appeles mojos), Maven offre de nombreux services.
Figure1.8 Structure de rpertoires standard de Maven.

Chapitre 1

Tour dhorizon de Java EE 6

29

Descripteur de projet

Maven repose sur le fait quune grande majorit de projets Java et Java EE ont des besoins similaires lors de la construction des applications. Un projet Maven doit respecter des standards et dfinir des fonctionnalits spcifiques dans un descripteur de projet ou POM (Project Object Model). Ce POM est un fichier XML (pom.xml) situ la racine du projet. Comme le montre le Listing1.5, linformation minimale permettant de dfinir lidentit dun projet est le groupId, lartifactId, la version et le type de paquetage.
Listing1.5: pom.xml minimal
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter01</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> </project>

Un projet est souvent divis en diffrents artfacts qui sont alors regroups sous le mme groupId (comme les paquetages en Java) et identifis de faon unique par lartifactId. Le marqueur packaging permet Maven de produire lartfact dans un format standard (jar, war, ear, etc.). Enfin, version identifie un artfact au cours de son volution (version1.1, 1.2, 1.2.1, etc.). Maven impose cette numrotation des versions pour quune quipe puisse grer lvolution du dveloppement de son projet. Maven introduit galement le concept de versions SNAPSHOT (le numro de version se termine alors par la chane -SNAPSHOT) pour identifier un artfact en cours de dveloppement. Le POM dfinit bien plus dinformations sur vos projets. Certaines sont purement descriptives (nom, description, etc.), dautres concernent lexcution de lapplication, comme la liste des bibliothques externes quelle utilise, etc. Enfin, le fichier pom.xml prcise lenvironnement de construction du projet (outils de contrle de versions, serveur dintgration, dpts dartfacts) et tout autre processus spcifique ncessaire la construction du projet.
Gestion des artfacts

Maven ne se contente pas de construire des artfacts: il permet galement de les archiver et de les partager. Pour ce faire, il utilise un dpt local sur le disque dur

30

Java EE 6 et GlassFish 3

(%USER_HOME%/ .m2/repository par dfaut) o il stocke tous les artfacts manipuls par les descripteurs du projet. Ce dpt local (voir Figure1.9) est rempli soit par les artfacts locaux du dveloppeur (monProjet-1.1.jar, par exemple) soit par des artfacts externes (glassfish-3.0.jar, par exemple) que Maven tlcharge partir de dpts distants. Par dfaut, Maven utilise le dpt principal situ lURL http:// repo1.maven.org/maven2 pour tlcharger les artfacts manquants.
Figure1.9 Exemple de dpt local.

Comme le montre le Listing1.6, un projet Maven dclare ses dpendances dans le POM (groupId, artifactId, version, type). Si ncessaire, Maven les tlchargera dans le dpt local partir de dpts distants. En outre, grce aux descripteurs POM de ces artfacts externes, Maven tlchargera galement les artfacts dont ils dpendent, etc. Lquipe de dveloppement na donc pas besoin de grer manuellement les dpendances des projets: toutes les bibliothques ncessaires sont automatiquement ajoutes par Maven.
Listing1.6: Dpendances dans le fichier pom.xml
... <dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1.0</version> <scope>provided</scope>

Chapitre 1

Tour dhorizon de Java EE 6

31

</dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> </dependencies> ...

Les dpendances peuvent avoir une visibilit limite (dsigne par scope):
test.

La bibliothque sert compiler et excuter les classes de test mais nest pas assemble dans lartfact produit.

provided. compile. runtime.

La bibliothque est fournie par lenvironnement (fournisseur de persistance, serveur dapplication, etc.) et ne sert qu compiler le code. La bibliothque est ncessaire la compilation et lexcution. La bibliothque nest requise que pour lexcution mais est exclue de la compilation (composants JSF ou bibliothques de marqueurs JSTL, par exemple).

Modularit des projets

Pour rsoudre le problme de la modularit des projets, Maven fournit un mcanisme reposant sur des modules, chaque module tant lui-mme un projet. Maven peut ainsi construire un projet compos de plusieurs modules en calculant les dpendances entre eux (voir Figure 1.10). Pour faciliter la rutilisation des paramtres classiques, les descripteurs POM peuvent hriter des POM des projets parents.
Figure1.10 Un projet et ses modules.

32

Java EE 6 et GlassFish 3

Extensions et cycle de vie

Maven utilise un cycle de vie en plusieurs phases (voir Figure1.11): nettoyage des ressources, validation du projet, production des sources ncessaires, compilation des classes Java, excution des classes de test, assemblage du projet et installation de celui-ci dans le dpt local. Ce cycle de vie constitue une ossature sur laquelle viennent sajouter les extensions Maven (alias mojos). Ces mojos dpendent du type de projet (un mojo pour compiler, un autre pour tester, un autre pour construire, etc.). Dans la description du projet, vous pouvez lier de nouvelles extensions une phase du cycle de vie, modifier la configuration dune extension, etc. Lorsque vous construisez un client dun service web, par exemple, vous pouvez ajouter un mojo qui produit les artfacts du service web au cours de la phase de production des sources.
Figure1.11 Cycle de vie dun projet.

Installation

Les exemples de ce livre ont t dvelopps avec Maven 2.2.1. Aprs avoir install le JDK1.6, assurez-vous que la variable JAVA_HOME pointe sur le rpertoire de celuici puis tlchargez Maven partir de lURL http://maven.apache.org/, dzippez le fichier sur votre disque dur et ajoutez le rpertoire apache-maven-2.2.1/bin votre variable PATH.

Chapitre 1

Tour dhorizon de Java EE 6

33

Puis, dans une fentre de commande DOS, tapez mvn -version pour tester votre installation. Comme le montre la Figure1.12, Maven devrait afficher sa version et celle du JDK.
Figure1.12 Maven affiche sa version.

Maven a besoin dun accs Internet pour pouvoir tlcharger les extensions et les dpendances des projets partir du dpt principal. Si vous vous trouvez derrire un proxy, consultez la documentation pour ajuster votre configuration en consquence.
Utilisation

Voici quelques commandes que nous utiliserons pour les exemples de ce livre. Elles invoquent toute une phase diffrente du cycle de vie (nettoyage, compilation, installation, etc.) et utilisent le fichier pom.xml pour ajouter des bibliothques, personnaliser la compilation ou ajouter des comportements via des extensions:
mvn clean.

Supprime tous les fichiers gnrs (classes compiles, code produit, artfacts, etc.). Compile les classes Java principales. Compile les classes de test.

mvn compile.

mvn test-compile. mvn test.

Compile les classes de test et excute les tests. Compile et excute les tests et cre larchive du paquetage. Construit et installe les artfacts dans votre dpt local.

mvn package. mvn install.

mvn clean install.

Nettoie et installe (remarquez que vous pouvez indiquer plusieurs commandes en les sparant par un espace).

34

Java EE 6 et GlassFish 3

INFO Maven vous permet de compiler, dexcuter et dassembler les exemples de ce livre. Cependant, pour crire le code, vous aurez besoin dun environnement de dveloppement intgr (EDI). Personnellement, nous utilisons IntelliJ IDEA de JetBrains, dont vous apercevrez quelques copies dcran. Vous pouvez choisir nimporte quel IDE car cet ouvrage ne repose que sur Maven, non sur des fonctionnalits spcifiques dIntelliJ IDEA.

JUnit 4

JUnit est un framework open-source pour lcriture et lexcution de tests. Parmi ses fonctionnalits, citons:

les assertions pour tester des rsultats attendus; les "fixtures" pour partager des donnes de test communes; les lanceurs pour excuter les tests.

JUnit est une bibliothque de tests unitaires qui est le standard de facto pour Java; elle est assemble dans un unique fichier jar que vous pouvez tlcharger partir de lURL http://www.junit.org/ (ou utilisez la gestion des dpendances de Maven pour le rcuprer). La bibliothque contient une API complte vous permettant dcrire vos tests unitaires, ainsi quun outil pour les excuter. Ces tests unitaires aident rendre votre code plus robuste et plus fiable.
Historique

La premire version de JUnit a t crite par Erich Gamma et Kent Beck en 1998. Elle sinspirait du framework de test Sunit de Smalltalk, galement crit par Kent Beck, et est rapidement devenue lun des frameworks les plus connus du monde Java. Apportant les avantages des tests unitaires une grande varit de langages, JUnit a inspir une famille doutils xUnit comme nUnit (.NET), pyUnit (Python), CppUnit (C++), dUnit (Delphi), et bien dautres encore. JUnit joue un rle important dans le dveloppement pilot par les tests (DPT).
Fonctionnement

Depuis JUnit 4, lcriture des tests unitaires sest simplifie grce lusage des annotations, des importations statiques et des autres fonctionnalits de Java. Par

Chapitre 1

Tour dhorizon de Java EE 6

35

rapport aux versions prcdentes, JUnit4 fournit un modle de test plus simple, plus riche et plus facile demploi. Il introduit galement des initialisations plus souples, des rgles de nettoyage, des dlais dexpiration et des cas de tests paramtrables. tudions quelques-unes de ses fonctionnalits au moyen dun exemple simple. Le Listing1.7 reprsente un POJO Customer possdant quelques attributs dont une date de naissance, des constructeurs, des getters et des setters.
Listing1.7: Une classe Customer
public class Customer { private Long id; private String firstName; private String lastName private String email; private String phoneNumber; private Date dateOfBirth; private Date creationDate; // Constructeurs, getters, setters }

La classe

CustomerHelper,

calculateAge()

prsente dans le Listing 1.8, fournit une mthode permettant de calculer lge dun client donn.

Listing1.8: La classe CustomerHelper


public class CustomerHelper { private int ageCalcResult; private Customer customer; public void calculateAge() { Date dateOfBirth = customer.getDateOfBirth(); Calendar birth = new GregorianCalendar(); birth.setTime(dateOfBirth); Calendar now = new GregorianCalendar(2001, 1, 1); ageCalcResult = now.get(Calendar.YEAR) birth.get(Calendar.YEAR); // Pas encore implmente public Date getNextBirthDay() { return null; } public void clear() { ageCalcResult = 0; customer = null; } // Getters, setters }

36

Java EE 6 et GlassFish 3

La mthode calculateAge() utilise lattribut dateOfBirth pour renvoyer lge du client. La mthode clear() rinitialise ltat de CustomerHelper et la mthode getNextBirthDay() nest pas encore implmente. Cette classe auxiliaire a quelques dfauts: il semble notamment quil y ait un bogue dans le calcul de lge. Pour tester la mthode calculateAge(), nous utiliserions la classe JUnit TestCustomerHelper dcrite dans le Listing1.9.
Listing1.9: Classe de test pour CustomerHelper
import import import import org.junit.Before; org.junit.Ignore; org.junit.Test; static org.junit.Assert.*;

public class CustomerHelperTest { private static CustomerHelper customerHelper = new CustomerHelper(); @Before public void clearCustomerHelper() { customerHelper.clear(); } @Test public void notNegative() { Customer customer = new Customer(); customer.setDateNaissance(new GregorianCalendar(1975, 5, 27).getTime()); customerHelper.setCustomer(customer); customerHelper.calculateAge(); int calculatedAge = customerHelper.getAgeCalcResult(); assert calculatedAge >= 0; } @Test public void expectedValue() { int expectedAge = 33; Calendar birth = new GregorianCalendar(); birth.roll(Calendar.YEAR, expectedAge * (-1)); birth.roll(Calendar.DAY_OF_YEAR, -1); Customer customer = new Customer(); customer.setDateOfBirth(birth.getTime()); customerHelper.setCustomer(customer); customerHelper.calculateAge();

Chapitre 1

Tour dhorizon de Java EE 6

37

assertTrue(customerHelper.getAgeCalcResult() == expectedAge); } @Test(expected = NullPointerException.class) public void emptyCustomer() { Customer customer = new Customer(); customerHelper.setCustomer(customer); customerHelper.calculateAge(); assertEquals( customerHelper.getAgeCalcResult(), -1); } @Ignore("not ready yest") @Test public void nextBirthDay() { // to do.. } }

La classe de test du Listing1.9 contient quatre mthodes de test. La mthode expectedValue() chouera car il y a un bogue dans le calcul de lge effectu par la classe CustomerHelper. La mthode nextBirthDay() est ignore car elle nest pas encore implmente. Les deux autres mthodes russiront. emptyCustomer() sattend ce que la mthode lance une exception NullPointerException.
Mthodes de test

Avec JUnit4, les classes de test nont pas besoin dhriter dune classe quelconque. Pour tre excute comme un cas de test, une classe JUnit doit au moins possder une mthode annote par @Test. Si vous tentez dexcuter une classe qui ne comporte pas au moins une mthode @Test, vous obtiendrez une erreur (java.lang. Exception: No runnable methods). Une mthode de test doit utiliser lannotation @Test, renvoyer void et ne prendre aucun paramtre. Ces contraintes sont vrifies lors de lexcution et leur non-respect provoque la leve dune exception. Lannotation @Test peut prendre un paramtre facultatif expected pour indiquer que la mthode de test concerne doit lever une exception. Si elle ne le fait pas ou si lexception est diffrente de celle annonce, le test choue. Dans notre exemple, une tentative de calculer lge dun client vide doit provoquer la leve dune exception NullPointerException. La mthode nextBirthDay() nest pas implmente dans le Listing1.9 mais vous ne souhaitez pas pour autant que ce test choue: vous voulez simplement lignorer. Pour ce faire, il suffit dajouter lannotation @Ignore avant ou aprs lannotation @Test.

38

Java EE 6 et GlassFish 3

Les lanceurs de tests signaleront le nombre de tests ignors, ainsi que le nombre de tests qui ont russi ou chou. Si vous voulez indiquer la raison pour laquelle un test a t ignor, vous pouvez ventuellement passer un paramtre String contenant le message adquat.
Mthodes dassertions

Les cas de test doivent vrifier que les objets sont conformes ce qui est attendu. JUnit dispose pour cela dune classe Assert contenant plusieurs mthodes. Pour lutiliser, vous devez soit utiliser la notation prfixe (Assert.assertEquals(), par exemple) soit importer statiquement la classe Assert (cest ce que nous avons fait dans le Listing1.9). Comme vous pouvez le constater avec la mthode notNegative(), vous pouvez aussi vous servir du mot-cl assert de Java.
Fixtures

Les fixtures sont des mthodes permettant dinitialiser et de librer nimporte quel objet au cours des tests. JUnit utilise les annotations @Before et @After pour excuter du code respectivement avant et aprs chaque test. Les mthodes annotes par @ Before et @After peuvent porter nimporte quel nom (clearCustomerHelper() ici) et une mme classe de test peut en avoir plusieurs. JUnit utilise galement les annotations @BeforeClass et @AfterClass pour excuter un code spcifique une seule fois par classe. Ces mthodes doivent tre uniques et statiques et sont trs pratiques pour allouer et librer des ressources coteuses.
Lancement de JUnit

Pour excuter le lanceur de JUnit, vous devez ajouter le fichier jar de JUnit votre variable CLASSPATH (ou ajouter une dpendance Maven). Vous pouvez alors lancer vos tests via la commande Java suivante:
java ea org.junit.runner.JUnitCore com.apress.javaee6.CustomerHelperTest

Notez que, lorsque lon utilise le mot-cl assert, il faut prciser le paramtre -ea; sinon les assertions seront ignores. Cette commande produira le rsultat suivant:
JUnit version 4.5 ..E.I Time: 0.016 There was 1 failure:

Chapitre 1

Tour dhorizon de Java EE 6

39

1) expectedValue(com.apress.javaee6.CustomerHelperTest) java.lang. AssertionError: at CustomerHelperTest.expectedValue (CustomerHelperTest.java:52) FAILURES!!! Tests run: 3, Failures: 1

La premire information affiche est le numro de version de JUnit (4.5, ici). Puis JUnit indique le nombre de tests excuts (trois, ici) et le nombre dchecs (un seul dans cet exemple). La lettre I indique quun test a t ignor.
Intgration de JUnit

Actuellement, JUnit est trs bien intgr la plupart des EDI (IntelliJ IDEA, Eclipse, NetBeans, etc.). Avec ces environnements, JUnit utilise gnralement la couleur verte pour indiquer les tests qui ont russi et le rouge pour signaler les checs. La plupart des EDI fournissent galement des outils pour faciliter la cration des classes de test. JUnit est galement intgr Maven via lextension Surefire utilise au cours de la phase de test. Cette extension excute les classes de tests JUnit dune application et produit des rapports aux formats texte et XML. Pour lancer les tests JUnit via cette extension, faites la commande suivante:
mvn test

Derby 10.5

Initialement nomm Cloudscape, le systme de base de donnes Derby crit en Java a t offert par IBM la fondation Apache et est devenu open-source. De son ct, Sun Microsystems a produit sa propre distribution sous le nom de Java DB. Malgr une empreinte mmoire rduite (2Mo), Derby est un systme de base de donnes relationnelle entirement fonctionnel qui supporte les transactions et peut aisment sintgrer dans nimporte quelle solution Java. Derby a deux modes diffrents : intgr ou serveur rseau. Le mode intgr correspond au lancement de Derby par une simple application Java mono-utilisateur: en ce cas, il sexcute dans la mme JVM que lapplication. Cest ce mode que nous utiliserons dans ce livre pendant les tests unitaires. Le mode serveur rseau correspond au lancement de Derby sous forme de processus spar, fournissant une connectivit multi-utilisateurs. Nous utiliserons ce mode lorsque nous excuterons les applications.

40

Java EE 6 et GlassFish 3

Installation

Linstallation de Derby est trs simple; en fait, vous constaterez quil est dj install puisquil est fourni avec le JDK 1.6 au cours de linstallation de ce dernier (voir Figure1.6), lassistant propose dinstaller par dfaut Java DB. Sil nest pas install sur votre machine, vous pouvez rcuprer les binaires partir de lURL http://db.apache.org. Lorsque Derby est install, configurez la variable DERBY_HOME pour quelle contienne le rpertoire o il a t plac sur votre systme, puis ajoutez %DERBY_HOME%\bin votre variable PATH. Pour lancer Derby en mode serveur rseau, excutez le script %DERBY_HOME%\bin\startNetworkServer.bat: des informations safficheront alors sur la console pour indiquer, par exemple, le numro du port sur lequel il attend les connexions (1527 par dfaut). Derby est fourni avec plusieurs programmes utilitaires, dont sysinfo. Ouvrez une session DOS, tapez sysinfo et vous devriez voir apparatre des informations sur votre environnement Java et Derby, analogues celles de la Figure1.13.
Figure1.13 Rsultat de sysinfo aprs linstallation de Derby.

Derby fournit plusieurs outils (dans le sous-rpertoire bin) permettant dinteragir avec la base de donnes. Le plus simple est probablement ij, qui permet de saisir des commandes SQL, et dblook, qui permet de visualiser une partie ou la totalit du langage de dfinition des donnes (LDD) dune base. Assurez-vous davoir lanc le serveur rseau Derby et tapez la commande ij linvite de commande. Puis saisissez les commandes suivantes pour crer une base de donnes et une table, insrer des donnes dans cette table et linterroger:
ij> connect jdbc:derby://localhost:1527/Chapter01DB;create=true;

Chapitre 1

Tour dhorizon de Java EE 6

41

Cette commande vous connecte la base de donnes Chapter01DB. Comme celleci nexiste pas encore, nous utilisons le paramtre create=true pour demander sa cration.
ij> create table customer (custID int primary key, > firstname varchar(20), lastname varchar(20));

Cette commande cre une table customer avec une cl primaire et deux colonnes varchar(20) pour stocker le prnom et le nom de chaque client. Vous pouvez afficher la description de cette table laide de la commande suivante:
ij> describe customer; COLUMN_NAME |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL& -------------------------------------------------------------------------CUSTID |INTEGER |0 |10 |10 |NULL |NULL |NO FIRSTNAME |VARCHAR |NULL|NULL|20 |NULL |40 |YES LASTNAME |VARCHAR |NULL|NULL|20 |NULL |40 |YES

Maintenant que la table est cre, vous pouvez y ajouter des donnes laide dinstructions insert:
ij> insert into customer values (1, Fred, Chene); ij> insert into customer values (2, Sylvain, Verin); ij> insert into customer values (3, Robin, Riou);

Vous pouvez ensuite utiliser toute la puissance de SQL pour obtenir, trier ou regrouper des donnes:
ij> select count(*) from customer; 1 ----------3 1 ligne slectionne ij> select * from customer where custid=3; CUSTID |FIRSTNAME |LASTNAME --------------------------------------------------3 |Robin |Riou 1 ligne slectionne ij> exit;

Pour obtenir le LDD de la table cre, sortez dij et lancez dblook pour interroger la base de donnes Chapter01DB:
C:\> dblook -d jdbc:derby://localhost:1527/Chapter01DB -- Horodatage : 2010-01-29 19:21:09.379 -- La base de donnes source est : Chapter01DB -- LURL de connexion est : jdbc:derby://localhost:1527/Chapter01DB -- appendLogs: false

42

Java EE 6 et GlassFish 3

-- ----------------------------------------------- Instructions DDL pour tables -- ---------------------------------------------CREATE TABLE "APP"."CUSTOMER" ("CUSTID" INTEGER NOT NULL, "FIRSTNAME" VARCHAR(20), "LASTNAME" VARCHAR(20)); -- ----------------------------------------------- Instructions DDL pour cls -- ----------------------------------------------- primaire/unique ALTER TABLE "APP"."CUSTOMER" ADD CONSTRAINT "SQL100129191119440" PRIMARY KEY ("CUSTID");

GlassFish v3

Bien quil sagisse dun serveur dapplications assez rcent, GlassFish est dj utilis par un grand nombre de dveloppeurs et de socits. Non seulement il est limplmentation de rfrence de la technologie JavaEE, mais cest galement lui que vous obtenez lorsque vous tlchargez le SDK JavaEE de Sun. Vous pouvez galement dployer des applications critiques sur GlassFish en plus dtre un produit, GlassFish est galement une communaut runie autour de lOpen Source sur le site http://glassfish.org. Cette communaut est trs ractive sur les listes de diffusion et les forums.
Historique

Les origines de GlassFish remontent aux premiers jours de Tomcat, lorsque Sun et le groupe JServ firent don de cette technologie la fondation Apache. Depuis, Sun a continu utiliser Tomcat dans diffrents produits. En 2005, Sun lana le projet GlassFish, qui avait pour but le dveloppement dun serveur dapplications entirement certifi JavaEE. Sa premire version, la 1.0, vit le jour en mai 2006. Le conteneur web de GlassFish hrite beaucoup de Tomcat (en fait, une application qui sexcute sur Tomcat devrait galement sexcuter avec GlassFish sans avoir la modifier). GlassFish v2 est apparu en septembre 2007 et a reu depuis de nombreuses mises jour. Il sagit de la version la plus dploye actuellement. GlassFish sefforce de ne pas modifier les habitudes des utilisateurs entre ses versions majeures et de ne pas imposer de modification du code. En outre, il ny a aucune diffrence de qualit entre les versions "communautaire" et "commerciale": les utilisateurs payants ont accs des correctifs et des outils de gestion supplmentaires (GlassFish Enter-

Chapitre 1

Tour dhorizon de Java EE 6

43

prise Manager), mais la version open-source (http://glassfish. org) et la version commerciale (http://www.sun.com/appserver) ont t testes de la mme faon, ce qui facilite le basculement vers une version commerciale nimporte quel moment dans le cycle du projet. Dans ce livre, nous utiliserons GlassFish v3. Comme il est dusage avec lOpen Source, des versions quotidiennes et des versions "prludes" sont disponibles. Les buts principaux de cette nouvelle version majeure de GlassFish est la modularisation des fonctionnalits essentielles avec lintroduction dun noyau reposant sur OSGi et un support complet de Java EE6.
INFO Lquipe de GlassFish a fait un effort considrable pour raliser une documentation complte et jour en produisant de nombreux guides: Quick Start Guide, Installation Guide, Administration Guide, Administration Reference, Application Deployment Guide, Developers Guide, etc. Vous pouvez les lire lURL http:// wiki.glassfish.java.net/Wiki.jsp?page=GlassFishDocs. Consultez galement les FAQ, les How-To et le forum GlassFish pour obtenir encore plus dinformations.

Architecture de GlassFish v3

En tant que programmeur dapplication (et non en tant que dveloppeur de GlassFish), vous navez pas besoin de comprendre son architecture interne, mais son architecture gnrale et ses choix techniques peuvent vous intresser. partir de la version prliminaire de GlassFishv3, le serveur dapplications est construit sur un noyau modulaire reposant sur OSGi. GlassFish sexcute directement au-dessus de limplmentation de Felix dApache, mais devrait galement fonctionner avec les runtimes OSGi Equinox ou Knopflerfish. HK2 (Hundred-Kilobyte Kernel) abstrait le module systme OSGi pour fournir les composants qui peuvent tre vus comme des services. Ceux-ci peuvent tre dcouverts et injects en cours dexcution. Pour linstant, OSGi nest pas expos aux dveloppeurs JavaEE.
INFO OSGi est un standard pour la gestion et la dcouverte dynamique des composants. Les applications ou les composants peuvent tre installs, lancs, arrts, mis jour et dsinstalls chaud, sans ncessiter un redmarrage. Les composants peuvent galement dtecter dynamiquement lajout ou la suppression de services et sadapter en consquence ; Felix dApache, Equinox et Knopflerfish sont des implmentations dOSGi.

44

Java EE 6 et GlassFish 3

Cette modularit et cette extensibilit permettent GlassFish v3 de passer dun simple serveur web attendant des commandes dadministration un runtime plus puissant moyennant un simple dploiement dartfacts comme des fichiers war (un conteneur web est charg et lanc, puis lapplication est dploye) ou des fichiers jar EJB (qui chargeront et lanceront dynamiquement le conteneur EJB). En outre, le serveur initial se lance en quelques secondes (moins de 5secondes sur une machine rcente) et vous ne payez en temps de dmarrage et en consommation mmoire que ce que vous utilisez. Le lancement du conteneur web la vole prend environ 3secondes de plus et les dploiements seffectuent souvent en moins de 1seconde. Tout ceci fait de GlassFishv3 un environnement trs apprci des dveloppeurs. Quel que soit le nombre de modules que charge dynamiquement GlassFishv3, la console dadministration, linterface en ligne de commande et le fichier de configuration centralis sont tous extensibles et chacun reste unique. Mentionnons galement le framework Grizzly, qui tait au dpart un serveur HTTP non bloquant reposant sur les E/S et qui est dsormais devenu lun des lments essentiels de GlassFish, comme le montre la Figure1.14.
Figure1.14 Architecture de GlassFish v3.
Conteneur web JSF JPA Metro Conteneur EJB JMS

Console de gestion Service de nommage

Centre de mises jour

CLI gestion

Injection

Grizzly

Configuration

Surveillance

Transaction

Scurit

Cur de Glassfish V (Module Subsystem) HK2 OSGI Java SE

Dploiement

Clustering

Centre de mise jour

Lorsque vous disposez dun serveur dapplication modulaire, vous pouvez commencer jouer avec les diffrents modules pour construire votre propre environnement, exactement comme vous le feriez avec les EDI et les distributions Linux ou comme vous le faites avec les extensions de Firefox. Le centre de mise jour de GlassFish est un ensemble doutils graphiques et en ligne de commande qui vous permettent de grer cet environnement. La technologie derrire tout ceci sappelle IPS (Image

Chapitre 1

Tour dhorizon de Java EE 6

45

Packaging System, galement appel pkg), le systme de paquetage utilis par OpenSolaris. Outre lensemble de modules fourni par dfaut avec GlassFish, lutilisateur peut se connecter diffrents dpts pour mettre jour les fonctionnalits dj installes, en ajouter de nouvelles (support de Grails, conteneur de portlet, etc.), voire ajouter des applications tierces. Dans un environnement dentreprise, vous pouvez configurer votre propre dpt et utiliser pkg pour lancer linstallation dun logiciel reposant sur GlassFish. En pratique, avec GlassFish v3, le centre de mise jour est accessible via la console dadministration, le client graphique qui se trouve dans le rpertoire %GLASSFISH_ HOME%\updatetool\bin ou le programme pkg en ligne de commande. Tous trois vous permettent dnumrer, dajouter et de supprimer des composants partir dun ensemble de dpts. Dans le cas de pkg (qui se trouve dans le rpertoire %GLASSFISH_HOME%\pkg\bin), les commandes les plus frquentes sont pkg list, pkg install, pkg uninstall et pkg image-update.
Sous-projets GlassFish

Le serveur dapplications GlassFish tant compos de tant de parties diffrentes, le projet a t dcoup en sous-projets. Cette dcomposition permet de mieux comprendre non seulement les diffrentes parties mais galement ladoption des fonctionnalits individuelles en dehors de lenvironnement GlassFish, lorsque lon est en mode autonome ou dans un autre conteneur. La Figure1.15 prsente un rsum de larchitecture des composantes fonctionnelles du serveur dapplications. OpenMQ, par exemple, est une implmentation open-source de JMS de qualit professionnelle. Bien quil soit souvent utilis de faon autonome pour les architectures orientes messages, OpenMQ peut galement tre intgr de diffrentes faons GlassFish (in-process, out-of-process, ou distant). Son administration peut seffectuer via la console dadministration de GlassFish ou par linterface asadmin en ligne de commande (voir la section "asadmin"). Le site web de sa communaut se trouve lURL http://openmq.dev.java.net. Metro est le cur des services web. Cette pile complte est construite au-dessus de JAX-WS et lui ajoute des fonctionnalits avances, comme une scurit de bout en bout, un transport optimis (MTOM, FastInfoset), une messagerie fiable et un comportement transactionnel pour les services web SOAP. Cette qualit de service (QoS) pour les services web repose sur des standards (OASIS, W3C), sexprime par des politiques et ne ncessite pas lutilisation dune nouvelle API en plus de JAX-WS. Metro est galement rgulirement test avec les implmentations .NET

46

Java EE 6 et GlassFish 3

de Microsoft pour assurer linteroprabilit entre les deux technologies. Son site communautaire se trouve lURL http://metro.dev.java.net.
Serveur d'administration Console d'administration Application d'administration

Extensions du serveur web

Instance d'un serveur d'applications Serveur HTTP couteurs HTTP ORB couteurs IIOP Conteneur web Services web Conteneur EJB Gestion du cycle de vie Connecteur Java EE Java Message Service Gestionnaire de persistance JDBC Gestionnaire de transactions Base de donnes

Clients web Clients Java/C++/IIOP Conteneur d'applications client Autres serveurs d'applications compatibles Java EE

EIS Fournisseur de messages Serveur HADB

Processus, gestion des threads, contrle de l'excution

Figure1.15 Composantes fonctionnelles de GlassFish.

Mojarra est le nom de limplmentation de JSF dans GlassFish; elle est disponible lURL http:// mojarra.dev.java.net. Jersey est limplmentation de rfrence et de qualit professionnelle pour la nouvelle spcification JAX-RS. Cette spcification et son implmentation sont des nouveaux venus dans Java EE6 et GlassFish. En fait, Jersey1.0 est disponible via le centre de mise jour de GlassFishv2 etv3 depuis sa sortie en 2008.
Administration

GlassFish tant un serveur dapplications complet, il implmente videmment lintgralit des spcifications Java EE6, mais il dispose galement de fonctionnalits supplmentaires comme son administration, qui peut seffectuer via une interface web (la "console dadministration") ou au moyen dasadmin, une interface en ligne de commande puissante. Quasiment toute sa configuration est stocke dans un fichier nomm domain.xml (situ dans le rpertoire domains\domain1\config), ce qui simplifie la recherche des erreurs. Ce fichier ne doit pas tre modifi manuellement mais via lun des deux outils dadministration, qui reposent tous les deux sur linstrumentation JMX fournie par GlassFish.

Chapitre 1

Tour dhorizon de Java EE 6

47

Console dadministration

La console dadministration est une interface web (voir Figure1.16) pour le serveur dapplications. Cet outil est destin la fois aux administrateurs et aux dveloppeurs et fournit une reprsentation graphique des objets grs par le serveur, une visualisation amliore des journaux, de ltat du systme et de la surveillance des donnes. Au minimum, cette console permet de grer la cration et la modification des configurations (rglage de la JVM, niveau des journaux, rglage du pool et du cache, etc.), JDBC, JNDI, JavaMail, JMS et les ressources connecteur ainsi que les applications (dploiement). Dans le profil cluster de GlassFish, la console dadministration est amliore pour permettre lutilisateur de grer les clusters, les instances, les agents nuds et les configurations de rpartition de la charge. Une aide contextuelle est toujours disponible via le bouton daide situ en haut droite de la fentre. Dans une installation par dfaut, la console est accessible aprs le dmarrage de GlassFish par lURL http://localhost:4848. partir de GlassFishv3, il est possible de configurer un utilisateur anonyme afin dviter de devoir sauthentifier. Si cet utilisateur nexiste pas, une installation typique utilise admin comme nom dutilisateur et adminadmin comme mot de passe par dfaut.

Figure1.16 Console dadministration web.

48

Java EE 6 et GlassFish 3

Outil en ligne de commande asadmin

Linterface en ligne de commande asadmin est trs puissante et cest souvent elle que lon utilise en production car on peut crire des scripts pour crer des instances et des ressources, dployer des applications et surveiller les donnes dun systme en cours dexcution. Cette commande se trouve dans le rpertoire bin de GlassFish et peut grer plusieurs domaines de serveurs dapplication locaux ou distants. Elle reconnat plusieurs centaines de commandes mais vous nen utiliserez probablement quune petite partie. Pour en avoir la liste complte, faites asadmin help. Les commandes utiles dans un profil dveloppeur simple sont asadmin start-domain, asadmin stop-domain, asadmin deploy, asadmin deploydir et asadmin undeploy. Si vous faites une erreur de frappe, asadmin vous proposera la commande correspondante la plus proche. Tapez asadmin resource, par exemple, et vous constaterez quasadmin vous propose les commandes de la Figure1.17. Avec GlassFishv3, asadmin dispose dun historique des commandes et de la compltion de la saisie.
Figure1.17 Ligne de commande asadmin.

Installation de GlassFish

GlassFish v3 peut tre install sous diffrents profils (chaque profil dfinit un ensemble de fonctionnalits et de configurations). Le profil le plus classique en ce qui nous concerne est le profil dveloppeur. Si vous voulez utiliser les fonctionnalits de cluster de GlassFish, en revanche, vous devrez soit linstaller sous le profil cluster, soit mettre jour votre installation existante en choisissant "Ajouter le support Cluster" partir de la console dadministration. Pour le moment, il nexiste que le profil dveloppeur pour GlassFishv3 (qui est ncessaire pour excuter les applications Java EE6). GlassFish peut tre tlcharg via diffrents mcanismes de distribution. Les choix les plus vidents consistent le rcuprer partir de lURL http://glassfish.org,

Chapitre 1

Tour dhorizon de Java EE 6

49

linstaller avec le SDK JavaEE ou utiliser lEDI NetBeans. Nous expliquerons ici comment le tlcharger et linstaller partir du site communautaire. Rendez-vous sur la page principale de tlchargement, https://glassfish.dev.java. net/public/downloadsindex.html, et choisissez GlassFish Serverv3. Slectionnez larchive convenant votre plate-forme et aux besoins de votre systme dexploitation (la distribution Unix fonctionnera avec Linux, Solaris et MacOSX). Linstallation du programme dinstallation lancera linstallateur graphique qui:

vous demande daccepter les termes de la licence; vous demande le rpertoire o vous souhaitez installer GlassFish; vous permet de configurer un nom dutilisateur et un mot de passe pour ladministrateur (ou cre par dfaut un utilisateur anonyme); vous permet de configurer les ports HTTP et dadministration (en vrifiant quils ne sont pas dj utiliss); installe et active loutil de mise jour (les clients pkg et updatetool).

Puis il dcompresse une installation prconfigure de GlassFish avec une configuration par dfaut: le port dadministration est 4848, le port HTTP est 8080 et aucun utilisateur admin nest configur. Loutil de mise jour nest pas install par dfaut; il le sera partir du rseau lors du premier dmarrage. Lorsquil a t correctement install, GlassFish peut tre lanc avec la ligne de commande asadmin suivante (voir Figure1.18).
asadmin start-domain domain1

Vous pouvez ensuite afficher la console dadministration (que nous avons montre la Figure1.16) en faisant pointer votre navigateur vers http://localhost:4848 ou aller sur le serveur web par dfaut via http://localhost:8080.
INFO Si vous navez quun seul domaine, vous pouvez omettre le nom de domaine par dfaut et lancer GlassFish uniquement avec la commande asadmin start-domain. Si vous voulez voir apparatre le journal lcran au lieu de consulter le fichier qui lui est consacr (domains/ domain1/logs/server.log), utilisez la commande asadmin start-domain --verbose.

GlassFish a bien dautres fonctionnalits offrir: je vous en montrerai quelquesunes au cours de ce livre mais je vous laisserai explorer son support des langages dynamiques (JRuby on Rails, Groovy et Grails, etc.), les services de diagnostic, les

50

Java EE 6 et GlassFish 3

rgles de gestion, les proprits systme, la surveillance des donnes, le flux dappel et les diffrentes configurations de scurit.
Figure1.18 Lancement de GlassFish.

Rsum
Lorsquune socit dveloppe une application Java et doit ajouter des fonctionnalits professionnelles comme la gestion des transactions, la scurit, la concurrence ou la messagerie, JavaEE est attractif. Il est standard, les composants sont dploys dans diffrents conteneurs qui offrent de nombreux services et fonctionnent avec plusieurs protocoles. Java EE6 suit les traces de sa version prcdente en ajoutant la simplicit dutilisation de la couche web. Cette version est plus lgre (grce llagage, aux profils et EJB Lite), plus simple dutilisation (plus besoin dinterfaces sur les EJB ou dannotations sur la couche web), plus riche (elle ajoute de nouvelles spcifications et fonctionnalits) et, enfin, plus portable (elle inclut un conteneur EJB standardis et autorise les noms JNDI). La deuxime partie de ce chapitre a t consacre la mise en place de lenvironnement de dveloppement. Ce livre contient de nombreux extraits de code et des sections "Rcapitulatif". Vous aurez besoin de plusieurs outils et frameworks pour compiler, dployer, excuter et tester ces codes: JDK1.6, Maven2, JUnit4, Derby10.5 et GlassFishv3. Ce chapitre vous a donn un bref aperu de Java EE6. Les suivants tudieront plus en dtail ses spcifications.

2
Persistance en Java
Les applications sont composes dune logique mtier, dinteractions avec dautres systmes, dinterfaces utilisateur et... de persistance. La plupart des donnes manipules par les applications doivent tre stockes dans des bases de donnes pour pouvoir tre ensuite rcupres et analyses. Les bases de donnes sont importantes: elles stockent les donnes mtier, servent de point central entre les applications et traitent les donnes via des triggers ou des procdures stockes. Les donnes persistantes sont omniprsentes la plupart du temps, elles utilisent les bases de donnes relationnelles comme moteur sous-jacent. Dans un systme de gestion de base de donnes relationnelle, les donnes sont organises en tables formes de lignes et de colonnes; elles sont identifies par des cls primaires (des colonnes spciales ne contenant que des valeurs uniques) et, parfois, par des index. Les relations entre tables utilisent les cls trangres et joignent les tables en respectant des contraintes dintgrit. Tout ce vocabulaire est totalement tranger un langage orient objet comme Java. En Java, nous manipulons des objets qui sont des instances de classes; les objets hritent les uns des autres, peuvent utiliser des collections dautres objets et, parfois, se dsignent eux-mmes de faon rcursive. Nous disposons de classes concrtes, de classes abstraites, dinterfaces, dnumrations, dannotations, de mthodes, dattributs, etc. Cependant, bien que les objets encapsulent soigneusement leur tat et leur comportement, cet tat nest accessible que lorsque la machine virtuelle (JVM) sexcute: lorsquelle sarrte ou que le ramasse-miettes nettoie la mmoire, tout disparat. Ceci dit, certains objets nont pas besoin dtre persistants: par donnes persistantes, nous dsignons les donnes qui sont dlibrment stockes de faon permanente sur un support magntique, une mmoire flash, etc. Un objet est persistant sil peut stocker son tat afin de pouvoir le rutiliser plus tard.

52

Java EE 6 et GlassFish 3

Il existe diffrents moyens de faire persister ltat en Java. Lun deux consiste utiliser le mcanisme de srialisation qui consiste convertir un objet en une suite de bits: on peut ainsi srialiser les objets sur disque, sur une connexion rseau (notamment Internet), sous un format indpendant des systmes dexploitation. Java fournit un mcanisme simple, transparent et standard de srialisation des objets via limplmentation de linterface java.io.Serializable. Cependant, bien quelle soit trs simple, cette technique est assez fruste: elle ne fournit ni langage dinterrogation ni support des accs concurrents intensifs ou de la mise en cluster. Un autre moyen de mmoriser ltat consiste utiliser JDBC (Java Database Connectivity), qui est lAPI standard pour accder aux bases de donnes relationnelles. On peut ainsi se connecter une base et excuter des requtes SQL (Structured Query Language) pour rcuprer un rsultat. Cette API fait partie de la plate-forme Java depuis la version1.1 mais, bien quelle soit toujours trs utilise, elle a tendance tre dsormais clipse par les outils de correspondance entre modle objet et modle relationnel (ORM, Object-Relational Mapping), plus puissants. Le principe dun ORM consiste dlguer laccs aux bases de donnes relationnelles des outils ou des frameworks externes qui produisent une vue oriente objet des donnes relationnelles et vice versa. Ces outils tablissent donc une correspondance bidirectionnelle entre la base et les objets. Diffrents frameworks fournissent ce service, notamment Hibernate, TopLink et Java Data Objects (JDO), mais il est prfrable dutiliser JPA (Java Persistence API) car elle est intgre Java EE6.

Rsum de la spcification JPA


JPA 1.0 a t cre avec Java EE 5 pour rsoudre le problme de la persistance des donnes en reliant les modles objets et relationnels. Avec Java EE6, JPA2.0 conserve la simplicit et la robustesse de la version prcdente tout en lui ajoutant de nouvelles fonctionnalits. Grce cette API, vous pouvez accder des donnes relationnelles et les manipuler partir des EJB (Enterprise Java Beans), des composants web et des applications JavaSE. JPA est une couche dabstraction au-dessus de JDBC, qui fournit une indpendance vis--vis de SQL. Toutes les classes et annotations de cette API se trouvent dans le paquetage javax.persistence. Ses composants principaux sont les suivants:

ORM, qui est le mcanisme permettant de faire correspondre les objets des donnes stockes dans une base de donnes relationnelle.

Chapitre 2

Persistance en Java

53

Une API gestionnaire dentits permettant deffectuer des oprations sur la base de donnes, notamment les oprations CRUD (Create, Read, Update, Delete). Grce elle, il nest plus ncessaire dutiliser directement JDBC. JPQL (Java Persistence Query Language), qui permet de rcuprer des donnes laide dun langage de requtes orient objet. Des mcanismes de transaction et de verrouillage lorsque lon accde de faon concurrente aux donnes, fournis par JTA (Java Transaction API). Les transactions locales la ressource (non JTA) sont galement reconnues par JPA. Des fonctions de rappel et des couteurs permettant dajouter la logique mtier au cycle de vie dun objet persistant.

Historique de la spcification

Les solutions ORM existent depuis longtemps, bien avant Java. Des produits comme TopLink ont commenc tre utiliss avec Smalltalk en 1994, avant de basculer vers Java. Les produits ORM commerciaux comme TopLink sont donc disponibles depuis les premiers jours du langage Java. Cependant, bien quils aient prouv leur utilit, ils nont jamais t standardiss pour cette plate-forme. Une approche comparable ORM a bien t standardise sous la forme de JDO, mais elle na jamais russi pntrer le march de faon significative. En 1998, EJB 1.0 vit le jour et fut ensuite intgr J2EE1.2. Il sagissait dun composant distribu lourd, utilis pour la logique mtier transactionnelle. CMP (Entity Container Managed Persistence) fut ensuite ajout EJB1.0 et continua dvoluer jusqu EJB2.1 (J2EE1.4). La persistance ne pouvait prendre place qu lintrieur dun conteneur, via un mcanisme dinstanciation complexe utilisant des interfaces locales ou distantes. Les capacits ORM taient galement trs limites car lhritage tait difficile traduire en termes relationnels. Paralllement au monde J2EE, la solution open-source Hibernate apportait des modifications surprenantes en terme de persistance car ce framework fournissait un modle orient objet persistant et lger. Aprs des annes de plaintes propos des composants Entity CMP2.x et en rponse au succs et la simplicit des frameworks open-source comme Hibernate, le modle de persistance de JavaEE fut entirement revu dans Java EE5: JPA1.0 tait n et proposait dsormais une approche lgre, largement inspire des principes de conception dHibernate. La spcification JPA1.0 a donc t intgre EJB3.0 (JSR220).

54

Java EE 6 et GlassFish 3

Aujourdhui, avec Java EE 6, la seconde version de JPA continue dans cette voie de la simplicit tout en ajoutant de nouvelles fonctionnalits. Elle a volu pour possder sa propre spcification, la JSR317.
Nouveauts de JPA 2.0

Si JPA 1.0 tait un modle de persistance entirement nouveau par rapport son prdcesseur Entity CMP2.x, JPA2.0 est la suite de JPA1.0, dont elle conserve lapproche oriente objet utilisant les annotations et, ventuellement, des fichiers de correspondance en XML. Cette seconde version ajoute de nouvelles API, tend JPQL et intgre de nouvelles fonctionnalits:

Les collections de types simples (String, Integer, etc.) et dobjets intgrables (embeddable) peuvent dsormais tre associes des tables distinctes alors quauparavant on ne pouvait associer que des collections dentits. Les cls et les valeurs des associations peuvent dsormais tre de nimporte quel type de base, des entits ou des objets intgrables. Lannotation @OrderColumn permet maintenant davoir un tri persistant. La suppression des orphelins permet de supprimer les objets fils dune relation lorsque lobjet parent est supprim. Le verrouillage pessimiste a t ajout au verrouillage optimiste, qui existait dj. Une toute nouvelle API de dfinition de requtes a t ajoute afin de pouvoir construire des requtes selon une approche oriente objet. La syntaxe de JPQL a t enrichie (elle autorise dsormais les expressions case, par exemple). Les objets intgrables peuvent maintenant tre embarqus dans dautres objets intgrables et avoir des relations avec les entits. La notation pointe a t tendue afin de pouvoir grer les objets intgrables avec des relations ainsi que les objets intgrables dobjets intgrables. Le support dune nouvelle API de mise en cache a t ajout.

Nous prsenterons en dtail toutes ces fonctionnalits aux Chapitres3, 4 et 5.


Implmentation de rfrence

EclipseLink 1.1 est une implmentation open-source de JPA2.0, mais ce framework souple et puissant supporte galement la persistance XML via JAXB (Java XML

Chapitre 2

Persistance en Java

55

Binding) et dautres techniques comme SDO (Service Data Objects). Il offre un ORM, un OXM (Object XML Mapping) et la persistance des objets sur EIS (Enterprise Information Systems) laide de JCA (Java EE Connector Architecture). Les origines dEclipseLink remontent au produit TopLink dOracle, qui a t offert la fondation Eclipse en 2006. Cest limplmentation de rfrence de JPA et cest le framework de persistance que nous utiliserons dans ce livre. Il est galement dsign sous les termes de fournisseur de persistance ou, simplement, de fournisseur.

Comprendre les entits


Lorsque lon voque lassociation dobjets une base de donnes relationnelle, la persistance des objets ou les requtes adresses aux objets, il est prfrable dutiliser le terme dentits plutt que celui dobjets. Ces derniers sont des instances qui existent en mmoire; les entits sont des objets qui ont une dure de vie limite en mmoire et qui persistent dans une base de donnes. Les entits peuvent tre associes une base de donnes, tre concrtes ou abstraites, et elles disposent de lhritage, peuvent tre mises en relation, etc. Une fois associes, ces entits peuvent tre gres par JPA. Vous pouvez stocker une entit dans la base de donnes, la supprimer et linterroger laide dun langage de requte (Java Persistence Query Language, ou JPQL). Un ORM vous permet de manipuler des entits alors quen coulisse cest la base de donnes quon accde. Comme nous le verrons, une entit a un cycle de vie bien dfini et, grce aux mthodes de rappel et aux couteurs, JPA vous permet dassocier du code mtier certains vnements de ce cycle.
ORM = Object-Relational Mapping

Le principe dun ORM consiste dlguer des outils ou des frameworks externes (JPA, dans notre cas) la cration dune correspondance entre les objets et les tables. Le monde des classes, des objets et des attributs peut alors tre associ aux bases de donnes constitues de tables formes de lignes et de colonnes. Cette association offre une vue oriente objet aux dveloppeurs, qui peuvent alors utiliser de faon transparente des entits la place des tables. JPA utilise les mtadonnes pour faire correspondre les objets une base de donnes.

56

Java EE 6 et GlassFish 3

Les mtadonnes sont associes chaque entit pour dcrire son association : elles permettent au fournisseur de persistance de reconnatre une entit et dinterprter son association. Ces mtadonnes peuvent sexprimer dans deux formats diffrents:

Annotations. Le code de lentit est directement annot avec toutes sortes dannotations dcrites dans le paquetage javax.persistence. Descripteurs XML. Ils peuvent tre utiliss la place (ou en plus) des annotations. Lassociation est dfinie dans un fichier XML externe qui sera dploy avec les entits. Cette technique peut tre trs utile lorsque la configuration de la base de donnes varie en fonction de lenvironnement, par exemple.

Pour faciliter les correspondances, JPA (comme de nombreuses autres fonctionnalits de Java EE6) utilise le concept de "convention plutt que configuration" (galement appel "configuration par exception" ou "programmation par exception"). Le principe est que JPA utilise un certain nombre de rgles de correspondance par dfaut (le nom de la table est le mme que celui de lentit, par exemple): si ces rgles vous satisfont, vous navez pas besoin de mtadonnes supplmentaires (aucune annotation ni XML ne sont alors ncessaires) mais, dans le cas contraire, vous pouvez adapter la correspondance vos propres besoins laide des mtadonnes. En dautres termes, fournir une configuration est une exception la rgle. Voyons un exemple. Le Listing2.1 prsente une entit Livre avec quelques attributs. Comme vous pouvez le constater, certains sont annots (id, titre et description) alors que dautres ne le sont pas.
Listing2.1: Une entit Book simple
@Entity public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Chapitre 2

Persistance en Java

57

Pour tre reconnue comme entit, la classe Book doit tre annote par @javax. persistence.Entity (ou son quivalent XML). Lannotation @javax. persistence.Id sert indiquer la cl primaire, et la valeur de cet identifiant est automatiquement gnre par le fournisseur de persistance (@GeneratedValue). Lannotation @Column est utilise avec certains attributs pour adapter la correspondance par dfaut des colonnes (title ne peut plus contenir NULL et description a une longueur de 2000 caractres). Le fournisseur de persistance pourra ainsi faire correspondre lentit Book une table BOOK (rgle de correspondance par dfaut), produire une cl primaire et synchroniser les valeurs des attributs vers les colonnes de la table. La Figure2.1 montre cette association entre lentit et la table.
Figure2.1 Lentit Book est associe la table BOOK.
<<entity>> Book
-id : Long -title : String -price : Float -description : String -nbOfPage : Integer -illustrations : Boolean

Association

+ID TITLE PRICE DESCRIPTION ISBN NBOFPAGE ILLUSTRATIONS

bigint varchar(255) double varchar(2000) varchar(255) integer smallint

BOOK

Nullable = false Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true

Comme nous le verrons au Chapitre3, cette correspondance est riche et vous permet dassocier toutes sortes de choses. Le monde de la programmation oriente objet abonde de classes et dassociations entre elles (et les collections de classes). Les bases de donnes modlisent galement les relations, mais diffremment: en utilisant des cls trangres ou des jointures. JPA dispose donc dun ensemble de mtadonnes permettant de grer cette correspondance entre ces deux visions des relations. Lhritage peut galement tre traduit: bien que ce soit un mcanisme frquemment utilis en programmation pour rutiliser le code, ce concept est inconnu des bases de donnes relationnelles (elles doivent le simuler avec des cls trangres et des contraintes). Mme si cette traduction de lhritage implique quelques contorsions, JPA lautorise et vous offre diffrentes stratgies pour y parvenir. Nous les dcrirons au Chapitre3.
Interrogation des entits

JPA permet de faire correspondre les entits des bases de donnes et de les interroger en utilisant diffrents critres. La puissance de cette API vient du fait quelle offre la possibilit dinterroger les entits et leurs relations de faon oriente objet sans devoir utiliser les cls trangres ou les colonnes de la base de donnes sousjacente. Llment central de lAPI, responsable de lorchestration des entits, est le

58

Java EE 6 et GlassFish 3

gestionnaire dentits: son rle consiste grer les entits, lire et crire dans une base de donnes et autoriser les oprations CRUD simples sur les entits, ainsi que des requtes complexes avec JPQL. Dun point de vue technique, le gestionnaire dentits nest quune interface dont limplmentation est donne par le fournisseur de persistance, EclipseLink. Lextrait de code suivant montre comment crer un gestionnaire dentits et rendre une entit Livre persistante:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("chapter02PU"); EntityManager em = emf.createEntityManager(); em.persist(livre);

La Figure2.2 montre comment linterface EntityManager peut tre utilise par une classe (Main, ici) pour manipuler des entits (Livre, ici). Grce des mthodes comme persist() et find(), le gestionnaire dentits masque les appels JDBC adresss la base de donnes, ainsi que les instructions SQL INSERT ou SELECT.
Main Interface EntityManager
+persist(entity : Object) : void +find(entityClass, : Class<T>, primaryKey : Object) : <T>

SQL / JDBC

Base de donnes

Book
-id : Long -title : String -price : Float -description : String -nbOfPage : Integer -illustrations : Boolean

Figure2.2 Le gestionnaire dentits interagit avec lentit et la base de donnes sous-jacente.

Le gestionnaire dentits permet galement dinterroger les entits. Dans ce cas, une requte JPA est semblable une requte sur une base de donnes, sauf quelle utilise JPQL au lieu de SQL. La syntaxe utilise la notation pointe habituelle. Pour rcuprer, par exemple, tous les livres intituls H2G2, il suffirait dcrire:
SELECT b FROM Book b WHERE b.title = H2G2

Une instruction JPQL peut excuter des requtes dynamiques (cres lexcution), des requtes statiques (dfinies lors de la compilation), voire des instructions SQL natives. Les requtes statiques, galement appeles requtes nommes, sont dfinies par des annotations ou des mtadonnes XML. Linstruction JPQL prcdente peut, par exemple, tre dfinie comme une requte nomme sur lentit Livre.

Chapitre 2

Persistance en Java

59

Le Listing2.2 montre une entit Book dfinissant la requte nomme Title laide de lannotation @NamedQuery.
Listing2.2: Une requte nomme findBookByTitle
@Entity @NamedQuery(name = "findBookByTitle", query = "SELECT b FROM Book b WHERE b.title =H2G2") public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; } // Constructeurs, getters, setters

findBookBy-

Comme nous le verrons au Chapitre4, la mthode EntityManager.createNamedQuery() permet dexcuter la requte et renvoie une liste dentits Book correspondant aux critres de recherche.
Mthodes de rappel et couteurs

Les entits sont simplement des POJO (Plain Old Java Objects) qui sont grs ou non par le gestionnaire dentits. Lorsquelles sont gres, elles ont une identit de persistance et leur tat est synchronis avec la base de donnes. Lorsquelles ne le sont pas (elles sont, par exemple, dtaches du gestionnaire dentits), elles peuvent tre utilises comme nimporte quelle autre classe Java: ceci signifie que les entits ont un cycle de vie, comme le montre la Figure2.3. Lorsque vous crez une instance de lentit Book laide de loprateur new, lobjet existe en mmoire et JPA ne le connat pas (il peut mme finir par tre supprim par le ramasse-miettes); lorsquil devient gr par le gestionnaire dentits, son tat est associ et synchronis avec la table BOOK. Lappel de la mthode EntityManager.remove() supprime les donnes de la base, mais lobjet Java continue dexister en mmoire jusqu ce que le ramasse-miettes le dtruise. Les oprations qui sappliquent aux entits peuvent se classer en quatre catgories: persistance, mise jour, suppression et chargement, qui correspondent respectivement aux oprations dinsertion, de mise jour, de suppression et de slection dans

60

Java EE 6 et GlassFish 3

la base de donnes. Chaque opration a un vnement "Pre" et "Post" (sauf le chargement, qui na quun vnement "Post") qui peuvent tre intercepts par le gestionnaire dentits pour invoquer une mthode mtier.
Figure2.3 Cycle de vie dune entit.
Existe en mmoire

Dtach

Gr

Supprim

Base de donnes

Comme nous le verrons au Chapitre5, il existe donc des annotations @PrePersist, @PostPersist, etc. Ces annotations peuvent tre associes des mthodes dentits (appeles fonctions de rappel) ou des classes externes (appeles couteurs). Vous pouvez considrer les fonctions de rappel et les couteurs comme des triggers dune base de donnes relationnelle.

Rcapitulatif
Maintenant que vous connaissez un peu JPA, EclipseLink, les entits, le gestionnaire dentits et JPQL, rassemblons le tout pour crire une petite application qui stocke une entit dans une base de donnes. Nous allons donc crire une simple entit Book et une classe Main charge de stocker un livre. Nous la compilerons avec Maven2 et lexcuterons avec EclipseLink et une base de donnes cliente Derby. Pour montrer la simplicit des tests unitaires sur une entit, nous verrons galement comment crire une classe de test (BookTest) avec un cas de test JUnit4 et laide du mode intgr de Derby, qui nous permettra de stocker les donnes en utilisant une base de donnes en mmoire. Pour respecter la structure de rpertoires de Maven, les fichiers devront tre placs dans les rpertoires suivants:
src/main/java

pour lentit Book et la classe Main;

src/main/resources pour le fichier persistence.xml utilis par la classe Main;

Chapitre 2

Persistance en Java

61

src/test/java

pour la classe BookTest, qui servira aux tests unitaires;

src/test/resources pour le fichier persistence.xml utilis par les cas de test; pom.xml,

le Project Object Model (POM) de Maven, qui dcrit le projet et ses dpendances vis--vis dautres modules et composants externes.

criture de lentit Book

prsente dans le Listing2.3 doit tre dveloppe sous le rpertoire Elle a plusieurs attributs (un titre, un prix, etc.) de types diffrents (String, Float, Integer et Boolean) et certaines annotations JPA:
Book src/main/java.

Lentit

@Entity @Id

informe le fournisseur de persistance que cette classe est une entit et quil devra la grer. dfinit lattribut id comme tant la cl primaire. informe le fournisseur de persistance quil devra produire automatiquement la cl primaire laide des outils de la base de donnes sous-jacente.

@GeneratedValue

@Column

prcise que le titre ne pourra pas tre NULL lorsquil sera stock dans la base et modifie la longueur maximale par dfaut de la colonne description. dfinit une requte nomme qui utilise JPQL pour rcuprer tous les livres de la base.

@NamedQuery

Listing2.3: Une entit Book avec une requte nomme


package com.apress.javaee6.chapter02; @Entity @NamedQuery(name = "findAllBooks", query = "SELECT b FROM Book b") public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

62

Java EE 6 et GlassFish 3

Pour des raisons de lisibilit, nous avons omis ici les constructeurs, les getters et les setters de cette classe. Comme le montre ce code, hormis les quelques annotations, Book est un simple POJO. crivons maintenant une classe Main qui stockera un livre dans la base de donnes.
criture de la classe Main

La classe Main prsente dans le Listing2.4 se trouve dans le mme rpertoire que lentit Livre. Elle commence par crer une instance de Book (avec le mot-cl new de Java) puis initialise ses attributs. Vous remarquerez quil ny a rien de spcial ici: ce nest que du code Java traditionnel. Puis elle utilise la classe Persistence pour obtenir une instance dEntityManagerFactory afin de dsigner une unit de persistance appele chapter02PU que nous dcrirons plus tard dans la section "Unit de persistance pour la classe Main". Cette fabrique permet son tour de crer une instance dEntityManager (la variable em). Comme on la dj mentionn, le gestionnaire dentits est llment central de JPA car il permet de crer une transaction, de stocker lobjet Book laide de la mthode EntityManager.persist() puis de valider la transaction. la fin de la mthode main() on ferme les objets EntityManager et EntityManagerFactory afin de librer les ressources du fournisseur.
Listing2.4: Une classe Main pour stocker une entit Book
package com.apress.javaee6.chapter02; public class Main { public static void main(String[] args) { // Cre une instance de Book Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Comdie de science fiction"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); // Obtention dun gestionnaire dentits et dune transaction EntityManagerFactory emf = Persistence.createEntityManagerFactory("chapter02PU"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); // Stocke le livre dans la base tx.begin(); em.persist(book);

Chapitre 2

Persistance en Java

63

tx.commit(); em.close(); emf.close(); } }

L encore, nous avons omis la gestion des exceptions pour des raisons de lisibilit. Si une exception de persistance survenait, il faudrait annuler la transaction et enregistrer un message dans le journal.
Unit de persistance pour la classe Main

Comme vous pouvez le constater avec la classe Main, lobjet EntityManagerFactory a besoin dune unit de persistance appele chapter02PU qui doit tre dfinie dans le fichier persistence.xml situ dans le rpertoire src/main/resources/META-INF (voir Listing 2.5). Ce fichier, exig par la spcification de JPA, est important car cest lui qui relie le fournisseur JPA (EclipseLink dans notre cas) la base de donnes (Derby). Il contient toutes les informations ncessaires pour se connecter la base (cible, URL, pilote JDBC, nom et mot de passe de lutilisateur) et informe le fournisseur du mode de gnration de la base (create-tables signifie que les tables seront cres si elles nexistent pas). Llment <provider> dfinit le fournisseur de persistance EclipseLink ici.
Listing2.5: Le fichier persistence.xml utilis par la classe Main
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="chapter02PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>com.apress.javaee6.chapter02.Book</class> <properties> <property name="eclipselink.target-database" value="DERBY"/> <property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/> <property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/chapter02DB; create=true"/> <property name="eclipselink.jdbc.user" value="APP"/> <property name="eclipselink.jdbc.password" value="APP"/> <property name="eclipselink.ddl-generation" value="create-tables"/>

64

Java EE 6 et GlassFish 3

<property name="eclipselink.logging.level" value="INFO"/> </properties> </persistence-unit> </persistence>

Cette unit de persistance numre toutes les entits qui doivent tre gres par le gestionnaire dentits. Ici, le marqueur <class> dsigne lentit Book.
Compilation avec Maven

Vous disposez maintenant de tous les ingrdients pour lancer lapplication: lentit Book que vous voulez stocker, la classe Main qui effectue le travail laide dun gestionnaire dentits et lunit de persistance qui relie lentit la base de donnes Derby. Pour compiler ce code, nous utiliserons Maven au lieu dappeler directement le compilateur javac. Vous devez donc dabord crer un fichier pom.xml dcrivant le projet et ses dpendances (JPA, notamment). Vous devrez galement informer Maven que vous utilisez Java SE6 en configurant lextension maven-compiler-plugin comme cela est dcrit dans le Listing2.6.
Listing2.6: Fichier pom.xml de Maven pour compiler, construire, excuter et tester lapplication
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter02</artifactId> <version>1.0</version> <name>chapter02</name> <dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyclient</artifactId> <version>10.5.3.0</version> </dependency>

Chapitre 2

Persistance en Java

65

<dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>10.5.3.0</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.5</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Pour compiler le code, vous avez dabord besoin de lAPI de JPA, qui dfinit toutes les annotations et les classes qui se trouvent dans le paquetage javax.persistence. Vous obtiendrez ces classes dans une archive jar dsigne par lidentifiant dartfact javax.persistence et qui sera stocke dans le dpt Maven. Le runtime EclipseLink (cest--dire le fournisseur de persistance) est dfini dans lidentifiant dartfact eclipselink. Vous avez galement besoin des pilotes JDBC permettant de se connecter Derby. Lidentifiant dartfact derbyclient dsigne larchive jar qui contient le pilote JDBC pour se connecter Derby lorsquil sexcute en mode serveur (il est alors lanc dans un processus spar et coute sur un port), tandis que lidentifiant dartfact derby contient les classes pour utiliser Derby comme une base de donnes intgre. Notez que ce dernier est rserv aux tests (<scope>test</scope>) et dpend de JUnit4. Pour compiler les classes, ouvrez une fentre de commande dans le rpertoire racine contenant le fichier pom.xml, puis entrez la commande Maven suivante:
mvn compile

Vous devriez voir apparatre le message BUILD SUCCESSFUL vous informant que la compilation a russi. Maven cre alors un sous-rpertoire target contenant tous les fichiers class ainsi que le fichier persistence.xml.

66

Java EE 6 et GlassFish 3

Excution de la classe Main avec Derby

Avant dexcuter la classe Main, vous devez lancer Derby. Pour ce faire, le moyen le plus simple consiste se rendre dans le rpertoire %DERBY_HOME%\bin et lancer le script startNetworkServer.bat. Derby se lance et affiche les messages suivants sur la console:
2010-01-31 14:56:54.816 GMT : Le gestionnaire de scurit a t install au moyen de la stratgie de scurit de serveur de base. 2010-01-31 14:56:55.562 GMT : Apache Derby Serveur rseau - 10.5.3.0 (802917) dmarr et prt accepter les connexions sur le port 1527

Le processus Derby coute sur le port 1527 et attend que le pilote JDBC lui envoie une instruction SQL. Pour excuter la classe Main, vous pouvez utiliser linterprteur java ou la commande Maven suivante:
mvn exec:java -Dexec.mainClass="com.apress.javaee6.chapter02.Main"

Lorsque vous excutez la classe Main, plusieurs choses se passent. Tout dabord, Derby cre automatiquement la base de donnes chapter02tDB ds que lentit Book est initialise car nous avions ajout la proprit create=true lURL de JDBC dans le fichier persistence.xml:
<property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/chapter02DB;create=true"/>

Ce raccourci est trs pratique lorsque lon est en phase de dveloppement car nous navons pas besoin dcrire un script SQL pour crer la base. Puis la proprit eclipselink.ddl-generation demande EclipseLink de crer automatiquement la table LIVRE. Enfin, le livre est insr dans cette table (avec un identifiant produit automatiquement).
em.persist(book);

Utilisons maintenant les commandes Derby pour afficher la structure de la table: tapez la commande ij dans une fentre de commandes (comme on la expliqu plus haut, le rpertoire %DERBY_HOME%\bin doit avoir t ajout votre variable PATH). Cette commande lance linterprteur de Derby partir duquel vous pouvez excuter des commandes pour vous connecter la base, afficher les tables de la base chapter02DB (show tables), vrifier la structure de la table BOOK (describe book) et mme consulter son contenu laide dinstructions SQL comme SELECT * FROM BOOK.
C:\> ij version ij 10.5

Chapitre 2

Persistance en Java

67

ij> connect jdbc:derby://localhost:1527/chapter02DB; ij> show tables; TABLE_SCHEM |TABLE_NAME |REMARKS APP |BOOK | APP |SEQUENCE | ij> describe book; COLUMN_NAME |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL& --------------------------------------------------------------------------ID |BIGINT |0 |10 |19 |NULL |NULL |NO NBOFPAGE |INTEGER |0 |10 |10 |NULL |NULL |YES PRICE |DOUBLE |NULL|2 |52 |NULL |NULL |YES ILLUSTRATIONS |SMALLINT |0 |10 |5 |0 |NULL |YES DESCRIPTION |VARCHAR |NULL|NULL|2000 |NULL |4000 |YES ISBN |VARCHAR |NULL|NULL|255 |NULL |510 |YES TITLE |VARCHAR |NULL|NULL|255 |NULL |510 |NO

Comme nous avons utilis lannotation @GeneratedValue pour produire automatiquement un identifiant dans lentit Book, EclipseLink a cr une table de squence pour stocker la numrotation (table SEQUENCE). En tudiant la structure de la table BOOK, on constate que JPA a respect certaines conventions par dfaut pour nommer la table et ses colonnes daprs les noms de lentit et de ses attributs. Lannotation @Column a redfini certaines de ces conventions, comme la longueur de la colonne description, qui a t fixe 2000.
criture de la classe BookTest

On a reproch aux versions prcdentes dEntity CMP 2.x la complexit de mise en place des tests unitaires pour les composants persistants. Lun des atouts principaux de JPA est, justement, que lon peut aisment tester les entits sans avoir besoin dun serveur dapplication ou dune base de donnes. Que peut-on tester, alors ? Les entits nont gnralement pas besoin dtre testes isolment car la plupart des mthodes sur les entits sont de simples getters ou setters; elles contiennent peu de mthodes mtier. Vrifier quun setter affecte une valeur un attribut et que le getter correspondant permet de rcuprer cette mme valeur napporte pas grand-chose (sauf si cela permet de dtecter un effet de bord dans les getters ou les setters). Quen est-il des tests des requtes ? Certains dveloppeurs prtendront quil ne sagit pas de tests unitaires puisquil faut une vraie base de donnes pour les excuter. Effectuer des tests spars avec des objets factices demanderait beaucoup de travail. En outre, tester une entit lextrieur de tout conteneur (EJB ou conteneur de servlet) aurait des rpercussions sur le code car il faudrait modifier la gestion des transactions. Lutilisation dune base de donnes en mmoire et des transactions non JPA semble donc un bon compromis. Les oprations CRUD et les requtes JPQL

68

Java EE 6 et GlassFish 3

peuvent en effet tre testes avec une base de donnes trs lgre qui na pas besoin de sexcuter dans un processus distinct (il suffit dajouter un fichier jar au classpath). Cest de cette faon que nous excuterons notre classe BookTest, en utilisant le mode intgr de Derby. Maven utilise deux rpertoires diffrents: lun pour stocker le code de lapplication, un autre pour les classes de test. La classe BookTest, prsente dans le Listing2.7, est place dans le rpertoire src/test/java et teste que le gestionnaire dentits peut stocker un livre dans la base de donnes et le rcuprer ensuite.
Listing2.7: Classe de test qui cre un livre et rcupre tous les livres de la base de donnes
public class BookTest { private static EntityManagerFactory emf; private static EntityManager em; private static EntityTransaction tx; @BeforeClass public static void initEntityManager() throws Exception { emf = Persistence.createEntityManagerFactory("chapter02PU"); em = emf.createEntityManager(); } @AfterClass public static void closeEntityManager() throws SQLException { em.close(); emf.close(); } @Before public void initTransaction() { tx = em.getTransaction(); } @Test public void createBook() throws Exception { // Cration dune instance de Livre Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Comdie de science fiction"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false);

Chapitre 2

Persistance en Java

69

// Stocke le livre dans la base de donnes tx.begin(); em.persist(book); tx.commit(); assertNotNull("ID ne doit pas tre null", book.getId()); // Rcupre tous les livres de la base de donnes List<Book> books = em.createNamedQuery("findAllBooks").getResultList(); assertNotNull(book); } }

Comme la classe Main, BookTest doit crer une instance EntityManager laide dune fabrique EntityManagerFactory. Pour initialiser ces composants, nous nous servons des fixtures de JUnit4: les annotations @BeforeClass et @AfterClass permettent dexcuter un code une seule fois, avant et aprs lexcution de la classe cest donc lendroit idal pour crer et fermer une instance EntityManager. Lannotation @Before, quant elle, permet dexcuter un certain code avant chaque test cest l que nous crons une transaction. Le cas de test est reprsent par la mthode createBook() car elle est prcde de lannotation @Test de JUnit. Cette mthode stocke un livre (en appelant EntityManager.persist()) et vrifie avec assertNotNull que lidentifiant a bien t produit automatiquement par EclipseLink. En ce cas, la requte nomme findAllBooks est excute et lon vrifie que la liste renvoye nest pas null.
Unit de persistance pour la classe BookTest

Maintenant que la classe de test est crite, vous avez besoin dun autre fichier persistence.xml pour utiliser Derby intgr car le prcdent dfinissait un pilote JDBC et une URL de connexion Derby en mode serveur rseau. Le fichier src/ test/resources/META-INF/persistence.xml du Listing2.8 utilise au contraire un pilote JDBC pour le mode intgr.
Listing2.8: Fichier persistence.xml utilis par la classe BookTest
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="chapter02PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider

70

Java EE 6 et GlassFish 3

</provider> <class>com.apress.javaee6.chapter02.Book</class> <properties> <property name="eclipselink.target-database" value="DERBY"/> <property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/> <property name="eclipselink.jdbc.url" value="jdbc:derby:chap02DB;create=true"/> <property name="eclipselink.jdbc.user" value="APP"/> <property name="eclipselink.jdbc.password" value="APP"/> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.logging.level" value="FINE"/> </properties> </persistence-unit> </persistence>

Il y a dautres diffrences entre les deux fichiers persistence.xml. La valeur de ddl-generation est ici drop-and-create-tables au lieu de create-tables car, avant de tester, il faut supprimer et recrer les tables afin de repartir sur une structure de base de donnes propre. Notez galement que le niveau des journaux est FINE au lieu dINFO car cela permet dobtenir plus dinformations au cas o les tests choueraient.
Excution de la classe BookTest avec Derby intgr

Lexcution du test est trs simple puisquil suffit de se reposer sur Maven. Ouvrez une fentre de commande dans le rpertoire o se trouve le fichier pom.xml et tapez la commande suivante:
mvn test

Le niveau de journalisation des traces tant FINE, vous devriez voir apparatre de nombreuses informations indiquant que Derby cre une base et des tables en mmoire. Puis la classe BookTest est excute et Maven devrait vous informer du succs du test.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 9.415 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO]---------------------------------------------------------------[INFO] BUILD SUCCESSFUL [INFO]---------------------------------------------------------------[INFO] Total time: 19 seconds [INFO] Finished [INFO] Final Memory: 4M/14M [INFO]----------------------------------------------------------------

Chapitre 2

Persistance en Java

71

Rsum
Ce chapitre est un tour dhorizon rapide de JPA 2.0. Comme la plupart des autres spcifications de Java EE6, JPA utilise une architecture objet simple et abandonne le modle composant lourd de son anctre (EJB CMP 2.x). Ce chapitre a galement prsent les entits, qui sont des objets persistants utilisant des mtadonnes exprimes via des annotations ou un fichier XML. Dans la section "Rcapitulatif", nous avons vu comment lancer une application JPA avec EclipseLink et Derby. Les tests unitaires jouent un rle important dans les projets: avec JPA et des bases de donnes en mmoire comme Derby, la persistance peut dsormais tre teste trs simplement. Dans les chapitres suivants, vous en apprendrez plus sur les composants principaux de JPA. Le Chapitre3 expliquera comment associer les entits, les relations et lhritage une base de donnes. Le Chapitre4 sera consacr lAPI du gestionnaire dentits, la syntaxe de JPQL et lutilisation des requtes et des mcanismes de verrouillage. Le Chapitre5, le dernier de cette prsentation de JPA, expliquera le cycle de vie des entits et montrera comment ajouter une logique mtier dans les fonctions de rappel et les couteurs.

3
ORM: Object-Relational Mapping
Dans ce chapitre, nous passerons en revue les bases des ORM (Object-Relational Mapping), qui consistent essentiellement faire correspondre des entits des tables et des attributs des colonnes. Nous nous intresserons ensuite des associations plus complexes comme les relations, la composition et lhritage. Un modle objet est compos dobjets interagissant ensemble; or les objets et les bases de donnes utilisent des moyens diffrents pour stocker les informations sur ces relations (via des pointeurs ou des cls trangres). Les bases de donnes relationnelles ne disposent pas naturellement du concept dhritage et cette association entre objets et bases nest par consquent pas vidente. Nous irons donc dans les dtails et prsenterons des exemples qui montreront comment les attributs, les relations et lhritage peuvent tre traduits dun modle objet vers une base de donnes. Les chapitres prcdents ont montr que les annotations taient trs utilises dans ldition Entreprise depuis Java EE5 (essentiellement pour les EJB, JPA et les services web). JPA2.0 poursuit dans cette voie et introduit de nouvelles annotations de mapping (associations), ainsi que leurs quivalents XML. Mme si nous utiliserons surtout les annotations pour expliquer les diffrents concepts dassociations, nous prsenterons galement les associations au moyen de XML.

Association dune entit


Comme premier exemple, commenons par lassociation la plus simple possible. Dans le modle de persistance de JPA, une entit est un objet Java classique (POJO): ceci signifie quune entit est dclare, instancie et utilise comme nimporte quelle autre classe Java. Une entit possde des attributs (son tat) qui peuvent tre manipuls au moyen de getters et de setters. Chaque attribut est stock dans une colonne dune table. Le Listing3.1 prsente une entit simple.

74

Java EE 6 et GlassFish 3

Listing3.1: Exemple dentit Book


@Entity public class Book { @Id private private private private private private private

Long id; String title; Float price; String description; String isbn; Integer nbOfPage; Boolean illustrations;

public Book() { } // Getters, setters }

Cet exemple de code issu de lapplication CD-BookStore reprsente une entit Book dans laquelle on a omis les getters et les setters pour plus de clart. Comme vous pouvez le constater, part les annotations, cette entit ressemble exactement nimporte quelle classe Java: elle a plusieurs attributs (id, title, price, etc.) de diffrents types (Long, String, Float, Integer et Boolean), un constructeur par dfaut et des getters et setters pour chaque attribut. Les annotations vont permettre dassocier trs simplement cette entit une table dans une base de donnes. Tout dabord, la classe est annote avec @javax.persistence.Entity, ce qui permet au fournisseur de persistance de la reconnatre comme une classe persistance et non plus comme une simple classe POJO. Puis lannotation @javax.persistence.Id dfinit lidentifiant unique de lobjet. JPA tant destin associer des objets des tables relationnelles, les objets doivent possder un identifiant qui sera associ une cl primaire. Les autres attributs (title, price, description, etc.) ne sont pas annots et seront donc stocks dans la table en appliquant une association standard. Cet exemple de code ne contient que des attributs mais, comme nous le verrons au Chapitre 5, une entit peut galement avoir des mthodes mtier. Notez que cette entit Book est une classe Java qui nimplmente aucune interface et qui nhrite daucune classe. En fait, pour tre une entit, une classe doit respecter les rgles suivantes:

La classe de lentit doit tre annote par @javax.persistence.Entity (ou dnote comme telle dans le descripteur XML). Lannotation @javax.persistence.Id doit tre utilise pour dsigner une cl primaire simple.

Chapitre 3

ORM: Object-Relational Mapping

75

La classe de lentit doit possder un constructeur sans paramtre, public ou protg. Elle peut galement avoir dautres constructeurs. La classe de lentit doit tre une classe de premier niveau. Une numration ou une interface ne peut pas tre considre comme une entit. La classe de lentit ne peut pas tre finale et aucune mthode ou variable dinstance persistante ne peut tre finale non plus. Si une instance dentit doit tre passe par valeur sous forme dobjet dtach (via une interface distante, par exemple), la classe de lentit doit implmenter linterface Serializable.

Lentit Book du Listing3.1 respectant ces rgles simples, le fournisseur de persistance peut synchroniser les donnes entre les attributs de lentit et les colonnes de la table BOOK. Par consquent, si lattribut isbn est modifi par lapplication, la colonne ISBN le sera galement (si lentit est gre, si le contexte de transaction est actif, etc.). Comme le montre la Figure3.1, lentit Book est stocke dans une table BOOK dont chaque colonne porte le nom de lattribut correspondant de la classe (lattribut isbn de type String est associ une colonne ISBN de type VARCHAR). Ces rgles dassociations par dfaut sont un aspect important du principe appel "convention plutt que configuration" (ou "configuration par exception").
Figure3.1 Synchronisation des donnes entre lentit et la table.
<<entity>> Book
-id : Long -title : String -price : Float -description : String -nbOfPage : Integer -illustrations : Boolean +Book ()

Couche de persistance Couche base de donnes Association


+ID TITLE PRICE DESCRIPTION ISBN NBOFPAGE ILLUSTRATIONS bigint varchar(255) double varchar(2000) varchar(255) integer smallint

BOOK

Nullable = false Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true

76

Java EE 6 et GlassFish 3

Configuration par exception

Java EE 5 a introduit lide de configuration par exception. Ceci signifie que, sauf mention contraire, le conteneur ou le fournisseur doivent appliquer les rgles par dfaut. En dautres termes, fournir une configuration est une exception la rgle. Cette politique permet donc de configurer une application avec un minimum deffort. Revenons lexemple prcdent (celui du Listing 3.1). Sans annotation, lentit Book serait traite comme nimporte quel POJO et ne serait pas persistante cest la rgle: sans configuration spciale, le comportement par dfaut sapplique et il consiste videmment considrer que la classe Book est une classe comme les autres. Comme nous souhaitons modifier ce comportement, nous annotons la classe avec @ Entity. Il en va de mme pour lidentifiant: nous avons besoin dindiquer au fournisseur de persistance que cet attribut doit tre associ une cl primaire, et cest la raison pour laquelle nous lannotons avec @Id. Ce type de dcision caractrise bien la politique de configuration par exception: les annotations ne sont pas ncessaires dans le cas gnral ; elles ne sont utilises que pour outrepasser une convention. Ceci signifie donc que tous les autres attributs de notre classe seront associs selon les rgles par dfaut:

Le nom de lentit est associ un nom de table relationnelle (lentit Book sera donc associe une table BOOK). Si vous voulez lassocier une autre table, vous devrez utiliser lannotation @Table, comme nous le verrons dans la section "Associations lmentaires". Les noms des attributs sont associs des noms de colonnes (lattribut id, ou la mthode getId(), est associ une colonne ID). Si vous voulez changer ce comportement, vous devrez utiliser lannotation @Column. Ce sont les rgles JDBC qui sappliquent pour associer les types primitifs de Java aux types de donnes de la base. Ainsi, un String sera associ un VARCHAR, un Long un BIGINT, un Boolean un SMALLINT, etc. La taille par dfaut dune colonne associe un String est de 255caractres (VARCHAR(255)). Ces rgles par dfaut peuvent varier en fonction du SGBDR: un String est associ un VARCHAR avec Derby, mais un VARCHAR2 avec Oracle; de la mme faon, un Integer est associ un INTEGER avec Derby, mais un NUMBER avec Oracle. Les informations concernant la base de donnes sous-jacentes sont fournies par le fichier persistence.xml, que nous tudierons dans la section "Contexte de persistance" du Chapitre4.

Selon toutes ces rgles, lentit Book sera donc associe une table Derby ayant la structure dcrite dans le Listing3.2.

Chapitre 3

ORM: Object-Relational Mapping

77

Listing3.2: Structure de la table BOOK


CREATE TABLE BOOK ( ID BIGINT NOT NULL, TITLE VARCHAR(255), PRICE DOUBLE(52, 0), DESCRIPTION VARCHAR(255), ISBN VARCHAR(255), NBOFPAGE INTEGER, ILLUSTRATIONS SMALLINT, PRIMARY KEY (ID) );

Cest donc un exemple dassociation trs simple. Les relations et lhritage ont galement des rgles dassociation par dfaut, que nous tudierons dans la section "Association des relations". La plupart des fournisseurs de persistance, dont EclipseLink, permettent de produire automatiquement la base de donnes partir des entits. Cette fonctionnalit est tout spcialement pratique lorsque lon est en phase de dveloppement car, avec uniquement les rgles par dfaut, on peut associer trs simplement les donnes en se contentant des annotations @Entity et @Id. Cependant, la plupart du temps, nous devrons nous connecter un SGBDR classique ou suivre des conventions de nommage strictes: cest la raison pour laquelle JPA dfinit un nombre important dannotations (ou leurs quivalents XML) vous pourrez ainsi personnaliser chaque partie de lassociation (les noms des tables et des colonnes, les cls primaires, la taille des colonnes, les colonnes NULL ou NOT NULL, etc.).

Associations lmentaires
Dimportantes diffrences existent entre la gestion des donnes par Java et par un SGBDR. En Java, nous utilisons des classes pour dcrire la fois les attributs qui contiennent les donnes et les mthodes qui accdent et manipulent ces donnes. Lorsquune classe a t dfinie, nous pouvons crer autant dinstances que ncessaire laide du mot-cl new. Dans un SGBDR, en revanche, seules les donnes sont stockes pas les comportements (exception faite des triggers et des procdures stockes) , et la structure du stockage est totalement diffrente de la structure des objets puisquelle utilise une dcomposition en lignes et en colonnes. Lassociation dobjets Java une base de donnes sous-jacente peut donc tre simple et se contenter des rgles par dfaut ; parfois, cependant, ces rgles peuvent ne pas convenir aux besoins, auquel cas nous sommes obligs de les outrepasser. Les annotations

78

Java EE 6 et GlassFish 3

des associations lmentaires permettent de remplacer les rgles par dfaut pour la table, les cls primaires et les colonnes, et de modifier certaines conventions de nommage ou de contenu des colonnes (valeurs non nulles, longueur, etc.).
Tables

La convention tablit que les noms de lentit et de la table sont identiques (une entit Book est associe une table BOOK, une entit AncientBook, une table ANCIENTBOOK, etc.). Toutefois, si vous le souhaitez, vous pouvez associer vos donnes une table diffrente, voire associer une mme entit plusieurs tables.
@Table

Lannotation @javax.persistence.Table permet de modifier les rgles par dfaut pour les tables. Vous pouvez, par exemple, indiquer le nom de la table dans laquelle vous voulez stocker vos donnes, le catalogue et le schma de la base. Le Listing3.3 montre comment associer la table T_BOOK lentit Book.
Listing3.3: Association de lentit Book la table T_BOOK
@Entity @Table(name = "t_book") public class Book { @Id private private private private private private private

Long id; String title; Float price; String description; String isbn; Integer nbOfPage; Boolean illustrations;

public Book() { } // Getters, setters }

INFO Dans lannotation @Table, le nom de la table est en minuscules (t_book). Par dfaut, la plupart des SGBDR lui feront correspondre un nom en majuscules (cest notamment le cas de Derby), sauf si vous les configurez pour quils respectent la casse.

Chapitre 3

ORM: Object-Relational Mapping

79

@SecondaryTable

Jusqu maintenant, nous avons toujours suppos quune entit ntait associe qu une seule table, galement appele table primaire. Si lon a dj un modle de donnes, en revanche, on voudra peut-tre dissminer les donnes sur plusieurs tables, ou tables secondaires. Cette annotation permet de mettre en place cette configuration. permet dassocier une table secondaire une entit, alors que @SecondaryTables (avec un "s") en associe plusieurs. Vous pouvez distribuer les donnes dune entit entre les colonnes de la table primaire et celles des tables secondaires en dfinissant simplement les tables secondaires avec des annotations, puis en prcisant pour chaque attribut la table dans laquelle il devra tre stock ( laide de lannotation @Column, que nous dcrirons dans la section "Attributs"). Le Listing 3.4 montre comment rpartir les attributs dune entit Address entre une table primaire et deux tables secondaires.
@SecondaryTable

Listing3.4: Les attributs de lentit Address sont rpartis dans trois tables diffrentes
@Entity @SecondaryTables({ @SecondaryTable(name = "city"), @SecondaryTable(name = "country") }) public class Address { @Id private Long id; private String street1; private String street2; @Column(table = "city") private String city; @Column(table = "city") private String state; @Column(table = "city") private String zipcode; @Column(table = "country") private String country; } // Constructeurs, getters, setters

Par dfaut, les attributs de lentit Address seraient associs la table primaire (qui sappelle ADDRESS par dfaut). Lannotation @SecondaryTables prcise quil y a deux tables secondaires: CITY et COUNTRY. Vous devez ensuite indiquer dans quelle table secondaire stocker chaque attribut ( laide de lannotation @Column(table="city") ou @Column(table="country")). Le rsultat, comme le montre la Figure3.2, est la cration de trois tables se partageant les diffrents attributs mais ayant la mme cl

80

Java EE 6 et GlassFish 3

primaire (afin de pouvoir les joindre). Noubliez pas que Derby met en majuscules (CITY) les noms de tables en minuscules (villes).
Figure3.2 Lentit Address est associe trois tables.
<<entity>> Address
-id : Long -street1 : String -street2 : String -city : String -zipcode : String -country : String +Address() +#ID COUNTRY +ID STREET1 STREET2 +#ID CITY STATE ZIPCODE bigint

COUNTRY

Nullable = false Nullable = true Nullable = false Nullable = true Nullable = true Nullable = false Nullable = true Nullable = true Nullable = true

varchar(255) bigint varchar(255) varchar(255) bigint varchar(255) varchar(255) varchar(255)

ADDRESS

CITY

Comme vous lavez srement compris, la mme entit peut contenir plusieurs annotations. Si vous voulez renommer la table primaire, vous pouvez donc ajouter une annotation @Table. Cest ce que nous faisons dans le Listing3.5.
Listing3.5: La table primaire est renomme en T_ADDRESS
@Entity @Table(name = "t_address") @SecondaryTables({ @SecondaryTable(name = "t_city"), @SecondaryTable(name = "t_country") }) public class Address { } // Attributs, constructeur, getters, setters

INFO Vous devez tre conscient de limpact des tables secondaires sur les performances car, chaque fois que vous accderez une entit, le fournisseur de persistance devra accder plusieurs tables et les joindre. En revanche, les tables secondaires peuvent tre intressantes si vous avez des attributs de grande taille, comme des BLOB (Binary Large Objects), car vous pourrez les isoler dans une table part.

Cls primaires

Dans les bases de donnes relationnelles, une cl primaire identifie de faon unique chaque ligne dune table. Cette cl peut tre une simple colonne ou un ensemble

Chapitre 3

ORM: Object-Relational Mapping

81

de colonnes. Les cls primaires doivent videmment tre uniques (et la valeur NULL nest pas autorise). Des exemples de cls primaires classiques sont un numro de client, un numro de tlphone, un numro de commande et un ISBN. JPA exige que les entits aient un identifiant associ une cl primaire qui suivra les mmes rgles: identifier de faon unique une entit laide dun simple attribut ou dun ensemble dattributs (cl compose). Une fois affecte, la valeur de la cl primaire dune entit ne peut plus tre modifie.
@Id et @GeneratedValue

Une cl primaire simple (non compose) doit correspondre un seul attribut de la classe de lentit. Lannotation @Id que nous avons dj rencontre sert indiquer une cl simple. Lattribut qui servira de cl doit tre de lun des types suivants:

Types primitifs de Java. byte, int, short, long, char. Classes enveloppes des types primitifs. Byte, Integer, Short, Long, Character. Tableau de types primitifs ou de classes enveloppes. int[], Integer[], etc. Chane, nombre ou dates. util.Date, java.sql.Date.
java.lang.String, java.math.BigInteger, java.

Lorsque lon cre une entit, la valeur de cet identifiant peut tre produite manuellement par lapplication, ou automatiquement par le fournisseur de persistance si lon prcise lannotation @GeneratedValue. Celle-ci peut avoir quatre valeurs:
SEQUENCE et IDENTITY prcisent, respectivement, une squence SQL de la base de

donnes ou une colonne identit.


TABLE

demande au fournisseur de persistance de stocker le nom de la squence et sa valeur courante dans une table et dincrmenter cette valeur chaque fois quune nouvelle instance de lentit est stocke dans la base. Derby, par exemple, cre une table SEQUENCE de deux colonnes: une pour le nom de la squence (qui est arbitraire) et lautre pour la valeur (un entier incrment automatiquement par Derby). demande que la gnration dune cl seffectue automatiquement par la base de donnes sous-jacente, qui est libre de choisir la technique la plus approprie. Cest la valeur par dfaut de lannotation @GeneratedValue.

AUTO

En labsence de @GeneratedValue, lapplication est responsable de la production des identifiants laide dun algorithme qui devra renvoyer une valeur unique. Lecode

82

Java EE 6 et GlassFish 3

du Listing 3.6 montre comment obtenir automatiquement un identifiant. GenerationType.AUTO tant la valeur par dfaut de lannotation, nous aurions pu omettre llment strategy. Notez galement que lattribut id est annot deux fois: avec @Id et avec @GeneratedValue.
Listing3.6: Lentit Book avec un identifiant produit automatiquement
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private Float price; private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters

Cls primaires composes

Lorsque lon associe des entits, il est conseill de ddier une seule colonne la cl primaire. Dans certains cas, toutefois, on est oblig de passer par une cl primaire compose (pour, par exemple, crer une association avec une base de donnes existante ou lorsque les cls primaires doivent respecter une convention interne lentreprise une date et un code pays, ou une tiquette temporelle, par exemple). Dans ce cas, nous devons crer une classe de cl primaire pour reprsenter la cl primaire compose. Pour ce faire, nous disposons de deux annotations pour cette classe, en fonction de la faon dont on souhaite structurer lentit: @EmbeddedId et @IdClass. Comme nous le verrons, le rsultat final est le mme on aboutira au mme schma de base de donnes mais cela modifiera lgrement la faon dont on interrogera lentit. Lapplication CD-BookStore, par exemple, doit frquemment poster des articles sur la page daccueil pour signaler de nouveaux livres, titres musicaux ou artistes. Ces articles ont un contenu, un titre et, comme ils sont crits dans des langues diffrentes, un code langue (EN pour langlais, FR pour le franais, etc.). La cl primaire des articles pourrait donc tre compose du titre et du code langue car un article peut tre traduit en plusieurs langues tout en gardant son titre initial. La classe de cl primaire NewsId sera donc compose de deux attributs de type String: title et language. Pour pouvoir grer les requtes et les collections internes, les classes de cls

Chapitre 3

ORM: Object-Relational Mapping

83

primaires doivent redfinir les mthodes equals() et hashCode(); en outre, leurs attributs doivent tre de lun des types dj mentionns. Elles doivent galement tre publiques et implmenter Serializable si elles doivent traverser des couches de larchitecture (elles peuvent tre gres dans la couche de persistance et tre utilises dans la couche prsentation, par exemple). Enfin, elles doivent possder un constructeur par dfaut (sans paramtre).
@EmbeddedId

Comme nous le verrons plus loin, JPA utilise diffrentes sortes dobjets intgrs (embedded). Pour faire court, un objet intgr na pas didentit (il na pas de cl primaire) et ses attributs sont stocks dans des colonnes de la table associe lentit qui le contient. Le Listing3.7 prsente la classe NewsId comme une classe intgrable (embeddable). Il sagit simplement dun objet intgr (annot avec @Embeddable) compos de deux attributs (title et language). Cette classe doit avoir un constructeur par dfaut, des getters, des setters et redfinir equals() et hashCode(). Vous remarquerez que la classe na pas didentit par elle-mme (aucune annotation @Id): cest ce qui caractrise un objet intgrable.
Listing3.7: La classe de cl primaire est annote par @Embeddable
@Embeddable public class NewsId { private String title; private String language; // Constructeurs, getters, setters, equals et hashcode }

Lentit News, prsente dans le Listing3.8, doit maintenant intgrer la classe de cl primaire NewsId avec lannotation @EmbeddedId. Toutes les annotations @EmbeddedId doivent dsigner une classe intgrable annote par @Embeddable.
Listing3.8: Lentit intgre la classe de cl primaire avec @EmbeddedId
@Entity public class News { @EmbeddedId private NewsId id; private String content; // Constructeurs, getters, setters }

84

Java EE 6 et GlassFish 3

Dans le prochain chapitre, nous verrons plus prcisment comment retrouver les entits laide de leur cl primaire, mais le Listing3.9 prsente le principe gnral: la cl primaire tant une classe avec un constructeur, vous devez dabord linstancier avec les valeurs qui forment la cl, puis passer cet objet au gestionnaire dentits (lattribut em).
Listing3.9: Code simplifi permettant de retrouver une entit partir de sa cl primaire compose
NewsId cle = new NewsId("Richard Wright est mort", "FR") News news = em.find(News.class, cle);

@IdClass

Lautre mthode pour dclarer une cl primaire compose consiste utiliser lannotation @IdClass. Cette approche est diffrente de la prcdente car, ici, chaque attribut de la classe de la cl primaire doit galement tre dclar dans la classe entit et annot avec @Id. La cl primaire de lexemple du Listing3.10 est maintenant un objet classique qui ne ncessite aucune annotation.
Listing3.10: La classe cl primaire nest pas annote
public class NewsId { private String title; private String language; // Constructeurs, getters, setters, equals et hashcode }

Comme le montre le Listing3.11, lentit News doit simplement dfinir la classe de la cl primaire laide de lannotation @IdClass et annoter chaque attribut de la cl avec @Id. Pour stocker lentit News, vous devrez maintenant donner une valeur aux attributs title et language.
Listing3.11: Lentit dfinit sa classe de cl primaire avec lannotation @IdClass
@Entity @IdClass(NewsId.class) public class News { @Id private String title; @Id private String language;

Chapitre 3

ORM: Object-Relational Mapping

85

private String content; // Constructeurs, getters, setters, equals et hashcode }

Les deux approches, @EmbeddedId et @IdClass, donneront la mme structure de table: celle du Listing3.12. Les attributs de lentit et de la cl primaire se retrouveront bien dans la mme table et la cl primaire sera forme des attributs de la classe cl primaire (title et language).
Listing3.12: Dfinition de la table NEWS avec une cl primaire compose
create table NEWS ( CONTENT VARCHAR(255), TITLE VARCHAR(255) not null, LANGUAGE VARCHAR(255) not null, primary key (TITLE, LANGUAGE) );

Lapproche @IdClass est plus sujette aux erreurs car vous devez dfinir chaque attribut de la cl primaire la fois dans la classe de la cl primaire et dans lentit, en vous assurant dutiliser les mmes noms et les mmes types. Lavantage est que vous navez pas besoin de modifier le code de la classe de la cl primaire. Vous pourriez, par exemple, utiliser une classe existante que vous navez pas le droit de modifier. La seule diffrence visible est la faon dont vous ferez rfrence lentit dans JPQL. Dans le cas de @IdClass, vous utiliseriez un code comme celui-ci:
select n.title from News n

Alors quavec @EmbeddedId vous cririez:


select n.newsId.title from News n

Attributs

Une entit doit possder une cl primaire (simple ou compose) pour tre identifiable dans une base de donnes relationnelle. Elle dispose galement de toutes sortes dattributs qui forment son tat, qui doit galement tre associ la table. Cet tat peut contenir quasiment tous les types Java que vous pourriez vouloir associer:

types primitifs de Java (int, double, (Integer, Double, Float, etc.);

float,

etc.) et leurs classes enveloppes

tableaux doctets ou de caractres (byte[], Byte[], char[], Character[]);

86

Java EE 6 et GlassFish 3

chanes, grands nombres et types temporels (java.lang.String, java.math. BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp); types numrs et types implmentant linterface lutilisateur; collection de types de base et de types intgrables.
Serializable,

dfinis par

Bien sr, une entit peut galement avoir des attributs entits, collections dentits ou dinstances de classes intgrables. Ceci implique dintroduire des relations entre les entits (que nous tudierons dans la section "Association des relations"). Comme nous lavons vu, en vertu de la configuration par exception, les attributs sont associs selon des rgles par dfaut. Parfois, cependant, vous aurez besoin dadapter certaines parties de cette association: cest l que les annotations JPA (ou leurs quivalents XML) entrent une nouvelle fois en jeu.
@Basic

Lannotation @javax.persistence.Basic (voir Listing3.13) est le type dassociation le plus simple avec une colonne dune table car il redfinit les options de base de la persistance.
Listing3.13: lments de lannotation @Basic
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Basic { FetchType fetch() default EAGER; boolean optional() default true; }

Cette annotation a deux paramtres : optional et fetch. Le premier indique si la valeur de lattribut peut tre null il est ignor pour les types primitifs. Le second peut prendre deux valeurs, LAZY ou EAGER: il indique au fournisseur de persistance que les donnes doivent tre rcupres de faon "paresseuse" (uniquement lorsque lapplication en a besoin) ou "immdiate" (lorsque lentit est charge par le fournisseur). Considrons, par exemple, lentit Track du Listing3.14. Un album CD est constitu de plusieurs pistes ayant chacune un titre, une description et un fichier .WAV dune certaine dure. Ce dernier est un BLOB qui peut occuper plusieurs mgaoctets. Lorsque nous accdons lentit Track, nous ne voulons pas charger immdiatement le fichier WAV : nous annotons donc lattribut avec @Basic(fetch =

Chapitre 3

ORM: Object-Relational Mapping

87

pour que ces donnes ne soient lues dans la base que lorsquelles seront vraiment ncessaires (lorsque nous accderons lattribut wav via son getter, par exemple).
FetchType.LAZY)

Listing3.14: Lentit Track avec un chargement paresseux de lattribut wav


@Entity public class Track { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private Float duration; @Basic(fetch = FetchType.LAZY) @Lob private byte[] wav; @Basic(optional = true) private String description; // Constructeurs, getters, setters }

Notez que lattribut wav de type byte[] est galement annot par @Lob afin que sa valeur soit stocke comme un LOB (Large Object) les colonnes pouvant stocker ces types de gros objets ncessitent des appels JDBC spciaux pour tre accessibles partir de Java. Pour en informer le fournisseur, il faut donc ajouter une annotation @Lob lassociation de base.
@Column

Lannotation @javax.persistence.Column dfinit les proprits dune colonne. Grce elle, nous pouvons modifier le nom de la colonne (qui, par dfaut, est le mme que celui de lattribut), sa taille et autoriser (ou non) la colonne tre NULL, unique, modifiable ou utilisable dans une instruction INSERT de SQL. Le Listing3.15 montre les diffrents lments de son API, avec leurs valeurs par dfaut.
Listing3.15: lments de lannotation @Column
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Column { String name() default ""; boolean unique() default false; boolean nullable() default true; boolean insertable() default true;

88

Java EE 6 et GlassFish 3

boolean updatable() default true; String columnDefinition() default ""; String table() default ""; int length() default 255; int precision() default 0; // prcision dcimale int scale() default 0; // chelle dcimale }

Pour redfinir lassociation par dfaut de lentit Book initiale, nous pouvons utiliser de diffrentes faons lannotation @Column (voir Listing3.16). Ici, nous modifions les noms des colonnes associes aux attributs title et nbOfPage, pour lesquelles nous nautorisons pas les valeurs NULL; nous prcisons galement la longueur de la colonne associe description.
Listing3.16: Personnalisation de lassociation de lentit Book
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "book_title", nullable = false, updatable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; @Column(name = "nb_of_page", nullable = false) private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Lentit Book du Listing3.6 sera donc associe la table dfinie dans le Listing3.17.
Listing3.17: Dfinition de la table BOOK
create table BOOK ( ID BIGINT not null, BOOK_TITLE VARCHAR(255) not null, PRICE DOUBLE(52, 0), DESCRIPTION VARCHAR(2000), ISBN VARCHAR(255), NB_OF_PAGE INTEGER not null, ILLUSTRATIONS SMALLINT, primary key (ID) );

Chapitre 3

ORM: Object-Relational Mapping

89

La plupart des lments de lannotation @column influent sur lassociation. Si lon fixe 2000 la longueur de lattribut description, par exemple, la taille de la colonne correspondante sera galement de 2000. Par dfaut, updatable et insertable valent true, ce qui signifie que lon peut insrer ou modifier nimporte quel attribut dans la base de donnes. En les mettant false, on demande au fournisseur de persistance de garantir quil ninsrera ni ne modifiera les donnes des colonnes associes ces attributs lorsque lentit sera modifie. Notez que ceci nimplique pas que lentit ne pourra pas tre modifie en mmoire elle pourra ltre mais, en ce cas, elle ne sera plus synchronise avec la base car linstruction SQL qui sera produite (INSERT ou UPDATE) ne portera pas sur ces colonnes.
@Temporal

En Java, vous pouvez utiliser java.util.Date et java.util.Calendar pour stocker des dates puis obtenir des reprsentations diffrentes, comme une date, une heure ou des millisecondes. Pour utiliser une date avec un ORM, vous pouvez utiliser lannotation @javax.persistence.Temporal, qui a trois valeurs possibles: DATE, TIME ou TIMESTAMP. Le Listing3.18, par exemple, dfinit une entit Customer contenant une date de naissance et un attribut technique qui stocke le moment exact o ce client a t ajout au systme ( laide dune valeur TIMESTAMP).
Listing3.18: Entit Customer avec deux attributs @Temporal
@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Temporal(TemporalType.DATE) private Date dateOfBirth; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; // Constructeurs, getters et setters }

Lentit Customer du Listing3.18 sera associe la table dcrite dans le Listing3.19. Lattribut dateOfBirth est associ une colonne de type DATE et lattribut creationDate, une colonne de type TIMESTAMP.

90

Java EE 6 et GlassFish 3

Listing3.19: Dfinition de la table CUSTOMER


create table CUSTOMER ( ID BIGINT not null, FIRSTNAME VARCHAR(255), LASTNAME VARCHAR(255), EMAIL VARCHAR(255), PHONENUMBER VARCHAR(255), DATEOFBIRTH DATE, CREATIONDATE TIMESTAMP, primary key (ID) );

@Transient

Avec JPA, tous les attributs dune classe annote par @Entity sont automatiquement associs une table. Si vous ne souhaitez pas associer un attribut particulier, utilisez lannotation @javax. persistence.Transient. Ajoutons, par exemple, un attribut age lentit Customer (voir Listing 3.20) : lge pouvant tre automatiquement calcul partir de la date de naissance, il nest pas ncessaire de stocker cet attribut, qui peut donc tre dclar comme transitoire.
Listing3.20: Entit Customer avec un ge transitoire
@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Temporal(TemporalType.DATE) private Date dateOfBirth; @Transient private Integer age; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; } // Constructeurs, getters et setters

Cet attribut naura pas de colonne AGE associe.


@Enumerated

Java SE 5 a introduit les numrations, qui sont si souvent utilises quelles font partie de la vie du dveloppeur. Les valeurs dune numration sont des constantes

Chapitre 3

ORM: Object-Relational Mapping

91

auxquelles est implicitement associ un numro dtermin par leur ordre dapparition dans lnumration. Ce numro ne peut pas tre modifi en cours dexcution mais sert stocker la valeur du type numr dans la base de donnes. Le Listing3.21 montre une numration de types de cartes de crdit.
Listing3.21: numration de types de cartes de crdit
public enum CreditCardType { VISA, MASTER_CARD, AMERICAN_EXPRESS }

Les numros affects lors de la compilation aux valeurs de ce type numr seront 0 pour VISA, 1 pour MASTER_CARD et 2 pour AMERICAN_EXPRESS. Par dfaut, les fournisseurs de persistance associeront ce type numr la base de donnes en supposant que la colonne est de type Integer. Le Listing3.22 montre une entit CreditCard qui utilise lnumration prcdente avec une association par dfaut.
Listing3.22: Association dun type numr des numros
@Entity @Table(name = "credit_card") public class CreditCard { @Id private private private private // }

String number; String expiryDate; Integer controlNumber; CreditCardType creditCardType;

Constructeurs, getters et setters

Les rgles par dfaut feront que lnumration sera associe une colonne de type entier et tout ira bien. Imaginons maintenant que nous ajoutions une nouvelle constante au dbut de lnumration. Laffectation des numros dpendant de lordre dapparition des constantes, les valeurs dj stockes dans la base de donnes ne correspondront plus lnumration. Une meilleure solution consiste donc stocker le nom de la constante la place de son numro dordre. Cest ce que fait le Listing3.23 laide de lannotation @Enumerated avec la valeur STRING (sa valeur par dfaut est ORDINAL).

92

Java EE 6 et GlassFish 3

Listing3.23: Association dun type numr avec une chane


@Entity @Table(name = "credit_card") public class CreditCard { @Id private String number; private String expiryDate; private Integer controlNumber; @Enumerated(EnumType.STRING) private CreditCardType creditCardType; // } Constructeurs, getters et setters

Dsormais, la colonne CREDITCARDTYPE de la table sera de type VARCHAR et une carte Visa sera stocke sous la forme "VISA".
Types daccs

Pour linstant, nous navons vu que des annotations de classes (@Entity ou @Table) et dattributs (@Basic, @Column, @Temporal, etc.), mais les annotations qui sappliquent un attribut (accs au champ) peuvent galement tre places sur la mthode getter correspondante (accs la proprit). Lannotation @Id, par exemple, peut tre affecte lattribut id ou la mthode getId(). Il sagit surtout ici dune question de got personnel et nous prfrons utiliser les accs aux proprits (getters annots) car nous trouvons le code plus lisible: nous pouvons lire rapidement les attributs dune entit sans tre perturbs par les annotations (dans ce livre, toutefois, nous avons dcid dannoter les attributs afin dviter de devoir alourdir les listings par les codes des getters). Dans certains cas comme lhritage, cependant, ce nest plus simplement une affaire de got car cela peut avoir un impact sur lassociation.
INFO Java dfinit un champ comme un attribut dinstance. Une proprit est un champ avec un accesseur (getter et setter) respectant la convention des Java beans (le nom de la mthode daccs est de la forme getXXX, setXXX ou isXXX si elle renvoie un Boolean).

Lorsque lon choisit entre un accs au champ (attribut) ou la proprit (getter), on choisit un type daccs. Par dfaut, cest un type daccs simple qui sapplique une entit: il peut sagir dun accs au champ ou la proprit, mais pas les deux.

Chapitre 3

ORM: Object-Relational Mapping

93

La spcification indique que le comportement dune application qui mlangerait les emplacements des annotations sur les champs et les proprits sans prciser explicitement le type daccs est indfini. Lorsque lon utilise un accs aux champs (voir Listing3.24), le fournisseur de persistance associe les attributs. Toutes les variables dinstance qui ne sont pas annotes par @Transient sont persistantes.
Listing3.24: Entit Customer avec des champs annots
@Entity public class Customer { @Id @GeneratedValue private Long id; @Column(nom = "first_name", nullable = false, length = 50) private String firstName; @Column(nom = "last_name", nullable = false, length = 50) private String lastName; private String email; @Column(nom = "phone_number", length = 15) private String phoneNumber; // Constructeurs, getters et setters }

Lorsque lon utilise un accs aux proprits, comme dans le Listing3.25, le fournisseur de persistance accde ltat persistant via les mthodes getter et lassociation repose sur ces getters plutt que sur les attributs. Tous les getters non annots par @Transient sont persistants.
Listing3.25: Entit Client avec des proprits annotes
@Entity public class Customer { private private private private private Long id; String firstName; String lastName; String email; String phoneNumber;

// Constructeur... @Id @GeneratedValue public Long getId() { return id; }

94

Java EE 6 et GlassFish 3

@Column(nom = "first_name", nullable = false, length = 50) public String getfirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Column(nom = "last_name", nullable = false, length = 50) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Column(name = "phone_number", length = 555) public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }

En termes dassociations, les deux entits des Listings3.24 et 3.25 sont en tout point identiques car les noms des attributs sont les mmes que ceux des getters. Au lieu dutiliser le type daccs par dfaut, vous pouvez galement le prciser explicitement avec lannotation @javax.persistence.Access. Cette annotation a deux valeurs possibles, FIELD ou PROPERTY, et peut tre utilise sur lentit elle-mme et/ou sur chaque attribut ou getter. Lorsque @Access(AccessType. FIELD) est appliqu lentit, par exemple, seules les annotations dassociations places sur les attributs seront prises en compte par le fournisseur de persistance. Ilest galement possible dannoter avec @Access(AccessType.PROPERTY) des getters individuels pour effectuer des accs par proprit. Les types daccs explicites peuvent tre trs pratiques (avec les objets intgrables et lhritage, par exemple), mais leur mlange provoque souvent des erreurs. Le Listing3.26 montre ce qui pourrait se passer si vous mlangez ces deux types daccs.

Chapitre 3

ORM: Object-Relational Mapping

95

Listing3.26: Entit Customer avec types daccs explicites


@Entity @Access(AccessType.FIELD) public class Customer { @Id @GeneratedValue private Long id; @Column(nom = "first_name", nullable = false, length = 50) private String firstName; @Column(nom = "last_name", nullable = false, length = 50) private String lastName; private String email; @Column(nom = "phone_number", length = 15) private String phoneNumber; // Constructeurs, getters et setters @Access(AccessType.PROPERTY) @Column(nom = "phone_number", length = 555) public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this. phoneNumber = phoneNumber; } }

Cet exemple dfinit explicitement le type daccs FIELD au niveau de lentit, ce qui indique au gestionnaire de persistance quil ne doit traiter que les annotations sur les attributs. phoneNumber est annot par @Column, qui limite sa taille 15caractres. En lisant ce code, on pourrait sattendre ce que le type de la colonne correspondante dans la base de donnes soit VARCHAR(15), mais ce ne sera pas le cas. En effet, le type daccs a t explicitement modifi pour la mthode getter getPhoneNumber(): la longueur dun numro de tlphone dans la base sera donc de 555caractres. Ici, lAccessType.FIELD de lentit a t cras par AccessType.PROPERTY et la colonne sera donc de type VARCHAR(555).
Collections de types de base

Les collections sont trs utilises en Java. Dans cette section, nous tudierons les relations entre entits (qui peuvent tre des collections dentits): essentiellement, ceci signifie quune entit contient une collection dautres entits ou dobjets intgrables. En terme dassociation, chaque entit est associe sa propre table et lon cre des rfrences entre les cls primaires et les cls trangres. Comme vous le savez, une

96

Java EE 6 et GlassFish 3

entit est une classe Java avec une identit et de nombreux autres attributs: mais comment faire pour stocker une simple collection de types Java comme des String et/ou des Integer ? Depuis JPA 2.0, il nest plus ncessaire de crer une classe distincte car on dispose des annotations @ElementCollection et @CollectionTable. Lannotation @ElementCollection indique quun attribut de type java.util.Collection contient des types Java tandis que @CollectionTable permet de modifier les dtails de la table de la collection son nom, par exemple. Si cette dernire est omise, le nom de la table sera form par la concatnation du nom de lentit conteneur et de celui de lattribut collection, spars par un blanc soulign ("_", ou underscore). Utilisons une nouvelle fois lentit Book et ajoutons-lui un attribut pour stocker des tags. De nos jours, les tags et les nuages de tags sont partout et sont trs pratiques pour trier les donnes: dans notre exemple, nous voulons nous en servir pour dcrire un livre et le retrouver rapidement. Un tag ntant quune simple chane, lentit Book contiendra donc une collection de chanes pour stocker ces informations, comme le montre le Listing3.27.
Listing3.27: Lentit Book contient une collection de chanes
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private Float price; private String description; private String isbn; private Integer nbDePages; private Boolean illustrations; @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name="Tag") @Column(name = "Value") private ArrayList<String> tags; // Constructeurs, getters et setters

Lannotation @ElementCollection informe le fournisseur de persistance que lattribut tags est une liste de chanes qui devra tre rcupre de faon paresseuse. En labsence de @CollectionTable, le nom de la table sera BOOK_TAGS au lieu du nom prcis dans llment name de lannotation (name = "Tag"). Vous remarquerez que nous avons ajout une annotation @Column supplmentaire pour renommer la colonne en Value. Le rsultat obtenu est reprsent par la Figure3.3.

Chapitre 3

ORM: Object-Relational Mapping

97

+ID TITLE PRICE DESCRIPTION ISBN NBOFPAGE ILLUSTRATIONS

bigint varchar(255) double varchar(2000) varchar(255) integer smallint

BOOK

Nullable = false Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true

#BOCK ID VALUE

bigint varchar(255)

TAG

Nullable = false Nullable = true

Figure3.3 Relation entre les tables BOOK et TAG.

INFO Ces annotations nexistaient pas en JPA 1.0. Cependant, il tait possible de stocker une liste de types primitifs dans la base de donnes sous la forme dun BLOB. En effet, java.util.ArrayList implmente Serializable et JPA sait associer automatiquement des objets Serializable des BLOB. En revanche, lorsque lon utilisait une collection java.util.List, on obtenait une exception car List nimplmente pas Serializable. Lannotation @ElementCollection est donc un moyen plus lgant et plus pratique de stocker les listes de types primitifs car leur stockage sous un format binaire opaque aux requtes les rend inaccessibles.

Association des types de base

Comme les collections, les tables de hachage sont trs utiles pour le stockage des donnes. Avec JPA1.0, on ne pouvait pas en faire grand-chose en terme dORM. Dsormais, les tables de hachage peuvent utiliser nimporte quelle combinaison de types de base, dobjets intgrables et dentits comme cls ou comme valeurs: ceci apporte beaucoup de souplesse. Pour linstant, intressons-nous aux hachages qui utilisent des types de base. Lorsquun hachage emploie des types de base, vous pouvez vous servir des annotations @ElementCollection et @CollectionTable exactement comme nous venons de le voir pour les collections. En ce cas, les donnes du hachage sont stockes dans une table collection. Prenons lexemple dun CD contenant un certain nombre de pistes (voir Listing3.28). Une piste peut tre considre comme un titre et une position (la premire piste de lalbum, la seconde, etc.). Vous pourriez alors utiliser un hachage de pistes utilisant un entier pour reprsenter la position (une cl du hachage) et une chane pour reprsenter le titre (la valeur de cette cl dans le hachage).

98

Java EE 6 et GlassFish 3

Listing3.28: Album CD avec un hachage de pistes


@Entity public class CD { @Id @GeneratedValue private Long id; private String title; private Float price; private String description; @Lob private byte[] cover; @ElementCollection @CollectionTable(name="track") @MapKeyColumn (name = "position") @Column(name = "title") private HashMap<Integer, String> tracks; // Constructeurs, getters et setters }

Comme on la dj indiqu, lannotation @ElementCollection sert indiquer que les objets du hachage seront stocks dans une table collection. Lannotation @CollectionTable, quant elle, est utilise ici pour modifier le nom par dfaut de la table collection en TRACK. La diffrence avec les collections est que lon introduit ici une nouvelle annotation, @MapKeyColumn, pour prciser lassociation correspondant la colonne cl du hachage. En son absence, le nom de cette colonne est form par concatnation du nom de lattribut qui rfrence la relation et du suffixe _KEY. Le Listing3.28 utilise cette annotation pour la renommer en POSITION afin quelle porte un nom plus lisible. Lannotation @Column indique que la colonne contenant les valeurs du hachage sera nomme TITLE. Le rsultat obtenu est reprsent par la Figure3.4.
+ID TITLE PRICE DESCRIPTION COVER bigint varchar(255) double varchar(255) blob(64000)

CD

Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true

#CD ID POSITION TITLE

bigint integer varchar(255)

TRACK

Nullable = false Nullable = true Nullable = true

Figure3.4 Relation entre les tables CD et TRACK.

Chapitre 3

ORM: Object-Relational Mapping

99

Associations avec XML


Maintenant que vous connaissez mieux les bases des associations avec les annotations, tudions celles qui utilisent XML. Si vous avez dj utilis un framework ORM comme Hibernate, vous savez dj comment associer vos entits via un fichier de descripteurs de dploiement XML. Depuis le dbut de ce chapitre, nous navons pourtant pas utilis une seule ligne de XML uniquement des annotations. Nous nentrerons pas trop dans les dtails des associations XML car nous avons dcid de nous concentrer sur les annotations (parce quelles sont plus simples utiliser dans un livre et parce que la plupart des dveloppeurs les prfrent XML). Considrez simplement que toutes les annotations que nous avons prsentes dans ce chapitre ont un quivalent XML: cette section serait norme si nous les prsentions toutes. Nous vous renvoyons donc au Chapitre11 de la spcification JPA2.0, qui prsente en dtail tous les marqueurs XML. Les descripteurs de dploiement XML sont une alternative aux annotations. Bien que chaque annotation ait un marqueur XML quivalent et vice versa, il y a toutefois une diffrence car les marqueurs XML ont priorit sur les annotations: si vous annotez un attribut ou une entit avec une certaine valeur et que vous dployiez en mme temps le descripteur XML correspondant avec une valeur diffrente, celle de lannotation sera ignore. Quand utiliser les annotations plutt que XML et pourquoi? Cest avant tout une question de got car les deux mthodes ont exactement le mme effet. Lorsque les mtadonnes sont vraiment couples au code (une cl primaire, par exemple), les annotations sont judicieuses car, en ce cas, les mtadonnes ne sont quun autre aspect du programme. Dautres types de mtadonnes comme la longueur des colonnes ou autres dtails concernant le schma peuvent en revanche dpendre de lenvironnement de dploiement (le schma de la base peut, par exemple, varier entre les environnements de dveloppement, de test et de production). En ce cas, il est prfrable de les exprimer laide de descripteurs de dploiement externes (un par environnement), afin de ne pas devoir modifier le code. Revenons notre entit Book. Imaginons que nous travaillons dans deux environnements : nous voulons associer lentit la table BOOK dans lenvironnement de dveloppement et la table BOOK_XML_MAPPING dans celui de test. La classe ne sera annote que par @Entity (voir Listing3.29) et ne contiendra pas dinformation sur la table laquelle elle est associe (elle ne contiendra donc pas dannotation @Table). Lannotation @Id dfinit une cl primaire produite automatiquement et @Column fixe la taille de la description 500caractres.

100

Java EE 6 et GlassFish 3

Listing3.29: Lentit Book ne contient que quelques annotations


@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private Float price; @Column(length = 500) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Vous pouvez modifier les associations de nimporte quelle donne de lentit en utilisant un fichier book_mapping.xml (voir Listing3.30) qui doit respecter un certain schma XML. Le marqueur <table>, par exemple, permet de modifier le nom de la table laquelle sera associe lentit (BOOK_XML_MAPPING au lieu du nom BOOK par dfaut). Dans le marqueur <attributes>, vous pouvez adapter les attributs en prcisant non seulement les noms ou les tailles de leurs colonnes, mais galement leurs relations avec dautres entits. Dans notre exemple, nous modifions les noms des colonnes title et nbOfPage.
Listing3.30: Association utilisant le fichier META-INF/book_mapping.xml
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" version="1.0"> <entity class="com.apress.javaee6.chap03.Livre"> <table name="book_xml_mapping"/> <attributes> <basic name="title"> <column name="book_title" nullable="false" updatable="false"/> </basic> <basic name="description"> <column length="2000"/> </basic> <basic name="nbOfPage"> <column name="nb_of_page" nullable="false"/> </basic> </attributes>

Chapitre 3

ORM: Object-Relational Mapping 101

</entity> </entity-mappings>

Il ne faut jamais oublier que XML a priorit sur les annotations. Mme si lattribut description est annot par @Column(length = 500), la longueur de la colonne utilise sera celle dfinie dans le fichier book_mapping.xml, cest--dire 2000. Pensez toujours consulter le descripteur de dploiement XML en cas de doute. La fusion des mtadonnes XML et des mtadonnes des annotations fera que lentit Book sera finalement associe la table BOOK_XML_MAPPING, dont la structure est dfinie dans le Listing3.31.
Listing3.31: Structure de la table BOOK_XML_MAPPING
create table BOOK_XML_MAPPING ( ID BIGINT not null, BOOK_TITLE VARCHAR(255) not null, DESCRIPTION VARCHAR(2000), NB_OF_PAGE INTEGER not null, PRICE DOUBLE(52, 0), ISBN VARCHAR(255), ILLUSTRATIONS SMALLINT, primary key (ID) );

Il ne manque plus quune information pour que ceci fonctionne: vous devez rfrencer le fichier book_mapping.xml dans le fichier persistence.xml laide dun marqueur <mapping-file>. Le fichier persistence.xml dfinit le contexte de persistance de lentit et la base de donnes laquelle elle sera associe: le fournisseur de contenu a absolument besoin de ce fichier pour pouvoir retrouver les associations XML externes. Dployez lentit Book avec ces deux fichiers XML (placs dans le rpertoire META-INF), et cest fini (voir Listing3.32).
Listing3.32: Fichier persistence.xml faisant rfrence un fichier dassociation externe
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="javaee6PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>com.apress.javaee6.chapter03.Book</class> <mapping-file>META-INF/book_mapping.xml</mapping-file> <properties>

102

Java EE 6 et GlassFish 3

<!--Persistence provider properties--> </properties> </persistence-unit> </persistence>

Objets intgrables
Dans la section "Cls primaires composes" plus haut dans ce chapitre, nous avons rapidement vu comment une classe pouvait tre intgre pour servir de cl primaire avec lannotation @EmbeddedId. Les objets intgrables sont des objets qui nont pas didentit persistante par eux-mmes. Une entit peut contenir des collections dobjets intgrables ainsi quun simple attribut dune classe intgrable: dans les deux cas, ils seront stocks comme faisant partie de lentit et partageront son identit. Ceci signifie que chaque attribut de lobjet intgr est associ la table de lentit. Ilsagit donc dune relation de proprit stricte (une composition): quand lentit est supprime, lobjet intgr disparat galement. Cette composition entre deux classes passe par des annotations. La classe incluse utilise @Embeddable et lentit qui inclut utilise @Embedded. Prenons lexemple dun client possdant un identifiant, un nom, une adresse e-mail et une adresse. Tous ces attributs pourraient se trouver dans une entit Customer (voir Listing3.34 un peu plus loin) mais, pour des raisons de modlisation, ils sont rpartis en deux classes: Customer et Address. Cette dernire nayant pas didentit propre mais tant simplement une composante de ltat de Customer, cest une bonne candidate au statut de classe intgrable (voir Listing3.33).
Listing3.33: La classe Address est une classe intgrable
@Embeddable public class Address { private private private private private private String String String String String String street1; street2; city; state; zipcode; country;

// Constructors, getters, setters }

Comme vous pouvez le constater la lecture du Listing3.33, la classe Address est annote comme tant non pas une entit mais une classe intgrable lannotation

Chapitre 3

ORM: Object-Relational Mapping 103

indique quAddress peut tre intgre dans une classe entit (ou dans une autre classe intgrable). lautre extrmit de la composition, lentit Customer doit utiliser lannotation @Embedded pour indiquer quAddress est un attribut persistant qui sera stock comme composante interne et quil partage son identit (voir Listing3.34).
@Embeddable

Listing3.34: Lentit Customer intgre un objet Address


@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Embedded private Address address; // Constructors, getters, setters }

Chaque attribut dAddress est associ la table de lentit Customer. Il ny aura donc quune seule table qui aura la structure dfinie dans le Listing3.35. Comme nous le verrons plus loin dans la section "Cls primaires composes", les entits peuvent redfinir les attributs des objets quelles intgrent (avec lannotation @AttributeOverrides).
Listing3.35: Structure de la table CUSTOMER avec tous les attributs dAddress
create table CUSTOMER ( ID BIGINT not null, LASTNAME VARCHAR(255), PHONENUMBER VARCHAR(255), EMAIL VARCHAR(255), FIRSTNAME VARCHAR(255), STREET2 VARCHAR(255), STREET1 VARCHAR(255), ZIPCODE VARCHAR(255), STATE VARCHAR(255), COUNTRY VARCHAR(255), CITY VARCHAR(255), primary key (ID) );

104

Java EE 6 et GlassFish 3

Types daccs dune classe intgrable

Le type daccs dune classe intgrable est dtermin par celui de la classe entit dans laquelle elle est intgre. Si cette entit utilise explicitement un type daccs par proprit, lobjet intgrable utilisera implicitement un accs par proprit aussi. Vous pouvez prciser un type daccs diffrent pour une classe intgrable au moyen de lannotation @Access. Dans le Listing3.36, lentit Customer et la classe Address (voir Listing3.37) utilisent des types daccs diffrents.
Listing3.36: Lentit Customer avec un type daccs par champ
@Entity @Access(AccessType.FIELD) public class Customer { @Id @GeneratedValue private Long id; @Column(name = "first_name", nullable = false, length = 50) private String firstName; @Column(name = "last_name", nullable = false, length = 50) private String lastName; private String email; @Column(name = "phone_number", length = 15) private String phoneNumber; @Embedded private Address address; } // Constructeurs, getters, setters

Listing3.37: La classe intgrable utilise un type daccs par proprit


@Embeddable @Access(AccessType.PROPERTY) public class Address { private private private private private private String String String String String String street1; street2; city; state; zipcode; country;

// Constructeurs @Column(nullable = false) public String getStreet1() {

Chapitre 3

ORM: Object-Relational Mapping 105

return street1; } public void setStreet1(String street1) { this.street1 = street1; } public String getStreet2() { return street2; } public void setStreet2(String street2) { this.street2 = street2; } @Column(nullable = false, length = 50) public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Column(length = 3) public String getState() { return state; } public void setState(String state) { this.state = state; } @Column(name = "zip_code", length = 10) public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }

Il est fortement conseill de configurer le type daccs des classes intgrables afin dviter les erreurs dassociation qui pourraient se produire lorsquune telle classe est intgre dans plusieurs entits. tendons notre modle en ajoutant une entit Order (voir Figure 3.5). La classe Address est maintenant intgre la fois par Customer (pour reprsenter ladresse personnelle du client) et dans Order (pour reprsenter ladresse de livraison).

106

Java EE 6 et GlassFish 3

. Figure3.5

<<entity>> <<field access>> Customer

<<embeddable>> Address

<<entity>> <<property access>> Order

Address est intgre par Customer et Order.

Chaque entit dfinit un type daccs diffrent: Customer utilise un accs par champ et Order, un accs par proprit. Le type daccs dun objet intgrable tant dtermin par celui de la classe entit dans laquelle il est dclar, Address sera associe de deux faons diffrentes, ce qui peut poser des problmes. Pour les viter, le type daccs dAddress doit tre indiqu explicitement.
INFO Les types daccs explicites sont galement trs utiles avec lhritage. Par dfaut, les entits filles hritent du type daccs de leur entit parente. Dans une hirarchie dentits, il est cependant possible daccder chacune diffremment des autres: lajout dune annotation @Access permet de redfinir localement le type daccs par dfaut utilis dans la hirarchie.

Correspondance des relations


Le monde de la programmation oriente objet est rempli de classes et de relations entre classes. Ces relations sont structurelles car elles lient des objets dun certain type des objets dautres types, permettant ainsi un objet de demander un autre de raliser une action. Il existe plusieurs types dassociations entre les classes. Premirement, une relation a une direction. Elle peut tre unidirectionnelle (un objet peut aller vers un autre) ou bidirectionnelle (un objet peut aller vers un autre et vice versa). En Java, on utilise le point (.) pour naviguer entre les objets. Lorsque lon crit, par exemple, customer.getAddress().getCountry(), on navigue dun objet Customer vers un objet Address puis un objet Country. En UML (Unified Modeling Language), une relation unidirectionnelle entre deux classes est reprsente par une flche indiquant la direction. la Figure 3.6, par exemple, Class1 (la source) peut naviguer vers Class2 (la cible), mais pas linverse.
Figure3.6 Relation unidirectionnelle entre deux classes.
Class1 Class2

Chapitre 3

ORM: Object-Relational Mapping 107

Comme le montre la Figure3.7, une relation bidirectionnelle nutilise pas de flche: Class1 peut naviguer vers Class2 et vice versa. En Java, ce type de relation est reprsent par une classe Class1 ayant un attribut instance de Class2 et par une classe Class2 ayant un attribut instance de Class1.
Figure3.7 Relation bidirectionnelle entre deux classes.
Class1 Class2

Une relation a galement une cardinalit. Chaque extrmit peut prciser le nombre dobjets impliqus dans cette relation. Le diagramme UML de la Figure3.8 indique, par exemple, quune instance de Class1 est en relation avec zro ou plusieurs instances de Class2.
Figure3.8 Cardinalit des relations entre classes.
Class1 1 0..* Class2

En UML, une cardinalit est un intervalle de valeurs compris entre un minimum et un maximum: 0..1 signifie quil y aura au minimum zro objet et au maximum un objet, 1 signifie quil ny aura quune et une seule instance, 1..*, quil y aura une ou plusieurs instances et 3..6, quil y aura entre trois et six objets. En Java, une relation qui reprsente plusieurs objets utilise les collections de java.util.Collection, java.util.Set, java.util.List ou java.util.Map. Une relation a un propritaire. Dans une relation unidirectionnelle, ce propritaire est implicite: la Figure3.6, il est vident que le propritaire est Class1. Dans une relation bidirectionnelle comme celle de la Figure3.7, il faut en revanche lindiquer explicitement en dsignant le ct propritaire, qui spcifie lassociation physique, et le ct oppos (non propritaire). Dans les sections qui suivent, nous verrons comment associer des collections dobjets avec les annotations JPA.
Relations dans les bases de donnes relationnelles

Dans le monde relationnel, les choses sont diffrentes puisque, proprement parler, une base de donnes relationnelle est un ensemble de relations (galement appeles tables): tout est modlis sous forme de table pour modliser une relation, vous ne disposez ni de listes, ni densembles, ni de tables de hachage: vous navez que des

108

Java EE 6 et GlassFish 3

tables. Une relation entre deux classes Java sera reprsente dans la base de donnes par une rfrence une table qui peut tre modlise de deux faons: avec une cl trangre (une colonne de jointure) ou avec une table de jointure. titre dexemple, supposons quun client nait quune seule adresse, ce qui implique une relation11. En Java, la classe Customer aurait donc un attribut Address; dans le monde relationnel, vous pourriez avoir une table CUSTOMER pointant vers une table ADDRESS via une cl trangre, comme le montre la Figure3.9.
Customer
Cl primaire Firstname 1 2 3 James Dominic Maca Lastname Cl trangre Rorisson Johnson Macaron 11 12 13

Address
Cl primaire 11 12 13 Street Aligre Balham Alfama City Paris London Lisbon Country France UK Portugal

Figure3.9 Une relation entre deux tables utilisant une colonne de jointure.

La seconde mthode consiste utiliser une table de jointure. La table CUSTOMER de la Figure3.10 ne stocke plus la cl trangre vers ADDRESS mais utilise une table intermdiaire pour reprsenter la relation liant ces deux tables. Cette liaison est constitue par les cls primaires des deux tables.
Customer
Cl primaire Firstname 1 2 3 James Dominic Maca Lastname Cl trangre Rorisson Johnson Macaron 11 12 13

Address
Cl primaire 11 12 13 Street Aligre Balham Alfama City Paris London Lisbon Country France UK Portugal

Table de jointure
Customer Pk Address Pk 1 2 3 11 12 13

Figure3.10 Relation utilisant une table de jointure.

On nutilise pas une table de jointure pour reprsenter une relation 11 car cela pourrait avoir des consquences sur les performances (il faudrait toujours accder la troisime table pour obtenir ladresse dun client); elles sont gnralement rserves aux relations1N ouNM. Comme nous le verrons dans la section suivante,

Chapitre 3

ORM: Object-Relational Mapping 109

JPA utilise ces deux mthodes pour associer les relations entre les objets une base de donnes.
Relations entre entits

Revenons maintenant JPA. La plupart des entits doivent pouvoir rfrencer ou tre en relation avec dautres entits: cest ce que produisent les diagrammes utiliss pour modliser les applications professionnelles. JPA permet dassocier ces relations de sorte quune entit puisse tre lie une autre dans un modle relationnel. Comme pour les annotations dassociations lmentaires, que nous avons dj tudies, JPA utilise une configuration par exception pour ces relations: il utilise un mcanisme par dfaut pour stocker une relation mais, si cela ne vous convient pas, vous disposez de plusieurs annotations pour adapter lassociation vos besoins. La cardinalit dune relation entre deux entits peut tre 11, 1N, N1 ou NM. Les annotations des associations correspondantes sont donc nommes @OneToOne, @ OneToMany, @ManyToOne et @ManyToMany. Chacune delles peut tre utilise de faon unidirectionnelle ou bidirectionnelle: le Tableau3.1 numre toutes les combinaisons possibles.
Tableau3.1: Combinaisons possibles entre cardinalits et directions

Cardinalit 11 11 1N N1/1N N1 NM NM

Direction Unidirectionnelle Bidirectionnelle Unidirectionnelle Bidirectionnelle Unidirectionnelle Unidirectionnelle Bidirectionnelle

Vous pouvez constater que unidirectionnel et bidirectionnel sont des concepts rptitifs qui sappliquent de la mme faon toutes les cardinalits. Vous verrez bientt la diffrence entre les relations unidirectionnelles et bidirectionnelles, puis comment implmenter certaines de ces combinaisons, dont nous ne dcrirons quun sous-ensemble: les expliquer toutes serait rptitif. Limportant est de comprendre comment traduire la cardinalit et la direction en relations.

110

Java EE 6 et GlassFish 3

Unidirectionnelle et bidirectionnelle

Du point de vue de la modlisation objet, la direction entre les classes est naturelle. Dans une relation unidirectionnelle, un objet A pointe uniquement vers un objet B alors que, dans une relation bidirectionnelle, ils se font mutuellement rfrence. Cependant, comme le montre lexemple suivant dun client et de son adresse, un peu de travail est ncessaire lorsque lon veut reprsenter une relation bidirectionnelle dans une base de donnes. Dans une relation unidirectionnelle, une entit Customer a un attribut de type Address (voir Figure3.11). Cette relation ne va que dans un seul sens: on dit que le client est le propritaire de la relation. Du point de vue de la base de donnes, ceci signifie que la table CUSTOMER contiendra une cl trangre (une colonne de jointure) pointant vers la table ADDRESS. Par ailleurs, le propritaire de la relation peut personnaliser la traduction de cette relation: si vous devez modifier le nom de la cl trangre, par exemple, cette annotation aura lieu dans lentit Customer (le propritaire).
Figure3.11 Relation unidirectionnelle entre Customer et Address.
Customer
-id : Long -firstname : String -lastname : String -email : String -phoneNumber : String

Address
-id : Long -sreet1 : String -street2 : String -city : String -state : String -zipcode : String -country : String

Comme on la mentionn prcdemment, les relations peuvent galement tre bidirectionnelles. Pour naviguer entre Address et Customer, nous devons ajouter un attribut Customer lentit Address (voir Figure3.12). Notez que les attributs reprsentant une relation napparaissent pas dans les diagrammes UML.
Figure3.12 Relation bidirectionnelle entre Customer et Address.
Customer
-id : Long -firstName : String -lastName : String -email : String -phoneNumber : String

Address
-id : Long -street1 : String -street2 : String -city : String -state : String -zipcode : String -country : String

En termes de Java et dannotations, ceci revient avoir deux associations de type11 dans les deux directions opposes. Nous pouvons donc considrer une relation bidirectionnelle comme une paire de relations unidirectionnelles allant dans les deux sens (voir Figure3.13).

Chapitre 3

ORM: Object-Relational Mapping 111

Figure3.13 Relation bidirectionnelle reprsente par deux relations unidirectionnelles.


Customer
-id : Long -firstName : String -lastName : String -email : String -phoneNumber : String

Address
-id : Long -street1 : String -street2 : String -city : String -state : String -zipcode : String -country : String

Comment associer tout cela une base de donnes? Qui est le propritaire de cette relation bidirectionnelle? qui appartient linformation sur la colonne ou la table de jointure ? Si les relations unidirectionnelles ont un ct propritaire, les bidirectionnelles ont la fois un ct propritaire et un ct oppos, qui doivent tre indiqus explicitement par llment mappedBy des annotations @OneToOne, @OneToMany et @ManyToMany. mappedBy identifie lattribut propritaire de la relation; il est obligatoire pour les relations bidirectionnelles. Pour illustrer tout ceci, comparons du code Java sa traduction dans la base de donnes. Comme vous pouvez le constater dans la partie gauche de la Figure3.14, les deux entits pointent lune vers lautre au moyen dattributs: Customer possde un attribut address annot par @OneToOne et lentit Address a un attribut customer galement annot. Dans la partie droite de cette figure se trouvent les tables CUSTOMER et ADDRESS. CUSTOMER est la table propritaire de la relation car elle contient la cl trangre vers ADDRESS.
@Entity public class C {
+ID LASTNAME PHONENUMBER EMAIL FIRSTNAME #ADDRESS FK

CUSTOMER
bigint varchar(255) varchar(255) varchar(255) varchar(255) bigint

@Id @GeneratedValue private Long i ; private String firstName; private String lastName; private String email; private String phoneNumber; @On T O @JoinColumn(name = " s private Address address;

Nullable Nu lable Nu lable Nu lable Nu lable Nu lable

false true true true true true

k")
ADDRESS

@Entity public class Address { @Id @GeneratedValue private Long id; private String street1; private String street2; private String city; private String state; private String zipcode; private String country; ( p d = " d @ n private Customer customer;

+ID STREET2 STREET1 ZIPCODE STATE COUNTRY CITY

bigint varchar(255) varchar(255) varchar(255) varchar(255) varchar(255) varchar(255)

Nullable Nullable Nullable Nullable Nullable Nullable Nullable

false true true true true true true

s")

Figure3.14 Code de Customer et Address avec leur correspondance dans la base de donnes.

112

Java EE 6 et GlassFish 3

Lentit Address utilise llment mappedBy de son annotation @OneToOne. Ici, mappedBy indique que la colonne de jointure (address) est dclare lautre extrmit de la relation. De son ct, lentit Customer dfinit la colonne de jointure avec lannotation @JoinColumn et renomme la cl trangre en address_fk. Customer est lextrmit propritaire de la relation et, en tant que telle, elle est la seule dfinir lassociation de la colonne de jointure. Address est lextrmit oppose et cest donc la table de lentit propritaire qui contient la cl trangre (la table CUSTOMER a une colonne ADDRESS_FK). Il existe un lment mappedBy pour les annotations @OneToOne, @OneToMany et @ManyToMany, mais pas pour @ManyToOne.
INFO Si vous connaissez Hibernate, vous pouvez considrer que llment mappedBy de JPA est lquivalent de lattribut inverse de Hibernate, qui indique lextrmit ignorer dans une relation.

@OnetoOne unidirectionnelle

Une relation 11 unidirectionnelle entre deux entits a une rfrence de cardinalit1 qui ne peut tre atteinte que dans une seule direction. Reprenons lexemple dun client et de son adresse en supposant quil na quune seule adresse (cardinalit1). Il faut pouvoir naviguer du client (la source) vers ladresse (la cible) pour savoir o habite le client. Dans le modle de la Figure 3.15, nous navons pas besoin de faire le trajet inverse (on na pas besoin de savoir quel client habite une adresse donne).
Figure3.15 Un client a une seule adresse.
Customer
-id : Long -firstName : String -lastName : String -email : String -phoneNumber : String

Address
-id : Long -street1 : String -street2 : String -city : String -state : String -zipcode : String -country : String

En Java, ceci signifie que la classe Customer aura un attribut Address (voir Listings3.38 et 3.39).

Chapitre 3

ORM: Object-Relational Mapping 113

Listing3.38: Une entit Customer avec une seule adresse


@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; private Address address; // Constructeurs, getters, setters }

Listing3.39: Lentit Address


@Entity public class Address { @Id @GeneratedValue private Long id; private String street1; private String street2; private String city; private String state; private String zipcode; private String country; // Constructeurs, getters, setters }

Comme vous pouvez le constater la lecture des Listings 3.38 et 3.39, ces deux entits utilisent un nombre minimal dannotations @Entity plus @Id et @GeneratedValue pour la cl primaire, cest tout... Grce la configuration par exception, le fournisseur de persistance les associera deux tables et ajoutera une cl trangre pour reprsenter la relation (allant du client ladresse). Cette relation 11 est dclenche par le fait quAddress est dclare comme une entit et quelle est incluse dans lentit Customer sous la forme dun attribut. Il ny a donc pas besoin dannotation @OneToOne car le comportement par dfaut suffit (voir Listings 3.40 et 3.41).
Listing3.40: La table CUSTOMER avec une cl trangre vers ADDRESS
create table CUSTOMER ( ID BIGINT not null, FIRSTNAME VARCHAR(255), LASTNAME VARCHAR(255), EMAIL VARCHAR(255),

114

Java EE 6 et GlassFish 3

PHONENUMBER VARCHAR(255), ADDRESS_ID BIGINT, primary key (ID), foreign key (ADDRESS_ID) references ADDRESS(ID) );

Listing3.41: La table ADDRESS


create table ADDRESS ( ID BIGINT not null, STREET1 VARCHAR(255), STREET2 VARCHAR(255), CITY VARCHAR(255), STATE VARCHAR(255), ZIPCODE VARCHAR(255), COUNTRY VARCHAR(255), primary key (ID) );

Comme vous le savez, si un attribut nest pas annot, JPA lui applique les rgles dassociation par dfaut. La colonne de cl trangre sappellera donc ADDRESS_ID (voir Listing3.40), qui est la concatnation du nom de lattribut (address, ici), dun blanc soulign et du nom de la cl primaire de la table destination (ici, la colonne ID de la table ADDRESS). Notez galement que, dans le langage de dfinition des donnes, la colonne ADDRESS_ID peut, par dfaut, recevoir des valeurs NULL: par dfaut, une relation 11 est donc associe zro (NULL) ou une valeur. Il existe deux annotations permettant dadapter lassociation dune relation 11. La premire est @OneToOne (car la cardinalit de la relation est un) : elle permet de modifier certains attributs de la relation elle-mme, comme la faon dont elle sera parcourue. Son API est dcrite dans le Listing3.42.
Listing3.42: API de lannotation @OneToOne
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OneToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default EAGER; boolean optional() default true; String mappedBy() default ""; boolean orphanRemoval() default false; }

Lautre annotation sappelle @JoinColumn (son API ressemble beaucoup celle de @Column). Elle permet de personnaliser la colonne de jointure, cest--dire la cl

Chapitre 3

ORM: Object-Relational Mapping 115

trangre, du ct du propritaire de la relation. Le Listing3.43 prsente un exemple dutilisation de ces deux annotations.
Listing3.43: Lentit Customer avec une association de relation personnalise
@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @OneToOne (fetch = FetchType.LAZY) @JoinColumn(name = "add_fk", nullable = false) private Address address; // Constructeurs, getters, setters }

Dans le Listing 3.43, on utilise @JoinColumn pour renommer la colonne de cl trangre en ADD_FK et rendre la relation obligatoire en refusant les valeurs NULL(nullable=false). Lannotation @OneToOne, quant elle, demande au fournisseur de persistance de parcourir la relation de faon paresseuse.
@OnetoMany unidirectionnelle

Dans une relation 1N, lobjet source rfrence un ensemble dobjets cibles. Une commande, par exemple, est compose de plusieurs lignes de commande (voir Figure3.16). Inversement, une ligne de commande pourrait faire rfrence la commande dont elle fait partie laide dune annotation @ManyToOne. Dans la figure, Order est lextrmit "One" (la source) de la relation et OrderLine est son extrmit "Many" (la cible).
Figure3.16 Une commande compte plusieurs lignes.
Order
-id : Long -creationDate : Date

OrderLine *
-id : Long -item : String -unitPrice : Double -quantity : Integer

La cardinalit est multiple et la navigation ne se fait que dans le sens Order vers OrderLine. En Java, cette multiplicit est dcrite par les interfaces Collection, List et Set du paquetage java.util. Le Listing3.44 prsente le code de lentit Order avec une relation 1N vers OrderLine (voir Listing3.45).

116

Java EE 6 et GlassFish 3

Listing3.44: Lentit Order contient des OrderLine


@Entity public class Order { @Id @GeneratedValue private Long id; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; private List<OrderLine> orderLines; // Constructeurs, getters, setters }

Listing3.45: Lentit OrderLine


@Entity @Table(name = "order_line") public class OrderLine { @Id @GeneratedValue private Long id; private String item; private Double unitPrice; private Integer quantity; // Constructeurs, getters, setters }

Le code du Listing3.44 nutilise pas dannotation particulire car il repose sur le paradigme de configuration par exception. Le fait quune entit ait un attribut qui soit une collection dun type dune autre entit dclenche une association OneToMany par dfaut. Les relations 1N unidirectionnelles utilisent par dfaut une table de jointure pour reprsenter la relation; cette table est une liste de couples de cls trangres: une cl fait rfrence la table ORDER et est du mme type que sa cl primaire, lautre dsigne la table ORDER_LINE. Cette table de jointure sappelle par dfaut ORDER_ORDER_ LINE et possde la structure dcrite la Figure3.17. Si vous naimez pas le nom de la table de jointure ou celui des cls trangres, ou si vous voulez associer la relation une table existante, vous pouvez vous servir des annotations de JPA pour redfinir ces valeurs par dfaut. Le nom dune colonne de jointure est form par dfaut par la concatnation du nom de lentit, dun blanc soulign et du nom de la cl primaire dsigne par la cl trangre. Comme lannotation @JoinColumn permet de modifier le nom des colonnes de cls trangres, @JoinTable fait de mme pour la table de jointure. Vous pouvez galement utiliser lannotation @OneToMany (voir Listing3.46), qui, comme @OneToOne, permet de personnaliser la relation elle-mme (mode de parcours, etc.).

Chapitre 3

ORM: Object-Relational Mapping 117

+ID CREATIONDATE

ORDER
bigint timestamp

ORDER LINE
Nullable = false Nullable = true +ID ITEM UNITPRICE QUANTITY bigint double integer Nullable = false Nullable = true Nullable = true varchar(255) Nullable = true

ORDER ORDER LINE


+#ORDER ID +#ORDERLINES ID bigint bigint Nullable = false Nullable = false

Figure3.17 Table de jointure entre ORDER et ORDER_LINE.

Listing3.46: API de lannotation @JoinTable


@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface JoinTable { String name() default ""; String catalog() default ""; String schema() default ""; JoinColumn[] joinColumns() default {}; JoinColumn[] inverseJoinColumns() default {}; UniqueConstraint[] uniqueConstraints() default {}; }

Dans lAPI de lannotation @JoinTable prsente dans le Listing3.46, vous pouvez remarquer deux attributs de type @JoinColumn : joinColumns et inverseJoinColumns. Ils permettent de diffrencier lextrmit propritaire de la relation et son extrmit oppose. Lextrmit propritaire de la relation est dcrite dans llment joinColumns et, dans notre exemple, dsigne la table ORDER. Lextrmit oppose, la cible de la relation, est prcise par llment inverseJoinColumns et dsigne la table ORDER_LINE. Dans lentit Order (voir Listing3.47), vous pouvez ajouter les annotations @OneToMany et @JoinTable pour lattribut orderLines afin de modifier le nom de la table de jointure en JND_ORD_LINE (au lieu dORDER_ORDER_LINE) et pour renommer les deux colonnes de cl trangre.
Listing3.47: Entit Order avec une relation 1N annote
@Entity public class Order { @Id @GeneratedValue

118

Java EE 6 et GlassFish 3

private Long id; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; @OneToMany @JoinTable(name = "jnd_ord_line", joinColumns = @JoinColumn(name = "order_fk"), inverseJoinColumns = @JoinColumn(name = "order_line_fk") ) private List<OrderLine> orderLines; // Constructors, getters, setters

Lentit Order du Listing 3.47 sera associe la table de jointure dcrite dans le Listing3.48.
Listing3.48: Structure de la table de jointure
create table JND_ORD_LINE ( ORDER_FK BIGINT not null, ORDER_LINE_FK BIGINT not null, primary key (ORDER_FK, ORDER_LINE_FK), foreign key (ORDER_LINE_FK) references ORDER_LINE(ID), foreign key (ORDER_FK) references ORDER(ID) );

La rgle par dfaut pour une relation 1N unidirectionnelle consiste utiliser une table de jointure, mais il est trs facile (et utile si vous utilisez une base de donnes existante) de faire en sorte dutiliser des cls trangres (via une colonne de jointure). Pour cela, lentit Order doit utiliser une annotation @JoinColumn la place de @JoinTable, comme le montre le Listing3.49.
Listing3.49: Entit Order avec une colonne de jointure
@Entity public class Order { @Id @GeneratedValue private Long id; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; @OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "order_fk") private List<OrderLine> orderLines; // Constructeurs, getters, setters }

Le code de lentit OrderLine ne change pas, il est identique celui du Listing3.45. Vous remarquerez quici lannotation @OneToMany redfinit le mode de parcours par dfaut (en le fixant EAGER au lieu de LAZY). En utilisant @JoinColumn, la relation unidirectionnelle est ensuite traduite en utilisant une cl trangre. Cette cl est

Chapitre 3

ORM: Object-Relational Mapping 119

renomme en ORDER_FK et existe dans la table cible (ORDER_LINE). On obtient alors la structure reprsente la Figure3.18. Il ny a plus de table de jointure et la liaison entre les deux tables seffectue grce la cl trangre ORDER_FK.
+ID CREATIONDATE bigint timestamp

ORDER

Nullable = false Nullable = true

ORDER LINE
+ID ITEM QUANTITY UNITPRICE #ORDER FK bigint varchar(255) integer double bigint Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true

Figure3.18 Colonne de jointure entre Order et OrderLine.

@ManytoMany bidirectionnelle

Une relation NM bidirectionnelle intervient lorsquun objet source fait rfrence plusieurs cibles et quune cible fait rfrence plusieurs sources. Un album CD, par exemple, est cr par plusieurs artistes, et un mme artiste peut apparatre sur plusieurs albums. Ct Java, chaque entit contiendra donc une collection dentits cibles. En terme de base de donnes relationnelle, la seule faon de reprsenter une relation NM consiste utiliser une table de jointure (une colonne de jointure ne peut pas convenir); comme nous lavons vu prcdemment, il faut galement dfinir explicitement le propritaire dune relation bidirectionnelle laide de llment mappedBy. Si lon suppose que lentit Artist est propritaire de la relation, ceci implique que CD est lextrmit oppose (voir Listing3.50) et quelle doit utiliser llment mappedBy de son annotation @ManyToMany. Ici, mappedBy indique au fournisseur de persistance quappearsOnCDs est le nom de lattribut correspondant dans lentit propritaire de la relation.
Listing3.50: Un CD est cr par plusieurs artistes
@Entity public class CD { @Id @GeneratedValue private Long id; private String title; private Float price; private String description; @ManyToMany(mappedBy = "appearsOnCDs") private List<Artist> createdByArtists; // Constructeurs, getters, setters

120

Java EE 6 et GlassFish 3

Si Artist est propritaire de la relation (voir Listing3.51), cest donc cette entit quil revient de personnaliser la table de jointure via les annotations @JoinTable et @JoinColumn.
Listing3.51: Un artiste apparat sur plusieurs CD
@Entity public class Artist { @Id @GeneratedValue private Long id; private String firstName; private String lastName; @ManyToMany @JoinTable(name = "jnd_art_cd", joinColumns = @JoinColumn(name = "artist_fk"), inverseJoinColumns = @JoinColumn(name = "cd_fk")) private List<CD> appearsOnCDs; // Constructors, getters, setters

La table de jointure entre Artist et CD est renomme en JND_ART_CD et les noms de ses colonnes sont galement modifis. Llment joinColumns fait rfrence lextrmit propritaire (lArtist), tandis quinverseJoinColumns dsigne lextrmit inverse (le CD). La structure de la base obtenue est prsente la Figure3.19.
ARTIST
+ID TITLE PRICE DESCRIPTION bigint varchar(255) double varchar(255)

CD

+ID bigint LASTNAME varchar(255) FIRSTNAME varchar(255)

Nullable = false Nullable = true Nullable = true

Nullable = false Nullable = true Nullable = true Nullable = true

JND ART CD
+#CD FK +#ARTIST FK bigint bigint Nullable = false Nullable = false

Figure3.19 Les tables Artist, CD et la table de jointure.

Dans une relation NM et 11 bidirectionnelle, chaque extrmit peut, en fait, tre considre comme la propritaire de la relation. Quoi quil en soit, lautre extrmit doit inclure llment mappedBy: dans le cas contraire, le fournisseur considrera que les deux extrmits sont propritaires et traitera cette relation comme deux relations 1N unidirectionnelles distinctes. Ici, cela produirait donc quatre tables: ARTIST et CD et deux tables de jointures, ARTIST_CD et CD_ARTIST. On ne peut pas non plus utiliser un lment mappedBy des deux cts dune relation.

Chapitre 3

ORM: Object-Relational Mapping 121

Chargement des relations

Toutes les annotations que nous avons vues (@OneToOne, @OneToMany, @ManyToOne et @ManyToMany) dfinissent un attribut de chargement qui prcise que les objets associs doivent tre chargs immdiatement (chargement "glouton") ou plus tard (chargement "paresseux") et qui influe donc sur les performances. Selon lapplication, certaines relations sont utilises plus souvent que dautres: dans ces situations, vous pouvez optimiser les performances en chargeant les donnes de la base lors de la premire lecture de lentit (glouton) ou uniquement lorsquelle est utilise (paresseux). titre dexemple, prenons deux cas extrmes. Supposons que nous ayons quatre entits toutes relies les unes aux autres avec des cardinalits diffrentes (11, 1N). Dans le premier cas (voir Figure3.20), elles ont toutes des relations "gloutonnes", ce qui signifie que, ds que lon charge Class1 (par une recherche par ID ou par une requte), tous les objets qui en dpendent sont automatiquement chargs en mmoire, ce qui peut avoir certaines rpercussions sur votre systme.
Figure3.20 Quatre entits avec des relations gloutonnes.
Class1 1
gloutonne

Class2

1..*
gloutonne

Class3

1..*
gloutonne

Class4

Dans le scnario oppos, toutes les relations utilisent un chargement paresseux (voir Figure3.21). Lorsque lon charge Class1, rien dautre nest plac en mmoire (sauf les attributs directs de Class1, bien sr). Il faut explicitement accder Class2 (via la mthode getter, par exemple) pour que le fournisseur de persistance charge les donnes partir de la base, etc. Pour manipuler le graphe complet des objets, il faut donc appeler explicitement chaque entit:
class1.getClass2().getClass3().getClass4()

Figure3.21 Quatre entits avec des relations paresseuses.

Class1

1
paresseuse

Class2

1..*
paresseuse

Class3

1..*
paresseuse

Class4

Mais ne pensez pas quEAGER est le Mal et LAZY, le Bien. EAGER placera toutes les donnes en mmoire laide dun petit nombre daccs la base (le fournisseur de persistance utilisera srement des jointures pour extraire ces donnes). Avec LAZY, vous ne risquez plus de remplir la mmoire puisque vous contrlez les objets qui sont chargs, mais vous devrez faire plus daccs la base chaque fois.

122

Java EE 6 et GlassFish 3

Le paramtre fetch est trs important car, mal utilis, il peut pnaliser les performances. Chaque annotation a une valeur fetch par dfaut que vous devez connatre et changer si elle ne convient pas (voir Tableau3.2).
Tableau3.2: Stratgie de chargements par dfaut

Annotation @OneToOne @ManyToOne @OneToMany @ManyToMany

Stratgie de chargement par dfaut EAGER EAGER LAZY LAZY

Lorsque vous chargez une commande (Order) dans votre application, vous avez toujours besoin daccder aux lignes de cette commande (OrderLine). Il peut donc tre avantageux de changer le mode de chargement par dfaut de lannotation @OneToMany en EAGER (voir Listing3.52).
Listing3.52: Order est en relation "gloutonne" vers OrderLine
@Entity public class Order { @Id @GeneratedValue private Long id; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; @OneToMany(fetch = FetchType.EAGER) private List<OrderLine> orderLines; // Constructeurs, getters, setters }

Tri des relations

Avec les relations 1N, les entits grent des collections dobjets. Du point de vue de Java, ces collections ne sont gnralement pas tries et les bases de donnes relationnelles ne garantissent pas non plus dordre sur leurs tables. Si vous voulez obtenir une liste trie, vous devez donc soit trier la collection dans votre programme, soit utiliser une requte JPQL avec une clause Order By. Pour le tri des relations, JPA dispose de mcanismes plus simples reposant sur les annotations.

Chapitre 3

ORM: Object-Relational Mapping 123

@OrderBy

Lannotation @OrderBy permet deffectuer un tri dynamique : les lments de la collection seront tris lors de leur rcupration partir de la base de donnes. Lexemple de lapplication CD-BookStore permet un utilisateur dcrire des articles propos de musique et de livres: ces articles sont affichs sur le site web et peuvent ensuite tre comments (voir Listing3.53). Comme nous souhaitons que ces commentaires apparaissent chronologiquement, nous devons les ordonner.
Listing3.53: Entit Comment avec une date de publication
@Entity public class Comment { @Id @GeneratedValue private Long id; private String nickname; private String content; private Integer note; @Column(name = "posted_date") @Temporal(TemporalType.TIMESTAMP) private Date postedDate; // Constructeurs, getters, setters }

Les commentaires sont modliss par lentit Comment du Listing3.53. Ils ont un contenu, sont posts par un visiteur anonyme (identifi par un pseudo) et ont une date de publication de type TIMESTAMP automatiquement cre par le systme. Dans lentit News du Listing3.54, nous trions la liste des commentaires par ordre dcroissant des dates de publication en combinant lannotation @OrderBy avec @OneToMany.
Listing3.54: Les commentaires dune entit News sont tris par ordre dcroissant des dates de publication
@Entity public class News { @Id @GeneratedValue private Long id; @Column(nullable = false) private String content; @OneToMany(fetch = FetchType.EAGER) @OrderBy("postedDate desc") private List<Comment> comments; // Constructeurs, getters, setters }

124

Java EE 6 et GlassFish 3

Lannotation @OrderBy prend en paramtre les noms des attributs sur lesquels portera le tri (postedDate, ici) et la mthode (reprsente par la chane ASC ou DESC pour signifier, respectivement, un tri croissant ou dcroissant). Vous pouvez utiliser plusieurs paires attribut/mthode en les sparant par des virgules : OrderBy("postedDate desc, note asc"), par exemple, demande de trier dabord sur les dates de publication (par ordre dcroissant), puis sur le champ note (par ordre croissant). Cette annotation na aucun impact sur lassociation dans la base de donnes le fournisseur de persistance est simplement inform quil doit utiliser une clause order by lorsque la collection est rcupre.
@OrderColumn

JPA 1.0 supportait le tri dynamique avec lannotation @OrderBy mais ne permettait pas de maintenir un ordre persistant. JPA 2.0 rgle ce problme laide dune nouvelle annotation, @OrderColumn (voir Listing 3.55), qui informe le fournisseur de persistance quil doit grer la liste trie laide dune colonne spare contenant un index.
Listing3.55: LAPI de @OrderColumn est semblable celle de @Column
@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OrderColumn { String name() default ""; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; boolean contiguous() default true; int base() default 0; String table() default ""; }

Reprenons lexemple des articles et de leurs commentaires en les modifiant lgrement. Cette fois-ci, lentit Comment du Listing3.56 na plus dattribut postedDate: il ny a donc plus moyen de trier chronologiquement les commentaires.
Listing3.56: Entit Comment sans date de publication
@Entity public class Comment { @Id @GeneratedValue

Chapitre 3

ORM: Object-Relational Mapping 125

private private private private

Long id; String nickname; String content; Integer note;

// Constructeurs, getters, setters }

Lentit News prsente dans le Listing3.57 peut alors annoter la relation avec @OrderColumn afin que le fournisseur de persistance associe lentit News une table contenant une colonne supplmentaire pour stocker lordre.
Listing3.57: Lordre des commentaires est maintenant persistant
@Entity public class News { @Id @GeneratedValue private Long id; @Column(nullable = false) private String content; @OneToMany(fetch = FetchType.EAGER) @OrderColumn("posted_index") private List<Comment> comments; // Constructeurs, getters, setters }

Dans le Listing3.57, @OrderColumn renomme la colonne supplmentaire en POSTED_INDEX. Si ce nom ntait pas redfini, cette colonne porterait un nom form de la concatnation de lentit rfrence et de la chane _ORDER (COMMENT_ORDER, ici). Le type de cette colonne doit tre numrique. Cette annotation a des consquences sur les performances car le fournisseur de persistance doit maintenant galement grer les modifications de lindex. Il doit maintenir le tri aprs chaque insertion, suppression ou rordonnancement. Si des donnes sont insres au milieu dune liste trie, le fournisseur devra retrier tout lindex. Les applications portables ne devraient pas supposer quune liste est toujours trie dans la base sous prtexte que certains SGBDR optimisent automatiquement leurs index pour que les donnes des tables apparaissent dans le bon ordre. Elles doivent plutt utiliser soit @OrderColumn, soit @OrderBy. Ces deux annotations ne peuvent pas tre utilises en mme temps.

126

Java EE 6 et GlassFish 3

Traduction de lhritage
Depuis leur cration, les langages orients objet utilisent le paradigme de lhritage. C++ autorise lhritage multiple alors que Java ne permet dhriter que dune seule classe. En programmation oriente objet, les dveloppeurs rutilisent souvent le code en hritant des attributs et des comportements de classes existantes. Nous venons de voir que les relations entre entits ont des quivalents directs dans les bases de donnes. Ce nest pas le cas avec lhritage car ce concept est totalement inconnu du modle relationnel. Il impose donc plusieurs contorsions pour tre traduit dans un SGBDR. Pour reprsenter un modle hirarchique dans un modle relationnel plat, JPA propose trois stratgies possibles:

Une seule table par hirarchie de classes. Lensemble des attributs de toute la hirarchie des entits est mis plat et regroup dans une seule table (il sagit de la stratgie par dfaut). Jointures entre sous-classes. Dans cette approche, chaque entit de la hirarchie, concrte ou abstraite, est associe sa propre table. Une table par classe concrte. Chaque entit concrte de la hirarchie est associe une table.
INFO

Le support de la stratgie une table par classe concrte est encore facultatif avec JPA2.0. Les applications portables doivent donc lviter tant que ce support na pas t officiellement dclar comme obligatoire dans toutes les implmentations.

Tirant parti de la simplicit dutilisation des annotations, JPA 2.0 fournit un support dclaratif pour dfinir et traduire les hirarchies dhritage comprenant des entits concrtes, des entits abstraites, des classes traduites et des classes transitoires. Lannotation @Inheritance sapplique une entit racine pour imposer une stratgie dhritage cette classe et ses classes filles. JPA traduit aussi la notion objet de redfinition qui permet aux attributs de la classe racine dtre redfinis dans les classes filles. Dans la section suivante, nous verrons galement comment utiliser les types daccs avec lhritage afin de mlanger les accs par champ et par proprit.

Chapitre 3

ORM: Object-Relational Mapping 127

Stratgies dhritage

JPA propose trois stratgies pour traduire lhritage. Lorsquil existe une hirarchie dentits, sa racine est toujours une entit qui peut dfinir la stratgie dhritage laide de lannotation @Inheritance. Si elle ne le fait pas, cest la stratgie par dfaut, consistant crer une seule table par hirarchie, qui sapplique. Pour expliquer chacune de ces stratgies, nous tudierons comment traduire les entits CD et Book, qui hritent toutes les deux de lentit Item (voir Figure3.22).
Figure3.22 Hirarchie dhritage entre CD, Book et Item.
<<entity>> Item
-id : Long -title : String -price : Float -description : String

<<entity>> Book
-isbn : String -publisher : String -nbOfPage : Integer -illustrations : Boolean

<<entity>> CD
-musicCompany : String -numberOfCDs : Integer -totalDuration : Float -gender : String

Item est lentit racine; elle possde un identifiant qui servira de cl primaire et dont hritent les entits CD et Book. Chacune de ces classes filles ajoute des attributs supplmentaires comme lISBN pour Book ou la dure totale dun album pour CD.

Stratgie utilisant une seule table

Il sagit de la stratgie de traduction de lhritage par dfaut, dans laquelle toutes les entits de la hirarchie sont associes la mme table. Il nest donc pas ncessaire dutiliser lannotation @Inheritance sur lentit racine, comme le montre le code dItem dans le Listing3.58.
Listing3.58: Lentit Item dfinit une stratgie dhritage avec une seule table
@Entity public class Item { @Id @GeneratedValue protected Long id; @Column(nullable = false) protected String title; @Column(nullable = false)

128

Java EE 6 et GlassFish 3

protected Float price; protected String description; // Constructeurs, getters, setters }

Item est la classe parente des entits Book (voir Listing3.59) et CD (voir Listing3.60).

Ces entits hritent des attributs dItem ainsi que de la stratgie dhritage par dfaut: elles nont donc pas besoin dutiliser lannotation @Inheritance.
Listing3.59: Book hrite dItem
@Entity public class Book extends Item { private private private private } String isbn; String publisher; Integer nbOfPage; Boolean illustrations;

// Constructeurs, getters, setters

Listing3.60: CD hrite dItem


@Entity public class CD extends Item { private String musicCompany; private Integer numberOfCDs; private Float totalDuration; private String gender; // Constructeurs, getters, setters }

Sans lhritage, ces trois entits seraient traduites en trois tables distinctes. Avec la stratgie de traduction de lhritage par une seule table, elles finiront toutes dans la mme table portant par dfaut le nom de la classe racine: ITEM. La structure de cette table est dcrite la Figure3.23. Comme vous pouvez le constater, la table ITEM rassemble tous les attributs des entits Item, Book et CD. Cependant, elle contient une colonne supplmentaire qui nest lie aucun des attributs des entits: la colonne discriminante, DTYPE. La table ITEM sera remplie darticles, de livres et de CD. Lorsquil accde aux donnes, le fournisseur de persistance doit savoir quelle entit appartient chaque ligne afin dinstancier la classe dobjet approprie (Item, Book ou CD): la colonne discriminante est donc l pour prciser explicitement le type de chaque colonne.

Chapitre 3

ORM: Object-Relational Mapping 129

Figure3.23 Structure de la table ITEM.

+ID DTYPE TITLE PRICE DESCRIPTION ILLUSTRATIONS ISBN NBOFPAGE PUBLISHER MUSICCOMPANY NUMBEROFCDS TOTALDURATION GENDER

bigint

ITEM

Nullable = false Nullable = true Nullable = false Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true Nullable = true

varchar(31) varchar(255) double varchar(255) smallint varchar(255) integer varchar(255) varchar(255) integer double varchar(255)

La Figure3.24 montre un fragment de la table ITEM contenant quelques donnes. Comme vous pouvez le constater, la stratgie avec une seule table a quelques dfauts. On voit, par exemple, que toutes les colonnes ne sont pas utiles toutes les entits: la premire ligne stocke les donnes dune entit Item (comme lindique sa colonne DTYPE) or les instances dItem nont quun titre, un prix et une description (voir Listing3.58); elles nont pas de compagnie de disque, dISBN, etc. Ces colonnes resteront donc toujours vides.
Figure3.24 Fragment de contenu de la table ITEM.
ID 1 2 3 4 5 DTYPE TITLE Item CD CD Book Book Pen SoulTrane ZootAllures The robots of dawn H2G2 PRICE DESCRIPTION 2,10 23,50 18 22,30 17,50 Beautiful black pen Fantastic jazz album One of the best of Zappa Robots everywhere Funny IT book ;0) Prestige Warner 0-554-456 1-278-983 MUSIC COMPANY ISBN ... ... ... ... ... ...

La colonne discriminante sappelle DTYPE par dfaut, est de type String (traduit en VARCHAR) et contient le nom de lentit. Si ce comportement ne vous convient pas, vous pouvez utiliser lannotation @DiscriminatorColumn pour modifier le nom et le type de cette colonne. Dans le Listing 3.61, nous avons renomm la colonne discriminante en DISC (au lieu de DTYPE) et modifi son type en Char au lieu de String; chaque entit change galement sa valeur discriminante en I pour Item, B pour Book (voir Listing3.62) et C pour CD (voir Listing3.63).
Listing3.61: Item redfinit la colonne discriminante
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn (name = "disc", discriminatorType = DiscriminatorType.CHAR) @ DiscriminatorValue("I")

130

Java EE 6 et GlassFish 3

public class Item { @Id @GeneratedValue private Long id; private String title; private Float price; private String description; // Constructeurs, getters, setters }

Lentit racine Item dfinit la colonne discriminante pour toute la hirarchie laide de lannotation @DiscriminatorColumn. Elle change ensuite sa propre valeur en I avec lannotation @DiscriminatorValue. Les entits filles doivent uniquement redfinir leur propre valeur discriminante.
Listing3.62: La valeur discriminante de Book est maintenant B
@Entity @DiscriminatorValue("B") public class Book extends Item { private private private private String isbn; String publisher; Integer nbOfPage; Boolean illustrations;

// Constructeurs, getters, setters }

Listing3.63: La valeur discriminante de CD est maintenant C


@Entity @DiscriminatorValue("C") public class CD extends Item { private private private private String musicCompany; Integer numberOfCDs; Float totalDuration; String gender;

// Constructeurs, getters, setters }

Le rsultat est prsent la Figure3.25.

Chapitre 3

ORM: Object-Relational Mapping 131

Figure3.25 La table ITEM avec un nom et des valeurs diffrentes pour la colonne discriminante.

ID 1 2 3 4 5

DISC TITLE I C C B B Pen SoulTrane ZootAllures The robots of dawn H2G2

PRICE DESCRIPTION 2,10 23,50 18 22,30 17,50

MUSIC COMPANY

ISBN

... ... ... ... ... ...

Beautiful black pen Fantastic jazz album Prestige One of the best of Zappa Warner Robots everywhere Funny IT book ;0)

0-554-456 1-278-983

Cette stratgie de table unique est la stratgie par dfaut; cest la plus facile comprendre et elle fonctionne bien lorsque la hirarchie est relativement simple et stable. En revanche, elle a quelques dfauts: lajout de nouvelles entits dans la hirarchie ou dattributs dans des entits existantes implique dajouter des colonnes la table, de migrer les donnes et de modifier les index. Cette stratgie exige galement que les colonnes des entits filles puissent recevoir la valeur NULL: si lISBN de lentit Book ntait pas nullable, par exemple, on ne pourrait pas insrer de CD car lentit CD na pas dattribut ISBN.
Stratgie par jointure

Dans cette stratgie, chaque entit de la hirarchie est associe sa propre table. Lentit racine est traduite dans une table dfinissant la cl primaire qui sera utilise par toutes les tables de la hirarchie, ainsi quune colonne discriminante. Chaque sous-classe est reprsente par une table distincte contenant ses propres attributs (non hrits de la classe racine) et une cl primaire qui fait rfrence celle de la table racine. Les classes filles nont en revanche pas de colonne discriminante. Pour implmenter une stratgie par jointure, on utilise lannotation @Inheritance comme dans le Listing3.64 (le code de CD et Book nest pas modifi).
Listing3.64: Lentit Item avec une stratgie par jointure
@Entity @Inheritance(strategy = InheritanceType.JOINED) public class Item { @Id @GeneratedValue protected Long id; protected String title; protected Float price; protected String description; // Constructeurs, getters, setters }

132

Java EE 6 et GlassFish 3

Du point de vue du dveloppeur, la stratgie par jointure est naturelle car chaque entit, quelle soit abstraite ou concrte, sera traduite dans une table distincte. LaFigure3.26 montre comment seront transposes les entits Item, Book et CD.
+#ID ILLUSTRATIONS ISBN NBOFPAGE PUBLISHER bigint

BOOK

Nullable

false true true true true

+ID DTYPE TITLE PRICE

bigint

ITEM

Nullable

false true true true true

+#ID

bigint

CD

Nullable

false true true true true

smallint Nullable varchar(255) Nullable integer Nullable varchar(255) Nullable

varchar(31) Nullable varchar(255) Nullable double Nullable

MUSICCOMPANY varchar(255) Nullable NUMBEROFCDS integer Nullable TOTALDURATION double GENDER Nullable varchar(255) Nullable

DESCRIPTION varchar(255) Nullable

Figure3.26 Traduction de lhritage avec une stratgie par jointure.

Vous pouvez l aussi utiliser les annotations @DiscriminatorColumn et @DiscriminatorValue dans lentit racine pour personnaliser la colonne discriminante et ses valeurs (la colonne DTYPE de la table ITEM). La stratgie par jointure est intuitive et proche de ce que vous connaissez du mcanisme dhritage. Cependant, elle a un impact sur les performances des requtes. En effet, son nom vient du fait que, pour recrer une instance dune sous-classe, il faut joindre sa table celle de la classe racine. Plus la hirarchie est profonde, plus il faudra donc de jointures pour recrer lentit feuille.
Stratgie une table par classe

Dans cette stratgie (une table par classe concrte), chaque entit est traduite dans sa propre table, comme avec la stratgie par jointure. La diffrence est quici tous les attributs de lentit racine seront galement traduits en colonnes de la table associe lentit fille. Du point de vue de la base de donnes, cette stratgie utilise donc un modle dnormalis. Ici, il ny a pas de table partage, pas de colonne partage ni de colonne discriminante. La seule exigence est que toutes les tables de la hirarchie doivent partager la mme cl primaire. Adapter notre exemple cette stratgie consiste simplement prciser TABLE_PER_ CLASS dans lannotation @Inheritance de lentit racine Item (voir Listing3.65).
Listing3.65: Lentit Item avec une stratgie une table par classe
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Item { @Id @GeneratedValue protected Long id;

Chapitre 3

ORM: Object-Relational Mapping 133

protected String title; protected Float price; protected String description; // Constructeurs, getters, setters }

La Figure3.27 montre les tables ITEM, BOOK et CD obtenues. Vous remarquerez que BOOK et CD dupliquent les colonnes ID, TITLE, PRICE et DESCRIPTION de la table ITEM et que les tables ne sont pas lies.
+ID TITLE PRICE ILLUSTRATIONS DESCRIPTION ISBN NBOFPAGE PUBLISHER bigint varchar(255) double smallint varchar(255) varchar(255) integer varchar(255)

BOOK

Nullable Nullable Nullable Nullable Nullable Nullable Nullable Nullable

false true true true true true true true

+ID TITLE PRICE DESCRIPTION

bigint varchar(255) double varchar(255)

ITEM

Nullable false Nullable true Nullable true Nullable true

+ID MUSICCOMPANY NUMBEROFCDS TITLE TOTALDURATION PRICE DESCRIPTION GENDER

bigint varchar(255) integer varchar(255) double double varchar(255) varchar(255)

CD

Nullable Nullable Nullable Nullable Nullable Nullable Nullable Nullable

false true true true true true true true

Figure3.27 Les tables BOOK et CD dupliquent les colonnes dITEM.

Chaque table peut tre redfinie en annotant chaque entit avec @Table. Cette stratgie est performante lorsque lon interroge des instances dune seule entit car lon se retrouve alors dans un scnario comparable lutilisation de la stratgie une seule table la requte ne porte que sur une table. Linconvnient est que les requtes polymorphiques travers une hirarchie de classes sont plus coteuses que les deux autres stratgies: pour, par exemple, trouver tous les articles, dont les livres et les CD, il faut interroger toutes les tables des sous-classes avec une opration en utilisant une UNION, ce qui est coteux lorsquil y a beaucoup de donnes.
Redfinition des attributs

Avec la stratgie une table par classe, les colonnes de la classe racine sont dupliques dans les classes filles en portant le mme nom. Un problme se pose donc si lon utilise une base existante o ces colonnes ont des noms diffrents. Pour le rsoudre, JPA utilise lannotation @AttributeOverride pour redfinir lassociation de la colonne et @AttributeOverrides pour en redfinir plusieurs. Pour renommer les colonnes ID, TITLE et DESCRIPTION dans les tables BOOK et CD, par exemple, le code de lentit Item ne change pas, mais Book (voir Listing3.66) et CD (voir Listing3.67) doivent utiliser lannotation @AttributeOverride.

134

Java EE 6 et GlassFish 3

Listing3.66: Book redfinit certaines colonnes dItem


@Entity @AttributeOverrides({ @AttributeOverride(name = "id", column = @Column(name = "book_id")), @AttributeOverride(name = "title", column = @Column(name = "book_title")), @AttributeOverride(name = "description", column = @Column(name = "book_description")) }) public class Book extends Item { private private private private String isbn; String publisher; Integer nbOfPage; Boolean illustrations;

// Constructeurs, getters, setters }

Listing3.67: CD redfinit certaines colonnes dItem


@Entity @AttributeOverrides({ @AttributeOverride(name = "id", column = @Column(name = "cd_id")), @AttributeOverride(name = "title", column = @Column(name = "cd_title")), @AttributeOverride(name = "description", column = @Column(name = "cd_description")) }) public class CD extends Item { private private private private String musicCompany; Integer numberOfCDs; Float totalDuration; String gender;

// Constructeurs, getters, setters }

Ici, il faut redfinir plusieurs attributs et donc utiliser @AttributeOverrides, qui prend en paramtre un tableau dannotations @AttributeOverride. Chacune delles dsigne un attribut de lentit Item et redfinit lassociation de la colonne laide dune annotation @Column. Ainsi, name = "title" dsigne lattribut title dItem et @Column(name = "cd_title") informe le fournisseur de persistance que cet attri-

Chapitre 3

ORM: Object-Relational Mapping 135

but doit tre traduit par une colonne CD_TITLE. Le rsultat obtenu est prsent la Figure3.28.
+BOOK_ID bigint

BOOK

Nullable

false true true true true true true true

+ID TITLE PRICE

bigint

ITEM

Nullable

false true true true

+CD_ID

bigint

CD

Nullable

false true true true true true true true

BOOK TITLE varchar(255) Nullable BOOK DESCRIPTION varchar(255) Nullable PRICE ILLUSTRATIONS ISBN NBOFPAGE PUBLISHER double Nullable small nt Nullable varchar(255) Nullable nteger Nullable varchar(255) Nullable

varchar(255) Nullable double Nullable

CD TITLE varchar(255) Nullable CD DESCRIPTION varchar(255) Nullable PRICE double Nullable MUSICCOMPANY varchar(255) Nullable NUMBEROFCDS integer Nullable TOTALDURATION double GENDER Nullable varchar(255) Nullable

DESCRIPTION varchar(255) Nullable

Figure3.28 Les tables BOOK et CD ont redfini des colonnes dITEM.

INFO Dans la section "Classes intgrables" de ce chapitre, nous avons vu quun objet intgrable pouvait tre partag par plusieurs entits (Address tait intgr dans Customer et Order). Les objets intgrables tant des composantes part entire de lentit qui les intgre, leurs colonnes seront galement dupliques dans les tables de chaque entit. Vous pouvez alors utiliser lannotation @AttributeOverrides si vous avez besoin de redfinir les colonnes des objets intgrables.

Type de classes dans une hirarchie dhritage

Lexemple utilis pour expliquer les stratgies de traduction de lhritage nutilise que des entits, mais les entits nhritent pas que dentits. Une hirarchie de classes peut contenir un mlange dentits, de classes qui ne sont pas des entits (classes transitoires), dentits abstraites et de superclasses dj traduites. Hriter de ces diffrents types de classes a un impact sur la traduction de la hirarchie.
Entits abstraites

Dans les exemples prcdents, lentit Item tait concrte. Elle tait annote par @ Entity et ne comprenait pas de mot-cl abstract; mais une classe abstraite peut galement tre dsigne comme une entit. Elle ne diffre dune entit concrte que parce quelle ne peut pas tre directement instancie avec le mot-cl new, mais elle fournit une structure de donnes que partageront toutes ses entits filles (Book et CD) et elle respecte les stratgies de traduction dhritage. Du point de vue du fournisseur de persistance, la seule diffrence se situe du ct de Java, pas dans la correspondance en table.

136

Java EE 6 et GlassFish 3

Non-entits

Les non-entits sont galement appeles classes transitoires, ce qui signifie quelles sont des POJO. Une entit peut hriter dune non-entit ou peut tre tendue par une non-entit. La modlisation objet et lhritage permettent de partager les tats et les comportements; dans une hirarchie de classes, les non-entits peuvent donc servir fournir une structure de donnes commune leurs entits filles. Ltat dune superclasse non entit nest pas persistant car il nest pas gr par le fournisseur de persistance (noubliez pas que la condition pour quune classe le soit est la prsence de lannotation @Entity). Comme le montre le Listing3.68, Item est dsormais une non-entit.
Listing3.68: Item est un simple POJO sans annotation @Entity
public class Item { protected String title; protected Float price; protected String description; // Constructeurs, getters, setters }

Lentit Book du Listing3.69 hrite dItem; le code Java peut donc accder aux attributs title, price et description ainsi qu toutes les mthodes dItem. Que cette dernire soit concrte ou abstraite naura aucune influence sur la traduction finale.
Listing3.69: Lentit Book hrite dun POJO
@Entity public class Book extends Item { @Id @GeneratedValue private Long id; private String isbn; private String publisher; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Chapitre 3

ORM: Object-Relational Mapping 137

est une entit qui hrite dItem, mais seuls les attributs de Book seront stocks dans une table. Aucun attribut dItem napparat dans la structure de la table du Listing3.70. Pour quun Book soit persistant, vous devez crer une instance de Book, initialiser les attributs que vous souhaitez (title, price, isbn, publisher, etc.), mais seuls ceux de Book (id, isbn, etc.) seront stocks.
Book

Listing3.70: La table BOOK ne contient aucun des attributs dItem


create table BOOK ( ID BIGINT not null, ILLUSTRATIONS SMALLINT, ISBN VARCHAR(255), NBOFPAGE INTEGER, PUBLISHER VARCHAR(255), primary key (ID) );

Superclasse "mapped"

JPA dfinit un type de classe spciale, appele superclasse "mapped", qui partage son tat, son comportement ainsi que les informations de traduction des entits qui en hritent. Cependant, les superclasses "mapped" ne sont pas des entits, elles ne sont pas gres par le fournisseur de persistance, nont aucune table qui leur soit associe et ne peuvent pas tre interroges ni faire partie dune relation; en revanche, elles peuvent fournir des proprits de persistance aux entits qui en hritent. Les superclasses "mapped" ressemblent aux classes intgrables, sauf quelles peuvent tre utilises avec lhritage. Elles sont annotes par @MappedSuperclass. Dans le Listing3.71, la classe racine Item est annote par @MappedSuperclass, pas par @Entity. Elle dfinit une stratgie de traduction de lhritage (JOINED) et annote certains de ces attributs avec @Column. Cependant, les superclasses "mapped" ntant pas associes des tables, lannotation @Table nest pas autorise.
Listing3.71: Item est une superclasse "mapped"
@MappedSuperclass @Inheritance(strategy = InheritanceType.JOINED) public class Item { @Id @GeneratedValue protected Long id; @Column(length = 50, nullable = false) protected String title;

138

Java EE 6 et GlassFish 3

protected Float price; @Column(length = 2000) protected String description; // Constructeurs, getters, setters }

Comme vous pouvez le constater, les attributs title et description sont annots par @Column. Le Listing3.72 montre lentit Book qui hrite dItem.
Listing3.72: Book hrite dune superclasse "mapped"
@Entity public class Book extends Item { private private private private String isbn; String publisher; Integer nbOfPage; Boolean illustrations;

// Constructeurs, getters, setters }

Cette hirarchie sera traduite en une seule table. Item nest pas une entit et na donc aucune table associe. Les attributs dItem et de Book seront traduits en colonnes de la table BOOK les superclasses "mapped" partageant galement leurs informations de traduction, les annotations @Column dItem seront donc hrites. Le Listing3.73 montre que les colonnes TITLE et DESCRIPTION de la table BOOK ont bien t modifies selon les annotations dItem.
Listing3.73: Structure de la table BOOK
create table BOOK ( ID BIGINT not null, TITLE VARCHAR(50) not null, PRICE DOUBLE(52, 0), ILLUSTRATIONS SMALLINT, DESCRIPTION VARCHAR(2000), ISBN VARCHAR(255), NBOFPAGE INTEGER, PUBLISHER VARCHAR(255), primary key (ID) );

Chapitre 3

ORM: Object-Relational Mapping 139

Rsum
Grce la configuration par exception, il ny a pas besoin de faire grand-chose pour traduire des entits en tables: il faut simplement informer le fournisseur de persistance quune classe est une entit (avec @Entity) et quun attribut est un identifiant (avec @Id), et JPA soccupe du reste. Ce chapitre aurait t bien plus court sil stait content du comportement par dfaut, mais JPA fournit galement un grand nombre dannotations pour adapter le moindre dtail de lORM. Les annotations lmentaires permettent dadapter la traduction des attributs (@Basic, etc.) ou des classes. Vous pouvez ainsi modifier le nom de la table ou le type de la cl primaire, voire empcher le stockage avec lannotation @Transient. partir de JPA2.0, il devient possible de stocker dans la base de donnes des collections de types de base ou dobjets intgrables. Selon votre modle, vous pouvez traduire des relations (@OneToOne, @ManyToMany, etc.) de directions et de cardinalits diffrentes. Il en va de mme pour lhritage (@Inheritance, @MappedSuperclass, etc.), o vous pouvez choisir entre plusieurs stratgies pour traduire une hirarchie dentits et de non-entits.
@Temporal,

Ce chapitre sest intress la partie statique de JPA, la faon dassocier des entits des tables. Le chapitre suivant prsentera les aspects dynamiques: linterrogation de ces entits.

4
Gestion des objets persistants
LAPI de persistance de Java, JPA, a deux aspects. Le premier est la possibilit dassocier des objets une base de donnes relationnelle. La configuration par exception permet aux fournisseurs de persistance de faire lessentiel du travail sans devoir ajouter beaucoup de code, mais la richesse de JPA tient galement la possibilit dadapter ces associations laide dannotations ou de descriptions XML. Que ce soit une modification simple (changer le nom dune colonne, par exemple) ou une adaptation plus complexe (pour traduire lhritage), JPA offre un large spectre de possibilits. Vous pouvez donc associer quasiment nimporte quel modle objet une base de donnes existante. Le second aspect concerne linterrogation de ces objets une fois quils ont t associs une base. lment central de JPA, le gestionnaire dentits permet de manipuler de faon standard les instances des entits. Il fournit une API pour crer, rechercher, supprimer et synchroniser les objets avec la base de donnes et permet dexcuter diffrentes sortes de requtes JPQL sur les entits, comme des requtes dynamiques, statiques ou natives. Le gestionnaire dentits autorise galement la mise en place de mcanismes de verrouillage sur les donnes. Le monde des bases de donnes relationnelles repose sur SQL (Structured Query Language). Ce langage de programmation a t conu pour faciliter la gestion des donnes relationnelles (rcupration, insertion, mise jour et suppression), et sa syntaxe est oriente vers la manipulation de tables. Vous pouvez ainsi slectionner des colonnes de tables constitues de lignes, joindre des tables, combiner les rsultats de deux requtes SQL laide dune union, etc. Ici, il ny a pas dobjets mais uniquement des lignes, des colonnes et des tables. Dans le monde Java, o lon manipule des objets, un langage conu pour les tables (SQL) doit tre un peu dform pour convenir un langage objets (Java). Cest l que JPQL (Java Persistence Query Language) entre en jeu.

142

Java EE 6 et GlassFish 3

JPQL est le langage quutilise JPA pour interroger les entits stockes dans une base de donnes relationnelle. Sa syntaxe ressemble celle de SQL mais opre sur des objets entits au lieu dagir directement sur les tables. JPQL ne voit pas la structure de la base de donnes sous-jacente et ne manipule ni les tables ni les colonnes uniquement des objets et des attributs. Il utilise pour cela la notation pointe que connaissent bien tous les dveloppeurs Java. Dans ce chapitre, nous verrons comment grer les objets persistants. Nous apprendrons comment raliser les oprations CRUD (Create, Read, Update et Delete) avec le gestionnaire dentits et crerons des requtes complexes en JPQL. La fin du chapitre expliquera comment JPA gre la concurrence daccs aux donnes.

Interrogation dune entit


Comme premier exemple, tudions une requte simple : trouver un livre par son identifiant. Le Listing4.1 prsente une entit Book utilisant lannotation @Id pour informer le fournisseur de persistance que lattribut id doit tre associ une cl primaire.
Listing4.1: Entit Book simple
@Entity public class Book { @Id private private private private private private private

Long id; String title; Float price; String description; String isbn; Integer nbOfPage; Boolean illustrations;

// Constructeurs, getters, setters }

Lentit Book contient les informations pour lassociation. Ici, elle utilise la plupart des valeurs par dfaut: les donnes seront donc stockes dans une table portant le mme nom que lentit (BOOK) et chaque attribut sera associ une colonne homonyme. Nous pouvons maintenant utiliser une classe Main distincte (voir Listing4.2) qui utilise linterface javax.persistence.EntityManager pour stocker une instance de Book dans la table.

Chapitre 4

Gestion des objets persistants 143

Listing4.2: Classe Main pour stocker et rcuprer une entit Book


public class Main { public static void main(String[] args) { // 1-Cration dune instance de lentit Book. Book book = new Book(); book.setId(1234L); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction by Douglas Adams."); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); // 2- Cration dun gestionnaire dentits et dune // transaction. EntityManagerFactory emf = Persistence.createEntityManagerFactory("chapter04PU"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); // 3-Stockage du livre dans la base de donnes. tx.begin(); em.persist(book); tx.commit(); // 4-Rcupration du livre par son identifiant. book = em.find(Book.class, 1234L); System.out.println(book); em.close(); emf.close(); } }

La classe Main du Listing4.2 utilise quatre tapes pour stocker un livre dans la base de donnes puis le rcuprer. 1. Cration dune instance de lentit Book. Les entits sont des POJO grs par le fournisseur de persistance. Du point de vue de Java, une instance de classe doit tre cre avec le mot-cl new, comme nimporte quel POJO. Il faut bien insister sur le fait qu ce stade le fournisseur de persistance ne connat pas encore lobjet Book. 2. Cration dun gestionnaire dentits et dune transaction. Cest la partie importante du code car on a besoin dun gestionnaire dentits pour les manipuler. On cre donc dabord une fabrique de gestionnaires dentits pour lunit de

144

Java EE 6 et GlassFish 3

persistance chapter04PU. Cette fabrique sert ensuite fournir un gestionnaire (la variable em) qui permettra de crer une transaction (la variable tx), puis stocker et rcuprer un objet Book. 3. Stockage du livre dans la base de donnes. Le code lance une transaction (tx.begin()) et utilise la mthode EntityManager.persist() pour insrer une instance de Book. Lorsque la transaction est valide (tx.commit()), les donnes sont crites dans la base de donnes. 4. Rcupration dun livre par son identifiant. L encore, on utilise le gestionnaire dentits afin de retrouver un livre partir de son identifiant laide de la mthode EntityManager.find(). Vous remarquerez que ce code ne contient aucune requte SQL ou JPQL ni dappels JDBC. La Figure 4.1 montre linteraction entre ces composants. La classe Main interagit avec la base de donnes sous-jacente via linterface EntityManager, qui fournit un ensemble de mthodes standard permettant de raliser des oprations sur lentit Book. En coulisse, cet EntityManager utilise le fournisseur de persistance pour interagir avec la base de donnes. Lorsque lon appelle lune des mthodes de lEntityManager, le fournisseur de persistance produit et excute une instruction SQL via le pilote JDBC correspondant.
Main <<Interface>> EntityManager
+persist(entity : Object) : void +find(entityClass, : Class<T>, primaryKey : Object) : <T>

SQL / JDBC

Base de donnes

Book
-id : Long -title : String -price : Float -description : String -nbOfPage : Integer -illustrations : Boolean

Figure4.1 Le gestionnaire dentits interagit avec lentit et la base de donnes sous-jacente.

Quel pilote JDBC utiliser? Comment se connecter la base? Quel est le nom de la base? Toutes ces informations sont absentes du code prcdent. Lorsque la classe Main cre une fabrique EntityManagerFactory, elle lui passe le nom dune unit de persistance en paramtre chapter04PU ici. Cette unit de persistance indique au gestionnaire dentits le type de la base utiliser et les paramtres de connexion:

Chapitre 4

Gestion des objets persistants 145

toutes ces informations sont prcises dans le fichier ting4.3) qui doit tre dploy avec les classes.

persistence.xml

(voir Lis-

Listing4.3: Le fichier persistence.xml dfinit lunit de persistance


<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="chapter04PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>com.apress.javaee6.chapter04.Book</class> <properties> <property name="eclipselink.target-database" value="DERBY"/> <property name="eclipselink.jdbc.driver" value= "org.apache.derby.jdbc.ClientDriver"/> <property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/chapter04DB"/> <property name="eclipselink.jdbc.user" value="APP"/> <property name="eclipselink.jdbc.password" value="APP"/> </properties> </persistence-unit> </persistence>

Lunit de persistance chapter04PU dfinit une connexion JDBC pour la base de donnes Derby nomme chapter04DB. Elle se connecte cette base sous le compte utilisateur APP avec le mot de passe APP. Le marqueur <class> demande au fournisseur de persistance de grer la classe Book. Pour que ce code fonctionne, le SGBDR Derby doit sexcuter sur le port 1527 et les classes Book et Main doivent avoir t compiles et dployes avec ce fichier META-INF/persistence.xml. Si vous avez activ la trace dexcution, vous verrez apparatre quelques instructions SQL mais, grce lAPI dEntityManager, votre code manipule des objets de faon oriente objet, sans instruction SQL ni appel JDBC.

Le gestionnaire dentits
Le gestionnaire dentits est une composante essentielle de JPA. JPA gre ltat et le cycle de vie des entits et les interroge dans un contexte de persistance. Cest galement lui qui est responsable de la cration et de la suppression des instances dentits persistantes et qui les retrouve partir de leur cl primaire. Il peut les verrouiller

146

Java EE 6 et GlassFish 3

pour les protger des accs concurrents en utilisant un verrouillage optimiste ou pessimiste et se servir de requtes JPQL pour rechercher celles qui rpondent certains critres. Lorsquun gestionnaire dentits obtient une rfrence une entit, celle-ci est dite gre. Avant cela, elle ntait considre que comme un POJO classique (elle tait dtache). Lavantage de JPA est que les entits peuvent tre utilises comme des objets normaux par diffrentes couches de lapplication et devenir gres par le gestionnaire dentits lorsquil faut charger ou insrer des donnes dans la base. Lorsquune entit est gre, il devient possible deffectuer des oprations de persistance: le gestionnaire dentits synchronisera automatiquement ltat de lentit avec la base de donnes. Lorsquune entit est dtache (non gre), elle redevient un simple POJO et peut tre utilise par les autres couches (une couche de prsentation JSF, par exemple) sans que son tat ne soit synchronis avec la base. Le vritable travail de persistance commence avec le gestionnaire dentits. Linterface javax.persistence.EntityManager est implmente par un fournisseur de persistance qui produira et excutera des instructions SQL. Le Listing4.4 prsente son API de manipulation des entits.
Listing4.4: EntityManager API
public interface EntityManager { public public public public public public public public public EntityTransaction getTransaction(); EntityManagerFactory getEntityManagerFactory(); void close(); boolean isOpen();

void persist(Object entity); <T> T merge(T entity); void remove(Object entity); <T> T find(Class<T> entityClass, Object primaryKey); <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode); public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties); public <T> T getReference(Class<T> entityClass, Object primaryKey); public void flush(); public void setFlushMode(FlushModeType flushMode); public FlushModeType getFlushMode();

Chapitre 4

Gestion des objets persistants 147

public void lock(Object entity, LockModeType lockMode); public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties); public void refresh(Object entity); public void refresh(Object entity, LockModeType lockMode); public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties); public void clear(); public void detach(Object entity); public boolean contains(Object entity); public Map<String, Object> getProperties(); public Set<String> getSupportedProperties(); public public public public public createQuery(String qlString); createQuery(QueryDefinition qdef); createNamedQuery(String name); createNativeQuery(String sqlString); createNativeQuery(String sqlString, Class resultClass); public Query createNativeQuery(String sqlString, String resultSetMapping); public void joinTransaction(); public <T> T unwrap(Class<T> cls); public Object getDelegate(); public QueryBuilder getQueryBuilder(); } Query Query Query Query Query

Dans la section suivante, nous verrons comment obtenir une instance dEntityManager. Ne soyez pas effray par lAPI du Listing4.4: ce chapitre expliquera la plupart de ces mthodes.
Obtenir un gestionnaire dentits

Le gestionnaire dentits est linterface centrale pour interagir avec les entits, mais lapplication doit dabord en obtenir un. Selon que lon soit dans un environnement gr par un conteneur (comme nous le verrons au Chapitre6 avec les EJB) ou gr par une application, le code peut tre trs diffrent. Dans le premier cas, par exemple, cest le conteneur qui gre les transactions, ce qui signifie que lon na pas besoin dappeler explicitement les oprations commit() ou rollback(), contrairement un environnement gr par lapplication.

148

Java EE 6 et GlassFish 3

Le terme "gr par lapplication" signifie que cest lapplication qui est responsable de lobtention explicite dune instance dEntityManager et de la gestion de son cycle de vie (elle doit fermer le gestionnaire dentits lorsquelle nen a plus besoin, par exemple). Dans le Listing4.2, nous avons vu comment une classe qui sexcutait dans lenvironnement JavaSE obtenait une instance du gestionnaire: elle utilise la classe Persistence pour lancer une fabrique EntityManagerFactory associe une unit de persistance (chapter04PU), et cette fabrique sert ensuite crer un gestionnaire dentits. Lutilisation dune fabrique pour crer un gestionnaire dentits est assez simple, mais ce qui diffrencie un environnement gr par lapplication dun environnement gr par un conteneur est la faon dont on obtient cette fabrique. Dans un environnement gr par un conteneur, lapplication sexcute dans une servlet ou dans un conteneur dEJB. Avec JavaEE, la mthode la plus classique pour obtenir un gestionnaire dentits consiste alors soit utiliser lannotation @PersistenceContext pour en injecter un, soit utiliser JNDI. Un composant qui sexcute dans un conteneur (servlet, EJB, service web, etc.) na en revanche pas besoin de crer ou de fermer le gestionnaire dentits puisque son cycle de vie est gr par le conteneur. Le Listing4.5 montre le code dune session sans tat dans laquelle on injecte une rfrence lunit de persistance chapter04PU.
Listing4.5: Injection dune rfrence un gestionnaire dentits dans un EJB sans tat
@Stateless public class BookBean { @PersistenceContext(unitName = "chapter04PU") private EntityManager em; public void createBook() { // Cration dune instance de Book. Book book = new Book(); book.setId(1234L); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction by Douglas Adams."); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); // Stockage de linstance dans la base de donnes. em.persist(book);

Chapitre 4

Gestion des objets persistants 149

// Rcupration dune instance par son identifiant. book = em.find(Book.class, 1234L); System.out.println(book); } }

Le code du Listing4.5 est bien plus simple que celui du Listing4.2: il ny a pas besoin des objets Persistence ou EntityManagerFactory car le gestionnaire dentits est inject par le conteneur. En outre, les beans sans tat grant les transactions, il nest pas non plus ncessaire dappeler explicitement commit() ou rollback(). Nous reviendrons sur ce style de gestionnaire dentits au Chapitre6.
Contexte de persistance

Avant dexplorer en dtail lAPI de lEntityManager, vous devez avoir compris un concept essentiel: le contexte de persistance, qui est lensemble des instances dentits gres un instant donn. Dans un contexte de persistance, il ne peut exister quune seule instance dentit avec le mme identifiant de persistance si, par exemple, une instance de Book ayant lidentifiant 1234 existe dans le contexte de persistance, aucun autre livre portant cet identifiant ne peut exister dans le mme contexte. Seules les entits contenues dans le contexte de persistance sont gres par le gestionnaire dentits leurs modifications seront refltes dans la base de donnes. Le gestionnaire dentits modifie ou consulte le contexte de persistance chaque appel dune mthode de linterface javax.persistence.EntityManager. Lorsque la mthode persist() est appele, par exemple, lentit passe en paramtre sera ajoute au contexte de persistance si elle ne sy trouve pas dj. De mme, lorsque lon recherche une entit partir de son identifiant, le gestionnaire dentits vrifie dabord si elle existe dj dans le contexte de persistance. Ce contexte peut donc tre considr comme un cache de premier niveau : cest un espace rduit o le gestionnaire stocke les entits avant dcrire son contenu dans la base de donnes. Les objets ne vivent dans le contexte de persistance que le temps de la transaction. La configuration dun gestionnaire dentits est lie la fabrique qui la cr. Que lon se trouve dans un environnement gr par lapplication ou par un conteneur, la fabrique a besoin dune unit de persistance pour crer le gestionnaire. Celle-ci, dfinie dans le fichier META-INF/persistence.xml (voir Listing 4.6), prcise les informations ncessaires pour la connexion la base de donnes et donne la liste

150

Java EE 6 et GlassFish 3

des entits qui pourront tre gres dans un contexte persistant. Elle porte un nom (chapter04PU) et a un ensemble dattributs.
Listing4.6: Unit de persistance avec un ensemble dentits grables
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="chapter04PU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>com.apress.javaee6.chapter04.Book</class> <class>com.apress.javaee6.chapter04.Customer</class> <class>com.apress.javaee6.chapter04.Address</class> <properties> <property name="eclipselink.target-database" value="DERBY"/> <property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/> <property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/chapter04DB"/> <property name="eclipselink.jdbc.user" value="APP"/> <property name="eclipselink.jdbc.password" value="APP"/> </properties> </persistence-unit> </persistence>

Lunit de persistance est le pont qui relie le contexte de persistance et la base de donnes. Dun ct, les marqueurs <class> donnent la liste des entits pouvant tre gres dans le contexte de persistance, de lautre, le fichier donne toutes les informations permettant de se connecter physiquement la base. Ici, nous sommes dans un environnement gr par lapplication (transaction-type="RESOURCE_ LOCAL"). Dans un environnement gr par un conteneur, le fichier persistence.xml dfinirait une source de donnes la place des informations de connexion et le type de transaction serait JTA (transaction-type="JTA").
Manipulation des entits

Le gestionnaire dentits sert galement crer des requtes JPQL complexes pour rcuprer une ou plusieurs entits. Lorsquelle manipule des entits uniques, linterface EntityManager peut tre considre comme un DAO (Data Access Object) gnrique permettant deffectuer les oprations CRUD sur nimporte quelle entit (voir Tableau4.1).

Chapitre 4

Gestion des objets persistants 151

Tableau 4.1: Mthodes de linterface EntityManager pour manipuler les entits

Mthode
void persist(Object entity) <T> T find(Class<T> entityClass, Object primaryKey) <T> T getReference(Class<T> entityClass, Object primaryKey) void remove(Object entity) <T> T merge(T entity) void refresh(Object entity)

Description Cre une instance gre et persistante. Recherche une entit de la classe et de la cl indiques. Obtient une instance dont ltat peut tre rcupr de faon paresseuse. Supprime linstance dentit du contexte de persistance et de la base de donnes. Fusionne ltat de lentit indique dans le contexte de persistance courant. Rafrachit ltat de linstance partir de la base de donnes en crasant les ventuelles modifications apportes lentit. Synchronise le contexte de persistance avec la base de donnes. Vide le contexte de persistance. Toutes les entits gres deviennent dtaches. Supprime lentit indique du contexte de persistance. Teste si linstance est une entit gre appartenant au contexte de persistance courant

void flush() void clear() void clear(Object entity) boolean contains(Object entity)

Pour mieux comprendre ces mthodes, nous utiliserons un exemple simple dune relation 11 unidirectionnelle entre un client et son adresse. Les deux entits Customer (voir Listing4.7) et Address (voir Listing4.8) ont des identifiants produits automatiquement (grce lannotation @GeneratedValue), et Customer rcupre lAddress de faon paresseuse (cest--dire uniquement lorsquil en a besoin).
Listing4.7: Lentit Customer avec une relation 11 unidirectionnelle avec Address
@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName;

152

Java EE 6 et GlassFish 3

private String email; @OneToOne (fetch = FetchType.LAZY) @JoinColumn(name = "address_fk") private Address address; // Constructeurs, getters, setters }

Listing4.8: Lentit Address


@Entity public class Address { @Id @GeneratedValue private Long id; private String street1; private String city; private String zipcode; private String country; // Constructeurs, getters, setters }

Ces deux entits seront traduites dans la base de donnes avec la structure prsente la Figure4.2. Notez que la colonne ADDRESS_FK est la colonne de type cl trangre permettant daccder ADDRESS.

+ID LASTNAME EMAIL FIRSTNAME #ADDRESS FK

CUSTOMER
bigint varchar(255) varchar(255) varchar(255) bigint

Nullable = false Nullable = false Nullable = true Nullable = true Nullable = true

+ID STREET1 ZIPCODE COUNTRY CITY

bigint varchar(255) varchar(255) varchar(255) varchar(255)

ADDRESS

Nullable = false Nullable = true Nullable = true Nullable = true Nullable = true

Figure4.2 Les tables CUSTOMER et ADDRESS.

Pour plus de visibilit, les fragments de code utiliss dans la section suivante supposent que lattribut em est de type EntityManager et que tx est de type EntityTransaction.
Rendre une entit persistante

Rendre une entit persistante signifie que lon insre les donnes dans la base si elles ne sy trouvent pas dj (sinon une exception sera lance). Pour ce faire, il faut crer une instance de lentit avec loprateur new, initialiser ses attributs, lier une entit

Chapitre 4

Gestion des objets persistants 153

une autre lorsquil y a des relations et, enfin, appeler la mthode EntityManager. persist() comme le montre le cas de test JUnit du Listing4.9.
Listing4.9: Rendre persistant un Customer avec une Address
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); Address address = new Address("Ritherdon Rd", "London", "8QE", "UK"); customer.setAddress(address); tx.begin(); em.persist(customer); em.persist(address); tx.commit(); assertNotNull(customer.getId()); assertNotNull(address.getId());

Dans le Listing 4.9, le client et ladresse ne sont que deux objets qui rsident dans la mmoire de la JVM. Tous les deux ne deviennent des entits gres que lorsque le gestionnaire dentits (la variable em) les prend en compte en les rendant persistantes (em.persist(customer)). Ds cet instant, les deux objets deviennent candidats une insertion dans la base de donnes. Lorsque la transaction est valide (tx.commit()), les donnes sont crites dans la base: une ligne dadresse est ajoute la table ADDRESS et une ligne client, la table CUSTOMER. Lentit Customer tant la propritaire de la relation, sa table contient une cl trangre vers ADDRESS. Les deux expressions assertNotNull testent que les deux entits ont bien reu un identifiant (fourni automatiquement par le fournisseur de persistance grce aux annotations). Notez lordre dappel des mthodes persist(): on rend dabord le client persistant, puis son adresse. Si lon avait fait linverse, le rsultat aurait t le mme. Plus haut, nous avons crit que lon pouvait considrer le gestionnaire dentits comme un cache de premier niveau: tant que la transaction nest pas valide, les donnes restent en mmoire et il ny a aucun accs la base. Le gestionnaire dentits met les donnes en cache et, lorsquil est prt, les crit dans lordre quattend la base de donnes (afin de respecter les contraintes dintgrit). cause de la cl trangre que contient la table CUSTOMER, linstruction insert dans ADRESS doit seffectuer en premier, suivie de celle de CUSTOMER.

154

Java EE 6 et GlassFish 3

INFO La plupart des entits de ce chapitre nimplmentent pas linterface Serializable car elles nen ont tout simplement pas besoin pour tre rendues persistantes. Elles sont passes par rfrence dune mthode lautre et ce nest que lorsquil faut les rendre persistantes que lon appelle la mthode EntityManager.persist(). Si, toutefois, vous devez passer des entits par valeur (appel distant, conteneur EJB externe, etc.), celles-ci doivent implmenter linterface java.io.Serializable pour indiquer au compilateur quil faut que tous les champs de lentit soient srialisables afin quune instance puisse tre srialise dans un flux doctets et passe par RMI (Remote Method Invocation).

Recherche par identifiant

Nous pouvons utiliser deux mthodes pour trouver une entit par son identifiant. La premire est EntityManager.find(), qui prend deux paramtres: la classe de lentit et lidentifiant (voir Listing4.10). Cet appel renvoie lentit si elle a t trouve, null sinon.
Listing4.10: Recherche dun client par son identifiant
Customer customer = em.find(Customer.class, 1234L); if (customer!= null) { // Traiter lobjet }

La seconde mthode est getReference() (voir Listing4.11). Elle ressemble beaucoup find() car elle prend les mmes paramtres, mais elle permet de rcuprer une rfrence dentit partir de sa cl primaire, pas partir de ses donnes. Cette mthode est prvue pour les situations o lon a besoin dune instance dentit gre, mais daucune autre donne que la cl primaire de lentit recherche. Lors dun appel getReference(), les donnes de ltat sont rcupres de faon paresseuse, ce qui signifie que, si lon naccde pas ltat avant que lentit soit dtache, les donnes peuvent tre manquantes. Cette mthode lve lexception EntityNotFoundException si elle ne trouve pas lentit.
Listing4.11: Recherche dun client par rfrence
try { Customer customer = em.getReference(Customer.class, 1234L); // Traiter lobjet } catch(EntityNotFoundException ex) { // Entit non trouve }

Chapitre 4

Gestion des objets persistants 155

Suppression dune entit

La mthode EntityManager.remove() supprime une entit qui est alors galement te de la base, dtache du gestionnaire dentits et qui ne peut plus tre synchronise avec la base. En termes dobjets Java, lentit reste accessible tant quelle ne sort pas de la porte et que le ramasse-miettes ne la pas supprime. Le code du Listing4.12 montre comment supprimer une entit aprs lavoir cre.
Listing4.12: Cration et suppression dentits Customer et Address
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); Address address = new Address("Ritherdon Rd", "London", "8QE", "UK"); customer.setAddress(address); tx.begin(); em.persist(customer); em.persist(address); tx.commit(); tx.begin(); em.remove(customer); tx.commit(); // Les donnes sont supprimes de la base // mais lobjet reste accessible assertNotNull(customer);

Le code du Listing4.12 cre une instance de Customer et dAddress, lie ladresse au client (customer.setAddress(address)) et les rend persistantes. Dans la base de donnes, la ligne du client est lie son adresse via une cl trangre. Puis le code ne supprime que lentit Customer: selon la configuration de la suppression en cascade, linstance dAddress peut tre laisse intacte alors quaucune autre entit ne la rfrence plus la ligne dadresse est alors orpheline.
Suppression des orphelins

Pour des raisons de cohrence des donnes, il faut viter de produire des orphelins car ils correspondent des lignes de la base de donnes qui ne sont plus rfrences par aucune autre table et qui ne sont donc plus accessibles. Avec JPA vous pouvez demander au fournisseur de persistance de supprimer automatiquement les orphelins ou de rpercuter en cascade une opration de suppression. Si une entit cible (Address) appartient uniquement une source (Customer) et que cette source soit supprime par lapplication, le fournisseur doit galement supprimer la cible.

156

Java EE 6 et GlassFish 3

Les relations 11 ou 1N disposent dune option demandant la suppression des orphelins. Dans notre exemple, il suffit dajouter llment orphanRemoval=true lannotation @OneToOne, comme dans le Listing4.13.
Listing4.13: Lentit Customer gre la suppression des Address orphelines
@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; @OneToOne (fetch = FetchType.LAZY, orphanRemoval = true) private Address address; // Constructeurs, getters, setters }

Dsormais, le code du Listing4.12 supprimera automatiquement lentit Address lorsque le client sera supprim. Lopration de suppression intervient au moment de lcriture dans la base de donnes (lorsque la transaction est valide).
Synchronisation avec la base de donnes

Jusqu maintenant, la synchronisation avec la base sest effectue lorsque la transaction est valide. Le gestionnaire dentits est un cache de premier niveau qui attend cette validation pour crire les donnes dans la base, mais que se passe-t-il lorsquil faut insrer un client et une adresse?
tx.begin(); em.persist(customer); em.persist(address); tx.commit();

Toutes les modifications en attente exigent une instruction SQL et les deux insert ne seront produits et rendus permanents que lorsque la transaction sera valide par commit(). Pour la plupart des applications, cette synchronisation automatique suffit : on ne sait pas exactement quand le fournisseur crira vraiment les donnes dans la base, nous pouvons tre srs que lcriture aura lieu lorsque la transaction sera valide. Bien que la base de donnes soit synchronise avec les entits dans le contexte de persistance, nous pouvons explicitement crire des donnes dans la base (avec flush()) ou, inversement, rafrachir des donnes partir de la base (avec refresh()). Si des donnes sont crites dans la base un instant prcis et que lapplication appelle plus tard la mthode rollback() pour annuler la transaction, les donnes crites seront supprimes de la base.

Chapitre 4

Gestion des objets persistants 157

criture de donnes

La mthode EntityManager.flush force le fournisseur de persistance crire les donnes dans la base; elle permet donc de dclencher manuellement le mme processus que celui utilis en interne par le gestionnaire dentits lorsquil crit le contexte de persistance dans la base de donnes.
tx.begin(); em.persist(customer); em.flush(); em.persist(address); tx.commit();

Il se passe deux choses intressantes dans le code prcdent. La premire est que em.flush() nattendra pas que la transaction soit valide pour crire le contexte de persistance dans la base de donnes: une instruction insert sera produite et excute linstant de lappel flush(). La seconde est que ce code ne fonctionnera pas cause des contraintes dintgrit. Sans criture explicite, le gestionnaire dentits met en cache toutes les modifications, les ordonne et les excute de faon cohrente du point de vue de la base. Avec une criture explicite, linstruction insert sur la table CUSTOMER sexcutera mais la contrainte dintgrit sur la cl trangre (la colonne ADDRESS_FK de CUSTOMER) sera viole et la transaction sera donc annule. Les donnes crites seront alors supprimes de la base. Vous devez donc faire attention lorsque vous utilisez des critures explicites et ne les utiliser que lorsquelles sont ncessaires.
Rafrachissement dune entit

La mthode refresh() effectue une synchronisation dans la direction oppose de flush(), cest--dire quelle crase ltat courant dune entit gre avec les donnes qui se trouvent dans la base. Son utilisation typique consiste annuler des modifications qui ont t faites sur lentit en mmoire. Lextrait de classe de test du Listing4.14 recherche un client par son identifiant, modifie son prnom et annule ce changement en appelant la mthode refresh().
Listing4.14: Rafrachissement de lentit Customer partir de la base de donnes
Customer customer = em.find(Customer.class, 1234L); assertEquals(customer.getFirstName(), "Antony"); customer.setFirstName("William"); em.refresh(customer); assertEquals(customer.getFirstName(), "Antony");

158

Java EE 6 et GlassFish 3

Contenu du contexte de persistance

Le contexte de persistance contient les entits gres. Grce linterface EntityManager, vous pouvez tester si une entit est gre et supprimer toutes les entits du contexte de persistance.
Contains

La mthode EntityManager.contains() renvoie un Boolean indiquant si une instance dentit particulire est actuellement gre par le gestionnaire dentits dans le contexte de persistance courant. Le cas de test du Listing4.15 rend un Customer persistant on peut immdiatement vrifier que lentit est gre (em. contains(customer) renvoie true). Puis on appelle la mthode remove() pour supprimer cette entit de la base de donnes et du contexte de persistance; lappel em.contains(customer) renvoie alors false.
Listing4.15: Cas de test pour vrifier que lentit Customer se trouve dans le contexte de persistance
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); tx.begin(); em.persist(customer); tx.commit(); assertTrue(em.contains(customer)); tx.begin(); em.remove(customer); tx.commit(); assertFalse(em.contains(customer));

Clear et Detach

La mthode clear() porte bien son nom car elle vide le contexte de persistance: toutes les entits qui taient gres deviennent donc dtaches. La mthode detach(Object entity) supprime lentit indique du contexte de persistance aprs cette viction, les modifications apportes cette entit ne seront plus synchronises avec la base de donnes. Le Listing4.16 cre une entit, vrifie quelle est gre, puis la supprime du contexte de persistance et vrifie quelle est bien dtache.

Chapitre 4

Gestion des objets persistants 159

Listing4.16: Test si lentit Customer se trouve dans le contexte de persistance


Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); tx.begin(); em.persist(customer); tx.commit(); assertTrue(em.contains(customer)); em.detach(customer); assertFalse(em.contains(customer));

La mthode clear() peut agir sur tout le contexte de persistance (clear uniquement sur une entit (clear(Object entity)).
Fusion dune entit

())

ou

Une entit dtache nest plus associe un contexte de persistance. Si vous voulez la grer, vous devez la fusionner. Prenons lexemple dune entit devant safficher dans une page JSF. Lentit est dabord charge partir de la base de donnes dans la couche de persistance (elle est gre), elle est renvoye par un appel dun EJB local (elle est dtache car le contexte de transaction sest termin), la couche de prsentation laffiche (elle est toujours dtache), puis elle revient pour tre mise jour dans la base de donnes. Cependant, ce moment-l, lentit est dtache et doit donc tre attache nouveau fusionne afin de synchroniser son tat avec la base. Le Listing 4.17 simule cette situation en vidant le contexte de persistance avec clear() afin de dtacher lentit.
Listing4.17: Nettoyage du contexte de persistance
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); tx.begin(); em.persist(customer); tx.commit(); em.clear(); // Modifie une valeur dune entit dtache. customer.setFirstName("William"); tx.begin(); em.merge(customer); tx.commit();

160

Java EE 6 et GlassFish 3

Le Listing4.17 cre un client et le rend persistant. Lappel em.clear() force le dtachement de lentit client mais les entits dtaches continuent de vivre en dehors du contexte de persistance dans lequel elles taient; par contre, la synchronisation de leur tat avec celui de la base de donnes nest plus garantie. Le setter customer. setFirstName("William") est donc excut sur une entit dtache et les donnes ne sont pas modifies dans la base. Pour rpercuter cette modification, il faut rattacher lentit (cest--dire la fusionner) avec un appel em.merge(customer).
Modification dune entit

Bien que la modification dune entit soit simple, elle peut en mme temps tre difficile comprendre. Comme nous venons de le voir, vous pouvez utiliser EntityManager.merge() pour attacher une entit et synchroniser son tat avec la base de donnes. Lorsquune entit est gre, les modifications qui lui sont apportes seront automatiquement refltes mais, si elle ne lest pas, vous devez appeler explicitement merge().
em.persist()

Le Listing 4.18 rend persistant un client prnomm Antony. Lorsque la mthode est appele, lentit devient gre et toutes les modifications qui lui seront dsormais appliques seront donc rpercutes dans la base de donnes. Lappel de la mthode setFirstName() modifie ltat de lentit. Le gestionnaire dentits met en cache toutes les actions excutes partir de tx.begin() et ne les rpercute dans la base que lorsque la transaction est valide avec tx.commit().

Listing4.18: Modification du prnom dun client


Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); tx.begin(); em.persist(customer); customer.setFirstName("William"); tx.commit();

Rpercussion dvnements

Par dfaut, chaque opration du gestionnaire dentits ne sapplique qu lentit passe en paramtre lopration. Parfois, cependant, on souhaite propager son action ses relations cest ce que lon appelle rpercuter un vnement. Jusqu prsent, nos exemples reposaient sur ce comportement par dfaut: le Listing4.19, par exemple, cre un client en instanciant une entit Customer et une entit Address,

Chapitre 4

Gestion des objets persistants 161

en les liant (avec customer. deux persistantes.

setAddress(address)),

puis en les rendant toutes les

Listing4.19: Rendre Customer et son Address persistantes


Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); Address address = new Address("Ritherdon Rd", "London", "8QE", "UK"); customer.setAddress(address); tx.begin(); em.persist(customer); em.persist(address); tx.commit();

Comme il existe une relation entre Customer et Address, on peut rpercuter laction persist() du client vers son adresse. Ceci signifie quun appel em.persist(customer) rpercutera lvnement persist lentit Address si elle autorise la propagation de ce type dvnement. Le code peut donc tre allg en tant linstruction em.persist(address), comme le montre le Listing4.20.
Listing4.20: Propagation dun vnement persist Address
Customer customer = new Customer("Antony", "Balla", "tballa@mail.com"); Address address = new Address("Ritherdon Rd", "London", "8QE", "UK"); customer.setAddress(address); tx.begin(); em.persist(customer); tx.commit();

Sans cette rpercussion, le client serait persistant, mais pas son adresse. Pour que cette rpercussion ait lieu, lassociation de la relation doit donc tre modifie. Les annotations @OneToOne, @OneToMany, @ManyToOne et @ManyToMany disposent dun attribut cascade pouvant recevoir un tableau dvnements propager. Nous devons donc modifier lassociation de lentit Customer (voir Listing4.21) en ajoutant un attribut cascade lannotation @OneToOne. Ici, on ne se contente pas de propager persist, on fait de mme pour lvnement remove, afin que la suppression dun client entrane celle de son adresse.
Listing4.21: Lentit Customer propage les vnements persist et remove
@Entity public class Customer {

162

Java EE 6 et GlassFish 3

@Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; @OneToOne (fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) @JoinColumn(name = "address_fk") private Address address; // Constructeurs, getters, setters }

Le Tableau4.2 numre les vnements que vous pouvez propager vers une cible de relation. Vous pouvez mme tous les propager en utilisant le type CascadeType.ALL.
Tableau4.2: vnements pouvant tre propags

Type
PERSIST REMOVE MERGE REFRESH CLEAR ALL

Description Propage les oprations persist la cible de la relation. Propage les oprations remove la cible de la relation. Propage les oprations merge la cible de la relation. Propage les oprations refresh la cible de la relation. Propage les oprations clear la cible de la relation. Propage toutes les oprations prcdentes.

LAPI de cache

La plupart des spcifications (pas seulement JAVA EE) sintressent beaucoup aux fonctionnalits et considrent le reste, comme les performances, ladaptabilit ou la mise en cluster, comme des dtails dimplmentation. Les implmentations doivent respecter strictement la spcification mais peuvent galement ajouter des fonctionnalits spcifiques. Un parfait exemple pour JPA serait la gestion dun cache. Jusqu JPA 2.0, la mise en cache ntait pas mentionne dans la spcification. Comme on la dj voqu, le gestionnaire dentits est un cache de premier niveau utilis pour traiter les donnes afin quelles conviennent la base de donnes et pour mettre en cache les entits en cours dutilisation. Ce cache permet de rduire le nombre de requtes SQL de chaque transaction si un objet est modifi plusieurs

Chapitre 4

Gestion des objets persistants 163

fois au cours de la mme transaction, le gestionnaire dentits ne produira quune seule instruction UPDATE la fin de cette transaction , mais un cache de premier niveau nest pas un cache de performance. Toutes les implmentations de JPA utilisent un cache de performance (appel cache de second niveau) pour optimiser les accs la base de donnes, les requtes, les jointures, etc. Les caches de second niveau rduisent le trafic avec la base de donnes car ils conservent les objets en mmoire et les rendent disponibles toute lapplication. Chaque implmentation utilise sa propre technique de cache en dveloppant ses propres mcanismes ou en utilisant des solutions open-source. Le cache peut tre distribu sur un cluster ou non en fait, tout est possible puisque la spcification ne dit rien sur le sujet. JPA 2.0 reconnat la ncessit dun cache de second niveau et a donc ajout des oprations de gestion du cache dans une API standard. Celle-ci, prsente dans le Listing 4.22, est minimaliste le but de JPA nest pas de standardiser un cache pleinement fonctionnel mais elle permet dinterroger et de supprimer des entits dun cache de second niveau de faon standard. Comme EntityManager, javax. persistence.Cache est une interface implmente par le systme de cache du fournisseur de persistance.
Listing4.22: API de cache
public interface Cache { // Teste si le cache contient les donnes de lentit indique. public boolean contains(Class cls, Object primaryKey); // Supprime du cache les donnes de lentit indique. public void evict(Class cls, Object primaryKey); // te du cache les donnes des entits de la classe indique. public void evict(Class cls); // Vide le cache. public void evictAll(); }

JPQL
Nous venons de voir comment manipuler sparment les entits avec lAPI dEntityManager. Vous savez maintenant comment rcuprer une entit partir de son

164

Java EE 6 et GlassFish 3

identifiant, la supprimer, modifier ses attributs, etc. Mais rechercher une entit par son identifiant est assez limit (ne serait-ce que parce quil vous faut connatre cet identifiant...): en pratique, vous aurez plutt besoin de rcuprer une entit en fonction de critres autres que son identifiant (par son nom ou son ISBN, par exemple) ou de rcuprer un ensemble dentits satisfaisant certaines conditions (tous les clients qui habitent en France, par exemple). Cette possibilit est inhrente aux bases de donnes relationnelles et JPA dispose dun langage permettant ce type dinteractions: JPQL. JPQL (Java Persistence Query Language) sert dfinir des recherches dentits persistantes indpendamment de la base de donnes sous-jacente. Cest un langage de requte qui sinspire de SQL (Structured Query Language), le langage standard pour interroger les bases de donnes relationnelles. La diffrence principale est que le rsultat dune requte SQL est un ensemble de lignes et de colonnes (une table) alors que celui dune requte JPQL est une entit ou une collection dentits. Sa syntaxe est oriente objet et est donc plus familire aux dveloppeurs ne connaissant que ce type de programmation. Ils peuvent ainsi manipuler un modle objet en utilisant la notation pointe classique (maClasse.monAttribut, par exemple) et oublier la structure des tables. En coulisse, JPQL utilise un mcanisme de traduction pour transformer une requte JPQL en langage comprhensible par une base de donnes SQL. La requte sexcute sur la base de donnes sous-jacente avec SQL et des appels JDBC, puis les instances dentits sont initialises et sont renvoyes lapplication tout ceci de faon simple et laide dune syntaxe riche. La requte JPQL la plus simple qui soit slectionne toutes les instances dune seule entit:
SELECT b FROM Book b

Si vous connaissez SQL, cette instruction devrait vous sembler familire. Au lieu de slectionner le rsultat partir dune table, JPQL slectionne des entits, Book ici. La clause FROM permet galement de donner un alias cette entit: ici, b est un alias de Book. La clause SELECT indique que le type de la requte est lentit b (Book). Lexcution de cette instruction produira donc une liste de zros ou plusieurs instances de Book. Pour restreindre le rsultat, on utilise la clause WHERE afin dintroduire un critre de recherche:

Chapitre 4

Gestion des objets persistants 165

SELECT b FROM Book b WHERE b.title = "H2G2"

Lalias sert naviguer dans les attributs de lentit via loprateur point. Lentit Book ayant un attribut persistant nomm title de type String, b.title dsigne donc lattribut title de lentit Book. Lexcution de cette instruction produira une liste de zros ou plusieurs instances de Book ayant pour titre H2G2. La requte la plus simple est forme de deux parties obligatoires: les clauses SELECT et FROM. La premire dfinit le format du rsultat de la requte tandis que la seconde indique lentit ou les entits partir desquelles le rsultat sera obtenu. Une requte peut galement contenir des clauses WHERE, ORDER BY, GROUP BY et HAVING pour restreindre ou trier le rsultat. La syntaxe complte de SELECT est dfinie dans le Listing4.23. Il existe galement les instructions DELETE et UPDATE, qui permettent respectivement de supprimer et de modifier plusieurs instances dune classe dentit.
Select

La clause SELECT porte sur une expression qui peut tre une entit, un attribut dentit, une expression constructeur, une fonction agrgat ou toute squence de ce qui prcde. Ces expressions sont les briques de base des requtes et servent atteindre les attributs des entits ou traverser les relations (ou une collection dentits) via la notation pointe classique. Le Listing 4.23 dfinit la syntaxe dune instruction SELECT.
Listing4.23: Syntaxe de linstruction SELECT
SELECT <expression select> FROM <clause from> [WHERE <expression conditionnelle>] [ORDER BY <clause order by>] [GROUP BY <clause group by>] [HAVING <clause having>]

Une instruction SELECT simple renvoie une entit. Si une entit Customer a un alias c, par exemple, SELECT c renverra une entit ou une liste dentits.
SELECT c FROM Customer c

166

Java EE 6 et GlassFish 3

Une clause SELECT peut galement renvoyer des attributs. Si lentit Customer a un attribut firstName, SELECT c.firstName renverra un String ou une collection de String contenant les prnoms.
SELECT c.firstName FROM Customer c

Pour obtenir le prnom et le nom dun client, il suffit de crer une liste contenant les deux attributs correspondants:
SELECT c.firstName, c.lastName FROM Customer c

Si lentit Customer est en relation 11 avec Address, c.address dsigne ladresse du client et le rsultat de la requte suivante renverra donc non pas une liste de clients mais une liste dadresses:
SELECT c.address FROM Customer c

Les expressions de navigation peuvent tre relies les unes aux autres pour traverser des graphes dentits complexes. Avec cette technique, nous pouvons construire des expressions comme c.address.country.code afin de dsigner le code du pays de ladresse dun client.
SELECT c.address.country.code FROM Customer c

Lexpression SELECT peut contenir un constructeur afin de renvoyer une instance de classe Java initialise avec le rsultat de la requte. Cette classe na pas besoin dtre une entit, mais le constructeur doit tre pleinement qualifi et correspondre aux attributs.
SELECT NEW com.apress.javaee6.CustomerDTO(c.firstName, c.lastName, c.address.street1) FROM Customer c

Le rsultat de cette requte sera une liste dobjets CustomerDTO instancis avec loprateur new et initialiss avec le prnom, le nom et la rue des clients. Lexcution des requtes prcdentes renverra soit une valeur unique, soit une collection de zros ou plusieurs entits (ou attributs) pouvant contenir des doublons. Pour supprimer ces derniers, il faut utiliser loprateur DISTINCT:
SELECT DISTINCT c FROM Customer c

Chapitre 4

Gestion des objets persistants 167

SELECT DISTINCT c.firstName FROM Customer c

Le rsultat dune requte peut tre le rsultat dune fonction agrgat applique une expression. La clause SELECT peut utiliser les fonctions agrgats AVG, COUNT, MAX, MIN et SUM. En outre, leurs rsultats peuvent tre regroups par une clause GROUP BY et filtrs par une clause HAVING.
SELECT COUNT(c) FROM Customer c

Les clauses SELECT, WHERE et HAVING peuvent galement utiliser des expressions scalaires portant sur des nombres (ABS, SQRT, MOD, SIZE, INDEX), des chanes (CONCAT, SUBSTRING, TRIM, LOWER, UPPER, LENGTH) et des dates (CURRENT_DATE, CURRENT_ TIME, CURRENT_TIMESTAMP).
From

La clause FROM dune requte dfinit les entits en dclarant des variables didentification ou alias qui pourront tre utiliss dans les autres clauses (SELECT, WHERE, etc.). Sa syntaxe est simplement forme du nom de lentit et de son alias. Dans lexemple suivant, lentit est Customer et lalias est c:
SELECT c FROM Customer c

Where

La clause WHERE dune requte est forme dune expression conditionnelle permettant de restreindre le rsultat dune instruction SELECT, UPDATE ou DELETE. Il peut sagir dune expression simple ou dun ensemble dexpressions conditionnelles permettant de filtrer trs prcisment la requte. La faon la plus simple de restreindre le rsultat dune requte consiste utiliser un attribut dune entit. Linstruction suivante, par exemple, slectionne tous les clients prnomms Vincent:
SELECT c FROM Customer c WHERE c.firstName = Vincent

168

Java EE 6 et GlassFish 3

Vous pouvez restreindre encore plus les rsultats en utilisant les oprateurs logiques AND et OR. Lexemple suivant utilise AND pour slectionner tous les clients prnomms Vincent qui habitent en France:
SELECT c FROM Customer c WHERE c.firstName = Vincent AND c.address.country = France

La clause WHERE utilise galement les oprateurs de comparaison =, >, >=, <, <=, <>, [NOT] BETWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY et [NOT] MEMBER [OF]. Voici quelques exemples dutilisation:
SELECT c FROM Customer c WHERE c.age > 18 SELECT c FROM Customer c WHERE c.age NOT BETWEEN 40 AND 50 SELECT c FROM Customer c WHERE c.address.country IN (USA, Portugal)

Lexpression LIKE est forme dune chane pouvant contenir des caractres "jokers": le blanc soulign (_) capture un caractre quelconque et le caractre pourcent (%) capture un nombre quelconque (ventuellement nul) de caractres:
SELECT c FROM Customer c WHERE c.email LIKE %mail.com

Liaison de paramtres

Jusqu maintenant, les clauses WHERE dont nous nous sommes servis nutilisaient que des valeurs fixes. Dans une application, cependant, les requtes dpendent souvent de paramtres et JPQL fournit donc deux moyens pour lier ces paramtres: par position ou par nom. Les paramtres positionnels sont indiqus par un point dinterrogation suivi dun entier (?1, par exemple). Lorsque la requte sera utilise, il faudra fournir les valeurs qui viendront remplacer ces paramtres.
SELECT c FROM Customer c WHERE c.firstName = ?1 AND c.address.country = ?2

Chapitre 4

Gestion des objets persistants 169

Les paramtres nomms sont reprsents par un identifiant de type String prfix par le caractre deux-points (:). Lorsque la requte sera utilise, il faudra fournir des valeurs ces paramtres nomms.
SELECT c FROM Customer c WHERE c.firstName = :fname AND c.address.country = :country

Nous verrons dans la section "Requtes", plus loin dans ce chapitre, comment lier ces paramtres dans une application.
Sous-requtes

Une sous-requte est une requte SELECT intgre dans lexpression conditionnelle dune clause WHERE ou HAVING. Le rsultat de cette sous-requte est valu et interprt dans lexpression conditionnelle de la requte principale. Pour, par exemple, obtenir les clients les plus jeunes de la base de donnes, on excute dabord une sous-requte avec MIN(age) et lon value son rsultat dans la requte principale:
SELECT c FROM Customer c WHERE c.age = (SELECT MIN(c.age) FROM Customer c)

Order By

La clause ORDER BY permet de trier les entits ou les valeurs renvoyes par une requte SELECT. Le tri sapplique lattribut prcis dans cette clause. Si cet attribut est suivi dASC ou daucun mot-cl, le tri sera ascendant; sil est suivi de DESC, le tri sera descendant.
SELECT c FROM Customer c WHERE c.age > 18 ORDER BY c.age DESC

Le tri peut utiliser plusieurs expressions.


SELECT c FROM Customer c WHERE c.age > 18 ORDER BY c.age DESC, c.address.country ASC

Group By et Having

La clause GROUP BY permet de regrouper des valeurs du rsultat en fonction dun ensemble de proprits. Les entits sont alors divises en groupes selon les valeurs

170

Java EE 6 et GlassFish 3

de lexpression de la clause GROUP BY. Pour, par exemple, regrouper les clients par pays et les compter, on utilisera la requte suivante:
SELECT c.address.country, count(c) FROM Customer c GROUP BY c.address.country

GROUP BY dfinit les expressions de regroupement (c.address.country) qui serviront agrger et compter les rsultats. Notez que les expressions qui apparaissent dans la clause GROUP BY doivent galement apparatre dans la clause SELECT.

La clause HAVING dfinit un filtre qui sappliquera aprs le regroupement des rsultats, un peu comme une seconde clause WHERE qui filtrerait le rsultat de GROUP BY. En ajoutant une clause HAVING la requte prcdente, on peut nobtenir que les pays ayant plus de 100clients.
SELECT c.address.country, count(c) FROM Customer c GROUP BY c.address.country HAVING count(c) > 100

GROUP BY

et HAVING ne peuvent apparatre que dans une clause SELECT.

Suppressions multiples

Nous savons supprimer une entit laide de la mthode EntityManager.remove() et interroger une base de donnes pour obtenir une liste dentits correspondant certains critres. Pour supprimer un ensemble, nous pourrions donc excuter une requte et parcourir son rsultat pour supprimer sparment chaque entit. Bien que ce soit un algorithme tout fait valide, ses performances seraient dsastreuses car il implique trop daccs la base. Il existe une meilleure solution: les suppressions multiples. JPQL sait effectuer des suppressions multiples sur les diffrentes instances dune classe dentit prcise, ce qui permet de supprimer un grand nombre dentits en une seule opration. Linstruction DELETE ressemble linstruction SELECT car elle peut utiliser une clause WHERE et prendre des paramtres. Elle renvoie le nombre dentits concernes par lopration. Sa syntaxe est dcrite dans le Listing4.24.
Listing4.24: Syntaxe de linstruction DELETE
DELETE FROM <nom entit> [[AS] <variable identification>] [WHERE <expression conditionnelle>]

Chapitre 4

Gestion des objets persistants 171

Linstruction suivante, par exemple, supprime tous les clients gs de moins de 18ans:
DELETE FROM Customer c WHERE c.age < 18

Mises jour multiples

Linstruction UPDATE permet de modifier toutes les entits rpondant aux critres de sa clause WHERE. Sa syntaxe est dcrite dans le Listing4.25.
Listing4.25: Syntaxe de linstruction UPDATE
UPDATE <nom entit> [[AS] <variable identification>] SET <mise jour> {, <mise jour>}* [WHERE <expression conditionnelle>]

Linstruction suivante, par exemple, modifie le prnom de tous nos jeunes clients en "trop jeune":
UPDATE Customer c SET c.firstName = TROP JEUNE WHERE c.age < 18

Requtes
Nous connaissons maintenant la syntaxe de JPQL et savons comment crire ses instructions laide de diffrentes clauses (SELECT, FROM, WHERE, etc.): le problme consiste maintenant les intgrer dans une application. Pour ce faire, JPA2.0 permet dintgrer quatre sortes de requtes dans le code, chacune correspondant un besoin diffrent:

Les requtes dynamiques. Ce sont les requtes les plus faciles car il sagit simplement de chanes de requtes JPQL indiques dynamiquement au moment de lexcution. Les requtes nommes. Ce sont des requtes statiques et non modifiables. Les requtes natives. Elles permettent dexcuter une instruction SQL native la place dune instruction JPQL. API des critres. Ce nouveau concept a t introduit par JPA 2.0.

172

Java EE 6 et GlassFish 3

Le choix entre ces quatre types est centralis au niveau de linterface EntityManager, qui dispose de plusieurs mthodes fabriques (voir Tableau4.3) renvoyant toutes une interface Query.
Tableau4.3: Mthodes dEntityManager pour crer des requtes

Mthode
Query createQuery(String jpqlString)

Description Cre une instance de Query permettant dexcuter une instruction JPQL pour des requtes dynamiques. Cre une instance de Query permettant dexcuter une requte par critre. Cre une instance de Query permettant dexcuter une requte nomme (en JPQL ou en SQL natif). Cre une instance de Query permettant dexcuter une instruction SQL native. Cre une instance de Query permettant dexcuter une instruction SQL native en lui passant la classe du rsultat attendu.

Query createQuery(QueryDefinition qdef) Query createNamedQuery(String name)

Query createNativeQuery(String sqlString) Query createNativeQuery(String sqlString, Class resultClass)

Une API complte permet de contrler limplmentation de Query obtenue par lune de ces mthodes. LAPI Query, prsente dans le Listing4.26, est utilisable avec les requtes statiques (requtes nommes) et les requtes dynamiques en JPQL, ainsi quavec les requtes natives en SQL. Cette API permet galement de lier des paramtres aux requtes et de contrler la pagination.
Listing4.26: API Query
public interface Query { // Excute une requte et renvoie un rsultat. public List getResultList(); public Object getSingleResult(); public int executeUpdate(); // Initialise les paramtres de la requte. public Query setParameter(String name, Object value); public Query setParameter(String name, Date value, TemporalType temporalType); public Query setParameter(String name, Calendar value, TemporalType temporalType);

Chapitre 4

Gestion des objets persistants 173

public Query setParameter(int position, Object value); public Query setParameter(int position, Date value, TemporalType temporalType); public Query setParameter(int position, Calendar value, TemporalType temporalType); public Map<String, Object> getNamedParameters(); public List getPositionalParameters(); // Restreint le nombre de rsultats renvoys par une requte. public Query setMaxResults(int maxResult); public int getMaxResults(); public Query setFirstResult(int startPosition); public int getFirstResult(); // Fixe et obtient les "hints" dune requte. public Query setHint(String hintName, Object value); public Map<String, Object> getHints(); public Set<String> getSupportedHints(); // Fixe le mode flush pour lexcution de la requte. public Query setFlushMode(FlushModeType flushMode); public FlushModeType getFlushMode(); // Fixe le mode de verrouillage utilis par la requte. public Query setLockMode(LockModeType lockMode); public LockModeType getLockMode(); // Permet daccder lAPI spcifique du fournisseur. public <T> T unwrap(Class<T> cls); }

Les mthodes les plus utilises de cette API sont celles qui excutent la requte. Ainsi, pour effectuer une requte SELECT, vous devez choisir entre deux mthodes en fonction du rsultat que vous voulez obtenir:

La mthode getResultList() excute la requte et renvoie une liste de rsultats (entits, attributs, expressions, etc.). La mthode getSingleResult() excute la requte et renvoie un rsultat unique.

Pour excuter une mise jour ou une suppression, utilisez la mthode executeUpdate(), qui excute la requte et renvoie le nombre dentits concernes par son excution. Comme nous lavons vu plus haut dans la section "JPQL", une requte peut prendre des paramtres nomms (:monParam, par exemple) ou positionnels (?1, par exemple). LAPI Query dfinit plusieurs mthodes setParameter() pour initialiser ces paramtres avant lexcution dune requte.

174

Java EE 6 et GlassFish 3

Une requte peut renvoyer un grand nombre de rsultats. Selon lapplication, ceux-ci peuvent tre traits tous ensemble ou par morceaux (une application web, par exemple, peut vouloir nafficher que dix lignes la fois). Pour contrler cette pagination, linterface Query dfinit les mthodes setFirstResult() et setMaxResults(), qui permettent respectivement dindiquer le premier rsultat que lon souhaite obtenir (en partant de zro) et le nombre maximal de rsultats par rapport ce point prcis. Le mode flush indique au fournisseur de persistance comment grer les modifications et les requtes en attente. Deux modes sont possibles: AUTO et COMMIT. Le premier (qui est galement celui par dfaut) prcise que cest au fournisseur de sassurer que les modifications en attente soient visibles par le traitement de la requte. COMMIT est utilis lorsque lon souhaite que leffet des modifications apportes aux entits ncrase pas les donnes modifies dans le contexte de persistance. tre setLockMode(LockModeType). Les requtes peuvent verrouilles par un appel la mthode

Les sections qui suivent dcrivent les trois types de requtes en utilisant quelquesunes des mthodes que nous venons de dcrire.
Requtes dynamiques

Les requtes dynamiques sont dfinies la vole par lapplication lorsquelle en a besoin. Elles sont cres par un appel la mthode EntityManager.createQuery(), qui prend en paramtre une chane reprsentant une requte JPQL. Dans le code qui suit, la requte JPQL slectionne tous les clients de la base. Le rsultat tant une liste, on utilise la mthode getResultList() pour renvoyer une liste dentits Customer (List<Customer>). Si vous savez que la requte ne renverra quune seule entit, utilisez plutt la mthode getSingleResult() car cela vous vitera de devoir ensuite extraire cette entit dune liste.
Query query = em.createQuery("SELECT c FROM Customer c"); List<Customer> customers = query.getResultList();

La chane contenant la requte peut galement tre labore dynamiquement par lapplication en cours dexcution laide de loprateur de concatnation et en fonction de certains critres.
String jpqlQuery = "SELECT c FROM Customer c"; if (someCriteria)

Chapitre 4

Gestion des objets persistants 175

jpqlQuery += " WHERE c.firstName = Vincent"; query = em.createQuery(jpqlQuery); List<Customer> customers = query.getResultList();

La requte prcdente rcupre les clients prnomms Vincent, mais vous voudrez peut-tre pouvoir choisir ce prnom et le passer en paramtre: vous pouvez le faire en utilisant des noms ou des positions. Dans lexemple suivant, on utilise un paramtre nomm :fname (notez le prfixe deux-points) dans la requte et on le lie une valeur avec la mthode setParameter():
jpqlQuery = "SELECT c FROM Customer c"; if (someCriteria) jpqlQuery += " where c.firstName = :fname"; query = em.createQuery(jpqlQuery); query.setParameter("fname", "Vincent"); List<Customer> customers = query.getResultList();

Notez que le nom de paramtre fname ne contient pas le symbole deux-points utilis dans la requte. Le code quivalent avec un paramtre positionnel serait le suivant:
jpqlQuery = "SELECT c FROM Customer c"; if (someCriteria) jpqlQuery += " where c.firstName = ?1"; query = em.createQuery(jpqlQuery); query.setParameter(1, "Vincent"); List<Customer> customers = query.getResultList();

Si vous voulez paginer la liste des clients par groupes de dix, utilisez la mthode setMaxResults() de la faon suivante:
Query query = em.createQuery("SELECT c FROM Customer c"); query.setMaxResults(10); List<Customer> customers = query.getResultList();

Le problme des requtes dynamiques est le cot de la traduction de la chane JPQL en instruction SQL au moment de lexcution. La requte tant cre lexcution, elle ne peut pas tre prvue la compilation : chaque appel, le fournisseur de persistance doit donc analyser la chane JPQL, obtenir les mtadonnes de lORM et produire la requte SQL correspondante. Ce surcot de traitement des requtes dynamiques peut donc tre un problme: lorsque cela est possible, utilisez plutt des requtes statiques (requtes nommes).
Requtes nommes

Les requtes nommes sont diffrentes des requtes dynamiques parce quelles sont statiques et non modifiables. Bien que cette nature statique noffre pas la souplesse

176

Java EE 6 et GlassFish 3

des requtes dynamiques, lexcution des requtes nommes peut tre plus efficace car le fournisseur de persistance peut traduire la chane JPQL en SQL au dmarrage de lapplication au lieu dtre oblig de le faire chaque fois que la requte est excute. Les requtes nommes sont exprimes dans les mtadonnes via une annotation @ NamedQuery ou son quivalent XML. Cette annotation prend deux lments: le nom de la requte et son contenu. Dans le Listing4.27, nous modifions lentit Customer pour dfinir trois requtes statiques laide dannotations.
Listing4.27: Lentit Customer avec des requtes nommes
@Entity @NamedQueries({ @NamedQuery(name = "findAll", query="select c from Customer c"), @NamedQuery(name = "findVincent", query="select c from Customer c where c.firstName = Vincent"), @NamedQuery(name = "findWithParam", query="select c from Customer c where c.firstName = :fname") )} public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private Integer age; private String email; @OneToOne @JoinColumn(name = "address_fk") private Address address; // Constructeurs, getters, setters }

Lentit Customer dfinissant plusieurs requtes nommes, nous utilisons lannotation @NamedQueries, qui prend en paramtre un tableau de @NamedQuery. La premire requte, nomme findAll, renvoie toutes les entits Customer de la base, sans aucune restriction (pas de clause WHERE). La requte findWithParam prend quant elle un paramtre fname pour choisir les clients en fonction de leur prnom. Si lentit Customer navait dfini quune seule requte, nous aurions simplement utilis une annotation @NamedQuery, comme dans lexemple suivant:
@Entity @NamedQuery(name = "findAll", query="select c from Customer c") public class Customer { ... }

Chapitre 4

Gestion des objets persistants 177

Lexcution de ces requtes ressemble celle des requtes dynamiques: on appelle la mthode EntityManager.createNamedQuery() en lui passant le nom de la requte tel quil est dfini dans les annotations. Cette mthode renvoie un objet Query qui peut servir initialiser les paramtres, le nombre maximal de rsultats, le mode de rcupration, etc. Pour, par exemple, excuter la requte findAll, on crirait le code suivant:
Query query = em.createNamedQuery("findAll"); List<Customer> customers = query.getResultList();

Le fragment de code qui suit appelle la requte findWithParam en lui passant le paramtre fname et en limitant le nombre de rsultats 3:
Query query = em.createNamedQuery("findWithParam"); query.setParameter("fname", "Vincent"); query.setMaxResults(3); List<Customer> customers = query.getResultList();

La plupart des mthodes de lAPI Query renvoyant un objet Query, vous pouvez utiliser un raccourci lgant qui consiste appeler les mthodes les unes aprs les autres (setParameter().setMaxResults(), etc.).
Query query = em.createNamedQuery("findWithParam"). setParameter("fname", "Vincent").setMaxResults(3); List<Customer> customers = query.getResultList();

Les requtes nommes permettent dorganiser les dfinitions de requtes et amliorent les performances de lapplication. Cette organisation vient du fait quelles sont dfinies de faon statique sur les entits et gnralement places sur la classe entit qui correspond directement au rsultat de la requte (ici, findAll renvoie des clients et doit donc tre dfinie sur lentit Customer). Cependant, la porte du nom de la requte est celle de lunit de persistance et ce nom doit tre unique dans cette porte, ce qui signifie quil ne peut exister quune seule requte findAll: ceci implique donc de nommer diffremment cette requte si lon devait, par exemple, en crire une autre pour rechercher toutes les adresses. Une pratique courante consiste prfixer le nom de la requte par celui de lentit: on aurait ainsi une mthode Customer.findAll pour Customer et Address.findAll pour Address. Un autre problme est que le nom de la requte, qui est une chane, est modifiable et que vous risquez donc dobtenir une exception indiquant que la requte nexiste pas si vous faites une erreur de frappe ou que vous refactorisiez le code. Pour limiter

178

Java EE 6 et GlassFish 3

ce risque, vous pouvez remplacer ce nom par une constante. Le Listing4.28 montre comment refactoriser lentit Customer.
Listing4.28: Lentit Customer dfinit une requte nomme laide dune constante
@Entity @NamedQuery(name = Customer.FIND_ALL, query="select c from Customer c") public class Customer { public static final String FIND_ALL = "Customer.findAll"; // Attributs, constructeurs, getters, setters }

La constante FIND_ALL identifie la requte findAll sans ambigut en prfixant son nom du nom de lentit. Cest cette mme constante qui est ensuite utilise dans lannotation @NamedQuery et que vous pouvez utiliser pour excuter la requte:
Query query = em.createNamedQuery(Customer.FIND_ALL); List<Customer> customers = query.getResultList();

Requtes natives

JPQL dispose dune syntaxe riche permettant de grer les entits sous nimporte quelle forme et de faon portable entre les diffrentes bases de donnes, mais JPA autorise galement lutilisation des fonctionnalits spcifiques dun SGBDR via des requtes natives. Celles-ci prennent en paramtre une instruction SQL (SELECT, UPDATE ou DELETE) et renvoient une instance de Query pour excuter cette instruction. En revanche, les requtes natives peuvent ne pas tre portables dune base de donnes lautre. Si le code nest pas portable, pourquoi alors ne pas utiliser des appels JDBC? La raison principale dutiliser des requtes JPA natives plutt que des appels JDBC est que le rsultat de la requte sera automatiquement converti en entits. Pour, par exemple, rcuprer toutes les entits Customer de la base en utilisant SQL, vous devez appeler la mthode EntityManager.createNativeQuery(), qui prend en paramtre la requte SQL et la classe dentit dans laquelle le rsultat sera traduit:
Query query = em.createNativeQuery("SELECT * FROM t_customer", Customer.class); List<Customer> customers = query.getResultList();

Chapitre 4

Gestion des objets persistants 179

Comme vous pouvez le constater, la requte SQL est une chane qui peut tre cre dynamiquement en cours dexcution (exactement comme les requtes JPQL dynamiques). L aussi la requte pourrait tre complexe et, ne la connaissant pas lavance, le fournisseur de persistance sera oblig de linterprter chaque fois, ce qui aura des rpercussions sur les performances de lapplication. Toutefois, comme les requtes nommes, les requtes natives peuvent utiliser le mcanisme des annotations pour dfinir des requtes SQL statiques. Ici, cette annotation sappelle @NamedNativeQuery et peut tre place sur nimporte quelle entit (voir Listing4.29) comme avec JPQL, le nom de la requte doit tre unique dans lunit de persistance.
Listing4.29: Lentit Customer dfinit une requte native nomme
@Entity @NamedNativeQuery(name = "findAll", query="select * from t_customer") @Table(name = "t_customer") public class Customer { // Attributs, constructeurs, getters, setters }

Concurrence
JPA peut servir modifier des donnes persistantes et JPQL permet de rcuprer des donnes rpondant certains critres. Lapplication qui les utilise peut sexcuter dans un cluster de plusieurs nuds, avoir plusieurs threads et une seule base de donnes: il est donc assez frquent daccder aux entits de faon concurrente. Dans cette situation, lapplication doit contrler la synchronisation des donnes au moyen dun mcanisme de verrouillage. Que votre programme soit simple ou complexe, il y a de grandes chances pour que vous soyez oblig dutiliser des verrous un endroit ou un autre de votre code. Pour illustrer le problme de laccs concurrent une base de donnes, prenons lexemple dune application comprenant les deux mthodes de la Figure4.3. Lune des mthodes recherche un livre par son identifiant et augmente son prix de 2. Lautre fait la mme chose, mais augmente le prix de 5. Si les deux mthodes sont excutes en mme temps dans des transactions distinctes et quelles manipulent le mme livre, vous ne pouvez donc pas prvoir le prix final. Dans notre exemple, son

180

Java EE 6 et GlassFish 3

prix initial tait de 10: selon la transaction qui se termine en dernier, son prix final sera de 12 ou de 15.
tx1.begin() // Le prix du livre est de 10 Book book = em.find(Book.class, 12); book.raisePriceByT Euros(); tx2.begin() // Le prix du livre est de 10 Book book = em.find(Book.class, 12); book.raisePriceByFi Euros();

tx1.comit(); // Le prix est maintenant de 12 temps

tx2.comit(); // Le prix est maintenant de 15

Figure4.3 Les transactions tx1 et tx2 modifient le prix dun livre de faon concurrente.

Ce problme de concurrence, o le "gagnant" est celui qui valide la transaction en dernier, nest pas spcifique JPA. Cela fait bien longtemps que les SGBD ont d rsoudre ce problme et ont trouv diffrentes solutions pour isoler les transactions les unes des autres. Un mcanisme classique consiste verrouiller la ligne sur laquelle porte linstruction SQL. JPA 2.0 dispose de deux types de verrouillages (JPA 1.0 ne proposait que le verrouillage optimiste):

Le verrouillage optimiste. Il repose sur la supposition que la plupart des transactions nentreront pas en conflit les unes avec les autres, ce qui permet une concurrence aussi permissive que possible. Le verrouillage pessimiste. Il fait la supposition inverse, ce qui impose dobtenir un verrou sur la ressource avant de la manipuler.

Prenons un exemple de la vie quotidienne pour illustrer ces concepts: la traverse dune avenue. Dans une zone faible trafic, vous pourriez traverser lavenue sans regarder si des voitures arrivent (traverse optimiste) alors que, dans une zone fort trafic, il ne faut certainement pas le faire (traverse pessimiste). JPA utilise diffrents mcanismes de verrouillage en fonction des niveaux de lAPI. Les verrous optimistes et pessimistes peuvent tre obtenus via les mthodes EntityManager.find() et EntityManager.refresh() (en plus de la mthode lock()), ainsi que par les requtes JPQL: ceci signifie donc que le verrouillage peut seffectuer au niveau du gestionnaire dentits et au niveau Query avec les mthodes numres dans les Tableaux4.4 et 4.5.

Chapitre 4

Gestion des objets persistants 181

Tableau4.4: Mthodes dEntityManager pour verrouiller les entits

Mthode
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) void lock(Object entity, LockModeType lockMode) void refresh(Object entity, LockModeType lockMode)

Description Recherche une entit de la classe avec la cl indique et la verrouille selon le type du verrou. Verrouille une instance dentit contenue dans le contexte de persistance avec le type de verrou indiqu. Rafrachit ltat de linstance partir de la base de donnes en crasant les ventuelles modifications apportes lentit et verrouille celle-ci selon le type de verrou indiqu.

Tableau4.5: Mthodes de Query pour verrouiller les requtes JPQL

Mthode
Query setLockMode(LockModeType lockMode)

Description Fixe le type de verrou utilis pour lexcution de la requte.


LockModeType

Toutes ces mthodes attendent un paramtre valeurs suivantes:


OPTIMISTIC. Verrouillage

pouvant prendre les

optimiste.

OPTIMISTIC_FORCE_INCREMENT.

Verrouillage optimiste et incrmentation de la colonne version de lentit (voir la section "Gestion de version"). la fin de la transaction pour obtenir un verrou.

PESSIMISTIC_READ. Verrouillage pessimiste sans avoir besoin de relire les donnes PESSIMISTIC_WRITE. Verrouillage pessimiste et srialisation entre les transactions

pour mettre jour lentit.


PESSIMISTIC_FORCE_INCREMENT. Verrouillage NONE. Aucun

pessimiste et incrmentation de la colonne version de lentit (voir la section "Gestion de version"). mcanisme de verrouillage nest utilis.

Vous pouvez utiliser ces paramtres diffrents endroits en fonction de vos besoins. Vous pouvez lire puis verrouiller:

182

Java EE 6 et GlassFish 3

Book book = em.find(Book.class, 12); // Verrouille pour augmenter le prix em.lock(book, LockModeType.PESSIMISTIC); book.raisePriceByTwoEuros();

Ou vous pouvez lire et verrouiller:


Book book = em.find(Book.class, 12, LockModeType.PESSIMISTIC); // Le livre est dj verrouill : on augmente son prix book.raisePriceByTwoEuros();

La concurrence et le verrouillage sont les motivations essentielles de la gestion des versions.


Gestion de version

Java utilise le systme des versions: Java SE5.0, Java SE6.0, EJB3.1, JAX-RS1.0, etc. Lorsquune nouvelle version de JAX-RS apparat, par exemple, son numro de version est augment et vous mettez jour votre environnement avec JAX-RS1.1. JPA utilise exactement le mme mcanisme lorsque lon a besoin de versions dentits. La premire fois que vous rendez une entit persistante, elle prend le numro de version1. Si, plus tard, vous modifiez un attribut et que vous rpercutiez cette modification dans la base de donnes, le numro de version de lentit passe 2, etc. La version de lentit volue chaque fois quelle est modifie. Pour que ceci fonctionne, lentit doit possder un attribut annot par @Version, lui permettant de stocker son numro de version. Cet attribut est ensuite traduit par une colonne dans la base de donnes. Les types autoriss pour les numros de version sont int, Integer, short, Short, long, Long ou Timestamp. Le Listing4.30 montre comment ajouter un numro de version lentit Book.
Listing4.30: Lentit Book avec une annotation @Version
@Entity public class Book { @Id @GeneratedValue private Long id; @Version private Integer version; private String title; private Float price; private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters

Chapitre 4

Gestion des objets persistants 183

Lentit peut lire la valeur de sa version mais ne peut pas la modifier: seul le fournisseur de persistance peut initialiser ou modifier cette valeur lorsque lobjet est crit ou modifi dans la base de donnes. Dans le Listing4.31, par exemple, on rend une nouvelle entit Book persistante. Lorsque la transaction se termine, le fournisseur de persistance fixe son numro de version 1. Puis on modifie le prix du livre et, aprs lcriture des donnes dans la base, le numro de version est incrment et vaut donc2.
Listing4.31: Modification du prix dun livre
Book book = new Book("H2G2", 21f, "Best IT book", "123-456", 321, false); tx.begin(); em.persist(book); tx.commit(); assertEquals(1, book.getVersion()); tx.begin(); book.raisePriceByTwoEuros(); tx.commit(); assertEquals(2, book.getVersion());

Lattribut de version nest pas obligatoire, mais il est conseill lorsque lentit est susceptible dtre modifie en mme temps par plusieurs processus ou plusieurs threads. La gestion de version est au cur du verrouillage optimiste car elle offre une protection pour les modifications concurrentes pisodiques des entits. En fait, une entit est automatiquement gre par verrouillage optimiste lorsquelle utilise lannotation @Version.
Verrouillage optimiste

Comme son nom lindique, le verrouillage optimiste part du principe que les transactions sur la base de donnes nentreront pas en conflit les unes avec les autres. En dautres termes, on estime quil y a de fortes chances pour que la transaction qui modifie une entit soit la seule modifier cette entit cet instant. La dcision de verrouiller lentit est donc prise la fin de la transaction, afin de garantir que les modifications apportes lentit seront cohrentes avec ltat courant de la base de donnes. Les transactions qui violeraient cette contrainte provoqueraient la leve dune exception OptimisticLockException et seraient annules.

184

Java EE 6 et GlassFish 3

Comment lever une OptimisticLockException? Soit en verrouillant explicitement lentit (avec les mthodes lock() ou find()), soit en laissant le fournisseur de persistance contrler lattribut annot par @Version. Lutilisation de cette annotation permet au gestionnaire dentits deffectuer un verrouillage optimiste simplement en comparant la valeur de lattribut de version dans linstance de lentit avec la valeur de la colonne correspondante dans la base. Sans cette annotation, le gestionnaire dentits ne peut pas raliser de verrouillage optimiste. la Figure4.4, les transactions tx1 et tx2 obtiennent toutes les deux une instance de la mme entit de Book. ce moment prcis, la version de lentit est 1. La premire transaction augmente le prix du livre de 2 et valide cette modification: lorsque les donnes sont crites dans la base, le fournisseur de persistance incrmente le numro de version, qui passe donc 2. Si la seconde transaction augmente le prix de 5 et valide galement cette modification, le gestionnaire dentits de tx2 ralisera que le numro de version dans la base est diffrent de celui de lentit, ce qui signifie que la version a t modifie par une autre transaction: une exception OptimisticLockException sera alors lance.
tx1.begin(); // Le prix du livre est de 10 Book book = em.find(Book.class, 12); b t sio ) == 1 book.raisePriceByTwoEuros(); book.raisePriceByFiveEuros(); tx1.comit(); // Le prix est maintenant de 12 b sio ( == 2 temps tx2.begin(); // Le prix du livre est de 10 Book book = em.find(Book.class, 12); == 1 k t s

tx2.comit(); d p

t t k p

ll

ut 2

Figure4.4
OptimisticLockException est lance par la transaction tx2.

Le comportement par dfaut de lannotation @Version consiste lancer lexception OptimisticLockException lorsque les donnes sont crites dans la base (lorsque la transaction est valide ou par un appel explicite la mthode em.flush()), mais vous pouvez galement contrler lendroit de placement du verrou optimiste en choisissant une stratgie "lire puis verrouiller" ou "lire et verrouiller". Le code de lire et verrouiller, par exemple, serait de la forme:
Book book = em.find(Book.class, 12); // Verrouillage pour augmenter le prix em.lock(book, LockModeType.OPTIMISTIC); book.raisePriceByTwoDollars();

Chapitre 4

Gestion des objets persistants 185

Avec le verrouillage optimiste, la valeur du paramtre LockModeType peut tre OPTIMISTIC ou OPTIMISTIC_FORCE_INCREMENT (ou, respectivement, READ ou WRITE, mais ces valeurs sont dprcies). La seule diffrence entre les deux est quOPTIMISTIC_ FORCE_INCREMENT forcera une mise jour (incrmentation) de la colonne contenant la version de lentit. Il est fortement conseill dutiliser le verrouillage optimiste pour toutes les entits auxquelles on est susceptible daccder de faon concurrente. Ne pas utiliser de verrou peut provoquer un tat incohrent de lentit, la perte de modifications et dautres problmes. Ce type de verrouillage donne de meilleures performances car il dcharge la base de ce travail; cest une alternative au verrouillage pessimiste, qui, lui, exige un verrouillage de bas niveau de la base de donnes.
Verrouillage pessimiste

Le verrouillage pessimiste part du principe oppos celui du verrouillage optimiste puisquil consiste verrouiller systmatiquement lentit avant de la manipuler. Ce mcanisme est donc trs restrictif et dgrade les performances de faon significative puisquil implique que la base pose un verrou avec SELECT ... FOR UPDATE SQL lorsquelle lit les donnes. Gnralement, les bases de donnes offrent un service de verrouillage pessimiste permettant au gestionnaire dentits de verrouiller une ligne de la table pour empcher un autre thread de modifier cette mme ligne. Cest donc un mcanisme efficace pour garantir que deux clients ne modifieront pas la mme ligne en mme temps, mais il exige des vrifications de bas niveau qui pnalisent les performances. Les transactions qui violent cette contrainte provoquent la leve dune exception PessimisticLockException et sont annules. Le verrouillage optimiste convient bien lorsquil y a peu de contention entre les transactions mais, quand cette contention augmente, le verrouillage pessimiste peut se rvler prfrable car le verrou sur la base est obtenu immdiatement, alors que les transactions optimistes chouent souvent plus tard. En temps de crise, par exemple, les marchs boursiers reoivent dnormes ordres de ventes. Si 100millions de personnes veulent vendre leurs actions en mme temps, le systme doit utiliser un verrouillage pessimiste pour assurer la cohrence des donnes. Notez quactuellement le march est plutt pessimiste quoptimiste, mais cela na rien voir avec JPA. Le verrouillage pessimiste peut sappliquer aux entits qui ne sont pas annotes par @Version.

186

Java EE 6 et GlassFish 3

Rsum
Dans ce chapitre, nous avons vu comment interroger les entits. Le gestionnaire dentits est la pice matresse de la persistance des entits: il peut crer, modifier, rechercher par identifiant, supprimer et synchroniser les entits avec la base de donnes en utilisant le contexte de persistance, qui se comporte comme un cache de premier niveau. JPA fournit galement JPQL, un langage de requte trs puissant et indpendant des SGBDR. Grce lui, vous pouvez rcuprer les entits laide dune syntaxe claire disposant de clauses WHERE, ORDER BY ou GROUP BY. Lorsque vous accdez aux entits de faon concurrente, vous savez comment utiliser les numros de version et quand utiliser le verrouillage optimiste ou le verrouillage pessimiste. Dans le prochain chapitre, nous en apprendrons plus sur le cycle de vie des entits et verrons comment y greffer du code laide de mthodes de rappel ou dcouteurs.

5
Mthodes de rappel et couteurs
Au chapitre prcdent, nous avons vu comment interroger les entits lies une base de donnes. Nous savons maintenant comment rendre une entit persistante, la supprimer, la modifier et la retrouver partir de son identifiant. Grce JPQL, nous pouvons rcuprer une ou plusieurs entits en fonction de certains critres de recherche avec des requtes dynamiques, statiques et natives. Toutes ces oprations sont ralises par le gestionnaire dentits la composante essentielle qui manipule les entits et gre leur cycle de vie. Nous avons dcrit ce cycle de vie en crivant que les entits sont soit gres par le gestionnaire dentits (ce qui signifie quelles ont une identit de persistance et quelles sont synchronises avec la base de donnes), soit dtaches de la base de donnes et utilises comme des POJO classiques. Mais le cycle de vie dune entit est un peu plus riche. Surtout, JPA permet dy greffer du code mtier lorsque certains vnements concernent lentit: ce code est ensuite automatiquement appel par le fournisseur de persistance laide de mthodes de rappel. Vous pouvez considrer les mthodes de rappel et les couteurs comme les triggers dune base de donnes relationnelle. Un trigger excute du code mtier pour chaque ligne dune table alors que les mthodes de rappel et les couteurs sont appels sur chaque instance dune entit en rponse un vnement ou, plus prcisment, avant et aprs la survenue dun vnement. Pour dfinir ces mthodes "Pre" et "Post", nous pouvons utiliser des annotations ou des descripteurs XML.

Cycle de vie dune entit


Maintenant que nous connaissons la plupart des mystres des entits, intressonsnous leur cycle de vie. Lorsquune entit est cre ou rendue persistante par le gestionnaire dentits, celle-ci est dite gre. Auparavant, elle ntait considre par

188

Java EE 6 et GlassFish 3

la JVM que comme un simple POJO (elle tait alors dtache) et pouvait tre utilise par lapplication comme un objet normal. Ds quune entit devient gre, le gestionnaire synchronise automatiquement la valeur de ses attributs avec la base de donnes sous-jacente. Pour mieux comprendre tout ceci, examinez la Figure5.1, qui reprsente les tats que peut prendre une entit Customer, ainsi que les transitions entre ces tats.
Figure5.1 Cycle de vie dune entit.
Customer cust = new Customer() Customer cust = em.find() Requte JPQL Supprime par le ramasse-miettes Existe en mmoire em.persist(cust)

em.clear() em.merge(cust) Srialise vers une autre couche Dtache em.merge(cust) Gre

Supprime de la base de donnes, mais toujours en mmoire

em.remove(cust) Supprime

em.refresh(cust) Modifie avec les accesseurs Base de donnes

On cre une instance de lentit Customer laide de loprateur new. Ds lors, cet objet existe en mmoire bien que JPA ne le connaisse pas. Si lon nen fait rien, il devient hors de porte et finit par tre supprim par le ramasse-miettes, ce qui marque la fin de son cycle de vie. Nous pouvons aussi le rendre persistant laide de la mthode EntityManager.persist(), auquel cas lentit devient gre et son tat est synchronis avec la base de donnes. Pendant quelle est dans cet tat, nous pouvons modifier ses attributs en utilisant ses mthodes setters (customer. SetFirstName(), par exemple) ou rafrachir son contenu par un appel EntityManager.refresh(). Toutes ces modifications garderont lentit synchronise avec la base. Si lon appelle la mthode EntityManager.contains(customer), celle-ci renverra true car customer appartient au contexte de persistance (il est gr). Un autre moyen de grer une entit consiste la charger partir de la base de donnes laide de la mthode EntityManager.find() ou dune requte JPQL rcuprant une liste dentits qui seront alors toutes automatiquement gres. Dans ltat gr, un appel la mthode EntityManager.remove() supprime lentit de la base de donnes et elle nest plus gre. Cependant, lobjet Java continue dexister en mmoire, et il reste utilisable tant que le ramasse-miettes ne le supprime pas.

Chapitre 5

Mthodes de rappel et couteurs 189

Examinons maintenant ltat dtach. Nous avons vu au chapitre prcdent quun appel explicite EntityManager.clear() supprimait lentit du contexte de persistance elle devient alors dtache. Il y a un autre moyen, plus subtil, de dtacher une entit: en la srialisant. Bien que dans de nombreux exemples de ce livre les entits nhritent daucune classe, elles doivent implmenter linterface java. io.Serializable pour passer par un rseau afin dtre invoques distance ou pour traverser des couches afin dtre affiches dans une couche prsentation cette restriction est due non pas JPA mais Java. Une entit qui est srialise, qui passe par le rseau et est dsrialise est considre comme un objet dtach: pour la rattacher, il faut appeler la mthode EntityManager.merge(). Les mthodes de rappel et les couteurs permettent dajouter une logique mtier qui sexcutera lorsque certains vnements du cycle de vie dune entit surviennent, voire chaque fois quun vnement intervient dans le cycle de vie dune entit.

Mthodes de rappel
Le cycle de vie dune entit se dcompose en quatre parties: persistance, modification, suppression et chargement, qui correspondent aux oprations quivalentes sur la base de donnes. Chacune de ces parties est associe un vnement "Pr" et "Post" qui peut tre intercept par le gestionnaire dentits pour appeler une mthode mtier qui doit avoir t marque par lune des annotations du Tableau5.1.
Tableau5.1: Annotations des mthodes de rappel du cycle de vie

Annotation
@PrePersist @PostPersist

Description La mthode sera appele avant lexcution dEntityManager.persist(). La mthode sera appele aprs que lentit sera devenue persistante. Si lentit produit sa cl primaire (avec @GeneratedValue), sa valeur est accessible dans la mthode. La mthode sera appele avant une opration de modification de lentit dans la base de donnes (appel des setters de lentit ou de la mthode EntityManager.merge()). La mthode sera appele aprs une opration de modification de lentit dans la base de donnes. La mthode sera appele avant lexcution dEntityManager.remove(). La mthode sera appele aprs la suppression de lentit.

@PreUpdate

@PostUpdate @PreRemove @PostRemove

190

Java EE 6 et GlassFish 3

Annotation
@PostLoad

Description La mthode sera appele aprs le chargement de lentit (par une requte JPQL, par un appel EntityManager.find()) ou avant quelle soit rafrachie partir de la base de donnes. Il nexiste pas dannotation @PreLoad car cela naurait aucun sens dagir sur une entit qui na pas encore t construite.

La Figure5.2 a ajout ces annotations au diagramme dtats de la Figure5.1.


Figure5.2 Cycle de vie dune entit avec les annotations des mthodes de rappel.
Supprime par le ramasse-miettes findById ou JPQL @PostLoad @PrePersist @PostPersist @PreRemove Dtache @PostLoad aprs fusion @PreUpdate et @PostUpdate si l'entit a t modifie Gre Supprime @PostRemove Existe en mmoire

@PreUpdate and @PostUpdate lorsque les accesseurs sont appels @PostLoad aprs le rafrachissement

Avant dinsrer une entit dans la base de donnes, le gestionnaire dentits appelle la mthode annote par @PrePersist. Si linsertion ne provoque pas dexception, lentit est rendue persistante, son identifiant est cr, puis la mthode annote par @PostPersist est appele. Il en va de mme pour les mises jour (@PreUpdate, @ PostUpdate) et les suppressions (@PreRemove, @PostRemove). Lorsquune entit est charge partir de la base de donnes (via un appel EntityManager.find() ou une requte JPQL), la mthode annote par @PostLoad est appele. Lorsque lentit dtache a besoin dtre fusionne, le gestionnaire dentits doit dabord vrifier si la version en mmoire est diffrente de celle de la base (@PostLoad) et modifier les donnes (@PreUpdate, @PostUpdate) si cest le cas. Outre les attributs, les constructeurs, les getters et les setters, les entits peuvent contenir du code mtier pour valider leur tat ou calculer certains de leurs attributs. Comme le montre le Listing5.1, ce code peut tre plac dans des mthodes Java classiques invoques par dautres classes ou dans des mthodes de rappel (callbacks). Dans ce dernier cas, cest le gestionnaire dentits qui les appellera automatiquement en fonction de lvnement qui a t dclench.

Chapitre 5

Mthodes de rappel et couteurs 191

Listing5.1: Entit Customer avec mthodes de rappel


@Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Temporal(TemporalType.DATE) private Date dateOfBirth; @Transient private Integer age; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; @PrePersist @PreUpdate private void validate() { if (dateOfBirth.getTime() > new Date().getTime()) throw new IllegalArgumentException("Invalid date of birth"); if (!phoneNumber.startsWith("+")) throw new IllegalArgumentException("Invalid phone number"); } @PostLoad @PostPersist @PostUpdate public void calculateAge() { if (dateOfBirth == null) { age = null; return; } Calendar birth = new GregorianCalendar(); birth.setTime(dateOfBirth); Calendar now = new GregorianCalendar(); now.setTime(new Date()); int adjust = 0; if (now.get(DAY_OF_YEAR) - birth.get(DAY_OF_YEAR) < 0) { adjust = -1; } age = now.get(YEAR) - birth.get(YEAR) + adjust; } // Constructeurs, getters, setters }

Dans le Listing5.1, lentit Customer dfinit une mthode pour valider les donnes (elle vrifie les valeurs des attributs dateOfBirth et phoneNumber). Cette mthode tant annote par @PrePersist et @PreUpdate, elle sera appele avant linsertion

192

Java EE 6 et GlassFish 3

ou la modification des donnes dans la base. Si ces donnes ne sont pas valides, la mthode lvera une exception lexcution et linsertion ou la modification sera annule: ceci garantit que la base contiendra toujours des donnes valides. La mthode calculateAge() calcule lge du client. Lattribut age est transitoire et nest donc pas crit dans la base de donnes: lorsque lentit est charge, rendue persistante ou modifie, cette mthode calcule lge partir de la date de naissance et initialise lattribut. Les mthodes de rappel doivent respecter les rgles suivantes:

Elles peuvent avoir un accs public, priv, protg ou paquetage, mais elles ne peuvent pas tre statiques ni finales. Dans le Listing5.1, la mthode validate() est prive. Elles peuvent tre marques par plusieurs annotations du cycle de vie (la mthode validate() est annote par @PrePersist et @PreUpdate). Cependant, une annotation de cycle de vie particulire ne peut apparatre quune seule fois dans une classe dentit (il ne peut pas y avoir deux annotations @PrePersist dans la mme entit, par exemple). Elles peuvent lancer des exceptions non contrles mais pas dexceptions contrles. Le lancement dune exception annule la transaction sil y en a une en cours. Elles peuvent invoquer JNDI, JDBC, JMS et les EJB, mais aucune opration dEntityManager ou de Query. Avec lhritage, si une mthode est dfinie dans la superclasse, elle sera appele avant la mthode de la classe fille. Si, par exemple, la classe Customer du Listing5.1 hritait dune classe Person fournissant une mthode @PrePersist, cette dernire serait appele avant celle de Customer. Si une relation utilise la rpercussion des vnements, la mthode de rappel associe sera galement appele en cascade. Si un Customer contient une collection dadresses et que la suppression dun Customer soit rpercute sur Address, la suppression dun client invoquera la mthode @PreRemove dAddress et celle de Customer.

couteurs (listeners)

Chapitre 5

Mthodes de rappel et couteurs 193

Les mthodes de rappel dune entit fonctionnent bien lorsque la logique mtier nest lie qu cette entit. Les couteurs permettent dextraire cette logique dans une classe spare qui pourra tre partage par plusieurs entits. En ralit, un couteur dentit est simplement un POJO qui dfinit une ou plusieurs mthodes de rappel du cycle de vie. Pour enregistrer un couteur, il suffit que lentit utilise lannotation @EntityListeners. Par rapport lexemple prcdent, nous allons extraire les mthodes calculateAge() et validate() pour les placer respectivement dans deux classes couteurs, AgeCalculationListener (voir Listing5.2) et DataValidationListener (voir Listing5.3).
Listing5.2: couteur pour calculer lge dun client
public class AgeCalculationListener { @PostLoad @PostPersist @PostUpdate public void calculateAge(Customer customer) { if (customer.getDateOfBirth() == null) { customer.setAge(null); return; } Calendar birth = new GregorianCalendar(); birth.setTime(customer.getDateOfBirth()); Calendar now = new GregorianCalendar(); now.setTime(new Date()); int adjust = 0; if (now.get(DAY_OF_YEAR) - birth.get(DAY_OF_YEAR) < 0) { adjust = -1; } customer.setAge(now.get(YEAR) - birth.get(YEAR) + adjust);

Listing5.3: couteur pour valider les attributs dun client


public class DataValidationListener { @PrePersist @PreUpdate private void validate(Customer customer) { if (dateOfBirth.getTime() > new Date().getTime()) throw new IllegalArgumentException("Invalid date of birth"); if (!phoneNumber.startsWith("+")) throw new IllegalArgumentException("Invalid phone number"); } }

194

Java EE 6 et GlassFish 3

Une classe couteur ne doit obir qu des rgles simples. La premire est quelle doit avoir un constructeur public sans paramtre. La seconde est que les signatures des mthodes de rappel sont lgrement diffrentes de celles du Listing5.1. Lorsquelle est appele sur un couteur, une mthode de rappel doit en effet avoir accs ltat de lentit (le prnom et le nom du client, par exemple): elle doit donc avoir un paramtre dun type compatible avec celui de lentit. Nous avons vu que, lorsquelle est dfinie dans lentit, une mthode de rappel a la signature suivante, sans paramtre:
void <MTHODE>();

Les mthodes de rappel dfinies dans un couteur peuvent en revanche avoir deux types de signatures. Si une mthode doit servir plusieurs entits, elle doit prendre un paramtre de type Object:
void <MTHODE>(Object uneEntit)

Si elle nest destine qu une seule entit ou ses sous-classes, le paramtre peut tre celui de lentit:
void <MTHODE>(Customer customerOuSousClasses)

Pour indiquer que ces deux couteurs seront prvenus des vnements du cycle de vie de lentit Customer, celle-ci doit le prciser laide de lannotation @EntityListeners (voir Listing5.4). Cette annotation prend en paramtre une classe couteur ou un tableau dcouteurs. Lorsquil y a plusieurs couteurs et quun vnement du cycle de vie survient, le fournisseur de persistance parcourt chacun de ces couteurs dans lordre o ils ont t indiqus et invoquera la mthode de rappel en lui passant une rfrence lentit concerne par lvnement. Puis il appellera les mthodes de rappel de lentit elle-mme (sil y en a).
Listing5.4: Lentit Customer dfinit deux couteurs
@EntityListeners({DataValidationListener.class, AgeCalculationListener.class}) @Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Temporal(TemporalType.DATE)

Chapitre 5

Mthodes de rappel et couteurs 195

private Date dateOfBirth; @Transient private Integer age; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; // Constructeurs, getters, setters }

Ce code produit exactement le mme rsultat que lexemple prcdent (voir Listing 5.1). Lentit Customer utilise la mthode DataValidationListener.validate() pour valider ses donnes avant toute insertion ou mise jour et la mthode AgeCalculationListener.calculateAge() pour calculer son ge. Les rgles que doivent respecter les mthodes dun couteur sont les mmes que celles suivies par les mthodes de rappel, mis part quelques dtails:

Elles ne peuvent lancer que des exceptions non contrles. Ceci implique que les autres couteurs et mthodes de rappel ne seront pas appels et que lventuelle transaction sera annule. Dans une hirarchie de classes, si plusieurs entits dfinissent des couteurs, ceux de la superclasse seront appels avant ceux des sous-classes. Si une entit ne veut pas hriter des couteurs de sa superclasse, elle peut explicitement les exclure laide dune annotation @ExcludeSuperclassListeners (ou son quivalent XML).

Lentit Customer du Listing5.4 dfinissait deux couteurs, mais il est galement possible quun couteur soit dfini par plusieurs entits, ce qui peut se rvler utile lorsque lcouteur fournit une logique gnrale dont les entits pourront profiter. Le Listing5.5, par exemple, cre un couteur de dbogage affichant le nom des vnements dclenchs.
Listing5.5: couteur de dbogage utilisable par nimporte quelle entit
public class DebugListener { @PrePersist void prePersist(Object object) { System.out.println("prePersist"); } @PostPersist void postPersist(Object object) { System.out.println("postPersist"); }

196

Java EE 6 et GlassFish 3

@PreUpdate void preUpdate(Object object) { System.out.println("preUpdate"); } @PostUpdate void postUpdate(Object object) { System.out.println("postUpdate"); } @PreRemove void preRemove(Object object) { System.out.println("preRemove"); } @PostRemove void postRemove(Object object) { System.out.println("postRemove"); } @PostLoad void postLoad(Object object) { System.out.println("postLoad"); } }

Notez que chaque mthode prend un Object en paramtre, ce qui signifie que nimporte quel type dentit peut utiliser cet couteur en ajoutant la classe de DebugListener son annotation @EntityListeners. Cependant, pour que toutes les entits dune application utilisent cet couteur, il faudrait ajouter manuellement cette annotation chacune delles: pour viter cela, JPA permet de dfinir des couteurs par dfaut qui couvrent toutes les entits dune unit de persistance. Comme il nexiste pas dannotation sappliquant la porte entire dune unit de persistance, ces couteurs par dfaut ne peuvent tre dclars que dans un fichier dassociation XML. Au Chapitre3, nous avons vu comment utiliser les fichiers XML la place des annotations. Il suffit de suivre ici les mmes tapes pour dfinir DebugListener comme couteur par dfaut. Pour cela, vous devez crer et dployer avec lapplication le fichier XML prsent dans le Listing5.6.
Listing5.6: couteur de dbogage dfini comme couteur par dfaut
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" version="2.0">

Chapitre 5

Mthodes de rappel et couteurs 197

<persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="com.apress.javaee6.DebugListener"/> </entity-listeners> </persistence-unit-defaults> </persistence-unit-metadata> </entity-mappings>

Dans ce fichier, le marqueur <persistence-unit-metadata> sert dfinir toutes les mtadonnes qui nont pas dquivalent avec les annotations. Le marqueur <persistence-unit-defaults> dfinit toutes les valeurs par dfaut de lunit de persistance et <entity-listener> dfinit lcouteur par dfaut. Ce fichier doit tre nomm persistence.xml et tre dploy avec lapplication. DebugListener sera alors automatiquement appel par toutes les entits. Si lon dfinit une liste dcouteurs par dfaut, chacun deux sera appel dans lordre o il apparat dans le fichier XML. Les couteurs par dfaut sont toujours invoqus avant ceux dfinis par lannotation @EntityListeners. Pour quils ne sappliquent pas une entit particulire, celle-ci doit le prciser avec lannotation @ExcludeDefaultListeners, comme dans le Listing5.7.
Listing5.7: Lentit Customer exclut les couteurs par dfaut
@ExcludeDefaultListeners @Entity public class Customer { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String email; private String phoneNumber; @Temporal(TemporalType.DATE) private Date dateOfBirth; @Transient private Integer age; @Temporal(TemporalType.TIMESTAMP) private Date creationDate; // Constructeurs, getters, setters

198

Java EE 6 et GlassFish 3

Rsum
Ce chapitre a dcrit le cycle de vie dune entit et expliqu comment le gestionnaire dentits capture les vnements pour appeler les mthodes de rappel. Celles-ci peuvent tre dfinies sur une seule entit et marques par plusieurs annotations (@PrePersist, @PostPersist, etc.). Les mthodes de rappel peuvent galement tre extraites dans des classes couteurs pour tre utilises par plusieurs entits, voire toutes (en utilisant des couteurs par dfaut). Avec les mthodes de rappel, nous avons vu que les entits ne sont pas de simples objets anmiques qui ne contiendraient que des attributs, des getters et des setters: elles peuvent contenir une logique mtier appele par dautres objets de lapplication ou invoque automatiquement par le gestionnaire dentits au gr du cycle de vie de lentit. Les autres composants de Java EE6, comme les EJB, utilisent galement ce type dinterception.

6
Enterprise Java Beans
Le chapitre prcdent a montr comment implmenter des objets persistants avec JPA et comment les interroger avec JPQL. La couche de persistance utilise des objets qui encapsulent et associent leurs attributs une base de donnes relationnelle grce des annotations. Le principe consiste garder les entits aussi transparentes que possible et ne pas les mlanger avec la logique mtier. Les entits peuvent bien sr possder des mthodes pour valider leurs attributs, mais elles ne sont pas conues pour reprsenter des tches complexes, qui ncessitent souvent une interaction avec dautres composants (autres objets persistants, services externes, etc.). La couche de persistance pas plus que linterface utilisateur ne sont faites pour traiter du code mtier, surtout quand il y a plusieurs interfaces (web, Swing, terminaux mobiles, etc.). Pour sparer la couche de persistance de la couche prsentation, pour implmenter la logique mtier, pour ajouter la gestion des transactions et la scurit, les applications ont besoin dune couche mtier : avec Java EE, cette couche est implmente par les EJB (Enterprise Java Beans). La dcomposition en couches est importante pour la plupart des applications. En suivant une approche descendante, les chapitres prcdents sur JPA ont modlis les classes de domaine en dfinissant gnralement des noms (Artist, CD, Book, Customer, etc.). Au-dessus de cette couche, la couche mtier modlise les actions (ou verbes) de lapplication (crer un livre, acheter un livre, afficher une commande, livrer un livre). Souvent, cette couche interagit avec des services web externes (SOAP ou REST), envoie des messages asynchrones dautres systmes ( laide de JMS) ou poste des e-mails; elle orchestre diffrents composants allant des bases de donnes aux systmes externes, sert de plaque tournante aux transactions et la scurit et constitue un point dentre pour toutes sortes de clients comme les interfaces web (servlets ou beans grs par JSF), le traitement par lot ou les systmes externes.

200

Java EE 6 et GlassFish 3

Ce chapitre est une introduction aux EJB et les trois chapitres suivants vous donneront toutes les informations ncessaires pour construire la couche mtier dune application dentreprise. Nous y expliquerons les diffrents types dEJB et leurs cycles de vie; nous dcrirons galement la notion de programmation oriente aspect (POA), ainsi que la gestion des transactions et de la scurit.

Introduction aux EJB


Les EJB sont des composants ct serveur qui encapsulent la logique mtier et la prennent en charge; ils soccupent aussi de la scurit. Les EJB savent galement traiter les messages, lordonnancement, laccs distant, les services web (SOAP et REST), linjection de dpendances, le cycle de vie des composants, la programmation oriente aspect avec intercepteurs, etc. En outre, ils sintgrent parfaitement avec les autres technologies de JavaSE et JavaEE JDBC, JavaMail, JPA, JTA (Java Transaction API), JMS (Java Messaging Service), JAAS (Java Authentication and Authorization Service), JNDI (Java Naming and Directory Interface) et RMI (Remote Method Invocation). Cest la raison pour laquelle on les utilise pour construire les couches mtier (voir Figure6.1) au-dessus de la couche de persistance et comme point dentre pour les technologies de la couche prsentation, comme JSF (JavaServer Faces).
Figure6.1 Architecture en couches.
<<layer>> Prsentation

<<layer>> Logique mtier

<<layer>> Persistance

<<layer>> Base de donnes

Les EJB utilisent un modle de programmation trs puissant qui allie simplicit dutilisation et robustesse il rduit la complexit tout en ajoutant la rutilisabilit et

Chapitre 6

Enterprise Java Beans 201

ladaptabilit aux applications essentielles pour lentreprise. Cest srement actuellement le modle de dveloppement Java ct serveur le plus simple et, pourtant, tout ceci est facilement obtenu en annotant un objet Java ordinaire (un POJO) qui sera dploy dans un conteneur. Un conteneur EJB est un environnement dexcution qui fournit des services comme la gestion des transactions, le contrle de la concurrence, la gestion des pools et la scurit, mais les serveurs dapplications lui ont ajout dautres fonctionnalits, comme la mise en cluster, la rpartition de la charge et la reprise en cas de panne. Les dveloppeurs EJB peuvent dsormais se concentrer sur limplmentation de la logique mtier et laisser au conteneur le soin de soccuper des dtails techniques. Avec la version3.1, les EJB peuvent, plus que jamais, tre crits une bonne fois pour toutes et tre dploys sur nimporte quel conteneur respectant la spcification. Les API standard, les noms JNDI portables, les composants lgers et la configuration par exception facilitent ce dploiement sur les implmentations open-source ou commerciales. La technologie sous-jacente ayant t cre il y a dix ans, les applications EJB bnficient dune base de code stable et de haute qualit, utilise depuis longtemps par de nombreux environnements.
Types dEJB

Les applications dentreprise pouvant tre complexes, la plate-forme Java EE dfinit plusieurs types dEJB. Les Chapitres6 9 ne sintresseront quaux beans de session et au service timer: les premiers encapsulent la logique mtier de haut niveau et forment donc la partie la plus importante de la technologie des EJB. Un bean de session peut avoir les caractristiques suivantes:

Sans tat. Le bean de session ne contient aucun tat conversationnel entre les mthodes et nimporte quel client peut utiliser nimporte quel instance. Avec tat. Le bean de session contient ltat conversationnel qui doit tre mmoris entre les mthodes pour un utilisateur donn. Singleton. Un bean de session unique est partag par les clients et autorise les accs concurrents.

Le service timer est la rponse standard de Java EE au problme de lordonnancement des tches. Les applications dentreprise qui dpendent de notifications temporelles lutilisent pour modliser les processus mtier de type workflow. Les MDB (Message-Driven Beans) reoivent des messages asynchrones laide de JMS. Bien quils ne fassent pas partie de la spcification EJB, nous les traiterons

202

Java EE 6 et GlassFish 3

au Chapitre 13 car ce modle de composants sert essentiellement intgrer des systmes avec MOM (Message-Oriented Middleware). Les MDB dlguent gnralement la logique mtier aux beans de session. Les EJB peuvent galement tre utiliss comme points terminaux dun service web. Les Chapitres14 et15 prsenteront les services SOAP et REST, qui peuvent tre soit de simples POJO dploys dans un conteneur web, soit des beans de session dploys dans un conteneur EJB.
INFO Pour des raisons de compatibilit, la spcification EJB 3.1 mentionne encore les beans entits. Ce modle de composants persistants a t lagu et est susceptible dtre supprim de Java EE7. JPA tant la technologie qui a t retenue pour associer et interroger les bases de donnes, nous ne prsenterons pas les beans entits dans ce livre.

Anatomie dun EJB

Les beans de session encapsulent la logique mtier, sont transactionnels et reposent sur un conteneur qui gre un pool, la programmation multithreads, la scurit, etc. Pour crer un composant aussi puissant, il suffit pourtant dune seule classe Java et dune seule annotation. Au chapitre suivant, nous verrons toutefois que les beans de session peuvent tre plus complexes: ils peuvent utiliser diffrents types dinterfaces, dannotations, de configuration XML et dappels dinterception. Le Listing6.1 montre la simplicit avec laquelle un conteneur peut savoir quune classe est un bean de session et quelle fournit tous les services dentreprise.
Listing6.1: Un EJB sans tat simple
@Stateless public class BookEJB { @PersistenceContext(unitName = "chapter06PU") private EntityManager em; public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; } }

Chapitre 6

Enterprise Java Beans 203

Les versions prcdentes de J2EE exigeaient des dveloppeurs quils crent plusieurs artfacts pour obtenir un bean de session: une interface locale ou distante (ou les deux), une interface "home" locale ou distante (ou les deux) et un descripteur de dploiement. Java EE5 et EJB3.0 ont considrablement simplifi ce modle pour ne plus exiger quune seule classe et une ou plusieurs interfaces mtier. EJB 3.1 va encore plus loin puisquil permet un POJO annot dtre un bean de session. Comme le montre le code du Listing6.1, la classe nimplmente aucune interface et nutilise pas non plus de configuration XML: lannotation @Stateless suffit transformer une classe Java en composant transactionnel et scuris. Puis, en utilisant le gestionnaire dentits que nous avons prsent aux chapitres prcdents, BookEJB cre et rcupre des livres de la base de donnes de faon simple mais efficace. Nous verrons au chapitre suivant quil est galement trs simple de dclarer un bean tat ou un bean singleton. Cette simplicit sapplique aussi au code client. Lappel dune mthode de BookEJB ne ncessite quune seule annotation, @EJB, pour obtenir une rfrence laide dune injection. Cette injection de dpendances permet un conteneur (client, web ou EJB) dinjecter automatiquement une rfrence vers un EJB. Dans le Listing6.2, par exemple, la classe Main obtient une rfrence BookEJBRemote en annotant par @ EJB lattribut statique et priv bookEJB. Si lEJB est dploy dans un conteneur, Main doit accder cet EJB distance: il suffit dajouter une interface distante lEJB pour quon puisse y accder distance.
Listing6.2: Classe client invoquant lEJB sans tat
public class Main { @EJB private static BookEJBRemote bookEJB; public static void main(String[] args) { Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Scifi book created by Douglas Adams"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); bookEJB.createBook(book); }

Le Listing6.2 montre lune des diffrences qui existent entre une classe Java pure et un bean de session. Ici, la classe Main nutilise pas le mot-cl new pour crer une instance de BookEJB: elle doit dabord obtenir une rfrence lEJB par injection

204

Java EE 6 et GlassFish 3

ou par une recherche JNDI avant dappeler lune de ses mthodes. On utilise ce mcanisme parce que lEJB sexcute dans un environnement gr il doit tre dploy dans un conteneur (intgr ou non). Comme la plupart des composants de Java EE6, les EJB ont besoin des mtadonnes (exprimes sous forme dannotation ou de XML) pour informer le conteneur des actions requises (dmarcation des transactions) ou des services injecter. Les EJB peuvent tre dploys avec le descripteur de dploiement facultatif ejb-jar. xml, qui, sil est prsent, a priorit sur les annotations. Grce la configuration par exception, une simple annotation suffit gnralement transformer un POJO en EJB puisque le conteneur applique le comportement par dfaut.
Conteneur dEJB

Comme on la mentionn prcdemment, un EJB est un composant ct serveur qui doit sexcuter dans un conteneur. Cet environnement dexcution fournit les fonctionnalits essentielles, communes de nombreuses applications dentreprise:

Communication distante. Sans crire de code complexe, un client EJB (un autre EJB, une interface utilisateur, un processus non interactif, etc.) peut appeler des mthodes distance via des protocoles standard. Injection de dpendances. Le conteneur peut injecter plusieurs ressources dans un EJB (destinations et fabriques JMS, sources de donnes, autres EJB, variables denvironnement, etc.). Gestion de ltat. Le conteneur gre ltat des beans tat de faon transparente. Vous pouvez ainsi grer ltat dun client particulier, comme si vous dveloppiez une application classique. Pooling. Le conteneur cre pour les beans sans tat et les MDB un pool dinstances qui peut tre partag par plusieurs clients. Une fois quil a t invoqu, un EJB nest pas dtruit mais retourne dans le pool pour tre rutilis. Cycle de vie. Le conteneur prend en charge le cycle de vie de chaque composant. Messages. Le conteneur permet aux MDB dcouter les destinations et de consommer les messages sans quil soit ncessaire de trop se plonger dans les dtails de JMS. Gestion des transactions. Avec la gestion dclarative des transactions, un EJB peut utiliser des annotations pour informer le conteneur de la politique de

Chapitre 6

Enterprise Java Beans 205

transaction quil doit utiliser. Cest le conteneur qui prend en charge la validation ou lannulation des transactions.

Scurit. Les EJB peuvent prciser un contrle daccs au niveau de la classe ou des mthodes afin dimposer une authentification de lutilisateur et lutilisation de rles. Gestion de la concurrence. part les singletons, tous les autres types dEJB sont thread-safe par nature. Vous pouvez donc dvelopper des applications parallles sans vous soucier des problmes lis aux threads. Intercepteurs transversaux. Les problmes transversaux peuvent tre placs dans des intercepteurs qui seront automatiquement appels par le conteneur. Appels de mthodes asynchrones. Avec EJB 3.1, il est dsormais possible davoir des appels asynchrones sans utiliser de messages.

Lorsque lEJB est dploy, le conteneur soccupe de toutes ces fonctionnalits, ce qui permet au dveloppeur de se concentrer sur la logique mtier tout en bnficiant de ces services sans devoir ajouter le moindre code systme. Les EJB sont des objets grs. Lorsquun client appelle un EJB (comme dans le Listing6.2), il travaille non pas directement avec une instance de cet EJB mais avec un proxy de cette instance. chaque fois quun client invoque une mthode de lEJB, cet appel est en ralit pris en charge par le proxy. Tout ceci est, bien entendu, transparent pour le client: de sa cration sa destruction, un EJB vit dans un conteneur. Dans une application Java EE, le conteneur EJB interagira gnralement avec dautres conteneurs: le conteneur de servlets (responsable de la gestion de lexcution des servlets et des pages JSF), le conteneur client dapplication (pour la gestion des applications autonomes), le gestionnaire de messages (pour lenvoi, la mise en attente et la rception des messages), le fournisseur de persistance, etc. Ces conteneurs sexcutent tous dans un serveur dapplications (GlassFish, JBoss, Weblogic, etc.) dont limplmentation est spcifique mais qui fournit le plus souvent des fonctionnalits de clustering, de monte en charge, de rpartition de la charge, de reprise en cas de panne, dadministration, de cache, etc.
Conteneur intgr

Ds le moment o ils sont crs, les EJB doivent sexcuter dans un conteneur qui sexcute lui-mme dans une JVM spare. Pensez GlassFish, JBoss, Weblogic, etc.

206

Java EE 6 et GlassFish 3

et vous vous rappellerez que le serveur dapplications doit dabord tre lanc avant que vous puissiez dployer et utiliser vos EJB. Pour un environnement en production, o le serveur tourne en permanence, cest tout fait souhaitable; par contre, pour un environnement de dveloppement o lon a souvent besoin de dployer pour dboguer, par exemple, cela prend trop de temps. Un autre problme avec les serveurs qui sexcutent dans un processus diffrent est que cela limite les possibilits de tests unitaires car ils ne peuvent sexcuter simplement sans dployer lEJB sur un serveur. Pour rsoudre ces problmes, certaines implmentations de serveurs dapplications taient fournies avec des conteneurs intgrs, mais ceux-ci leur taient spcifiques. Dsormais, EJB3.1 contient la spcification dun conteneur intgr, ce qui assure la portabilit entre les diffrents serveurs. Le principe dun conteneur intgr est de pouvoir excuter des applications EJB dans un environnement JavaSE afin de permettre aux clients de sexcuter dans la mme JVM. Ceci permet notamment de faciliter les tests et lutilisation des EJB dans les applications classiques. LAPI du conteneur intgr (dfinie dans javax. ejb.embeddable) fournit le mme environnement gr que le conteneur dexcution de JavaEE et inclut les mmes services: injection, accs lenvironnement dun composant, gestion des transactions, etc. Lextrait de code suivant montre comment crer une instance dun conteneur intgr, obtenir un contexte JNDI, rechercher un EJB et appeler lune de ses mthodes:
EJBContainer ec = EJBContainer.createEJBContainer(); Context ctx = ec.getContext(); BookEJB bookEJB = (BookEJB) ctx.lookup("java:global/BookEJB"); bookEJB.createBook(book);

Dans le prochain chapitre, nous verrons comment utiliser lAPI de "bootstrap" pour lancer le conteneur et excuter les EJB.
Injection de dpendances et JNDI

Les EJB utilisent linjection de dpendances pour accder diffrents types de ressources (autres EJB, destinations JMS, ressources denvironnement, etc.). Dans ce modle, le conteneur pousse les donnes dans le bean. Comme le montre le Listing6.2, un client sinjecte une dpendance un EJB laide de lannotation @EJB:
@EJB private static BookEJB bookEJB;

Chapitre 6

Enterprise Java Beans 207

Linjection a lieu lors du dploiement. Si les donnes risquent de ne pas tre utilises, le bean peut viter le cot de linjection en effectuant la place une recherche JNDI. En ce cas, le code ne prend les donnes que sil en a besoin au lieu daccepter des donnes qui lui sont transmises et dont il naura peut-tre pas besoin. JNDI est une API permettant daccder diffrents types de services dannuaires, elle permet au client de lier et de rechercher des objets par nom. JNDI est dfinie dans JavaSE et est indpendante de limplmentation sous-jacente, ce qui signifie que les objets peuvent tre recherchs dans un annuaire LDAP (Lightweight Directory Access Protocol) ou dans un DNS (Domain Name System) laide dune API standard. Lalternative au code prcdent consiste donc utiliser un contexte JNDI et y rechercher un EJB dploy portant le nom java:global/chapter06/BookEJB:
Context ctx = new InitialContext(); BookEJB bookEJB = (BookEJB)ctx.lookup("java:global/chapter06/BookEJB");

JNDI existe depuis longtemps mais, bien que son API ft standardise et portable entre les serveurs dapplications, ce ntait pas le cas des noms JNDI, qui restaient spcifiques aux plates-formes. Lorsquun EJB tait dploy dans GlassFish ou JBoss, son nom dans le service dannuaire tait diffrent et donc non portable: un client devait rechercher un EJB avec un certain nom sous GlassFish et un autre sous JBoss... EJB3.1 a standardis les noms JNDI afin quils soient dsormais portables. Dans lexemple prcdent, le nom java:global/chapter06/BookEJB respecte cette nouvelle convention de nommage:
java:global[/<nom-app>]/<nom-module>/<nom-bean> [!<nom-interface-pleinement-qualifi>]

Le chapitre suivant montrera comment utiliser ce nom pour rechercher des EJB.
Mthodes de rappel et intercepteurs

Le cycle de vie de tous les types dEJB (sans et avec tat, singleton et MDB) est gr par le conteneur. Un EJB peut ainsi avoir des mthodes annotes (@PostConstruct, @ PreDestroy, etc.) ressemblant aux mthodes de rappel utilises par les entits et qui seront automatiquement appeles par le conteneur au cours des diffrentes tapes de son cycle de vie. Ces mthodes peuvent initialiser ltat du bean, rechercher des ressources avec JNDI ou librer les connexions aux bases de donnes.

208

Java EE 6 et GlassFish 3

Pour les problmes transversaux, les dveloppeurs peuvent utiliser des intercepteurs qui reposent sur le modle de la programmation par aspect, dans lequel lappel dune mthode est automatiquement enrichi de fonctionnalits supplmentaires. Les cycles de vie des EJB, les mthodes de rappel et les intercepteurs seront tudis au Chapitre8 (le Chapitre5 a prsent le cycle de vie des entits).
Assemblage

Comme la plupart des composants Java EE (servlets, pages JSF, services web, etc.), les EJB doivent tre assembls avant dtre dploys dans un conteneur dexcution. Dans la mme archive, on trouve gnralement la classe bean mtier, ses interfaces, intercepteurs, les ventuelles superclasses ou superinterfaces, les exceptions, les classes utilitaires et, ventuellement, un descripteur de dploiement (ejb-jar.xml). Lorsque tous ces artfacts sont assembls dans un fichier jar, on peut les dployer directement dans un conteneur. Une autre possibilit consiste intgrer le fichier jar dans un fichier ear (entreprise archive) et dployer ce dernier. Un fichier ear sert assembler un ou plusieurs modules (des EJB ou des applications web) en une archive unique afin que leur dploiement sur un serveur dapplications soit simultan et cohrent. Comme le montre la Figure6.2, pour dployer une application web on peut assembler les EJB et les entits dans des fichiers jar spars, les servlets dans un fichier war et tout regrouper dans un fichier ear. Il suffit ensuite de dployer ce fichier sur le serveur dapplications pour pouvoir manipuler les entits partir de la servlet en utilisant les EJB.
Figure6.2 Assemblage des EJB.
<<artifact>> BookApplication.ear
<<artifact>> BookEJB.jar session/BookEJB.Class META-INF/ejb-jar.xml <<artifact>> BookEntity.jar entity/Book.Class META-INF/persitence.xml <<artifact>> BookServlet.war servlet/BookServlet.Class WEB-INF/web.xml

<<artifact>> BookApplication.war session/BookEJB.class entity/Book.class servlet.BookServlet META INF/ejb jar.xml META INF/persistence.xml WEB INF/web.xml

Depuis EJB 3.1, les EJB peuvent galement tre assembls directement dans un module web (un fichier war). droite de la Figure 6.2, la servlet, lEJB et lentit sont tous assembls dans le mme fichier war, avec tous les descripteurs de

Chapitre 6

Enterprise Java Beans 209

dploiement. Vous remarquerez que le descripteur de dploiement est stock dans META-INF/ejb-jar.xml dans le module EJB et dans WEB-INF/web-jar.xml dans le module web.

Tour dhorizon de la spcification EJB


La spcification EJB 1.0 remonte 1998 et EJB 3.1 est apparue en 2009 avec Java EE6. Au cours de ces dix annes, la spcification a beaucoup volu tout en conservant ses bases solides. Des composants lourds aux POJO annots en passant par les beans entits et par JPA, les EJB se sont rinvents afin de mieux correspondre aux besoins des dveloppeurs et des architectures modernes. Plus que jamais, la spcification EJB 3.1 permet dviter la dpendance vis--vis des diteurs en fournissant des fonctionnalits qui, auparavant, ntaient pas standard (les noms JNDI ou les conteneurs intgrs, par exemple). Elle est donc bien plus portable que par le pass.
Historique

Peu aprs la cration du langage Java, lindustrie a ressenti le besoin de disposer dune technologie permettant de satisfaire les besoins des applications grande chelle et qui intgrerait RMI et JTA. Lide dun framework de composants mtier distribu et transactionnel fit donc son chemin et, en rponse, IBM commena dvelopper ce qui allait ensuite devenir EJB. EJB 1.0 reconnaissait les beans de session avec et sans tat et disposait dun support optionnel des beans entits. Le modle de programmation utilisait des interfaces "home" et distantes en plus du bean session lui-mme; les EJB taient accessibles via une interface qui offrait un accs distant avec des paramtres passs par valeur. EJB 1.1 ajouta le support des beans entits et introduisit les descripteurs de dploiement XML pour stocker les mtadonnes (qui taient ensuite srialises en binaire dans un fichier). Cette version grait mieux lassemblage et le dploiement des applications grce lintroduction des rles. En 2001, EJB 2.0 fut la premire version tre standardise par le JCP (sous le nom de JSR 19). Elle rsolvait le problme du surcot du passage des paramtres par valeur en introduisant les interfaces locales. Les clients sexcutant dans le conteneur accdaient aux EJB par leur interface locale (en utilisant des paramtres passs

210

Java EE 6 et GlassFish 3

par rfrence) et ceux qui sexcutaient dans un autre conteneur utilisaient linterface distante. Cette version a galement introduit les MDB, et les beans entits ont reu le support des relations et dun langage de requtes (EJB QL). Deux ans plus tard, EJB 2.1 (JSR 153) a ajout le support des services web, permettant ainsi aux beans de session dtre invoqus par SOAP/HTTP. Un service timer fut galement cr pour pouvoir appeler les EJB des instants prcis ou des intervalles donns. Trois ans se sont couls entre EJB 2.1 et EJB 3.0, ce qui a permis au groupe dexperts de remodliser entirement la conception. En 2006, la spcification EJB3.0 (JSR220) amora une rupture avec les versions prcdentes en sattachant la simplicit dutilisation grce des EJB ressemblant plus des POJO. Les beans entits furent remplacs par une toute nouvelle spcification (JPA) et les beans de session neurent plus besoin dinterfaces "home" ou spcifiques. Linjection des dpendances, les intercepteurs et les mthodes de rappel du cycle de vie firent leur apparition. En 2009, la spcification EJB 3.1 (JSR 318) fut intgre Java EE6; elle poursuit dans la voie de la version prcdente en simplifiant encore le modle de programmation et en lui ajoutant de nouvelles fonctionnalits.
Nouveauts dEJB 3.1

La spcification EJB 3.1 (JSR 318) a apport plusieurs modifications : JPA ne fait dsormais plus partie de la spcification EJB et volue dans une JSR distincte (JSR317). La spcification est maintenant organise en deux documents diffrents:

"EJB Core Contracts and Requirements" est le document principal qui spcifie les EJB. "Interceptor Requirements" est le document qui spcifie les intercepteurs.

Il faut garder lesprit que la spcification doit supporter le modle de composant EJB2.x, ce qui signifie que ses 600pages doivent tenir compte des interfaces "home", des beans entits, dEJB QL, etc. Pour simplifier ladoption future de la spcification, le groupe dexperts Java EE6 a rassembl une liste de fonctionnalits ventuellement amenes disparatre: aucune na t supprime dEJB3.1, mais la prochaine version en retiendra et en supprimera certaines:

beans entits 2.x; vue cliente dun bean entit 2.x;

Chapitre 6

Enterprise Java Beans 211

EJB QL (langage de requte pour la persistance gre par un conteneur); services web JAX-RPC; vue cliente dun service web JAX-RPC. Vue sans interface. On peut accder aux beans de session avec une vue locale sans passer par une interface mtier locale. Dploiement war. Il est dsormais possible dassembler et de dployer directement les composants EJB dans un fichier war. Conteneur intgr. Une nouvelle API embeddable permet dexcuter les composants EJB dans un environnement JavaSE (pour les tests unitaires, les traitements non interactifs, etc.). Singleton. Ce nouveau type de composant facilite laccs ltat partag. Service timer plus labor. Cette fonctionnalit permet de crer automatiquement des expressions temporelles. Asynchronisme. Les appels asynchrones sont dsormais possibles sans MDB. EJB Lite. Dfinit un sous-ensemble de fonctionnalits utilisables dans les profils JavaEE (le profil web, par exemple). Noms JNDI portables. La syntaxe de recherche des composants EJB est dsormais standard.

La spcification EJB 3.1 ajoute les fonctionnalits et les simplifications suivantes:

EJB Lite

Les Enterprise Java Beans sont le modle de composant prdominant de Java EE6 car cest la mthode la plus simple pour effectuer des traitements mtiers transactionnels et scuriss. Cependant, EJB 3.1 continue de dfinir les beans entits, les interfaces "home", EJB QL, etc., ce qui signifie quun nouvel diteur qui implmenterait la spcification EJB3.1 devrait galement implmenter les beans entits. Les dveloppeurs dbutant avec les EJB seraient donc submergs par de nombreuses technologies dont ils nont finalement pas besoin. Pour toutes ces raisons, la spcification dfinit EJB Lite, un sous-ensemble minimal de lAPI EJB. Ce sous-ensemble comprend un choix rduit mais efficace des fonctionnalits des EJB adaptes lcriture dune logique mtier portable, transactionnelle et

212

Java EE 6 et GlassFish 3

scurise. Toute application EJB Lite peut tre dploye sur nimporte quel produit JAVAEE implmentant EJB3.1. Le Tableau6.1 numre ses composantes.
Tableau6.1: Comparaison entre EJB Lite et EJB complte

Fonctionnalit Beans de session beans (avec et sans tat, singleton) MDB Beans entits 1.x/2.x Vue sans interface Interface locale Interface distante Interfaces 2.x Services web JAX-WS Services web JAX-RS Services web JAX-RPC Timer service Asynchronous calls Interceptors Interoprabilit RMI/IIOP Support des transactions Scurit API Embeddable

EJB Lite Oui Non Non Oui Oui Non Non Non Non Non Non Non Oui Non Oui Oui Oui

EJB 3.1 complte Oui Oui Oui (lagable) Oui Oui Oui Oui (lagable) Oui Oui Oui (lagable) Oui Oui Oui Oui Oui Oui Oui

Implmentation de rfrence GlassFish est un projet de serveur dapplications open-source conduit par Sun Microsystems pour la plate-forme JavaEE. Lanc en 2005, il est devenu limplmentation de rfrence de JavaEE5 en 2006. Aujourdhui, GlassFishv3 est limplmentation de rfrence dEJB3.1. Ce produit est construit de faon modulaire (il repose sur le runtime OSGi Felix dApache), ce qui lui permet de dmarrer trs rapidement, et il utilise diffrents conteneurs dapplications (Java EE6, bien sr, mais galement Ruby, PHP, etc.).

Chapitre 6

Enterprise Java Beans 213

Dans ce livre, nous utiliserons GlassFish comme serveur dapplications pour dployer et excuter les EJB, les pages JSF, les services web SOAP et REST et les MDB JMS.

Rcapitulatif
Dans la section "Rcapitulatif" du Chapitre2, nous avons vu le dveloppement complet dune entit Book (prsente dans le Listing2.3) qui tait associe une base de donnes Derby. Puis le Listing2.4 a prsent une classe Main utilisant le gestionnaire dentits pour rendre un livre persistant et rcuprer tous les livres de la base (en utilisant des dmarcations explicites de transactions tx.begin() et tx.commit()). Nous allons ici reprendre ce cas dutilisation, mais en remplaant la classe Main du Chapitre2 par un bean de session sans tat (BookEJB). Par nature, les EJB sont transactionnels: BookEJB prendra donc en charge les oprations CRUD (Create, Read, Update, Delete) de lentit Book. BookEJB et Book seront ensuite assembls et dploys dans GlassFish. LEJB a besoin dune interface distante car une application cliente externe (la classe Main) appellera distance les mthodes de lEJB (voir Figure6.3) en se servant dun conteneur client dapplication.
Figure6.3 Rcapitulatif.
Main +main(args : String []) : void <<Interface>> BookEJBRemote +findBooks() : List<Book> +findBookByld(id : Long) : Book +createBook(book : Book) : Book +deleteBook(book : Book) : void +updateBook(book : Book) : Book

<<stateless ejb>> BookEJB em : EntityManager +findBooks() : List<Book> +findBookByld(id : Long) : Book +createBook(book : Book) : Book +deleteBook(book : Book) : void +updateBook(book : Book) : Book

<<entity>> Book id : Long jdbc/chapter06DS title : String chapter06DB price : Float description : String isbn : String nbOfPage : Integer illustrations : Boolean

214

Java EE 6 et GlassFish 3

Pour utiliser les transactions, le bean de session sans tat doit accder la base en passant par une source de donnes (jdbc/chapter06DS) qui devra tre cre dans GlassFish et tre lie la base chapter06DB. La structure de rpertoire du projet respecte les conventions de Maven; les classes et les fichiers seront donc placs dans les rpertoires suivants:
src/main/java,

pour lentit

Book, BookEJB,

linterface

BookEJBRemote

et la

classe Main;
src/main/resources:, src/test/java,

pour le fichier persistence.xml, qui contient lunit de persistance pour le SGBDR Derby; pour la classe des tests unitaires BookTest; pour le fichier persistence.xml, utilis pour la base de donnes intgre Derby servant aux cas de tests; modules et composants externes.

src/test/resources,

pom.xml, le fichier Maven dcrivant le projet et ses dpendances vis--vis dautres

Lentit Book

Le Listing 6.3 dcrivant la mme entit Book que celle du Chapitre 2 (voir Listing2.3), nous ny reviendrons pas. Notez toutefois que son fichier doit se trouver dans le rpertoire src/main/java.
Listing6.3: Entit Book avec une requte nomme
@Entity @NamedQuery(name = "findAllBooks", query = "SELECT b FROM Book b") public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Chapitre 6

Enterprise Java Beans 215

Le bean de session sans tat BookEJB


BookEJB

est un bean de session sans tat qui sert de faade et gre les oprations CRUD de lentit Book. Le Listing6.4 montre que cette classe doit tre annote par @ javax.ejb.Stateless et quelle implmente linterface BookEJBRemote dcrite dans le Listing6.5. Par injection de dpendances, lEJB obtient une rfrence un gestionnaire dentits qui est ensuite employ pour chacune des mthodes suivantes:
findBooks utilise la requte nomme findAllBooks dfinie dans lentit Book pour

rcuprer toutes les instances de Book dans la base de donnes.


findBookById invoque EntityManager.find() pour retrouver un livre dans la base

partir de son identifiant.


createBook updateBook

rend persistante linstance de Book qui lui est passe en paramtre.

utilise la mthode merge() pour attacher au gestionnaire dentits lobjet Book dtach qui lui est pass en paramtre. Cet objet est alors synchronis avec la base de donnes. est une mthode qui rattache lobjet qui lui est pass en paramtre au gestionnaire dentits, puis le supprime.

deleteBook

Listing6.4: Bean de session sans tat servant de faade aux oprations CRUD
@Stateless public class BookEJB implements BookEJBRemote { @PersistenceContext(unitName = "chapter06PU") private EntityManager em; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); } public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; } public void deleteBook(Book book) { em.remove(em.merge(book));

216

Java EE 6 et GlassFish 3

} public Book updateBook(Book book) { return em.merge(book); } }

Les principales diffrences entre la classe Main du Chapitre2 (voir Listing2.4) et celle du Listing6.4 est quune instance dEntityManager est directement injecte dans le bean de session: on nutilise plus une EntityManagerFactory pour crer le gestionnaire. Le conteneur EJB grant le cycle de vie de lEntityManager, il en injecte une instance puis la ferme lorsque lEJB est supprim. En outre, les appels JPA ne sont plus encadrs par tx.begin() et tx.commit() car les mthodes des beans de session sont implicitement transactionnelles. Ce comportement par dfaut sera dcrit au Chapitre9. Le BookEJB doit implmenter une interface distante puisquil est invoqu distance par la classe Main. Comme le montre le Listing6.5, la seule diffrence entre une interface Java normale et une interface distante est la prsence de lannotation @Remote.
Listing6.5: Interface distante
@Remote public interface BookEJBRemote { public public public public public } List<Book> findBooks(); Book findBookById(Long id); Book createBook(Book book); void deleteBook(Book book); Book updateBook(Book book);

Unit de persistance pour le BookEJB

Au Chapitre2, les transactions taient gres par lapplication (transaction-type ="RESOURCE_LOCAL") et lunit de persistance (voir Listing2.5) devait donc dfinir le pilote et lURL JDBC, ainsi que lutilisateur et son mot de passe afin dtablir une connexion la base de donnes Derby. Dans un environnement gr par un conteneur comme celui des EJB, les transactions sont en revanche gres par le conteneur, non par lapplication; cest la raison pour laquelle le type de transaction de lunit de persistance doit valoir JTA (voir Listing6.6).

Chapitre 6

Enterprise Java Beans 217

Listing6.6: Unit de persistance utilisant la source de donnes chapter06DS


<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="chapter06PU" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <jta-data-source>jdbc/chapter06DS</jta-data-source> <class>com.apress.javaee6.chapter06.Book</class> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.logging.level" value="INFO"/> </properties> </persistence-unit> </persistence>

Dans le Listing6.4, on injecte dans le BookEJB une rfrence un EntityManager associ lunit de persistance chapter06PU. Celle-ci (dfinie dans le Listing6.6) doit dfinir la source de donnes laquelle se connecter (jdbc/chapter06DS) sans prciser dautres informations daccs (URL, pilote JDBC, etc.) car elles sont contenues dans la source de donnes qui sera cre plus tard dans GlassFish.
La classe Main

La classe Main (voir Listing6.7) dclare une instance de linterface BookEJBRemote et la dcore avec lannotation @EJB pour quune rfrence puisse tre injecte noubliez pas que cette classe Main est excute dans le conteneur client dapplication et que linjection est donc possible. La mthode main() commence par crer une nouvelle instance de Book, initialise ses attributs et utilise la mthode createBook() de lEJB pour la rendre persistante. Puis elle modifie le titre, met jour le livre dans la base et le supprime. Ce code nayant pas de contexte de persistance, lentit Book est un objet dtach manipul comme une classe Java normale, sans intervention de JPA. Cest lEJB qui dtient le contexte de persistance et utilise le gestionnaire dentits pour accder la base de donnes.
Listing6.7: Classe Main utilisant le BookEJB
public class Main { @EJB private static BookEJBRemote bookEJB; public static void main(String[] args) { Book book = new Book();

218

Java EE 6 et GlassFish 3

book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Scifi book created by Douglas Adams"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); bookEJB.createBook(book); book.setTitle("H2G2"); bookEJB.updateBook(book); bookEJB.deleteBook(book); }

Compilation et assemblage avec Maven

Nous pouvons maintenant utiliser Maven pour compiler lentit Book, le BookEJB, linterface BookEJBRemote et la classe Main, puis assembler le rsultat dans un fichier jar avec lunit de persistance. Maven utilise un fichier pom.xml (voir Listing6.8) pour dcrire le projet et les dpendances externes. Ici, on a besoin de lAPI JPA (javax.persistence) et de lAPI EJB (javax.ejb). Les classes seront compiles et assembles (<packaging>jar</packaging>) dans un fichier jar nomm chapter06- 1.0.jar. Comme le montre le Listing6.8, llment maven-compiler-plugin indique Maven que lon utilise Java SE6.
Listing6.8: Fichier pom.xml utilis par Maven pour construire lapplication
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId> com.apress.javaee6</groupId> <artifactId>chapter06</artifactId> <packaging>jar</packaging> <version>1.0</version> <name>chapter06</name> <dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.0</version> </dependency> <dependency>

Chapitre 6

Enterprise Java Beans 219

<groupId>org.glassfish.embedded</groupId> <artifactId>glassfish-embedded-all</artifactId> <version>3.0</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.2</version> <configuration> <archive> <manifest> <mainClass> com.apress.javaee6.chapter06.Main </mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

le fichier jar produit contiendra un fichier permettant dajouter des mtadonnes la structure mme du jar. Pour que le jar soit excutable, on ajoute la classe Main llment Main-Class.
META-INF\MANIFEST.MF

Grce lextension

maven-jar-plugin,

Vous remarquerez que ce code contient la dpendance glassfish-embedded-all, qui est utilise par la classe de test (<scope>test</scope>) pour invoquer le conteneur intgr et lancer lEJB. Pour compiler et assembler les classes, tapez la commande suivante dans une fentre de commandes:
mvn package

220

Java EE 6 et GlassFish 3

Le message BUILD SUCCESSFUL devrait safficher pour vous informer du succs de lopration. Si vous vrifiez le contenu du rpertoire target, vous constaterez que Maven a cr le fichier chapter06-1.0.jar.
Dploiement sur GlassFish

Maintenant que le bean de session BookEJB a t assembl dans une archive jar, nous pouvons le dployer sur le serveur dapplications GlassFish (aprs nous tre assurs que GlassFish et Derby sexcutent). La source de donnes jdbc/chapter06DS ncessaire lunit de persistance doit tre cre laide de la console dadministration de GlassFish ou partir de la ligne de commandes, lutilisation de cette dernire tant la plus rapide et la plus simple reproduire. Avant de crer une source de donnes, nous avons besoin dun pool de connexions. GlassFish dfinit un ensemble de pools prts lemploi, mais nous pouvons crer le ntre laide de la commande suivante:
asadmin create-jdbc-connection-pool --datasourceclassname=org.apache.derby.jdbc.ClientDataSource --restype=javax.sql.DataSource --property portNumber=1527:password=APP:user=APP: serverName=localhost:databaseName=chapter06DB: connectionAttributes=;create\=true Chapter06Pool

Cette commande cre le pool Chapter06Pool en utilisant une source de donnes Derby et un ensemble de proprits dfinissant la connexion la base: son nom (chapter06DB), le serveur (localhost), le port (1527), un utilisateur (APP) et un mot de passe (APP). Si lon teste maintenant cette source de donnes, Derby crera automatiquement la base (car lon a prcis connectionAttributes=;create\ =true). La commande suivante permet de tester la source de donnes:
asadmin ping-connection-pool Chapter06Pool

Aprs lexcution de cette commande, le rpertoire chapter06DB devrait apparatre sur le disque dur lendroit o Derby stocke les donnes. La base et le pool de connexions tant crs, nous devons maintenant dclarer la source de donnes jdbc/ chapter06DS et la lier ce pool:
asadmin create-jdbc-resource --connectionpoolid Chapter06Pool jdbc/chapter06DS

La commande suivante numre toutes les sources de donnes hberges par GlassFish:
asadmin list-jdbc-resources

Chapitre 6

Enterprise Java Beans 221

Lutilitaire asadmin permet de dployer lapplication sur GlassFish. Aprs excution, la commande suivante affichera un message nous informant du rsultat du dploiement:
asadmin deploy --force=true target\chapter06-1.0.jar

Maintenant que lEJB est dploy sur GlassFish avec lentit et lunit de persistance, que Derby sexcute et que la source de donnes a t cre, il est temps de lancer la classe Main.
Excution de la classe Main avec Derby

La classe Main (voir Listing6.7) est une application autonome qui sexcute lextrieur du conteneur GlassFish, mais elle utilise lannotation @EJB, qui a besoin dun conteneur pour injecter une rfrence linterface BookEJBRemote. La classe Main doit sexcuter dans un conteneur client dapplication (ACC); nous aurions pu utiliser une recherche JNDI au lieu dun ACC, mais ce dernier peut comprendre un fichier jar pour lui donner accs aux ressources du serveur dapplications. Pour excuter lACC, il suffit dutiliser le programme appclient fourni avec GlassFish en lui passant le fichier jar en paramtre:
appclient -client chapter06-1.0.jar

Noubliez pas que le fichier chapter06-1.0.jar est excutable puisque nous avons ajout un lment Main-Class au fichier MANIFEST.MF. Avec la commande prcdente, lACC excute la classe Main et injecte une rfrence linterface BookEJBRemote, qui, son tour, cre, modifie et supprime lentit Book.
La classe BookEJBTest

Pour les quipes de dveloppement modernes, la classe Main ne suffit pas il faut appliquer des tests unitaires aux classes. Avec la version prcdente des EJB, tester unitairement BookEJB ntait pas chose facile car il fallait utiliser des fonctionnalits spcifiques de certains serveurs dapplications ou bricoler le code. Dsormais, grce au nouveau conteneur intgr, un EJB devient une classe testable comme une autre car elle peut sexcuter dans un environnement JavaSE. La seule exigence consiste ajouter un fichier jar spcifique au classpath, comme on la fait dans le fichier pom. xml du Listing6.8 avec la dpendance glassfish-embedded-all.

222

Java EE 6 et GlassFish 3

Au Chapitre2, nous avons dj prsent tous les artfacts requis par les tests unitaires avec une base de donnes intgre. Pour tester unitairement lEJB, nous avons besoin de la base Derby intgre, dune unit de persistance diffrente et du conteneur dEJB intgr. Il suffit ensuite dcrire une classe de test JUnit (voir Listing 6.9) pour initialiser lEJBContainer (EJBContainer.createEJBContainer()), lancer quelques tests (createBook()) et fermer le conteneur (ec.close()).
Listing6.9: Classe JUnit pour tester lEJB avec le conteneur intgr
public class BookEJBTest { private static EJBContainer ec; private static Context ctx; @BeforeClass public static void initContainer() throws Exception { ec = EJBContainer.createEJBContainer(); ctx = ec.getContext(); } @AfterClass public static void closeContainer() throws Exception { ec.close(); } @Test public void createBook() throws Exception { // Cration dune instance de Book Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction comedy book"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); // Recherche de lEJB BookEJBRemote bookEJB = (BookEJBRemote) ctx.lookup("java:global/chapter06/BookEJBRemote"); // Rend le livre persistant dans la base book = bookEJB.createBook(book); assertNotNull("ID should not be null", book.getId()); // Rcupre tous les livres de la base List<Book> books = bookEJB.findBooks(); assertNotNull(books); } }

Chapitre 6

Enterprise Java Beans 223

La mthode de test createBook() cre une instance de Book, recherche linterface distante en utilisant JNDI pour rendre le livre persistant et rcupre la liste de tous les livres stocks dans la base. Assurez-vous que la classe BookEJBTest soit dans le rpertoire src/test/java de Maven, puis faites la commande suivante:
mvn test

BookEJBTest

sexcute et Maven devrait vous informer que le test sest bien pass:

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 12.691 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ---------------------------------------------------------------- [INFO] BUILD SUCCESSFUL [INFO] --------------------------------------------------------------- [INFO] Total time: 26 seconds [INFO] Finished [INFO] Final Memory: 4M/14M [INFO] ---------------------------------------------------------------

Rsum
Ce chapitre a prsent EJB 3.1. partir des versions 2.x, la spcification EJB a volu pour passer dun modle lourd dans lequel il fallait assembler les interfaces home et distante/locale avec une grande quantit de fichiers XML une simple classe Java sans interface et avec une seule annotation. La fonctionnalit sous-jacente est pourtant toujours la mme: fournir une logique mtier transactionnelle et scurise. EJB 3.1 permet de simplifier encore plus le modle de programmation (vue sans interface, dploiement war), lenrichit (conteneur intgr, singletons, service timer, appels asynchrones) et amliore sa portabilit entre les serveurs dapplications (noms JNDI standardiss). La simplification la plus importante est probablement la cration dEJB Lite, un sous-ensemble de lAPI EJB qui offre une version dEJB plus simple mais nanmoins efficace pouvant tre utilise dans le profil web de JavaEE. Le conteneur EJB intgr, de son ct, facilite la mise en place et la portabilit des tests unitaires. EJB3.1 est donc le digne successeur dEJB3.0. Le Chapitre7 sintressera aux beans de session avec et sans tat, aux singletons et au service timer. Le Chapitre8 prsentera les mthodes de rappel et les intercepteurs. Le Chapitre9 sera consacr aux transactions et la scurit.

7
Beans de session et service timer
Les Chapitres2 5 se sont intresss aux objets persistants qui utilisent les entits JPA. Ces entits encapsulent les donnes, lassociation avec le modle relationnel et, parfois, la logique de validation. Nous allons maintenant prsenter le dveloppement dune couche mtier qui gre ces objets persistants avec les beans de session qui prennent en charge les tches complexes ncessitant des interactions avec dautres composants (entits, services web, messages, etc.). Cette sparation logique entre entits et beans de session respecte le paradigme de "sparation des problmes" selon lequel une application est divise en plusieurs composants dont les oprations se recouvrent le moins possible. Dans ce chapitre, nous prsenterons les trois types de beans de session: sans tat, avec tat et singleton. Les premiers sont les plus adaptables des trois car ils ne mmorisent aucune information et effectuent toute la logique mtier dans un seul appel de mthode. Les seconds grent un tat conversationnel avec un seul client. Les beans de session singletons (une seule instance par application) ont t ajouts dans la spcification EJB3.1. La dernire section du chapitre sera consacre lutilisation du service timer pour planifier les tches. Les beans pilots par messages (MDB), qui font galement partie de la spcification EJB, seront prsents au Chapitre13 avec JMS (Java Message Service). Comme nous le verrons aux Chapitres14 et 15, un bean de session sans tat peut tre transform en service web SOAP ou REST. Ou, plus exactement, ces services web peuvent profiter de certaines fonctionnalits dEJB comme les transactions, la scurit, les intercepteurs, etc.

226

Java EE 6 et GlassFish 3

Beans de session
Les beans de session sont parfaits pour implmenter la logique mtier, les processus et le workflow mais, avant de les utiliser, vous devez choisir le type qui convient:

Sans tat. Ne mmorisent aucun tat conversationnel pour lapplication. Ils servent grer les tches qui peuvent seffectuer laide dun seul appel de mthode. Avec tat. Mmorisent ltat et sont associs un client prcis. Ils servent grer les tches qui demandent plusieurs tapes. Singletons. Implmentent le patron de conception Singleton. Le conteneur sassurera quil nen existe quune seule instance pour toute lapplication.

Bien que ces trois types de beans de session aient des fonctionnalits spcifiques, ils en ont aussi beaucoup en commun et, surtout, ils utilisent tous le mme modle de programmation. Comme nous le verrons plus tard, un bean de session peut avoir une interface locale ou distante, ou aucune interface. Les beans de session sont des composants grs par un conteneur et doivent donc tre assembls dans une archive (un fichier jar, war ou ear) et dploys dans le conteneur. Ce dernier est responsable de la gestion de leur cycle de vie (qui sera tudi au chapitre suivant), des transactions, des intercepteurs et de bien dautres choses encore. La Figure7.1 montre un schma trs synthtique des beans de session et du service timer dans un conteneur EJB.
Figure7.1 Beans de session et service timer dans un conteneur EJB.
<<executionEnvironment>> Conteneur EJB
<<component>>

Bean de session Service timer


<<component>>

Beans sans tat

Les beans sans tat sont les beans de session les plus connus dans les applications JavaEE. Ils sont simples, puissants, efficaces et rpondent aux besoins frquents des tches mtiers. "Sans tat" signifie simplement quune tche doit se raliser par un seul appel de mthode.

Chapitre 7

Beans de session et service timer 227

titre dexemple, revenons aux racines de la programmation oriente objet, o un objet encapsule son tat et son comportement. Pour rendre un livre persistant dans une base de donnes en utilisant un seul objet, vous devez raliser les oprations suivantes: crer une instance book de Book, initialiser ses attributs et appeler une mthode afin quil se stocke lui-mme dans la base de donnes (book.persistToDatabase()). Dans le code suivant, vous pouvez constater que lobjet book est appel plusieurs fois entre la premire et la dernire ligne et quil mmorise son tat:
Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction by Douglas Adams."); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); book.persistToDatabase();

Les beans sans tat sont la solution idale lorsque lon doit implmenter une tche qui peut se raliser en un seul appel de mthode. Si lon reprend le code prcdent et que lon y ajoute un composant sans tat, il faut donc crer un objet Book, initialiser ses attributs, puis utiliser un composant sans tat pour invoquer une mthode qui stockera le livre en un seul appel. Ltat est donc gr par Book, non par le composant sans tat.
Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction by Douglas Adams."); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); statelessComponent.persistToDatabase(book);

Les beans de session sans tat sont galement les beans les plus efficaces car ils peuvent tre placs dans un pool pour y tre partags par plusieurs clients le conteneur conserve en mmoire un certain nombre dinstances (un pool) de chaque EJB sans tat et les partage entre les clients. Ces beans ne mmorisant pas ltat des clients, toutes leurs instances sont donc quivalentes. Lorsquun client appelle une mthode dun bean sans tat, le conteneur choisit une instance du pool et laffecte au client; lorsque ce dernier en a fini, linstance retourne dans le pool pour y tre rutilise. Comme le montre la Figure 7.2, il suffit donc dun petit nombre de beans pour grer plusieurs clients (le conteneur ne garantit pas quil fournira toujours la mme instance du bean pour un client donn).

228

Java EE 6 et GlassFish 3

Figure7.2 Clients accdant des beans sans tat placs dans un pool.
<<component>>

<<executionEnvironment>> Conteneur EJB Client 1 Client 2 <<executionEnvironment>> Pool


<<component>>

<<component>>

...

Instance 1 Instance 2

<<component>>

<<component>>

Client n

Le Listing7.1 montre quun EJB sans tat ressemble une simple classe Java avec uniquement une annotation @Stateless. Il peut utiliser nimporte quel service du conteneur dans lequel il se trouve, notamment linjection de dpendances. Lannotation @PersistenceContext sert injecter une rfrence de gestionnaire dentits. Le contexte de persistance des beans de session sans tat tant transactionnel, toutes les mthodes appeles sur cet EJB (createBook(), createCD(), etc.) le seront galement (nous y reviendrons plus en dtail au Chapitre9). Vous remarquerez que toutes les mthodes reoivent les paramtres ncessaires au traitement de la logique mtier en un seul appel: createBook(), par exemple, prend un objet Book en paramtre et le rend persistant sans avoir besoin daucune autre information.
Listing7.1: Bean de session sans tat ItemEJB
@Stateless public class ItemEJB { @PersistenceContext(unitName = "chapter07PU") private EntityManager em; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); } public List<CD> findCDs() { Query query = em.createNamedQuery("findAllCDs"); return query.getResultList(); } public Book createBook(Book book) { em.persist(book); return book; } public CD createCD(CD cd) { em.persist(cd); return cd; }

Chapitre 7

Beans de session et service timer 229

Les beans de session sans tat offrent souvent plusieurs mthodes mtiers troitement lies. Le bean ItemEJB du Listing7.1, par exemple, dfinit des mthodes qui concernent les articles vendus par lapplication CD-BookStore: vous y trouverez donc les oprations de cration, de modification ou de recherche de livres et de CD, ainsi que dautres traitements mtiers apparents. Grce lannotation @javax.ejb.Stateless, le POJO ItemEJB devient un bean de session sans tat elle transforme donc une simple classe Java en composant pour conteneur. Le Listing7.2 contient la spcification de cette annotation.
Listing7.2: API de lannotation @Stateless
@Target({TYPE}) @Retention(RUNTIME) public @interface Stateless { String name() default ""; String mappedName() default ""; String description() default ""; }

Le paramtre name prcise le nom du bean, qui est, par dfaut, celui de la classe (ItemEJB dans lexemple du Listing7.1). Ce paramtre peut tre utilis pour rechercher un EJB particulier avec JNDI, par exemple. description est une chane permettant de dcrire lEJB et mappedName est le nom JNDI global affect par le conteneur ce dernier est spcifique lditeur et nest donc pas portable. mappedName na aucun rapport avec le nom JNDI global et portable que nous avons voqu au chapitre prcdent et que nous dcrirons en dtail dans la section "Accs JNDI global", plus loin dans ce chapitre. Les beans de session sans tat peuvent supporter un grand nombre de clients en minimisant les ressources ncessaires: cest la raison pour laquelle les applications qui les utilisent sont plus adaptables. Les beans de session avec tat, au contraire, ne sont lis qu un et un seul client.
Beans avec tat

Les beans sans tat fournissent des mthodes mtiers aux clients mais nentretiennent pas dtat conversationnel avec eux. Les beans de session avec tat, par contre, prservent cet tat : ils permettent donc dimplmenter les tches qui ncessitent plusieurs tapes, chacune tenant compte de ltat de ltape prcdente. Prenons comme exemple le panier virtuel dun site de commerce en ligne : un client se connecte (sa session dbute), choisit un premier livre et lajoute son panier, puis

230

Java EE 6 et GlassFish 3

choisit un second livre et lajoute galement. Puis le client valide la commande, la paye et se dconnecte (la session se termine). Ici, le panier virtuel conserve ltat les livres choisis pendant tout le temps de la session.
Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction by Douglas Adams."); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); statefullComponent.addBookToShoppingCart(book); book.setTitle("The Robots of Dawn"); book.setPrice(18.25F); book.setDescription("Isaac Asimovs Robot Series"); book.setIsbn("0-553-29949-2"); book.setNbOfPage(276); book.setIllustrations(false); statefullComponent.addBookToShoppingCart(book); statefullComponent.checkOutShoppingCart();

Le code prcdent montre bien comment fonctionne un bean de session avec tat. Il cre deux livres et les ajoute au panier virtuel dun composant avec tat. la fin, la mthode checkOutShoppingCart() se fie a ltat mmoris pour commander les deux livres. Quand un client invoque un bean avec tat sur le serveur, le conteneur EJB doit fournir la mme instance chaque appel de mthode ce bean ne peut pas tre rutilis par un autre client. La Figure7.3 montre la relation 11 qui stablit entre linstance et le client; du point de vue du dveloppeur, aucun code supplmentaire nest ncessaire car cette relation est gre automatiquement par le conteneur.
Figure7.3 Clients accdant des beans avec tat.
<<component>>

<<executionEnvironment>> Conteneur EJB


<<component>>

Client 1 Client 2

Instance 1 Instance 2

<<component>>

<<component>>

Cette relation 11 a videmment un prix: si lon a 1million de clients, ceci signifie que lon aura 1million de beans en mmoire. Pour rduire cette occupation, les beans doivent donc tre supprims temporairement de la mmoire entre deux requtes cette technique est appele passivation et activation. La passivation consiste supprimer une instance de la mmoire et la sauvegarder dans un emplacement

Chapitre 7

Beans de session et service timer 231

persistant (un fichier sur disque, une base de donnes, etc.): elle permet de librer la mmoire et les ressources. Lactivation est le processus inverse: elle restaure ltat et lapplique une instance. Ces deux oprations sont ralises par le conteneur: le dveloppeur na pas sen occuper comme nous le verrons au prochain chapitre, il doit simplement se charger de la libration des ressources (connexion une base de donnes ou une fabrique JMS, etc.) avant que la passivation nait lieu. Le Listing7.3 applique lexemple du panier virtuel un bean avec tat. Un client se connecte au site web, parcourt le catalogue des articles et ajoute deux livres au panier ( laide de la mthode addItem()). Lattribut cartItems stocke le contenu du panier. Supposons que le client dcide alors daller chercher un caf: pendant ce temps, le conteneur peut passiver linstance pour librer la mmoire, ce qui entrane la sauvegarde du contenu du panier dans une zone de stockage permanente. Quelques minutes plus tard, le client revient et veut connatre le montant total de son panier (avec la mthode getTotal()) avant de passer commande. Le conteneur active donc lEJB pour restaurer les donnes dans le panier et le client peut alors commander (mthode checkout()) ses livres. Lorsquil se dconnecte, sa session se termine et le conteneur libre la mmoire en supprimant dfinitivement linstance du bean en mmoire.
Listing7.3: Bean de session avec tat
@Stateful @StatefulTimeout(20000) public class ShoppingCartEJB { private List<Item> cartItems = new ArrayList<Item>(); public void addItem(Item item) { if (!cartItems.contains(item)) cartItems.add(item); } public void removeItem(Item item) { if (cartItems.contains(item)) cartItems.remove(item); } public Float getTotal() { if (cartItems == null || cartItems.isEmpty()) return 0f; Float total = 0f; for (Item cartItem : cartItems) { total += (cartItem.getPrice()); } return total;

232

Java EE 6 et GlassFish 3

@Remove public void checkout() { // Code mtier cartItems.clear(); } @Remove public void empty() { cartItems.clear(); } }

Ce que nous venons de voir pour le panier virtuel reprsente lutilisation classique des beans avec tat, dans laquelle le conteneur gre automatiquement ltat conversationnel. Ici, la seule annotation ncessaire est @javax.ejb.Stateful, qui utilise les mmes paramtres que @Stateless (voir Listing7.2). Nous avons galement utilis les annotations facultatives @javax.ejb.StatefulTimeout et @javax.ejb.Remove. @Remove dcore les mthodes checkout() et empty(): leur appel provoquera dsormais la suppression dfinitive de linstance de la mmoire. @StatefulTimeout met en place un dlai dexpiration en millisecondes si le bean ne reoit aucune demande du client au bout de ce dlai, il sera supprim par le conteneur. Il est galement possible de se passer de ces annotations en se fiant au fait que le conteneur supprime automatiquement une instance lorsquune session client se termine ou expire, mais sassurer que le bean est dtruit au moment adquat permet de rduire loccupation mmoire, ce qui peut se rvler essentiel pour les applications haute concurrence.
Singletons

Un bean singleton est simplement un bean de session qui nest instanci quune seule fois par application. Cest donc une implmentation du fameux patron de conception du Gang of Four, dcrit dans louvrage Design Patterns: Elements of Reusable Object-Oriented Software, dErich Gamma, Richard Helm, Ralph Johnson et John M. Vlissides (Addison-Wesley, 1995). Il garantit quune seule instance dune classe existera dans lapplication et fournit un point daccs global vers cette classe. Les objets singletons sont ncessaires dans toutes les situations o lon na besoin que dun seul exemplaire dun objet pour dcrire une souris, un gestionnaire de fentres, un spooler dimpression, un systme de fichiers, etc. Un autre cas dutilisation des singletons est la cration dun cache unique pour toute lapplication afin dy stocker des objets. Dans un environnement gr par

Chapitre 7

Beans de session et service timer 233

lapplication, vous devez modifier le code de la classe du cache afin de la transformer en singleton (voir Listing7.4). Pour cela, il faut dabord rendre son constructeur priv pour empcher la cration dune nouvelle instance. La mthode publique et statique getInstance() se chargera alors de renvoyer la seule instance possible de la classe. Pour ajouter un objet au cache en utilisant le singleton, une classe cliente devra alors raliser lappel suivant:
CacheSingleton.getInstance().addToCache(myObject);

Le mot-cl synchronized permet dempcher toute interfrence lors de laccs cette mthode par plusieurs threads.
Listing7.4: Classe Java respectant le patron de conception Singleton
public class CacheSingleton { private static CacheSingleton instance = new CacheSingleton(); private Map<Long, Object> cache = new HashMap<Long, Object>(); private CacheSingleton() { } public static synchronized CacheSingleton getInstance() { return instance; } public void addToCache(Long id, Object object) { if (!cache.containsKey(id)) cache.put(id, object); } public void removeFromCache(Long id) { if (cache.containsKey(id)) cache.remove(id); } public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

EJB 3.1 introduit les beans de session singletons qui respectent le patron de conception Singleton: une fois instanci, le conteneur garantit quil ny aura quune seule instance du singleton pour toute la dure de lapplication. Comme le montre la Figure 7.4, une instance est partage par plusieurs clients. Les beans singletons mmorisent leur tat entre les appels des clients.

234

Java EE 6 et GlassFish 3

Figure7.4 Clients accdant un bean singleton.


<<component>>

<<executionEnvironment>> Conteneur EJB Client 1 Client 2

<<component>>

Instance unique

<<component>>

INFO Les singletons ne sont pas compatibles avec les clusters. Un cluster est un groupe de conteneurs fonctionnant de concert (ils partagent les mmes ressources, les mmes EJB, etc.). Lorsquil y a plusieurs conteneurs rpartis en cluster sur des machines diffrentes, chaque conteneur aura donc sa propre instance du singleton.

Il ny a pas grand-chose faire pour transformer le code du Listing7.4 en bean de session singleton (voir Listing7.5). En fait, il suffit dannoter la classe avec @Singleton et de ne pas soccuper du constructeur priv ou de la mthode statique getInstance(): le conteneur sassurera quune seule instance est cre. Lannotation @javax.ejb.Singleton a la mme API que celle de lannotation @Stateless dcrite dans le Listing7.2.
Listing7.5: Bean de session singleton
@Singleton public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); public void addToCache(Long id, Object object) { if (!cache.containsKey(id)) cache.put(id, object); } public void removeFromCache(Long id) { if (cache.containsKey(id)) cache.remove(id); } public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

Chapitre 7

Beans de session et service timer 235

Comme vous pouvez le constater, les beans de session sans tat, avec tat et singletons sont trs simples crire puisquil suffit dune seule annotation. Les singletons, toutefois, ont plus de possibilits: ils peuvent tre initialiss au lancement de lapplication, chans ensemble, et il est possible de personnaliser leurs accs concurrents.
Initialisation

Lorsquune classe client veut appeler une mthode dun bean singleton, le conteneur sassure de crer linstance ou dutiliser celle qui existe dj. Parfois, cependant, linitialisation dun singleton peut tre assez longue: CacheEJB peut, par exemple, devoir accder une base de donnes pour charger un millier dobjets. En ce cas, le premier appel au bean prendra du temps et le premier client devra attendre la fin de son initialisation. Pour viter ce temps de latence, vous pouvez demander au conteneur dinitialiser un bean singleton ds le dmarrage de lapplication en ajoutant lannotation @Startup la dclaration du bean:
@Singleton @Startup public class CacheEJB { // ... }

Chanage de singletons

Dans certains cas, lordre explicite des initialisations peut avoir une importance lorsque lon a plusieurs beans singletons. Supposons que le bean CacheEJB ait besoin de stocker des donnes provenant dun autre bean singleton (un CountryCodeEJB renvoyant tous les codes ISO des pays, par exemple): ce dernier doit donc tre initialis avant le CacheEJB. Lannotation @javax.ejb.DependsOn est justement prvue pour exprimer les dpendances entre les singletons:
@Singleton public class CountryCodeEJB { ... } @DependsOn("CountryCodeEJB") @Singleton public class CacheEJB { ... }

236

Java EE 6 et GlassFish 3

prend en paramtre une ou plusieurs chanes dsignant chacune le nom dun bean singleton dont dpend le singleton annot. Le code suivant, par exemple, montre que CacheDB dpend de linitialisation de CountryCodeEJB et ZipCodeEJB @DependsOn("CountryCodeEJB", "ZipCodeEJB") demande au conteneur de garantir que les singletons CountryCodeEJB et ZipCodeEJB seront initialiss avant CacheEJB.
@DependsOn
@Singleton public class CountryCodeEJB { ... } @Singleton public class ZipCodeEJB { ... } @DependsOn("CountryCodeEJB", "ZipCodeEJB") @Startup @Singleton public class CacheEJB { ... }

Comme vous pouvez le constater dans le code prcdent, il vous est mme possible de combiner ces dpendances avec une initialisation lors du dmarrage de lapplication: CacheEJB tant initialis ds le lancement (car il est annot par @Startup), CountryCodeEJB et ZipCodeEJB le seront galement, mais avant lui.
Concurrence

Un singleton nayant quune seule instance partage par plusieurs clients, les accs concurrents peuvent tre contrls de trois faons diffrentes par lannotation @ConcurrencyManagement:

Concurrence gre par le conteneur (Container-Managed Concurrency ou CMC). Le conteneur contrle les accs concurrents en utilisant les mtadonnes (annotation ou lquivalent en XML). Concurrence gre par le bean (Bean-Managed Concurrency ou BMC). Le conteneur autorise tous les accs concurrents et dlgue la responsabilit de la synchronisation de ces accs au bean lui-mme. Concurrence interdite. Si un client appelle une mthode mtier qui est en cours dutilisation par un autre client, lexception ConcurrentAccessException est leve.

En labsence dindication explicite, la gestion par dfaut est CMC. Un bean singleton peut utiliser CMC ou BMC, mais pas les deux.

Chapitre 7

Beans de session et service timer 237

Concurrence gre par le conteneur

Avec CMC, la valeur par dfaut, le conteneur est responsable du contrle des accs concurrents linstance du bean singleton. Vous pouvez alors vous servir de lannotation @Lock pour prciser le type de verrouillage:
@Lock(LockType.WRITE).

Une mthode annote par un verrou WRITE (exclusif) nautorisera aucun autre appel concurrent tant quelle est en cours dexcution. Si un client C1 appelle une mthode avec un verrou exclusif, le client C2 ne pourra pas lappeler tant que lappel deC1 ne sest pas termin. Une mthode annote par un verrou READ (partag) autorisera un nombre quelconque dappels concurrents. Deux clientsC1 etC2 pourront appeler simultanment une mthode avec un verrou partag.

@Lock(LockType.READ).

Lannotation @Lock peut tre associe la classe, aux mthodes ou aux deux. Dans le premier cas, cela revient lassocier toutes les mthodes. En labsence dindication, le type de verrouillage par dfaut est WRITE. Dans le code du Listing7.6, le bean CacheEJB utilise un verrou READ, ce qui implique que toutes ses mthodes auront un verrou partag, sauf getFromCache(), qui la redfini WRITE.
Listing7.6: Bean de session Singleton avec CMC
@Singleton @Lock(LockType.READ) public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); public void addToCache(Long id, Object object) { if (!cache.containsKey(id)) cache.put(id, object); } public void removeFromCache(Long id) { if (cache.containsKey(id)) cache.remove(id); } @AccessTimeout(2000) @Lock(LockType.WRITE) public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

238

Java EE 6 et GlassFish 3

Vous remarquerez que la mthode getFromCache() utilise galement une annotation @AccessTimeout. Celle-ci permet de limiter le temps pendant lequel un accs concurrent sera bloqu: si le verrou na pas pu tre obtenu dans ce dlai, la requte sera rejete. Ici, si un appel getFromCache() est bloqu pendant plus de 2secondes, lappelant recevra lexception ConcurrentAccessTimeoutException.
Concurrence gre par le bean

Avec BMC, le conteneur autorise tous les accs linstance du bean singleton. Cest donc le dveloppeur qui doit protger ltat contre les erreurs de synchronisation dues aux accs concurrents. Pour ce faire, il peut utiliser les primitives de synchronisation de Java, comme synchronized et volatile. Dans le Listing7.7, le bean CacheEJB utilise BMC (@ConcurrencyManagement(BEAN)) et protge les accs la mthode addToCache() laide du mot-cl synchronized.
Listing7.7: Bean de session singleton avec BMC
@Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); public synchronized void addToCache(Long id, Object object) { if (!cache.containsKey(id)) cache.put(id, object); } public void removeFromCache(Long id) { if (cache.containsKey(id)) cache.remove(id); } public synchronized Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

Concurrence interdite

Les accs concurrents peuvent galement tre interdits sur une mthode ou sur lensemble du bean: en ce cas, un client appelant une mthode en cours dutilisation

Chapitre 7

Beans de session et service timer 239

par un autre client recevra lexception ConcurrentAccessException. Ceci peut avoir des consquences sur les performances puisque les clients devront grer lexception, ressayeront daccder au bean, etc. Dans le Listing7.8, le bean CacheEJB interdit la concurrence sur la mthode addToCache(); les deux autres mthodes utilisent le verrouillage par dfaut dfini au niveau de la classe: CMC avec @LockREAD).
Listing7.8: Bean de session singleton interdisant la concurrence
@Singleton @Lock(LockType.READ) public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); @ConcurrencyManagement(ConcurrencyManagementType.CONCURRENCY_NOT_ALLOWED) public void addToCache(Long id, Object object) { if (!cache.containsKey(id)) cache.put(id, object); } public void removeFromCache(Long id) { if (cache.containsKey(id)) cache.remove(id); } @AccessTimeout(2000) @Lock(LockType.WRITE) public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

Modle des beans de session

Pour linstant, les exemples de beans de session que nous avons prsents utilisaient le modle de programmation le plus simple: un POJO annot sans interface. En fonction de vos besoins, les beans peuvent vous offrir un modle bien plus riche vous permettant de raliser des appels distants, linjection de dpendances ou des appels asynchrones.

240

Java EE 6 et GlassFish 3

Interfaces et classe bean

Les beans de session que nous avons tudis ntaient composs que dune seule classe. En ralit, ils peuvent inclure les lments suivants:

Interfaces mtiers. Ces interfaces contiennent les dclarations des mthodes mtiers visibles par les clients et implmentes par la classe bean. Un bean de session peut avoir des interfaces locales, distantes, ou aucune interface (une vue sans interface avec uniquement un accs local). Une classe bean. Cette classe contient les implmentations des mthodes mtiers et peut implmenter aucune ou plusieurs interfaces mtiers. En fonction du type de bean, elle doit tre annote par @Stateless, @Stateful ou @Singleton.

Comme le montre la Figure7.5, une application cliente peut accder un bean de session par lune de ses interfaces (locale ou distante) ou directement en invoquant la classe elle-mme.
Figure7.5 Les beans de session peuvent avoir diffrents types dinterfaces.
<<component>>

Local Distant

Client

Bean de session

<<component>>

pas d'interface

Vues distantes, locales et sans interface

Selon do un client invoque un bean de session, la classe de ce dernier devra implmenter des interfaces locales ou distantes, voire aucune interface. Si, dans votre architecture, les clients se trouvent lextrieur de linstance JVM du conteneur dEJB, ils devront utiliser une interface distante. Comme le montre la Figure7.6, ceci sapplique galement aux clients qui sexcutent dans une JVM spare (un client riche, par exemple), dans un conteneur client dapplication (ACC) ou dans un conteneur web ou EJB externe. Dans ces situations, les clients devront invoquer les mthodes des beans de session via RMI (Remote Method Invocation). Les appels locaux, en revanche, ne peuvent tre utiliss que lorsque le bean et le client sexcutent dans la mme JVM un EJB invoquant un autre EJB ou un composant web (servlet, JSF) tournant dans un conteneur web de la mme JVM, par exemple. Une application peut galement utiliser des appels distants et locaux sur le mme bean de session.

Chapitre 7

Beans de session et service timer 241

<<executionEnvironment>> Serveur d'applications <<executionEnvironment>> Conteneur web <<component>> Servlet Appel local <<executionEnvironment>> Conteneur EJB <<component>> Bean de session Appel local <<component>> Bean de session * *

<<executionEnvironment>> Conteneur client d'applications <<component>> Application cliente <<component>> Application cliente <<executionEnvironment>> Serveur d'applications <<component>> Composant quelconque * : Appel distant

<<component>> JSF

Figure7.6 Beans de session appels par plusieurs clients.

Un bean de session peut implmenter plusieurs interfaces ou aucune. Une interface mtier est une interface classique de Java qui nhrite daucune interface EJB spcifique. Comme toute interface Java, les interfaces mtiers numrent les mthodes qui seront disponibles pour lapplication cliente. Elles peuvent utiliser les annotations suivantes:
@Remote indique une interface mtier distante. Les paramtres des mthodes sont

passs par valeur et doivent tre srialisables pour tre pris en compte par le protocole RMI.
@Local

indique une interface mtier locale. Les paramtres des mthodes sont passs par rfrence du client au bean.

Une interface donne ne peut pas utiliser plus dune de ces annotations. Les beans de session que nous avons vu jusqu prsent navaient pas dinterface la vue sans interface est une variante de la vue locale qui expose localement toutes les mthodes mtiers publiques de la classe bean sans ncessiter lemploi dune interface mtier. Le Listing 7.9 prsente une interface locale (ItemLocal) et une interface distante (ItemRemote) implmentes par le bean de session sans tat ItemEJB. Dans cet exemple, les clients pourront appeler localement ou distance la mthode findCDs() puisquelle est dfinie dans ces deux interfaces. La mthode createCd(), par contre, ne pourra tre appele que par RMI.

242

Java EE 6 et GlassFish 3

Listing7.9: Bean de session sans tat implmentant une interface distante et locale
@Local public interface ItemLocal { List<Book> findBooks(); List<CD> findCDs(); } @Remote public interface ItemRemote { List<Book> findBooks(); List<CD> findCDs(); Book createBook(Book book); CD createCD(CD cd); } @Stateless public class ItemEJB implements ItemLocal, ItemRemote { ... }

Dans le code du Listing7.9, vous pourriez galement prciser la nature de linterface dans la classe du bean. En ce cas, il faudrait inclure le nom de linterface dans les annotations @Local et @Remote comme le montre le Listing7.10. Cette approche est tout particulirement adapte lorsque lon dispose dinterfaces existantes et que lon souhaite les utiliser avec le bean de session.
Listing7.10: Une classe bean dfinissant une interface locale et une interface distante
public interface ItemLocal { List<Book> findBooks(); List<CD> findCDs(); } public interface ItemRemote { List<Book> findBooks(); List<CD> findCDs(); Book createBook(Book book); CD createCD(CD cd); } @Stateless @Remote (ItemRemote) @Local (ItemLocal) public class ItemEJB implements ItemLocal, ItemRemote { ... }

Interfaces de services web

Outre les appels distants par RMI, les beans sans tat peuvent galement tre appels distance comme services web SOAP ou REST. Ceux-ci faisant lobjet des

Chapitre 7

Beans de session et service timer 243

Chapitres 14 et 15, nous ne les citons ici que parce quils font partie des diffrentes faons dappeler un bean de session sans tat, simplement en implmentant des interfaces annotes diffrentes. Le Listing7.11 prsente un bean sans tat avec uneinterface locale, un service web SOAP (@WebService) et un service web REST (@Path).
Listing7.11: Bean de session sans tat implmentant une interface de services web
@Local public interface ItemLocal { List<Book> findBooks(); List<CD> findCDs(); } @WebService public interface ItemWeb { List<Book> findBooks(); List<CD> findCDs(); Book createBook(Book book); CD createCD(CD cd); } @Path(/items) public interface ItemRest { List<Book> findBooks(); } @Stateless public class ItemEJB implements ItemLocal, ItemWeb, ItemRest { ... }

Classes bean

Un bean de session sans tat est une classe Java classique qui implmente une logique mtier. Pour quelle devienne une classe bean de session, elle doit satisfaire les obligations suivantes:

Elle doit tre annote par @Stateless, @Stateful, @Singleton ou leurs quivalents XML dans un descripteur de dploiement. Elle doit implmenter les mthodes de ses ventuelles interfaces. Elle doit tre publique et ni finale ni abstraite. Elle doit fournir un constructeur public sans paramtre qui servira crer les instances. Elle ne doit pas dfinir de mthode finalize().

244

Java EE 6 et GlassFish 3

Les noms des mthodes mtiers ne doivent pas commencer par ejb et ne peuvent tre ni finals ni statiques. Le paramtre et la valeur de retour dune mthode distante doivent tre dun type reconnu par RMI.

Vue cliente

Maintenant que nous avons vu des exemples de beans de session et de leurs diffrentes interfaces, nous pouvons tudier la faon dont le client les appelle. Le client dun bean de session peut tre nimporte quel type de composant: un POJO, une interface graphique (Swing), une servlet, un bean gr par JSF, un service web (SOAP ou REST) ou un autre EJB (dploy dans le mme conteneur ou dans un autre). Pour appeler une mthode dun bean de session, un client ninstancie pas directement le bean avec loprateur new. Pourtant, il a besoin dune rfrence ce bean (ou lune de ses interfaces): il peut en obtenir une via linjection de dpendances (avec lannotation @EJB) ou par une recherche JNDI. Sauf mention contraire, un client invoque un bean de faon synchrone mais, comme nous le verrons plus loin, EJB3.1 autorise maintenant les appels de mthodes asynchrones.
@EJB

Java EE utilise plusieurs annotations pour injecter des rfrences de ressources (@Resource), de gestionnaires dentits (@PersistenceContext), de services web (@WebServiceRef), etc. Lannotation @javax.ejb.EJB, en revanche, est spcialement conue pour injecter des rfrences de beans de session dans du code client. Linjection de dpendances nest possible que dans des environnements grs, comme les conteneurs EJB, les conteneurs web et les conteneurs clients dapplication. Reprenons nos premiers exemples dans lesquels les beans de session navaient pas dinterface. Pour quun client invoque une vue de bean sans interface, il doit obtenir une rfrence la classe elle-mme. Dans le code suivant, par exemple, le client obtient une rfrence la classe ItemEJB en utilisant lannotation @EJB:
@Stateless public class ItemEJB { ... } // Code client @EJB ItemEJB itemEJB;

Si le bean de session implmente plusieurs interfaces, par contre, le client devra indiquer celle quil veut rfrencer. Dans le code qui suit, le bean ItemEJB implmente

Chapitre 7

Beans de session et service timer 245

deux interfaces et le client peut invoquer cet EJB via son interface locale ou distante, mais il ne peut plus linvoquer directement.
@Stateless @Remote (ItemRemote) @Local (ItemLocal) public class ItemEJB implements ItemLocal, ItemRemote { ... } // Code client @EJB ItemEJB itemEJB; // Impossible @EJB ItemLocal itemEJBLocal; @EJB ItemRemote itemEJBRemote;

Si le bean expose au moins une interface, il doit prciser quil propose galement une vue sans interface en utilisant lannotation @LocalBean. Comme vous pouvez le constater dans le code suivant, le client peut maintenent appeler le bean via son interface locale, distante, ou directement via sa classe.
@Stateless @Remote (ItemRemote) @Local (ItemLocal) @LocalBean public class ItemEJB implements ItemLocal, ItemRemote { ... } // Code client @EJB ItemEJB itemEJB; @EJB ItemLocal itemEJBLocal; @EJB ItemRemote itemEJBRemote;

Si linjection nest pas possible (lorsque le composant nest pas gr par le conteneur), vous pouvez utiliser JNDI pour rechercher les beans de session partir de leur nom JNDI portable.
Accs JNDI global

Les beans de session peuvent galement tre recherchs par JNDI, qui est surtout utilise pour les accs distants lorsquun client non gr par un conteneur ne peut pas utiliser linjection de dpendances. Mais JNDI peut galement tre utilise par des clients locaux, mme si linjection de dpendances produit un code plus clair. Pour rechercher des beans de session, une application cliente doit faire communiquer lAPI JNDI avec un service dannuaire. Un bean de session dploy dans un conteneur est automatiquement li un nom JNDI. Avant Java EE6, ce nom ntait pas standardis, ce qui impliquait quun bean dploy dans des conteneurs diffrents (GlassFish, JBoss, WebLogic, etc.) portait

246

Java EE 6 et GlassFish 3

des noms diffrents. La spcification Java EE6 a corrig ce problme en dfinissant des noms JNDI portables ayant la syntaxe suivante:
java:global[/<nom-app>]/<nom-module>/<nom-bean> [!<nom-interface-pleinement-qualifi>]

Les diffrentes parties dun nom JNDI ont les significations suivantes: est facultative car cette partie ne sapplique que si le bean est assembl dans un fichier ear. En ce cas, <nom-app> est, par dfaut, le nom du fichier ear (sans lextension .ear). <nom-module> est le nom du module dans lequel a t assembl le bean de session. Ce module peut tre un module EJB dans un fichier jar autonome ou un module web dans un fichier war. Par dfaut, <nom-module> est le nom du fichier archive, sans son extension. <nom-bean> est le nom du bean de session. <nom-interface-pleinement-qualifi> est le nom pleinement qualifi de chaque interface mtier qui a t dfinie. Dans le cas des vues sans interface, ce nom est le nom pleinement qualifi de la classe du bean. Pour illustrer cette convention, prenons lexemple du bean ItemEJB. ItemEJB est le <nom-bean> et est assembl dans larchive cdbookstore.jar (le <nom-module>). LEJB a une interface distante et une vue sans interface (signale par lannotation @LocalBean). Lorsquil sera dploy, le conteneur crera donc les noms JNDI suivants:
<nom-app>
package com.apress.javaee6; @Stateless @LocalBean @Remote (ItemRemote) public class ItemEJB implements ItemRemote { ... } // noms JNDI java:global/cdbookstore/ItemEJB!com.apress.javaee6.ItemEJB java:global/cdbookstore/ItemEJB!com.apress.javaee6.ItemRemote

Outre cette convention, si le bean nexpose quune seule interface cliente (ou na quune vue sans interface), le conteneur enregistre une entre JNDI pour cette vue avec la syntaxe suivante:
java:global[/<nom-app>]/<nom-module>/<nom-bean>

Le code qui suit reprsente le bean ItemEJB avec seulement une vue sans interface. Le nom JNDI est alors uniquement compos du nom du module (cdbookstore) et de celui du bean.

Chapitre 7

Beans de session et service timer 247

package com.apress.javaee6; @Stateless public class ItemEJB { ... } // Nom JNDI } java:global/cdbookstore/ItemEJB

Contexte de session

Les beans de session sont des composants mtiers rsidant dans un conteneur. Gnralement, ils naccdent pas au conteneur et nutilisent pas directement ses services (transactions, scurit, injection de dpendances, etc.), qui sont prvus pour tre grs de faon transparente par le conteneur pour le compte du bean. Parfois, cependant, le bean a besoin dutiliser explicitement les services du conteneur (pour annuler explicitement une transaction, par exemple): en ce cas, il doit passer par linterface javax.ejb.SessionContext, qui donne accs au contexte dexcution qui lui a t fourni. SessionContext hrite de linterface javax.ejb.EJBContext ; une partie des mthodes de son API est dcrite dans le Tableau7.1.
Tableau7.1: Une partie des mthodes de linterface SessionContext

Mthode
getCallerPrincipal getRollbackOnly getTimerService

Description Renvoie le java.security.Principal associ lappel. Teste si la transaction courante a t marque pour annulation. Renvoie linterface javax.ejb.TimerService. Cette mthode ne peut tre utilise que par les beans sans tat et singletons. Les beans avec tat ne peuvent pas utiliser les services timer. Renvoie linterface javax.transaction.UserTransaction permettant de dlimiter les transactions. Cette mthode ne peut tre utilise que par les beans de session avec des transactions gres par les beans (BMT). Teste si lappelant a fourni un rle de scurit prcis. Autorise le bean marquer la transaction pour annulation. Cette mthode ne peut tre utilise que par les beans avec BMT. Teste si un client a appel la mthode cancel() sur lobjet Future client correspondant la mthode mtier asynchrone en cours dexcution.

getUserTransaction

isCallerInRole setRollbackOnly wasCancelCalled

248

Java EE 6 et GlassFish 3

Un bean de session peut avoir accs son contexte denvironnement en injectant une rfrence SessionContext laide dune annotation @Resource.
@Stateless public class ItemEJB { @Resource private SessionContext context; ... public Book createBook(Book book) { ... if (cantFindAuthor()) context.setRollbackOnly(); } }

Descripteur de dploiement

Les composants Java EE 6 utilisent une configuration par exception, ce qui signifie que le conteneur, le fournisseur de persistance ou le serveur de message appliqueront un ensemble de services par dfaut ces composants. Si lon souhaite disposer dun comportement particulier, il faut explicitement utiliser une annotation ou son quivalent XML: cest ce que nous avons dj fait avec les entits JPA pour personnaliser les associations. Ce principe sapplique galement aux beans de session: une seule annotation (@Stateless, @Stateful, etc.) suffit pour que le conteneur applique certains services (transaction, cycle de vie, scurit, intercepteurs, concurrence, asynchronisme, etc.) mais, si vous voulez les modifier, dautres annotations (ou leurs quivalents XML) sont votre disposition. Les annotations et les descripteurs de dploiement XML permettent en effet dattacher des informations supplmentaires une classe, une interface, une mthode ou une variable. Un descripteur de dploiement XML est une alternative aux annotations, ce qui signifie que toute annotation a un marqueur XML quivalent. Lorsque les deux mcanismes sont utiliss, la configuration dcrite dans le descripteur de dploiement a priorit sur les annotations. Nous ne rentrerons pas ici dans les dtails de la structure dun descripteur de dploiement XML (stock dans un fichier nomm ejbjar.xml) car il est facultatif et peut tre trs verbeux. Le Listing7.12 montre quoi pourrait ressembler le fichier ejb-jar.xml dItemEJB (voir Listing7.9). Ildfinit la classe bean, linterface locale et distante, son type (Stateless) et indique quil utilise des transactions gres par le conteneur (CMT). Llment <env-entry> dfinit les entres de lenvironnement du bean de session. Nous y reviendrons dans la section "Contexte de nommage de lenvironnement".

Chapitre 7

Beans de session et service timer 249

Listing7.12: Le fichier ejb-jar.xml


<ejb-jar> <enterprise-beans> <session> <ejb-name>ItemEJB</ejb-name> <ejb-class>com.apress.javaee6.ItemEJB</ejb-class> <local>com.apress.javaee6.ItemLocal</local> <remote>com.apress.javaee6.ItemLocal</remote> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <env-entry> <env-entry-name>aBookTitle</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Beginning Java EE 6</env-entry-value> </env-entry> </session> </enterprise-beans> </ejb-jar>

Si le bean de session est dploy dans un fichier jar, le descripteur de dploiement doit tre stock dans le fichier META-INF/ejb-jar.xml. Sil est dploy dans un fichier war, il doit tre stock dans le fichier WEB-INF/web.xml.
Injection de dpendances

Nous avons dj voqu linjection de dpendances, et vous la rencontrerez encore plusieurs fois dans les prochains chapitres. Il sagit dun mcanisme simple mais puissant utilis par JavaEE6 pour injecter des rfrences de ressources dans des attributs : au lieu que lapplication recherche les ressources dans JNDI, celles-ci sont injectes par le conteneur. Les conteneurs peuvent injecter diffrents types de ressources dans les beans de session laide de plusieurs annotations (ou descripteurs de dploiement):
@EJB

injecte dans la variable annote une rfrence de la vue locale, distante ou sans interface de lEJB. et @PersistenceUnit expriment, respectivement, une dpendance sur un EntityManager et sur une EntityManagerFactory. injecte une rfrence un service web.

@PersistenceContext @WebServiceRef @Resource

injecte plusieurs ressources, comme les sources de donnes JDBC, les contextes de session, les transactions utilisateur, les fabriques de connexion JMS et les destinations, les entres denvironnement, le service timer, etc.

250

Java EE 6 et GlassFish 3

Lextrait de code du Listing 7.13 montre un extrait de bean de session sans tat utilisant plusieurs annotations pour injecter diffrentes ressources dans les attributs. Vous remarquerez que ces annotations peuvent porter sur les variables dinstance ainsi que sur les mthodes setter.
Listing7.13: Un bean sans tat utilisant linjection
@Stateless public class ItemEJB { @PersistenceContext(unitName = "chapter07PU") private EntityManager em; @EJB private CustomerEJB customerEJB; @WebServiceRef private ArtistWebService artistWebService; private SessionContext context; @Resource public void setCtx(SessionContext ctx) { this.ctx = ctx; } ...

Contexte de nommage de lenvironnement

Les paramtres des applications dentreprise peuvent varier dun dploiement lautre (en fonction du pays, de la version de lapplication, etc.). Dans lapplication CDBookStore, par exemple, ItemConverterEJB (voir Listing7.14) convertit le prix dun article dans la monnaie du pays dans lequel lapplication a t dploye (en appliquant un taux de change par rapport au dollar). Si ce bean sans tat est dploy en Europe, le prix de larticle doit tre multipli par 0,8 et le nom de la monnaie doit tre leuro.
Listing7.14: Bean de session sans tat convertissant des prix en euros
@Stateless public class ItemConverterEJB { public Item convertPrice(Item item) { item.setPrice(item.getPrice() * 0.80); item.setCurrency("Euros"); return item; } }

Chapitre 7

Beans de session et service timer 251

Comme vous laurez compris, coder en dur ces paramtres implique de modifier le code, de le recompiler et de redployer le composant pour chaque pays ayant une monnaie diffrente. Une autre possibilit consisterait utiliser une base de donnes chaque appel de la mthode convertPrice(), mais cela gaspillerait des ressources. En ralit, on veut simplement stocker ces paramtres un endroit o ils pourront tre modifis lors du dploiement: le descripteur de dploiement est donc un emplacement de choix. Avec EJB3.1, le descripteur de dploiement (ejb-jar.xml) est facultatif, mais son utilisation est justifie lorsque lon a des paramtres lis lenvironnement. Ces entres peuvent en effet tre places dans le fichier et tre accessibles via linjection de dpendances (ou par JNDI). Elles peuvent tre de type String, Character, Byte, Short, Integer, Long, Boolean, Double et Float. Le Listing7.15, par exemple, montre que le fichier ejb-jar.xml dItemConverterEJB dfinit deux entres: currencyEntry, de type String et de valeur Euros, et changeRateEntry, de type Float et de valeur 0.80.
Listing7.15: Entres denvironnement dItemConverterEJB dans ejb-jar.xml
<ejb-jar> <enterprise-beans> <session> <ejb-name>ItemConverterEJB</ejb-name> <ejb-class>com.apress.javaee6.ItemConverterEJB</ejb-class> <env-entry> <env-entry-name>currencyEntry</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Euros</env-entry-value> </env-entry> <env-entry> <env-entry-name>changeRateEntry</env-entry-name> <env-entry-type>java.lang.Float</env-entry-type> <env-entry-value>0.80</env-entry-value> </env-entry> </session> </enterprise-beans> </ejb-jar>

Maintenant que les paramtres de lapplication ont t externaliss dans le descripteur de dploiement, ItemConverterEJB peut utiliser linjection de dpendances pour obtenir leurs valeurs. Dans le Listing7.16, @Resource(name = "currencyEntry") injecte la valeur de lentre currencyEntry dans lattribut currency; si les types de lentre et de lattribut ne sont pas compatibles, le conteneur lve une exception.

252

Java EE 6 et GlassFish 3

Listing7.16: Un ItemConverterEJB utilisant des entres denvironnement


@Stateless public class ItemConverterEJB { @Resource(name = "currencyEntry") private String currency; @Resource(name = "changeRateEntry") private Float changeRate; public Item convertPrice(Item item) { item.setPrice(item.getPrice() * changeRate); item.setCurrency(currency); return item; } }

Appels asynchrones

Par dfaut, les appels des beans de session via des vues distantes, locales et sans interface sont synchrones: un client qui appelle une mthode reste bloqu pendant la dure de cet appel. Un traitement asynchrone est donc souvent ncessaire lorsque lapplication doit excuter une opration qui dure longtemps. Limpression dune commande, par exemple, peut prendre beaucoup de temps si des dizaines de documents sont dj dans la file dattente de limprimante, mais un client qui appelle une mthode dimpression dun document souhaite simplement dclencher un processus qui imprimera ce document, puis continuer son traitement. Avant EJB 3.1, les traitements asynchrones pouvaient tre pris en charge par JMS et les MDB (voir Chapitre13). Il fallait crer des objets administrs (fabriques et destinations JMS), utiliser lAPI JMS de bas niveau afin denvoyer un message un destinataire, puis dvelopper un MDB pour consommer et traiter le message. Cela fonctionnait mais se rvlait assez lourd dans la plupart des cas car il fallait mettre en uvre un systme MOM pour simplement appeler une mthode de faon asynchrone. Depuis EJB 3.1, on peut appeler de faon asynchrone une mthode de bean de session en lannotant simplement avec @javax.ejb.Asynchronous. Le Listing 7.17 montre le bean OrderEJB, qui dispose dune mthode pour envoyer un e-mail un client et dune autre pour imprimer la commande. Ces deux mthodes durant longtemps, elles sont toutes les deux annotes par @Asynchronous.

Chapitre 7

Beans de session et service timer 253

Listing7.17: Le bean OrderEJB dclare deux mthodes asynchrones


@Stateless public class OrderEJB { @Asynchronous private void sendEmailOrderComplete(Order order, Customer customer) { // Envoie un mail } @Asynchronous private void printOrder(Order order) { // Imprime une commande }

Lorsquun client appelle printOrder() ou sendEmailOrderComplete(), le conteneur lui redonne immdiatement le contrle et continue le traitement de cet appel dans un thread spar. Comme vous pouvez le voir dans le Listing7.17, le type du rsultat de ces deux mthodes est void, mais une mthode asynchrone peut galement renvoyer un objet de type java.util.concurrent.Future<V>, o V reprsente la valeur du rsultat. Les objets Future permettent dobtenir le rsultat dune mthode qui sexcute dans un thread distinct: le client peut alors utiliser lAPI de Future pour obtenir ce rsultat ou annuler lappel. Le Listing7.18 montre un exemple de mthode renvoyant un objet Future<Integer>: sendOrderToWorkflow() utilise un workflow pour traiter un objet Order. Supposons quelle appelle plusieurs composants dentreprise (messages, services web, etc.) et que chaque tape renvoie un code dtat (un entier): lorsque le client appelle de faon asynchrone la mthode sendOrderToWorkflow(), il sattend donc recevoir le code dtat du workflow, quil peut rcuprer par un appel la mthode Future. get(). Si, pour une raison ou pour une autre, il souhaite annuler lappel, il peut utiliser Future.cancel(), auquel cas le conteneur tentera dannuler lappel asynchrone sil na pas encore dmarr. Notez que la mthode sendOrderToWorkflow() appelle SessionContext.wasCancelCalled() pour tester si le client a demand ou non lannulation de lappel. Le rsultat est de type javax.ejb.AsyncResult<V>, qui est une implmentation pratique de Future<V>. En fait, AsyncResult est utilise pour passer la valeur du rsultat au conteneur au lieu de la passer directement lappelant.
Listing7.18: Mthode asynchrone renvoyant un Future<Integer>
@Stateless @Asynchronous public class OrderEJB {

254

Java EE 6 et GlassFish 3

@Resource SessionContext ctx; private void sendEmailOrderComplete(Order order, Customer customer) { // Envoie un mail } private void printOrder(Order order) { // Imprime une commande } private Future<Integer> sendOrderToWorkflow(Order order) { Integer status = 0; // Traitement status = 1; if (ctx.wasCancelCalled()) { return new AsyncResult<Integer>(2); // Traitement return new AsyncResult<Integer>(status);

Dans le Listing7.18, vous remarquerez que lannotation @Asynchronous est applique au niveau de la classe, ce qui implique que toutes les mthodes dfinies sont asynchrones. Pour rcuprer la valeur du rsultat dun appel sendOrderToWorkflow(), le client devra appeler Future.get():
Future<Integer> status = orderEJB.sendOrderToWorkflow (order); Integer statusValue = status.get();

Conteneurs intgrs

Lavantage des beans de session est quils sont grs par un conteneur: ce dernier soccupe de tous les services (transaction, cycle de vie, asynchronisme, intercepteurs, etc.), ce qui permet au dveloppeur de se concentrer sur le code mtier. Linconvnient est quil faut toujours excuter les EJB dans un conteneur, mme pour les tester. Pour rsoudre ce problme, on finit gnralement par bricoler le code mtier afin de pouvoir le tester: on ajoute des interfaces distantes alors que lEJB na besoin que dun accs local, on cre une faade TestEJB distante qui dlgue les appels aux vritables EJB ou lon utilise des fonctionnalits spcifiques un diteur dune faon ou dune autre, on doit excuter un conteneur avec les EJB dploys. Ce problme a t rsolu par EJB 3.1 avec la cration dun conteneur EJB intgrable. EJB3.1 ajoute en effet une API standardise pour excuter les EJB dans un environnement JavaSE. Celle-ci (lAPI javax.ejb.embeddable) permet un client

Chapitre 7

Beans de session et service timer 255

dinstancier un conteneur EJB qui sexcutera dans sa propre JVM. Ce conteneur fournit un environnement gr disposant des mmes services de base que ceux dun conteneur JavaEE, mais il ne fonctionne quavec lAPI EJB Lite (pas de MDB, pas de beans entits2.x, etc.). Le Listing7.19 montre une classe JUnit qui utilise lAPI de "bootstrap" pour lancer le conteneur (la classe abstraite javax.ejb.embeddable.EJBContainer), rechercher un EJB et appeler ses mthodes.
Listing7.19: Classe de test utilisant un conteneur intgr
public class ItemEJBTest { private static EJBContainer ec; private static Context ctx; @BeforeClass public static void initContainer() throws Exception { ec = EJBContainer.createEJBContainer(); ctx = ec.getContext(); } @AfterClass public static void closeContainer() throws Exception { ec.close(); } @Test public void createBook() throws Exception { // Cration dun livre Book book = new Book(); book.setTitle("The Hitchhikers Guide to the Galaxy"); book.setPrice(12.5F); book.setDescription("Science fiction comedy book"); book.setIsbn("1-84023-742-2"); book.setNbOfPage(354); book.setIllustrations(false); // Recherche de lEJB ItemEJB bookEJB = (ItemEJB) ctx.lookup("java:global/chapter07/ItemEJB"); // Stockage du livre dans la base de donnes book = itemEJB.createBook(book); assertNotNull("ID should not be null", book.getId()); // Rcupre tous les livres de la base List<Book> books = itemEJB.findBooks(); assertNotNull(books); } }

256

Java EE 6 et GlassFish 3

Comme vous pouvez le constater dans la mthode initContainer() du Listing7.19, EJBContainer dispose dune mthode fabrique (createEJBContainer()) permettant de crer une instance de conteneur. Par dfaut, le conteneur intgr recherche les EJB initialiser dans le classpath du client. Lorsque le conteneur a t initialis, la mthode rcupre son contexte (EJBContainer.getContext(), qui renvoie un javax.naming.Context) pour rechercher le bean ItemEJB (en utilisant la syntaxe des noms portables JNDI). Notez quItemEJB (voir Listing7.1) est un bean de session sans tat qui expose ses mthodes via une vue sans interface. Il utilise linjection, les transactions gres par le conteneur et une entit JPA Book. Le conteneur intgr soccupe dinjecter un gestionnaire dentits et de valider ou dannuler les transactions. La mthode closeContainer() appelle simplement EJBContainer.close() pour fermer linstance du conteneur intgr. Nous nous sommes servis dune classe de test dans cet exemple pour vous montrer comment utiliser un conteneur EJB intgr, mais les EJB peuvent dsormais tre employs dans nimporte quel environnement Java SE : des classes de tests aux applications Swing en passant par une simple classe Java avec une mthode public static void main().

Le service timer
Certaines applications Java EE ont besoin de planifier des tches pour tre averties des instants donns. Lapplication CD-BookStore, par exemple, veut envoyer tous les ans un e-mail danniversaire ses clients, afficher les statistiques mensuelles des ventes, produire des rapports toutes les nuits sur ltat du stock et rafrachir un cache toutes les 30secondes. Pour ce faire, EJB 2.1 a introduit un service timer car les clients ne pouvaient pas utiliser directement lAPI Thread, mais il tait moins riche que dautres outils ou certains frameworks (lutilitaire cron dUnix, Quartz, etc.). Il a fallu attendre EJB3.1 pour voir apparatre une amlioration considrable de ce service, qui sest inspir de cron et dautres outils reconnus. Dsormais, il peut rpondre la plupart des besoins de planification. Le service timer EJB est un service conteneur qui permet aux EJB de senregistrer pour tre rappels. Les notifications peuvent tre planifies pour intervenir une date ou une heure donnes, aprs un certain dlai ou intervalles rguliers.

Chapitre 7

Beans de session et service timer 257

Leconteneur mmorise tous les timers et appelle la mthode dinstance approprie lorsquun timer a expir. La Figure7.7 montre les deux tapes de lutilisation de ce service. LEJB doit dabord crer un timer (automatiquement ou explicitement) et senregistrer pour tre rappel, puis le service timer dclenche la mthode dinstance enregistre de lEJB.
<<executionEnvironment>> Conteneur EJB <<component>> Service timer Enregistrement automatique ou explicite Prvient le bean l'expiration du timer <<component>> Bean de session

Figure7.7 Interaction entre le service timer et un bean de session.

Les timers sont destins aux processus mtiers longs et ils sont donc persistants par dfaut, ce qui signifie quils survivent aux arrts du serveur: lorsquil redmarre, les timers sexcutent comme sil ne stait rien pass. Selon vos besoins, vous pouvez galement demander des timers non persistants.
INFO Le service de timer peut enregistrer des beans sans tat et singletons ainsi que des MDB, mais pas de beans avec tat. Ces derniers ne doivent donc pas utiliser lAPI de planification.

Les timers peuvent tre crs automatiquement par le conteneur au moment du dploiement si le bean comprend des mthodes annotes par @Schedule. Ils peuvent galement tre crs explicitement par programme et doivent fournir une mthode de rappel annote par @Timeout.
Expressions calendaires

Le service timer utilise une syntaxe calendaire inspire de celle du programme cron dUnix. Cette syntaxe est utilise pour la cration des timers par programme (avec la classe ScheduleExpression) ou pour les crations automatiques (via lannotation @Schedule ou le descripteur de dploiement). Le Tableau7.2 numre les attributs de cration des expressions calendaires.

258

Java EE 6 et GlassFish 3

Tableau7.2: Attributs dune expression calendaire

Attribut
second

Description Une ou plusieurs secondes dans une minute Une ou plusieurs minutes dans une heure Une ou plusieurs heures dans une journe Un ou plusieurs jours dans un mois

Valeurs possibles
[0,59]

Valeur par dfaut


0

minute

[0,59]

hour

[0,23]

dayOfMonth

[1,31] ou {"1st", "2nd", "3rd", . . . ,"30th", "31st"} ou {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} ou "Last" (le dernier jour du mois) ou -x (x

jour(s) avant le dernier jour du mois)


month

Un ou plusieurs mois dans une anne

[1,12] ou {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} [0,7] ou {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} "0" et "7"

dayOfWeek

Un ou plusieurs jours dans une semaine

signifient Dimanche
year timezone

Une anne particulire Une zone horaire particulire

Une anne sur quatre chiffres Liste des zones horaires fournies par la base de donnes zoneinfo (ou tz)

Chaque attribut dune expression calendaire (second, minute, hour, etc.) permet dexprimer les valeurs sous diffrentes formes. Vous pouvez, par exemple, avoir une liste de jours ou un intervalle entre deux annes. Le Tableau7.3 prsente les diffrentes formes que peut prendre un attribut.

Chapitre 7

Beans de session et service timer 259

Tableau7.3: Forme dune expression calendaire

Forme Valeur simple Joker Liste

Description Une seule valeur possible Toutes les valeurs possibles dun attribut Deux valeurs ou plus spares par une virgule Intervalle de valeurs spares par un tiret Un point de dpart et un intervalle spars par une barre de fraction

Exemple
year = "2009" month= "May" second = "*" dayOfWeek = "*" year = "2008,2012,2016" dayOfWeek = "Sat,Sun" minute = "0-10,30,40" second="1-10" dayOfWeek = "Mon-Fri" minute = "*/15" second = "30/10"

Intervalle Incrments

Cette syntaxe devrait sembler familire ceux qui connaissent cron, mais elle est bien plus simple. Comme le montre le Tableau7.4, elle permet dexprimer quasiment nimporte quel type dexpression calendaire.
Tableau7.4: Exemples dexpressions calendaires

Exemple Tous les mercredis minuit Tous les mercredis minuit

Expression
dayOfWeek="Wed" second="0", minute="0", hour="0", dayOfMonth="*", month="*", dayOfWeek="Wed", year="*" minute="55", hour="6", dayOfWeek="Mon- Fri" minute="55", hour="6", dayOfWeek="Mon- Fri", timezone="Europe/Paris" minute="*", hour="*" second="*", minute="*", hour="*" second="30", hour="12", dayOfWeek="Mon, Fri" minute="*/5", hour="*"

Tous les jours de la semaine 6:55 Tous les jours de la semaine 6:55 heure de Paris Toutes les minutes Toutes les secondes Tous les lundis et vendredis, 30 secondes aprs midi Toutes les cinq minutes

260

Java EE 6 et GlassFish 3

Tableau7.4: Exemples dexpressions calendaires (suite)

Exemple Toutes les cinq minutes Le dernier lundi de dcembre 15h00 Trois jours avant le dernier jour de chaque mois 13h00 Toutes les deux heures partir de midi le second mardi de chaque mois Toutes les 14 minutes de 1h00 et 2h00 Toutes les 14 minutes de 1h00 et 2h00 Toutes les 10 secondes partir de la 30e seconde Toutes les 10 secondes partir de la 30e seconde

Expression
minute="0,5,10,15,20,25,30,35,40,45, 50,55", hour="*" hour="15", dayOfMonth="Last Mon", month="Dec" hour="13", dayOfMonth="-3" hour="12/2", dayOfMonth="2nd Tue" minute = "*/14", hour="1,2" minute = "0,14,28,42,56", hour = "1,2" second = "30/10" second = "30,40,50"

Cration automatique dun timer

Le conteneur peut crer automatiquement les timers au moment du dploiement en utilisant les mtadonnes. Il cre un timer pour chaque mthode annote par @javax. ejb.Schedule ou @Schedules (ou leur quivalent XML dans le descripteur de dploiement ejb-jar.xml). Par dfaut, chaque annotation @Schedule correspond un seul timer persistant, mais il est galement possible de dfinir des timers non persistants. Le Listing 7.20 montre un bean StatisticsEJB qui dfinit plusieurs mthodes : statisticsItemsSold() cre un timer qui appellera la mthode le premier jour de chaque mois 05h30; generateReport() cre deux timers (avec @Schedules): lun pour chaque jour 02h00, lautre pour chaque mercredi 14h00; refreshCache() cre un timer non persistant qui rafrachit le cache toutes les 10minutes.
Listing7.20: Le bean StatisticsEJB enregistre quatre timers
@Stateless public class StatisticsEJB { @Schedule(dayOfMonth = "1", hour = "5", minute = "30")

Chapitre 7

Beans de session et service timer 261

public void statisticsItemsSold() { // ... } @Schedules({ @Schedule(hour = "2"), @Schedule(hour = "14", dayOfWeek = "Wed") }) public void generateReport() { // ... } @Schedule(minute = "*/10", hour = "*", persistent = false) public void refreshCache() { // ... } }

Cration dun timer par programme

Pour crer un timer par programme, lEJB doit accder linterface javax. ejb.TimerService en utilisant soit linjection de dpendances, soit lEJBContext (EJBContext.getTimerService()), soit une recherche JNDI. LAPI TimerService dfinit plusieurs mthodes permettant de crer quatre sortes de timers:
createTimer

cre un timer reposant sur des dates, des intervalles ou des dures. Ces mthodes nutilisent pas les expressions calendaires.

createSingleActionTimer

cre un timer simple-action qui expire un instant donn ou aprs une certaine dure. Le conteneur supprime le timer aprs lappel la mthode de rappel. cre un timer intervalle dont la premire expiration intervient un instant donn et les suivantes, aprs les intervalles indiqus. de la classe ScheduleExpression.

createIntervalTimer

createCalendarTimer cre un timer utilisant les expressions calendaires laide

La classe ScheduleExpression permet de crer des expressions calendaires par programme. Ses mthodes sont lies aux attributs du Tableau 7.2 et permettent deprogrammer tous les exemples du Tableau7.4. Voici quelques exemples:
new ScheduleExpression().dayOfMonth("Mon").month("Jan"); new ScheduleExpression().second("10,30,50").minute("*/5"). hour("10-14"); new ScheduleExpression().dayOfWeek("1,5"). timezone("Europe/Lisbon");

262

Java EE 6 et GlassFish 3

Toutes les mthodes de TimerService (createSingleActionTimer, createCalendarTimer, etc.) renvoient un objet Timer contenant des informations sur le timer cr (date de cration, persistant ou non, etc.). Timer permet galement lEJB dannuler le timer avant son expiration. Lorsque le timer expire, le conteneur appelle la mthode annote par @Timeout correspondante du bean en lui passant lobjet Timer. Un bean ne peut pas possder plus dune mthode @Timeout. Lorsque CustomerEJB (voir Listing7.21) ajoute un nouveau client au systme (avec la mthode createCustomer()), il cre galement un timer calendaire reposant sur la date de naissance de ce client: chaque anne, le conteneur pourra ainsi dclencher un bean pour crer et envoyer un courrier lectronique afin de souhaiter lanniversaire du client. Pour ce faire, le bean sans tat doit dabord injecter une rfrence au service timer (avec @Resource). La mthode createCustomer() stocke le client dans la base de donnes et utilise le jour et le mois de sa naissance pour crer un objet ScheduleExpression qui sert ensuite crer un timer calendaire avec TimerConfig lappel new TimerConfig(customer, true) configure un timer persistant (indiqu par son paramtre true) qui passe lobjet customer reprsentant le client.
Listing7.21: Le bean CustomerEJB cre explicitement un timer
@Stateless public class CustomerEJB { @Resource TimerService timerService; @PersistenceContext(unitName = "chapter07PU") private EntityManager em; public void createCustomer(Customer customer) { em.persist(customer); ScheduleExpression birthDay = new ScheduleExpression(). dayOfMonth(customer.getBirthDay()). month(customer.getBirthMonth()); timerService.createCalendarTimer(birthDay, new TimerConfig(customer, true)); } @Timeout public void sendBirthdayEmail(Timer timer) { Customer customer = (Customer) timer.getInfo(); // ... } }

Chapitre 7

Beans de session et service timer 263

Une fois le timer cr, le conteneur invoquera tous les ans la mthode @Timeout (sendBirthdayEmail()) en lui passant lobjet Timer. Le timer ayant t srialis avec lobjet customer, la mthode peut y accder en appelant getinfo().

Rsum
Ce chapitre a t consacr aux beans de session et au service timer (les MDB seront prsents au Chapitre 13, les services web SOAP, au Chapitre 14 et les services web REST, au Chapitre15). Les beans de session sont des composants grs par un conteneur qui permettent de dvelopper des couches mtiers. Il existe trois types de beans de session: sans tat, avec tat et singletons. Les beans sans tat sadaptent facilement car ils ne mmorisent aucune information, sont placs dans un pool et traitent les tches qui peuvent tre ralises par un seul appel de mthode. Les beans avec tat sont en relation 11 avec un client et peuvent tre temporairement ts de la mmoire grce aux mcanismes de passivation et dactivation. Les singletons nont quune seule instance partage par plusieurs clients et peuvent tre initialiss au lancement de lapplication, chans ensemble; en outre, leurs accs concurrents peuvent sadapter en fonction des besoins. Malgr ces diffrences, tous les beans de session partagent le mme modle de programmation. Ils peuvent avoir une vue locale, distante ou sans interface, utiliser des annotations ou tre dploys avec un descripteur de dploiement. Les beans de session peuvent utiliser linjection de dpendances pour obtenir des rfrences plusieurs ressources (sources de donnes JDBC, contexte persistant, entres denvironnement, etc.); ils peuvent galement se servir de leur contexte denvironnement (lobjet SessionContext). Depuis EJB3.1, vous pouvez appeler des mthodes de faon asynchrone, rechercher les EJB laide de noms JNDI portables ou utiliser un conteneur EJB intgr dans lenvironnement JavaSE. EJB3.1 a galement amlior le service timer, qui peut dsormais rivaliser avec les autres outils de planification. Le chapitre suivant prsente le cycle de vie des beans de session et explique comment interagir avec les annotations de rappel. Les intercepteurs, qui permettent de mettre en uvre la programmation oriente aspect (POA) avec les beans de session, y sont galement dtaills.

8
Mthodes de rappel etintercepteurs
Le chapitre prcdent a montr que les beans sont des composants grs par un conteneur. Ils rsident dans un conteneur EJB qui encapsule le code mtier avec plusieurs services (injection de dpendances, gestion des transactions, de la scurit, etc.). La gestion du cycle de vie et linterception font galement partie de ces services. Le cycle de vie signifie quun bean de session passe par un ensemble dtats bien prcis, qui dpendent du type de bean (sans tat, avec tat ou singleton). chaque phase de ce cycle. le conteneur peut invoquer les mthodes qui ont t annotes comme mthodes de rappel. Vous pouvez utiliser ces annotations pour initialiser les ressources de vos beans de session ou pour les librer avant leur destruction. Les intercepteurs permettent dajouter des traitements transverses vos beans. Lorsquun client appelle une mthode dun bean de session, le conteneur peut intercepter lappel et traiter la logique mtier avant que la mthode du bean ne soit invoque. Ce chapitre prsente les diffrents cycles de vie des beans de session, ainsi que les annotations de rappel que vous pouvez utiliser pour traiter la logique mtier au cours des diffrentes phases. Nous verrons galement comment intercepter les appels de mthodes et les encapsuler par notre propre code.

266

Java EE 6 et GlassFish 3

Cycles de vie des beans de session


Comme nous lavons vu au chapitre prcdent, un client ne cre pas une instance dun bean de session laide de loprateur new: il obtient une rfrence ce bean via linjection de dpendances ou par une recherche JNDI. Cest le conteneur qui cre linstance et qui la dtruit, ce qui signifie que ni le client ni le bean ne sont responsables du moment o linstance est cre, o les dpendances sont injectes et o linstance est supprime. La responsabilit de la gestion du cycle de vie du bean incombe au conteneur. Tous les beans de session passent par deux phases videntes de leur cycle de vie: leur cration et leur destruction. En outre, les beans avec tat passent par les phases de passivation et dactivation que nous avons dcrites au chapitre prcdent.
Beans sans tat et singletons

Les beans sans tat et singletons partagent la caractristique de ne pas mmoriser ltat conversationnel avec leur client et dautoriser leur accs par nimporte quel client les beans sans tat le font en srie, instance par instance, alors que les singletons fournissent un accs concurrent une seule instance. Tous les deux partagent le cycle de vie suivant, reprsent par la Figure8.1: 1. Le cycle de vie commence lorsquun client demande une rfrence au bean (par injection de dpendances ou par une recherche JNDI). Le conteneur cre alors une nouvelle instance de bean de session. 2. Si cette nouvelle instance utilise linjection de dpendances via des annotations (@Resource, @EJB, @PersistenceContext, etc.) ou des descripteurs de dploiement, le conteneur injecte toutes les ressources ncessaires. 3. Si linstance contient une mthode annote par lappelle.
@PostConstruct,

le conteneur

4. Linstance traite lappel du client et reste prte pour traiter les appels suivants. Les beans sans tat restent prts jusqu ce que le conteneur libre de la place dans le pool, les singletons restent prts jusqu la terminaison du conteneur. 5. Le conteneur na plus besoin de linstance. Si celle-ci contient une mthode annote par @PreDestroy, il lappelle et met fin linstance.

Chapitre 8

Mthodes de rappel etintercepteurs 267

Figure8.1 Cycle de vie des beans sans tat et singletons.

N'existe pas 1. Nouvelle instance 2. Injection de dpendances 3. @PostConstruct Prt

5. @PreDestroy

4. Appel de mthode

Bien quils partagent le mme cycle de vie, les beans sans tat et singletons ne sont pas crs et dtruits de la mme faon. Lorsquun bean de session sans tat est dploy, le conteneur en cre plusieurs instances et les place dans un pool. Quand un client appelle une mthode de ce bean, le conteneur choisit une instance dans le pool, lui dlgue lappel de mthode et la replace dans le pool. Lorsque le conteneur na plus besoin de linstance (parce que, par exemple, il veut rduire le nombre dinstances du pool), il la supprime.
INFO GlassFish permet de paramtrer le pool des EJB. Vous pouvez ainsi fixer une taille (nombre initial, minimal et maximal de beans dans le pool), le nombre de beans supprimer du pool lorsque son temps dinactivit a expir et le nombre de millisecondes du dlai dexpiration du pool.

La cration des beans singletons varie selon quils ont t instancis ds le dmarrage (@Startup) ou non, ou quils dpendent (@DependsOn) dun autre singleton dj cr : dans ce cas, une instance sera cre au moment du dploiement ; sinon le conteneur crera linstance lorsquun client appellera une mthode mtier. Comme les singletons durent tout le temps de lapplication, leur instance nest dtruite que lorsque le conteneur se termine.
Beans avec tat

Du point de vue du programme, les beans de session avec tat ne sont pas trs diffrents des beans sans tat ou singletons: seules leurs mtadonnes changent (@Stateful au lieu de @Stateless ou @Singleton). La vritable diffrence rside dans le fait que les beans avec tat mmorisent ltat conversationnel avec leurs

268

Java EE 6 et GlassFish 3

clients et quils ont donc un cycle de vie lgrement diffrent. Le conteneur produit une instance et ne laffecte qu un seul client. Ensuite, chaque requte de ce client sera transmise la mme instance. Selon ce principe et en fonction de lapplication, il peut finalement stablir une relation 11 entre un client et un bean avec tat (un millier de clients simultans peuvent produire un millier de beans avec tat). Si un client ninvoque pas son instance de bean au cours dune priode suffisamment longue, le conteneur doit le supprimer avant que la JVM ne soit court de mmoire, prserver ltat de cette instance dans une zone de stockage permanente, puis la rappeler lorsque son tat redevient ncessaire. Pour ce faire, le conteneur utilise le mcanisme de passivation et activation. Comme on la expliqu au Chapitre7, la passivation consiste srialiser linstance du bean sur un support de stockage permanent (fichier sur disque, base de donnes, etc.) au lieu de la maintenir en mmoire. Lactivation, qui est lopration oppose, a lieu lorsque linstance est redemande par le client. Le conteneur dsrialise alors le bean et le replace en mmoire. Ceci signifie donc que les attributs du bean doivent tre srialisables (donc tre dun type Java primitif ou qui implmente linterface java.io.Serializable). Le cycle de vie dun bean avec tat passe donc par les tapes suivantes, qui sont reprsentes par la Figure8.2: 1. Le cycle de vie dmarre lorsquun client demande une rfrence au bean (soit par injection de dpendances, soit par une recherche JNDI): le conteneur cre alors une nouvelle instance du bean de session et la stocke en mmoire. 2. Si la nouvelle instance utilise linjection de dpendances via des annotations (@Resource, @EJB, @PersistenceContext, etc.) ou des descripteurs de dploiement, le conteneur injecte les ressources ncessaires. 3. Si linstance contient une mthode annote par lappelle.
@PostConstruct,

le conteneur

4. Le bean excute lappel demand et reste en mmoire en attente dautres requtes du client. 5. Si le client reste inactif pendant un certain temps, le conteneur appelle la mthode annote par @PrePassivate, sil y en a une, et stocke le bean sur un support de stockage permanent. 6. Si le client appelle un bean qui a t passiv, le conteneur le replace en mmoire et appelle la mthode annote par @PostActivate, sil y en a une. 7. Si le client ninvoque pas une instance passive avant la fin du dlai dexpiration de la session, le conteneur supprime cette instance.

Chapitre 8

Mthodes de rappel etintercepteurs 269

8. Alternative ltape 7: si le client appelle une mthode annote par @Remove, le conteneur invoque alors la mthode annote par @PreDestroy, sil y en a une, et met fin au cycle de vie de linstance.
Figure8.2 Cycle de vie dun bean avec tat.
N'existe pas 1. Nouvelle instance 2. Injection de dpendances 3. @PostConstruct Prt 5. @PrePassivate 7. Expiration du dlai

8. @Remove et @Predestroy 6. PostActivate

Passiv

4. Appel de mthode

Dans certains cas, un bean avec tat contient des ressources ouvertes comme des sockets ou des connexions de bases de donnes. Un conteneur ne pouvant garder ces ressources ouvertes pour chaque bean, vous devez fermer et rouvrir ces ressources avant et aprs la passivation: cest l que les mthodes de rappel interviennent.
Mthodes de rappel

Comme nous venons de le voir, le cycle de vie de chaque bean de session est gr par son conteneur. Ce dernier permet de greffer du code mtier aux diffrentes phases de ce cycle: les passages dun tat lautre sont alors intercepts par le conteneur, qui appellera les mthodes annotes par lune des annotations du Tableau8.1.
Tableau8.1: Annotations de rappel du cycle de vie

Annotation
@PostConstruct

Description Indique la mthode appeler immdiatement aprs la cration de linstance et linjection de dpendances par le conteneur. Cette mthode sert le plus souvent raliser les initialisations. Indique la mthode appeler immdiatement avant la suppression de linstance par le conteneur. Cette mthode sert le plus souvent librer les ressources utilises par le bean. Dans le cas des beans avec tat, cette mthode est appele aprs la fin de lexcution de la mthode annote par @Remove.

@PreDestroy

270

Java EE 6 et GlassFish 3

Annotation
@PrePassivate

Description Indique la mthode appeler avant que le conteneur passive linstance. Elle donne gnralement loccasion au bean de se prparer la srialisation et de librer les ressources qui ne peuvent pas tre srialises (connexions une base de donnes, serveur de message, socket rseau, etc.). Indique la mthode appeler immdiatement aprs la ractivation de linstance par le conteneur. Elle lui permet de rinitialiser les ressources quil a fermes au cours de la passivation.

@PostActivate

INFO Les annotations @PrePassivate et @PostActivate sont dfinies dans le paquetage javax. ejb et font partie de la spcification EJB3.1 (JSR318). @PostConstruct et @PreDestroy font partie de la spcification Common Annotations1.0 (JSR250) et proviennent du paquetage
javax.annotation (comme @Resource et les annotations concernant la scurit, que nous

prsenterons au chapitre suivant).

Une mthode de rappel doit avoir la signature suivante:


void <nom-mthode>();

et respecter les rgles suivantes:


Elle ne doit pas prendre de paramtres et doit renvoyer void. Elle ne doit pas lancer dexception contrle, mais elle peut dclencher une exception runtime : dans ce cas, si une transaction est en cours, celle-ci sera annule (voir chapitre suivant). Elle peut avoir un accs public, private, mais ne peut tre ni static ni final.
protected

ou de niveau paquetage,

Elle peut tre annote par plusieurs annotations (la mthode init() du Listing 8.2, par exemple, est annote par @PostConstruct et @PostActivate). Cependant, il ne peut y avoir quune seule annotation du mme type dans le bean (il ne peut pas exister deux annotations @PostConstruct dans le mme bean de session, par exemple). Elle peut accder aux entres denvironnement du bean (voir la section "Contexte de nommage de lenvironnement" du Chapitre7).

Chapitre 8

Mthodes de rappel etintercepteurs 271

Ces mthodes de rappel servent gnralement allouer et/ou librer les ressources du bean. Le Listing8.1, par exemple, montre que le bean singleton CacheEJB utilise une annotation @PostConstruct pour initialiser son cache: immdiatement aprs la cration de lunique instance de ce bean, le conteneur invoquera donc la mthode initCache().
Listing8.1: Singleton initialisant son cache avec lannotation @PostConstruct
@Singleton public class CacheEJB { private Map<Long, Object> cache = new HashMap<Long, Object>(); @PostConstruct private void initCache() { // Initialise le cache } public Object getFromCache(Long id) { if (cache.containsKey(id)) return cache.get(id); else return null; } }

Le Listing 8.2 prsente un extrait de code pour un bean avec tat. Le conteneur gre ltat conversationnel, qui peut contenir des ressources importantes comme une connexion une base de donnes. Louverture dune telle connexion tant coteuse, elle devrait tre partage par tous les appels, mais libre lorsque le bean est inactif (ou passiv). Aprs la cration de linstance du bean, le conteneur injecte la rfrence dune source de donnes dans lattribut ds. Il pourra ensuite appeler la mthode annote par @PostConstruct (init()), qui cre une connexion vers une base de donnes. Si le conteneur passive linstance, la mthode close() (annote par @PrePassivate) sera dabord invoque afin de fermer la connexion JDBC, qui ne sert plus pendant la passivation. Lorsque le client appelle une mthode mtier du bean, le conteneur lactive et appelle nouveau la mthode init() (car elle est galement annote par @PostActivate). Si le client invoque la mthode checkout() (annote par @Remove), le conteneur supprime linstance aprs avoir appel nouveau la mthode close() (car elle est aussi annote par @PreDestroy).

272

Java EE 6 et GlassFish 3

Listing8.2: Bean avec tat, initialisant et librant des ressources


@Stateful public class ShoppingCartEJB { @Resource private DataSource ds; private Connection connection; private List<Item> cartItems = new ArrayList<Item>(); @PostConstruct @PostActivate private void init() { connection = ds.getConnection(); } @PreDestroy @PrePassivate private void close() { connection.close(); } // ... @Remove public void checkout() { cartItems.clear(); } }

Pour plus de lisibilit, la gestion des exceptions SQL a t omise dans les mthodes de rappel.

Intercepteurs
Avant de prsenter les intercepteurs, passons un peu de temps voquer la programmation oriente aspect (POA). La POA est un paradigme de programmation qui spare les traitements transverses (ceux qui apparaissent partout dans lapplication) du code mtier. La plupart des applications contiennent du code qui se rpte dans tous les composants. Il peut sagir de traitements techniques (enregistrer lentre et la sortie de chaque mthode, la dure dun appel de mthode, les statistiques dutilisation dune mthode, etc.) ou de traitements mtiers (effectuer des vrifications supplmentaires si un client achte pour plus de 10000 darticles, envoyer une demande de rapprovisionnement lorsque linventaire est trop bas, etc.). Avec la POA, ces traitements peuvent sappliquer automatiquement toute lapplication ou uniquement un sous-ensemble de celle-ci.

Chapitre 8

Mthodes de rappel etintercepteurs 273

Les EJB permettent dutiliser la POA en fournissant la possibilit dintercepter les appels de mthodes laide dintercepteurs, qui seront automatiquement dclenchs par le conteneur lorsquune mthode EJB est invoque. Comme le montre la Figure8.3, les intercepteurs peuvent tre chans et sont appels avant et/ou aprs lexcution dune mthode.
INFO Les intercepteurs sappliquent aux beans de session et aux beans pilots par messages (MDB). Aux Chapitres14 et 15, nous verrons quun service web SOAP ou REST peut galement tre implment comme un EJB (en ajoutant lannotation @Stateless). En ce cas, ces services web peuvent galement utiliser des intercepteurs.

La Figure 8.3 montre que plusieurs intercepteurs sont appels entre le client et lEJB. En fait, vous pouvez considrer quun conteneur EJB est lui-mme une chane dintercepteurs: lorsque vous dveloppez un bean de session, vous vous concentrez sur le code mtier mais, en coulisse, le conteneur intercepte les appels de mthodes effectus par le client et applique diffrents services (gestion du cycle de vie, transactions, scurit, etc.). Grce aux intercepteurs, vous pouvez ajouter vos propres traitements transverses et les appliquer au code mtier de faon transparente.
<<executionEnvironment>> Conteneur EJB <<component>> Client Intercepteur 1 Intercepteur n <<component>> Bean de session

Figure8.3 Conteneur interceptant un appel et invoquant un intercepteur.

Il existe trois types dintercepteurs (que nous dcrirons dans la section suivante):

intercepteurs autour des appels; intercepteurs des mthodes mtiers; intercepteurs des mthodes de rappel du cycle de vie.

274

Java EE 6 et GlassFish 3

INFO La spcification EJB 3.1 (JSR 318) est compose de deux documents : la spcification EJB centrale et le document nonant les besoins des intercepteurs. Ce dernier dfinit le fonctionnement des intercepteurs et la faon de les utiliser. La raison de cette sparation est lie au fait que, dans de prochaines versions, les intercepteurs pourraient tre traits indpendamment des EJB.

Intercepteurs autour des appels

Le moyen le plus simple de dfinir un intercepteur consiste ajouter une annotation @javax.interceptor.AroundInvoke (ou llment de dploiement <aroundinvoke>) dans le bean lui-mme, comme dans le Listing8.3. CustomerEJB annote la mthode logMethod(), qui sert enregistrer un message lorsque lon entre dans une mthode et un autre lorsquon en sort. Lorsque cet EJB est dploy, tous les appels aux mthodes createCustomer() ou findCustomerById() seront intercepts et le code de logMethod() sappliquera. Notez que la porte de cet intercepteur est limite au bean. Les intercepteurs autour des appels ninterviennent que dans la mme transaction et dans le mme contexte de scurit que la mthode pour laquelle ils sinterposent.
Listing8.3: CustomerEJB utilise un intercepteur
@Stateless public class CustomerEJB { @PersistenceContext(unitName = "chapter08PU") private EntityManager em; private Logger logger = Logger.getLogger("com.apress.javaee6"); public void createCustomer(Customer customer) { em.persist(customer); } public Customer findCustomerById(Long id) { return em.find(Customer.class, id); } @AroundInvoke private Object logMethod(InvocationContext ic) throws Exception {

Chapitre 8

Mthodes de rappel etintercepteurs 275

logger.entering(ic.getTarget().toString(), ic.getMethod().getName()); try { return ic.proceed(); } finally { logger.exiting(ic.getTarget().toString(), ic.getMethod().getName()); } }

Bien quelle soit annote par une signature bien prcise:

@AroundInvoke,

la mthode

logMethod()

doit avoir

@AroundInvoke Object <nom-mthode>(InvocationContext ic) throws Exception;

Une mthode intercepteur autour des appels doit respecter les rgles suivantes:

Elle peut tre public, private, protected ou avoir un accs paquetage, mais ne peut pas tre static ou final. Elle doit avoir un paramtre javax.interceptor.InvocationContext et renvoyer un Object, qui est le rsultat de lappel de la mthode cible (si cette mthode renvoie void, cet objet vaudra null). Elle peut lever une exception contrle.

Lobjet InvocationContext permet aux intercepteurs de contrler le comportement de la chane des appels. Lorsque plusieurs intercepteurs sont chans, cest la mme instance dInvocationContext qui est passe chacun deux, ce qui peut impliquer un traitement de ces donnes contextuelles par les autres intercepteurs. LeTableau8.2 dcrit lAPI dInvocationContext.
Tableau8.2: Dfinition de linterface InvocationContext

Mthode
getContextData getMethod getParameters getTarget

Description Permet de passer des valeurs entre les mmes mthodes intercepteurs dans la mme instance dInvocationContext laide dune Map. Renvoie la mthode du bean pour laquelle lintercepteur a t invoqu. Renvoie les paramtres qui seront utiliss pour invoquer la mthode mtier. Renvoie linstance du bean laquelle appartient la mthode intercepte.

276

Java EE 6 et GlassFish 3

Mthode
getTimer proceed

Description Renvoie le timer associ une mthode @Timeout. Appelle la mthode intercepteur suivante de la chane. Renvoie le rsultat de la mthode suivante. Si une mthode est de type void, proceed renvoie null. Modifie la valeur des paramtres utiliss pour lappel de la mthode cible. Si les types et le nombre de paramtres ne correspondent pas la signature de la mthode, lexception IllegalArgumentException est leve.

setParameters

Pour expliquer le fonctionnement du code du Listing8.3, examinons le diagramme de squence de la Figure8.4 pour voir ce qui se passe lorsquun client invoque la mthode createCustomer(). Tout dabord, le conteneur intercepte cet appel et, au lieu dexcuter directement createCustomer(), appelle la mthode logMethod(). Celle-ci utilise linterface InvocationContext pour obtenir le nom du bean (ic. getTarget()) et de la mthode (ic.getMethod()) appels afin de produire un message (logger.entering()). Puis logMethod() appelle la mthode InvocationContext.proceed(), qui lui indique quelle doit passer lintercepteur suivant ou appeler la mthode mtier du bean. Cet appel est trs important car, sans lui, la chane des intercepteurs serait rompue et la mthode mtier ne serait pas appele. Enfin, la mthode createCustomer() est finalement excute lorsquelle se termine, lintercepteur termine son excution en enregistrant un message (logger. exiting()). La mme squence se serait produite si un client avait appel la mthode findCustomerById().
Figure8.4 Chanage de diffrents intercepteurs.
Client Conteneur CustomerEJB Logger InvocationContext

1 : createCustomer

2 : logMethod 3 : entering 4 : proceed 5 : createCustomer 6 : exiting

Chapitre 8

Mthodes de rappel etintercepteurs 277

Intercepteurs de mthode

Le Listing 8.3 dfinit un intercepteur qui nest disponible que pour CustomerEJB mais, la plupart du temps, on souhaite isoler un traitement transverse dans une classe distincte et demander au conteneur dintercepter les appels de mthodes de plusieurs beans de session. Lenregistrement de journaux est un exemple typique de situation dans laquelle on veut enregistrer les entres et les sorties de toutes les mthodes de tous les EJB. Pour disposer de ce type dintercepteur, on doit crer une classe distincte et informer le conteneur de lappliquer un bean prcis ou une mthode de bean particulire. Le Listing8.4 isole la mthode logMethod() du Listing8.3 dans une classe part, LoggingInterceptor, qui est un simple POJO disposant dune mthode annote par @AroundInvoke.
Listing8.4: Classe intercepteur enregistrant lentre et la sortie dune mthode
public class LoggingInterceptor { private Logger logger = Logger.getLogger("com.apress.javaee6"); @AroundInvoke public Object logMethod(InvocationContext ic) throws Exception { logger.entering(ic.getTarget().toString(), ic.getMethod().getName()); try { return ic.proceed(); } finally { logger.exiting(ic.getTarget().toString(), ic.getMethod().getName()); } }

peut maintenant tre utilise de faon transparente par nimporte quel EJB souhaitant disposer dun intercepteur. Pour ce faire, le bean doit informer le conteneur avec lannotation @javax.interceptor.Interceptors. Dans le Listing8.5, cette annotation est place sur la mthode createCustomer(), ce qui signifie que tout appel cette mthode sera intercept par le conteneur qui invoquera la classe .LoggingInterceptor (pour enregistrer un message signalant lentre et la sortie de la mthode).
LoggingInterceptor

Listing8.5: CustomerEJB utilise un intercepteur sur une mthode


@Stateless public class CustomerEJB { @PersistenceContext(unitName = "chapter08PU")

278

Java EE 6 et GlassFish 3

private EntityManager em; @Interceptors(LoggingInterceptor.class) public void createCustomer(Customer customer) { em.persist(customer); } public Customer findCustomerById(Long id) { return em.find(Customer.class, id); }

Dans le Listing8.5, @Interceptors nest attache qu la mthode createCustomer(), ce qui signifie que le conteneur ninterceptera pas un appel findCustomerById(). Si vous voulez que ces deux mthodes soient interceptes, vous pouvez placer lannotation @Interceptors sur chacune de ces mthodes ou sur le bean lui-mme (dans ce dernier cas, lintercepteur sera dclench pour toutes les mthodes du bean):
@Stateless @Interceptors(LoggingInterceptor.class) public class CustomerEJB { public void createCustomer(Customer customer) { ... } public Customer findCustomerById(Long id) { ... } }

Si vous voulez que toutes les mthodes, sauf une, soient interceptes, utilisez lannotation javax.interceptor.ExcludeClassInterceptors pour exclure la mthode concerne. Dans le code suivant, lappel updateCustomer() ne sera pas intercept alors que les appels toutes les autres mthodes le seront:
@Stateless @Interceptors(LoggingInterceptor.class) public class CustomerEJB { public void createCustomer(Customer customer) { ... } public Customer findCustomerById(Long id) { ... } public void removeCustomer(Customer customer) { ... } @ExcludeClassInterceptors public Customer updateCustomer(Customer customer) { ... } }

Intercepteur du cycle de vie

Dans la premire partie de ce chapitre, nous avons vu comment grer les mthodes de rappel dans un EJB. Avec une annotation de rappel, vous pouvez demander au conteneur dappeler une mthode lors dune phase prcise du cycle de vie (@PostConstruct, @PrePassivate, @PostActivate et @PreDestroy). Si vous souhaitez, par exemple, ajouter une entre dans un journal chaque fois quune instance dun bean

Chapitre 8

Mthodes de rappel etintercepteurs 279

est cre, il suffit de placer lannotation @PostConstruct sur une mthode du bean et dajouter un peu de code pour enregistrer lentre dans le journal. Mais comment faire pour capturer les vnements du cycle de vie entre plusieurs types de beans? Les intercepteurs du cycle de vie permettent disoler du code dans une classe et de linvoquer lorsque lun de ces vnements se dclenche. Les intercepteurs du cycle de vie ressemblent ce que nous venons de voir dans le Listing8.4, sauf que les mthodes utilisent des annotations de rappel au lieu de @AroundInvoke. Le Listing8.6 prsente une classe ProfileInterceptor avec deux mthodes: logMethod(), qui sera appele aprs la construction dune instance et profile(), qui sera invoque avant la destruction dune instance.
Listing8.6: Intercepteur du cycle de vie dfinissant deux mthodes
public class ProfileInterceptor { private Logger logger = Logger.getLogger("com.apress.javaee6"); @PostConstruct public void logMethod(InvocationContext ic) { logger.entering(ic.getTarget().toString(), ic.getMethod().getName()); try { return ic.proceed(); } finally { logger.exiting(ic.getTarget().toString(), ic.getMethod().getName()); } } @PreDestroy public void profile(InvocationContext ic) { long initTime = System.currentTimeMillis(); try { return ic.proceed(); } finally { long diffTime = System.currentTimeMillis() - initTime; logger.fine(ic.getMethod() + " took " + diffTime + " millis"); } } }

Comme vous pouvez le voir dans le Listing8.6, les mthodes intercepteurs du cycle de vie prennent en paramtre un objet InvocationContext, renvoient void au lieu dObject (car, comme on la expliqu dans la section "Mthodes de rappel", les mthodes de rappel du cycle de vie renvoient void) et ne peuvent pas lancer dexceptions contrles.

280

Java EE 6 et GlassFish 3

Pour appliquer lintercepteur du Listing8.6, le bean de session doit utiliser lannotation @Interceptors: dans le Listing8.7, CustomerEJB prcise quil sagit de la classe ProfileInterceptor. Ds lors, quand lEJB sera instanci par le conteneur, la mthode logMethod() de lintercepteur sera invoque avant la mthode init(). Les appels aux mthodes createCustomer() ou findCustomerById() ne seront en revanche pas intercepts, mais la mthode profile() de lintercepteur sera appele avant que le CustomerEJB soit dtruit par le conteneur.
Listing8.7: CustomerEJB utilisant un intercepteur de rappel
@Stateless @Interceptors(ProfileInterceptor.class) public class CustomerEJB { @PersistenceContext(unitName = "chapter08PU") private EntityManager em; @PostConstruct public void init() { // ... } public void createCustomer(Customer customer) { em.persist(customer); } public Customer findCustomerById(Long id) { return em.find(Customer.class, id); }

Les mthodes de rappel du cycle de vie et les mthodes @AroundInvoke peuvent tre dfinies dans la mme classe intercepteur.
Chanage et exclusion dintercepteurs

Nous venons de voir comment intercepter les appels dans un seul bean (avec @AroundInvoke) et entre plusieurs beans (avec @Interceptors). EJB 3.1 permet galement de chaner plusieurs intercepteurs et de dfinir des intercepteurs par dfaut qui sappliqueront tous les beans de session. En fait, il est possible dattacher plusieurs intercepteurs avec lannotation @Interceptors en lui passant en paramtre une liste dintercepteurs spars par des virgules. En ce cas, lordre dans lequel ils seront invoqus est dtermin par leur ordre dapparition dans cette liste. Le code du Listing8.8, par exemple, utilise @Interceptors la fois au niveau du bean et au niveau des mthodes.

Chapitre 8

Mthodes de rappel etintercepteurs 281

Listing8.8: CustomerEJB utilisant un intercepteur de rappel


@Stateless @Interceptors(I1.class, I2.class) public class CustomerEJB { public void createCustomer(Customer customer) { ... } @Interceptors(I3.class, I4.class) public Customer findCustomerById(Long id) { ... } public void removeCustomer(Customer customer) { ... } @ExcludeClassInterceptors public Customer updateCustomer(Customer customer) { ... } }

Aucun intercepteur ne sera invoqu lorsquun client appelle la mthode updateCustomer() (car elle est annote par @ExcludeClassInterceptors). Lorsque createCustomer() est appele, lintercepteur I1 sexcutera, suivi de lintercepteur I2. Lorsque findCustomerById() est appele, les intercepteurs I1, I2, I3 et I4 seront excuts dans cet ordre. Outre les intercepteurs au niveau des mthodes et des classes, EJB 3.1 permet de crer des intercepteurs par dfaut, qui seront utiliss pour toutes les mthodes de tous les EJB dune application. Aucune annotation nayant la porte dune application, ces intercepteurs doivent tre dfinis dans le descripteur de dploiement (ejbjar.xml). Voici, par exemple, la partie XML ajouter ce fichier pour appliquer par dfaut lintercepteur ProfileInterceptor tous les EJB:
<assembly-descriptor> <interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class> com.apress.javaee6.ProfileInterceptor </interceptor-class> </interceptor-binding> </assembly-descriptor>

Le caractre joker * dans llment <ejb-name> signifie que tous les EJB appliqueront lintercepteur dfini dans llment <interceptor-class>. Si vous dployez le bean CustomerEJB du Listing8.7 avec cet intercepteur par dfaut, le ProfileInterceptor sera invoqu avant tous les autres intercepteurs. Si plusieurs types dintercepteurs sont dfinis pour un mme bean de session, le conteneur les applique dans lordre dcroissant des portes: le premier sera donc lintercepteur par dfaut et le dernier, lintercepteur de mthode. Les rgles qui gouvernent ces appels sont dcrites la Figure8.5. Pour dsactiver les intercepteurs par dfaut pour un EJB spcifique, il suffit dappliquer lannotation @javax. interceptor.ExcludeDefaultInterceptors sur la classe ou sur les mthodes, comme le montre le Listing8.9.

282

Java EE 6 et GlassFish 3

<<executionEnvironment>> Conteneur EJB <<component>> Client Intercepteur par dfaut Intercepteur de classe Intercepteur de mthode <<component>> Bean de session

Figure8.5 Chanage des diffrents types dintercepteurs.

Listing8.9: EJB excluant les intercepteurs par dfaut


@Stateless @ExcludeDefaultInterceptors @Interceptors(LoggingInterceptor.class) public class CustomerEJB { public void createCustomer(Customer customer) { ... } public Customer findCustomerById(Long id) { ... } public void removeCustomer(Customer customer) { ... } @ExcludeClassInterceptors public Customer updateCustomer(Customer customer) { ... } }

Rsum
Dans ce chapitre, nous avons vu que les beans de session sans tat et singletons partagent le mme cycle de vie et que celui des beans avec tat est lgrement diffrent. En effet, ces derniers mmorisent ltat conversationnel avec leur client et doivent temporairement srialiser cet tat sur un support de stockage permanent (passivation). Nous avons galement vu que les annotations de rappel permettent dajouter de la logique mtier aux beans, qui sexcutera avant ou aprs la survenue dun vnement (@PostConstruct, @PreDestroy, etc.). Les intercepteurs sont des mcanismes permettant de mettre en uvre la POA avec les EJB car ils permettent au conteneur dinvoquer des traitements transverses sur lapplication. Ils sont simples utiliser, puissants et peuvent tre chans pour appliquer plusieurs traitements la suite. Il est galement possible de dfinir des intercepteurs par dfaut qui sappliqueront toutes les mthodes de tous les beans dune application. Un conteneur EJB peut lui-mme tre considr comme une chane dintercepteurs: les appels de mthodes sont intercepts par le conteneur, qui applique alors plusieurs services comme la gestion des transactions et de la scurit. Le chapitre suivant est consacr ces deux services.

9
Transactions et scurit
La gestion des transactions et de la scurit est un problme important pour les entreprises car elle permet aux applications de disposer de donnes cohrentes et de scuriser les accs ces donnes. Ces deux services sont des traitements de bas niveau dont ne devraient pas se soucier ceux qui dveloppent du code mtier. Ils sont offerts par les EJB de faon trs simple: soit par programmation un haut niveau dabstraction, soit de faon dclarative en utilisant les mtadonnes. Lessentiel du travail dune application dentreprise consiste grer des donnes: les stocker (gnralement dans une base de donnes), les rcuprer, les traiter, etc. Ces traitements sont souvent raliss simultanment par plusieurs applications qui tentent daccder aux mmes donnes. Les SGBDR disposent de mcanismes debas niveau pour synchroniser les accs concurrents le verrouillage pessimiste, par exemple et utilisent les transactions pour garantir la cohrence des donnes. Les EJB utilisent tous ces mcanismes. La scurisation des donnes est galement un point important. La couche mtier doit agir comme un pare-feu et autoriser certaines oprations certains groupes dutilisateurs tout en interdisant laccs dautres (les utilisateurs et les employs peuvent lire les donnes, mais seuls les employs sont autoriss les stocker, par exemple). La premire partie de ce chapitre est consacre la gestion des transactions avec EJB3.1. Nous prsenterons les transactions en gnral, puis les diffrents types de transactions reconnus par les EJB. La seconde partie du chapitre sintressera la scurit.

Transactions
Les donnes sont cruciales et elles doivent tre correctes, quelles que soient les oprations effectues et le nombre dapplications qui y accdent. Une transaction

284

Java EE 6 et GlassFish 3

sert garantir que les donnes resteront dans un tat cohrent. Elle reprsente un groupe logique doprations qui doivent tre excutes de faon atomique elle forme donc ce que lon appelle une unit de traitement. Les oprations qui la constituent peuvent impliquer le stockage de donnes dans une ou plusieurs bases, lenvoi de messages ou lappel de services web. Les socits utilisent quotidiennement les transactions pour les applications bancaires ou de commerce en ligne, ainsi que pour les interactions B2B (business-to-business) avec leurs partenaires. Ces oprations mtiers indivisibles sexcutent en squence ou en parallle pendant une dure relativement courte. Pour quune transaction russisse, toutes ses oprations doivent russir (on dit alors que la transaction est valide committed). Ilsuffit que lune des oprations choue pour que la transaction choue galement (la transaction est annule rolled back). Les transactions doivent garantir un certain niveau de fiabilit et de robustesse et respecter les proprits ACID.
ACID

ACID est un acronyme des quatre proprits qui dfinissent une transaction fiable: atomicit, cohrence, isolement et dure (voir Tableau9.1). Pour expliquer chacune delles, prenons lexemple classique dun transfert bancaire dans lequel on dbite un compte pargne pour crditer un compte courant.
Tableau9.1: Proprits ACID

Proprit Atomicit

Description Une transaction est compose dune ou de plusieurs oprations regroupes dans une unit de traitement. la fin de la transaction, soit toutes les oprations se sont droules correctement (transaction valide commit), soit il sest pass un problme inattendu, auquel cas aucune ne sera ralise (transaction annule rollback). la fin dune transaction, les donnes sont dans un tat cohrent. Ltat intermdiaire dune transaction nest pas visible aux applications externes. Lorsquune transaction est valide, les modifications apportes aux donnes sont visibles aux autres applications.

Cohrence Isolement Dure

On peut imaginer que le transfert dun compte vers un autre reprsente une suite daccs la base de donnes: le compte pargne est dbit laide dune instruction update de SQL, le compte courant est crdit par une autre instruction update et un

Chapitre 9

Transactions et scurit 285

enregistrement est ajout dans une autre table afin de garder la trace de ce transfert. Ces oprations doivent seffectuer dans la mme unit de traitement (atomicit) car il ne faut pas que le dbit ait lieu et quil ny ait pas de crdit correspondant. Du point de vue dune application externe interrogeant les comptes, les deux oprations ne seront visibles que lorsquelles se seront toutes les deux correctement ralises (isolement). Lorsque la transaction est valide ou annule, la cohrence des donnes est assure par les contraintes dintgrit de la base de donnes (cls primaires, relations ou champs). Lorsque le transfert est termin, il est possible daccder aux donnes par les autres applications (dure).
Transactions locales

Pour que les transactions fonctionnent et respectent les proprits ACID, plusieurs composants doivent tre mis en place. Commenons par lexemple le plus simple qui soit dune application effectuant plusieurs modifications sur une ressource unique (une base de donnes, par exemple). Lorsquune seule ressource transactionnelle est ncessaire, il suffit dutiliser une transaction locale on peut utiliser des transactions distribues la JTA, mais ce nest pas strictement ncessaire. La Figure9.1 reprsente linteraction entre une application et une ressource via un gestionnaire de transactions et un gestionnaire de ressources.
Figure9.1 Transaction nimpliquant quune seule ressource.
Application JTA Gestionnaire de transactions JTA Gestionnaire de ressources

Ressource

Les composants prsents la Figure 9.1 permettent dabstraire de lapplication lessentiel du traitement spcifique une transaction:

Le gestionnaire de transactions est le composant central de la gestion des oprations transactionnelles. Il cre les transactions pour le compte de lapplication, informe le gestionnaire de ressources quil participe une transaction (opration

286

Java EE 6 et GlassFish 3

de recrutement) et conduit la validation ou lannulation de cette transaction sur ce gestionnaire de ressources. Le gestionnaire de ressources soccupe de grer les ressources et de les enregistrer auprs du gestionnaire de transactions. Un pilote de SGBDR, une ressource JMS ou un connecteur Java sont des gestionnaires de ressources. La ressource est le support de stockage persistant sur lequel on lit ou crit (une base de donnes, etc.). Lapplication nest pas responsable du respect des proprits ACID: elle se borne simplement dcider sil faut valider ou annuler la transaction, et cest le gestionnaire de transactions qui prpare toutes les ressources pour que tout se passe bien. Avec Java EE, ces composants grent les transactions via JTA (Java Transaction API), qui est dcrite par la JSR907. JTA dfinit un ensemble dinterfaces permettant lapplication de dlimiter des frontires de transactions, ainsi que des API pour fonctionner avec le gestionnaire de transactions. Ces interfaces sont dfinies dans le paquetage javax.transaction; le Tableau9.2 dcrit les principales.
Tableau9.2: Interfaces principales de JTA

Interface
UserTransaction

Description Dfinit les mthodes quune application peut utiliser pour contrler par programme les frontires de transactions. Les EJB BMT (beanmanaged transaction) sen servent pour lancer, valider ou annuler une transaction (voir la section "Transactions gres par les beans"). Permet au conteneur EJB de dlimiter les frontires de transaction du ct EJB. Permet deffectuer des oprations sur la transaction dans un objet Transaction. quivalent Java de linterface standard X/Open XA (voir la section suivante).

TransactionManager Transaction XAResource

XA et transactions distribues

Comme nous venons de le voir, une transaction qui nutilise quune seule ressource (comme la Figure9.1) est une transaction locale. Cependant, de nombreuses applications dentreprise utilisent plusieurs ressources : si lon revient lexemple du transfert de fonds, le compte pargne et le compte courant pourraient se trouver dans deux bases de donnes distinctes. Il faut alors grer les transactions entre ces diffrentes ressources ou entre des ressources distribues sur le rseau. Ces transactions

Chapitre 9

Transactions et scurit 287

lchelle dune entreprise ncessitent une coordination particulire impliquant XA et JTS (Java Transaction Service). La Figure9.2 reprsente une application qui utilise une frontire de transaction entre plusieurs ressources. Elle peut ainsi stocker des donnes dans une base et envoyer un message JMS dans la mme unit de traitement, par exemple.
Figure9.2 Transaction XA impliquant deux ressources.
Application JTA Gestionnaire de transactions Ressource JTA / XA Gestionnaire de ressources Gestionnaire de ressources

Ressource

Ressource

Pour disposer dune transaction fiable entre plusieurs ressources, le gestionnaire de transactions doit utiliser une interfaceXA du gestionnaire de ressources, un standard de lOpen Group (http:// www.opengroup.org) pour le traitement des transactions distribues (DTP) qui prserve les proprits ACID. Cette interface est reconnue par JTA et permet des gestionnaires de ressources htrognes provenant dditeurs diffrents de fonctionner ensemble en passant par une interface commune. XA utilise une validation de transaction en deux phases pour garantir que toutes les ressources valideront ou annuleront simultanment chaque transaction. Dans notre exemple de transfert de fonds, supposons que le compte pargne soit dbit sur une premire base de donnes et que la transaction soit valide. Puis le compte courant est crdit sur une seconde base, mais la transaction choue: il faudrait donc revenir la premire base et annuler les modifications apportes par la transaction. Comme le montre la Figure9.3, pour viter ce problme dincohrence des donnes, la validation en deux phases effectue une tape supplmentaire avant la validation finale. Au cours de la premire phase, chaque gestionnaire de ressources est prvenu via une commande "de prparation" quune validation va avoir lieu, ce qui leur permet dindiquer sils peuvent ou non appliquer leurs modifications. Sils annoncent tous quils sont prts, la transaction peut se poursuivre et on demande tous les gestionnaires de ressources de valider leurs transactions dans la seconde phase.

288

Java EE 6 et GlassFish 3

Figure9.3 Validation en deux phases.

Phase 1 Prparation prpare prt Gestionnaire de transactions prt prpare Gestionnaire de ressources Gestionnaire de ressources

Phase 2 Validation valide valide Gestionnaire de transactions valide valide Gestionnaire de ressources Gestionnaire de ressources

La plupart du temps, les ressources sont distribues sur le rseau (voir Figure9.4). Un tel systme utilise JTS, qui implmente la spcification OTS (Object Transaction Service) de lOMG (Object Management Group) permettant aux gestionnaires de transactions de participer aux transactions distribues via IIOP (Internet Inter-ORB Protocol). JTS est conu pour les diteurs qui fournissent les infrastructures de systmes de transaction. Les dveloppeurs EJB nont pas sen soucier: il suffit quils utilisent JTA, qui sinterface avec JTS un niveau suprieur.
Figure9.4 Une transaction distribue XA.
Application JTA Gestionnaire de transactions JTA / XA Gestionnaire de ressources JTS / OTS Gestionnaire de transactions JTA / XA Gestionnaire de ressources

Ressource

Ressource

Support des transactions avec les EJB


Lorsque lon dveloppe de la logique mtier avec les EJB, il nest pas ncessaire de se soucier de la structure interne des gestionnaires de transactions ou de ressources car JTA abstrait la plus grosse partie de la complexit sous-jacente. Grce aux EJB, le dveloppement dune application transactionnelle est donc trs simple car cest le conteneur qui implmente les protocoles de bas niveau pour les transactions, comme

Chapitre 9

Transactions et scurit 289

la validation en deux phases ou la propagation du contexte de transaction. Un conteneur EJB est donc un gestionnaire de transactions qui utilise la fois JTA et JTS pour participer aux transactions distribues impliquant dautres conteneurs EJB. Dans une application JavaEE typique, les beans de session tablissent les frontires de la transaction, appellent des entits pour dialoguer avec la base de donnes ou envoient des messages JMS dans un contexte de transaction. Depuis sa cration, le modle EJB a t conu pour grer les transactions: elles font donc partie des EJB et chacune de leurs mthodes est, par dfaut, automatiquement enveloppe dans une transaction. Ce comportement par dfaut sappelle transaction gre par le conteneur (CMT), ou dmarcation de transaction dclarative. Vous pouvez galement choisir de grer vous-mme les transactions en utilisant des transactions gres par le bean (BMT), ou dmarcation de transaction par programme. Cest la dmarcation de transaction qui dtermine quand commencent et finissent les transactions.
Transactions gres par le conteneur

Lorsque lon gre les transactions de faon dclarative, on dlgue la politique de dmarcation au conteneur. Il nest pas ncessaire dutiliser explicitement JTA dans le code (mme sil est utilis en coulisse); on peut laisser le conteneur marquer les frontires de transactions en les ouvrant et en les validant partir des mtadonnes. Le conteneur EJB fournit les services de gestion des transactions aux beans de session et aux MDB (voir Chapitre13). Au Chapitre7, nous avons vu plusieurs exemples de beans de session, dannotations et dinterfaces, mais rien de spcifique aux transactions. Le Listing9.1 montre le code dun bean sans tat utilisant CMT. Comme vous pouvez le constater, aucune annotation particulire na t ajoute et il ny a pas dinterface spciale implmenter comme on la dj indiqu, les EJB sont transactionnels par nature. Grce la configuration par exception, cest la gestion des transactions par dfaut qui sapplique ici (comme nous le verrons plus loin, REQUIRED est lattribut de transaction par dfaut).
Listing9.1: Bean sans tat avec CMT
@Stateless public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @EJB

290

Java EE 6 et GlassFish 3

private InventoryEJB inventory; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); } public Book createBook(Book book) { em.persist(book); inventory.addItem(book); return book; } }

Vous pourriez vous demander ce qui rend le code du Listing 9.1 transactionnel : la rponse est le conteneur. La Figure9.5 montre ce qui se passe quand un client invoque la mthode createBook(): son appel est intercept par le conteneur, qui vrifie immdiatement avant lappel de cette mthode si un contexte de transaction est associ cet appel. Dans la ngative, le conteneur ouvre par dfaut une nouvelle transaction avant dentrer dans la mthode, puis invoque celle-ci. la fin de la mthode, le conteneur valide automatiquement la transaction (ou lannule automatiquement si une exception particulire est lance, comme nous le verrons dans la section "Traitement des exceptions").
Client Conteneur EJB ItemEJB InventoryEJB Transaction

1 : createBook 2 : begin 3 : createBook 4 : additem 5 : validation ou annulation

Figure9.5 Le conteneur gre la transaction.

Dans le Listing 9.1 et la Figure9.5, il est intressant de noter quune mthode mtier dun bean (ItemEJB.createBook()) peut tre cliente dune mthode mtier dun autre bean (InventoryEJB. AddItem()). Avec le comportement par dfaut, le contexte de transaction utilis pour createBook() (celui du client ou celui cr par le conteneur) est appliqu addItem(). La validation finale a lieu si les deux mthodes se sont termines correctement mais ce comportement peut tre modifi

Chapitre 9

Transactions et scurit 291

laide de mtadonnes (annotations ou descripteur de dploiement XML). Lattribut de transaction choisi (REQUIRED, REQUIRES_NEW, SUPPORTS, MANDATORY, NOT_SUPPORTED ou NEVER) modifie la faon dont le conteneur dmarque la transaction du client: soit il utilise la transaction du client, soit il excute la mthode dans une nouvelle transaction, soit il lexcute sans transaction, soit il lance une exception. Les attributs de transactions sont dcrits dans le Tableau9.3.
Tableau9.3: Attributs CMT Attributes

Attribut
REQUIRED

Description Cet attribut, qui est celui par dfaut, signifie quune mthode doit toujours tre invoque dans une transaction. Le conteneur en cre une nouvelle si la mthode a t appele par un client non transactionnel. Si le client dispose dun contexte de transaction, la mthode mtier sexcute dans celui-ci. On utilise REQUIRED lorsque lon modifie des donnes et que lon ne sait pas si le client a lanc ou non une transaction. Le conteneur cre toujours une nouvelle transaction avant dexcuter une mthode, que le client sexcute ou non dans une transaction. Si le client est dans une transaction, le conteneur la suspend temporairement, en cre une seconde, la valide, puis revient la premire. Ceci signifie que le succs ou lchec de la seconde transaction na pas deffet sur la transaction existante du client. On utilise REQUIRED_NEW lorsque lon ne souhaite pas quune annulation de la transaction ait un effet sur le client. La mthode de lEJB hrite du contexte de transaction du client. Si ce contexte est disponible, il est utilis par la mthode; sinon le conteneur invoque la mthode sans contexte de transaction. On utilise SUPPORTS lorsque lon a un accs en lecture seule la table de la base de donnes. Le conteneur exige une transaction avant dappeler la mthode mtier, mais nen crera pas de nouvelle. Si le client a un contexte de transaction, celui-ci est propag; sinon une exception javax. ejb.exception" EJBTransactionRequiredException est leve. La mthode de lEJB ne peut pas tre appele dans un contexte de transaction. Si le client nen possde pas, rien ne se passe; sil en a un, le conteneur suspend la transaction du client, appelle la mthode puis relance la transaction la fin de lappel. La mthode de lEJB ne doit pas tre appele par un client transactionnel. Si le client sexcute dans un contexte de transition, le conteneur lve une exception javax.ejb.EJBException.

REQUIRES_NEW

SUPPORTS

MANDATORY

NOT_SUPPORTED

NEVER

292

Java EE 6 et GlassFish 3

La Figure9.6 illustre tous les comportements possibles dun EJB en fonction de la prsence ou non dun contexte de transaction du client. Si, par exemple, la mthode createBook() na pas de contexte transactionnel et quelle appelle addItem() avec un attribut MANDATORY, une exception est lance. Le bas de la Figure9.6 montre les mmes combinaisons, mais avec un client disposant dun contexte transactionnel.
ItemEJB InventoryEJB
Attribut CMT REQUIRED REQUIRES NEW SUPPORTS 1. createBook() La mthode X n'est pas appele dans une transaction. additem MANDATORY NEVER Attribut CMT REQUIRED additem 1. createBook() La mthode X est dans une transaction. REQUIRES NEW SUPPORTS MANDATORY NEVER Rsultat Nouvelle transaction Nouvelle transaction Pas de transaction Exception Pas de transaction Rsultat Transaction du client Nouvelle transaction Transaction du client Transaction du client Exception

NOT SUPPORTED Pas de transaction

NOT SUPPORTED Pas de transaction

Figure9.6 Deux appels InventoryEJB avec des politiques de transactions diffrentes.

Pour appliquer lun de ces six attributs de dmarcation un bean de session, il suffit dutiliser lannotation @javax.ejb.TransactionAttribute ou le descripteur de dploiement (llment <trans-attribute> du fichier ejb-jar.xml). Ces mtadonnes peuvent sappliquer aux diffrentes mthodes ou au bean entier dans ce cas, toutes les mthodes mtiers du bean hritent de la valeur de lattribut. Dans le Listing9.2, ItemEJB utilise une politique de dmarcation SUPPORT, sauf la mthode createBook(), qui utilise REQUIRED.
Listing9.2: Bean sans tat avec CMT
@Stateless @TransactionAttribute(TransactionAttributeType.SUPPORTS) public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @EJB private InventoryEJB inventory; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); }

Chapitre 9

Transactions et scurit 293

@TransactionAttribute(TransactionAttributeType.REQUIRED) public Book createBook(Book book) { em.persist(book); inventory.addItem(book); return book; } }

INFO Le contexte de transaction du client ne se propage pas lors des appels de mthodes asynchrones. En outre, comme nous le verrons au Chapitre 13, les MDB nautorisent que les attributs REQUIRED et NOT_SUPPORTED.

Marquage dun CMT pour annulation

Nous avons vu que le conteneur dlimitait automatiquement les transactions et effectuait notre place les oprations de lancement, de validation et dannulation. En tant que dveloppeur, on peut cependant vouloir empcher la validation dune transaction en cas derreur ou dune condition mtier particulire. En outre, il faut bien comprendre quun bean CMT nest pas autoris annuler explicitement la transaction: il faut utiliser le contexte de lEJB (voir la section "Contexte de session" du Chapitre7) pour informer le conteneur de lannuler. Comme le montre le Listing 9.3, le bean InventoryEJB dispose dune mthode oneItemSold() qui accde la base de donnes via le gestionnaire de persistance et envoie un messsage JMS pour informer la socit de transport quun article a t vendu et quil doit tre livr. Si le niveau du stock est gal zro (ce qui signifie quil ny a plus darticle en stock), la mthode doit explicitement annuler la transaction. Pour ce faire, le bean doit dabord obtenir la SessionContext via linjection de dpendances, puis appeler la mthode setRollbackOnly() de cette interface. Cet appel nannule pas immdiatement la transaction mais positionne un indicateur dont tiendra compte le conteneur lorsquil terminera la transaction.
Listing9.3: Un bean sans tat marque la transaction pour annulation
@Stateless public class InventoryEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @Resource

294

Java EE 6 et GlassFish 3

private SessionContext ctx; public void oneItemSold(Item item) { em.merge(item); item.decreaseAvailableStock(); sendShippingMessage(); if (inventoryLevel(item) == 0) ctx.setRollbackOnly(); } }

Un bean peut galement appeler la mthode SessionContext.getRollbackOnly() pour tester si la transaction courante a t marque pour annulation. Un autre moyen dinformer par programme le conteneur quil doit annuler une transaction consiste lancer des types dexceptions prcis.
Traitement des exceptions

Le traitement des exceptions en Java est, depuis la cration du langage, assez troublant car il utilise la notion dexception contrle non contrle. Lassociation des transactions et des exceptions dans les EJB est galement assez pique... Avant daller plus loin, prcisons que le lancement dune exception dans une mthode mtier ne marquera pas toujours la transaction pour annulation cela dpend du type de lexception ou des mtadonnes qui la dfinissent. En fait, la spcification EJB3.1 met en relief deux types dexceptions:

Les exceptions dapplication. Ce sont les exceptions lies la logique mtier traite par lEJB. Une exception dapplication peut, par exemple, tre leve si des paramtres incorrects sont passs une mthode, si le stock est trop faible ou si le numro de carte de crdit est incorrect. Le lancement dune exception dapplication nimplique pas automatiquement que la transaction soit marque pour annulation. Comme on lexplique plus loin dans le Tableau9.4, le conteneur nannule pas une transaction lorsque des exceptions contrles (celles qui hritent de java.lang.Exception) sont leves par contre, il le fait pour les exceptions non contrles (qui hritent de RuntimeException). Les exceptions systmes. Elles sont causes par des erreurs au niveau systme, comme les erreurs JNDI, les erreurs de la JVM, limpossibilit dtablir une connexion avec la base de donnes, etc. Une exception systme peut tre une sous-classe de RuntimeException ou de java.rmi.RemoteException (et donc une sous-classe de javax.ejb.EJBException). La leve dune exception systme marque la transaction pour annulation.

Chapitre 9

Transactions et scurit 295

Avec cette dfinition, nous savons maintenant que le conteneur annulera la transaction sil dtecte une exception systme comme ArithmeticException, ClassCastException, IllegalArgumentException ou NullPointerException. Les exceptions dapplication dpendent en revanche de nombreux facteurs. titre dexemple, le Listing9.4 modifie le code du Listing9.3 et utilise une exception dapplication.
Listing9.4: Bean sans tat levant une exception dapplication
@Stateless public class InventoryEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; public void oneItemSold(Item item) throws InventoryLevelTooLowException { em.merge(item); item.decreaseAvailableStock(); sendShippingMessage(); if (inventoryLevel(item) == 0) throw new InventoryLevelTooLowException();

InventoryLevelTooLowException est une exception dapplication car elle est lie la logique mtier de la mthode oneItemSold(). Selon que lon veuille ou non annuler la transaction, on peut la faire hriter dune exception contrle ou non contrle, ou lannoter avec @javax.ejb.ApplicationException (ou llment XML quivalent dans le descripteur de dploiement). Cette annotation a un paramtre rollback qui peut tre initialis true pour annuler explicitement la transaction. Dans le Listing9.5, InventoryLevelTooLowException est une exception annote et contrle.

Listing9.5: Exception dapplication avec rollback = true


@ApplicationException(rollback = true) public class InventoryLevelTooLowException extends Exception { public InventoryLevelTooLowException() { } public InventoryLevelTooLowException(String message) { super(message); } }

Si le bean InventoryEJB du Listing9.4 lance lexception dfinie dans le Listing9.5, la transaction sera marque pour annulation et cest le conteneur qui se chargera de

296

Java EE 6 et GlassFish 3

cette annulation la fin de la transaction. Le Tableau9.4 prsente toutes les combinaisons possibles dexceptions dapplication. Sa premire ligne pourrait tre interprte comme "si lexception dapplication hrite dException et quelle ne soit pas annote par @ApplicationException, son lancement ne marquera pas la transaction pour annulation".
Tableau9.4: Combinaisons des exceptions dapplications

Hrite de
Exception

@ApplicationException

Description Par dfaut, la leve dune exception contrle ne marque pas la transaction pour annulation La transaction est marque pour annulation La transaction nest pas marque pour annulation Par dfaut, la leve dune exception non contrle marque la transaction pour annulation La transaction est marque pour annulation La transaction nest pas marque pour annulation

Pas dannotation

Exception Exception RuntimeException

rollback = true rollback = false

Pas dannotation

RuntimeException RuntimeException

rollback = true rollback = false

Transactions gres par le bean

Avec CMT, on laisse au conteneur le soin de raliser la dmarcation des transactions en prcisant simplement un attribut et en utilisant le contexte de session ou des exceptions pour marquer une transaction pour annulation. Dans certains cas, toutefois, lapproche dclarative de CMT ne permet pas dobtenir la finesse de dmarcation voulue (une mthode ne peut pas participer plusieurs transactions, par exemple). Pour rsoudre ce problme, les EJB permettent de grer les dmarcations par programme avec BMT (Bean-Managed Transaction), qui autorise la gestion explicite des frontires de transaction (lancement, validation, annulation) avec JTA.

Chapitre 9

Transactions et scurit 297

Pour dsactiver la dmarcation CMT par dfaut et basculer dans le mode BMT, un bean doit simplement utiliser lannotation @javax.ejb.TransactionManagement (ou son quivalent XML dans le fichier ejb-jar.xml):
@Stateless @TransactionManagement(TransactionManagementType.BEAN) public class ItemEJB { ... }

Avec la dmarcation BMT, lapplication demande la transaction et le conteneur EJB cre la transaction physique puis soccupe uniquement de quelques dtails de bas niveau. En outre, il ne propage pas les transactions dun EJB BMT vers un autre. Linterface principale pour mettre en uvre BMT est javax.transaction.-UserTransaction. Elle permet au bean de dlimiter une transaction, de connatre son statut, de fixer un dlai dexpiration, etc. Cette interface est instancie par le conteneur EJB et est rendue disponible via linjection de dpendances, une recherche JNDI ou le SessionContext (avec la mthode SessionContext.get-UserTransaction()). Son API est dcrite dans le Tableau9.5.
Tableau9.5: Mthodes de linterface javax.transaction.UserTransaction

Interface
begin commit rollback setRollbackOnly getStatus setTransactionTimeout

Description Dbute une nouvelle transaction et lassocie au thread courant Valide la transaction attache au thread courant Annule la transaction attache au thread courant Marque la transaction courante pour annulation Rcupre le statut de la transaction courante Modifie le dlai dexpiration de la transaction courante

Le Listing 9.6 montre comment dvelopper un bean BMT. On commence par obtenir une rfrence linterface UserTransaction par injection via lannotation @Resource. La mthode oneItemSold() dbute la transaction, effectue un traitement mtier puis, en fonction dune condition mtier, valide ou annule cette transaction. Notez galement que la transaction est marque pour annulation dans le bloc catch (nous avons simplifi le traitement dexception pour des raisons de lisibilit).

298

Java EE 6 et GlassFish 3

Listing9.6: Bean sans tat avec BMT


@Stateless public class InventoryEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @Resource private UserTransaction ut; public void oneItemSold(Item item) { try { ut.begin(); em.merge(item); item.decreaseAvailableStock(); sendShippingMessage(); if (inventoryLevel(item) == 0) ut.rollback(); else ut.commit(); } catch (Exception e) { ut.setRollbackOnly(); } sendInventoryAlert(); } }

Dans le code CMT du Listing9.3, cest le conteneur qui dbutait la transaction avant lexcution de la mthode et la validait immdiatement aprs. Avec le code BMT du Listing9.6, cest vous qui dfinissez manuellement les frontires de la transaction dans la mthode elle-mme.

Scurit
La scurisation des applications est (ou devrait tre) un souci majeur pour les socits. Ceci peut aller de la scurisation dun rseau au chiffrement des transferts de donnes, en passant par loctroi de certaines permissions aux utilisateurs dun systme. Au cours de notre navigation quotidienne sur Internet, nous rencontrons de nombreux sites o nous devons entrer un nom dutilisateur et un mot de passe pour avoir accs certaines parties dune application. La scurit est devenue une ncessit sur le Web et, en consquence, Java EE a dfini plusieurs mcanismes pour scuriser les applications.

Chapitre 9

Transactions et scurit 299

La scurit ncessite de comprendre plusieurs concepts. Lun deux est la liaison des utilisateurs un principal et le fait quils peuvent avoir plusieurs rles. Chaque rle donne des permissions pour un ensemble de ressources mais, pour avoir une identit dans le domaine de scurit, un utilisateur doit pouvoir tre authentifi: la plate-forme contrlera alors laccs en autorisant les ressources en fonction du rle de lutilisateur.
Principal et rle

Les "principaux" et les rles tiennent une place importante dans la scurit logicielle. Un principal est un utilisateur qui a t authentifi (par un nom et un mot de passe stocks dans une base de donnes, par exemple). Les principaux peuvent tre organiss en groupes, appels rles, qui leur permettent de partager un ensemble de permissions (accs au systme de facturation ou possibilit denvoyer des messages dans un workflow, par exemple). La Figure9.7 montre comment les utilisateurs peuvent tre reprsents dans un systme scuris. Comme vous pouvez le constater, un utilisateur authentifi est li un principal qui a un identifiant unique et qui peut tre associ plusieurs rles. Le principal de lutilisateur Frank, par exemple, est li aux rles Employ et Admin.
Figure9.7 Principaux et rles.

300

Java EE 6 et GlassFish 3

Authentification et habilitation

La scurisation dune application implique deux fonctions : lauthentification et lhabilitation. La premire consiste vrifier lidentit de lutilisateur (son identifiant et son mot de passe, son OpenID, son empreinte biomtrique, etc.) en utilisant un systme dauthentification et en affectant un principal cet utilisateur. Lhabilitation consiste dterminer si un principal (un utilisateur authentifi) a accs une ressource particulire (un livre, par exemple) ou une fonction donne (supprimer un livre, par exemple). Selon son rle, lutilisateur peut avoir accs toutes les ressources, aucune ou certaines dentre elles. La Figure9.8 dcrit un scnario de scurit classique. Lutilisateur doit entrer son identifiant et son mot de passe via une interface client (web ou Swing). Ces informations sont vrifies avec JAAS (Java Authentication and Authorization Service) via un systme dauthentification sous-jacent. Si lauthentification russit, lutilisateur est associ un principal qui est ensuite lui-mme associ un ou plusieurs rles. Lorsque lutilisateur accde un EJB scuris, le principal est transmis de faon transparente lEJB, qui lutilise pour savoir si le rle de lappelant lautorise accder aux mthodes quil tente dexcuter.
Figure9.8 Scnario de scurit classique avec JAAS.
Conteneur web ou AAC Principal authentifi par mot de passe Conteneur EJB

Authentifie

Autorise JAAS

Authentifie

Systme d'authentification

Comme le montre la Figure9.8, la scurit de Java EE repose largement sur lAPI JAAS. En fait, JAAS est lAPI utilise en interne par les couches web et EJB pour raliser les oprations dauthentification et dhabilitation. Elle accde galement aux systmes dauthentification sous-jacents comme LDAP (Lightweight Directory Access Protocol), Microsoft Active Directory, etc.

Chapitre 9

Transactions et scurit 301

Gestion de la scurit dans EJB


Le but principal du modle de scurit EJB est de contrler laccs au code mtier. Comme nous venons de le voir, lauthentification est prise en charge par la couche web (ou une application cliente); le principal et ses rles sont ensuite transmis la couche EJB et le service scurit du conteneur EJB vrifie si le rle dun utilisateur authentifi lautorise accder une mthode. Comme la gestion des transactions, celle des habilitations peut seffectuer de faon dclarative ou par programme. Dans le cas des habilitations dclaratives, le contrle des accs est assur par le conteneur EJB, tandis quavec les habilitations par programme cest le code qui sen charge en utilisant lAPI JAAS.
Scurit dclarative

La politique de scurit dclarative peut tre dfinie dans le bean laide dannotations ou dans le descripteur de dploiement XML. Elle consiste dclarer les rles, affecter des permissions aux mthodes (ou tout le bean) ou modifier temporairement une identit de scurit. Tous ces contrles seffectuent par les annotations du Tableau9.6, chacune delles pouvant porter sur une mthode et/ou sur le bean entier.
Tableau9.6: Annotation de scurit

Annotation
@PermitAll

Bean X

Mthode X

Description La mthode (ou tout le bean) est accessible par tout le monde (tous les rles sont autoriss). Aucun rle nest autoris excuter la mthode (tous les rles sont refuss).

@DenyAll

@RolesAllowed

X X

Donne la liste des rles autoriss excuter la mthode (ou tout le bean). Dfinit les rles pour la scurit.

@DeclareRoles @RunAs

Affecte temporairement un nouveau rle au principal.

302

Java EE 6 et GlassFish 3

INFO Les annotations @TransactionManagement et @TransactionAttribute que nous avons prsentes au dbut de ce chapitre sont dfinies dans le paquetage javax.ejb de la spcification EJB3.1 (JSR318). Les annotations de scurit (@RolesAllowed, @DenyAll, etc.) font partie de la spcification Common Annotations1.0 (JSR250) et proviennent du paquetage javax.annotation.security.

Lannotation @RolesAllowed sert autoriser une liste de rles accder une mthode. Elle peut sappliquer une mthode particulire ou lensemble du bean (toutes ses mthodes mtier hritent de cet accs). Elle peut prendre en paramtre un String unique (dsignant le seul rle autoris) ou un tableau de String (tous les rles habilits). Lannotation @DeclareRoles que nous tudierons plus tard permet de dclarer dautres rles. Dans le Listing9.7, ItemEJB utilise @RolesAllowed la fois au niveau du bean et des mthodes. Ce code indique que toutes les mthodes sont accessibles un principal associ aux rles utilisateur, employ ou admin. La mthode deleteBook(), en revanche, redfinit la configuration du bean pour nautoriser laccs quau rle admin.
Listing9.7: Bean sans tat autorisant certains rles
@Stateless @RolesAllowed({"utilisateur", "employ", "admin"}) public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; } @RolesAllowed("admin") public void deleteBook(Book book) { em.remove(em.merge(book)); } }

Chapitre 9

Transactions et scurit 303

Les annotations @PermitAll et @DenyAll sappliquent tous les rles: vous pouvez donc utiliser @PermitAll pour annoter un EJB ou une mthode particulire pour quils puissent tre invoqus par nimporte quel rle. Inversement, @DenyAll interdit laccs une mthode tous les rles. Comme vous pouvez le constater dans le Listing9.8, la mthode findBookById() est dsormais accessible nimporte quel rle, pas simplement utilisateur, employ ou admin. Par contre, la mthode findConfidentialBook() nest pas accessible du tout.
Listing9.8: Bean sans tat utilisant les annotations @PermitAll et @DenyAll
@Stateless @RolesAllowed({"utilisateur", "employ", "admin"}) public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @PermitAll public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; } @RolesAllowed("admin") public void deleteBook(Book book) { em.remove(em.merge(book)); } @DenyAll public Book findConfidentialBook(Long secureId){ return em.find(ConfidentialBook.class, id); }

Lannotation @DeclareRoles est lgrement diffrente car elle ne sert ni autoriser ni interdire un accs elle dclare des rles pour toute lapplication. Lorsque lEJB du Listing 9.8 est dploy, le conteneur dclare automatiquement les rles utilisateur, employ et admin en inspectant @RolesAllowed, mais vous pourriez vouloir dclarer dautres rles dans le domaine de scurit avec @DeclareRoles. Cette annotation, qui ne sapplique quau niveau dune classe, prend en paramtre un tableau de rles et les dclare dans le domaine. En fait, les rles peuvent donc tre dclars laide de lune de ces deux annotations ou de leur combinaison.

304

Java EE 6 et GlassFish 3

Lorsquelles sont utilises toutes les deux, cest lensemble des rles de @DeclareRoles et @RolesAllowed qui est dclar. Ceci dit, les rles tant gnralement dclars pour lensemble dune application dentreprise, il est plus judicieux de le faire dans le descripteur de dploiement quavec une annotation @DeclareRoles. Lorsque le bean ItemEJB du Listing9.9 est dploy, les cinq rles HR, deptVentes, utilisateur, employ et admin sont dclars. Puis, avec lannotation @RolesAllowed, certains dentre eux permettent daccder certaines mthodes.
Listing9.9: Bean sans tat dclarant des rles
@Stateless @DeclareRoles({"HR", "deptVentes"}) @RolesAllowed({"utilisateur", "employ", "admin"}) public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; public Book findBookById(Long id) { return em.find(Book.class, id); } public Book createBook(Book book) { em.persist(book); return book; } @RolesAllowed("admin") public void deleteBook(Book book) { em.remove(em.merge(book)); } }

La dernire annotation, @RunAs, permet daffecter temporairement un nouveau rle un principal. Ceci peut tre utile si, par exemple, on invoque un autre EJB depuis une mthode et que cet EJB exige un rle diffrent. Dans le Listing 9.10, par exemple, ItemEJB autorise laccs aux rles utilisateur, employ et admin. Lorsque lun de ces rles accde une mthode, celle-ci sexcute avec le rle temporaire deptStock (@RunAS("deptStock")), ce qui signifie que, lorsque la mthode createBook() est excute, InventoryEJB.addItem() sera invoque avec le rle deptStock.

Chapitre 9

Transactions et scurit 305

Listing9.10: Bean sans tat sexcutant avec un rle diffrent


@Stateless @RolesAllowed({"utilisateur", "employ", "admin"}) @RunAS("deptStock") public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @EJB private InventoryEJB inventory; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); } public Book createBook(Book book) { em.persist(book); inventory.addItem(book); return book; } }

Comme vous pouvez le constater, la scurit dclarative permet daccder de faon simple une politique dauthentification puissante. Mais comment faire si vous devez fournir une scurit spciale un utilisateur particulier ou appliquer une logique mtier en fonction du rle courant du principal? La rponse est la scurit par programmation.
Scurit par programmation

La scurit dclarative couvre la majeure partie de la scurit dune application. Cependant, on a parfois besoin dune finesse dhabilitation supplmentaire (pour autoriser un bloc de code au lieu de la mthode entire, pour autoriser ou interdire laccs une personne particulire, etc.). En ce cas, la gestion des habilitations par programmation permet dautoriser ou de bloquer slectivement laccs un rle ou un principal car on dispose alors dun accs direct linterface javax. security.Principal de JAAS et au contexte de lEJB pour vrifier le rle du principal dans le code. Linterface SessionContext dfinit les mthodes suivantes pour grer la scurit:
isCallerInRole()

teste si lappelant a le rle indiqu. renvoie le


java.security.Principal

getCallerPrincipal()

qui identifie

lappelant.

306

Java EE 6 et GlassFish 3

Pour comprendre lutilisation de ces mthodes, tudions le bean ItemEJB du Listing9.11: celui-ci nutilise aucune annotation de scurit mais doit quand mme faire certaines vrifications par programme. Le bean doit dabord obtenir une rfrence son contexte (via lannotation @Resource) qui permettra la mthode deleteBook() de vrifier si lappelant a le rle admin ou non. Sil ne la pas, la mthode lve java.lang.SecurityException pour prvenir lutilisateur dune violation des autorisations. La mthode createBook() effectue un traitement mtier en utilisant les rles et le principal: en se servant de la mthode getCallerPrincipal() pour obtenir lobjet Principal correspondant lappelant, elle peut vrifier quil sagit de paul et ajouter une valeur spciale lentit book.
Listing9.11: Bean utilisant une scurit par programmation
@Stateless public class ItemEJB { @PersistenceContext(unitName = "chapter09PU") private EntityManager em; @Resource private SessionContext ctx; public Book findBookById(Long id) { return em.find(Book.class, id); } public void deleteBook(Book book) { if (!ctx.isCallerInRole("admin")) throw new SecurityException("Admins uniquement"); em.remove(em.merge(book)); } public Book createBook(Book book) { if (ctx.isCallerInRole("employ") && !ctx.isCallerInRole("admin")) { book.setCreatedBy("Employs uniquement"); } else if (ctx.getCallerPrincipal().getName().equals("paul")){ book.setCreatedBy("Utilisateur spcial"); } em.persist(book); return book; } }

Chapitre 9

Transactions et scurit 307

Rsum
Dans ce dernier chapitre consacr aux EJB, nous avons vu comment grer les transactions et la scurit. Ces deux services trs importants peuvent tre dfinis de faon dclarative ou par programmation. Les transactions permettent la couche mtier de maintenir les donnes dans un tat cohrent, mme lorsque plusieurs applications y accdent de faon concurrente. Elles respectent les proprits ACID et peuvent tre distribues entre plusieurs ressources (bases de donnes, destinations JMS, services web, etc.). CMT permet de personnaliser aisment la dmarcation des transactions effectue par le conteneur EJB et vous pouvez influencer son comportement en marquant une transaction pour annulation en vous servant du contexte EJB ou des exceptions. Il est galement possible dutiliser BMT et JTA si vous avez besoin dun contrle plus fin sur la dmarcation des transactions. Concernant la scurit, noubliez pas que la couche mtier nauthentifie pas les utilisateurs : elle autorise des rles accder aux mthodes. La scurit dclarative seffectue au moyen dun nombre relativement rduit dannotations et permet de traiter la plupart des situations auxquelles sera confronte une application dentreprise. L aussi, vous pouvez utiliser une scurit par programmation et manipuler directement lAPI JAAS. Les trois chapitres qui suivent expliquent comment dvelopper une couche prsentation avec JSF. Les pages JSF utilisent des beans grs pour invoquer les mthodes mtiers des EJB.

10
JavaServer Faces
Pour afficher graphiquement les informations provenant du serveur, nous avons besoin dune interface utilisateur. Les applications utilisant des interfaces pour interagir avec lutilisateur sont de diffrents types: applications de bureau, applications web sexcutant dans un navigateur ou applications mobiles sur un terminal portable. Les Chapitres10 12 sont consacrs aux interfaces web. Initialement, le World Wide Web (WWW) tait un moyen de partager des documents crits en HTML (Hypertext Markup Language). Le protocole HTTP (Hypertext Transfer Protocol) a t conu pour vhiculer ces documents, qui taient lorigine essentiellement statiques (leur contenu nvoluait pas beaucoup au cours du temps). Les pages statiques sont composes de HTML pur contenant ventuellement des graphiques eux aussi statiques (JPG, PNG, par exemple). Les pages dynamiques sont en revanche composes en temps rel partir de donnes calcules partir des informations fournies par lutilisateur. Pour crer un contenu dynamique, il faut analyser les requtes HTTP, comprendre leur signification et crer des rponses dans un format que le navigateur saura traiter. LAPI des servlets simplifie ce processus en fournissant une vue oriente objet du monde HTTP (HttpRequest, HttpResponse, etc.). Cependant, le modle des servlets tait de trop bas niveau et cest la raison pour laquelle on utilise dsormais les JSP (JavaServer Pages) pour simplifier la cration des pages dynamiques. En coulisse, une JSP est une servlet, sauf quelle est crite essentiellement en HTML avec un peu de Java pour effectuer les traitements. JSF (JavaServer Faces, ou simplement Faces) a t cr en rponse certaines limitations de JSP et utilise un autre modle consistant porter des composants graphiques vers le Web. Inspir par le modle Swing et dautres frameworks graphiques, JSF permet aux dveloppeurs de penser en termes de composants, dvnements, de beans grs et de leurs interactions plutt quen termes de requtes, de

310

Java EE 6 et GlassFish 3

rponses et de langages marqueurs. Son but est de faciliter et dacclrer le dveloppement des applications web en fournissant des composants graphiques (comme les zones de texte, les listes, les onglets et les grilles) afin dadopter une approche RAD (Rapid Application Development). Ce chapitre est une introduction JSF ; les Chapitres 11 et 12 prsentent diffrentes technologies proposes par Java EE6 pour crer des interfaces web (JSP, EL et JSTL) et sintressent essentiellement JSF2.0, qui est la technologie la plus puissante et la plus adapte la cration dapplications web modernes en Java.

Introduction JSF
Lorsque lon connat dj des frameworks web, larchitecture de JSF est facile comprendre (voir Figure10.1). Les applications JSF sont des applications web classiques qui interceptent HTTP via la servlet Faces et produisent du HTML. En coulisse, cette architecture permet de greffer nimporte quel langage de dclaration de page (PDL), de lafficher sur des dispositifs diffrents (navigateur web, terminaux mobiles, etc.) et de crer des pages au moyen dvnements, dcouteurs et de composants, comme en Swing. Ce dernier est un toolkit graphique intgr Java depuis sa version1.6 cest un framework permettant de crer des applications de bureau (pas des applications web) laide de composants graphiques (widgets) et en utilisant le modle vnement-couteur pour traiter les entres des utilisateurs. JSF fournit galement un ensemble de widgets standard (boutons, liens hypertextes, cases cocher, zones de saisie, etc.) et facilite son extension par lajout de composants tiers. La Figure10.1 reprsente son architecture au niveau le plus abstrait.
Figure10.1
HTTP (Ajax) Rponse HTTP Faces Servlet Moteur de rendu XUL JSP XHTML Composant Composant Composant Validateur Convertisseur

Architecture de JSF.

Requte

Navigation

faces-config.xml (facultatif)

Bean gr

Chapitre 10

JavaServer Faces 311

Cette figure reprsente les parties importantes de JSF qui rendent cette architecture aussi riche et aussi souple:
FacesServlet

et faces-config.xml. FacesServlet est la servlet principale de lapplication et peut ventuellement tre configure par un fichier descripteur faces-config.xml. Pages et composants. JSF permet dutiliser plusieurs PDL (Presentation Description Language), comme JSP ou Facelets. Moteurs de rendu. Ils sont responsables de laffichage dun composant et de la traduction de la valeur saisie par lutilisateur en valeur pour le composant. Convertisseurs. Ils effectuent les conversions entre les valeurs de composants (Date, Boolean, etc.) et les valeurs de marqueurs (String), et rciproquement. Validateurs. Ils garantissent que la valeur saisie par lutilisateur est correcte. Bean gr et navigation. La logique mtier seffectue dans des beans grs (ou "manags") qui contrlent galement la navigation entre les pages. Support dAjax. Comme lexplique le Chapitre12, JSF 2.0 reconnat nativement Ajax.

FacesServlet et faces-config.xml

La plupart des frameworks web utilisent le patron de conception MVC (ModleVue-Contrleur) JSF ny fait pas exception. MVC permet de dcoupler la vue (la page) et le modle (les donnes affiches dans la vue). Le contrleur prend en charge les actions de lutilisateur qui pourraient impliquer des modifications dans le modle et dans les vues. Avec JSF, ce contrleur est la servlet FacesServlet. Toutes les requtes de lutilisateur passent par cette servlet, qui les examine et appelle les diffrentes actions correspondantes du modle en utilisant des beans grs.
FacesServlet

est intgre JSF et le seul moyen de la configurer consiste utiliser des mtadonnes externes. Jusqu JSF1.2, la seule source de configuration tait le fichier faces-config.xml. partir de JSF2.0, ce fichier est facultatif et la plupart des mtadonnes peuvent tre dfinies par des annotations (sur les beans grs, les convertisseurs, les composants, les moteurs de rendu et les validateurs).

Pages et composants

Le framework JSF doit envoyer une page sur le dispositif de sortie du client (un navigateur, par exemple) et exige donc une technologie daffichage appele PDL.

312

Java EE 6 et GlassFish 3

Une application JSF peut utiliser plusieurs technologies pour son PDL, comme JSP ou Facelets. Une implmentation conforme la spcification JSF2.0 doit inclure une implmentation complte de JSP, qui tait le PDL par dfaut de JSF 1.1 et JSF1.2 JSF2.0 lui prfre dsormais Facelets. JSP et Facelets sont tous les deux forms dune arborescence de composants (galement appels widgets ou contrles) fournissant des fonctionnalits spcifiques pour interagir avec lutilisateur (champs de saisie, boutons, listes, etc.). JSF dispose dun ensemble standard de composants et permet de crer facilement les vtres. Pour grer cette arborescence, une page passe par un cycle de vie complexe (initialisation, vnements, affichage, etc.). Le code du Listing 10.1 est une page Facelets en XHTML qui utilise les marqueurs JSF (xmlns:h="http:// java.sun.com/jsf/html") pour afficher un formulaire avec deux champs de saisie (lISBN et le titre dun livre) et un bouton. Cette page est compose de plusieurs composants JSF: certains nont pas dapparence visuelle, comme ceux qui dclarent len-tte (<h:head>), le corps (<h:body>) ou le formulaire (<h:form>). Dautres ont une reprsentation graphique et affichent un label (<h:outputLabel>), un champ de saisie (<h:inputText>) ou un bouton (<h:commandButton>). Vous remarquerez que lon peut galement utiliser des marqueurs HTML purs (<table>, <tr>, <hr/>, etc.) dans la page.
Listing10.1: Extrait dune page XHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Creates a new book</title> </h:head> <h:body> <h1>Create a new book</h1> <hr/> <h:form> <table border="0"> <tr> <td><h:outputLabel value="ISBN : "/></td> <td> <h:inputText value="#{bookController.book.isbn}"/> </td> </tr> <tr> <td><h:outputLabel value="Title :"/></td>

Chapitre 10

JavaServer Faces 313

<td> <h:inputText value="#{bookController.book.title}"/> </td> </tr> </table> <h:commandButton value="Create a book" action="#{bookController.doCreateBook}" styleClass="submit"/> </h:form> <hr/> <i>APress - Beginning Java EE 6</i> </h:body> </html>

Moteurs de rendu

JSF reconnat deux modles de programmation pour afficher les composants : limplmentation directe et limplmentation dlgue. Avec le modle direct, les composants doivent eux-mmes sencoder vers une reprsentation graphique et rciproquement. Avec le modle dlgu, ces oprations sont confies un moteur de rendu, ce qui permet aux composants dtre indpendants de la technologie daffichage (navigateur, terminal mobile, etc.) et donc davoir plusieurs reprsentations graphiques possibles. Un moteur de rendu soccupe dafficher un composant et de traduire la saisie dun utilisateur en valeur de composant. On peut donc le considrer comme un traducteur plac entre le client et le serveur: il dcode la requte de lutilisateur pour initialiser les valeurs du composant et encode la rponse pour crer une reprsentation du composant que le client pourra comprendre et afficher. Les moteurs de rendu sont organiss en kits de rendu spcialiss dans un type spcifique de sortie. Pour garantir la portabilit de lapplication, JSF inclut le support dun kit de rendu standard et les moteurs de rendu pour HTML4.01. Les implmentations de JSF peuvent ensuite crer leurs propres kits pour produire du WML (Wireless Markup Language), du SVG (Scalable Vector Graphics), etc.
Convertisseurs et validateurs

Lorsque la page est affiche, lutilisateur peut sen servir pour entrer des donnes. Comme il ny a pas de contraintes sur les types, un moteur de rendu ne peut pas prvoir laffichage de lobjet. Voil pourquoi les convertisseurs existent: ils traduisent

314

Java EE 6 et GlassFish 3

un objet (Integer, Date, Enum, Boolean, etc.) en chane afin quil puisse safficher et, inversement, construisent un objet partir dune chane qui a t saisie. JSF fournit un ensemble de convertisseurs pour les types classiques dans le paquetage javax. faces.convert, mais vous pouvez dvelopper les vtres ou ajouter des types provenant de tierces parties. Parfois, les donnes doivent galement tre valides avant dtre traites par le backend: cest le rle des validateurs; on peut ainsi associer un ou plusieurs validateurs un composant unique afin de garantir que les donnes saisies sont correctes. JSF fournit quelques validateurs (LengthValidator, RegexValidator, etc.) et vous permet den crer dautres en utilisant vos propres classes annotes. En cas derreur de conversion ou de validation, un message est envoy dans la rponse afficher.
Beans grs et navigation

Tous les concepts que nous venons de prsenter quest-ce quune page, quest-ce quun composant, comment sont-ils affichs, convertis et valids sont lis une page unique, mais les applications web sont gnralement formes de plusieurs pages et doivent raliser un traitement mtier (en appelant une couche EJB, par exemple). Le passage dune page une autre, linvocation dEJB et la synchronisation des donnes avec les composants sont pris en charge par les beans grs. Un bean gr est une classe Java spcialise qui synchronise les valeurs avec les composants, traite la logique mtier et gre la navigation entre les pages. On associe un composant une proprit ou une action spcifique dun bean gr en utilisant EL (Expression Language). Voici un extrait de lexemple prcdent:
<h:inputText value="#{bookController.book.isbn}"/> <h:commandButton value="Create" action="#{bookController.doCreateBook}"/>

La premire ligne lie directement la valeur du champ de saisie la proprit book. isbn du bean gr bookController. Cette valeur est synchronise avec la proprit du bean gr. Un bean gr peut galement traiter des vnements. La seconde ligne associe un bouton de soumission de formulaire une action : lorsquon aura cliqu sur ce bouton, celui-ci dclenchera un vnement sur le bean gr, qui excutera alors une mthode couteur (ici, la mthode doCreateBook()). Le Listing 10.2 contient le code du bean BookController. Cette classe Java est annote par @ManagedBean et possde une proprit, book, qui est synchronise avec

Chapitre 10

JavaServer Faces 315

la valeur du composant de la page. La mthode doCreateBook() invoque un EJB sans tat et renvoie une chane permettant de naviguer entre les pages.
Listing10.2: Le bean gr BookController
@ManagedBean public class BookController { @EJB private BookEJB bookEJB; private Book book = new Book(); public String doCreateBook() { book = bookEJB.createBook(book); return "listBooks.xhtml"; } // Getters, setters }

INFO Un bean gr est la classe qui agit comme un contrleur, navigue dune page lautre, appelle les EJB, etc. Les backing beans sont les objets qui contiennent les proprits lies aux composants. Dans cet exemple, nous pourrions donc dire que BookController est un bean gr et que lattribut book est le "backing bean".

Support dAjax

Une application web doit fournir une interface riche et rapide. Cette ractivit peut tre obtenue en ne modifiant que de petites parties de la page de faon asynchrone, et cest exactement pour cela quAjax a t conu. Les versions prcdentes de JSF noffraient pas de solution toute prte et des bibliothques tierces, comme a4jsf, sont donc venues combler ce manque. partir de JSF2.0, le support dAjax a t ajout sous la forme dune bibliothque JavaScript (jsf.js) dfinie dans la spcification. Le code suivant, par exemple, utilise la fonction request pour soumettre un formulaire de faon asynchrone:
<h:commandButton id="submit" value="Cration dun livre" onclick="jsf.ajax.request(this, event, {execute:isbn title price description nbOfPage illustrations, render:booklist}); return false;" actionListener="#{bookController.doCreateBook}" />

316

Java EE 6 et GlassFish 3

Rsum des spcifications de linterface web


Le dveloppement web en Java a commenc en 1996 avec lAPI servlet, un moyen trs rudimentaire de crer du contenu web dynamique. Il fallait alors manipuler une API HTTP de bas niveau (HttpServletRequest, HttpServletResponse, HttpSession, etc.) pour rendre les marqueurs HTML partir dun code Java. Les JSP sont apparues en 1999 et ont ajout un niveau dabstraction suprieur celui des servlets. En 2004, la premire version de JSF a vu le jour et sa version1.2 a t intgre Java EE5 en 2006. JSF2.0 fait dsormais partie de Java EE6.
Bref historique des interfaces web

Au dbut du Web, les pages taient statiques: un utilisateur demandait une ressource (une page, une image, une vido, etc.) et le serveur la lui renvoyait simple, mais trs limit. Avec laugmentation de lactivit commerciale sur le Web, les socits se sont trouves obliges de fournir du contenu dynamique leurs clients. La premire solution a donc consist utiliser CGI (Common Gateway Interface): en utilisant des pages HTML et des scripts CGI crits dans diffrents langages (allant de Perl Visual Basic), une application pouvait accder des bases de donnes et servir ainsi du contenu dynamique. Mais CGI tait de trop bas niveau (il fallait grer les en-ttes HTTP, appeler les commandes HTTP, etc.) et une solution plus labore semblait ncessaire. En 1995, Java fit son apparition avec une API dinterface utilisateur indpendante des plates-formes, appele AWT (Abstract Window Toolkit). Plus tard, avec Java SE1.2, AWT, qui reposait sur linterface utilisateur du systme dexploitation, fut remplac par lAPI Swing (qui dessine ses propres widgets en utilisant Java2D). Ds les premiers jours de Java, le navigateur Netscape Navigator proposa le support de ce nouveau langage, ce qui marqua le dbut de lre des applets des applications qui sexcutent sur le client, dans un navigateur. Les applets permettent dcrire des applications AWT ou Swing et de les intgrer dans une page web, mais leur utilisation ne dcolla jamais vraiment. De son ct, Netscape avait galement cr un langage de script appel JavaScript qui sexcutait directement dans le navigateur: malgr certaines incompatibilits entre les navigateurs, ce langage est toujours trs utilis actuellement car cest un moyen efficace de crer des applications web dynamiques. Aprs lchec des applets, Sun prsenta les servlets comme un moyen de crer des clients web dynamiques lgers. Les servlets taient une alternative aux scripts CGI

Chapitre 10

JavaServer Faces 317

car elles offraient une bibliothque de plus haut niveau pour grer HTTP, permettaient daccder toute lAPI de Java (ce qui incluait donc les accs aux bases de donnes, les appels distants, etc.) et pouvaient crer une rponse en HTML pouvant safficher chez le client. En 1999, Sun prsenta JSP comme une amlioration du modle des servlets mais, comme les JSP mlangeaient du code Java et du code HTML, un framework opensource, Struts, vit le jour en 2001 et proposa une nouvelle approche. Ce framework tendait lAPI des servlets et encourageait les dveloppeurs adopter une architecture MVC. Lhistoire rcente est remplie dautres frameworks tentant, chacun, de combler les lacunes des prcdents (Tapestry, Wicket, WebWork, DWR, etc.). Aujourdhui, le framework web conseill pour Java EE6 est JSF2.0, qui rivalise avec Struts et Tapestry dans le monde Java. Rails et Grails rivalisent un peu partout avec JSF, tout comme Java rivalise avec Ruby et Groovy. Par ailleurs, GWT (Google Web Toolkit), Flex et JavaFX peuvent tre complmentaires de JSF.
JSP 2.2, EL 2.2 et JSTL 1.2

Du point de vue de leur architecture, les JSP sont une abstraction de haut niveau des servlets et ont t implmentes comme une extension de Servlet2.1. JSP1.2 et Servlet2.3 ont t spcifies ensemble dans la JSR53 alors quen mme temps JSTL (JSP Standard Tag Library) faisait lobjet de la JSR52. Depuis 2002, la spcification JSP 2.0 a volu sparment dans la JSR 152. En 2006, JSP2.1 a t ajoute Java EE5 et a facilit lintgration entre JSF et JSP en introduisant un langage dexpressions (EL) unifi. Avec Java EE6, les spcifications de JSP et EL sont passes la version2.0 lune des principales modifications est quil est dsormais possible dinvoquer une mthode avec EL.
JSF 2.0

JSF est une spcification publie par le JCP (Java Community Process) et a t cre en 2001 par la JSR127. Sa version de maintenance1.1 est apparue en 2004 et ce nest quen 2006 que JSF1.2 a t ajoute dans JavaEE par la JSR252 (avec Java EE5). Le plus gros dfi de cette version consistait prserver la compatibilit ascendante et intgrer JSP avec un EL unifi. Malgr ces efforts, JSF et JSP ne fonctionnent pas trs bien ensemble et dautres frameworks comme Facelets ont donc t introduits pour fournir une alternative aux JSP.

318

Java EE 6 et GlassFish 3

JSF 2.0 est une version majeure (JSR 314) et est dsormais le choix conseill pour le dveloppement web avec Java EE6 (JSP reste maintenue, mais na reu aucune amlioration importante avec Java EE 6). JSF 2.0 sest inspir de nombreux frameworks web open-source et leur ajoute de nouvelles fonctionnalits.
Nouveauts de JSF 2.0

Avec ses nouvelles fonctionnalits, JSF 2.0 est une volution de 1.2, mais il va galement au-del les Facelets sont prfres JSP, par exemple. Parmi les ajouts de JSF 2.0, citons:

une autre technologie de prsentation que JSP, reposant sur Facelets; un nouveau mcanisme de gestion des ressources (pour les images, les scripts JavaScript, etc.); des portes supplmentaires (porte de vue et porte de composant); le dveloppement plus simple grce aux annotations pour les beans grs, les moteurs de rendu, les convertisseurs, les validateurs, etc.; la rduction de la configuration XML en exploitant les annotations et la configuration par exception (le fichier faces-config.xml est facultatif); le support dAjax; le dveloppement de composant facilit.

Implmentation de rfrence

Mojarra, qui est le nom dune famille de poissons des Carabes, est limplmentation de rfrence open-source de JSF2.0. Elle est disponible via les mises jour de GlassFishV3 et permet de dvelopper des applications web JSF2.0 en invoquant une couche mtier EJB3.1 et une couche de persistance JPA2.0. Cest elle que nous utiliserons dans notre rcapitulatif.

Rcapitulatif
Nous allons crire une petite application web proposant deux pages web : lune quiaffiche un formulaire afin de pouvoir crer un livre (newBook.xhtml), lautre qui

Chapitre 10

JavaServer Faces 319

numre tous les livres prsents dans la base (listBooks.xhtml). Ces deux pages utilisent le bean gr BookController pour stocker les proprits ncessaires et pour la navigation. En utilisant JPA pour la persistance et EJB pour la logique mtier, tout sembote: le bean gr dlgue tous les traitements mtier BookEJB, qui contient deux mthodes, lune pour stocker un livre dans une base de donnes (createBook()), une autre pour rcuprer tous les livres (findBooks()). Ce bean de session sans tat utilise lAPI EntityManager pour manipuler une entit Book. La navigation est trs simple: lorsquun livre est cr, on affiche la liste. Un lien sur la page de la liste permet de revenir ensuite la page newBook.xhtml et de crer un autre livre. La Figure10.2 montre linteraction des composants de cette application; ceux-ci assembls dans un fichier war et dploys sur une instance de GlassFish et une base de donnes Derby.
Figure10.2 Pages et classes impliques dans lapplication web.
<<entity>> Book
-id : Long -title : String -price : Float -description : String -nbOfPage : Integer -illustrations : Boolean

newBook .xhtml

<<managed bean>> BookController


+doList() : String +doCreateBook() : String

listBook .xhtml

<<stateless ejb>> BookEJB


-cm : EntityManager +findBooks() : List<Book> +createBook(book : Book) : Book

Cette application web utilisant la structure de rpertoires de Maven, les classes, les fichiers et les pages web doivent donc tre placs dans les rpertoires suivants:
src/main/java
BookController.

contient lentit

Book,

lEJB

BookEJB

et le bean gr utilis pour associer

src/main/resources src/webapp

contient le fichier lentit la base de donnes.

persistence.xml

contient les deux pages web newBook.xhtml et listBooks.xhtml. contient le fichier web.xml qui dclare la FacesServlet.

src/webapp/WEB-INF

pom.xml est un fichier POM (Project Object Model) de Maven dcrivant le projet,

ses dpendances et ses extensions.

320

Java EE 6 et GlassFish 3

Lentit Book

Nous ne dtaillerons pas beaucoup le Listing 10.3 car vous devriez maintenant comprendre le code de lentit Book. Outre les annotations de mapping, notez la requte nomme findAllBooks, qui permet de rcuprer les livres partir de la base de donnes.
Listing10.3: Entit Book avec une requte nomme
@Entity @NamedQuery(name = "findAllBooks", query = "SELECT b FROM Book b") public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; // Constructeurs, getters, setters }

Comme vous le savez dsormais, cette entit doit galement tre associe un fichier persistence.xml que, pour simplifier, nous ne reproduirons pas ici.
LEJB BookEJB

Le Listing10.4 reprsente un bean de session sans tat avec une vue sans interface, ce qui signifie que le client (cest--dire le bean gr) na pas besoin dinterface (locale ou distante) et peut invoquer directement lEJB. Ce dernier obtient par injection une rfrence un gestionnaire dentits grce auquel il peut rendre persistante une entit Book (avec la mthode createBook()) et rcuprer tous les livres de la base (avec la requte nomme findAllBooks). Cet EJB na besoin daucun descripteur de dploiement.
Listing10.4: EJB sans tat crant et rcuprant des livres
@Stateless public class BookEJB {

Chapitre 10

JavaServer Faces 321

@PersistenceContext(unitName = "chapter10PU") private EntityManager em; public List<Book> findBooks() { Query query = em.createNamedQuery("findAllBooks"); return query.getResultList(); } public Book createBook(Book book) { em.persist(book); return book; } }

Le bean gr BookController

Lun des rles dun bean gr consiste interagir avec les autres couches de lapplication (la couche EJB, par exemple) ou effectuer des validations. Dans le Listing10.5, BookController est un bean gr car il est annot par @ManagedBean. La seconde annotation, @RequestScoped, dfinit la dure de vie du bean: ici, il vivra le temps de la requte (on peut galement choisir dautres portes). Ce bean gr contient deux attributs qui seront utiliss par les pages:
bookList book

est la liste des livres rcuprs partir de la base de donnes, qui doit safficher dans la page listBooks.xhtml. est lobjet qui sera associ au formulaire (dans la page newBook.xhtml) et rendu persistant.

Tout le traitement mtier (cration et rcupration des livres) seffectue via BookEJB. Le bean gr obtient une rfrence lEJB par injection, via lannotation @EJB, et dispose de deux mthodes qui seront invoques par les pages:
doNew().

Cette mthode neffectue aucun traitement mais permet de naviguer vers newBook.xhtml. Comme nous le verrons au Chapitre12, il existe plusieurs moyens de naviguer de page en page: le plus simple consiste renvoyer le nom de la page cible.

doCreateBook().

Cette mthode permet de crer un livre en invoquant lEJB sans tat et en lui passant lattribut book. Puis elle appelle nouveau lEJB pour obtenir tous les livres de la base et stocke la liste dans lattribut bookList du bean gr. Ensuite, la mthode renvoie le nom de la page vers laquelle elle doit naviguer.

322

Java EE 6 et GlassFish 3

Le Listing10.5 contient le code de BookController. Pour plus de lisibilit, nous avons omis les getters et les setters, mais ils sont ncessaires pour chaque attribut (book et bookList).
Listing10.5: Le bean gr BookController qui invoque lEJB
@ManagedBean @RequestScoped public class BookController { @EJB private BookEJB bookEJB; private Book book = new Book(); private List<Book> bookList = new ArrayList<Book>(); public String doNew() { return "newBook.xhtml"; } public String doCreateBook() { book = bookEJB.createBook(book); bookList = bookEJB.findBooks(); return "listBooks.xhtml"; } // Getters, setters }

La page newBook.xhtml

La page newBook.xhtml du Listing 10.6 est un formulaire permettant lutilisateur de saisir les informations ncessaires la cration dun livre (ISBN, titre, prix, description, nombre de pages et illustrations).
Listing10.6: La page newBook.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Creates a new book</title> </h:head> <h:body> <h1>Create a new book</h1>

Chapitre 10

JavaServer Faces 323

<hr/> <h:form> <table border="0"> <tr> <td><h:outputLabel value="ISBN : "/></td> <td> <h:inputText value="#{bookController.book.isbn}"/> </td> </tr> <tr> <td><h:outputLabel value="Title :"/></td> <td> <h:inputText value="#{bookController.book.title}"/> </td> </tr> <tr> <td><h:outputLabel value="Price : "/></td> <td> <h:inputText value="#{bookController.book.price}"/> </td> </tr> <tr> <td><h:outputLabel value="Description : "/></td> <td><h:inputTextarea value="#{bookController.book.description}" cols="20" rows="5"/></td> </tr> <tr> <td><h:outputLabel value="Number of pages : "/></td> <td> <h:inputText value="#{bookController.book.nbOfPage}"/> </td> </tr> <tr> <td><h:outputLabel value="Illustrations : "/></td> <td><h:selectBooleanCheckbox value="#{bookController.book.illustrations}"/></td> <tr> </table> <h:commandButton value="Create a book" action="#{bookController.doCreateBook}"/> </h:form> <hr/> <i>APress - Beginning Java EE 6</i> </h:body> </html>

324

Java EE 6 et GlassFish 3

Comme le montre la Figure10.3, la plupart des informations sont entres dans des champs de saisie, sauf la description, qui utilise une zone de texte et les illustrations qui sont indiques par une case cocher.
Figure10.3 La page newBook.xhtml.

Create a new book


ISBN : Tiltle : Price :

Description :

Number of pages : Illustrations :

Create a book
APress Beginning Java EE 6

Un clic sur le bouton Create a book provoque lappel de la mthode doCreateBook() du bean gr et lEJB stocke alors le livre dans la base de donnes. Bien que ce code ait t simplifi, il contient lessentiel. Il dclare dabord lespace de noms h pour les composants HTML de JSF: pour les utiliser, il faudra donc les prfixer par cet espace de noms (<h:body>, <h:outputText>, <h:commandButton>, etc.). Le langage dexpressions EL permet ensuite de lier dynamiquement la valeur du composant la proprit correspondante du bean gr. Le code suivant, par exemple:
<h:inputText value="#{bookController.book.isbn}"/>

lie la valeur de lattribut isbn de book avec le contenu de ce composant inputText lors de la soumission du formulaire. bookController tant le nom par dfaut du bean gr, ce code est donc quivalent celui-ci:
bookController.getBook().setISBN("ce qui a t saisi")

La page utilise diffrents composants graphiques dont voici un bref rsum:


<h:form>

permet de crer un formulaire dont les valeurs seront envoyes au serveur lorsquil sera soumis.
value="ISBN : ")

<h:outputLabel>

affiche un label partir dune chane fixe (comme ou en liant un bean la proprit.

Chapitre 10

JavaServer Faces 325

<h:inputTextarea> affiche une zone de texte et lie sa valeur lattribut description

du livre.
<h:selectBooleanCheckbox>

affiche une case cocher et la lie lattribut illus-

trations (un Boolean).


<h:commandButton>

affiche un bouton de soumission de formulaire qui, lorsquon cliquera dessus, invoquera la mthode doCreateBook() du bean gr (action="#{bookController.doCreateBook}").

La page listBooks.xhtml

La mthode doCreateBook() du bean gr est appele lors du clic sur le bouton de soumission de la page newBook.xhtml (voir Figure10.3); elle stocke le livre dans la base et, si aucune exception na t lance, renvoie le nom de la page afficher ensuite, listBooks.xhtml, qui affiche tous les livres de la base (voir Figure10.4). Un lien sur cette page permet ensuite de revenir newBook.xhtml pour crer un autre livre.
Figure10.4 La page
listBooks.xhtml.
ISBN 564 694 Title Price 12.0 Description Scifi IT book Asimov Best seller Number Of Pages 241 317 529 Illustrations false true false 1234 234 H2G2 256 6 56 Dune Create a new book APress Beginning Java EE 6

List of the books

Robots 18.5

23.25 The trilogy

Le code de la page listBooks.xhtml (voir Listing10.7) utilise des composants diffrents, mais le principe est le mme que celui de la page prcdente. Le composant le plus important est celui qui affiche les donnes sous la forme dun tableau:
<h:dataTable value="#{bookController.bookList}" var="bk">

Llment <h:dataTable> est li lattribut bookList du bean gr (une ArrayList de livres) et dclare la variable bk qui permettra de parcourir cette liste. Dans cet lment, on peut ensuite utiliser des expressions comme #{bk.isbn} pour obtenir lattribut isbn dun livre. Chaque colonne du tableau est dfinie par un lment <h:column>. Le marqueur <h:commandLink> en bas de la page cre un lien qui, lorsquon clique dessus, appelle la mthode doNew() du bean gr (celle-ci permet de revenir la page newBook.xhtml).

326

Java EE 6 et GlassFish 3

Listing10.7: La page listBooks.xhtml


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>List of the books</title> </h:head> <h:body> <h1>List of the books</h1> <hr/> <h:dataTable value="#{bookController.bookList}" var="bk"> <h:column> <f:facet name="header"> <h:outputText value="ISBN"/> </f:facet> <h:outputText value="#{bk.isbn}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Title"/> </f:facet> <h:outputText value="#{bk.title}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Price"/> </f:facet> <h:outputText value="#{bk.price}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Description"/> </f:facet> <h:outputText value="#{bk.description}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Number Of Pages"/> </f:facet> <h:outputText value="#{bk.nbOfPage}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Illustrations"/>

Chapitre 10

JavaServer Faces 327

</f:facet> <h:outputText value="#{bk.illustrations}"/> </h:column> </h:dataTable> <h:form> <h:commandLink action="#{bookController.doNew}"> Create a new book </h:commandLink> </h:form> <hr/> <i>APress - Beginning Java EE 6</i> </h:body> </html>

Configuration avec web.xml

Les applications web sont gnralement configures laide dun descripteur de dploiement web.xml. Nous avons crit "gnralement" car ce fichier est devenu facultatif avec la nouvelle spcification Servlet3.0. Cependant, JSF2.0 reposant sur Servlet2.5 (et non sur Servlet3.0), nous devons quand mme dployer notre application web avec un descripteur. Les applications JSF ont besoin dune servlet nomme FacesServlet qui agit comme un contrleur frontal pour toute lapplication. Cette servlet et son association doivent tre dfinies dans le fichier web.xml, comme le montre le Listing10.8.
Listing10.8: Fichier web.xml dclarant une FacesServlet
<?xml version=1.0 encoding=UTF-8?> <web-app version="2.5" 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"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> </web-app>

328

Java EE 6 et GlassFish 3

Le descripteur de dploiement associe la servlet les requtes dURL se terminant par .faces, ce qui signifie que toute demande dune page se terminant par .faces sera traite par FacesServlet.
Compilation et assemblage avec Maven

Lapplication web doit tre compile et assemble dans un fichier war (<packaging>war </packaging>). Le fichier pom.xml du Listing10.9 dclare toutes les dpendances ncessaires la compilation du code (jsf-api, javax.ejb et javax.persistence) et prcise que cette compilation utilisera la version1.6 du JDK. Avec JSF2.0, le fichier faces-config.xml nest plus obligatoire et nous ne lutilisons pas ici.
Listing10.9: Fichier pom.xml de Maven pour compiler et assembler lapplication web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter10</artifactId> <packaging>war</packaging> <version>1.0</version> <dependencies> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins>

Chapitre 10

JavaServer Faces 329

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Pour compiler et assembler les classes, il suffit douvrir un interprteur en ligne de commande dans le rpertoire contenant le fichier pom.xml et dentrer la commande Maven suivante:
mvn package

Cette commande cre le fichier chapter10-1.0.war dans le rpertoire cible. Ouvrez-le et vous constaterez quil contient lentit Book, le bean BookEJB, le bean gr BookController, les deux descripteurs de dploiement (persistence.xml et web.xml) et les deux pages web (newBook.xhtml et listBooks.xhtml).
Dploiement dans GlassFish

Lapplication web assemble doit ensuite tre dploye dans GlassFish. Aprs avoir vrifi que Derby sexcute et coute sur son port par dfaut, ouvrez un interprteur en ligne de commande, placez-vous dans le rpertoire target contenant le fichier chapter10-1.0.war et entrez la commande suivante:
asadmin deploy chapter10-1.0.war

Si le dploiement russit, la commande qui suit devrait renvoyer le nom et le type de lapplication. Ici, il y a deux types: web car cest une application web et ejb car elle contient un EJB:
asadmin list-components chapter10-1.0 <ejb, web>

Excution de lapplication

Lorsque lapplication a t dploye, ouvrez votre navigateur et faites-le pointer vers lURL suivante:
http://localhost:8080/chapter10-1.0/newBook.faces

330

Java EE 6 et GlassFish 3

Le fichier point est newBook.faces, pas newBook.xhtml, car avec lextension .faces JSF sait quil doit traiter la page avant de lafficher (voir lassociation de.faces avec FacesServlet dans le Listing10.8). Lorsque la page newBook saffiche, saisissez les informations et cliquez sur le bouton denvoi du formulaire pour tre redirig sur la page listBooks.

Rsum
Aujourdhui, la comptition entre les interfaces utilisateurs continue de plus belle avec la prolifration des RDA (Rich Desktop Application), des RIA (Rich Internet Application), des applications pour terminaux mobiles, etc. JSF est entr dans la course il y a quelques annes dj et continue de tenir son rang grce aux nouvelles fonctionnalits de JSF2.0. Larchitecture de JSF repose sur des composants et une API riche permettant de dvelopper des moteurs de rendu, des convertisseurs, des validateurs, etc. Elle reconnat plusieurs langages, bien que le langage de dclaration de page (PDL) prfr de JSF2.0 soit Facelets. Les annotations ont t introduites avec JSF 2.0 et sont dsormais utilises dans la plupart des spcifications de Java EE6. Le Chapitre11 sintresse la partie prsentation de Java EE6 et couvre les spcifications JSP2.2, EL2.2 et JSTL1.2, il introduit galement Facelets et se concentre principalement sur JSF. Le Chapitre12 aborde tous les aspects dynamiques de la spcification. Vous y apprendrez le fonctionnement de la navigation, les beans grs et comment crire votre propre convertisseur et validateur.

11
Pages et composants
Nous vivons dans le monde de lInternet. Munis dun backend transactionnel qui traite des milliers de requtes et communique avec des systmes htrognes au moyen de services web, nous avons maintenant besoin dune couche de prsentation pour interagir avec les utilisateurs de prfrence une interface qui sexcute dans un navigateur car les navigateurs sont partout et parce que les interfaces web sont plus riches, plus dynamiques et plus simples utiliser. Les RIA (Rich Internet Applications) sont de plus en plus apprcies car les utilisateurs peuvent profiter de leur connaissance de leurs navigateurs: ils ont besoin de consulter des catalogues de livres et de CD, mais ils veulent galement accder au courrier lectronique et des documents, recevoir des notifications par courrier ou voir une partie de leur navigateur se mettre jour en fonction des informations reues du serveur. Ajoutons cela que la philosophie du Web2.0 est de faire partager toutes sortes dinformations des groupes damis qui peuvent interagir les uns avec les autres et lon comprend que les interfaces web soient de plus en plus compliques dvelopper. Aux premiers jours de Java, les dveloppeurs mettaient directement du HTML partir des servlets. Puis nous sommes passs des servlets JSP (Java Server Pages), qui utilise des marqueurs personnaliss. Dsormais, Java EE6 et sa nouvelle version de JSF simplifie encore plus le dveloppement des interfaces web. Dans ce chapitre, nous prsenterons diffrentes technologies utilises par Java EE6 pour crer des pages web. Nous expliquerons dabord quelques concepts de base comme HTML, CSS et JavaScript, puis nous passerons JSP, EL et JSTL. Nous introduirons alors Facelets, le langage de prsentation (PDL) conseill pour JSF. Le reste du chapitre sintressera la cration dinterfaces web avec JSF ou des composants personnaliss. Le chapitre suivant expliquera comment naviguer entre les pages et interagir avec un backend pour afficher des donnes dynamiques.

332

Java EE 6 et GlassFish 3

Pages web
Lorsque lon cre une application web, on affiche gnralement un contenu dynamique: une liste darticles dun catalogue (des CD et des livres, par exemple), les informations associes un identifiant de client, un panier virtuel contenant lesarticles que lutilisateur veut acheter, etc. Inversement, le contenu statique, comme ladresse dun diteur et les FAQ expliquant comment acheter ou se faire livrer les articles, change rarement, voire jamais ce contenu peut galement tre une image, une vido ou un dessin. Le but ultime de la cration dune page est son affichage dans un navigateur. Elle doit donc utiliser les langages compris par les navigateurs: HTML, XHTML, CSS et JavaScript.
HTML

Hypertext Markup Language (HTML) est le langage qui prdomine dans les pages web. Il repose sur SGML (Standard Generalized Markup Language), un mtalangage standard permettant de dfinir des langages marqueurs. HTML utilise des balises, ou marqueurs, pour structurer le texte en paragraphes, listes, liens, boutons, zones de texte, etc. Une page HTML est un document texte utilis par les navigateurs pour prsenter du texte et des images: ce sont des fichiers texte portant souvent lextension .html ou .htm. Une page web est forme dun contenu, de marqueurs permettant de changer certains aspects de ce contenu et dobjets externes comme des images, des vidos, du code JavaScript ou des fichiers CSS. La section "Rcapitulatif" du chapitre prcdent a montr deux pages JSF, dont lune affichait un formulaire pour crer un nouveau livre. Le Listing11.1 montre cette page crite en HTML pur, sans utiliser aucun marqueur JSF.
Listing11.1: La page newBook.html
<h1>Create a new book</h1> <hr> <table border=0> <TR> <TD>ISBN :</TD> <TD><input type=text/></td> </tr> <tr>

Chapitre 11

Pages et composants 333

<td>Title :</td> <td><input type=text/></td> </tr> <tr> <td>Price :</td> <td><input type=text/></td> </tr> <tr> <td>Description : <td><textarea name=textarea cols=20 rows=5></textarea> </tr> <TR> <TD>Number of pages : <td><input type=text/> </tr> <tr> <td>Illustrations : <td><input type=checkbox/> </tr> </table> <input type=submit value=Create> <hr> <i>APress - Beginning Java EE 6</i>

Normalement, une page HTML valide commence par un marqueur <html> qui agit comme un conteneur du document. Il est suivi des marqueurs <head> et <body>. Ce dernier contient la partie visible ici, un tableau constitu de labels et de champs de saisie, et un bouton. Comme vous pouvez le constater, le fichier newBook.html du Listing11.1 ne respecte pas ces rgles mais les navigateurs peuvent afficher des pages HTML non valides (jusqu un certain point). Le rsultat affich ressemblera donc la Figure11.1.
Figure11.1 Reprsentation graphique de la page newBook.html.

Create a new book


ISBN : Tiltle : Price :

Description :

Number of pages : Illustrations :

Create
APress Beginning Java EE 6

334

Java EE 6 et GlassFish 3

La reprsentation graphique de la Figure11.1 est celle que lon attendait; pourtant, le Listing11.1 nest pas correctement format en termes de XML:

La page na pas de marqueurs <html>, <head> ou <body>. Le marqueur <input


type=submit value=Create>

nest pas ferm.

Les marqueurs mlangent les majuscules et les minuscules (<TR> et </tr> apparaissent dans le code).

La plupart des navigateurs autorisent ce type derreur et afficheront correctement le formulaire. En revanche, si vous voulez traiter ce document avec des parsers XML, par exemple, le traitement chouera. Pour en comprendre la raison, tudions une page web qui utilise une structure XML stricte avec XHTML (eXtensible Hypertext Markup Language).
XHTML

XHTML a t cr peu de temps aprs HTML 4.01. Ses racines puisent dans HTML, mais avec une reformulation en XML strict. Ceci signifie quun document XHTML est un document XML qui respecte un certain schma et peut tre reprsent graphiquement par les navigateurs un fichier XHTML (qui porte lextension .xhtml) peut tre directement utilis comme du XML ou tre affich dans un navigateur. Par rapport HTML, il a lavantage de permettre une validation et une manipulation du document laide doutils XML standard (XSL ou eXtensible Stylesheet Language; XSLT ou XSL Transformations; etc.). Le Listing11.2 montre la version XHTML de la page web du Listing11.1.
Listing11.2: La page newBook.xhtml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Creates a new book</title> </head> <body> <h1>Create a new book</h1> <hr/> <table border="0"> <tr>

Chapitre 11

Pages et composants 335

<td>ISBN :</td> <td><input type="text"/></td> </tr> <tr> <td>Title :</td> <td><input type="text"/></td> </tr> <tr> <td>Price :</td> <td><input type="text"/></td> </tr> <tr> <td>Description :</td> <td><textarea name="textarea" cols="20" rows="5"></textarea></td> </tr> <tr> <td>Number of pages :</td> <td><input type="text"/></td> </tr> <tr> <td>Illustrations :</td> <td><input type="checkbox"/></td> </tr> </table> <input name="" type="submit" value="Create"/> <hr/> <i>APress - Beginning Java EE 6</i> </body> </html>

Notez les diffrences entre les Listings 11.1 et 11.2: ce dernier respecte une structure stricte et contient les marqueurs <html>, <head> et <body>; tous les marqueurs sont ferms, mme les vides (chaque <td> est ferm et on utilise <hr/> au lieu de <hr>); les valeurs des attributs sont toujours entre apostrophes ou entre guillemets (<table border="0"> ou <table border=0>, mais pas <table border=0>); tous les marqueurs sont en minuscules (<tr> au lieu de <TR>). Le respect strict des rgles syntaxiques de XML et les contraintes de schma rendent XHTML plus facile maintenir et traiter que HTML, et cest la raison pour laquelle il est dsormais le langage prfr pour les pages web.
CSS

Les navigateurs utilisent des langages ct client comme HTML, XHTML, CSS et JavaScript. CSS (Cascading Style Sheets) sert dcrire la prsentation dun document crit en HTML ou en XHTML. Il permet de dfinir les couleurs, les polices, la

336

Java EE 6 et GlassFish 3

disposition et les autres aspects de la prsentation dun document et, donc, de sparer son contenu (crit en XHTML) de sa prsentation (crite en CSS). Comme HTTP, HTML et XHTML, les spcifications de CSS sont dictes par le W3C (World Wide Web Consortium). Supposons, par exemple, que vous vouliez modifier les labels de la page newBook. xhtml pour quils soient tous en italique (font-style: italic;), de couleur bleue (color: #000099;) et dans une taille de police plus grande (font-size: 22px;). Au lieu de rpter ces modifications pour chaque marqueur, il suffit de dfinir un style CSS (dans un marqueur <style type="text/css">) et de lui donner un alias (title et row, par exemple): la page appliquera alors ce style pour tous les lments qui utilisent cet alias afin de modifier leur prsentation (<h1 class="title">).
Listing11.3: La page newBook.xhtml avec des styles CSS
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Creates a new book</title> <style type="text/css"> .title { font-family: Arial, Helvetica, sans-serif; font-size: 22px; color: #000099; font-style: italic; } .row { font-family: Arial, Helvetica, sans-serif; color: #000000; font-style: italic; } </style> </head> <body> <h1 class="title">Create a new book</h1> <hr/> <table border="0"> <tr> <td class="row">ISBN :</td> <td><input type="text"/></td> </tr> <tr> <td class="row">Title :</td> <td><input type="text"/></td> </tr> <tr> <td class="row">Price :</td>

Chapitre 11

Pages et composants 337

<td><input type="text"/></td> </tr> <tr> <td class="row">Description :</td> <td><textarea name="textarea" cols="20" rows="5"></textarea></td> </tr> <tr> <td class="row">Number of pages :</td> <td><input type="text"/></td> </tr> <tr> <td class="row">Illustrations :</td> <td><input type="checkbox"/></td> </tr> </table> <input name="" type="submit" value="Create"/> <hr/> <i>APress - Beginning Java EE 6</i> </body> </html>

Dans cet exemple, le code CSS est intgr la page XHTML mais, dans une vraie application, tous les styles seraient placs dans un fichier distinct qui serait import par la page web. Le webmestre peut ainsi crer un ou plusieurs fichiers CSS pour diffrents groupes de pages et les contributeurs de contenu peuvent crire ou modifier leurs pages sans tre concerns par laspect final de leurs documents. la Figure 11.2, tous les labels sont dsormais en italique et le titre de la page apparat en bleu.
Figure11.2 Reprsentation graphique de la page newBook.xhtml.

Create a new book


ISBN : Tiltle : Price :

Description :

Number of pages : Illustrations :

Create
APress Beginning Java EE 6

338

Java EE 6 et GlassFish 3

DOM

Une page XHTML est un document XML et a donc une reprsentation DOM (Document Object Model). DOM est une spcification du W3C pour accder et modifier le contenu et la structure des documents XML ainsi quune API abstraite pour interroger, parcourir et manipuler ce type de document il peut tre considr comme une reprsentation arborescente de la structure dun document. La Figure11.3 montre une reprsentation DOM de la page newBook.xhtml: la racine est le marqueur html, ses deux fils sont head et body et ce dernier a lui-mme un fils table avec une liste de fils tr.
Figure11.3 Reprsentation arborescente de la page newBook.xhtml.

DOM fournit un moyen standard dinteraction avec les documents XML. Grce lui, vous pouvez parcourir larbre dun document et modifier le contenu dun nud. Moyennant un peu de code JavaScript, il est possible dajouter un comportement dynamique une page web. Comme nous le verrons au chapitre suivant, Ajax utilise JavaScript sur la reprsentation DOM dune page.
JavaScript

Les langages que nous avons voqus jusqu maintenant permettent de reprsenter le contenu statique et les aspects graphiques dune page web. Cependant, une page doit souvent interagir avec lutilisateur en affichant du contenu dynamique. Ce contenu dynamique peut tre trait par des technologies ct serveur comme JSP ou JSF, mais les navigateurs peuvent galement en produire de leur ct en excutant du code JavaScript. JavaScript est un langage de script pour le dveloppement web ct client. Contrairement ce que son nom pourrait laisser supposer, il na rien voir avec le langage

Chapitre 11

Pages et composants 339

de programmation Java car cest un langage interprt et faiblement typ. Avec JavaScript, il est possible de crer des applications web dynamiques en crivant des fonctions qui agissent sur le DOM dune page. Il a t standardis par lECMA (European Computer Manufacturers Association) sous le nom dECMAScript. Toute page crite en respectant les standards XHTML, CSS et JavaScript devrait safficher et se comporter de faon quasiment identique avec tout navigateur respectant ces normes. Le Listing11.4 contient un exemple de code JavaScript manipulant le DOM de la page newBook.xhtml qui affiche un formulaire permettant de saisir des informations sur un livre. Le prix du livre doit tre fourni par lutilisateur ct client avant datteindre le serveur: une fonction JavaScript (priceRequired()) permet de valider ce champ en testant sil est vide ou non.
Listing11.4: La page newBook.xhtml avec du JavaScript
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Creates a new book</title> <script type="text/JavaScript"> function priceRequired() { if (document.getElementById("price").value == "") { document.getElementById("priceError").innerHTML = "Please, fill the price !"; } </script> </head> <body> <h1>Create a new book</h1> <hr/> <table border="0"> <tr> <td>ISBN :</td> <td><input type="text"/></td> </tr> <tr> <td>Title :</td> <td><input type="text"/></td> </tr> <tr> <td>Price :</td> <td><input id="price" type="text" onblur="JavaScript:priceRequired()"/> <span id="priceError"/> </td> </tr> <tr> <td>Description :</td>

340

Java EE 6 et GlassFish 3

<td><textarea name="textarea" cols="20" rows="5"></textarea></td> </tr> <tr> <td>Number of pages :</td> <td><input type="text"/></td> </tr> <tr> <td>Illustrations :</td> <td><input type="checkbox"/></td> </tr> </table> <input name="" type="submit" value="Create"/> <hr/> <i>APress - Beginning Java EE 6</i> </body> </html>

Dans le Listing11.4, la fonction priceRequired() est intgre dans la page au moyen dun marqueur <script> et est appele lorsque le champ de saisie du prix perd le focus (reprsent par lvnement onblur). Elle utilise lobjet document implicite qui reprsente le DOM du document XHTML. Lappel getElementById("price") recherche un lment ayant un identifiant price (<input id="price">): on rcupre sa valeur et lon teste si elle est vide. Si cest le cas, la fonction recherche un autre lment appel priceError (getElementById("priceError")) et fixe sa valeur "Please, fill the price !". Cette procdure de validation affichera donc le message de la Figure11.4 si le prix na pas t indiqu.
Figure11.4 La page newBook.html affiche un message derreur.

Create a new book


ISBN : Tiltle : Price : Please, fill the price !

Description :

Number of pages : Illustrations :

Create
APress Beginning Java EE 6

JavaScript est un langage puissant: nous nen avons prsent quune petite partie pour montrer son interaction avec DOM mais il est important de comprendre quune

Chapitre 11

Pages et composants 341

fonction JavaScript peut accder un nud de la page (par son nom ou son identifiant) et modifier dynamiquement son contenu ct client. Nous donnerons plus de dtails dans la section "Ajax" du prochain chapitre.

Java Server Pages


Nous venons de prsenter des technologies et des langages, comme XHTML ou CSS, qui reprsentent le contenu et laspect visuel dune page web. Pour ajouter de linteractivit et modifier dynamiquement des parties dune page, vous pouvez utiliser des fonctions JavaScript qui sexcuteront dans le navigateur mais, la plupart du temps, vous devrez faire appel une couche mtier dEJB pour afficher des informations provenant dune base de donnes. Ce contenu dynamique peut tre obtenu laide de JSP (ou JSF avec JSP ou Facelets, comme nous le verrons plus loin). JSP a t ajout J2EE 1.2 en 1999 et permet de crer dynamiquement des pages web en rponse une requte dun client. Les pages sont traites sur le serveur et compiles sous forme de servlets. Les pages JSP ressemblent des pages HTML ou XHTML, sauf quelles contiennent des marqueurs spciaux pour effectuer des traitements sur le serveur et appeler du code Java ct serveur. La plupart du travail de JSP repose sur lAPI servlet. Les servlets ont t cres pour permettre un serveur daccepter des requtes HTTP des clients et de crer des rponses dynamiques. Comme JSP, elles peuvent se servir de nimporte quelle ressource serveur comme les EJB, les bases de donnes, les services web et dautres composants. Les JSP sont dynamiques parce quelles excutent du code Java pour former une rponse en fonction dune requte. Les JSP sexcutent sur un serveur dans un conteneur de servlets et rpondent aux requtes des clients, qui sont des utilisateurs accdant une application web au moyen dun navigateur via HTTP, le mme protocole que celui quils utilisent pour demander des pages XHTML au serveur. Le conteneur de servlets gre le cycle de vie dune JSP en:

compilant le code JSP dans une servlet; chargeant et initialisant la JSP; traitant les requtes des clients et les faisant suivre la JSP;

342

Java EE 6 et GlassFish 3

renvoyant les rponses aux clients (ces rponses ne contiennent que des marqueurs HTML ou XHTML pour pouvoir safficher dans un navigateur); dchargeant la JSP et arrtant de lui envoyer des requtes (lorsque le serveur sarrte, par exemple).

Une page JSP pouvant produire du code HTML ou XHTML, vous pouvez utiliser des extensions diffrentes pour lindiquer .jsp pour HTML et .jspx pour XHTML, par exemple. Examinons le code suivant:
<html> <head> <title>Lists all the books</title> </head> <body> <h1>Lists all the books</h1> <hr/> </body> </html>

Comme vous pouvez le constater, une JSP valide peut ne contenir que des marqueurs HTML: vous pourriez sauvegarder ce code dans un fichier listBooks.jsp et le dployer dans un conteneur de servlets qui renverrait alors une simple page HTML. En fait, une page JSP ressemble du HTML, mais elle peut galement contenir des marqueurs supplmentaires qui permettent dajouter du contenu dynamique afin que les rponses produites dpendent des requtes. La spcification JSP dfinit les lments suivants:

directives; scripts; actions.

Comme nous le verrons, il existe deux syntaxes pour ces lments: la syntaxe XML pour les pages XHTML (<jsp:directive attributs/>) et la syntaxe JSP, qui nest pas conforme XML (<%@ attributs %>).
Directives

Les directives fournissent des informations sur la JSP et ne produisent rien. Il existe trois directives: page, include et taglib. Les deux syntaxes possibles sont:
<%@ directive attributs %> <jsp:directive attributs />

Chapitre 11

Pages et composants 343

La directive page sert indiquer les attributs de page tels que le langage de programmation de la page (Java, ici), le type MIME, lencodage des caractres de la rponse, si la JSP est une page derreur, etc.
<%@ page contentType=" text/html; ISO-8859-1" language="java" %> <jsp:directive.page contentType="text/html; ISO-8859-1" language="java"/>

La directive include sert inclure une autre page (HTML, XHTML ou JSP) dans la page courante. Vous pouvez lutiliser pour inclure une page standard (un en-tte ou un pied de page, par exemple) dans plusieurs JSP.
<%@ include file="header.jsp"%> <jsp:directive.include file="header.jsp" />

La section "Bibliothque des marqueurs JSP standard" montre que lon peut tendre les JSP laide dune bibliothque de marqueurs. La directive taglib dclare quune page utilise lune de ces bibliothques en lidentifiant de faon unique par une URI et un prfixe. Avec la syntaxe XML, ces deux informations sont regroupes dans un espace de noms unique (xmlns). Dans lexemple suivant, la bibliothque de marqueurs http://java.sun.com/jstl/core est disponible pour la page via le prfixe c:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:root xmlns:c="http://java.sun.com/jstl/core">

Scripts

Les scripts incluent du code Java permettant de manipuler des objets et deffectuer des traitements affectant le contenu. Ils peuvent utiliser les deux syntaxes suivantes:
<%! dclaration %> <jsp:declaration>ceci est une dclaration</jsp:declaration> <% scriptlet %> <jsp:scriptlet>ceci est un scriptlet</jsp:scriptlet> <%= expression %> <jsp:expression>ceci est une expression</jsp:expression>

Les dclarations permettent de dclarer les variables ou les mthodes qui seront disponibles pour tous les autres scripts de la page. La dclaration napparat que dans la JSP traduite (cest--dire dans la servlet), pas dans ce qui est envoy au client. Le code suivant, par exemple, dclare une instance dArrayList qui sera globale toute la page:
<%! ArrayList books = new ArrayList(); %> <jsp:declaration> ArrayList books = new ArrayList(); </jsp:declaration>

344

Java EE 6 et GlassFish 3

Les scriptlets contiennent du code Java permettant de dcrire les actions raliser en rponse aux requtes. Ils peuvent servir effectuer des itrations ou excuter conditionnellement dautres lments de la JSP. Comme les dclarations, le code dun scriptlet napparat que dans la JSP traduite (la servlet). Le code suivant, par exemple, ajoute un objet Book lArrayList dclare plus haut:
<% books.add(new Book("H2G2", 12f, "Scifi IT book", "1234-234", 241, true)); %> <jsp:scriptlet> books.add(new Book("H2G2", 12f, "Scifi IT book", "1234-234", 241, true)); </jsp:scriptlet>

Les expressions servent envoyer la valeur dune expression Java au client. Elles sont values au moment de la rponse et leur rsultat est converti en chane de caractres puis insr dans le flux affich par le navigateur. Le fragment de code suivant, par exemple, affichera lISBN dun livre:
<%= book.getIsbn()%> <jsp:expression>book.getIsbn()</jsp:expression>

Les dclarations, les scriptlets et les expressions doivent contenir du code Java correct. Si vous choisissez dutiliser la syntaxe XML, leur contenu doit galement tre du XML valide. Le code suivant, par exemple, dclare une ArrayList de livres en utilisant une classe gnrique:
<%! ArrayList<Book> books = new ArrayList<Book>(); %>

Si vous voulez faire la mme dclaration dans un format XML strict, vous ne pouvez pas utiliser les symboles < et > car ils sont rservs louverture et la fermeture des marqueurs XML. Vous devez donc utiliser une section CDATA (qui signifie Character DATA) afin que le parser XML ne tente pas de lanalyser:
<jsp:declaration><![CDATA[ ArrayList<Book> books = new ArrayList<Book>(); ]]></jsp:declaration>

Actions

Les actions standard sont dfinies par la spcification JSP et forcent la page effectuer certaines actions (inclure des ressources externes, faire suivre une requte vers une autre page ou utiliser les proprits dobjets Java). Elles ressemblent des marqueurs HTML car elles sont reprsentes par des lments XML prfixs par jsp

Chapitre 11

Pages et composants 345

(<jsp:useBean>, <jsp:include>, etc.). Le Tableau11.1 numre toutes les actions disponibles.


Tableau11.1: lments des actions JSP

Action
useBean setProperty getProperty include forward param

Description Associe une instance dobjet une porte donne et un identifiant. Fixe la valeur dune proprit dun bean. Affiche la valeur dune proprit dun bean. Permet dinclure des ressources statiques et dynamiques dans le mme contexte que celui de la page courante. Fait suivre la requte courante une ressource statique, une JSP ou une servlet dans le mme contexte que celui de la page courante. Utilis avec les lments include, forward et params. La page incluse ou transfre verra lobjet requte initial avec les paramtres originaux, plus les nouveaux. Permet une JSP de produire du HTML contenant des constructions spcifiques au navigateur (OBJECT ou EMBED), qui provoquera le tlchargement dune extension. Passe des paramtres. Fait partie de laction plugin. Dfinit dynamiquement la valeur du marqueur dun lment XML. Dfinit un attribut XML. Fait partie de laction element. Dfinit le corps dun lment XML. Fait partie de laction element.

plugin

params element attribute body

Rcapitulatif

Tous ces lments permettent dinvoquer du code Java et toutes sortes de composants (EJB, bases de donnes, services web, etc.). titre dexemple, nous allons crer une page qui affichera une liste de livres stocks dans une ArrayList. Ici, nous naccderons pas une base de donnes: nous nous contenterons dune ArrayList initialise avec un nombre dtermin dobjets Book, que nous parcourrons pour afficher les attributs de chaque livre (ISBN, titre, description, etc.). La Figure11.5 montre le rsultat attendu.

346

Java EE 6 et GlassFish 3

Figure11.5 La page listBooks.jsp affiche une liste de livres.

List of the books


ISBN 56 694 Title Price 12.0 Description Scifi IT book Best seller Number Of Pages 241 317 529 Illustrations true true true 1234 234 H2G2 256 6 56 Dune

Robots 18.5

23.25 The trilogy

APress Beginning Java EE 6

Nous avons besoin de plusieurs lments pour construire cette page. Comme le montre le Listing 11.5, il faut importer les classes java.util.ArrayList et Book avec une directive (<%@ page import="java.util.ArrayList" %>). Puis on dclare un attribut books, instance dArrayList, afin quil soit accessible toute la page (<%! ArrayList<Book> books = new ArrayList<Book>(); %>). Ensuite, un scriplet ajoute des objets livres dans une ArrayList et un autre parcourt cette liste avec une instruction for. Pour afficher les attributs de chaque livre, nous utilisons des lments expression (<%= book. getTitle()%>). Le Listing11.5 prsente le code complet de cette page.
Listing11.5: La page listBooks.jsp
<%@ page import="com.apress.javaee6.chapter11.Book" %> <%@ page import="java.util.ArrayList" %> <%! ArrayList<Book> books = new ArrayList<Book>(); %> <html> <head> <title>List all the books</title> </head> <body> <h1>Lists all the books</h1> <hr/> <% books.add(new Book("H2G2", 12f, "Scifi IT book", "1234-234", 241, true)); books.add(new Book("Robots", 18.5f, "Best seller", "564-694", 317, true)); books.add(new Book("Dune", 23.25f, "The trilogy", "256-6-56", 529, true)); %> <table border="1"> <tr> <td>ISBN</td> <td>Title</td> <td>Price</td> <td>Description</td> <td>Number of pages</td> <td>Illustrations</td>

Chapitre 11

Pages et composants 347

</tr> <% Book book; for (int i = 0; i < books.size(); i++) { book = books.get(i); %> <tr> <td><%= book.getIsbn()%></td> <td><%= book.getTitle()%></td> <td><%= book.getPrice()%></td> <td><%= book.getDescription()%></td> <td><%= book.getNbOfPage()%></td> <td><%= book.getIllustrations()%></td> </tr> <% } // fin de linstruction for %> </table> <hr/> <i>APress - Beginning Java EE 6</i> </body> </html>

Vous remarquerez que lon peut librement entrelacer du code Java, des marqueurs HTML et du texte. Tout ce qui est dans un scriptlet (entre <% et %>) est du code Java qui sera excut sur le serveur et tout ce qui est lextrieur est du texte qui sera affich dans la page de rponse. Notez galement que le bloc de linstruction for commence et se termine dans des scriptlets diffrents. Une JSP peut donc rapidement devenir difficile relire si lon commence trop mlanger des marqueurs HTML avec du code Java. En outre, il ny a pas de sparation entre la logique mtier et la prsentation, ce qui complique la maintenance des pages car on mlange deux langages destins deux catgories dintervenants: Java pour les dveloppeurs mtiers et XHTML/CSS pour les concepteurs web. Les JSP peuvent utiliser des bibliothques de marqueurs et le langage dexpressions (EL). JSTL (JSP Standard Tag Library) standardise un certain nombre dactions classiques en utilisant un langage marqueurs familier pour les dveloppeurs web, tandis quEL utilise une syntaxe plus simple pour effectuer certaines actions des scripts JSP.

Langage dexpressions (EL)


Nous venons de voir comment utiliser des scripts pour intgrer du code dans une page JSP. Les instructions EL fournissent une syntaxe plus simple pour effectuer des actions similaires et elles sont plus faciles utiliser pour ceux qui ne dveloppent pas en Java. Elles permettent dafficher les valeurs des variables ou daccder aux

348

Java EE 6 et GlassFish 3

attributs des objets et disposent dun grand nombre doprateurs mathmatiques, logiques et relationnels. La syntaxe de base dune instruction EL est de la forme:
${expr}

o expr est une expression valide qui est analyse et interprte. partir de JSP2.1, vous pouvez galement utiliser la syntaxe suivante:
#{expr}

Avec les JSP, ${expr} et #{expr} seront analyses et interprtes exactement de la mme manire. Avec JSF, en revanche, leur traitement sera diffrent car, comme nous le verrons, le cycle de vie des pages JSF nest pas le mme que celui des JSP. ${expr} sera value immdiatement (lexpression est compile en mme temps que la JSP et nest value quune fois: lorsque la JSP sexcute) alors que #{expr} est value plus tard (lorsque sa valeur sera ncessaire). Ces deux EL ayant t unifis, une page JSP et une page JSF peuvent utiliser les deux moyennant les diffrences de leurs cycles de vie. Les expressions EL peuvent utiliser la plupart des oprateurs Java habituels: arithmtiques, +, -, *, / (div), % (mod);

relationnels, == logiques, &&

(eq),!= (ne), < (lt), > (gt), <= (le), >= (ge);

(and), || (or),! (not);

autres, (), empty, [], ..

Notez que certains oprateurs ont la fois une forme symbolique et une forme littrale (> est quivalent gt, / div, etc.), ce qui permet de rendre une JSP conforme XML sans avoir besoin dutiliser des rfrences dentits (comme &lt; pour <). Un "infrieur " peut ainsi tre reprsent par #{2 lt 3} au lieu de #{2 < 3}. Loprateur empty teste si un objet est null ou sil rfrence un objet String, List, Map null ou un tableau null. Une JSP qui utilise une dclaration pour dfinir un objet Book (<%! Book book = new Book(); %>) peut donc tester si lobjet ou lun de ses attributs est null:
#{empty book} #{empty book.isbn}

Loprateur point permet daccder un attribut dun objet. Une autre syntaxe possible consiste utiliser loprateur []. On peut donc accder lattribut isbn laide de ces deux syntaxes:
#{book.isbn} #{book[isbn]}

Chapitre 11

Pages et composants 349

Avec EL 2.2, il est dsormais possible dappeler des mthodes. Le fragment de code qui suit montre comment acheter un livre en appelant la mthode book.buy() et comment lui passer un paramtre (la monnaie utilise):
#{book.buy} #{book.buy(EURO)}

Les instructions EL peuvent tre employes avec les pages JSP et JSF, le fichier faces-config.xml, les scriptlets ou JSTL.

La bibliothque de marqueurs standard de JSP (JSTL)


JSTL est un ensemble de marqueurs standard permettant dviter le mlange du code Java et des marqueurs XHTML: grce elle, vous pouvez manipuler les donnes dynamiques contenues dans une JSP en utilisant des marqueurs XML au lieu de passer par du code Java. Les actions possibles vont de laffectation dune valeur un objet la capture des exceptions en passant par le contrle du flux avec des conditions et des itrateurs et par laccs aux bases de donnes. Une bibliothque de marqueurs est une collection de fonctions pouvant tre utilises dans une page JSP ou JSF. Le Tableau11.2 numre ces fonctions, les URI permettant de rfrencer les bibliothques et les prfixes associs (ce sont ceux que lon utilise le plus souvent, mais ils peuvent tre modifis).
Tableau11.2: Bibliothques de marqueurs JSTL

Domaine Noyau Traitement XML I18N et formatage Accs aux BD Fonctions

URI
http://java.sun.com/jsp/jstl/core http://java.sun.com/jsp/jstl/xml http://java.sun.com/jsp/jstl/fmt http://java.sun.com/jsp/jstl/sql http://java.sun.com/jsp/jstl/functions

Prfixe classique
c x fmt sql fn

Avant de pouvoir utiliser ces actions, la JSP doit importer lURI de la bibliothque et choisir un prfixe, soit en utilisant une directive JSP avec le systme de marqueurs de JSP, soit en utilisant une syntaxe XML:

350

Java EE 6 et GlassFish 3

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> // ou <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jstl/core" version="1.2">

Avec cette dclaration, vous pouvez ensuite utiliser toutes les actions de la bibliothque des marqueurs fondamentaux en utilisant le prfixe c:
<c:set var="upperLimit" value="20"/>

Ce code initialise la variable upperLimit avec la valeur 20.


Actions fondamentales

Les actions fondamentales, numres dans le Tableau11.3, fournissent des marqueurs pour manipuler des variables, traiter les erreurs, effectuer des tests et excuter des boucles et des itrations.
Tableau11.3: Actions fondamentales

Action
<c:out> <c:set> <c:remove> <c:catch> <c:if> <c:choose> <c:when> <c:otherwise> <c:forEach> <c:forTokens> <c:import> <c:url> <c:param> <c:redirect>

Description value une expression et affiche son rsultat. Initalise la valeur dun objet. Supprime une variable. Capture une exception java.lang.Throwable lance par lune de ses actions imbriques. Teste si une expression est vraie. Fournit plusieurs alternatives exclusives. Reprsente une alternative dans une action <c:choose>. Reprsente la dernire alternative dune action <c:choose>. Rpte son corps pour chaque lment dune collection ou un nombre fix de fois. Itre sur une liste de tokens (lexmes) spars par des virgules. Importe une ressource. Encode une URL. Ajoute des paramtres de requte une URL. Redirige vers une URL prcise.

Chapitre 11

Pages et composants 351

Pour illustrer le fonctionnement de certains de ces marqueurs, le Listing11.6 prsente une JSP qui boucle sur les nombres de 3 15 en affichant, pour chacun deux, sil est pair ou impair.
Listing11.6: JSP affichant une liste de nombres pairs et impairs
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jstl/core" version="1.2"> <html> <body> <c:set var="upperLimit" value="20"/> <c:forEach var="i" begin="3" end="${upperLimit 5}"> <c:choose> <c:when test="${i%2 == 0}"> <c:out value="${i} is even"/><br/> </c:when> <c:otherwise> <c:out value="${i} is odd"/><br/> </c:otherwise> </c:choose> </c:forEach> </body> </html>

Pour utiliser la bibliothque des marqueurs fondamentaux, la page doit importer son URI avec un prfixe. Puis elle affecte la valeur 20 la variable upperLimit laide du marqueur <c:set> et itre sur tous les nombres compris entre 3 et 15 (nous avons volontairement ajout une expression arithmtique ${upperLimit 5}). La valeur de lindice (la variable i) est teste chaque tour de boucle pour savoir si elle est paire ou impaire (<c:when test="${i%2 == 0}">). Le marqueur <c:out> affiche ensuite la valeur de lindice, accompagne du texte "est pair" ou "est impair". Vous pouvez remarquer que tout le traitement seffectue grce aux marqueurs, que cette page est conforme XML et quelle peut tre comprise par les dveloppeurs qui ne connaissent pas Java.
Actions de formatage

Les actions de formatage, numres dans le Tableau11.4, permettent de formater des dates, des nombres, des valeurs montaires et des pourcentages. Elles reconnaissent aussi linternationalisation (i18n): vous pouvez obtenir ou modifier les locales (variables de langue) et les zones horaires et obtenir lencodage de la page web.

352

Java EE 6 et GlassFish 3

Tableau11.4: Actions de formatage

Action
<fmt:message> <fmt:param> <fmt:bundle> <fmt:setLocale> <fmt:requestEncoding> <fmt:timeZone> <fmt:setTimeZone> <fmt:formatNumber> <fmt:parseNumber> <fmt:formatDate> <fmt:parseDate>

Description Internationalise une JSP en extrayant un message en fonction de la langue. Fournit un paramtre <fmt:message>. Indique le paquetage contenant les messages par langue. Fixe la langue utiliser. Fixe lencodage des caractres de la requte. Prcise la zone horaire du format de lheure. Stocke la zone horaire indique dans une variable. Formate une valeur numrique (nombre, monnaie, pourcentage) selon la locale. Analyse la reprsentation textuelle des nombres, des valeurs montaires et des pourcentages. Formate les dates et les heures selon la langue. Analyse la reprsentation textuelle des dates et des heures.

Le Listing11.7 montre comment utiliser ces marqueurs.


Listing11.7: JSP formatant des dates et des nombres
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <html> <body> Dates <c:set var="now" value="<%=new java.util.Date()%>"/> <fmt:formatDate type="time" value="${now}"/> <fmt:formatDate type="date" value="${now}"/> <fmt:formatDate type="both" dateStyle="short" timeStyle="short" value="${now}"/> <fmt:formatDate type="both" dateStyle="long" timeStyle="long" value="${now}"/> Currency <fmt:setLocale value="en_us"/> <fmt:formatNumber value="20.50" type="currency"/> <fmt:setLocale value="en_gb"/> <fmt:formatNumber value="20.50" type="currency"/> </body> </html>

Chapitre 11

Pages et composants 353

La page importe les bibliothques des marqueurs fondamentaux et de formatage laide de directives <%@ taglib>. La ligne <c:set var="now" value="<%=new java. util.Date()%>"/> initialise la variable now avec la date courante, et le marqueur <fmt:formatDate> la formate selon diffrents critres: uniquement lheure, uniquement la date et les deux ensemble. La ligne <fmt:setLocale value="en_us"/> fixe la langue amricaine et formate la valeur montaire 20.50 pour obtenir $20.50. La locale est ensuite modifie pour la Grande-Bretagne afin que cette valeur soit exprime en livres sterling. Cette JSP produit donc le rsultat suivant:
Dates 11:31:12 14 may 2009 14/02/09 11:31 14 may 2009 11:31:12 CET Currency $20.50 20.50

Actions SQL

Les actions SQL de la JSTL permettent deffectuer des requtes sur une base de donnes (insertions, modifications et suppressions), daccder aux rsultats de ces requtes et mme de mettre en place un contexte transactionnel. Nous avons dj vu comment accder une base de donnes avec les entits JPA et les EJB mais, pour des applications spcifiques, on a parfois besoin daccder une base partir dune page web (pour une application web dadministration non critique utilise occasionnellement par un unique utilisateur, par exemple): dans ce cas, les marqueurs de la bibliothque SQL (voir Tableau11.5) peuvent se rvler utiles.
Tableau11.5 : Actions SQL

Action
<sql:query> <sql:update> <sql:transaction> <sql:setDataSource>

Description Interroge une base de donnes. Excute une instruction SQL INSERT, UPDATE ou DELETE. tablit un contexte transactionnel pour les marqueurs <sql:query> et <sql:update>. Indique la source de donnes.

354

Java EE 6 et GlassFish 3

Action
<sql:param> <sql:dateParam>

Description Fixe les valeurs des marqueurs demplacements (?) dune instruction SQL. Fixe les valeurs des marqueurs demplacements (?) dune instruction SQL pour les valeurs de type java.util.Date.

La page JSP du Listing11.8 accde une base de donnes, rcupre toutes les lignes de la table BOOK et les affiche.
Listing11.8: JSP accdant une base de donnes pour rcuprer tous les livres
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> <html> <head> <title>Lists all the books</title> </head> <body> <h1>Lists all the books</h1> <hr/> <sql:setDataSource dataSource="jdbc/__default"/> <sql:query var="books"> select * from book </sql:query> <table border="1"> <tr> <th>ISBN</th> <th>Title</th> <th>Price</th> <th>Description</th> <th>Number of pages</th> <th>Illustrations</th> </tr> <c:forEach var="row" items="${books.rows}"> <tr> <td><c:out value="${row.isbn}"/></td> <td><c:out value="${row.title}"/></td> <td><c:out value="${row.price}"/></td> <td><c:out value="${row.description}"/></td> <td><c:out value="${row.nbOfPage}"/></td> <td><c:out value="${row.illustrations}"/></td> </tr> </c:forEach> </table> <hr/>

Chapitre 11

Pages et composants 355

<i>APress - Beginning Java EE 6</i> </body> </html>

Cette JSP doit dabord importer la bibliothque sql avec une directive <%@ taglib> puis indiquer la source de donnes (ici, la source par dfaut jdbc/__default fournie avec GlassFish). Le rsultat de lexcution de la requte select * from book est stock dans la variable books et toutes les lignes obtenues se trouvent dans la collection books.rows, que lon parcourt avec le marqueur <c:forEach>. Le rsultat sera identique celui que nous avons dj prsent la Figure11.5.
Actions XML

Par certains aspects, la bibliothque de marqueurs XML ressemble la bibliothque des marqueurs fondamentaux : elle permet deffectuer une analyse XML, ditrer sur les lments des collections, deffectuer des oprations reposant sur les expressions Xpath et de raliser des transformations laide de documents XSL. LeTableau11.6 numre les actions de cette bibliothque.
Tableau11.6: Actions XML

Action
<x:parse> <x:out> <x:set> <x:if> <x:choose> <x:when> <x:otherwise> <x:forEach> <x:transform> <x:param>

Description Analyse un document XML. value une expression XPATH et produit son rsultat. value une expression XPATH et stocke son rsultat dans une variable. value lexpression XPATH si lexpression est vraie. Fournit plusieurs alternatives exclusives. Reprsente une alternative dune action <x:choose>. Reprsente la dernire alternative dune action <x:choose>. value une expression XPATH et rpte son contenu sur le rsultat. Applique une feuille de style XSLT un document XML. Fixe les paramtres de la transformation <x:transform>.

356

Java EE 6 et GlassFish 3

Nous avons besoin dun document XML pour effectuer les transformations ralises par ces marqueurs. Le fichier books.xml du Listing11.9 contient une liste de livres stocks sous forme dlments et dattributs XML.
Listing11.9: Le fichier books.xml
<?xml version=1.0 encoding=UTF-8?> <books> <book isbn=1234-234 price=12 nbOfPage=241 illustrations=true> <title>H2G2</title> <description>Scifi IT book</description> </book> <book isbn=564-694 price=18.5 nbOfPage=317 illustrations=true> <title>Robots</title> <description>Best seller</description> </book> <book isbn=256-6-56 price=23.25 nbOfPage=529 illustrations=false> <title>Dune</title> <description>The trilogy</description> </book> </books>

Ce fichier doit tre dploy avec la JSP ou tre import par une URL. La page JSP du Listing11.10 lanalyse et affiche tous les livres.
Listing11.10: JSP analysant le fichier books.xml et affichant son contenu
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %> <html> <body> <table border="1"> <tr> <th>ISBN</th> <th>Title</th> <th>Price</th> <th>Description</th> <th>Number of pages</th> <th>Illustrations</th> </tr> <c:import url="books.xml" var="bookUrl"/> <x:parse xml="${bookUrl}" var="doc"/> <x:forEach var="b" select="$doc/books/book"> <tr> <td><x:out select="$b/@isbn"/></td> <td><x:out select="$b/title"/></td>

Chapitre 11

Pages et composants 357

<td><x:out <td><x:out <td><x:out <td><x:out </tr> </x:forEach> </table> </body> </html>

select="$b/@price"/></td> select="$b/description"/></td> select="$b/@nbOfPage"/></td> select="$b/@illustrations"/></td>

Pour commencer, cette JSP doit importer la bibliothque de marqueurs XML (avec la directive <%@ taglib>) puis charger le fichier books.xml dans la variable bookUrl laide du marqueur <c:import>. bookUrl contient le texte brut, qui doit tre analys par le marqueur <x:parse> le DOM rsultant sera stock dans la variable doc. Une fois que le document a t analys, nous pouvons le parcourir et afficher les valeurs en utilisant des expressions XPATH avec <x:out> (/@isbn reprsente un attribut XML et /title, un lment). Le rsultat obtenu sera identique celui de la Figure11.5.
Fonctions

Les fonctions ne sont pas des marqueurs mais sont quand mme dfinies dans la spcification JSTL. Elles peuvent tre utilises avec EL et sont principalement employes pour traiter les chanes de caractres:
${fn:contains("H2G2", "H2")}

Ce code teste si une chane contient une sous-chane particulire: ici, cet appel renverra true car H2G2 contientH2. Lappel suivant renvoie la longueur dune chane ou dune collection: dans cet exemple prcis son rsultat sera4.
${fn:length("H2G2")}

Une JSP peut afficher les rsultats des fonctions (avec un marqueur <c:out>) ou les utiliser dans un test ou une boucle:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fn" prefix="fn" %> <html> <body> <c:out value="${fn:toLowerCase(sentence)}" /> <c:if test="${fn:length(H2G2) == 4}"> H2G2 is four caracters long </c:if> </body> </html>

358

Java EE 6 et GlassFish 3

Le Tableau11.7 numre toutes les fonctions fournies par la bibliothque.


Tableau11.7: Fonctions

Fonction
fn:contains fn:containsIgnoreCase fn:endsWith fn:escapeXml fn:indexOf fn:join fn:length fn:replace fn:split fn:startsWith fn:substring fn:substringAfter fn:substringBefore fn:toLowerCase fn:toUpperCase fn:trim

Description Teste si une chane contient la sous-chane indique. Idem, sans tenir compte de la casse. Teste si une chane se termine par le suffixe indiqu. Protge les caractres pouvant tre interprts comme du XML. Renvoie lindice de la premire occurrence dune sous-chane dans une chane. Joint tous les lments dun tableau pour former une chane. Renvoie le nombre dlments dune collection ou le nombre de caractres dune chane. Renvoie une chane o toutes les occurrences de la souschane indique ont t remplaces par une autre chane. Dcoupe une chane pour obtenir un tableau de sous-chanes. Teste si une chane commence par le prfixe indiqu. Renvoie une sous-chane. Renvoie la sous-chane situe aprs la sous-chane indique. Renvoie la sous-chane situe avant la sous-chane indique. Convertit une chane en minuscules. Convertit une chane en majuscules. Supprime les espaces aux deux extrmits dune chane.

Facelets
Lorsque JSF a t cr, le but consistait rutiliser JSP comme PDL principal car elle faisait dj partie de JavaEE. JSP utilisait EL et JSTL et lide consistait donc rutiliser toutes ces technologies avec JSF. JSP est un langage de page et JSF, une couche de composants situe au-dessus. Cependant, les cycles de vie de JSP et de JSF ne saccordent pas. Pour produire une rponse, les marqueurs de JSP sont traits de haut en bas, dans lordre o ils apparaissent; alors que le cycle de vie de JSF est un peu plus compliqu puisque la production de larborescence des composants et

Chapitre 11

Pages et composants 359

leur traitement ont lieu dans des phases diffrentes. Facelets entre donc en jeu pour correspondre au cycle de vie des JSF. Facelets est une alternative open-source JSP. la diffrence de JSP, EL et JSTL, Facelets na pas de JSR et ne fait pas partie de JavaEE: cest un remplaant de JSP qui fournit une alternative XML (XHTML) pour les pages dune application JSF. Facelets ayant t conu en tenant compte de JSF, il fournit un modle de programmation plus simple que celui de JSP. Facelets dispose dune bibliothque de marqueurs permettant dcrire linterface utilisateur et reconnat en partie les marqueurs JSTL. Bien que la bibliothque de fonctions soit intgralement disponible, seuls quelques marqueurs fondamentaux (c:if, c:forEach, c:catch et c:set) sont reconnus. La caractristique essentielle de Facelets est son mcanisme de templates de pages, qui est bien plus souple que celui de JSP. Il permet galement de crer des composants personnaliss utilisables dans le modle arborescent de JSF. La bibliothque des marqueurs Facelets est dfinie par lURI http://java.sun. com/jsf/facelets et utilise gnralement le prfixe ui. Ces marqueurs sont prsents dans le Tableau11.8.
Tableau11.8: Marqueurs Facelets

Marqueur
<ui:composition> <ui:component> <ui:debug> <ui:define> <ui:decorate> <ui:fragment> <ui:include> <ui:insert> <ui:param> <ui:repeat> <ui:remove>

Description Dfinit une composition qui utilise ventuellement un template. Plusieurs compositions peuvent utiliser le mme template. Cre un composant. Capture les informations de dbogage. Dfinit le contenu insr dans une page par un template. Dcore une partie du contenu dune page. Ajoute un fragment de page. Encapsule et rutilise un contenu dans plusieurs pages XHTML, comme le marqueur <jsp:include> de JSP. Insre un contenu dans un template. Passe des paramtres un fichier inclus par <ui:include> ou un template. Alternative <c:forEach>. Supprime du contenu dune page.

360

Java EE 6 et GlassFish 3

Les pages Facelets sont crites en XHTML et ressemblent ce que nous avons dj vu avec le Listing11.2. Nous verrons comment utiliser les templates dans la section qui leur est consacre dans ce chapitre.

JavaServer Faces
Nous avons commenc ce chapitre en prsentant JSP, JSTL et Facelets car ces technologies sont essentielles pour comprendre JSF2.0. Vous devez choisir un PDL pour crire une page JSF: jusqu JSF1.2, le PDL prfr tait JSP mais, les cycles de vie des pages JSP et JSF tant diffrents, Facelets (pages en XHTML) est dsormais le PDL conseill. Ceci ne signifie pas que vous ne puissiez pas utiliser JSP mais, si vous le faites, vous serez trs limit en termes de marqueurs et de fonctionnalits (vous naurez pas accs aux marqueurs de Facelets, notamment). Le cycle de vie des JSP est relativement simple. Un source JSP est compil en servlet, laquelle le conteneur web passera les requtes HTTP quil reoit. La servlet traite la requte puis renvoie la rponse au client. JSF a un cycle de vie plus complexe, et cest la raison pour laquelle, dans la section "Langage dexpressions (EL)", nous avons prsent deux syntaxes diffrentes: lune utilisant le symbole Dollar (${expression}), lautre, le symbole dise (#{expression}). $ est utilis pour les expressions qui peuvent sexcuter immdiatement (lorsque lon sait que les objets de lexpression sont disponibles) alors que # sert aux expressions diffres (qui doivent tre values plus tard dans le cycle de vie). Avec JSF1.2, les deux syntaxes taient unifies, mais cela impliquait trop de confusions et beaucoup derreurs. Le Tableau11.9 numre toutes les bibliothques de marqueurs auxquelles a accs une page utilisant Facelets comme PDL. On y retrouve la bibliothque fondamentale et celle des fonctions que nous avons prsentes dans la section consacre JSTL, les marqueurs Facelets (avec le prfixe ui) et les nouveaux marqueurs fondamentaux, html et composites de JSF. Les autres marqueurs JSTL (formatage, SQL et XML) ne sont pas reconnus par Facelets. Intressons-nous aux composants HTML de JSF permettant de crer des interfaces web riches. Le Chapitre12 prsentera lessentiel de la bibliothque fondamentale de JSF avec les convertisseurs et les validateurs, mais examinons dabord le cycle de vie dune page JSF.

Chapitre 11

Pages et composants 361

Tableau11.9: Bibliothques de marqueurs autoriss avec le PDL Facelets

URI http://java.sun.com/jsf/html

Prfixe classique h

Description Contient les composants et leurs rendus HTML (h:commandButton, h:commandLink, h:inputText, etc.).

http://java.sun.com/jsf/core

Contient les actions personnalises indpendantes dun rendu particulier (f:selectItem, f:validateLength, f:convertNumber, etc.). Marqueurs pour le support des templates. Sert dclarer et dfinir des composants composites. Les pages Facelets peuvent utiliser certains marqueurs fondamentaux (c:if, c:forEach et c:catch). Les pages Facelets peuvent utiliser tous les marqueurs de fonctions.

http://java.sun.com/jsf/facelets http://java.sun.com/jsf/composite http://java.sun.com/jsp/jstl/core

ui composite c

http://java.sun.com/jsp/jstl/ functions

fn

Cycle de vie

Une page JSF est une arborescence de composants avec un cycle de vie spcifique quil faut bien avoir compris pour savoir quel moment les composants sont valids ou quand le modle est mis jour. Un clic sur un bouton provoque lenvoi dune requte du navigateur vers le serveur et cette requte est traduite en vnement qui peut tre trait par lapplication sur le serveur. Toutes les donnes saisies par lutilisateur passent par une tape de validation avant que le modle soit mis jour et que du code mtier soit appel. JSF se charge alors de vrifier que chaque composant graphique (composants parent et fils) est correctement rendu par le navigateur. Les diffrentes phases du cycle de vie dune page JSF sont reprsentes par la Figure11.6.

362

Java EE 6 et GlassFish 3

Figure11.6 Cycle de vie de JSF.


Requte Restauration de la vue Application des valeurs de la requte

Fin de la rponse Traitement des vnements Validations

Fin de la rponse Traitement des vnements

Fin de la rponse Fin de la rponse Traitement des vnements Appel de l'application

Fin de la rponse Traitement des vnements Modification des valeurs du modle

Rponse

Erreurs de conversion ou de validation

Le cycle de vie de JSF se divise en six phases: 1. Restauration de la vue. JSF trouve la vue cible et lui applique les entres de lutilisateur. Sil sagit de la premire visite, JSF cre la vue comme un composant UIViewRoot (racine de larborescence de composants, qui constitue une page particulire). Pour les requtes suivantes, il rcupre lUIViewRoot prcdemment sauvegarde pour traiter la requte HTTP courante. 2. Application des valeurs de la requte. Les valeurs fournies avec la requte (champs de saisie, dun formulaire, valeurs des cookies ou partir des en-ttes HTTP) sont appliques aux diffrents composants de la page. Seuls les composants UI modifient leur tat, non les objets mtiers qui forment le modle. 3. Validations. Lorsque tous les composants UI ont reu leurs valeurs, JSF traverse larborescence de composants et demande chacun deux de sassurer que la valeur qui leur a t soumise est correcte. Si la conversion et la validation russissent pour tous les composants, le cycle de vie passe la phase suivante. Sinon il passe la phase de Rendu de la rponse avec les messages derreur de validation et de conversion appropris. 4. Modification des valeurs du modle. Lorsque toutes les valeurs des composants ont t affectes et valides, les beans grs qui leur sont associs peuvent tre mis jour. 5. Appel de lapplication. On peut maintenant excuter la logique mtier. Les actions qui ont t dclenches seront excutes sur le bean gr. La navigation entre en jeu car cest la valeur quelle renvoie qui dterminera la rponse.

Chapitre 11

Pages et composants 363

6. Rendu de la rponse. Le but principal de cette phase consiste renvoyer la rponse lutilisateur. Son but secondaire est de sauvegarder ltat de la vue pour pouvoir la restaurer dans la phase de restauration si lutilisateur redemande la vue. Le thread dexcution dun cycle requte/rponse peut passer ou non par chacune de ces tapes en fonction de la requte et de ce qui se passe au cours de son traitement: en cas derreur, notamment, le flux dexcution passe directement la phase de Rendu de la rponse. Quatre de ces tapes peuvent produire des messages derreur: Application des valeurs de la requte (2), Validations (3), Modification des valeurs du modle (4) et Appel de lapplication (5). Avec ou sans erreur, la phase de Rendu de la rponse (6) renvoie toujours le rsultat lutilisateur.
Composants HTML standard

Larchitecture JSF est conue pour tre indpendante de tout protocole ou langage marqueurs particulier et pour crire des applications pour les clients HTML qui communiquent via HTTP. Une interface utilisateur pour une page web donne est cre en assemblant des composants qui fournissent des fonctionnalits spcifiques afin dinteragir avec lutilisateur (labels, cases cocher, etc.) JSF met disposition un certain nombre de classes composants couvrant la plupart des besoins classiques. Une page est une arborescence de classes hritant de javax.faces.component. UIComponent et ayant des proprits, des mthodes et des vnements. La racine de larbre est une instance de UIViewRoot et tous les autres composants respectent une relation dhritage. Intressons-nous ces composants dans une page web.
Commandes

Les commandes sont les contrles sur lesquels lutilisateur peut cliquer pour dclencher une action. Ces composants sont gnralement reprsents sous forme de boutons ou de liens hypertextes, indiqus par les marqueurs du Tableau11.10.
Tableau11.10: Marqueurs de commandes

Marqueur
<h:commandButton> <h:commandLink>

Description Reprsente un lment HTML pour un bouton de type submit ou reset. Reprsente un lment HTML pour un lien agissant comme un bouton submit. Ce composant doit tre plac dans un formulaire.

364

Java EE 6 et GlassFish 3

Le code suivant cre des boutons de soumission et de rinitialisation du formulaire, des images cliquables ou des liens permettant de dclencher un vnement:
<h:commandButton value="A submit button"/> <h:commandButton type="reset" value="A reset button"/> <h:commandButton image="javaee6.gif" title="A button with an image"/> <h:commandLink>A hyperlink</h:commandLink>

Par dfaut, un commandButton est de type submit. Pour utiliser une image comme bouton, utilisez non pas lattribut value (qui est le nom du bouton) mais lattribut image pour indiquer le chemin daccs du fichier image que vous souhaitez afficher. Voici le rsultat graphique que produira ce code:

Les boutons et les liens ont tous les deux un attribut action permettant dappeler une mthode dun bean gr. Voici comment invoquer la mthode doNew() du bookController, par exemple:
<h:commandLink action="#{bookController.doNew}"> Create a new book </h:commandLink>

Entres

Les entres sont des composants qui affichent leur valeur courante et permettent lutilisateur de saisir diffrentes informations textuelles. Il peut sagir de champs de saisie, de zones de texte ou de composants pour entrer un mot de passe ou des donnes caches. Leurs marqueurs sont numrs dans le Tableau11.11.

Chapitre 11

Pages et composants 365

Tableau11.11: Marqueurs dentres

Marqueur
<h:inputHidden> <h:inputSecret>

Description Reprsente un lment dentre HTML de type cach (non affich). Reprsente un lment dentre HTML de type mot de passe. Pour des raisons de scurit, tout ce qui a t saisi ne sera pas affich, sauf si la proprit redisplay vaut true. Reprsente un lment dentre HTML de type texte. Reprsente une zone de texte HTML.

<h:inputText> <h:inputTextarea>

De nombreuses pages web contiennent des formulaires pour que lutilisateur puisse saisir des donnes ou se connecter en fournissant un mot de passe. En outre, les composants dentre utilisent plusieurs attributs permettant de modifier leur longueur, leur contenu ou leur aspect:
<h:inputHidden value="Hidden data"/> <h:inputSecret maxlength="8"/> <h:inputText value="An input text"/> <h:inputText size="40" value="A longer input text"/> <h:inputTextarea rows="4" cols="20" value="A text area"/>

Tous les composants ont un attribut value pour fixer leur valeur par dfaut. Lattribut maxLength permet de sassurer que le texte saisi ne dpasse pas une longueur donne et lattribut size modifie la taille par dfaut du composant. Le code prcdent produira donc le rsultat suivant:

An input text A longer input text A text area

Sorties

Les composants de sortie affichent une valeur qui peut ventuellement avoir t obtenue partir dun bean gr, une expression valeur ou un texte littral. Lutilisateur ne peut pas modifier ce contenu car il nest quen lecture seule. Le Tableau11.12 numre les marqueurs de sortie disponibles.

366

Java EE 6 et GlassFish 3

Tableau11.12: Marqueurs de sortie

Marqueur
<h:outputLabel> <h:outputLink> <h:outputText>

Description Produit un lment <label> de HTML Produit un lment <a> Produit un texte littral

La plupart des pages web affichent du texte. Pour ce faire, vous pouvez utiliser des lments HTML classiques mais, grce EL, les marqueurs de sortie de JSF permettent dafficher le contenu dune variable lie un bean gr. Vous pouvez ainsi afficher du texte avec <h:outputText> et des liens hypertextes avec <h:outputLink>. Notez que les marqueurs <h:commandLink> et <h:outputLink> sont diffrents car le dernier affiche le lien sans invoquer de mthode lorsquon clique dessus il cre simplement un lien externe ou une ancre.
<h:outputLabel value="#{bookController.book.title}"/> <h:outputText value="A text"/> <h:outputLink value="http://www.apress.com/"> A link </h:outputLink>

Ce code na pas de reprsentation graphique particulire, il produira le code HTML suivant:


<label>The title of the book</label> A text <a href="http://www.apress.com/">A link</a>

Slections

Les composants de slection (voir Tableau11.13) permettent de choisir une ou plusieurs valeurs dans une liste. Graphiquement, ils sont reprsents par des cases cocher, des boutons radio, des listes ou des combo box.
Tableau11.13: Marqueurs de slection

Marqueur
<h:selectBooleanCheckbox>

Description Produit une case cocher reprsentant une valeur boolenne unique. Cette case sera initialement coche ou dcoche selon la valeur de sa proprit checked. Produit une liste de cases cocher.

<h:selectManyCheckbox>

Chapitre 11

Pages et composants 367

Tableau11.13: Marqueurs de slection (suite)

Marqueur
<h:selectManyListbox> <h:selectManyMenu> <h:selectOneListbox> <h:selectOneMenu>

Description Produit un composant choix multiples, dans lequel on peut choisir une ou plusieurs options. Produit un lment <select> HTML. Produit un composant choix unique, dans lequel on ne peut choisir quune seule option. Produit un composant choix unique, dans lequel on ne peut choisir quune seule option. Naffiche quune option la fois. Produit une liste de boutons radio.

<h:selectOneRadio>

Les marqueurs de ce tableau ont une reprsentation graphique mais ont besoin dimbriquer dautres marqueurs (<f:selectItem> ou <f:selectItems>) pour contenir les options disponibles. Pour reprsenter une combo box contenant une liste de genres littraires, par exemple, il faut imbriquer un ensemble de marqueurs <f:selectItem> dans un marqueur <h:selectOneMenu>:
<h:selectOneMenu> <f:selectItem itemLabel="History" /> <f:selectItem itemLabel="Biography"/> <f:selectItem itemLabel="Literature"/> <f:selectItem itemLabel="Comics"/> <f:selectItem itemLabel="Child"/> <f:selectItem itemLabel="Scifi"/> </h:selectOneMenu>

La Figure11.7 montre toutes les reprsentations possibles de ces marqueurs. Certaines listes sont choix multiples, dautres nautorisent quun seul choix; comme tous les autres composants, vous pouvez directement lier la valeur dun bean gr (List, Set, etc.) lune de ces listes.

368

Java EE 6 et GlassFish 3

Marqueur
h:selectBooleanCheckbox h:selectManyCheckbox History History Biography Literature Comics Child Scifi History History Biography Literature Comics Child Scifi History History

Reprsentation graphique
Biography Literature Comics Child Scifi

h:selectManyListbox

h:selectManyMenu

h:selectOneListbox

h:selectOneMenu h:selectOneRadio

Biography

Literature

Comics

Child

Scifi

Figure11.7 Les diffrentes reprsentations graphiques des listes.

Graphiques

Il nexiste quun seul composant pour afficher les images: <h:graphicImage>. Ce marqueur utilise un lment HTML <img> pour afficher une image que les utilisateurs nauront pas le droit de manipuler. Ses diffrents attributs permettent de modifier la taille de limage, de lutiliser comme image cliquable, etc. Une image peut tre lie une proprit dun bean gr et provenir dun fichier sur le systme ou dune base de donnes. Le code suivant, par exemple, affiche une image en modifiant sa taille:
<h:graphicImage value="book.gif" height="200" width="320"/>

Grilles et tableaux

Les donnes doivent trs souvent tre affiches sous forme de tableau. JSF fournit donc le marqueur <h:dataTable> permettant de parcourir une liste dlments afin de produire un tableau (reportez-vous au code de la page listBooks.xhtml qui apparat dans le Listing10.7 du chapitre prcdent). Les tableaux permettent galement de crer une interface utilisateur "en grille". Dans ce cas, vous pouvez utiliser les marqueurs <h:panelGrid> et <h:panelGroup> pour disposer les composants (voir Tableau11.14).

Chapitre 11

Pages et composants 369

Tableau11.14: Marqueurs de grilles et de tableaux

Marqueur
<h:dataTable> <h:column> <h:panelGrid> <h:panelGroup>

Description Reprsente un ensemble de donnes qui seront affiches dans un lment <table> de HTML. Produit une colonne de donnes dans un composant <h:dataTable>. Produit un lment <table> HTML. Conteneur de composants pouvant simbriquer dans un <h:panelGrid>.

la diffrence de <h:dataTable>, le marqueur <h:panelGrid> nutilise pas de modle de donnes sous-jacent pour produire les lignes de donnes cest un conteneur permettant de produire les autres composants JSF dans une grille de lignes et de colonnes. Vous pouvez prciser le nombre de colonnes: <h:panelGrid> dterminera le nombre de lignes ncessaire (lattribut column indique le nombre de colonnes produire avant de dbuter une nouvelle ligne). Le code suivant, par exemple, produira une grille de trois colonnes sur deux lignes:
<h:panelGrid columns="3" border="1"> <h:outputLabel value="One"/> <h:outputLabel value="Two"/> <h:outputLabel value="Three"/> <h:outputLabel value="Four"/> <h:outputLabel value="Five"/> <h:outputLabel value="Six"/> </h:panelGrid>

Pour combiner plusieurs composants dans la mme colonne, utilisez un <h:panelGroup> qui produira ses fils comme un seul composant. Vous pouvez galement dfinir un en-tte et un pied laide du marqueur spcial <f:facet>.
<h:panelGrid columns="3" border="1"> <f:facet name="header"> <h:outputText value="Header"/> </f:facet> <h:outputLabel value="One"/> <h:outputLabel value="Two"/> <h:outputLabel value="Three"/> <h:outputLabel value="Four"/> <h:outputLabel value="Five"/> <h:outputLabel value="Six"/> <f:facet name="footer"> <h:outputText value="Footer"/> </f:facet> </h:panelGrid>

370

Java EE 6 et GlassFish 3

Les deux grilles que nous venons de dcrire auront les reprsentations graphiques suivantes. La premire naura ni en-tte ni pied; la seconde aura les deux:
One Four Two Five Three Six

Header One Four Footer Two Five Three Six

Messages derreur

Les applications peuvent parfois lancer des exceptions en rponse des donnes mal formates ou pour certaines raisons techniques. Dans ce cas, il ne faut afficher dans linterface utilisateur que ce qui est ncessaire afin dattirer son attention et pour quil puisse corriger le problme. Le mcanisme de gestion des messages derreur passe par lutilisation des marqueurs <h:message> et <h:messages> (voir Tableau11.15). <h:message> est li un composant prcis, tandis que <h:messages> permet de dfinir un message global pour tous les composants de la page.
Tableau11.15: Marqueurs de messages

Marqueur
<h:message> <h:messages>

Description Affiche un seul message derreur. Affiche tous les messages derreur en attente.

Les messages peuvent avoir des importances diffrentes (INFO, WARN, ERROR et FATAL) correspondant chacune un style CSS (respectivement infoStyle, warnStyle, errorStyle et fatalStyle) : chaque type de message sera donc affich dans un style diffrent. Le code suivant, par exemple, affichera tous les messages en rouge:
<h:messages style="color:red"/> <h:form> Enter a title: <h:inputText value="#{bookController.title}" required="true"/> <h:commandButton action="#{bookController.save}" value="Save"/> </h:form>

Chapitre 11

Pages et composants 371

Cette page affichera un champ de saisie li une proprit dun bean gr. Ici, cette proprit est obligatoire: un message derreur saffichera si lutilisateur clique sur le bouton Save alors que ce champ est vide.
Validation Error : Value is required. Enter a tiltle :

Save

Informations complmentaires

Les marqueurs numrs dans le Tableau 11.16 nont pas de reprsentation graphique mais possdent un quivalent HTML. Bien que les marqueurs natifs de HTML puissent tre utiliss directement sans problme, les marqueurs JSF ont des attributs supplmentaires qui facilitent le dveloppement. Vous pouvez, par exemple, ajouter une bibliothque JavaScript laide du marqueur HTML standard <script type="text/JavaScript">, mais le marqueur <h:outputScript> de JSF permet dutiliser la nouvelle gestion des ressources, comme nous le verrons dans la section "Gestion des ressources".
Tableau11.16: Marqueurs divers

Marqueur
<h:body> <h:head> <h:form> <h:outputScript> <h:outputStylesheet>

Description Produit un lment <body> HTML. Produit un lment <head> HTML. Produit un lment <form> HTML. Produit un lment <script> HTML. Produit un lment <link> HTML.

Templates

Une application web typique contient plusieurs pages partageant toutes le mme aspect, un en-tte, un pied de page, un menu, etc. Facelets permet de dfinir une disposition de page dans un fichier template qui pourra tre utilis par toutes les pages: ce fichier dfinit les zones (avec le marqueur <ui:insert>) dont le contenu sera remplac grce aux marqueurs <ui:component>, <ui:composition>, <ui:fragment> ou <ui:decorate> des pages clientes. Le Tableau11.17 numre les marqueurs de templates.

372

Java EE 6 et GlassFish 3

Tableau11.17: Marqueurs de templates

Marqueur
<ui:composition> <ui:define> <ui:decorate> <ui:fragment> <ui:insert>

Description Dfinit une composition utilisant ventuellement un template. Le mme template peut tre utilis par plusieurs compositions. Dfinit un contenu qui sera insr dans llment <ui:insert> correspondant du template. Permet de dcorer le contenu dune page. Ajoute un fragment une page. Dfinit un point dinsertion dans un template dans lequel on pourra ensuite insrer un contenu plac dans un marqueur <ui:define>.

titre dexemple, rutilisons la page qui affichait un formulaire pour crer un livre (voir Figure11.1). Nous pourrions considrer que le titre est len-tte de la page et que le texte "Apress - Beginning Java EE6" est le pied de page. Le contenu du template layout.xml ressemblerait donc au code du Listing11.11.
Listing11.11: Le fichier layout.xml est un template Facelets
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xml:lang="en" lang="en"> <head> <title> <ui:insert name="title">Default title</ui:insert> </title> </head> <body> <h1> <ui:insert name="title">Default title</ui:insert> </h1> <hr/> <ui:insert name="content">Default content</ui:insert> <hr/> <i>APress - Beginning Java EE 6</i> </body> </html>

Le template doit dabord dfinir la bibliothque de marqueurs ncessaire (xmlns:ui="http://java.sun.com/ jsf/facelets"). Puis il utilise un marqueur

Chapitre 11

Pages et composants 373

pour insrer un attribut title dans les marqueurs HTML <h1>. Le corps de la page sera insr dans lattribut content.
<ui:insert>

<title>

et

Pour utiliser ce template, la page newBook.xhtml prsente dans le Listing 11.12 doit le dclarer (<ui:composition template="layout.xhtml">). Puis le principe consiste lier les attributs dfinis par les marqueurs <ui:define> de la page ceux des marqueurs <ui:insert> du template. Dans notre exemple, le titre de la page, "Create a new book", est stock dans la variable title (avec <ui:define name="title">) et sera donc li au marqueur correspondant dans le template (<ui:insert name="title">). Il en va de mme pour le reste de la page, qui est insr dans la variable content (<ui:define name="content">).
Listing11.12: La page newBook.xhtml utilise le template layout.xml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xml:lang="en" lang="en"> <ui:composition template="layout.xhtml">

<ui:define name="title">Create a new book</ui:define> <ui:define name="content"> <table border="0"> <tr> <td>ISBN :</td> <td><input type="text"/></td> </tr> <tr> <td>Title :</td> <td><input type="text"/></td> </tr> <tr> <td>Price :</td> <td><input type="text"/></td> </tr> <tr> <td>Description :</td> <td><textarea name="textarea" cols="20" rows="5"> </textarea></td> </tr> <tr> <td>Number of pages :</td> <td><input type="text"/></td> </tr> <tr>

374

Java EE 6 et GlassFish 3

<td>Illustrations :</td> <td><input type="checkbox"/></td> </tr> </table> <input name="" type="submit" value="Create"/> </ui:define> </ui:composition> </html>

La Figure11.8 montre que le rsultat obtenu est identique celui de la Figure11.1.


Figure11.8 La page newBook.html avec le template layout.xhtml.

Create a new book


ISBN : Tiltle : Price :

Description :

Number of pages : Illustrations :

Create a book
APress Beginning Java EE 6

Gestion des ressources

La plupart des composants ont besoin de ressources externes pour safficher correctement: <h:graphicImage> a besoin dune image, <h:commandButton> peut galement afficher une image pour reprsenter le bouton, <h:outputScript> rfrence un fichier JavaScript et les composants peuvent galement appliquer des styles CSS. Avec JSF, une ressource est un lment statique qui peut tre transmis aux lments afin dtre affich (images) ou trait (JavaScript, CSS) par le navigateur. Les versions prcdentes de JSF ne fournissaient pas de mcanisme particulier pour servir les ressources: lorsque lon voulait en fournir une, il fallait la placer dans le rpertoire WEB-INF pour que le navigateur du client puisse y accder. Pour la modifier, il fallait remplacer le fichier et, pour grer les ressources localises (une image avec un texte anglais et une autre avec un texte franais, par exemple),

Chapitre 11

Pages et composants 375

il fallait utiliser des rpertoires diffrents. JSF2.0 permet dsormais dassembler directement les ressources dans un fichier jar spar, avec un numro de version et une locale, et de le placer la racine de lapplication web, sous le rpertoire suivant:
resources/<identifiant_ressource>

ou:
META-INF/resources/<identifiant_ressource>

<identifiant_ressource>

est form de plusieurs sous-rpertoires indiqus sous la

forme:
[locale/][nomBib/][versionBib/]nomRessource[/versionRessource]

Tous les lments entre crochets sont facultatifs. La locale est le code du langage, suivi ventuellement dun code de pays (en, en_US, pt, pt_BR). Comme lindique cette syntaxe, vous pouvez ajouter un numro de version la bibliothque ou la ressource elle-mme. Voici quelques exemples:
book.gif en/book.gif en_us/book.gif en/myLibrary/book.gif myLibrary/book.gif myLibrary/1_0/book.gif myLibrary/1_0/book.gif/2_3.gif

Vous pouvez ensuite utiliser une ressource limage book.gif, par exemple directement dans un composant <h:graphicImage> ou en prcisant le nom de la bibliothque (library="myLibrary"). La ressource correspondant la locale du client sera automatiquement choisie.
<h:graphicImage <h:graphicImage <h:graphicImage <h:graphicImage value="book.gif" /> value="book.gif" library="myLibrary" /> value="#{resource[book.gif]}" /> value="#{resource[myLibrary:book.gif]}" />

Composants composites

Tous les composants que nous venons de prsenter font partie de JSF et sont disponibles dans toutes les implmentations qui respectent la spcification. En outre, comme elle repose sur des composants rutilisables, JSF fournit le moyen de crer et dintgrer aisment dans les applications ses propres composants ou des composants provenant de tierces parties.

376

Java EE 6 et GlassFish 3

Nous avons dj mentionn le fait que tous les composants hritaient, directement ou indirectement, de la classe javax.faces.component.UIComponent. Avant JSF2.0, pour crer son propre composant il fallait tendre la classe component la plus proche du nouveau composant (UICommand, UIGraphic, UIOutput, etc.), la dclarer dans le fichier faces-config.xml et fournir un descripteur de marqueur et une reprsentation. Ces tapes taient complexes: dautres frameworks comme Facelets ont alors montr quil tait possible de crer plus simplement des composants puissants. Le but des composants composites est de permettre aux dveloppeurs de crer de vrais composants graphiques rutilisables sans avoir besoin dcrire du code Java ou de mettre en place une configuration XML. Cette nouvelle approche consiste crer une page XHTML contenant les composants, puis de lutiliser comme composant dans dautres pages. Cette page XHTML est alors vue comme un vritable composant supportant des validateurs, des convertisseurs et des couteurs. Les composants composites peuvent contenir nimporte quel marqueur valide et utiliser des templates. Ils sont traits comme des ressources et doivent donc se trouver dans les nouveaux rpertoires standard des ressources. LeTableau11.18 numre les marqueurs permettant de les crer et de les dfinir.
Tableau11.18: Marqueurs pour la dclaration et la dfinition des composants composites

Marqueur
<composite:interface> <composite:implementation> <composite:attribute>

Description Dclare le contrat dun composant. Dfinit limplmentation dun composant. Dclare un attribut pouvant tre fourni une instance du composant. Un marqueur <composite:interface> peut en contenir plusieurs. Dclare que ce composant supporte une facet. Utilis dans un marqueur
<composite:implementation>.

<composite:facet> <composite:insertFacet>

La facet insre sera reprsente dans le composant. Utilis dans un marqueur les composants fils ou les templates seront insrs dans la reprsentation de ce composant.

<composite:insertChildren>

<composite:implementation>. Tous

Chapitre 11

Pages et composants 377

Tableau11.18: Marqueurs pour la dclaration et la dfinition des composants composites (suite)

Marqueur
<composite:valueHolder>

Description Le composant dont le contrat est dclar par le marqueur <composite:interface> dans lequel est imbriqu cet lment devra exposer une implmentation de ValueHolder. Le composant dont le contrat est dclar par le marqueur <composite:interface> dans lequel est imbriqu cet lment devra exposer une implmentation deditableValueHolder. Le composant dont le contrat est dclar par le marqueur <composite:interface> dans lequel est imbriqu cet lment devra exposer une implmentation de linterface actionSource.

<composite:editableValueHolder>

<composite:actionSource>

tudions un exemple montrant la facilit avec laquelle on peut crer un composant graphique et lutiliser dans dautres pages. Dans les chapitres prcdents, lapplication CD-BookStore vendait deux sortes darticles: des livres et des CD. Au Chapitre3, nous les avons reprsents comme trois objets diffrents: Book et CD hritaient dItem. Ce dernier contenait les attributs communs (title, price et description) alors que Book et CD contenaient des attributs spcialiss (isbn, publisher, nbOfPage et illustrations pour Book; musicCompany, numberOfCDs, totalDuration et gender pour CD). Pour que lapplication web puisse crer de nouveaux livres et de nouveaux CD, on a donc besoin de deux formulaires diffrents, mais les attributs dItem pourraient tre dans une page distincte qui agirait comme un composant part entire. La Figure11.9 montre ces deux formulaires. Nous allons donc crer un composant composite contenant deux champs de saisie (pour le titre et le prix) et une zone de texte (pour la description). Lcriture dun composant avec JSF2.0 est relativement proche de celle que lon utilise pour Java: on crit dabord une interface, <composite:interface> (voir Listing11.13), qui sert de point dentre pour le composant elle dcrit les noms et les paramtres quil utilise. Puis on passe limplmentation: <composite:implementation> est le corps du composant crit en XHTML avec des marqueurs JSF ou des templates.

378

Java EE 6 et GlassFish 3

Create a new CD
Tiltle : Price :

Create a new book


Tiltle : Price :

Description :

Description :

Music company : Number of CDs : Total duration : Gender :

ISBN : Number of pages : Illustrations :

Create a book
APress Beginning Java EE 6

Create a cd
APress Beginning Java EE 6

Figure11.9 Deux formulaires: lun pour crer un CD, lautre pour crer un livre.

Linterface et limplmentation se trouvent dans la mme page. Ici, notre implmentation utilise les lments <tr> et <td> car nous supposons que le composant sera plac dans un tableau <table> de deux colonnes.
Listing11.13: La page newItem.xhtml contient un composant composite
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:composite="http://java.sun.com/jsf/composite"> <composite:interface> <composite:attribute name="item" required="true"/> <composite:attribute name="style" required="false"/> </composite:interface> <composite:implementation> <tr style="#{compositeComponent.attrs.style}"> <td>Title :</td> <td> <h:inputText value="#{compositeComponent.attrs.item.title}"/> </td> </tr> <tr style="#{compositeComponent.attrs.style}">

Chapitre 11

Pages et composants 379

<td>Price :</td> <td> <h:inputText value="#{compositeComponent.attrs.item.price}"/> </td> </tr> <tr style="#{compositeComponent.attrs.style}"> <td>Description :</td> <td> <h:inputTextarea value="#{compositeComponent.attrs.item.description}" cols="20" rows="5"/> </td> </tr> </composite:implementation> </html>

Ce composant dclare une interface avec deux attributs : item reprsente lentit Item (et les sous-classes Book et CD) et style est une feuille de style CSS utilise pour la prsentation. Ces attributs sont ensuite utiliss par limplmentation du composant laide de la syntaxe suivante:
#{compositeComponent.attrs.style}

Ce code indique un appel de la mthode getAttributes() du composant composite courant; le code recherche ensuite dans lobjet Map quelle renvoie la valeur correspondant la cl style. Avant dexpliquer comment utiliser ce composant, il faut se rappeler les principes de la gestion des ressources et la notion de configuration par exception: le composant doit tre stock dans un fichier situ dans une bibliothque de ressources. Ici, par exemple, ce fichier sappelle newItem.xhtml et a t plac dans le rpertoire /resources/apress. Si lon se fie au comportement par dfaut, lutilisation du composant ncessite simplement de dclarer une bibliothque appele apress et de lui associer un espace de noms XML:
<html xmlns:ago="http://java.sun.com/jsf/composite/apress">

Puis on appelle le composant newItem (le nom de la page) en lui passant les paramtres quil attend: item dsigne lentit Item et style est le paramtre facultatif dsignant une feuille de style CSS:
<ago:newItem item="#{itemController.book}" style="myCssStyle"/> <ago:newItem item="#{itemController.cd}"/>

Le Listing 11.14 montre la page newBook.xhtml reprsentant le formulaire pour entrer les informations sur un livre. Elle inclut le composant newItem et ajoute des

380

Java EE 6 et GlassFish 3

champs de saisie pour lISBN et le nombre de pages, ainsi quune case cocher pour indiquer si le livre contient, ou non, des illustrations.
Listing11.14: La page newBook.xhtml utilise le composant newItem
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ago="http://java.sun.com/jsf/composite/apress"> <h:head> <title>Creates a new book</title> </h:head> <h:body> <h1>Create a new book</h1> <hr/> <h:form> <table border="0"> <ago:newItem item="#{itemController.book}"/> <tr> <td><h:outputLabel value="ISBN : "/></td> <td> <h:inputText value="#{itemController.book.isbn}"/> </td> </tr> <tr> <td><h:outputLabel value="Number of pages : "/></td> <td> <h:inputText value="#{itemController.book.nbOfPage}"/> </td> </tr> <tr> <td><h:outputLabel value="Illustrations : "/></td> <td> <h:selectBooleanCheckbox value="#{itemController.book.illustrations}"/> </td> </tr> </table> <h:commandButton value="Create a book" action="#{itemController.doCreateBook}"/>

Chapitre 11

Pages et composants 381

</h:form> <hr/> <i>APress - Beginning Java EE 6</i> </h:body> </html>

Objets implicites

Limplmentation du composant composite que nous venons de crer utilise un objet compositeComponent pourtant, on ne la dclar nulle part: il est simplement l pour permettre daccder aux attributs du composant. Ces types dobjets sont appels objets implicites (ou variables implicites): ce sont des identificateurs spciaux qui correspondent des objets spcifiques souvent utiliss. Ils sont implicites parce quune page y a accs et peut les utiliser sans avoir besoin de les dclarer ou de les initialiser explicitement. Ces objets (numrs dans le Tableau11.19) sont utiliss dans des expressions EL.
Tableau11.19: Objets implicites

Objet implicite
application

Description Reprsente lenvironnement de lapplication web. Sert obtenir les paramtres de configuration de cette application. Associe les noms dattributs de lapplication leurs valeurs. Dsigne le composant courant. Dsigne le composant composite courant. Dsigne un Map contenant les noms des cookies (cls) et des objets Cookie. Dsigne linstance FacesContext de cette requte. Fait correspondre chaque nom den-tte HTTP une seule valeur de type String. Fait correspondre chaque nom den-tte HTTP un tableau String[] contenant toutes les valeurs de cet en-tte.

Type renvoy
Object

applicationScope component compositeComponent cookie facesContext

Map UIComponent UIComponent Map FacesContext Map Map

header
headerValue

382

Java EE 6 et GlassFish 3

Tableau11.19: Objets implicites (suite)

Objet implicite
initParam

Description Fait correspondre les noms des paramtres dinitialisation du contexte leurs valeurs de type String. Fait correspondre chaque nom de paramtre une seule valeur de type String. Fait correspondre chaque nom de paramtre un tableau String[] contenant toutes les valeurs de ce paramtre. Reprsente lobjet requte HTTP. Fait correspondre les noms des attributs de la requte leurs valeurs. Indique lobjet ressource. Reprsente lobjet session http. Fait correspondre les noms des attributs de la session leurs valeurs. Reprsente la vue courante. Fait correspondre les noms des attributs de la vue leurs valeurs.

Type renvoy
Map

param paramValues

Map Map

request requestScope resource session sessionScope view viewScope

Object Map Object Object Map UIViewRoot Map

Tous ces objets implicites sont de vrais objets avec des interfaces : vous pouvez accder leurs attributs avec EL (consultez la spcification). #{view. Locale}, par exemple, permet dobtenir la locale de la vue courante (en_US, pt_PT, etc.). Si vous stockez un livre dans la porte de la session, par exemple, vous pouvez y accder par #{sessionScope.book}. Vous pouvez mme utiliser un algorithme plus labor pour afficher tous les en-ttes HTTP et leurs valeurs:
<h3>headerValues</h3> <c:forEach var="parameter" items="#{headerValues}"> <h:outputText value="#{parameter.key}"/> = <c:forEach var="value" items="#{parameter.value}"> <h:outputText value="#{value}" escape="false"/> <br/> </c:forEach> </c:forEach>

Si vous excutez cette page, vous obtiendrez le rsultat suivant:

Chapitre 11

Pages et composants 383

Rsum
Ce chapitre a prsent les diffrents moyens de crer des pages web statiques laide de langages comme HTML, XHTML ou CSS et dynamiques avec JavaScript ou les technologies ct serveur. Pour crer des interfaces web dynamiques avec Java EE 6, vous avez le choix entre plusieurs spcifications. JSP 2.2, EL 2.2 et JSTL1.2 ont t cres en pensant aux servlets, leurs pages sont formes dinformations HTML et de code Java compils dans une servlet qui renvoie une rponse une requte donne. Bien que lon puisse se servir de JSP comme PDL (Presentation Description Language) pour JSF, il est prfrable dutiliser Facelets afin de disposer de la puissance de larchitecture des composants JSF et de son cycle de vie labor. JSF fournit un ensemble de widgets standard (boutons, liens, cases cocher, etc.) et un nouveau modle pour crer ses propres composants (composants composites). JSF2.0 dispose galement dun nouveau mcanisme de gestion des ressources permettant de grer de faon simple les locales et les versions des ressources externes. JSF 2.0 utilise une architecture de composants UI sophistique ; ses composants peuvent tre convertis et valids, et interagir avec les beans grs, qui sont prsents dans le prochain chapitre.

12
Traitement et navigation
Au chapitre prcdent, nous avons vu comment crer des pages web avec diffrentes technologies (HTML, JSP, JSTL, etc.) en insistant sur le fait que JSF est la spcification conseille pour crire des applications web modernes avec JavaEE. Cependant, crer des pages contenant des composants graphiques ne suffit pas: ces pages doivent interagir avec un backend (un processus en arrire-plan), il faut pouvoir naviguer entre les pages et valider et convertir les donnes. JSF est une spcification trs riche: les beans grs permettent dinvoquer la couche mtier, de naviguer dans votre application, et, grce un ensemble de classes, vous pouvez convertir les valeurs des composants ou les valider pour quils correspondent aux rgles mtiers. Grce aux annotations, le dveloppement de convertisseurs et de validateurs personnaliss est dsormais chose facile. JSF 2.0 apporte la simplicit et la richesse aux interfaces utilisateurs dynamiques. Il reconnat nativement Ajax en fournissant une bibliothque JavaScript permettant deffectuer des appels asynchrones vers le serveur et de rafrachir une page par parties. La cration dinterfaces utilisateurs, le contrle de la navigation dans lapplication et les appels synchrones ou asynchrones de la logique mtier sont possibles parce que JSF utilise le modle de conception MVC (Modle-Vue-Contrleur). Chaque partie est donc isole des autres, ce qui permet de modifier linterface utilisateur sans consquence sur la logique mtier et vice versa.

Le modle MVC
JSF et la plupart des frameworks web encouragent la sparation des problmes en utilisant des variantes du modle MVC. Ce dernier est un modle darchitecture permettant disoler la logique mtier de linterface utilisateur car la premire ne se

386

Java EE 6 et GlassFish 3

mlange pas bien avec la seconde: leur mlange produit des applications plus difficiles maintenir et qui supportent moins bien la monte en charge. Dans la section "JavaServer Pages" du chapitre prcdent, nous avons vu une page JSP qui contenait la fois du code Java et des instructions SQL: bien que ce soit techniquement correct, imaginez la difficult de maintenir une telle page... Elle mlange deux types de dveloppement diffrents (celui de concepteur graphique et celui de programmeur mtier) et pourrait finir par utiliser bien plus dAPI encore (accs aux bases de donnes, appels dEJB, etc.), par grer les exceptions ou par effectuer des traitements mtiers complexes. Avec MVC, lapplication utilise un couplage faible, ce qui facilite la modification de son aspect visuel ou des rgles mtiers sous-jacentes sans pour autant affecter lautre composante. Comme le montre la Figure12.1, la partie "modle" de MVC reprsente les donnes de lapplication; la "vue" correspond linterface utilisateur et le "contrleur" gre la communication entre les deux.
Figure12.1 Le modle de conception MVC.
Navigateur Client Requte HTTP Server Contrleur (FacesServlet) cre et gre Modle (backing bean) accde Vue (pages XHTML)

Rponse HTTP manipule et redirige

Le modle est reprsent par le contenu, qui est souvent stock dans une base de donnes et affich dans la vue; il ne se soucie pas de laspect que verra lutilisateur. Avec JSF, il peut tre form de backing beans, dappels EJB, dentits JPA, etc. La vue JSF est la vritable page XHTML (XHTML est rserv aux interfaces web, mais il pourrait sagir dun autre type de vue, comme WML pour les dispositifs mobiles). Comme au chapitre prcdent, une vue fournit une reprsentation graphique dun modle et un modle peut avoir plusieurs vues pour afficher un livre sous forme de formulaire ou de liste, par exemple. Lorsquun utilisateur manipule une vue, celle-ci informe un contrleur des modifications souhaites. Ce contrleur se charge alors de rassembler, convertir et valider les donnes, appelle la logique mtier puis produit le contenu en XHTML. Avec JSF, le contrleur est un objet FacesServlet.

Chapitre 12

Traitement et navigation 387

FacesServlet
FacesServlet est une implmentation de javax.servlet.Servlet qui sert de contr-

leur central par lequel passent toutes les requtes. Comme le montre la Figure12.2, la survenue dun lment (lorsque lutilisateur clique sur un bouton, par exemple) provoque lenvoi dune notification au serveur via HTTP; celle-ci est intercepte par javax.faces.webapp.FacesServlet, qui examine la requte et excute diffrentes actions sur le modle laide de beans grs.
Figure12.2 Interactions de FacesServlet.
Cycle de vie 3. Traitement en 6 tapes 2. Passe le contrle au cycle de vie FacesContext 1. Cre un FaceContext Bouton vnement FacesServlet

En coulisse, la FacesServlet prend les requtes entrantes et donne le contrle lobjet javax.faces.lifecycle.Lifecycle. laide dune mthode fabrique, elle cre un objet javax.faces.context.FacesContext qui contient et traite les informations dtat de chaque requte. Lobjet Lifecycle utilise ce FacesContext en six tapes (dcrites au chapitre prcdent) avant de produire la rponse. Les requtes qui doivent tre traites par la FacesServlet sont rediriges laide dune association de servlet dans le descripteur de dploiement. Les pages web, les beans grs, les convertisseurs, etc. doivent tre assembls avec le fichier web.xml du Listing12.1.
Listing12.1: Fichier web.xml dfinissant la FacesServlet
<?xml version=1.0 encoding=UTF-8?> <web-app version="2.5" 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"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name>

388

Java EE 6 et GlassFish 3

<url-pattern>*.faces</url-pattern> </servlet-mapping> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> </web-app>

Ce fichier dfinit la javax.faces.webapp.FacesServlet en lui donnant un nom (Faces Servlet, ici) et une association. Dans cet exemple, toutes les requtes portant lextension .faces sont associes pour tre gres par la servlet toute requte de la forme http://localhost:8080/ chapter10-1.0/newBook.faces sera donc traite par JSF. Vous pouvez galement configurer quelques paramtres spcifiques JSF dans llment <context-param> (voir Tableau12.1).
Tableau12.1: Paramtres de configuration spcifiques JSF

Paramtre
javax.faces.CONFIG_FILES javax.faces.DEFAULT_SUFFIX javax.faces.LIFECYCLE_ID javax.faces.STATE_SAVING_ METHOD

Description Dfinit une liste de chemins de ressources lies au contexte dans laquelle JSF recherchera les ressources. Permet de dfinir une liste de suffixes possibles pour les pages ayant du contenu JSF (.xhtml, par exemple). Identifie linstance LifeCycle utilise pour traiter les requtes JSF. Dfinit lemplacement de sauvegarde de ltat. Les valeurs possibles sont server (valeur par dfaut qui indique que ltat sera gnralement sauvegard dans un objet HttpSession) et client (ltat sera sauvegard dans un champ cach lors du prochain envoi de formulaire). Dcrit ltape dans laquelle se trouve cette application JSF dans le cycle de vie (Development, UnitTest, SystemTest ou Production). Cette information peut tre utilise par une implmentation de JSF pour amliorer les performances lors de la phase de production en utilisant un cache pour les ressources, par exemple. Dsactive Facelets comme langage de dclaration de page (PDL). Liste des chemins qui seront considrs comme une bibliothque de marqueurs Facelets.

javax.faces.PROJECT_STAGE

javax.faces.DISABLE_FACELET_ JSF_VIEWHANDLER javax.faces.LIBRARIES

Chapitre 12

Traitement et navigation 389

FacesContext

JSF dfinit la classe abstraite javax.faces.context.FacesContext pour reprsenter les informations contextuelles associes au traitement dune requte et la production de la rponse correspondante. Cette classe permet dinteragir avec linterface utilisateur et le reste de lenvironnement JSF. Pour y accder, vous devez soit utiliser lobjet implicite facesContext dans vos pages (les objets implicites ont t prsents au chapitre prcdent), soit obtenir une rfrence dans vos beans grs laide de la mthode statique getCurrentInstance(): celle-ci renverra linstance de FacesContext pour le thread courant et vous pourrez alors invoquer les mthodes du Tableau12.2.
Tableau12.2: Quelques mthodes de FacesContext

Mthode
addMessage getApplication

Description Ajoute un message derreur. Renvoie linstance Application associe cette application web. Renvoie un objet Map reprsentant les attributs associs linstance FacesContext. Renvoie linstance FacesContext pour la requte traite par le thread courant. Renvoie le niveau dimportance maximal pour tout FacesMessage mis en file dattente. Renvoie une collection de FacesMessage. Renvoie le composant racine associ la requte. Libre les ressources associes cette instance de FacesContext. Signale limplmentation JSF que le contrle devra tre transmis la phase Render response ds la fin de ltape de traitement courante de la requte, en ignorant les tapes qui nont pas encore t excutes. Signale limplmentation JSF que la rponse HTTP de cette requte a dj t produite et que le cycle de vie du traitement de la requte doit se terminer ds la fin de ltape en cours.

getAttributes

getCurrentInstance

getMaximumSeverity

getMessages getViewRoot release renderResponse

responseComplete

390

Java EE 6 et GlassFish 3

Configuration de Faces

La FacesServlet est interne aux implmentations de JSF; bien que vous nayez pas accs son code, vous pouvez la configurer avec des mtadonnes. Vous savez dsormais quil existe deux moyens dindiquer des mtadonnes avec Java EE6: les annotations et les descripteurs de dploiement XML (/WEB-INF/faces-config. xml). Avant JSF2.0, le seul choix possible tait XML mais, dsormais, les beans grs, les convertisseurs, les moteurs de rendu et les validateurs pouvant utiliser les annotations, les fichiers de configuration XML sont devenus facultatifs. Nous conseillons lemploi des annotations mais, pour montrer quoi ressemble un fichier faces-config.xml, le Listing 12.2 dfinit une locale et un ensemble de messages pour linternationalisation et certaines rgles de navigation. Nous verrons ensuite comment naviguer avec et sans faces-config.xml.
Listing12.2: Extrait dun fichier faces-config.xml
<?xml version=1.0 encoding=UTF-8?> <faces-config 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-facesconfig_2_0.xsd" version="2.0"> <application> <locale-config> <default-locale>fr</default-locale> </locale-config> <resource-bundle> <base-name>messages</base-name> <var>msg</var> </resource-bundle> </application> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>doCreateBook-success</from-outcome> <to-view-id>/listBooks.htm</to-view-id> </navigation-case> </navigation-rule> </faces-config>

Beans grs
Comme on la indiqu plus haut, le modle MVC encourage la sparation entre le modle, la vue et le contrleur. Avec JavaEE, les pages JSF forment la vue et la

Chapitre 12

Traitement et navigation 391

FacesServlet

est le contrleur. Les beans grs, quant eux, sont une passerelle vers le modle. Les beans grs sont des classes Java annotes. Ils constituent le cur des applications web car ils excutent la logique mtier (ou la dlguent aux EJB, par exemple), grent la navigation entre les pages et stockent les donnes. Une application JSF typique contient un ou plusieurs beans grs qui peuvent tre partags par plusieurs pages. Les donnes sont stockes dans les attributs du bean gr, qui, en ce cas, est galement appel "backing bean". Un backing bean dfinit les donnes auxquelles est li un composant de linterface utilisateur (la cible dun formulaire, par exemple). Pour tablir cette liaison, on utilise EL, le langage dexpressions.

criture dun bean gr

crire un bean gr est aussi simple qucrire un EJB ou une entit JPA puisquil sagit simplement de crer une classe Java annote par @ManagedBean (voir Listing12.3) il ny a nul besoin de crer des entres dans faces-config.xml, de crer des classes utilitaires ou dhriter dune classe quelconque: JSF 2.0 utilisant galement le mcanisme de configuration par exception, une seule annotation suffit pour utiliser tous les comportements par dfaut et pour dployer une application web utilisant un bean gr.
Listing12.3: Bean gr simple
@ManagedBean public class BookController { private Book book = new Book(); public String doCreateBook() { createBook(book); return "listBooks.xhtml"; } } // Constructeurs, getters, setters

Le Listing 12.3 met en vidence le modle de programmation dun bean gr : il stocke ltat (lattribut book), dfinit les mthodes daction (doCreateBook()) utilises par une page et gre la navigation (return "listBooks.xhtml").
Modle dun bean gr

Les beans grs sont des classes Java prises en charge par la FacesServlet. Les composants de linterface utilisateur sont lis aux proprits du bean (backing bean) et

392

Java EE 6 et GlassFish 3

peuvent invoquer des mthodes daction. Un bean gr doit respecter les contraintes suivantes:

La classe doit tre annote par @javax.faces.model.ManagedBean ou son quivalent dans le descripteur de dploiement XML faces-config.xml. La classe doit avoir une porte (qui vaut par dfaut @RequestScoped). La classe doit tre publique et non finale ni abstraite. La classe doit fournir un constructeur public sans paramtre qui sera utilis par le conteneur pour crer les instances. La classe ne doit pas dfinir de mthode finalize(). Pour tre lis un composant, les attributs doivent avoir des getters et des setters publics.

Bien quun bean gr puisse tre un simple POJO annot, sa configuration peut tre personnalise grce aux lments de @ManagedBean et @ManagedProperty (ou leurs quivalents XML).
@ManagedBean

La prsence de lannotation @javax.faces.model.ManagedBean sur une classe lenregistre automatiquement comme un bean gr. La Figure12.4 prsente lAPI de cette annotation, dont tous les lments sont facultatifs.
Listing12.4: API de lannotation ManagedBean
@Target(TYPE) @Retention(RUNTIME) public @interface ManagedBean { String name() default ""; boolean eager() default false; }

Llment name indique le nom du bean gr (par dfaut, ce nom est celui de la classe commenant par une minuscule). Si llment eager vaut true, le bean gr est instanci ds le dmarrage de lapplication. Les composants de linterface utilisateur tant lis aux proprits dun bean gr, changer son nom par dfaut a des rpercussions sur la faon dappeler une proprit ou une mthode. Le code du Listing12.5, par exemple, renomme le bean gr Book Controller en myManagedBean.

Chapitre 12

Traitement et navigation 393

Listing12.5: Changement du nom par dfaut dun bean gr


@ManagedBean(name = "myManagedBean") public class BookController { private Book book = new Book(); public String doCreateBook() { createBook(book); return "listBooks.xhtml"; } // Constructeurs, getters, setters }

Pour invoquer les attributs ou les mthodes de ce bean, vous devez donc utiliser son nouveau nom:
<h:outputText value="#{myManagedBean.book.isbn}"/> <h:form> <h:commandLink action="#{myManagedBean.doCreateBook}"> Create a new book </h:commandLink> </h:form>

Portes

Les objets crs dans le cadre dun bean gr ont une certaine dure de vie et peuvent ou non tre accessibles aux composants de linterface utilisateur ou aux objets de lapplication. Cette dure de vie et cette accessibilit sont regroupes dans la notion de porte. Cinq annotations permettent de dfinir la porte dun bean gr:
@ApplicationScoped.

Il sagit de lannotation la moins restrictive, avec la plus longue dure de vie. Les objets crs sont disponibles dans tous les cycles requte/ rponse de tous les clients utilisant lapplication tant que celle-ci est active. Ces objets peuvent tre appels de faon concurrente et doivent donc tre threadsafe (cest--dire utiliser le mot-cl synchronized). Les objets ayant cette porte peuvent utiliser dautres objets sans porte ou avec une porte dapplication. de la session du client. Leur tat persiste entre les requtes et dure jusqu la fin de la session. Ils peuvent utiliser dautres objets sans porte, avec une porte de session ou dapplication.

@SessionScoped. Ces objets sont disponibles pour tous les cycles requte/rponse

@ViewScoped. Ces objets sont disponibles dans une vue donne jusqu sa modi-

fication. Leur tat persiste jusqu ce que lutilisateur navigue vers une autre vue,

394

Java EE 6 et GlassFish 3

auquel cas il est supprim. Ils peuvent utiliser dautres objets sans porte, avec une porte de vue, de session ou dapplication.
@RequestScoped.

Il sagit de la porte par dfaut. Ces objets sont disponibles du dbut dune requte jusquau moment o la rponse est envoye au client. Un client pouvant excuter plusieurs requtes tout en restant sur la mme vue, la dure de @ViewScoped est suprieure celle de @RequestScoped. Les objets ayant cette porte peuvent utiliser dautres objets sans porte, avec une porte de requte, de vue, de session ou dapplication. Les beans grs ayant cette porte ne sont visibles dans aucune page JSF; ils dfinissent des objets utiliss par dautres beans grs de lapplication. Ils peuvent utiliser dautres objets avec la mme porte.

@NoneScoped.

La porte des beans grs doit tre judicieusement choisie : vous ne devez leur donner que la porte dont ils ont besoin. Des beans ayant une porte trop grande (@ ApplicationScoped, par exemple) augmentent lutilisation mmoire et lutilisation du disque pour leur persistance ventuelle. Il ny a aucune raison de donner une porte dapplication un objet qui nest utilis que dans un seul composant. Inversement, un objet ayant une porte trop restreinte ne sera pas disponible dans certaines parties de lapplication. Le code du Listing12.6 dfinit un bean gr avec une porte dapplication. Il sera instanci ds le lancement de lapplication (eager = true) et initialise lattribut defaultBook ds quil est construit (@PostConstruct). Il pourrait donc tre le bean idal pour initialiser des parties de lapplication web ou pour tre rfrenc par des proprits dautres beans grs.
Listing12.6: Bean gr avec une porte dapplication et une instanciation eager
@ManagedBean(eager = true) @ApplicationScoped public class InitController { private Book defaultBook; @PostConstruct private void init() { defaultBook=new Book("default title", 0, "default description", "0000-000", 100, true); } // Constructeurs, getters, setters }

Chapitre 12

Traitement et navigation 395

@ManagedProperty

Dans un bean gr, vous pouvez demander au systme dinjecter une valeur dans une proprit (un attribut avec des getters et/ou des setters) en utilisant le fichier faces-config.xml ou lannotation @javax.faces.model.ManagedProperty, dont lattribut value peut recevoir une chane ou une expression EL. Le Listing 12.7 montre quelques exemples dinitialisations.
Listing12.7: Initialisation des proprits dun bean gr
@ManagedBean public class BookController { @ManagedProperty(value = "#{initController.defaultBook}") private Book book; @ManagedProperty(value = "this is a title") private String aTitle; @ManagedProperty(value = "999") private Integer aPrice; // Constructeurs, getters, setters & mthodes }

Dans le Listing12.7, les proprits aTitle et aPrice sont initialises avec une valeur de type String. Lattribut aTitle, de type String, sera initialis avec "this is a title" et lattribut aPrice, qui est un Integer, sera initialis avec le nombre 999 (bien que "999" soit une chane, celle-ci sera convertie en Integer). Les proprits tant values lors de lexcution (gnralement lorsquune vue est affiche), celles qui font rfrence dautres beans grs peuvent aussi tre initialises. Ici, par exemple, book est initialis par une expression utilisant la proprit defaultBook du bean gr initController (#{initController.defaultBook}) que nous avons prsent plus haut. Le Listing12.6 montre que defaultBook est un attribut de type Book initialis par le bean InitController : lorsque BookController est initialis, limplmentation JSF injectera donc cet attribut. Il est gnralement conseill dinitialiser les littraux dans le fichier faces-config.xml et dutiliser les annotations pour les rfrences croises entre les beans grs (en utilisant EL).
Annotation du cycle de vie et des mthodes de rappel

Le chapitre prcdent a expliqu le cycle de vie dune page (qui compte six phases, de la rception de la requte la production de la rponse), mais le cycle de vie des

396

Java EE 6 et GlassFish 3

beans grs (voir Figure12.3) est totalement diffrent: en fait, il est identique celui des beans de session sans tat.
Figure12.3 Cycle de vie dun bean gr.
N'existe pas @PostConstruct Prt @PreDestroy

Appel de mthode

Les beans grs qui sexcutent dans un conteneur de servlet peuvent utiliser les annotations @PostConstruct et @PreDestroy. Aprs avoir cr une instance de bean gr, le conteneur appelle la mthode de rappel @PostConstruct sil y en a une. Puis le bean est li une porte et rpond toutes les requtes de tous les utilisateurs. Avant de supprimer le bean, le conteneur appelle la mthode @PreDestroy. Ces mthodes permettent donc dinitialiser les attributs ou de crer et librer les ressources externes.
Navigation

Les applications web sont formes de plusieurs pages entre lesquelles vous devez naviguer. Selon les cas, il peut exister diffrents niveaux de navigation avec des flux de pages plus ou moins labors. JSF dispose de plusieurs options de navigation et vous permet de contrler le flux page par page ou pour toute lapplication. Les composants <h:commandButton> et <h:commandLink> permettent de passer simplement dune page une autre en cliquant sur un bouton ou sur un lien sans effectuer aucun traitement. Il suffit dinitialiser leur attribut action avec le nom de la page vers laquelle vous voulez vous rendre:
<h:commandButton value="Create" action="listBooks.xhtml"/>

Cependant, la plupart du temps, ceci ne suffira pas car vous aurez besoin daccder une couche mtier ou une base de donnes pour rcuprer ou traiter des donnes. En ce cas, vous aurez besoin dun bean gr. Dans la section "Rcapitulatif" du Chapitre10, une premire page (newBook.xhtml) affichait un formulaire permettant de crer un livre. Lorsque lon cliquait sur le bouton Create, le livre tait cr puis le bean gr passait la page listBooks.xhtml, qui affichait tous les livres. Cette page contenait un lien Create a new book permettant de revenir la page prcdente (voir Figure12.4).

Chapitre 12

Traitement et navigation 397

newBook.xhtml

listBooks.xhtml <h:commandButton>

Create a new book


ISBN Tiltle Price

List of the books


ISBN 564 694 Title Price 12 0 Description Scifi IT book Asimov Best seller Number Of Pages 241 317 529 Illustrations false true false 1234 234 H2G2 256 6 56 Dune Create a new book APress Beginning Java EE 6

Robots 18 5

<h:commandLink>
Description

23 25 The trilogy

Number of pages Illustrations

Create
APress Beginning Java EE 6

Figure12.4 Navigation entre newBook.xhtml et listBooks.xhtml.

Le flux des pages est simple, mais ces deux pages ont pourtant besoin dun bean gr (BookController) pour traiter la logique mtier et la navigation. Elles utilisent les composants bouton et lien pour naviguer et interagir avec ce bean. La page newBook.xhtml utilise un bouton pour appeler la mthode doCreateBook() du bean gr:
<h:commandButton value="Create" action="#{bookController.doCreateBook}"/>

La page listBooks.xhtml utilise un lien pour appeler la mthode doNewBookForm():


<h:commandLink action="#{bookController.doNewBookForm}"> Create a new book </h:commandLink>

Les composants bouton et lien nappellent pas directement la page vers laquelle ils doivent se rendre: ils appellent des mthodes du bean gr qui prennent en charge cette navigation et laissent le code dcider de la page qui sera charge ensuite. La navigation utilise un ensemble de rgles qui dfinissent tous les chemins de navigation possibles de lapplication. Dans le Listing12.8, le code du bean gr utilise la forme la plus simple de ces rgles de navigation: chaque mthode dfinit la page vers laquelle elle doit aller.
Listing12.8: Bean gr dfinissant explicitement la navigation
@ManagedBean public class BookController { @EJB private BookEJB bookEJB;

398

Java EE 6 et GlassFish 3

private Book book = new Book(); private List<Book> bookList = new ArrayList<Book>(); public String doNewBookForm() { return "newBook.xhtml"; } public String doCreateBook() { book = bookEJB.createBook(book); bookList = bookEJB.findBooks(); return "listBooks.xhtml"; } // Constructeurs, getters, setters }

Quand le <h:commandButton> invoque la mthode doCreateBook(), celle-ci cre un livre ( laide dun bean de session sans tat) et renvoie le nom de la page vers laquelle naviguer ensuite: listBooks.xhtml. La FacesServlet redirigera alors le flux de page vers la page dsire. La chane renvoye peut prendre plusieurs formes. Ici, nous avons utilis la plus simple : le nom de la page. Lextension de fichier par dfaut tant .xhtml, nous aurions mme pu simplifier le code en supprimant lextension:
public String doNewBookForm() { return "newBook"; }

Avec JSF, le flux de navigation peut tre dfini en externe via faces-config.xml, laide dlments <navigation-rule> qui identifient la page de dpart, une condition et la page vers laquelle naviguer lorsque la condition sera vrifie. Celle-ci utilise un nom logique au lieu du nom physique de la page. Comme le montre le Listing12.9, le code prcdent aurait pu utiliser, par exemple, le nom success.
Listing12.9: Extrait dun bean gr utilisant des noms logiques
@ManagedBean public class BookController { // ... public String doNewBookForm() { return "success"; } public String doCreateBook() { book = bookEJB.createBook(book); bookList = bookEJB.findBooks();

Chapitre 12

Traitement et navigation 399

} }

return "success";

// Constructeurs, getters, setters

Les deux mthodes renvoyant le mme nom logique, le fichier faces-config.xml doit donc faire correspondre ce nom la page newBook.xhtml dans un cas et la page listBooks.xhtml dans lautre. Le Listing12.10 montre la structure de facesconfig.xml: llment <from-view-id> dfinit la page dans laquelle a lieu laction. Dans le premier cas, on part de newBook.xhtml avant dappeler le bean gr: si le nom logique renvoy est success (<from-outcome>), la FacesServlet fera suivre lappel la page listBooks.xhtml (<to-view-id>).
Listing12.10: Fichier faces-config.xml dfinissant la navigation
<?xml version=1.0 encoding=UTF-8?> <faces-config 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-facesconfig_2_0.xsd" version="2.0"> <navigation-rule> <from-view-id>newBook.xhtml</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>listBooks.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>listBooks.xhtml</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>newBook.xhtml</to-view-id> </navigation-case> </navigation-rule> </faces-config>

La navigation pouvant avoir lieu directement dans les beans grs ou au moyen de faces-config.xml, quelle solution utiliser plutt quune autre? La premire motivation pour renvoyer directement le nom de la page dans les beans grs est la simplicit: le code Java est explicite et il ny a pas besoin de passer par un fichier XML supplmentaire. Si, en revanche, le flux des pages dune application web est complexe, il peut tre judicieux de le dcrire un seul endroit afin que les modifications soient centralises au lieu dtre disperses dans plusieurs pages. Vous pouvez galement mlanger ces deux approches et effectuer une partie de la navigation dans vos beans et une autre dans le fichier faces-config.xml.

400

Java EE 6 et GlassFish 3

Il existe un cas o une configuration XML est trs utile: cest lorsque plusieurs pages contiennent des liens globaux (lorsque, par exemple, la connexion ou la dconnexion peuvent tre appeles partir de toutes les pages de lapplication) car il serait assez lourd de devoir les dfinir dans chaque page. Avec XML, vous pouvez dfinir des rgles de navigation globales (ce qui nest pas possible avec les beans grs):
<navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>logout</from-outcome> <to-view-id>logout.xhtml</to-view-id> </navigation-case> </navigation-rule>

Si une action sapplique toutes les pages dune application, vous pouvez utiliser un lment <navigation- rule> sans <from-view-id> ou utiliser un joker (*). Le code prcdent indique que, quelle que soit la page o il se trouve, lutilisateur sera dirig vers la page logout.xhtml si la mthode du bean gr renvoie le nom logique logout. Les exemples prcdents ont montr une navigation simple o une page navait quune seule rgle de navigation et une seule page destination. Ce nest pas un cas si frquent: les utilisateurs peuvent gnralement tre redirigs vers des pages diffrentes, en fonction de certaines conditions. Cette navigation, l encore, peut tre mise en place dans les beans grs et dans le fichier faces-config.xml. Le code suivant utilise une instruction switch pour rediriger lutilisateur vers trois pages possibles. Si lon renvoie la valeur null, lutilisateur reviendra sur la page sur laquelle il se trouve dj.
public String doNewBookForm() { switch (value) { case 1: return "page1.xhtml"; break; case 2: return "page2.xhtml"; break; case 3: return "page3.xhtml"; break; default: return null; break; } }

Gestion des messages

Les beans grs traitent la logique mtier, appellent les EJB, utilisent les bases de donnes, etc. Parfois, cependant, un problme peut survenir et, en ce cas, lutilisateur doit en tre inform par un message qui peut tre un message derreur de lapplication (concernant la logique mtier ou la connexion la base ou au rseau) ou un message derreur de saisie (un ISBN incorrect ou un champ vide, par exemple). Les erreurs dapplication peuvent produire une page particulire demandant

Chapitre 12

Traitement et navigation 401

lutilisateur de ressayer dans un moment, par exemple, alors que les erreurs de saisie peuvent safficher dans la mme page avec un texte dcrivant lerreur. On peut galement utiliser des messages pour informer lutilisateur quun livre a t correctement ajout la base de donnes. Au chapitre prcdent, nous avons utilis des marqueurs pour afficher des messages sur les pages (<h:message> et <h:messages>). Pour produire ces messages, JSF vous permet de les placer dans une file dattente en appelant la mthode FacesContext. addMessage() dans les beans grs. Sa signature est la suivante:
void addMessage(String clientId, FacesMessage message)

Cette mthode ajoute un FacesMessage lensemble des messages afficher. Son premier paramtre est lidentifiant dun client qui dsigne le composant dinterface auquel le message est rattach. Sil vaut null, ceci signifie que le message nest li aucun composant particulier et quil est global toutes les pages. Un message est form dun texte rsum, dun texte dtaill et dun niveau dimportance (fatal, error, warning et info). Les messages peuvent galement tre internationaliss par des ensembles de textes localiss (message bundles).
FacesMessage(Severity severity, String summary, String detail)

Le code suivant est un extrait dun bean gr qui cre un livre. Selon que cette cration russit ou quune exception survient, un message dinformation ou derreur est ajout la file dattente des messages afficher. Notez que ces deux messages sont globaux car lidentifiant du client vaut null:
FacesContext ctx = FacesContext.getCurrentInstance(); try { book = bookEJB.createBook(book); ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Book has been created", "The book" + book.getTitle() + " has been created with id=" + book.getId()) ); } catch (Exception e) { ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Book hasnt been created", e.getMessage()) ); } }

Ces messages tant globaux, on peut les afficher dans une page laide dun simple marqueur <h:messages>. On peut galement prfrer afficher un message un

402

Java EE 6 et GlassFish 3

endroit prcis pour un composant spcifique (ce qui est gnralement le cas avec les erreurs de validation ou de conversion). La Figure12.5, par exemple, montre une page avec un message spcifiquement destin au champ de saisie du prix.
Figure12.5 Page affichant un message pour un composant dinterface prcis.

Create a new book


ISBN : Tiltle : Price : Please, fill the price !

Description :

Number of pages : Illustrations :

Create
APress Beginning Java EE 6

Dans cette page, le champ de saisie du prix a un identifiant (id="priceId") auquel fait rfrence le marqueur <h:message> (for="priceId"). En consquence, ce message prcis ne saffichera que pour ce composant:
<h:inputText id="priceId" value="#{bookController.book.price}"/> <h:message for="priceId"/>

Si le champ du prix na pas t rempli, un message saffiche ct du champ de saisie. Le code qui suit vrifie le prix saisi et cre un message davertissement associ lidentifiant du composant si la valeur nest pas correcte:
if (book.getPrice() == null || "".equals(book.getPrice())) { ctx.addMessage("priceId", new FacesMessage(SEVERITY_WARN, "Please, fill the price !", "Enter a number value"));

JSF utilise galement ce mcanisme de message pour les convertisseurs et les validateurs.

Conversion et validation
Nous venons de voir comment grer les messages pour informer lutilisateur sur les actions entreprendre. Lune delles consiste corriger une saisie incorrecte (un ISBN non valide, par exemple). JSF fournit un mcanisme standard de conversion

Chapitre 12

Traitement et navigation 403

et de validation permettant de traiter les saisies des utilisateurs afin dassurer lintgrit des donnes. Lorsque vous invoquez des mthodes mtiers, vous pouvez donc vous fier des donnes valides: la conversion et la validation permettent aux dveloppeurs de se concentrer sur la logique mtier au lieu de passer du temps vrifier que les donnes saisies ne sont pas null, quelles appartiennent bien un intervalle prcis, etc. La conversion a lieu lorsque les donnes saisies par lutilisateur doivent tre transformes de String en un objet et vice versa. Elle garantit que les informations sont du bon type en convertissant, par exemple, un String en java.util.Date, un String en Integer ou des dollars en euros. Comme pour la validation, elle garantit que les donnes contiennent ce qui est attendu (une date au format jj/mm/aaaa, un rel compris entre 3,14 et 3,15, etc.). Comme le montre la Figure 12.6, la conversion et la validation interviennent au cours des diffrentes phases du cycle de vie de la page (que nous avons prsent au chapitre prcdent).
Valeurs des composants Valeurs des composants valides converties en objets validation standard conversion par dfaut validation personnalise conversion personnalise appel de la mthode getAsObject()

Restauration de la vue

Application des valeurs de la requte

Traitement des vnements

validations

Traitement des vnements

Valeurs des composants reconverties pour l'affichage appel de la mthode getAsString()

Affichage de la rponse

Traitement des vnements

Appel de l'application

Traitement des vnements

Mise jour des valeurs du modle

Erreurs de conversion ou de validation

Figure12.6 Conversion et validation au cours du cycle de vie dune page.

Au cours de la phase Application des valeurs de la requte de la Figure12.6, la valeur du composant de linterface est convertie dans lobjet cible puis valide au cours de la phase Traitement des validations. Il est logique que la conversion et la validation interviennent avant que les donnes du composant ne soient lies

404

Java EE 6 et GlassFish 3

au backing bean (ce qui a lieu au cours de la phase Mise jour des valeurs du modle). En cas derreur, des messages derreur seront ajouts et le cycle de vie sera court afin de passer directement lAffichage de la rponse (les messages seront alors affichs sur linterface utilisateur avec <h:messages>). Au cours decette phase, les proprits du backing bean sont reconverties en chanes pour pouvoir tre affiches. JSF fournit un ensemble de convertisseurs et de validateurs standard et vous permet de crer les vtres trs facilement.
Convertisseurs

Lorsquun formulaire est affich par un navigateur, lutilisateur remplit les champs et appuie sur un bouton ayant pour effet de transporter les donnes vers le serveur dans une requte HTTP forme de chanes. Avant de mettre jour le modle du bean gr, ces donnes textuelles doivent tre converties dans les objets cibles (Float, Integer, BigDecimal, etc.). Lopration inverse aura lieu lorsque les donnes seront renvoyes au client dans la rponse pour tre affiches par le navigateur. JSF fournit des convertisseurs pour les types classiques comme les dates et les nombres. Si une proprit du bean gr est dun type primitif (Integer, int, Float, float, etc.), JSF convertira automatiquement la valeur du composant dinterface dans le type adquat et inversement. Si elle est dun autre type, vous devrez fournir votre propre convertisseur. Le Tableau12.3 numre tous les convertisseurs standard du paquetage javax.faces.convert.
Tableau12.3: Convertisseurs standard

Convertisseur
BigDecimalConverter BigIntegerConverter BooleanConverter ByteConverter CharacterConverter DateTimeConverter DoubleConverter

Description Convertit un String en java.math.BigDecimal et vice versa. Convertit un String en java.math.BigInteger et vice versa. Convertit un String en Boolean (et boolean) et vice versa. Convertit un String en Byte (et byte) et vice versa. Convertit un String en Character (et char) et vice versa. Convertit un String en java.util.Date et vice versa. Convertit un String en Double (et double) et vice versa.

Chapitre 12

Traitement et navigation 405

Tableau12.3: Convertisseurs standard (suite)

Convertisseur
EnumConverter FloatConverter IntegerConverter LongConverter NumberConverter ShortConverter

Description Convertit un String en Enum (et enum) et vice versa. Convertit un String en Float (et float) et vice versa. Convertit un String en Integer (et int) et vice versa. Convertit un String en Long (et long) et vice versa. Convertit un String en classe abstraite java.lang.Number et vice versa. Convertit un String en Short (et short) et vice versa.

JSF convertira automatiquement les valeurs saisies en nombre lorsque la proprit du bean gr est dun type numrique primitif et en date ou en heure lorsque la proprit est dun type date. Si ces conversions automatiques ne conviennent pas, vous pouvez les contrler explicitement via les marqueurs standard convertNumber et convertDateTime. Pour ce faire, vous devez imbriquer le convertisseur dans un marqueur dentre ou de sortie. Il sera appel par JSF au cours du cycle de vie. Le marqueur convertNumber possde des attributs permettant de convertir la valeur dentre en nombre (comportement par dfaut), en valeur montaire ou en pourcentage. Vous pouvez prciser un symbole montaire ou un nombre de chiffres aprs la virgule, ainsi quun motif dterminant le format du nombre et la faon dont il sera analys:
<h:inputText value="#{bookController.book.price}"> <f:convertNumber currencySymbol="$" type="currency"/> </h:inputText>

Le marqueur convertDateTime convertit les dates dans diffrents formats (date, heure ou les deux). Il possde plusieurs attributs pour contrler cette conversion ainsi que les zones horaires. Lattribut pattern permet dindiquer le format de la chane de date convertir:
<h:inputText value="#{bookController.book.publishedDate}"> <f:convertDateTime pattern="MM/dd/yy"/> </h:inputText>

406

Java EE 6 et GlassFish 3

Convertisseurs personnaliss

Parfois, la conversion de nombres, de dates, dnumrations, etc. ne suffit pas et ncessite une conversion adapte la situation. Il suffit pour cela dcrire une classe qui implmente linterface javax.faces. convert.Converter et de lui associer des mtadonnes. Cette interface expose deux mthodes:
Object getAsObject(FacesContext ctx, UIComponent component, String value) String getAsString(FacesContext ctx, UIComponent component, Object value)

La mthode getAsObject() convertit la valeur chane dun composant dinterface utilisateur dans le type correspondant et renvoie la nouvelle instance ; elle lance une exception ConverterException si la conversion choue. Inversement, getAsString() convertit lobjet en chane afin quil puisse tre affich laide dun langage marqueurs (comme XHTML). Pour utiliser ce convertisseur dans lapplication web, il faut lenregistrer : une mthode consiste le dclarer dans le fichier faces-config.xml, lautre, utiliser lannotation @FacesConverter. Le Listing12.11 montre comment crire un convertisseur personnalis pour convertir un prix en dollars en valeur en euros. On commence par associer ce convertisseur au nom euroConverter (value = "euroConverter") laide de lannotation @ FacesConverter, puis on implmente linterface Converter. Cet exemple ne redfinit que la mthode getAsString() pour quelle renvoie une reprsentation textuelle dun prix en euros.
Listing12.11: Convertisseur en euros
@FacesConverter(value = "euroConverter") public class EuroConverter implements Converter { @Override public Object getAsObject(FacesContext ctx, UIComponent component, String value) { return value; } @Override public String getAsString(FacesContext ctx, UIComponent component, Object value) {

Chapitre 12

Traitement et navigation 407

float amountInDollars = Float.parseFloat(value.toString()); double ammountInEuros = amountInDollars * 0.8; DecimalFormat df = new DecimalFormat("###,##0.##"); return df.format(ammountInEuros); } }

Pour utiliser ce convertisseur, on utilise soit lattribut converter dun marqueur, soit le marqueur <f:converter>: dans les deux cas, il faut fournir le nom du convertisseur dfini par lannotation @FacesConverter (euroConverter, ici). Le code suivant affiche deux textes, lun reprsentant le prix en dollars, lautre ce prix converti en euros:
<h:outputText value="#{book.price}"/> <h:outputText value="#{book.price}"> <f:converter converterId="euroConverter"/> </h:outputText>

Vous pouvez galement utiliser lattribut converter du marqueur outputText:


<h:outputText value="#{book.price}" converter="euroConverter"/>

Validateurs

Les applications web doivent garantir que les donnes saisies par les utilisateurs sont appropries. Cette vrification peut avoir lieu ct client avec JavaScript ou ct serveur avec des validateurs. JSF simplifie la validation des donnes en fournissant des validateurs standard et en permettant den crer de nouveaux, adapts vos besoins. Les validateurs agissent comme des contrles de premier niveau en validant les valeurs des composants de linterface utilisateur avant quelles ne soient traites par le bean gr. Gnralement, les composants dinterface mettent en uvre une validation simple, comme vrifier quune valeur est obligatoire. Le marqueur suivant, par exemple, exige quune valeur soit entre dans le champ de saisie:
<h:inputText value="#{bookController.book.title}" required="true"/>

Si aucune valeur nest saisie, JSF renvoie la page avec un message indiquant quil faut en fournir une (la page doit avoir un marqueur <h:messages>) en utilisant le mme mcanisme de messages que nous avons dj dcrit. Mais JSF fournit galement un ensemble de validateurs plus labors (voir Tableau12.4) dfinis dans le paquetage javax.faces.validator.

408

Java EE 6 et GlassFish 3

Tableau12.4: Validateurs standard

Convertisseur
DoubleRangeValidator LengthValidator LongRangeValidator RegexValidator

Description Compare la valeur du composant aux valeurs minimales et maximales indiques (de type double). Teste le nombre de caractres de la valeur textuelle du composant. Compare la valeur du composant aux valeurs minimales et maximales indiques (de type long). Compare la valeur du composant une expression rgulire.

Ces validateurs permettent de traiter les cas gnriques comme la longueur dune chane ou un intervalle de valeurs; ils peuvent tre associs facilement un composant, de la mme faon que les convertisseurs (un mme composant peut contenir les deux). Le code suivant, par exemple, garantit que le titre dun livre fait entre 2et 20caractres et que son prix varie de 1 500dollars:
<h:inputText value="#{bookController.book.title}" required="true"> <f:validateLength minimum="2" maximum="20"/> </h:inputText> <h:inputText value="#{bookController.book.price}"> <f:validateLongRange minimum="1" maximum="500"/> </h:inputText>

Validateurs personnaliss

Les validateurs standard de JSF peuvent ne pas convenir vos besoins: vous avez peut-tre des donnes qui respectent certains formats mtier, comme un code postal ou une adresse de courrier lectronique. En ce cas, vous devrez crer votre propre validateur. Comme les convertisseurs, un validateur est une classe qui doit implmenter une interface et redfinir une mthode. Dans le cas des validateurs, cette interface est javax.faces.validator.Validator, qui nexpose que la mthode validate():
void validate(FacesContext context, UIComponent component, Object value)

Le paramtre value est celui qui doit tre vrifi en fonction dune certaine logique mtier. Sil passe le test de validation, vous pouvez simplement sortir de cette mthode et le cycle de la page se poursuivra. Dans le cas contraire, vous pouvez lancer une exception ValidatorException et inclure un FacesMessage avec des messages

Chapitre 12

Traitement et navigation 409

rsums et dtaills pour dcrire lerreur de validation. Ce validateur doit tre enregistr dans le fichier faces-config.xml ou laide de lannotation @FacesValidator. Le Listing 12.12, par exemple, contient le code dun validateur qui garantit que lISBN saisi par lutilisateur est au bon format.
Listing12.12: Validateur dISBN
@FacesValidator(value = "isbnValidator") public class IsbnValidator implements Validator { private Pattern pattern; private Matcher matcher; @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { String componentValue = value.toString(); pattern = Pattern.compile("(?=[-0-9xX]{13}$)"); matcher = pattern.matcher(componentValue); if (!matcher.find()) { String message = MessageFormat.format("{0} is not a valid isbn format", componentValue); FacesMessage facesMessage = new FacesMessage(SEVERITY_ERROR, message, message); throw new ValidatorException(facesMessage); } } }

Le code du Listing12.12 commence par associer le validateur au nom isbnValidator afin que lon puisse lutiliser dans une page. Puis il implmente linterface Validator en ajoutant le code de validation la mthode validate(), qui utilise une expression rgulire pour vrifier que lISBN est au bon format dans le cas contraire, il ajoute un message au contexte et lance une exception. Dans ce cas, JSF terminera automatiquement le cycle de vie de la page, la rappellera et affichera le message derreur. Vous pouvez utiliser ce validateur dans vos pages en passant par lattribut validator ou par un marqueur <f:validator> imbriqu:
<h:inputText value="#{book.isbn}" validator="isbnValidator"/> // ou <h:inputText value="#{book.isbn}"> <f:validator validatorId="isbnValidator" /> </h:inputText>

410

Java EE 6 et GlassFish 3

Ajax
Le protocole HTTP repose sur un mcanisme requte/rponse: un client a besoin dune information, il envoie une requte et reoit une rponse du serveur gnralement une page web complte. La communication va toujours dans ce sens: cest le client qui est linitiative de la requte. Cependant, les applications web doivent produire des interfaces riches et ractives et rpondre aux vnements du serveur, mettre jour des parties dune page, agrger des widgets, etc. Dans un cycle requte/ rponse classique, le serveur devrait envoyer toute la page web, mme sil ne faut en modifier quune petite partie: si la taille de cette page nest pas ngligeable, ceci consomme de la bande passante et le temps de rponse sera mdiocre car le navigateur devra recharger toute la page. Pour amliorer la ractivit du navigateur et fournir plus de fluidit lutilisateur, il ne faut modifier que de petites parties de la page, et cest l quAjax entre en jeu. Ajax (acronyme dAsynchronous JavaScript and XML) est un ensemble de techniques de dveloppement web permettant de crer des applications web interactives. Grce lui, les applications rcuprent de faon asynchrone des portions de donnes partir du serveur sans interfrer avec laffichage et le comportement de la page en cours de consultation. Lorsque les donnes sont reues par le client, seules les parties qui ont besoin dtre modifies le seront: pour cela, on utilise le DOM de la page et du code JavaScript. Il existe galement un mcanisme appel Reverse Ajax (ou programmation Comet) pour pousser les donnes du serveur vers le navigateur. Ces mcanismes sont utiliss dans la plupart de nos applications web quotidiennes et sont dsormais intgrs JSF2.0.
Concepts gnraux

Le terme Ajax a t invent en 2005 pour dsigner un ensemble dalternatives permettant de charger des donnes de faon asynchrone dans les pages web. En 1999, Microsoft avait cr lobjet XMLHttpRequest comme un contrle ActiveX dans Internet Explorer5. En 2006, le W3C produisit le premier draft de la spcification de lobjet XMLHttpRequest, qui est dsormais reconnu par la plupart des navigateurs. Au mme moment, plusieurs socits rflchirent au moyen de garantir quAjax devienne un standard reposant sur des technologies ouvertes. Le rsultat de ce travail fut la cration de lOpenAjax Alliance, compose dditeurs de logiciels, de projets open-source et de socits utilisant les technologies Ajax. Comme le montre la Figure12.7, dans les applications web traditionnelles le navigateur doit demander des documents HTML complets au serveur. Lutilisateur clique

Chapitre 12

Traitement et navigation 411

sur un bouton pour envoyer ou recevoir linformation, attend la rponse du serveur puis reoit lintgralit de la page qui se charge dans le navigateur. Ajax, par contre, utilise des transferts de donnes asynchrones entre le navigateur et le serveur, ce qui permet aux pages de demander des petits morceaux dinformation (donnes au format JSON ou XML). Lutilisateur reste sur la mme page pendant que du code JavaScript demande ou envoie des donnes au serveur de faon asynchrone, et seules des parties de cette page seront rafrachies, ce qui a pour effet de produire des applications plus ractives et des interfaces plus fluides.
Figure12.7 Appels HTTP classiques vs. appels HTTP Ajax.
Appel HTTP Navigateur Page web Appel Ajax Navigateur Page web

Appel JavaScript

XML

Bibliothque Ajax Requte HTTP Page XHTML Requte XMLHttp Serveur XML

Serveur

En principe, Ajax repose sur les technologies suivantes:


XHTML et CSS pour la prsentation; DOM pour laffichage dynamique et linteraction avec les donnes; XML et XSLT pour les changes, la manipulation et laffichage des donnes XML; lobjet XMLHttpRequest pour la communication asynchrone; JavaScript pour relier toutes ces technologies.

joue un rle important dans Ajax car cest une API DOM utilise par JavaScript pour transfrer du XML du navigateur vers le serveur. Les donnes renvoyes en rponse doivent tre rcupres sur le client pour modifier dynamiquement les parties de la page avec JavaScript. Ces donnes peuvent tre dans diffrents formats, comme XHTML, JSON, voire du texte brut.
XMLHttpRequest

412

Java EE 6 et GlassFish 3

Ajax tant disponible nativement dans JSF 2.0, vous navez plus besoin dcrire de code JavaScript pour grer lobjet XMLHttpRequest: il suffit dutiliser la bibliothque JavaScript qui a t spcifie et qui est disponible dans les implmentations de JSF2.0.
Ajax et JSF

Les versions prcdentes de JSF noffrant pas de solution native pour Ajax, des bibliothques tierces sont venues combler ce manque, ce qui augmentait la complexit du code au dtriment des performances. partir de JSF2.0, tout est beaucoup plus simple puisque Ajax a t ajout la spcification et est dsormais intgr dans toutes les implmentations. La bibliothque JavaScript jsf.js permet de raliser les interactions Ajax, ce qui signifie quil nest plus ncessaire dcrire ses propres scripts pour manipuler directement les objets XMLHttpRequest: vous pouvez vous servir dun ensemble de fonctions standard pour envoyer des requtes asynchrones et recevoir les donnes. Pour utiliser cette bibliothque dans vos pages, il suffit dajouter la ressource jsf.js avec la ligne suivante:
<h:outputScript name="jsf.js" library="javax.faces" target="head"/>

Le marqueur <h:outputScript> produit un lment <script> faisant rfrence au fichier jsf.js de la bibliothque javax.faces (lespace de noms de premier niveau javax est enregistr par lOpenAjax Alliance). Cette API JavaScript sert lancer les interactions ct client. La fonction utilise directement dans les pages sappelle request: cest elle qui est responsable de lenvoi dune requte Ajax au serveur. Sasignature est la suivante:
jsf.ajax.request(ELEMENT, |EVENT|, { |OPTIONS| });

est le composant JSF ou llment XHTML qui dclenchera lvnement pour la soumission dun formulaire, il sagira gnralement dun bouton. EVENT est lvnement JavaScript support par cet lment, comme onmousedown, onclick, onblur, etc. Le paramtre OPTIONS est un tableau pouvant contenir les paires nom/ valeur suivantes:
ELEMENT

execute: <liste identifiants de composants interface>.

Envoie au serveur la liste des identifiants de composants pour quils soient traits au cours de la phase dexcution de la requte.

Chapitre 12

Traitement et navigation 413

render:<liste identifiants de composants interface>. Traduit la liste des

identifiants de composants qui sont mettre jour au cours de la phase de rendu de la requte. Le code suivant, par exemple, affiche un bouton qui appelle la fonction jsf. ajax.request lorsquon clique dessus (vnement onclick). Le paramtre this dsigne llment lui-mme (le bouton) et les options dsignent les identifiants des composants:
<h:commandButton id="submit" value="Create a book" onclick="jsf.ajax.request(this, event, {execute:isbn title price description nbOfPage illustrations, render:booklist}); return false;" actionListener="#{bookController.doCreateBook}" />

Lorsque le client fait une requte Ajax, le cycle de vie de la page sur le serveur reste le mme (il passe par les mmes six phases). Lavantage principal est que la rponse ne renvoie au navigateur quun petit extrait de donnes au lieu dune page HTML complte. La phase Application de la requte dtermine si la requte adresse est "partielle" ou non: lobjet PartialViewContext est utilis tout au long du cycle de vie de la page et contient les mthodes et les proprits pertinentes permettant detraiter une requte partielle et de produire une rponse partielle. la fin du cycle de vie, la rponse Ajax (ou, proprement parler, la rponse partielle) est envoye au client au cours de la phase Rendu de la rponse elle est gnralement forme de XHTML, XML ou JSON, qui sera analys par le code JavaScript sexcutant ct client.
Rcapitulatif

Lexemple du Listing12.13 montre comment utiliser Ajax et son support par JSF. La section "Rcapitulatif" du Chapitre10 a montr comment insrer de nouveaux livres dans la base de donnes laide dun bean gr nomm BookController. La navigation tait simple: ds que le livre tait cr, lutilisateur tait redirig vers la page affichant la liste de tous les livres; en cliquant sur un lien, il pouvait revenir sur le formulaire de cration. Nous allons reprendre cet exemple pour lui ajouter quelques fonctionnalits Ajax. Nous voulons maintenant afficher sur la mme page le formulaire et la liste des livres (voir Figure12.8). chaque fois quun livre est cr en cliquant sur le bouton du formulaire, la liste sera rafrachie afin de faire apparatre ce nouveau livre.

414

Java EE 6 et GlassFish 3

Figure12.8 Une seule page pour crer et afficher tous les livres.

Create a new book


ISBN : Tiltle : Price : 256 6 56 Dune 23.25 The trilogy Description :

Number of pages : 529 Illustrations :

Create a book

List of the books


ISBN 564 694 Title Price dollar 12.0 Description Scifi IT book Best seller Number Of Pages 241 317 Illustrations false true 1234 234 H2G2

Robots 18.5

APress Beginning Java EE 6

Le formulaire en haut de la page ne change pas : seule la liste a besoin dtre rafrachie. Le code du Listing12.13 contient le code du formulaire. Pour intgrer Ajax, la page doit dabord dfinir la bibliothque jsf.js laide du marqueur <h:outputScript> rien ne change vraiment par rapport au code du Chapitre10. La variable bookCtrl fait rfrence au bean gr BookController, qui est responsable de toute la logique mtier (en invoquant un EJB pour stocker et rcuprer les livres). On accde aux attributs de lentit Book via le langage dexpression (#{bookCtrl. book.isbn} est li lISBN). Chaque composant dentre possde un identifiant (id="isbn", id="title", etc.): ceci est trs important car cela permet didentifier chaque nud du DOM ayant besoin dinteragir de faon asynchrone avec le serveur. Ces identifiants doivent tre uniques pour toute la page car lapplication doit pouvoir faire correspondre les donnes un composant spcifique.
Listing12.13: La partie formulaire de la page newBook.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">

Chapitre 12

Traitement et navigation 415

<h:head> <title>Create a new book</title> </h:head> <h:body> <h:outputScript name="jsf.js" library="javax.faces" target="head"/> <h1>Create a new book</h1> <hr/> <h:form id="form" prependId="false"> <table border="0"> <tr> <td><h:outputLabel value="ISBN : "/></td> <td> <h:inputText id="isbn" value="#{bookCtrl.book.isbn}"/> </td> </tr> <tr> <td><h:outputLabel value="Title :"/></td> <td> <h:inputText id="title" value="#{bookCtrl.book.title}"/> </td> </tr> <tr> <td><h:outputLabel value="Price : "/></td> <td><h:inputText id="price" value="#{bookCtrl.book.price}"/> </td> </tr> <tr> <td><h:outputLabel value="Description : "/></td> <td> <h:inputTextarea id="description" value="#{bookCtrl.book.description}" cols="20" rows="5"/> </td> </tr> <tr> <td><h:outputLabel value="Number of pages : "/></td> <td> <h:inputText id="nbOfPage" value="#{bookCtrl.book.nbOfPage}"/> </td> </tr> <tr> <td><h:outputLabel value="Illustrations : "/></td>

416

Java EE 6 et GlassFish 3

<td> <h:selectBooleanCheckbox id="illustrations" value="#{bookCtrl.book.illustrations}"/> </td> </tr> </table> <h:commandButton id="submit" value="Create a book" onclick="jsf.ajax.request(this, event, {execute:isbn title price description nbOfPage illustrations, render:booklist}); return false;" actionListener="#{bookCtrl.doCreateBook}" /> </h:form>

Le marqueur <h:commandButton> reprsente le bouton qui dclenche lappel Ajax. Lorsque lutilisateur clique dessus (vnement onclick), la fonction jsf.ajax. request est invoque avec, pour paramtres, les identifiants des composants (isbn, title, etc.). Grce eux, les valeurs des composants correspondants sont alors postes vers le serveur. La mthode doCreateBook() du bean gr est appele, le nouveau livre est cr et la liste des livres est rcupre. Laffichage de cette liste ct client est effectu avec Ajax grce la bibliothque JS de JSF et la fonction jsf.ajax.request, appele par lvnement onclick. Llment render fait rfrence booklist, qui est lidentifiant du tableau qui affiche tous les livres (voir Listing12.14).
Listing12.14: La partie liste de la page newBook.xhtml
<hr/> <h1>List of the books</h1> <hr/> <h:dataTable id="booklist" value="#{bookCtrl.bookList}" var="bk"> <h:column> <f:facet name="header"> <h:outputText value="ISBN"/> </f:facet> <h:outputText value="#{bk.isbn}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Title"/> </f:facet> <h:outputText value="#{bk.title}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Price dollar"/> </f:facet>

Chapitre 12

Traitement et navigation 417

<h:outputText value="#{bk.price}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Description"/> </f:facet> <h:outputText value="#{bk.description}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Number Of Pages"/> </f:facet> <h:outputText value="#{bk.nbOfPage}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Illustrations"/> </f:facet> <h:outputText value="#{bk.illustrations}"/> </h:column> </h:dataTable> <i>APress - Beginning Java EE 6</i> </h:body> </html>

La rponse partielle du serveur contient la portion XHTML modifier. Le code JavaScript recherche llment booklist de la page et applique les modifications ncessaires. Le code de cette rponse dans le Listing12.15 est assez simple comprendre: il prcise quil faut modifier le composant identifi par booklist (<update id="booklist">). Le corps de llment <update> est le fragment XHTML qui doit remplacer les donnes courantes du tableau.
Listing12.15: La rponse partielle reue par le client
<partial-response> <changes> <update id="booklist"> <table id="booklist" border="1"> <tr> <th scope="col">ISBN</th> <th scope="col">Title</th> <th scope="col">Price</th> <th scope="col">Description</th> <th scope="col">Number Of Pages</th> <th scope="col">Illustrations</th> </tr> <tr>

418

Java EE 6 et GlassFish 3

<td>1234-234</td> <td>H2G2</td> <td>12.0</td> <td>Scifi IT book</td> <td>241</td> <td>false</td> </tr> <tr> <td>564-694</td> <td>Robots</td> <td>18.5</td> <td>Best seller</td> <td>317</td> <td>true</td> </tr> </table> </update> </changes> </partial-response>

Rsum
Le chapitre prcdent avait tudi laspect graphique de JSF alors que ce chapitre sest intress sa partie dynamique. JSF met en uvre le modle de conception MVC: sa spcification stend de la cration des interfaces utilisateurs laide de composants au traitement des donnes avec les beans grs. Les beans grs sont au cur de JSF car ce sont eux qui traitent la logique mtier, appellent les EJB, les bases de donnes, etc. et qui permettent de naviguer entre les pages. Ils ont une porte et un cycle de vie (qui ressemble celui des beans de session sans tat), et ils dclarent des mthodes et des proprits qui sont lies aux composants dinterface grce au langage dexpression. Les annotations et la configuration par exception ont permis de beaucoup simplifier JSF2.0 car la plupart des configurations XML sont dsormais facultatives. Nous avons montr comment chaque composant dentre peut grer les conversions et les validations. JSF dfinit un ensemble de convertisseurs et de validateurs pour les situations les plus courantes, mais vous pouvez galement crer et enregistrer les vtres de faon trs simple. Ajax existe depuis quelques annes et JSF2.0 dispose maintenant pour cette technologie dun support natif qui permet aux pages web dinvoquer de faon asynchrone des beans grs. JSF2.0 dfinit une bibliothque JavaScript standard afin que les dveloppeurs naient plus besoin dcrire de scripts et puissent simplement utiliser des fonctions pour rafrachir des portions de pages.

13
Envoi de messages
La plupart des communications entre les composants que nous avons vus jusqu prsent sont synchrones: une classe en appelle une autre, un bean gr invoque un EJB, qui appelle une entit, etc. Dans ces cas-l, lappelant et la cible doivent tre en cours dexcution pour que la communication russisse et lappelant doit attendre que la cible termine son excution avant de pouvoir continuer. lexception des appels asynchrones dans EJB, la plupart des composants de JavaEE utilisent des appels synchrones (locaux ou distants). Lorsque nous parlons de messages, nous pensons une communication asynchrone, faiblement couple, entre les composants. MOM (Middleware Orient Messages) est un logiciel (un fournisseur) qui autorise les messages asynchrones entre des systmes htrognes. On peut le considrer comme un tampon plac entre les systmes, qui produisent et consomment les messages leurs propres rythmes (un systme peut, par exemple, tourner 24heures sur24, 7jours sur7 alors quun autre ne tourne que la nuit). Il est faiblement coupl car ceux qui envoient les messages ne savent pas qui les recevra lautre extrmit du canal de communication. Pour communiquer, lexpditeur et le rcepteur ne doivent pas ncessairement fonctionner en mme temps en fait, ils ne se connaissent mme pas puisquils utilisent un tampon intermdiaire. De ce point de vue, le MOM est totalement diffrent des technologies comme RMI (Remote Method Invocation), qui exigent quune application connaisse les signatures des mthodes dune application distante. Aujourdhui, une socit typique utilise plusieurs applications, souvent crites dans des langages diffrents, qui ralisent des tches bien dfinies. Le MOM leur permet de fonctionner de faon indpendante tout en faisant partie dun workflow. Les messages sont une bonne solution pour intgrer les applications existantes et nouvelles en les couplant faiblement, de faon asynchrone, pourvu qumetteur et destinataire se mettent daccord sur le format du message et sur le tampon intermdiaire.

420

Java EE 6 et GlassFish 3

Cette communication peut tre locale une organisation ou distribue entre plusieurs services externes.

Prsentation des messages


Le MOM, qui existe dj depuis un certain temps, utilise un vocabulaire spcial. Lorsquun message est envoy, le logiciel qui le stocke et lexpdie est appel un fournisseur (ou, parfois, un broker). Lmetteur du message est appel producteur et lemplacement de stockage du message, une destination. Le composant qui reoit le message est appel consommateur tout composant intress par un message cette destination prcise peut le consommer. La Figure13.1 illustre ces concepts.
Figure13.1 Architecture du MOM.
Producteur Mess Fournisseur de messages

envoie

Destination

reoit

Consommateur

Avec Java EE, tous ces concepts sont pris en charge par lAPI JMS (Java Message Service), qui propose un ensemble dinterfaces et de classes permettant de se connecter un fournisseur, de crer un message, de lenvoyer et de le recevoir. JMS ne transporte pas les messages: il a besoin dun fournisseur qui soccupe de le faire. Lorsquils sexcutent dans un conteneur, les MDB (Message-Driven Beans) peuvent servir recevoir les messages de faon gre par le conteneur.
JMS

JMS est une API standard de Java permettant aux applications de crer, denvoyer, de recevoir et de lire les messages de faon asynchrone. Elle dfinit un ensemble dinterfaces et de classes que les programmes peuvent utiliser pour communiquer avec dautres fournisseurs de messages. JMS ressemble JDBC : cette dernire permet de se connecter plusieurs bases de donnes (Derby, MySQL, Oracle, DB2, etc.), alors que JMS permet de se connecter plusieurs fournisseurs (OpenMQ, MQSeries, SonicMQ, etc.). LAPI JMS couvre toutes les fonctionnalits ncessaires aux messages, cest--dire leur envoi et leur rception via des destinations:

Chapitre 13

Envoi de messages 421

Les producteurs de messages. LAPI permet aux clients de gnrer un message (metteurs et diteurs). Les consommateurs de messages. Elle permet aux applications clientes de consommer des messages (rcepteurs ou abonns). Les messages. Un message est form dun en-tte, de proprits et dun corps contenant diffrentes informations (texte, objets, etc.). Les connexions et les destinations. LAPI fournit plusieurs fabriques permettant dobtenir des fournisseurs et des destinations (files dattente et sujets).

MDB

Les MDB sont des consommateurs de messages asynchrones qui sexcutent dans un conteneur EJB. Comme on la vu aux Chapitres6 9, le conteneur prend en charge plusieurs services (transaction, scurit, concurrence, acquittement des messages, etc.); ce qui permet au MDB de se consacrer la consommation des messages JMS. Les MDB tant sans tat, le conteneur EJB peut avoir de nombreuses instances sexcutant de faon concurrente pour traiter les messages provenant de diffrents producteurs JMS. Bien que les MDB ressemblent aux beans sans tat, on ne peut pas y accder directement par les applications: la seule faon de communiquer avec eux consiste envoyer un message la destination que le MDB surveille. En gnral, les MDB surveillent une destination (file dattente ou sujet) et consomment et traitent les messages qui y arrivent. Ils peuvent galement dlguer la logique mtier dautres beans de session sans tat de faon transactionnelle. tant sans tat, les MDB ne mmorisent pas ltat dun message lautre. Ils rpondent aux messages JMS reus du conteneur, alors que les beans de session sans tat rpondent aux requtes clientes via une interface approprie (locale, distante ou sans interface).

Rsum de la spcification des messages


En Java, les messages sont essentiellement reprsents par JMS, qui peut tre utilise par les applications qui sexcutent dans un environnement JavaSE ou JavaEE. MDB est lextension entreprise dun consommateur JMS et est li la spcification EJB.

422

Java EE 6 et GlassFish 3

Bref historique des messages

Jusqu la fin des annes 1980, les socits ne disposaient pas de moyens simples pour lier leurs diffrentes applications: les dveloppeurs devaient crire des adaptateurs logiciels pour que les systmes traduisent les donnes des applications sources dans un format reconnu par les applications destinataires (et rciproquement). Pour grer les diffrences de puissance et de disponibilit de serveurs, il fallait crer des tampons pour faire attendre les traitements. En outre, labsence de protocoles de transport homognes exigeait de crer des adaptateurs de protocoles de bas niveau. Le middleware a commenc merger la fin des annes 1980 afin de rsoudre ces problmes dintgration. Les premiers MOM furent crs comme des composants logiciels spars que lon plaait au milieu des applications pour "faire de la plomberie" entre les systmes. Ils taient capables de grer des plates-formes, des langages de programmation, des matriels et des protocoles rseaux diffrents.
JMS 1.1

La spcification JMS fut publie en aot 1998. Elle a t mise au point par les principaux diteurs de middleware afin dajouter Java les fonctionnalits des messages. La JSR914 apporta quelques modifications mineures (JMS1.0.1, 1.0.2 et 1.0.2b) pour finalement atteindre la version1.1 en avril 2002. Depuis, cette spcification na pas chang. JMS1.1 a t intgre dans J2EE1.2 et fait depuis partie de JavaEE. Cependant, JMS et les MDB ne font pas partie de la spcification Web Profile que nous avons dcrite au premier chapitre, ce qui signifie quils ne seront disponibles que sur les serveurs dapplications qui implmentent lintgralit de la plate-forme Java EE6. Bien que JMS soit une API touffue et de bas niveau, elle fonctionne trs bien. Malheureusement, elle na pas t modifie ou amliore dans Java EE5 ni dans Java EE6.
EJB3.1

Les MDB ont t introduits avec EJB 2.0 et amliors avec EJB 3.0 et par le paradigme gnral de simplification de Java EE5. Ils nont pas t modifis en interne car ils continuent dtre des consommateurs de messages, mais lintroduction des annotations et de la configuration par exception facilite beaucoup leur criture. La nouvelle spcification EJB3.1 (JSR318) napporte pas de modifications notables aux MDB.

Chapitre 13

Envoi de messages 423

Comme on la vu au Chapitre7, les appels asynchrones sont dsormais possibles avec les beans de session sans tat (en utilisant lannotation @Asynchronous) et les threads ne sont pas autoriss dans les EJB. Dans les versions prcdentes de JavaEE, les appels asynchrones entre EJB tant impossibles, la seule solution consistait alors passer par JMS et les MDB, ce qui tait coteux puisquil fallait utiliser de nombreuses ressources (destinations, connexions, fabriques JMS, etc.) simplement pour appeler une mthode de faon asynchrone. Avec EJB3.1, les appels asynchrones tant devenus possibles entre les beans de session, nous pouvons utiliser les MDB pour raliser ce pourquoi ils ont t initialement crs: intgrer des systmes grce aux messages.
Implmentation de rfrence

Limplmentation de rfrence de JMS est OpenMQ (Open Message Queue), qui est open-source depuis 2006 et peut tre utilise dans des applications JMS autonomes ou intgres dans un serveur dapplications. OpenMQ est le fournisseur de messages par dfaut de GlassFish et, la date o ce livre est crit, en est sa version4.4. Elle respecte videmment la spcification JMS et lui ajoute de nombreuses fonctionnalits supplmentaires comme UMS (Universal Message Service), les clusters et bien dautres choses encore.

Envoi et rception dun message


tudions un exemple simple pour nous faire une ide de JMS. Comme on la dj voqu, JMS utilise des producteurs, des consommateurs et des destinations : le producteur envoie un message la destination, qui est surveille par le consommateur qui attend quun message y arrive. Les destinations sont de deux types: les files dattente (pour les communications point point) et les sujets (pour les communications de type Publication-Abonnement). Dans le Listing13.1, un producteur envoie un message de texte une file surveille par le consommateur.
Listing13.1: La classe Sender produit un message dans une Queue
public class Sender { public static void main(String[] args) { // Rcupration du contexte JNDI Context jndiContext = new InitialContext();

424

Java EE 6 et GlassFish 3

// Recherche des objets administrs ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory"); Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue"); // Cration des artfacts ncessaires pour se connecter // la file Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(queue); // Envoi dun message de texte la file TextMessage message = session.createTextMessage(); message.setText("This is a text message"); producer.send(message); connection.close(); } }

Le code du Listing 13.1 reprsente une classe Sender qui ne dispose que dune mthode main(). Celle-ci commence par instancier un contexte JNDI pour obtenir des objets ConnectionFactory et Queue. Les fabriques de connexions et les destinations (files dattente et sujets) sont appeles objets administrs: ils doivent tre crs et dclars dans le fournisseur de messages (OpenMQ, ici). Tous les deux ont un nom JNDI (la file dattente, par exemple, sappelle jms/javaee6/Queue) et doivent tre recherchs dans larborescence JNDI. Lorsque les deux objets administrs ont t obtenus, la classe Sender utilise lobjet ConnectionFactory pour crer une connexion (objet Connection) afin dobtenir une session. Cette dernire est utilise pour crer un producteur (objet MessageProducer) et un message (objet TextMessage) sur la file de destination (session. createProducer(queue)). Le producteur envoie ensuite ce message de type texte. Bien que ce code soit largement comment, nous avons dlibrment omis le traitement des exceptions JNDI et JMS. Le code pour recevoir le message est quasiment identique. En fait, les premires lignes de la classe Receiver du Listing12.2 sont exactement les mmes: on cre un contexte JNDI, on recherche la fabrique de connexion et la file dattente, puis on se connecte. Les seules diffrences tiennent au fait que lon utilise un MessageConsumer au lieu dun MessageProducer et que le rcepteur entre dans une boucle sans fin pour surveiller la file dattente (nous verrons plus tard que lon peut viter cette boucle en utilisant un couteur de messages, ce qui est une technique plus classique). Lorsque le message arrive, on le consomme et on affiche son contenu.

Chapitre 13

Envoi de messages 425

Listing13.2: La classe Receiver consomme un message dune file dattente


public class Receiver { public static void main(String[] args) { // Rcupration du contexte JNDI Context jndiContext = new InitialContext(); // Recherche des objets administrs ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory"); Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue"); // Cration des artfacts ncessaires pour se connecter // la file Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(queue); connection.start(); // Boucle pour recevoir les messages while (true) { TextMessage message = (TextMessage) consumer.receive(); System.out.println("Message received: " + message.getText()); } } }

Nous pouvons maintenant nous intresser lAPI de JMS, dfinir les objets administrs et les classes ncessaires et voir comment traduire tout ceci dans un MDB.

Java Messaging Service


Au niveau le plus haut, larchitecture de JMS est forme des composants suivants (voir Figure13.2):

Un fournisseur. JMS nest quune spcification et a donc besoin dune implmentation sous-jacente pour router les messages, cest--dire un fournisseur. Celui-ci gre le tampon et la livraison des messages en fournissant une implmentation de lAPI JMS. Clients. Un client est une application ou un composant Java qui utilise lAPI JMS pour consommer ou produire un message JMS. On parle de client JMS car cest un client du fournisseur sous-jacent. "Client" est le terme gnrique pour

426

Java EE 6 et GlassFish 3

dsigner un producteur, un metteur, un diteur, un consommateur, un rcepteur ou un abonn.

Messages. Ce sont les objets que les clients envoient et reoivent du fournisseur JMS. Objets administrs. Pour quun fournisseur implmente totalement JMS, les objets administrs (fabriques de connexion et destinations) doivent tre placs dans une arborescence JNDI et on doit pouvoir y accder par des recherches JNDI.
produit un message Fournisseur JMS consomme un message

Figure13.2 Architecture de JMS.

Producteur

Consommateur

recherche les objets administrs

Annuaire JNDI

recherche les objets administrs

Le fournisseur de messages permet de mettre en place une communication asynchrone en offrant une destination o seront stocks les messages en attendant de pouvoir tre dlivrs un client. Il existe deux types de destinations correspondant chacun un modle darchitecture spcifique:

Le modle point point (P2P). Dans ce modle, la destination qui stocke les messages est appele file dattente. Lorsquon utilise des messages point point, un client place un message dans une file et un autre client reoit le message. Lorsque le message a t acquitt, le fournisseur le supprime de la file. Le modle publication-abonnement. Ici, la destination sappelle un sujet. Avec ce modle, un client publie un message dans un sujet et tous les abonns le reoivent.

La spcification JMS fournit un ensemble dinterfaces pouvant tre utilises par les deux modles. Le Tableau13.1 numre le nom gnrique de chaque interface (Session, par exemple) et son nom spcifique pour chaque modle (QueueSession, TopicSession). Vous remarquerez galement que les vocabulaires sont diffrents: un consommateur est appel rcepteur dans le modle P2P et abonn dans le modle publication/abonnement.

Chapitre 13

Envoi de messages 427

Tableau13.1: Interfaces utilises en fonction du type de destination

Gnrique
Destination ConnectionFactory Connection Session MessageConsumer MessageProducer

Point Point
Queue QueueConnectionFactory QueueConnection QueueSession QueueReceiver QueueSender

Publication-abonnement
Topic TopicConnectionFactory TopicConnection TopicSession TopicSubscriber

TopicPublisher

Point point

Dans le modle P2P, un unique message va dun unique producteur (le pointA) un unique consommateur (le pointB). Ce modle repose sur le concept de files de messages, dmetteurs et de rcepteurs (voir Figure13.3). Une file stocke les messages envoys par lmetteur jusqu ce quils soient consomms les timings de lmetteur et du rcepteur sont indpendants, ce qui signifie que lmetteur peut produire des messages et les envoyer dans la file lorsquil le dsire et quun rcepteur peut les consommer quand il le souhaite. Lorsquun rcepteur est cr, il reoit tous les messages envoys dans la file, mme ceux qui ont t envoys avant sa cration.
Figure13.3 Le modle P2P.
metteur Mess Mess

envoie

reoit

Rcepteur

Chaque message est envoy dans une file spcifique do le rcepteur extrait les messages. Les files conservent tous les messages tant quils nont pas t consomms ou jusqu ce quils expirent. Le modle P2P est utilis lorsquil ny a quun seul rcepteur pour chaque message une file peut avoir plusieurs consommateurs mais, une fois quun rcepteur a consomm un message, ce dernier est supprim de la file et aucun autre consommateur ne peut donc le recevoir. la Figure13.4, un seul metteur produit trois messages et deux rcepteurs consomment chacun un message qui ne sera pas disponible

428

Java EE 6 et GlassFish 3

pour lautre. JMS garantit galement quun message ne sera dlivr quune seule fois.
Figure13.4 Plusieurs rcepteurs.
metteur Mess Mess Mess 1 2 3
Rcepteur 1

Mess 3 Mess 1

Mess 2

Rcepteur 2

Notez que P2P ne garantit pas que les messages seront dlivrs dans un ordre particulier. En outre, sil y a plusieurs rcepteurs possibles pour un message, le choix du rcepteur est alatoire.
Publication-abonnement

Dans ce modle, un unique message envoy par un seul producteur peut tre reu par plusieurs consommateurs. Ce modle repose sur le concept de sujets, dditeurs et dabonns (voir Figure13.5). Les consommateurs sont appels abonns car ils doivent dabord sabonner un sujet. Cest le fournisseur qui gre le mcanisme dabonnement/dsabonnement, qui a lieu de faon dynamique.
Figure13.5 Modle Publicationabonnement.
Mess diteur publie reoit Mess s'abonne
Abonn

Le sujet conserve les messages jusqu ce quils aient t distribus tous les abonns. la diffrence du modle P2P, les timings entre diteurs et abonns sont lis: ces derniers ne reoivent pas les messages envoys avant leur abonnement et un abonn rest inactif pendant une priode donne ne recevra pas les messages publis entre-temps lorsquil redeviendra actif. Comme nous le verrons plus tard, ceci peut tre vit car lAPI JMS dispose du concept dabonn durable. Plusieurs abonns peuvent consommer le mme message. Ce modle peut donc tre utilis par les applications de type "diffusion", dans lesquelles un mme message est

Chapitre 13

Envoi de messages 429

dlivr plusieurs consommateurs. la Figure13.6, par exemple, lditeur envoie trois messages qui seront reus par tous les abonns.
Figure13.6 Plusieurs abonns.
diteur Mess Mess Mess 3 2 1
Abonn 1

Mess 3 Mess 1

Mess 1 Mess 3

Mess 2 Mess 2

Abonn 2

API JMS

LAPI JMS se trouve dans le paquetage javax.jms et fournit des classes et des interfaces aux applications qui ont besoin dun systme de messages (voir Figure13.7). Elle autorise les communications asynchrones entre les clients en fournissant une connexion un fournisseur et une session au cours de laquelle les messages peuvent tre crs, envoys et reus. Ces messages peuvent contenir du texte ou diffrents types dobjets.
Figure13.7 API JMS (inspire de la Figure2.1 de la spcification JMS 1.1).
<<Interface>> ConnectionFactory cre <<Interface>> Connection cre cre <<Interface>> Session cre

<<Interface>> MessageProducer

<<Interface>> Message

<<Interface>> MessageConsumer

envoie

<<Interface>> Destination

reoit de

430

Java EE 6 et GlassFish 3

Objets administrs

Les objets administrs sont des objets qui ne sont pas configurs par programmation: le fournisseur de messages les rend disponibles dans lespace de noms JNDI. Comme les sources de donnes JDBC, les objets administrs ne sont crs quune seule fois. Il en existe deux types pour JMS:

Les fabriques de connexion. Ces objets sont utiliss par les clients pour crer une connexion vers une destination. Les destinations. Ces objets sont des points de distribution qui reoivent, stockent et distribuent les messages. Les destinations peuvent tre des files dattente (P2P) ou des sujets (Publication-abonnement).

Les clients JMS accdent ces objets via des interfaces portables en les recherchant dans lespace de noms JNDI. GlassFish fournit plusieurs moyens den crer : en utilisant la console dadministration, avec loutil en ligne de commande asadmin ou avec linterface REST.
Fabriques de connexion

Ce sont les objets administrs qui permettent une application de se connecter un fournisseur en crant par programme un objet Connection. Une fabrique de connexion encapsule les paramtres de configuration dfinis par un administrateur. Il en existe trois types:
javax.jms.ConnectionFactory

est une interface pouvant tre utilise la fois par les communications P2P et Publication-abonnement. et qui ne sert quaux communications P2P. et qui ne sert quaux communications Publication-abonnement.

javax.jms.QueueConnectionFactory est une interface qui hrite de ConnectionFactory

javax.jms.TopicConnectionFactory est une interface qui hrite de ConnectionFactory

Le programme doit dabord obtenir une fabrique de connexion en effectuant une recherche JNDI. Le fragment de code suivant, par exemple, obtient un objet InitialContext et sen sert pour rechercher un QueueConnectionFactory et un TopicConnectionFactory par leurs noms JNDI:
Context ctx = new InitialContext(); QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) ctx.lookup("QConnFactory"); TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) ctx.lookup("TConnFactory");

Chapitre 13

Envoi de messages 431

Les fabriques de files dattente/sujets sont ncessaires lorsque lon a besoin daccder des dtails spcifiques de ces modles. Dans le cas contraire, vous pouvez directement utiliser une ConnectionFactory gnrique pour les deux situations:
Context ctx = new InitialContext(); ConnectionFactory ConnectionFactory = (QueueConnectionFactory) ctx.lookup("GenericConnFactory");

La seule mthode disponible dans ces trois interfaces est la mthode createConnection(), qui renvoie un objet Connection. Cette mthode est surcharge pour pouvoir crer une connexion en utilisant lidentit de lutilisateur par dfaut ou en prcisant un nom dutilisateur et un mot de passe (voir Listing13.3).
Listing13.3: Interface ConnectionFactory
public interface ConnectionFactory { Connection createConnection() throws JMSException; Connection createConnection(String userName, String password) throws JMSException; }

Destinations

Une destination est un objet administr contenant des informations spcifiques un fournisseur, comme ladresse de destination. Cependant, ces informations sont caches au client JMS par linterface standard javax.jms.Destination. Il existe deux types de destinations, reprsentes par deux interfaces hritant de Destination:
javax.jms.Queue, javax.jms.Topic,

utilise pour les communications P2P; utilise pour les communications Publication-abonnement.

Ces interfaces ne contiennent quune seule mthode, qui renvoie le nom de la destination. Comme pour les fabriques de connexion, ces objets sobtiennent par une recherche JNDI.
Injection

Les fabriques de connexion et les destinations sont des objets administrs qui rsident dans un fournisseur de messages et qui doivent tre dclars dans lespace de noms JNDI. Lorsque le code client sexcute dans un conteneur (EJB, servlet, client dapplication), vous pouvez utiliser linjection des dpendances la place des recherches JNDI grce lannotation @Resource (voir la section "Injection de dpendances" du Chapitre7), ce qui est bien plus simple.

432

Java EE 6 et GlassFish 3

Le Tableau13.2 numre les lments de cette annotation.


Tableau13.2: API de lannotation @javax.annotation.Resource

lment
name type authenticationType shareable mappedName description

Description Nom JNDI de la ressource. Type Java de la ressource (javax.sql.DataSource ou javax. jms.Topic, par exemple). Type dauthentification utiliser pour la ressource (soit le conteneur, soit lapplication Indique si la ressource peut tre ou non partage. Nom spcifique auquel associer la ressource. Description de la ressource.

Pour tester cette annotation, nous utiliserons une classe Receiver avec une mthode qui reoit des messages textuels. Dans le Listing 13.2, la fabrique de connexions et la file dattente taient obtenues par des recherches JNDI; dans le Listing13.4, les noms JNDI sont indiqus dans les annotations @Resource: si cette classe Receiver sexcute dans un conteneur, elle recevra donc par injection des rfrences ConnectionFactory et Queue lors de son initialisation.
main()

Listing13.4: La classe Receiver obtient par injection des rfrences des ressources JMS
public class Receiver { @Resource(name private static @Resource(name private static = "jms/javaee6/ConnectionFactory") ConnectionFactory connectionFactory; = "jms/javaee6/Queue") Queue queue;

public static void main(String[] args) { // Cre les artfacts ncessaires la connexion la file Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(queue); connection.start(); // Boucle pour recevoir les messages while (true) { TextMessage message = (TextMessage) consumer.receive();

Chapitre 13

Envoi de messages 433

System.out.println("Message received: " + message.getText()); } } }

Pour simplifier, nous avons omis le traitement des exceptions. Notez que lannotation @Resource porte sur des attributs privs: linjection de ressources peut sappliquer diffrents niveaux de visibilit (prive, protge, paquetage ou publique).
Connexion

Lobjet javax.jms.Connection que lon cre avec la mthode createConnection() de la fabrique de connexion encapsule une connexion au fournisseur JMS. Les connexions sont thread-safe et conues pour tre partages car louverture dune connexion est une opration coteuse en terme de ressources. Une session (javax. jms.Session), par contre, fournit un contexte mono-thread pour envoyer et recevoir les messages; une connexion permet de crer une ou plusieurs sessions. Comme les fabriques de connexion, les connexions peuvent tre de trois types en utilisant linterface gnrique Connection ou les interfaces QueueConnection et TopicConnection qui drivent de celle-ci. La cration de lobjet "connexion" dpend du type de la fabrique dont vous disposez:
Connection connection = connFactory.createConnection(); QueueConnection connection = queueConnFactory.createQueueConnection(); TopicConnection connection = topicConnFactory.createTopicConnection();

Dans le Listing13.4, le rcepteur doit appeler la mthode start() avant de pouvoir consommer les messages. La mthode stop() permet darrter temporairement den recevoir sans pour autant fermer la connexion.
connection.start(); connection.stop();

Les connexions qui ont t cres doivent tre fermes lorsque lapplication se termine. La fermeture dune connexion ferme galement ses sessions, ses producteurs ou ses consommateurs.
connection.close();

Session

On cre une session en appelant la mthode createSession() de la connexion. Une session fournit un contexte transactionnel qui permet de regrouper plusieurs

434

Java EE 6 et GlassFish 3

messages dans une unit de traitement atomique: si vous envoyez plusieurs messages au cours de la mme session, JMS garantira quils arrivent tous destination dans leur ordre dmission, ou quaucun narrivera. Ce comportement est mis en place lors de la cration de la session:
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

Le premier paramtre de cette mthode indique si la session est transactionnelle ou non. Une valeur true prcise que la requte denvoi des messages ne sera pas ralise tant que lon na pas appel la mthode commit() de la session ou que la session est ferme. Si ce paramtre vaut false, la session ne sera pas transactionnelle et les messages seront envoys ds lappel de la mthode send(). Le second paramtre indique que la session accuse automatiquement rception des messages lorsquils ont t correctement reus. Une session sexcute dans un seul thread et permet de crer des messages, des producteurs et des consommateurs. Comme tous les objets que nous avons vus jusqu prsent, il existe deux variantes de sessions: QueueSession et TopicSession. Un objet Session gnrique permet dutiliser lune ou lautre via une interface unique.
Messages
Figure13.8 Structure dun message JMS.

En-tte
JMSMessageID JMSCorrelationID JMSDeliveryMode JMSDestination JMSExpiration JMSPriority JMSRedelivered JMSReplyTo JMSTimestamp

Proprits
<name><value> <name><value> <name><value>

Corps
BytesMessage TextMessage ObjectMessage MapMessage StreamMessage

Les clients changent des messages pour communiquer; un producteur envoie un message une destination o un client le recevra. Les messages sont des objets qui encapsulent les informations et qui sont diviss en trois parties (voir Figure13.8):

Un en-tte, qui contient les informations classiques pour identifier et acheminer le message. Les proprits, qui sont des paires nom/valeur que lapplication peut lire ou crire. Elles permettent galement aux destinations de filtrer les messages selon les valeurs de ces proprits.

Chapitre 13

Envoi de messages 435

Un corps, qui contient le vritable message et peut utiliser plusieurs formats (texte, octets, objet, etc.).

En-tte

Len-tte contient des paires nom/valeur prdfinies, communes tous les messages et que les clients et les fournisseurs utilisent pour identifier et acheminer les messages. Elles peuvent tre considres comme les mtadonnes du message car elles fournissent des informations sur le message lui-mme. Chaque champ a des mthodes getter et setter qui lui sont associes dans linterface javax.jms. Message. Bien que certains champs de len-tte soient prvus pour tre initialiss par un client, la plupart sont automatiquement fixs par la mthode send() ou publish(). Le Tableau13.3 dcrit tous les champs de len-tte dun message JMS.
Tableau13.3: Champs de len-tte1

Champ
JMSDestination JMSDeliveryMode

Description Destination laquelle est envoy le message.

Initialis par
send()

ou publish()

JMS reconnat deux modes de dlivrance send() ou publish() des messages. Le mode PERSISTENT demande au fournisseur de garantir que le message ne sera pas perdu lors de son transfert cause dune erreur. Le mode NON_PERSISTENT est le mode de dlivrance le moins coteux car il nexige pas denregistrer le message sur un support persistant. Valeur identifiant de manire unique chaque message envoy par un fournisseur.
send()

JMSMessageID

ou publish()

JMSTimestamp

Horodatage indiquant linstant o le send() ou publish() message a t pass un fournisseur pour tre envoy.

Spcification de JMS1.1, "3.4: Message Header Fields" (http://jcp.org/en/jsr/detail?id=914).

436

Java EE 6 et GlassFish 3

Tableau13.3: Champs de len-tte (suite)

Champ
JMSCorrelationID

Description Un client peut utiliser ce champ pour lier un message un autre: pour lier un message de rponse son message de requte, par exemple. Contient la destination laquelle renvoyer la rponse au message. Valeur boolenne initialise par le fournisseur pour indiquer si un message a t nouveau dlivr. Identifiant du type du message.

Initialis par Le client

JMSReplyTo JMSRedelivered

Le client Le fournisseur

JMSType JMSExpiration

Le client

Date dexpiration dun message envoy, send() ou publish() calcule et initialise en fonction de la valeur time-to-live passe la mthode send(). JMS dfinit dix niveaux de priorits, 0 tant la plus faible et 9, la plus forte.
send()

JMSPriority

ou publish()

Vous pouvez accder ces champs via linterface Message:


message.getJMSCorrelationID(); message.getJMSMessageID(); message.setJMSPriority(6);

Proprits

Outre les champs den-tte, linterface javax.jms.Message permet de grer les valeurs des proprits, qui sont exactement comme les en-ttes, mais cres explicitement par lapplication au lieu dtre standard pour tous les messages. Elles permettent dajouter des champs den-ttes optionnels un message, que le client peut choisir de recevoir ou non via des slecteurs. Leurs valeurs peuvent tre de type boolean, byte, short, int, long, float, double et String. Le code permettant de les initialiser et de les lire est de la forme:
message.setFloatProperty("orderAmount", 1245.5f); message.getFloatProperty("or derAmount");

Corps

Le corps dun message est facultatif et contient les donnes envoyer ou recevoir. Selon linterface utilise, il peut contenir des donnes dans diffrents formats, numrs dans le Tableau13.4.

Chapitre 13

Envoi de messages 437

Tableau13.4: Types de messages

Interface
StreamMessage MapMessage TextMessage ObjectMessage BytesMessage

Description Message contenant un flux de valeurs primitives Java. Il est rempli et lu squentiellement. Message contenant un ensemble de paires nom/valeur o les noms sont des chanes et les valeurs sont dun type primitif. Message contenant une chane (du XML, par exemple). Message contenant un objet srialisable ou une collection dobjets srialisables. Message contenant un flux doctets.

En drivant linterface javax.jms.Message, vous pouvez crer votre propre format de message. Notez que lorsquun message est reu, son corps est en lecture seule. Selon le type du message, des mthodes diffrentes permettent daccder son contenu. Un message textuel disposera des mthodes getText() et setText(), un message dobjet disposera des mthodes getObject() et setObject(), etc.
textMessage.setText("This is a text message"); textMessage.getText(); bytesMessage.readByte(); objectMessage.getObject();

MessageProducer

Un producteur de messages est un objet cr par une session pour envoyer des messages une destination. Linterface gnrique javax.jms.MessageProducer peut tre utilise pour obtenir un producteur spcifique disposant dune interface unique. Dans le cas du modle P2P, un producteur de messages est appel metteur et implmente linterface QueueSender. Avec le modle Publication-abonnement, il sappelle diteur et implmente TopicPublisher. Selon linterface utilise, un message cr est envoy (P2P) ou publi (Publication-abonnement):
messageProducer.send(message); queueSender.send(message); topicPublisher.publish(message);

Un producteur peut prciser un mode de livraison par dfaut, une priorit et un dlai dexpiration des messages quil envoie. Les tapes suivantes expliquent comment crer un diteur qui publie un message dans un sujet (voir Listing13.5):

438

Java EE 6 et GlassFish 3

1. Obtenir une fabrique de connexions et un sujet par injection (ou par une recherche JNDI). 2. Crer un objet Connection avec la fabrique de connexions. 3. Crer un objet Session avec la connexion. 4. Crer un MessageProducer (ici, nous aurions pu choisir un TopicPublisher) en utilisant lobjet Session. 5. Crer un ou plusieurs messages dun type quelconque (ici, nous avons utilis un TextMessage) en utilisant lobjet Session. Aprs sa cration, remplir le message avec les donnes (nous nous sommes servis ici de la mthode setText()). 6. Envoyer un ou plusieurs messages dans le sujet laide de la mthode MessageProducer.send() (ou TopicPublisher.publish()).
Listing13.5: La classe Sender envoie un message dans un sujet
public class Sender { @Resource(mappedName = "jms/javaee6/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName = "jms/javaee6/Topic") private static Topic topic; public static void main(String[] args) { // Cre les artfacts ncessaires la connexion au sujet. Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(topic); // Envoie un message texte dans le sujet TextMessage message = session.createTextMessage(); message.setText("This is a text message"); producer.send(message); connection.close(); } }

MessageConsumer

Un client utilise un MessageConsumer pour recevoir les messages provenant dune destination. Cet objet est cr en passant une Queue ou un Topic la mthode createConsumer() de la session. Dans le cas du modle P2P, un consommateur

Chapitre 13

Envoi de messages 439

de messages peut implmenter linterface QueueReceiver et, pour le modle Publication-abonnement, linterface TopicSubscriber. Les messages sont intrinsquement asynchrones car les timings des producteurs et des consommateurs sont totalement indpendants. Cependant, un client peut consommer les messages de deux faons:

De faon synchrone. Un rcepteur rcupre explicitement le message de la destination en appelant la mthode receive(). Les exemples prcdents, par exemple, utilisent une boucle sans fin qui se bloque jusqu ce que le message arrive. De faon asynchrone. Un rcepteur senregistre auprs dun vnement qui est dclench larrive du message. Il doit alors implmenter linterface MessageListener: chaque fois quun message arrive, le fournisseur lui dlivrera en appelant la mthode onMessage().

Ces deux types de consommateurs sont illustrs la Figure13.9.


Figure13.9 Consommateurs synchrones et asynchrones.
Mess metteur envoie collecte Mess
Rcepteur synchrone

metteur

Mess

enregistre prvient

Rcepteur asynchrone

envoie

collecte Mess

Livraison synchrone

Un rcepteur synchrone doit lancer une connexion, boucler en attendant quun message arrive et demander le message arriv laide de lune de ses mthodes receive(): leurs diffrentes variantes permettent au client de demander ou dattendre le prochain message. Les tapes suivantes expliquent comment crer un rcepteur synchrone qui consomme un message dun sujet (voir Listing13.6):

440

Java EE 6 et GlassFish 3

1. Obtenir une fabrique de connexions et un sujet en utilisant linjection (ou une recherche JNDI). 2. Crer un objet Connection laide de la fabrique. 3. Crer un objet Session laide de la connexion. 4. Crer un MessageConsumer (ici, il pourrait galement sagir dun scriber) laide de lobjet Session. 5. Lancer la connexion. 6. Boucler et appeler la mthode receive() sur lobjet consommateur. Cette mthode se bloque si la file est vide et attend quun message arrive. Ici, la boucle sans fin attend que dautres messages arrivent. 7. Traiter le message renvoy par receive() en utilisant la mthode TextMessage. getText() (si cest un message texte).
Listing13.6: Le Receiver consomme les messages de faon synchrone
public class Receiver { @Resource(mappedName = "jms/javaee6/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName = "jms/javaee6/Topic") private static Topic topic; public static void main(String[] args) { // Cre les artfacts ncessaires la connexion au sujet Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(topic); connection.start(); // Boucle pour recevoir les messages while (true) { TextMessage message = (TextMessage) consumer.receive(); System.out.println("Message received: " + message.getText()); } } }

TopicSub

Livraison asynchrone

La consommation asynchrone repose sur la gestion des vnements. Un client peut enregistrer un objet (y compris lui-mme) qui implmente linterface

Chapitre 13

Envoi de messages 441

MessageListener. Un couteur de messages est un objet qui se comporte comme un

gestionnaire dvnements asynchrones pour les messages: lorsque les messages arrivent, le fournisseur les dlivre en appelant la mthode onMessage() de lcouteur, qui prend un seul paramtre de type Message. Grce ce modle vnementiel, le consommateur na pas besoin de boucler indfiniment en attente dun message: les MDB utilisent ce modle. Les tapes suivantes dcrivent la cration dun couteur de messages asynchrone (voir Listing13.7): 1. La classe implmente linterface javax.jms.MessageListener, qui dfinit une seule mthode nomme onMessage(). 2. Obtenir une fabrique de connexions et un sujet en utilisant linjection (ou une recherche JNDI). 3. Crer un objet Connection laide de la fabrique et un objet Session laide de la connexion. Crer un MessageConsumer laide de lobjet Session. 4. Appeler la mthode setMessageListener() en lui passant une instance de linterface MessageListener (dans le Listing13.7, la classe Listener implmente elle-mme linterface MessageListener). 5. Aprs lenregistrement de lcouteur de messages, appeler la mthode start() pour lancer la surveillance de larrive dun message si lon appelle start() avant denregistrer lcouteur, on risque de perdre des messages. 6. Implmenter la mthode onMessage() et traiter le message reu. chaque fois quun message arrive, le fournisseur appellera cette mthode en lui passant le message.
Listing13.7: Le consommateur est un couteur de messages
public class Listener implements MessageListener { @Resource(mappedName = "jms/javaee6/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName = "jms/javaee6/Topic") private static Topic topic; public static void main(String[] args) { // Cre les artfacts ncessaires la connexion au sujet e Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

442

Java EE 6 et GlassFish 3

MessageConsumer consumer = session.createConsumer(topic); consumer.setMessageListener(new Listener()); connection.start(); } public void onMessage(Message message) { System.out.println("Message received: " + ((TextMessage) message).getText()); } }

Slecteurs

Certaines applications doivent filtrer les messages quelles reoivent. Lorsquun message est diffus de nombreux clients, par exemple, il peut tre utile de fixer des critres afin quils ne soient consomms que par certains rcepteurs: ceci permet dviter que le fournisseur gaspille son temps et sa bande passante transporter des messages des clients qui nen ont pas besoin. Nous avons vu que les messages taient constitus de trois parties: un en-tte, des proprits et un corps. Len-tte contient un nombre dtermin de champs (qui forment les mtadonnes du message) et les proprits sont un ensemble de paires nom/valeur personnalises que lapplication peut utiliser pour fixer des valeurs dont elle a besoin. Ces deux parties peuvent servir mettre en place une slection: les metteurs fixent une ou plusieurs proprits ou des valeurs de champs dans len-tte, et le rcepteur prcise les critres de choix dun message en utilisant des expressions de slection (slecteurs). Seuls les messages correspondant ces slecteurs seront alors dlivrs. Cest le fournisseur JMS, plutt que lapplication, qui filtrera les messages. Un slecteur de message est une chane contenant une expression dont la syntaxe sinspire de celle des expressions conditionnelles de SQL92:
session.createConsumer(topic, "JMSPriority < 6"); session. createConsumer(topic, "JMSPriority < 6 AND orderAmount < 200"); session.createConsumer(topic, "orderAmount BETWEEN 1000 AND 2000");

Le code prcdent cre un consommateur en lui passant un slecteur. Cette chane peut utiliser les champs de len-tte (JMSPriority < 6) ou des proprits propres lapplication (orderAmount < 200). De son ct, le producteur fixe ce champ dentte et cette proprit dans le message de la faon suivante:
message.setIntProperty("orderAmount", 1530); message.setJMSPriority(5);

Chapitre 13

Envoi de messages 443

Les expressions de slections peuvent utiliser des oprateurs logiques (NOT, AND, OR), de comparaison (=, >, >=, <, <=, <>), des oprateurs arithmtiques (+, -, *, /), des expressions ([NOT] BETWEEN, [NOT] IN, [NOT] LIKE, IS [NOT] NULL), etc.
Mcanismes de fiabilit

Nous avons vu comment se connecter un fournisseur, crer diffrents types de messages, les envoyer des files dattentes ou dans des sujets et comment les recevoir tous ou les filtrer laide de slecteurs. Mais quen est-il de la fiabilit? JMS dfinit diffrents mcanismes pour garantir la dlivrance des messages, mme lorsque le fournisseur tombe en panne ou est surcharg, ou que les destinations sont remplies de messages qui auraient d expirer. Les mcanismes qui permettent de fiabiliser la livraison des messages sont les suivants:

Dlai dexpiration pour les messages. Les messages ne seront pas dlivrs sils sont obsoltes. Persistance des messages. Les messages sont stocks dans lventualit dune panne du fournisseur. Accus de rception. Diffrents niveaux dacquittement des messages sont possibles. Abonnements durables. Garantit que les messages seront dlivrs un abonn indisponible (modle Publication-abonnement). Priorits. Fixe la priorit de livraison dun message.

Utilisation dun dlai dexpiration

Lorsque la charge est importante, un dlai dexpiration attribu aux messages permet de garantir que le fournisseur les supprimera de la destination lorsquils deviendront obsoltes. Ce dlai dexpiration est mis en place en utilisant lAPI MessageProducer ou en configurant le champ den-tte JMSExpiration. Un objet MessageProducer dispose dune mthode setTimeToLive() prenant un nombre de millisecondes en paramtre. On peut galement lindiquer dans lappel la mthode send() de chaque message:
MessageProducer producer = session.createProducer(topic); producer. setTimeToLive(1000); // ou producer.send(message, DeliveryMode.NON_PERSISTENT, 2, 1000);

444

Java EE 6 et GlassFish 3

Le premier appel fixe globalement le TTL (Time To Live) pour le producteur et tous les messages quil dlivre, alors que le second envoie un message en prcisant son mode de livraison, sa priorit et son dlai dexpiration. Lorsquun message est envoy, son dlai dexpiration est calcul comme tant la somme des TTL. Indiquer cette valeur dans le champ den-tte JMSExpiration fera que les messages obsoltes ne seront pas dlivrs:
message.setJMSExpiration(1000);

Persistance des messages

JMS reconnat deux modes de livraison des messages: persistant et non persistant. Le premier garantit quun message ne sera dlivr quune seule fois un consommateur, tandis que le second exige quun message ne soit dlivr quune fois au plus. La livraison persistante (mode par dfaut) est plus fiable mais plus coteuse car elle fait en sorte dviter la perte dun message en cas de panne du fournisseur. Le mode de livraison peut tre prcis de deux faons: en utilisant la mthode setDeliveryMode() de linterface MessageProducer ou en passant un paramtre la mthode send():
MessageProducer producer = session.createProducer(topic); producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // ou producer.send(message, DeliveryMode.NON_PERSISTENT, 2, 1000);

Le premier appel fixe le mode global de livraison pour le producteur et tous les messages quil dlivre, tandis que le second ne lindique que pour le message concern.
Contrle des accuss de rception

Jusqu maintenant, les scnarios que nous avons tudis supposaient quun message tait envoy et reu sans aucun accus de rception. Parfois, cependant, on souhaite que le rcepteur acquitte le message quil a reu (voir Figure13.10). Une phase dacquittement peut tre lance soit par le fournisseur JMS, soit par le client en fonction du mode choisi. Dans des sessions transactionnelles, lacquittement a automatiquement lieu lorsquune transaction est valide. Si elle est annule, tous les messages consomms sont nouveau dlivrs. Dans les sessions non transactionnelles, en revanche, laccus de rception doit tre explicite:
AUTO_ACKNOWLEDGE.

La session accuse automatiquement rception du message.

Chapitre 13

Envoi de messages 445

CLIENT_ACKNOWLEDGE.

Un client accuse rception dun message en appelant explicitement la mthode Message.acknowledge(). Demande la session dacquitter les messages de faon paresseuse. Ceci impliquera probablement la livraison de messages dupliqus si le fournisseur JMS tombe en panne, aussi cette option ne doit-elle tre utilise que par les consommateurs qui tolrent les messages en double. Lorsque le message est dlivr une nouvelle fois, le fournisseur fixe true la valeur du champ den-tte JMSRedelivered.

DUPS_OK_ACKNOWLEDGE.

Figure13.10 Un rcepteur accuse rception dun message.


Mess metteur envoie Mess reoit accuse rception
Abonn 1

Le code suivant utilise lacquittement explicite du client en appelant la mthode acknowledge():


// Producteur connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); producer = session.createProducer(topic); message = session.createTextMessage(); producer.send(message); // Consommateur message.acknowledge();

Abonnements durables

Linconvnient du modle Publication-abonnement est quil faut quun consommateur de messages sexcute lorsque les messages sont envoys au sujet; sinon il ne les recevra pas. En utilisant des abonns durables, lAPI JMS fournit un moyen de conserver les messages dans le sujet jusqu ce que tous les consommateurs abonns les aient reus. Grce labonnement durable, le rcepteur peut donc tre dconnect: lorsquil se reconnectera, il recevra les messages arrivs entre-temps. Pour cela, le client cre un abonn durable en utilisant la session:
session.createDurableSubscriber(topic, "javaee6DurableSubscription");

partir de l, le programme client lance la connexion et reoit les messages. Le nom javaee6DurableSubscription sert didentifiant pour labonnement durable:

446

Java EE 6 et GlassFish 3

il doit tre unique et provoque la dclaration dune fabrique de connexions unique pour chaque abonn durable.
Priorits

Les priorits permettent de faire en sorte que le fournisseur JMS dlivre en premier les messages urgents. JMS dfinit dix priorits, de 0 (la plus faible) 9 (la plus forte). Vous pouvez indiquer la valeur de la priorit par un appel la mthode setPriority() ou en la passant en paramtre la mthode send():
MessageProducer producer = session.createProducer(topic); producer. setPriority(2); // ou producer.send(message, DeliveryMode.NON_PERSISTENT, 2, 1000);

MDB: Message-Driven Beans


Ce chapitre montre comment les messages asynchrones de JMS autorisent un couplage faible entre les systmes dans un environnement autonome avec des classes principales, des recherches JNDI ou linjection de ressources avec un conteneur client dapplication. Les MDB offrent la mme chose pour les applications professionnelles qui sexcutent dans un conteneur EJB. Un MDB est un consommateur asynchrone qui est appel par le conteneur lorsquun message arrive. Du point de vue du producteur du message, un MDB nest quun simple consommateur cach derrire la destination quil surveille. Les MDB font partie de la spcification EJB et leur modle ressemble celui des beans de session sans tat car ils nont pas dtat et sexcutent dans un conteneur EJB. Le conteneur surveille une destination et dlgue lappel au MDB lorsquun message arrive. Comme nimporte quel EJB, les MDB peuvent accder aux ressources gres par le conteneur (autres EJB, connexions JDBC, ressources JMS, etc.). Pourquoi utiliser les MDB alors que lon peut utiliser des clients JMS autonomes comme on la fait jusqu prsent? cause du conteneur, qui gre les threads, la scurit et les transactions, ce qui simplifie beaucoup le code du consommateur JMS. Le conteneur gre galement les messages entrants en les rpartissant entre les diffrentes instances des MDB (places dans un pool), qui ne grent pas elles-mmes le

Chapitre 13

Envoi de messages 447

multithread. Ds quun nouveau message atteint la destination, une instance MDB est retire du pool pour grer le message.
Cration dun MDB

Les MDB peuvent tre transactionnels, multithreads et, naturellement, ils peuvent consommer des messages JMS. Avec lAPI JMS, vous pourriez vous attendre devoir utiliser des fabriques, des connexions, des consommateurs, des transactions, etc., comme prcdemment. Pourtant, laspect dun MDB simple comme celui du Listing13.8 risque de vous surprendre.
Listing13.8: Un MDB simple
@MessageDriven(mappedName = "jms/javaee6/Topic") public class BillingMDB implements MessageListener { public void onMessage(Message message) { TextMessage msg = (TextMessage)message; System.out.println("Message received: " + msg.getText()); } }

Le code du Listing13.8 (qui omet le traitement des exceptions pour plus de clart) montre que les MDB librent le programmeur de tous les aspects techniques du traitement des diffrents types de messages que nous avons prsents jusquici. Un MDB implmente linterface MessageListener et la mthode onMessage(), mais aucun autre code nest ncessaire pour se connecter au fournisseur ou pour lancer la consommation des messages. Les MDB utilisent galement le mcanisme de configuration par exception, ce qui implique que seul un petit nombre dannotations suffit pour les faire fonctionner (voir lannotation @MessageDriven).
Le modle des MDB

Les MDB sont diffrents des beans de session car ils nimplmentent pas dinterface mtier locale ou distante; ils implmentent la place javax.jms.MessageListener. Les clients ne peuvent pas appeler directement les mthodes des MDB; cependant, comme les beans de session, les MDB utilisent un modle de programmation labor, avec un cycle de vie, des annotations de rappel, des intercepteurs, linjection et les transactions. Les applications qui tirent parti de ce modle disposent donc de nombreuses fonctionnalits.

448

Java EE 6 et GlassFish 3

Il est important de se rappeler que les MDB ne font pas partie du nouveau modle EJB Lite, ce qui signifie quon ne peut pas les dployer dans une application web simple (dans un fichier war): ils ont besoin dun assemblage entreprise (archive ear). Une classe MDB doit respecter les rgles suivantes: Elle doit tre annote par @javax.ejb.MessageDriven ou son quivalent XML dans un descripteur de dploiement. Elle doit implmenter, directement ou non, linterface MessageListener. Elle doit tre publique et ne doit tre ni finale ni abstraite. Elle doit proposer un constructeur public sans paramtre qui servira crer les instances du MDB. Elle ne doit pas dfinir de mthode finalize(). La classe MDB peut implmenter dautres mthodes, invoquer dautres ressources, etc. Les MDB sont dploys dans un conteneur et peuvent ventuellement tre assembls avec un fichier ejb-jar.xml. Avec le modle simplifi de Java EE6, un MDB peut tre un simple POJO annot et faire lconomie de la majeure partie de sa configuration. Cependant, vous pouvez utiliser les lments des annotations @MessageDriven et @ActivationConfigProperty (ou leurs quivalents XML) pour adapter la configuration de JMS vos besoins.

@MessageDriven

Les MDB font partie des EJB les plus simples dvelopper car ils utilisent un minimum dannotations. @MessageDriven (ou son quivalent XML) est obligatoire car cest une mtadonne dont a besoin le conteneur pour savoir que la classe Java est un MDB. LAPI de cette annotation, prsente dans le Listing13.9, est trs simple et tous ses lments sont facultatifs.
Listing13.9: API de lannotation @MessageDriven
@Target(TYPE) @Retention(RUNTIME) public @interface MessageDriven { String name() default ""; Class messageListenerInterface default Object.class; ActivationConfigProperty[] activationConfig() default {}; String mappedName(); String description(); }

Llment name prcise le nom du MDB (qui est, par dfaut, celui de la classe). messageListenerInterface prcise lcouteur de message que le MDB implmente

Chapitre 13

Envoi de messages 449

(sil implmente plusieurs interfaces, cet lment indique au conteneur EJB quelle est linterface MessageListener). Llment mappedName est le nom JNDI de la destination que doit surveiller le MDB. description est une simple chane qui dcrit le MDB lorsquil est dploy. Llment activationConfig sert prciser les proprits de configuration; il ne prend pas de paramtre et renvoie un tableau dannotations @ActivationConfigProperty.
@ActivationConfigProperty

JMS permet de configurer certaines proprits comme les slecteurs de messages, le mode dacquittement, les abonnements durables, etc. Dans un MDB, ces proprits peuvent tre configures via lannotation facultative @ActivationConfigProperty. Celle-ci peut tre passe en paramtre lannotation @MessageDriven; par rapport son quivalent JMS, elle est trs simple puisquelle est forme dune paire nom/ valeur (voir Listing13.10).
Listing13.10: API de lannotation ActivationConfigProperty
@Target({}) @Retention(RUNTIME) public @interface ActivationConfigProperty { String propertyName(); String propertyValue(); }

Cette annotation permet dutiliser une configuration spcifique du systme de messages, ce qui signifie que les proprits ne sont pas portables entre les fournisseurs JMS. Le code du Listing13.11, par exemple, configure le mode dacquittement et le slecteur de message pour OpenMQ.
Listing13.11: Configuration de proprits avec les MDB
@MessageDriven(mappedName = "jms/javaee6/Topic", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "orderAmount < 3000") }) public class BillingMDB implements MessageListener { public void onMessage(Message message) { TextMessage msg = (TextMessage)message; System.out.println("Message received: " + msg.getText()); }

450

Java EE 6 et GlassFish 3

Chaque proprit dactivation est une paire nom/valeur interprte par le fournisseur de messages sous-jacent, qui lutilise pour configurer le MDB. Le Tableau13.5 numre quelques-unes des proprits reconnues par OpenMQ (consultez la documentation de votre fournisseur JMS si vous utilisez une autre implmentation).
Tableau13.5: Proprits dactivation dOpenMQ

Proprit
destinationType destination messageSelector acknowledgeMode subscriptionDurability subscriptionName

Description Type de destination, qui peut tre TOPIC ou QUEUE. Nom de la destination. Chane de slecteur utilise par le MDB. Mode dacquittement (AUTO_ACKNOWLEDGE par dfaut). Persistance de labonnement (NON_DURABLE par dfaut). Nom dabonn du consommateur.

Comme dans lAPI de la classe javax.jms.Session, il y a trois modes dacquittement, mais les MDB ne peuvent en utiliser que deux:
AUTO_ACKNOWLEDGE. La session accuse automatiquement rception dun message. DUPS_OK_ACKNOWLEDGE. CLIENT_ACKNOWLEDGE.

Demande la session dacquitter la livraison des messages de faon paresseuse (ce qui provoque parfois des livraisons en double).

Ce mode nest pas autoris car les MDB ne doivent pas accder directement lAPI JMS pour lacquittement des messages. Cet acquittement est gr automatiquement par le conteneur.

Injection des dpendances

Comme tous les autres EJB que nous avons prsents au Chapitre6, les MDB peuvent utiliser linjection des dpendances pour obtenir des rfrences des ressources comme des sources de donnes JDBC, des EJB ou dautres objets. Linjection est le mcanisme par lequel le conteneur insre automatiquement une rfrence dobjet pour les attributs annots. Ces ressources doivent tre disponibles dans le conteneur ou dans le contexte de lenvironnement. Le code suivant est donc autoris dans un MDB:
@PersistenceContext private EntityManager em;

Chapitre 13

Envoi de messages 451

@EJB private InvoiceBean invoice; @Resource(name = "jms/javaee6/ConnectionFactory") private ConnectionFactory connectionFactory;

Le contexte MDB peut galement tre inject laide de lannotation @Resource:


@Resource private MessageDrivenContext context;

Contexte MDB

Linterface MessageDrivenContext donne accs au contexte dexcution que fournit le conteneur une instance MDB. Le conteneur passe linterface MessageDrivenContext cette instance qui reste associe toute la dure de vie du MDB, ce qui lui donne la possibilit dannuler explicitement une transaction, de connatre le principal de lutilisateur, etc. Cette interface hrite de javax.ejb.EJBContext sans lui ajouter de mthode supplmentaire. En injectant une rfrence son contexte, le MDB aura accs aux mthodes du Tableau13.6.
Tableau13.6: Mthodes de linterface MessageDrivenContext

Mthode
getCallerPrincipal getRollbackOnly getTimerService getUserTransaction

Description Renvoie lobjet java.security.Principal associ lappel. Teste si la transaction courante a t marque pour annulation. Renvoie linterface javax.ejb.TimerService. Renvoie linterface javax.transaction.UserTransaction utiliser pour dlimiter les transactions. Seuls les MDB avec des transactions gres par les beans (BMT) peuvent appeler cette mthode. Teste si lappelant a le rle indiqu. Permet au MDB de rechercher des variables denvironnement qui lui sont propres dans le contexte des noms JNDI. Permet linstance de marquer la transaction courante pour annulation. Seuls les MDB avec des transactions gres par les beans (BMT) peuvent appeler cette mthode.

isCallerInRole Lookup setRollbackOnly

452

Java EE 6 et GlassFish 3

Cycle de vie et annotations de rappels

Le cycle de vie des MDB (voir Figure 13.11) est identique celui des beans de session sans tat: soit le MDB existe et est prt consommer des messages, soit il nexiste pas. Avant de se terminer, le conteneur cre dabord une instance du MDB et injecte au besoin les ressources ncessaires indiques par les annotations (@Resource, @Ejb, etc.) ou le descripteur de dploiement. Puis le conteneur appelle la mthode de rappel @PostConstruct du bean, sil en a une. Le MDB est alors dans ltat "prt" et attend de consommer tout message entrant. La mthode de rappel @PreDestroy est appele lorsque le MDB est supprim du pool ou dtruit.
Figure13.11 Cycle de vie dun MDB.
@PostConstruct Prt @PreDestroy N'existe pas

onMessage()

Ce comportement est identique celui des beans de session sans tat (voir Chapitre8 pour plus de dtails sur les mthodes de rappel et les intercepteurs) et vous pouvez, comme avec les autres EJB, ajouter des intercepteurs laide de lannotation @javax.ejb.AroundInvoke.
MDB comme consommateur

Comme on la expliqu dans la section "API JMS", plus haut dans ce chapitre, les consommateurs peuvent recevoir un message de faon synchrone en bouclant en attendant quil arrive ou de faon asynchrone en implmentant linterface MessageListener. Par nature, les MDB sont conus pour fonctionner comme des consommateurs asynchrones: ils implmentent une interface dcoute des messages qui est dclenche par le conteneur lorsquun message arrive. Un MDB peut galement consommer les messages de faon synchrone, mais ce nest pas conseill: les consommateurs synchrones bloquent et occupent les ressources du serveur (les EJB ne feront rien pendant quils bouclent et le conteneur ne pourra pas les librer). Les MDB, comme les beans de session sans tat, sont placs dans un pool dune certaine taille. Lorsque le conteneur a besoin dune instance, il en extrait une du pool et lutilise. Si chaque instance entrait dans une boucle infinie, le pool

Chapitre 13

Envoi de messages 453

finirait par se vider et toutes les instances disponibles seraient occupes boucler. Le conteneur EJB pourrait alors crer plus dinstances de MDB et ainsi augmenter la taille du pool et consommer de plus en plus de mmoire. Pour cette raison, les beans de session et les MDB ne doivent pas tre utiliss comme des consommateurs synchrones. Le Tableau13.7 prsente les diffrents modes de rception des MDB et des beans de session.
Tableau13.7: Comparaison des MDB et des beans de session

Beans Beans de session MDB

Producteur Oui Oui

Rcepteur synchrone Dconseill Dconseill

Rcepteur asynchrone Impossible Oui

MDB comme producteur

Les MDB peuvent devenir des producteurs de message, ce qui arrive souvent lorsquils sont utiliss dans un workflow puisquils reoivent un message dune destination, le traitent et lenvoient une autre destination. Pour ajouter cette possibilit, il faut utiliser lAPI JMS. Une destination et une fabrique de connexions peuvent tre injectes avec une annotation @Resource ou par une recherche JNDI, puis des appels aux mthodes de lobjet javax.jms.Session permettent de crer et denvoyer un message. Le code de BillingMDB (voir Listing13.12) surveille un sujet (jms/javaee6/Topic), reoit des messages (mthode onMessage()) et envoie un nouveau message dans une file dattente (jms/ javaee6/Queue).
Listing13.12: Un MDB consommateur et producteur de messages
@MessageDriven(mappedName = "jms/javaee6/Topic", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "orderAmount < 3000") }) public class BillingMDB implements MessageListener { @Resource(name = "jms/javaee6/Queue") private Destination printingQueue;

454

Java EE 6 et GlassFish 3

@Resource(name = "jms/javaee6/ConnectionFactory") private ConnectionFactory connectionFactory; private Connection connection; @PostConstruct private void initConnection() { connection = connectionFactory.createConnection(); } @PreDestroy private void closeConnection() { connection.close(); } public void onMessage(Message message) { TextMessage msg = (TextMessage)message; System.out.println("Message received: " + msg.getText()); sendPrintingMessage(); } private void sendPrintingMessage() throws JMSException { Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(printingQueue); TextMessage message = session.createTextMessage(); message.setText("This message has been received and sent again"); producer.send(message); session.close(); } }

Ce MDB utilise la plupart des concepts que nous avons prsents. Il se sert dabord de lannotation @MessageDriven pour dfinir le nom JNDI du sujet quil surveille (mappedName = "jms/javaee6/Topic"). Dans cette mme annotation, il dfinit un ensemble de proprits comme le mode dacquittement et un slecteur de message laide dun tableau dannotations @ActivationConfigProperty; enfin, il implmente MessageListener et sa mthode onMessage(). Ce MDB doit galement produire un message. Il reoit donc par injection les deux objets administrs ncessaires: une fabrique de connexions et une destination (la file nomme jms/ javaee6/Queue). Il peut alors crer et fermer une instance partage de javax.jms.Connection en utilisant des mthodes de rappel du cycle de vie. La cration dune connexion tant coteuse, placer ce code dans les mthodes annotes par @PostConstruct et @PreDestroy garantit quelles nauront lieu qu la cration et la destruction du MDB.

Chapitre 13

Envoi de messages 455

Enfin, la mthode mtier qui envoie les messages (la mthode sendPrintingMessage()) ressemble ce que nous avons dj vu: une session JMS est cre et sert crer un message texte et un producteur, puis le message est envoy. Pour plus de clart, nous avons omis le traitement des exceptions.
Transactions

Les MDB tant des EJB (voir Chapitre9 pour plus dinformations), ils peuvent donc utiliser des transactions gres par les beans (BMT) ou par le conteneur (CMT), annuler explicitement une transaction avec la mthode MessageDrivenContext. setRollbackOnly(), etc. Ceci dit, les MDB ont quelques spcificits qui mritent dtre connues. Lorsque lon parle de transactions, on pense toujours aux bases de donnes relationnelles, mais dautres ressources sont galement transactionnelles: les systmes de messages, notamment. Deux oprations ou plus qui doivent russir ou chouer ensemble forment une transaction : avec les messages, si deux messages ou plus sont envoys, ils doivent tous russir ou tous chouer. En pratique, le conteneur lancera une transaction avant lappel de la mthode onMessage() et la validera au retour de cette mthode (sauf si la transaction a t marque pour annulation avec setRollbackOnly()). Bien que les MDB soient transactionnels, ils ne peuvent pas sexcuter dans le contexte de transaction du client car ils nont pas de client ; personne nappelle explicitement leurs mthodes : ils se contentent de surveiller une destination et de consommer les messages. Aucun contexte nest pass dun client un MDB, notamment pas le contexte transactionnel du client pour la mthode onMessage(). Le Tableau13.8 compare les CMT avec les beans de session et les MDB.
Tableau13.8: Transactions MDB compares avec celles des beans de session

Attribut de transaction
NOT_SUPPORTED REQUIRED MANDATORY REQUIRES_NEW SUPPORTS NEVER

Beans de session Oui Oui Oui Oui Oui Oui

MDB Oui Oui Non Non Non Non

456

Java EE 6 et GlassFish 3

Avec les CMT, les MDB peuvent utiliser lannotation @javax. ejb.TransactionAttribute sur les mthodes mtiers avec les deux attributs suivants: REQUIRED (valeur par dfaut). Si le MDB invoque dautres beans dentreprise, le conteneur passe le contexte transactionnel avec lappel. Le conteneur tente de valider la transaction lorsque la mthode dcoute des messages sest termine.
NOT_SUPPORTED. Si le MDB invoque dautres beans dentreprise, le conteneur ne

passe pas de contexte transactionnel avec lappel.


Gestion des exceptions

Dans ce chapitre, nous avons omis le traitement des exceptions dans les extraits de code car lAPI JMS permettant de les grer est trs verbeuse. Elle dfinit douze exceptions diffrentes qui hritent toutes de javax.jms.JMSException : tous les appels de mthodes des connexions, des sessions, des consommateurs, des messages ou des producteurs lancent une JMSException ou lune de ses sous-classes en cas de problme. Il est important de noter que JMSException est une exception contrle. Comme on la dj voqu, la spcification EJB distingue deux types dexceptions:

Les exceptions dapplications. Ce sont des exceptions contrles qui hritent dException et qui ne provoquent pas lannulation dune transaction par le conteneur. Les exceptions systmes. Ce sont des exceptions non contrles qui hritent de RuntimeException et qui provoquent lannulation de la transaction par le conteneur.

Le dclenchement dune JMSException ne provoque donc pas lannulation dune transaction. Si cette annulation est ncessaire, il faut appeler explicitement la mthode setRollBackOnly() ou relancer une exception systme (comme EJBException):
public void onMessage(Message message) { TextMessage msg = (TextMessage)message; try { System.out.println("Message received: " + msg.getText()); sendPrintingMessage(); } catch (JMSException e) { context.setRollBackOnly(); } }

Chapitre 13

Envoi de messages 457

Rcapitulatif
Les concepts les plus importants des messages sont les modles P2P et Publication-abonnement, les objets administrs (fabriques de connexion et destinations), la faon de se connecter un fournisseur et de produire ou consommer les messages, les mcanismes de fiabilit utiliss par JMS et lutilisation des composants grs par le conteneur (MDB) pour surveiller les destinations. Nous allons ici tudier comment ces concepts fonctionnent ensemble travers un exemple, le compiler et lassembler avec Maven et le dployer sur GlassFish. Cet exemple utilise une classe autonome (OrderSender) qui envoie des messages dans une file dattente (jms/javaee6/Queue). Ces messages sont des objets reprsentant une commande de livres et de CD (OrderDTO) qui ont plusieurs attributs, dont le montant total de la commande. lautre extrmit de la file, un MDB (OrderMDB) ne consomme que les commandes dont le montant excde 1000. Le montant dune commande est pass en paramtre la classe OrderSender. Maven devant structurer le code en fonction des artfacts finaux, la classe OrderSender sera dploye dans un fichier jar, OrderMDB dans une autre et la classe OrderDTO sera commune ces deux fichiers.
OrderDTO

Lobjet qui sera envoy dans le message JMS est un POJO qui doit implmenter linterface Serializable. La classe OrderDTO prsente dans le Listing13.13 donne quelques informations sur la commande, dont son montant total; ce sont ces objets qui seront placs dans un ObjectMessage JMS et envoys dOrderSender OrderMDB.
Listing13.13: Un objet OrderDTO sera pass dans un ObjectMessage JMS
public class OrderDTO implements Serializable { private private private private Long orderId; Date creationDate; String customerName; Float totalAmount;

// Constructeurs, getters, setters }

458

Java EE 6 et GlassFish 3

OrderSender

La classe OrderSender du Listing 13.14 est un client autonome qui utilise lAPI JMS pour envoyer un ObjectMessage la file jms/javaee6/Queue. Elle reoit par injection la fabrique de connexions ainsi que la destination; sa mthode main() cre une instance de la classe OrderDTO notez que le totalAmount de la commande est un paramtre pass la classe (dans args[0]). La Session cre un ObjectMessage et place la commande dans le corps du message avec message.setObject(order). Le montant total est plac dans une proprit (message.setFloatProperty()) pour tre utilis plus tard par un slecteur.
Listing13.14: La classe OrderSender envoie un OrderDTO dans un message
public class OrderSender { @Resource(mappedName = "jms/javaee6/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName = "jms/javaee6/Queue") private static Queue queue; public static void main(String[] args) { // Cre un orderDto avec le montant total en paramtre Float totalAmount = Float.valueOf(args[0]); OrderDTO order = new OrderDTO(1234l, new Date(), "Serge Gainsbourg", totalAmount); try { // Cre les artfacts ncessaires la connexion la file Connection connection= connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(queue); // Envoie un message dans la file ObjectMessage message = session.createObjectMessage(); message.setObject(order); message.setFloatProperty("orderAmount", totalAmount); producer.send(message); connection.close(); } catch (Exception e) { e.printStackTrace(); } } }

Chapitre 13

Envoi de messages 459

OrderMDB

La classe OrderMDB (voir Listing 13.15) est un MDB annot par @MessageDriven qui surveille la destination jms/javaee6/Queue. Il ne sintresse quaux commandes suprieures 1 000 en utilisant pour cela un slecteur (orderAmount > 1000). Lorsquun message arrive, la mthode onMessage() le consomme, le transtype en ObjectMessage et rcupre son corps (msg.getObject()). Ici, on se contente dafficher le message, mais dautres traitements sont possibles.
Listing13.15: OrderMDB reoit un ObjectMessage
@MessageDriven(mappedName = "jms/javaee6/Queue", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "orderAmount > 1000") }) public class OrderMDB implements MessageListener { public void onMessage(Message message) { try { ObjectMessage msg = (ObjectMessage) message; OrderDTO order = (OrderDTO) msg.getObject(); System.out.println("Order received: " + order.toString()); } catch (JMSException e) { e.printStackTrace(); } } }

Compilation et assemblage avec Maven

Lmetteur et le MDB doivent tre assembls dans des fichiers jar distincts et utiliser des structures de rpertoires et des fichiers pom.xml diffrents. Le fichier pom. xml dOrderMDB prsent dans le Listing 13.16 est quasiment identique celui dOrderSender. Les MDB utilisant des annotations du paquetage EJB (@MessageDriven), il faut dclarer la dpendance javax.ejb. La dpendance javax.jms, quant elle, est ncessaire pour accder lAPI JMS (sessions, messages, etc.). Toutes les deux ont une porte provided car GlassFish, en tant que conteneur EJB et fournisseur JMS, fournit ces API lors de son excution. Vous devez galement informer Maven que vous utilisez Java SE6 en configurant en consquence maven-compiler-plugin.

460

Java EE 6 et GlassFish 3

Listing13.16: Fichier pom.xml pour compiler et assembler le MDB


<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter13-MDB</artifactId> <version>1.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.jms</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Pour compiler et assembler les classes, ouvrez un interprteur de commandes dans le rpertoire qui contient le fichier pom.xml et lancez la commande suivante:
mvn package

Le rpertoire target devrait maintenant contenir le fichier chapter13-MDB-1.0.jar. Si vous louvrez, vous pourrez constater quil contient le fichier class dOrderMDB. Pour OrderSender, vous obtiendrez un autre fichier jar, chapter13-Sender-1.0.jar.

Chapitre 13

Envoi de messages 461

Cration des objets administrs

JMS doit crer les objets administrs ncessaires lenvoi et la rception des messages. Chacun deux possde un nom JNDI afin que les clients puissent en obtenir une rfrence (ici, nous nous servons de linjection):

La fabrique de connexions sappelle jms/javaee6/ConnectionFactory. La file dattente sappelle jms/javaee6/Queue.

Ces objets tant administrs, GlassFish doit tre en cours dexcution pour quils soient crs car OpenMQ sexcute dans GlassFish. Si la commande asadmin est dans votre path, lancez la commande suivante linvite de linterprteur:
asadmin create-jms-resource -restype javax.jms.ConnectionFactory jms/javaee6/ConnectionFactory asadmin create-jms-resource --restype javax.jms.Queue jms/javaee6/Queue

La console web de GlassFish permet galement de mettre en place la fabrique de connexions et la file dattente mais, daprs mon exprience, le moyen le plus simple et le plus rapide dadministrer GlassFish consiste passer par asadmin. Lune de ses commandes permet dnumrer toutes les ressources JMS et de vrifier que les objets administrs ont bien t crs:
asadmin list-jms-resources jms/javaee6/Queue jms/javaee6/ConnectionFactory

Dploiement du MDB dans GlassFish

Lorsque le MDB est assembl dans un jar, il reste le dployer dans GlassFish. Il y a plusieurs moyens de le faire, notamment par la console dadministration web. Cependant, la ligne de commande asadmin permet de le faire trs simplement : ouvrez une fentre de commandes, placez-vous dans le rpertoire target o se trouve le fichier chapter13-MDB-1.0.jar, vrifiez que GlassFish sexcute et tapez la commande suivante:
asadmin deploy chapter13-MDB-1.0.jar

Si le dploiement a russi, la commande qui suit devrait renvoyer le nom et le type du jar dploy (ejb-module, ici):
asadmin list-components chapter13-MDB-1.0 <ejb-module>

462

Java EE 6 et GlassFish 3

Excution de lexemple

Le MDB est dploy dans GlassFish et surveille la destination jms/javaee6/Queue en attendant quun message arrive : il est temps de lancer le client OrderSender. Bien quil sagisse dune application autonome qui sexcute lextrieur de GlassFish, il faut lui injecter des ressources (la fabrique de connexions et la file dattente). Pour lexcuter, on peut utiliser un ACC (Application Client Container), qui est un conteneur enveloppant un fichier jar pour lui donner accs aux ressources du serveur dapplications. Pour lancer lACC, utilisez la commande appclient fournie avec GlassFish en lui passant en paramtre le nom du fichier jar ainsi que les paramtres de lapplication (le montant de la commande). La commande suivante, par exemple, envoie un message avec un montant de 2000:
appclient -client chapter13-Sender-1.0.jar 2000

Ce montant tant suprieur 1000 (la limite dfinie dans le slecteur de message), lOrderMDB devrait le recevoir et afficher le message (ce que vous pourrez vrifier en consultant le journal de GlassFish). Si vous passez un montant infrieur 1000, le MDB ne recevra pas le message:
appclient -client chapter13-Sender-1.0.jar 500

Rsum
Ce chapitre a montr que les messages sont une forme asynchrone, faiblement couple, de communication entre les composants. Le MOM peut tre considr comme un tampon plac entre les systmes qui ont besoin de produire et de consommer les messages leurs propres rythmes. Cest donc une architecture diffrente de celle de RPC (comme RMI) o les clients doivent connatre les mthodes du service quils utilisent. La premire section de ce chapitre sest intresse lAPI JMS et son vocabulaire. Le modle asynchrone est une API trs puissante qui peut tre utilise dans les environnements JavaSE ou JavaEE. Elle repose sur les modles P2P ou Publicationabonnement, sur les fabriques de connexions, les destinations, les connexions, les sessions, les messages (en-ttes, proprits, corps) de diffrents types (texte, objet, dictionnaires, flux, octets), les slecteurs et les mcanismes de fiabilit comme lacquittement ou la persistance.

Chapitre 13

Envoi de messages 463

Java EE dispose de composants dentreprise spciaux pour consommer les messages : les MDB. La seconde section du chapitre a montr comment utiliser ces MDB comme consommateurs asynchrones et comment ils dlguent plusieurs services leur conteneur (cycle de vie, intercepteurs, transactions, scurit, concurrence, acquittement des messages, etc.). Cette section a galement montr comment rassembler toutes ces pices avec Maven, GlassFish et OpenMQ en prsentant un exemple dmetteur autonome et de MDB rcepteur. Les chapitres suivants prsenteront dautres technologies dinteractions avec les systmes externes: les services web SOAP et REST.

14
Services web SOAP
Auparavant considr comme un terme la mode, SOA (Service-Oriented Architecture) fait aujourdhui partie de la vie quotidienne des architectures. Bien quelle soit souvent confondue avec les services web, SOA est une architecture reposant principalement sur des applications orientes services qui peuvent tre implmentes laide de services web, mais galement avec dautres technologies. On dit que les services web sont "faiblement coupls" car leurs clients nont pas besoin de connatre les dtails dimplmentation (le langage utilis pour les dvelopper ou les signatures des mthodes, notamment). Le consommateur peut invoquer un service web laide dune interface intuitive dcrivant les mthodes mtiers disponibles (paramtres et valeur de retour). Limplmentation sous-jacente peut tre ralise avec nimporte quel langage de programmation (Visual Basic, C#, C, C++, Java, etc.). Avec un couplage faible, un consommateur et un service peuvent quand mme changer des donnes: en utilisant des documents XML. Un consommateur envoie une requte un service web sous la forme dun document XML et, ventuellement, reoit une rponse galement en XML. Les services web concernent galement la distribution. Les logiciels distribus existent depuis longtemps mais, la diffrence des systmes distribus existants, les services web sont conus pour le Web: leur protocole rseau par dfaut est HTTP, un protocole sans tat bien connu et robuste. Les services web sont partout et peuvent sexcuter sur des machines de bureau ou intervenir dans une intgration B2B (business-to-business) pour que des oprations qui ncessitaient auparavant une intervention manuelle sexcutent automatiquement. Ils intgrent des applications utilises par diffrentes organisations sur Internet ou au sein de la mme socit dsignes par le terme EAI (Enterprise Application Integration). Dans tous les cas, les services web constituent un moyen standard de connecter diffrents composants logiciels.

466

Java EE 6 et GlassFish 3

Prsentation des services web


Pour rsumer, les services web sont une sorte de logique mtier offerte une application cliente (cest--dire un consommateur de service) via une interface de service. la diffrence des objets ou des EJB, les services web fournissent une interface faiblement couple en se servant de XML. Les standards prcisent que linterface laquelle on envoie un message doit dfinir le format du message de requte et de rponse, ainsi que les mcanismes pour publier et pour dcouvrir les interfaces du service (une base de registres). La Figure 14.1 reprsente linteraction dun service web de faon trs abstraite. Le service peut ventuellement enregistrer son interface dans une base de registres (UDDI) afin quun consommateur puisse le trouver. Une fois que le consommateur connat linterface du service et le format du message, il peut envoyer une requte et recevoir une rponse.
Figure14.1 Le consommateur dcouvre le service via un registre.
Consommateur <<registry>> UDDI

XML/HTTP

Service web

Les services web ncessitent plusieurs technologies et protocoles pour transporter et transformer les donnes dun client vers un service de faon standard. Les plus courants sont les suivants:

UDDI (Universal Description Discovery and Integration) est une base de registres et un mcanisme de dcouverte qui ressemble aux pages jaunes. Il sert stocker et classer les interfaces des services web. WSDL (Web Services Description Language) dfinit linterface du service web, les donnes et les types des messages, les interactions et les protocoles. SOAP (Simple Object Access Protocol) est un protocole dencodage des messages reposant sur les technologies XML. Il dfinit une enveloppe pour la communication des services web. Les messages sont changs laide dun protocole de transport. Bien que HTTP (Hypertext Transfer Protocol) soit le plus utilis, dautres comme SMTP ou JMS sont galement possibles.

Chapitre 14

Services web SOAP 467

XML (Extensible Markup Language) est la base sur laquelle sont construits et dfinis les services web (SOAP, WSDL et UDDI).

Grce ces technologies standard, les services web ont un potentiel quasiment illimit. Les clients peuvent appeler un service qui peut tre associ nimporte quel programme et accommoder nimporte quel type et structure de donnes pour changer des messages via XML.
UDDI

Les programmes qui interagissent avec un autre via le Web doivent pouvoir trouver les informations leur permettant de sinterconnecter. UDDI fournit pour cela une approche standardise permettant de trouver les informations sur un service web et sur la faon de linvoquer. UDDI est une base de registres de services web en XML, un peu comme les professionnels peuvent enregistrer leurs services dans les pages jaunes. Cet enregistrement inclut le type du mtier, sa localisation gographique, le site web, le numro de tlphone, etc. Les autres mtiers peuvent ensuite parcourir cette base et retrouver les informations sur un service web spcifique, qui contiennent des mtadonnes supplmentaires dcrivant son comportement et son emplacement. Ces informations sont stockes sous la forme de document WSDL: les clients peuvent lire ce document afin dobtenir linformation et invoquer le service.
WSDL

La base de registres UDDI pointe vers un fichier WSDL sur Internet, qui peut tre tlcharg par les consommateurs potentiels. WSDL est un langage de dfinition dinterfaces (IDL) permettant de dfinir les interactions entre les consommateurs et les services (voir Figure14.2). Cest donc le composant central dun service web puisquil dcrit le type du message, le port, le protocole de communication, les oprations possibles, son emplacement et ce que le client peut en attendre. Vous pouvez considrer WSDL comme une interface Java, mais crite en XML.
Figure14.2 Interface WSDL entre le consommateur et le service web.
JVM Cliente
<<component>> Consommateur Protocole de transport Message SOAP

Conteneur de service web


<<component>> Service web

WSDL

468

Java EE 6 et GlassFish 3

Pour garantir linteroprabilit, linterface standard du service web doit tre standardise, afin quun consommateur et un producteur puissent partager et comprendre un message. Cest le rle de WSDL; SOAP, de son ct, dfinit la faon dont le message sera envoy dun ordinateur lautre.
SOAP

SOAP est le protocole standard des services web. Il fournit le mcanisme de communication permettant de connecter les services qui changent des donnes au format XML au moyen dun protocole rseau HTTP le plus souvent. Comme WSDL, SOAP repose fortement sur XML : un message SOAP est un document XML contenant plusieurs lments (une enveloppe, un corps, etc.). SOAP est conu pour fournir un protocole indpendant et abstrait, permettant de connecter des services distribus. Ces services connects peuvent tre construits laide de nimporte quelle combinaison de matriels et de logiciels reconnaissant un protocole de transport donn.
Protocole de transport

Pour quun consommateur puisse communiquer avec un service web, il faut quils puissent senvoyer des messages. Les services web tant essentiellement utiliss sur le Web, ils utilisent gnralement HTTP, mais dautres protocoles, comme HTTPS (Secure HTTP), TCP/IP, SMTP (Simple Mail Transport Protocol), FTP (File Transfer Protocol), etc. sont galement possibles.
XML

XML est utilis par la plate-forme Java EE pour les descripteurs de dploiement, les mtadonnes, etc. Pour les services web, XML sert galement de technologie dintgration pour rsoudre les problmes dindpendance des donnes et dinteroprabilit. Il est utilis non seulement pour le format des messages mais galement pour dfinir les services (WSDL) ou la faon dont ils sont changs (SOAP). Des schmas sont associs ces documents XML pour valider les donnes changes.

Rsum de la spcification des services web


La persistance est essentiellement couverte par la spcification JPA. Pour les services web, la situation est plus complexe car il faut prendre en compte de nombreuses

Chapitre 14

Services web SOAP 469

-spcifications provenant de standards diffrents. En outre, les services web tant utiliss par de nombreux autres langages de programmation, ces spcifications ne sont pas directement lies au JCP (Java Community Process).
Bref historique des services web

Les services web sont un moyen standard permettant aux entreprises de communiquer sur un rseau. Leurs prcurseurs sappellent CORBA (Common Object Request Broker Architecture), initialement utilis par les systmes Unix et DCOM (Distributed Component Object Model), son rival Microsoft. un niveau plus bas, on trouve RPC (Remote Procedure Call) et, plus prs du monde Java, RMI (Remote Method Invocation). Avant le Web, il tait difficile de mettre daccord les acteurs majeurs du logiciel sur un protocole de transport. Lorsque HTTP est devenu un standard, il a galement obtenu petit petit le statut de support de communication universel. peu prs en mme temps, XML est devenu un standard officiel lorsque le W3C (World Wide Web Consortium) a annonc que XML1.0 pouvait tre dploy dans les applications. En 1998, ces deux ingrdients HTTP et XML taient prts travailler ensemble. SOAP 1.0, lanc en 1998 par Microsoft, fut finalement livr la fin de 1999. Il permettait alors de modliser les rfrences types et les tableaux dans un schma XML. En 2000, IBM commena travailler sur SOAP 1.1 et WSDL fut soumis au W3C en 2001. UDDI a t cr en 2000 par OASIS (Organization for the Advancement of Structured Information Standards) afin de permettre aux entreprises de publier et de retrouver les services web. Avec SOAP, WSDL et UDDI, les standards de facto pour la cration de services web taient dsormais en place. En juin 2002, Java a introduit les services web avec JAX-RPC 1.0 (Java API for XML-based RPC1.0) et JAX-RPC1.1 a t ajout J2EE1.4 en 2003, mais cette spcification tait trs touffue et difficile utiliser. Avec larrive de Java EE 5, une toute nouvelle spcification JAX-WS2.0 (Java API for XML-based Web Services2.0) vit le jour et devint le modle prfr pour les services web. Actuellement, Java EE6 est fourni avec JAX-WS2.2.
Spcifications Java EE

La matrise de tous les standards des services web exige de passer un peu de temps lire tout un lot de spcifications provenant du W3C, du JCP et dOASIS.

470

Java EE 6 et GlassFish 3

Le W3C est un consortium qui dveloppe et gre les technologies web comme HTML, XHTML, RDF, CSS, etc. Il nous intresse ici car il soccupe galement des technologies des services web: XML, XML Schema, SOAP et WSDL. OASIS hberge plusieurs standards lis aux services web, comme UDDI, WS-Addressing, WS-Security, WS-Reliability et bien dautres. Si lon revient Java, le JCP propose un ensemble de spcifications faisant partie de Java EE 6 et Java SE 6 notamment JAX-WS (JSR 224), Web Services 1.2 (JSR109), JAXB2.2 (JSR222), Web Services Metadata2.0 (JSR181) et JAXR1.0 (JSR93). Mises ensemble, ces spcifications sont gnralement dsignes informellement par le terme JWS (Java Web Services). Au premier abord, ces listes de spcifications pourraient vous faire croire que lcriture dun service web en Java est un exercice compliqu, notamment lorsquil sagit dutiliser toutes ces API. En ralit, vous navez pas vous soucier de toutes ces technologies sous-jacentes (XML, WSDL, SOAP, HTTP, etc.) car JWS sen occupera pour vous.
JAX-WS 2.2

JAX-WS (JSR 224) est le nouveau nom de JAX-RPC, qui a t lagu de Java EE6, ce qui signifie que lon a propos sa suppression de Java EE7. JAX-WS 2.2 dfinit un ensemble dAPI et dannotations permettant de construire et de consommer des services web en Java. Elle fournit les outils pour envoyer et recevoir des requtes de services web via SOAP en masquant la complexit du protocole. Ni le consommateur ni le service nont donc besoin de produire ou danalyser des messages SOAP car JAX-WS soccupe du traitement de bas niveau. JAX-WS dpend dautres spcifications comme JAXB (Java Architecture for XML Binding).
Web Services 1.2

La JSR 109 ("Implementing Enterprise Web Services") dfinit le modle de programmation et le comportement dexcution des services web dans le conteneur JavaEE. Elle dfinit galement lassemblage permettant dassurer la portabilit des services web entre les diffrentes implmentations des serveurs dapplications.
JAXB 2.2

Les services web envoient des requtes et des rponses en changeant des messages XML. En Java, il existe plusieurs API de bas niveau pour traiter les documents XML

Chapitre 14

Services web SOAP 471

et les schmas XML. La spcification JAXB fournit un ensemble dAPI et dannotations pour reprsenter les documents XML comme des artfacts Java, ce qui permet aux dveloppeurs de manipuler des objets Java reprsentant des documents XML. JAXB (JSR 222) facilite la dsrialisation des documents XML en objets et leur srialisation en documents XML. Mme si cette spcification peut tre utilise pour nimporte quel traitement XML, elle est fortement intgre JAX-WS.
WS-Metadata 2.0

Web Services Metadata (WS-Metadata, spcification JSR 181) fournit des annotations qui facilitent la dfinition et le dploiement des services web. Le but principal de la JSR181 consiste simplifier le dveloppement des services web: elle fournit des outils permettant dassocier WSDL avec les interfaces Java et vice versa au moyen dannotations. Ces dernires peuvent tre utilises avec des classes Java ou des EJB.
JAXR 1.0

La spcification JAXR (Java API for XML Registries) dfinit un ensemble standard dAPI permettant aux clients Java daccder UDDI. Comme JAX-RPC, JAXR est une spcification lague, dont la suppression a t propose pour la prochaine version de JavaEE. Si cette suppression est accepte, JAXR continuera dvoluer, mais en dehors de JavaEE.
Implmentation de rfrence

Metro est non pas une spcification JavaEE mais une implmentation de rfrence open-source des spcifications des services web Java. Elle est forme de JAX-WS et de JAXB et reconnat galement les API JAX-RPC historiques. Metro permet de crer et de dployer des services web et des consommateurs scuriss, fiables, transactionnels et interoprables. Bien que la pile Metro soit produite par la communaut GlassFish, elle peut tre utilise en dehors de celui-ci, dans un environnement JavaEE ou JavaSE.

Appel dun service web


Malgr tous ces concepts, spcifications, standards et organisations, lcriture et la consommation dun service web sont trs simples. Le Listing 14.1, par exemple, prsente le code dun service web qui valide une carte de crdit.

472

Java EE 6 et GlassFish 3

Listing14.1: Le service web CardValidator


@WebService public class CardValidator { public boolean validate(CreditCard creditCard) { String lastDigit = creditCard.getNumber().substring( creditCard.getNumber().length() - 1, creditCard.getNumber().length()); if (Integer.parseInt(lastDigit) % 2 != 0) { return true; } else { return false; } } }

Comme les entits ou les EJB, un service web utilise le modle de POJO annot avec une politique de configuration par exception. Si tous les choix par dfaut vous conviennent, ceci signifie quun service web peut se rduire une simple classe Java annote par @javax.jws.WebService. Le service CardValidator na quune seule mthode pour valider une carte de crdit:validate() prend une carte de crdit en paramtre et renvoie true ou false selon quelle est valide ou non. Ici, nous supposons que les cartes de crdit ayant un numro impair sont valides et que celles avec un numro pair ne le sont pas. Un objet CreditCard (voir Listing14.2) est chang entre le consommateur et le service web. Lorsque nous avons dcrit larchitecture dun service web, nous avons vu que les donnes changes devaient tre des documents XML: on a donc besoin dune mthode pour transformer un objet Java en XML et cest l que JAXB entre en jeu avec ses annotations et son API. Lobjet CreditCard doit simplement tre annot par @javax.xml.bind.annotation.XmlRootElement pour que JAXB le transforme en XML et rciproquement.
Listing14.2: La classe CreditCard avec une annotation JAXB
@XmlRootElement public class CreditCard { private private private private String number; String expiryDate; Integer controlNumber; String type;

// Constructeurs, getters, setters }

Chapitre 14

Services web SOAP 473

Grce aux annotations JAXB, il nest pas ncessaire dcrire de code de bas niveau pour effectuer lanalyse XML car elle a lieu en coulisse le service web et le consommateur manipulent un objet Java. Le consommateur peut tre une classe Java qui cre une instance de CreditCard puis invoque le service web, comme dans le Listing14.3.
Listing14.3: Un consommateur invoque le service web
public class Main { public static void main(String[] args) { CreditCard creditCard = new CreditCard(); creditCard.setNumber("12341234"); creditCard.setExpiryDate("10/10"); creditCard.setType("VISA"); creditCard.setControlNumber(1234); CardValidator cardValidator = new CardValidatorService().getCardValidatorPort(); cardValidator.validate(creditCard); } }

Le consommateur ninvoque pas directement le service CardValidator: il utilise une classe CardValidatorService et appelle la mthode getCardValidatorPort() pour obtenir une rfrence un CardValidator. Grce elle, il peut ensuite appeler la mthode validate() en lui passant la carte de crdit tester. Bien que ce code soit trs simple comprendre, beaucoup de choses se passent en arrire-plan. Pour que tout ceci fonctionne, plusieurs artfacts ont t produits: un fichier WSDL et des stubs clients qui contiennent toutes les informations pour se connecter lURL du service web, la srialisation de lobjet CreditCard en XML, lappel du service web et la rcupration du rsultat. La partie visible des services web en Java ne manipule pas directement XML, SOAP ou WSDL et est donc trs simple comprendre. Cependant, certaines parties invisibles sont trs importantes pour linteroprabilit.

JAXB: Java Architecture for XML Binding


Comme vous lavez compris, XML est utilis pour changer les donnes et dfinir les services web via WSDL et les enveloppes SOAP. Pourtant, dans le Listing14.3,

474

Java EE 6 et GlassFish 3

un consommateur invoquait un service web sans quil ny ait trace de XML car ce consommateur ne manipulait que des interfaces et des objets Java distants qui, leur tour, grent toute la plomberie XML et les connexions rseau. On manipule des classes Java un endroit de la chane et des documents XML un autre le rle de JAXB est de faciliter cette correspondance bidirectionnelle. Java fournit plusieurs moyens de manipuler du XML, qui vont des API classiques (javax.xml.stream.XmlStreamWriter et java.beans.XMLEncoder) des modles de bas niveau plus complexes comme SAX (Simple API for XML), DOM (Document Object Model) ou JAXP (Java API for XML Processing). JAXB, qui repose sur les annotations, offre un niveau dabstraction suprieur celui de SAX et DOM. JAXB dfinit un standard permettant de lier les reprsentations Java XML et rciproquement. Il gre les documents XML et les dfinitions des schmas XML (XSD) de faon transparente et oriente objet, qui masque la complexit du langage XSD. part lannotation @XmlRootElement, le code du Listing14.4 est celui dune classe Java normale. Grce cette annotation et un mcanisme de srialisation. JAXB est capable de crer une reprsentation XML dune instance de CreditCard, comme celle qui est prsente dans le Listing14.5.
Listing14.4: La classe CreditCard avec une annotation JAXB
@XmlRootElement public class CreditCard { private private private private String number; String expiryDate; Integer controlNumber; String type;

// Constructeurs, getters, setters }

Listing14.5: Document XML reprsentant les donnes dune carte de crdit


<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <creditCard> <controlNumber>6398</controlNumber> <expiryDate>12/09</expiryDate> <number>1234</number> <type>Visa</type> </creditCard>

La srialisation, ici, consiste transformer un objet en XML, mais JAXB permet galement de faire linverse: la dsrialisation prendrait ce document XML en entre et

Chapitre 14

Services web SOAP 475

crerait un objet CreditCard partir des valeurs de ce document. JAXB peut aussi produire automatiquement le schma qui validerait automatiquement la structure XML de la carte de crdit afin de garantir quelle est correcte et que les types des donnes conviennent. Le Listing14.6 montre la dfinition du schma XML (XSD) de la classe CreditCard.
Listing14.6: Schma XML validant le document XML prcdent
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="creditCard type="creditCard"/> <xs:complexType name="creditCard"> <xs:sequence> <xs:element name="controlNumber" type="xs:int" minOccurs="0"/> <xs:element name="expiryDate" type="xs:string" minOccurs="0"/> <xs:element name="number" type="xs:string" minOccurs="0"/> <xs:element name="type" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:schema>

Ce schma est constitu dlments simples (controlNumber, expiryDate, etc.) et dun type complexe (creditCard). Les types complexes modlisent le contenu de llment: ils dterminent lensemble des lments utiliss dans un document (une carte de crdit, ici). Vous remarquerez que tous les marqueurs sont prfixs par xs (xs:element, xs:string, etc.): ce prfixe est un espace de noms et est dfini dans llment xmlns (XML namespace) du marqueur den-tte du document.
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

Les espaces de noms crent des prfixes uniques pour les lments des documents ou des applications qui sont utiliss ensemble. Leur but principal consiste viter les conflits qui pourraient survenir lorsquun mme nom dlment apparat dans plusieurs documents (le marqueur <element>, par exemple, pourrait apparatre dans plusieurs documents et avoir des significations diffrentes). Ceci pose un problme non ngligeable pour les services web car ils manipulent plusieurs documents en mme temps (lenveloppe SOAP, le document WSDL, etc.): les espaces de noms sont donc trs importants pour les services web.

476

Java EE 6 et GlassFish 3

Liaison

LAPI JAXB, dfinie dans le paquetage javax.xml.bind, fournit un ensemble dinterfaces et de classes permettant de produire des documents XML et des classes Java en dautres termes, elle relie les deux modles. Le framework dexcution de JAXB implmente les oprations de srialisation et de dsrialisation. La srialisation (ou marshalling) consiste convertir les instances des classes annotes par JAXB en reprsentations XML. Inversement, la dsrialisation (unmarshalling) consiste convertir une reprsentation XML en arborescence dobjets. Les donnes XML srialises peuvent tre valides par un schma XML JAXB peut produire automatiquement ce schma partir dun ensemble de classes et vice versa. La Figure14.3 montre les interactions possibles entre une application et JAXB.
Figure14.3 Architecture JAXB.
Schma XML
<xs:schema> <xs:element/> <xs:complexType> <xs:element> ... </xs:sequence> </xs:complexType </xs:schema>

Classes Compilateur de schmas xjc Gnrateur de schmas schemagen

respecte

instancie Framework d'excution

<creditCard> <controlNumber> <expiryDate> <number> <type>VISA</type> </creditCard>

Dsrialisation ( +validation) Srialisation (+validation)

Document XML

Objets

Le cur de lAPI JAXB est la classe javax.xml.bind.JAXBContext. Cette classe abstraite gre la liaison entre les documents XML et les objets Java. Elle fournit:

une classe Unmarshaller qui transforme un document XML en graphe dobjets et, ventuellement, valide le code XML; une classe Marshaller qui transforme un graphe dobjets en document XML.

Pour transformer un objet CreditCard en document XML, par exemple, le Listing14.4 utilise la mthode Marshaller.marshal(). Cette mthode prend un objet

Chapitre 14

Services web SOAP 477

en paramtre et le srialise sur diffrents supports (StringWriter pour une reprsentation du document XML sous forme de chane ou FileOutputStream pour le stocker dans un fichier). Le code du Listing14.7 cre une instance de JAXBContext laide de sa mthode statique newInstance(), laquelle il passe la classe racine qui doit tre srialise (CreditCard.class). Puis il appelle la mthode marshal() de lobjet Marshaller pour produire une reprsentation XML (celle du Listing14.5) de lobjet carte de crdit dans un StringWriter afin de lafficher. On pourrait utiliser la mme approche pour dsrialiser un document XML en objets laide de la mthode Unmarshaller. unmarshal().
Listing14.7: Classe srialisant un objet CreditCard
public class Main { public static void main(String[] args) { CreditCard creditCard = new CreditCard("1234", "12/09", 6398, "Visa"); StringWriter writer = new StringWriter(); JAXBContext context = JAXBContext.newInstance(CreditCard.class); Marshaller m = context.createMarshaller(); m.marshal(creditCard, writer); System.out.println(writer.toString()); } }

JAXB fournit galement un compilateur de schmas (xjc) et un gnrateur de schmas (schemaGen) alors que la srialisation/dsrialisation manipule des objets et des documents XML, ce compilateur et ce gnrateur de schmas manipulent des classes et des schmas XML. Ces outils peuvent tre utiliss en ligne de commande (ils sont fournis avec Java SE6) ou comme buts Maven. Avec JAXB, les liaisons peuvent suivre deux scnarios:

Partir des classes Java. Les classes existent et servent produire un schma XML. Partir dun schma XML. Le schma existe et les classes Java sont cres laide dun compilateur de schmas.

478

Java EE 6 et GlassFish 3

Ces deux scnarios sont trs importants pour les services web un service web peut produire ses fichiers WSDL, en fonction desquels un consommateur pourra produire un ensemble de classes Java de faon transparente et portable.
Annotations

Par bien des aspects, JAXB ressemble JPA. Cependant, au lieu de faire correspondre les objets une base de donnes, JAXB les lie un document XML. Comme JPA, JAXB dfinit un certain nombre dannotations (dans le paquetage javax.xml.bind. annotation) afin de configurer cette association et sappuie sur la configuration par exception pour allger le travail du dveloppeur. Comme le montre le Listing14.8, lquivalent de @Entity des objets persistants est lannotation @XmlRootElement de JAXB.
Listing14.8: Une classe CreditCard personnalise
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class CreditCard { @XmlAttribute (required = true) private String number; @XmlElement(name = "expiry-date", defaultValue = "01/10") private String expiryDate; private String type; @XmlElement(name = "control-number") private Integer controlNumber; // Constructeurs, getters, setters }

Lannotation @XmlRootElement prvient JAXB que la classe CreditCard (prsente plus haut dans le Listing 14.4) est llment racine du document XML. Si cette annotation est absente, JAXB lancera une exception lorsquil tentera de srialiser la classe. Cette dernire est ensuite associe au schma du Listing14.6 en utilisant les associations par dfaut de JAXB (chaque attribut est traduit en un lment de mme nom). Avec un objet Marshaller, on obtient aisment une reprsentation XML dun objet (voir Listing14.5). Llment racine <creditCard> reprsente lobjet CreditCard et inclut la valeur de chaque attribut.
CreditCard

JAXB permet de personnaliser et de contrler cette structure XML. Un document XML tant compos dlments (<element>valeur</element>) et dattributs

Chapitre 14

Services web SOAP 479

(<element attribute="valeur"/>), JAXB utilise deux annotations pour les diffrencier : @XmlAttribute et @XmlElement. Chacune delles reconnat un certain nombre de paramtres permettant de renommer un attribut, dautoriser ou non les valeurs null, dattribuer des valeurs par dfaut, etc. Le Listing14.8 les utilise pour transformer le numro de carte en attribut (au lieu dun lment) et pour renommer la date dexpiration et le code de contrle. Cette classe sera lie un schma diffrent dans lequel le numro de carte est un <xs:attribute> obligatoire et la date dexpiration renomme a une valeur par dfaut de 01/10 (voir Listing14.9).
Listing14.9: Schma de carte de crdit avec des attributs et des valeurs par dfaut
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="creditCard" type="creditCard"/> <xs:complexType name="creditCard"> <xs:sequence> <xs:element name="expiry-date" type="xs:string" default="01/10" minOccurs="0"/> <xs:element name="type" type="xs:string" minOccurs="0"/> <xs:element name="control-number" type="xs:int" minOccurs="0"/> <xs:sequence> <xs:attribute name="number" type="xs:string" use="required"/> </xs:complexType> </xs:schema>

Comme le montre le Listing14.10, la reprsentation XML sera galement modifie.


Listing14.10: Document XML reprsentant un objet CreditCard personnalis
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <creditCard number="1234"> <expiry-date>12/09</expiry-date> <type>Visa</type> <control-number>6398</control-number> </creditCard>

Le Tableau14.1 numre les principales annotations de JAXB; la plupart avec les lments auxquels elles sappliquent; certaines peuvent annoter des attributs (getters), dautres, des classes et certaines peuvent sappliquer tout un paquetage (@Xml Schema, par exemple).

480

Java EE 6 et GlassFish 3

Tableau14.1: Annotations JAXB

Annotation
@XmlAccessorType @XmlAttribute @XmlElement @XmlElements @XmlEnum @XmlEnumValue @XmlID

Description Contrle si les attributs ou les getters doivent tre mis en correspondance (FIELD, NONE, PROPERTY, PUBLIC_MEMBER). Traduit un attribut ou un getter en attribut XML de type simple (String, Boolean, Integer, etc.). Traduit un attribut ou un getter non statique et non transitoire en lment XML. Agit comme un conteneur de plusieurs annotations @XmlElement. Traduit une numration en XML. Identifie une constante dnumration. Identifie le champ cl dun lment XML (de type String) qui pourra servir dsigner un lment avec lannotation @XmlIDREF (concepts dID et dIDREF des schmas XML). Traduit une proprit en IDREF XML dans le schma. Traduit une proprit en liste. Identifie une reprsentation textuelle du type MIME dune proprit. Identifie un espace de noms XML. Annotation exige pour toute classe lie llment racine du document XML. Traduit un nom de paquetage en espace de noms XML. Demande JAXB de ne pas lier un attribut (analogue au mot-cl transient de Java ou lannotation @Transient de JPA). Marque une classe comme tant un type complexe dans le schma XML. Permet de traduire une classe en un contenu ou un type de schma simple.

@XmlIDREF @XmlList @XmlMimeType @XmlNs @XmlRootElement @XmlSchema @XmlTransient @XmlType @XmlValue

Grce ces annotations, vous pouvez traduire des objets en un schma XML spcifique, ce qui est parfois ncessaire avec les services web existants. JPA dfinit un ensemble dannotations permettant dadapter chaque partie de la traduction (colonnes, tables, cls trangres, etc.) pour pouvoir associer des entits une base de donnes dj tablie, et il en va de mme avec les services web qui sont dcrits par un fichier WSDL crit en XML: sil sagit dun service existant, son WSDL ne

Chapitre 14

Services web SOAP 481

peut pas tre modifi les annotations JAXB permettent alors de le faire correspondre des objets.
INFO Dans cette section, nous avons plusieurs fois mentionn JPA car les technologies JPA et JAXB reposent fortement sur les annotations et permettent de traduire des objets vers diffrents supports (bases de donnes ou XML). En termes darchitecture, les entits ne devraient servir qu associer des donnes une base de donnes et les classes JAXB, traduire des donnes en XML. Parfois, cependant, le mme objet a besoin davoir une reprsentation sous forme de base de donnes et sous forme XML: il est alors techniquement possible dannoter la mme classe avec @Entity et @XmlRootElement, mme si cela nest pas vraiment conseill.

La partie immerge de liceberg


Mme si lon ne manipule pas explicitement des documents SOAP et WSDL lorsque lon dveloppe avec JAW-WS, il est important de comprendre un peu leur structure. Les services web fournissent deux ingrdients essentiels: un langage de dfinition dinterface (WSDL) et un standard de messages (SOAP). Lorsque lon dveloppe des services web, on peut se servir de WSDL pour dfinir leur interface, cest--dire dfinir les paramtres dentre et de sortie du service en termes de schma XML. Les messages SOAP, quant eux, permettent de transporter les paramtres dentre et de sortie prciss par le document WSDL. Lorsquun consommateur voulant invoquer le service web CardValidator (voir Figure14.4) rcupre son WSDL pour connatre son interface, il demande valider la carte (le message validate de SOAP) et reoit une rponse (le message validateResponse de SOAP).
Figure14.4 Consommateur invoquant un service web.
JVM Cliente
SOAP / HTTP <<component>> Consommateur validate validateResponse

Conteneur de service web


WSDL
<<component>> Service web

WSDL

Les documents WSDL sont hbergs dans le conteneur de service web et utilisent XML pour dcrire ce que fait un service, comment appeler ses oprations et o le

482

Java EE 6 et GlassFish 3

trouver. Ce document XML respecte une structure bien tablie, forme de plusieurs parties (voir Listing14.11). Le service web CardValidator, par exemple, utilise les lments suivants:
<definitions> <types>

est llment racine de WSDL. Il prcise les dclarations des espaces de noms visibles dans tout le document.

dfinit les types de donnes utiliss par les messages. Ici, cest la dfinition du schma XML (CardValidatorService?xsd=1) qui dcrit le type des paramtres de la requte adresse au service (un objet CreditCard) et le type de la rponse (un Boolean).

<message>

dfinit le format des donnes changes entre un consommateur du service web et le service lui-mme. Ici, il sagit de la requte (la mthode validate) et de la rponse (validateResponse). prcise les oprations du service (la mthode validate). dcrit le protocole concret (SOAP, ici) et les formats des donnes pour les oprations et les messages dfinis pour un type de port particulier.
<port>

<portType> <binding> <service>

contient une collection dlments extrmit (une adresse rseau ou une URL).

associs, chacun, une

Le fichier WSDL du Listing14.11 vous aidera mieux comprendre les informations dcrites dans le service web CardValidator.
Listing14.11: Fichier WSDL pour le service web CardValidator
<definitions targetNamespace="http://chapter14.javaee6.org/" name="CardValidatorService"> <types> <xsd:schema> <xsd:import namespace="http://chapter14.javaee6.org/" schemaLocation= "http://localhost:8080/chapter14/CardValidatorService?xsd=1"/> </xsd:schema> </types> <message name="validate"> <part name="parameters" element="tns:validate"/> </message> <message name="validateResponse"> <part name="parameters" element="tns:validateResponse"/> </message> <portType name="CardValidator"> <operation name="validate"> <input message="tns:validate"/>

Chapitre 14

Services web SOAP 483

<output message="tns:validateResponse"/> </operation> </portType> <binding name="CardValidatorPortBinding" type="tns:CardValidator"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="validate"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="CardValidatorService"> <port name="CardValidatorPort" binding="tns:CardValidatorPortBinding"> <soap:address location = "http://localhost:8080/chapter14/CardValidatorService"/> </port> </service> </definitions>

Llment <xsd:import namespace> fait rfrence un schma XML qui doit tre disponible sur le rseau pour les clients du WSDL. Le Listing14.12 montre que ce schma dfinit les types utiliss par le service web (structure dun objet CreditCard avec un numro, une date dexpiration, etc.).
Listing14.12: Schma import par le fichier WSDL
<xs:schema version="1.0" targetNamespace="http://chapter14.javaee6.org/"> <xs:element name="creditCard" type="tns:creditCard"/> <xs:element name="validate" type="tns:validate"/> <xs:element name="validateResponse" type="tns:validateResponse"/> <xs:complexType name="validate"> <xs:sequence> <xs:element name="arg0" type="tns:creditCard" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="creditCard"> <xs:sequence> <xs:element name="controlNumber" type="xs:int" minOccurs="0"/> <xs:element name="expiryDate" type="xs:string" minOccurs="0"/> <xs:element name="number" type="xs:string" minOccurs="0"/> <xs:element name="type" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType>

484

Java EE 6 et GlassFish 3

<xs:complexType name="validateResponse"> <xs:sequence> <xs:element name="return" type="xs:boolean"/> </xs:sequence> </xs:complexType> </xs:schema>

Ce fichier WSDL et ce schma sont gnralement produits par des outils fournis avec JAX-WS qui transforment les mtadonnes en XML.
SOAP

Alors que WSDL dcrit une interface abstraite du service web, SOAP fournit une implmentation concrte en dfinissant la structure XML des messages changs. Dans le cadre du Web, SOAP est une structure de messages pouvant tre dlivrs par HTTP (ou dautres protocoles de communication) la liaison HTTP de SOAP contient quelques en-ttes dextension HTTP standard. Cette structure de message est dcrite en XML. Au lieu dutiliser HTTP pour demander une page web partir dun navigateur, SOAP envoie un message XML via une requte HTTP et reoit une rponse HTTP. Un message SOAP est un document XML contenant les lments suivants:
<Envelope>. <Header>. <Body>.

Dfinit le message et lespace de noms utiliss dans le document. Ilsagit de llment racine obligatoire.

Contient les attributs facultatifs du message ou linfrastructure spcifique lapplication, comme les informations sur la scurit ou le routage rseau. Contient le message chang entre les applications. Fournit des informations sur les erreurs qui surviennent au cours du traitement du message. Cet lment est facultatif.

<Fault>.

Seuls lenveloppe et le corps sont obligatoires. Dans notre exemple, une application cliente appelle le service web pour valider une carte de crdit (une enveloppe SOAP pour la requte) et reoit un boolen indiquant si cette carte est valide ou non (une autre enveloppe SOAP pour la rponse). Les Listings 14.13 et 14.14 montrent les structures de ces deux messages SOAP.
Listing14.13: Enveloppe SOAP de la requte
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cc="http://chapter14.javaee6.org/"> <soap:Header/>

Chapitre 14

Services web SOAP 485

<soap:Body> <cc:validate> <arg0> <controlNumber>1234</controlNumber> <expiryDate>10/10</expiryDate> <number>9999</number> <type>VISA</type> </arg0> </cc:validate> </soap:Body> </soap:Envelope>

Listing14.14: Enveloppe SOAP de la rponse


<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:validateResponse xmlns:ns2="http://chapter14.javaee6.org/"> <return>true</return> </ns2:validateResponse> </soap:Body> </soap:Envelope>

JAX-WS: Java API for XML-Based Web Services


Nous venons de voir un document WSDL simple, ainsi quune requte et une rponse SOAP. Lorsque les services web proposent plusieurs oprations avec des paramtres complexes, ces documents XML deviennent un vritable cauchemar pour le dveloppeur; heureusement, JAX-WS leur facilite la vie en masquant cette complexit. Ceci dit, vous devrez hlas parfois vous plonger dans la structure WSDL. Le document WSDL tant le contrat qui lie le consommateur et le service, il peut servir crire le code Java pour ces deux parties: cest ce que lon appelle la mthode descendante, galement mthode par contrat car elle part du contrat (le WSDL) en dfinissant les oprations, les messages, etc. Lorsque le consommateur et le fournisseur sont daccord sur le contrat, on peut implmenter les classes Java en fonction de celui-ci. Metro fournit quelques outils permettant de produire des classes partir dun document WSDL. Dans lautre approche, la mthode ascendante, la classe de limplmentation existe dj et il suffit de crer le WSDL. L encore, Metro dispose doutils pour effectuer cette opration. Dans les deux cas, le code doit parfois tre ajust pour correspondre au WSDL ou vice versa, et cest l que JAX-WS vient votre secours: grce un

486

Java EE 6 et GlassFish 3

modle de dveloppement simple et quelques annotations, vous pouvez ajuster lassociation Java-WSDL. Lapproche ascendante peut produire des applications trs inefficaces car les mthodes et les classes Java nont aucune ide de la granularit idale des messages qui circulent sur le rseau. Si la latence est leve et/ou la bande passante, faible, il est prfrable dutiliser moins de messages, qui seront plus gros: seule lapproche par contrat permet de faire ce choix. Ce chapitre a prsent les concepts et les spcifications des services web en gnral puis a introduit JAXB et a effleur la surface des documents WSDL et SOAP. Cependant, les services web suivent le paradigme de facilit de dveloppement de Java EE6 et nobligent pas crire le moindre code WSDL ou SOAP. Un service web est simplement un POJO annot qui doit tre dploy dans un conteneur de service web. Toutefois, ce modle de programmation mrite notre attention.
Le modle JAX-WS

Comme la plupart des composants de Java EE 6, les services web sappuient sur le paradigme de la configuration par exception. Seule lannotation @WebService est ncessaire pour transformer un POJO en service web, mais cette classe doit respecter les rgles suivantes:

Elle doit tre annote par @javax.jws.WebService ou son quivalent XML dans un descripteur de dploiement. Pour transformer un service web en entit EJB, la classe doit tre annote par @javax.ejb.Stateless (voir Chapitre7). Elle doit tre publique et ne doit pas tre finale ni abstraite. Elle doit possder un constructeur par dfaut public. Elle ne doit pas dfinir la mthode finalize(). Un service doit tre un objet sans tat et ne pas mmoriser ltat spcifique dun client entre les appels de mthode.

La spcification WS-Metadata (JSR 181) nonce que, tant quil respecte ces rgles, un POJO peut tre utilis pour implmenter un service web dploy dans le conteneur de servlet il est alors souvent dsign sous le terme dextrmit de servlet (Servlet end point). Un bean de session sans tat peut galement servir implmenter un service web qui sera dploy dans un conteneur EJB il est alors appel extrmit EJB (EJB end point).

Chapitre 14

Services web SOAP 487

Extrmits dun service web

JAX-WS 2.2 permet de transformer des classes Java classiques et des EJB sans tat en services web. Les codes dun service web POJO (voir Listing14.1) et dun service web EJB (voir Listing14.18) sont peu diffrents: la seule diffrence est que le second a une annotation @Stateless supplmentaire. En outre, lassemblage est diffrent: un service web POJO est assembl dans un module web (un fichier war) et est appel extrmit de servlet, tandis quun service web EJB est assembl dans un fichier jar et est appel extrmit EJB. Le premier est dploy dans un conteneur de servlet, le second, dans un conteneur EJB. Ces deux extrmits ont un comportement quasiment identique, mais les extrmits EJB ont quelques avantages supplmentaires car, le service web tant en ce cas galement un EJB, il bnficie automatiquement des transactions et de la scurit qui sont gres par le conteneur. En outre, il peut utiliser des intercepteurs, ce qui nest pas possible avec les extrmits de servlets. Le code mtier peut tre expos simultanment comme un service web et comme un EJB, ce qui signifie quil peut tre expos la fois via SOAP et RMI en ajoutant une interface distante.
Annotations

Au niveau du service, les systmes sont dfinis en termes de messages XML, doprations WSDL et de messages SOAP. Cependant, au niveau Java, les applications sont dcrites en termes dobjets, dinterfaces et de mthodes. Il est donc ncessaire deffectuer une traduction des objets Java vers les oprations WSDL. Lenvironnement dexcution de JAXB utilise les annotations pour savoir comment srialiser/ dsrialiser une classe vers/ partir de XML. Gnralement, ces annotations sont caches au dveloppeur du service web. De mme, JWS se sert dannotations pour dterminer comment srialiser un appel de mthode vers un message de requte SOAP et comment dsrialiser une rponse SOAP vers une instance du type du rsultat de la mthode. La spcification WS-Metadata (JSR 181) dfinit deux sortes dannotations:

Les annotations de traduction WSDL. Elles appartiennent au paquetage javax.jws et permettent de modifier les associations entre WSDL et Java. Les annotations @WebMethod, @WebResult, @WebParam et @OneWay sont utilises sur le service web pour adapter la signature des mthodes exposes. Les annotations de traduction SOAP. Elles appartiennent au paquetage javax. jws.soap et permettent dadapter la liaison SOAP (@SOAPBinding et @SOAP MessageHandler).

488

Java EE 6 et GlassFish 3

Comme toutes les autres spcifications de Java EE 6, ces annotations peuvent tre redfinies par un descripteur de dploiement XML facultatif (webservices.xml). Les sections qui suivent dcrivent plus prcisment chacune delles.
@WebService

Lannotation @javax.jws.WebService marque une classe ou une interface Java comme tant un service web. Lorsquelle est utilise directement sur la classe (comme dans les exemples que nous avons prsents jusqu maintenant), le processeur dannotations du conteneur produira linterface les deux extraits de code qui suivent sont donc quivalents. Voici lannotation sur une classe:
@WebService public class CardValidator {

Voici lannotation sur une interface implmente par une classe:


@WebService public interface CCValidator { public class CardValidator implements CCValidator {

Cette annotation possde un certain nombre dattributs (voir Listing 14.15) permettant de personnaliser le nom du service web dans le fichier WSDL (lments <wsdl:portType> ou <wsdl:service>) et son espace de noms, ainsi que lemplacement du fichier WSDL lui-mme (attribut wsdlLocation).
Listing14.15: API de @WebService
@Retention(RUNTIME) @Target(TYPE) public @interface WebService { String name() default ""; String targetNamespace() default ""; String serviceName() default ""; String portName() default ""; String wsdlLocation() default ""; String endpointInterface() default ""; }

Lorsque lon utilise @WebService, toutes les mthodes publiques du service web qui nutilisent pas lannotation @WebMethod sont exposes.
@WebMethod

Par dfaut, toutes les mthodes publiques dun service web sont exposes dans le WSDL et utilisent toutes les rgles dassociation par dfaut. Lannotation @javax.jws.

Chapitre 14

Services web SOAP 489

permet de personnaliser certaines de ces associations de mthodes. Son API est assez simple et permet de renommer une mthode ou de lexclure du WSDL. Le Listing14.16, par exemple, montre comment le service web CardValidator peut renommer sa premire mthode en ValidateCreditCard et exclure la seconde.
WebMethod

Listing14.16: Renommage dune mthode et exclusion de lautre


@WebService public class CardValidator { @WebMethod(operationName = "ValidateCreditCard") public boolean validate(CreditCard creditCard) { // Logique mtier } @WebMethod(exclude = true) public void validate(String ccNumber) { // Logique mtier } }

@WebResult

Lannotation @javax.jws.WebResult fonctionne en relation avec @WebMethod pour contrler le nom de la valeur renvoye par le message dans le WSDL. Dans le Listing14.17, le rsultat de la mthode validate() est renomm en IsValid.
Listing14.17: Renommage du rsultat de la mthode
@WebService public class CardValidator { @WebMethod @WebResult(name = "IsValid") public boolean validate(CreditCard creditCard) { // Logique mtier } }

Cette annotation possde dautres lments permettant, par exemple, de personnaliser lespace de noms XML pour la valeur renvoye, un peu comme lannotation @WebParam.
@WebParam
@javax.jws.WebParam,

@WebResult

dont lAPI est prsente dans le Listing 14.18, ressemble car elle personnalise les paramtres des mthodes du service web.

490

Java EE 6 et GlassFish 3

CetteAPI permet de modifier le nom du paramtre dans le WSDL (voir Listing14.19), lespace de noms et le mode de passage des paramtres IN, OUT ou INOUT.
Listing14.18: API de @WebParam
@Retention(RUNTIME) @Target(PARAMETER) public @interface WebParam { String name() default ""; public enum Mode {IN, OUT, INOUT}; String targetNamespace() default ""; boolean header() default false; String partName() default ""; }

Listing14.19: Renommage du paramtre de la mthode


@WebService public class CardValidator { @WebMethod public boolean validate(@WebParam(name = "Credit-Card") // Logique mtier } }

@OneWay

Lannotation @OneWay peut tre utilise avec les mthodes qui ne renvoient aucun rsultat, comme celles de type void. Cette annotation na aucun lment et peut tre considre comme une interface de marquage informant le conteneur que lappel de cette mthode peut tre optimis (en utilisant un appel asynchrone, par exemple) puisquil ny a pas de valeur de retour.
Rcapitulatif des annotations

Pour mieux comprendre ces annotations, nous allons les utiliser avec le service web CardValidator (voir Listing 14.20) afin de montrer leur impact sur le document WSDL (voir Listing14.21) et le schma associ (voir Listing14.22). Nous avons soulign les diffrences du Listing 14.21 par rapport au WSDL original du Listing14.14 et celles du Listing14.22 par rapport au schma original du Listing14.15.
Listing14.20: Le service web CardValidator avec des annotations
@WebService(name = "CreditCardValidator", portName = "ValidatorPort") public class CardValidator {

Chapitre 14

Services web SOAP 491

@WebMethod(operationName = "ValidateCreditCard") @WebResult(name = "IsValid") public boolean validate(@WebParam(name = "CreditCard") CreditCard creditCard) { String lastDigit = creditCard.getNumber().substring( creditCard.getNumber().length() - 1, creditCard.getNumber().length()); if (Integer.parseInt(lastDigit) % 2 != 0) { return true; } else { return false; } } @WebMethod(exclude = true) public void validate(String ccNumber) { // Logique mtier } }

Lannotation @WebService renomme les lments <portType name> et <port name> du WSDL. Toutes les autres annotations ont un impact sur la mthode, qui est reprsente par llment <message> du WSDL.
Listing14.21: Le document WSDL aprs personnalisation
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://chapter14.javaee6.org/" name="CardValidatorService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://chapter14.javaee6.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types> <xsd:schema> <xsd:import namespace="http://chapter14.javaee6.org/" schemaLocation="CardValidatorService_schema1.xsd"/> </xsd:schema> </types> <message name="ValidateCreditCard"> <part name="parameters" element="tns:ValidateCreditCard"/> </message> <message name="ValidateCreditCardResponse"> <part name="parameters" element="tns:ValidateCreditCardResponse"/> </message> <portType name="CreditCardValidator"> <operation name="ValidateCreditCard"> <input message="tns:ValidateCreditCard"/> <output message="tns:ValidateCreditCardResponse"/> </operation> </portType>

492

Java EE 6 et GlassFish 3

<binding name="ValidatorPortBinding" type="tns:CreditCardValidator"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="ValidateCreditCard"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="CardValidatorService"> <port name="ValidatorPort" binding="tns:ValidatorPortBinding"> <soap:address location="REPLACE_WITH_ACTUAL_URL"/> </port> </service> </definitions>

Le schma est galement personnalis car deux lments, la requte et la rponse, sont dfinis dans un lment <xs:complexType>. La valeur renvoye par la mthode sappelle IsValid et est de type Boolean.
Listing14.22: Le schma XML aprs personnalisation
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" targetNamespace="http://chapter14.javaee6.org/" xmlns:tns="http://chapter14.javaee6.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="ValidateCreditCard" type="tns:ValidateCreditCard"/> <xs:element name="ValidateCreditCardResponse" type="tns:ValidateCreditCardResponse"/> <xs:element name="creditCard" type="tns:creditCard"/> <xs:complexType name="ValidateCreditCard"> <xs:sequence> <xs:element name="CreditCard" type="tns:creditCard" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="creditCard"> <xs:sequence> <xs:element name="controlNumber" type="xs:int" minOccurs="0"/>

Chapitre 14

Services web SOAP 493

<xs:element name="expiryDate" type="xs:string" minOccurs="0"/> <xs:element name="number" type="xs:string" minOccurs="0"/> <xs:element name="type" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="ValidateCreditCardResponse"> <xs:sequence> <xs:element name="IsValid" type="xs:boolean"/> </xs:sequence> </xs:complexType> </xs:schema>

Cycle de vie et mthodes de rappel

Comme vous pouvez le constater avec la Figure14.5, le cycle de vie des services web ressemble celui des beans sans tat et des MDB. Comme avec tous les composants qui ne mmorisent pas ltat, soit ils nexistent pas, soit ils sont prts traiter une requte. Ce cycle de vie est gr par le conteneur.
Figure14.5 Cycle de vie des services web.
@PostConstruct Prt N'existe pas @PreDestroy

Appel de mthode

Comme elles sexcutent dans un conteneur, les extrmits de servlet et EJB autorisent linjection des dpendances et les mthodes de rappel du cycle de vie: si elle existe, le conteneur appellera la mthode de rappel @PostConstruct lorsquil cre une instance dun service web et la mthode de rappel @PreDestroy lorsquil la dtruit. Une diffrence entre les extrmits de servlet et les extrmits EJB est que ces dernires peuvent utiliser des intercepteurs, qui sont limplmentation JavaEE du concept de programmation oriente aspects (POA) dcrit au Chapitre8.
Contexte dun service web

Un service a un contexte denvironnement auquel il peut accder en injectant une rfrence javax.xml.ws.WebServiceContext au moyen dune annotation @

494

Java EE 6 et GlassFish 3

Dans ce contexte, le service peut obtenir des informations dexcution comme la classe qui implmente lextrmit, le contexte du message et des informations concernant la scurit relative la requte qui est traite.
@Resource private WebServiceContext context;

Resource.

Le Tableau 14.2 numre les mthodes dfinies dans linterface ws.WebServiceContext.


Tableau14.2: Mthodes de linterface WebServiceContext

javax.xml.

Mthode
getMessageContext

Description Renvoie le MessageContext pour la requte en cours de traitement au moment de lappel. Permet daccder aux en-ttes du message SOAP, etc. Renvoie le principal qui identifie lmetteur de la requte en cours de traitement. Teste si lauteur authentifi appartient au rle logique indiqu. Renvoie lEndpointReference associ cette extrmit.

getUserPrincipal isUserInRole getEndpointReference

Appel dun service web

Vous pouvez invoquer un service web en utilisant le fichier WSDL et certains outils de gnration de classes Java relais (stubs). Lappel dun service web ressemble lappel dun objet distribu avec RMI: comme ce dernier, JAX-WS permet au programmeur dutiliser un appel de mthode local pour invoquer un service se trouvant sur un autre hte. La diffrence est quun service web sur lhte distant peut tre crit dans un autre langage de programmation (notez cependant que vous pouvez aussi appeler du code non Java avec RMI-IIOP). Le fichier WSDL tablit le contrat entre le consommateur et le service, et Metro fournit un outil de conversion WSDL vers Java (wsimport) qui produit des classes et des interfaces Java partir du code WSDL ces interfaces sont appeles SEI (service endpoint interfaces) car ce sont des reprsentations Java dune extrmit de service web (servlet ou EJB). Cette SEI agit comme un proxy qui route lappel Java local vers le service web distant via HTTP ou dautres protocoles de transport. Lorsquune mthode de ce proxy est appele (voir Figure14.6), elle convertit ses paramtres en message SOAP (la requte) et lenvoie lextrmit du web service.

Chapitre 14

Services web SOAP 495

Pour obtenir le rsultat, la rponse SOAP est reconvertie en une instance du type renvoy. Pour lutiliser, il nest pas ncessaire de connatre le fonctionnement interne du proxy ni dtudier son code. Avant de compiler le consommateur client, il faut produire la SEI afin dobtenir la classe proxy pour lappeler dans le code.
JVM du client Conteneur de service web

<<component>> Consommateur

WSDL
SOAP / HTTP

<<component>> Service web

Java interface
<<component>> Proxy consommateur

Java interface
<<component>> Extrmit de service web

validate validateResponse

Figure14.6 Un consommateur appelle un service web via un proxy.

Le consommateur peut obtenir une instance du proxy par injection ou en la crant par programme. On injecte un client de service web laide de lannotation @javax. xml.ws.WebServiceRef, qui ressemble aux annotations @Resource ou @EJB prsentes aux chapitres prcdents. Lorsquelle est applique un attribut (ou une mthode getter), le conteneur injecte une instance du service web lorsque lapplication est initialise. Le code est de la forme suivante:
@WebServiceRef private CardValidatorService cardValidatorService; // ... CardValidator cardValidator = cardValidatorService.getCardValidatorPort(); cardValidator. validate(creditCard);

La classe CardValidatorService est la SEI, pas le service web lui-mme. Vous devez ensuite obtenir la classe proxy CardValidator pour invoquer localement les mthodes mtiers. On appelle localement la mthode validate() du proxy, qui, son tour, invoquera le service web distant, crera la requte SOAP, srialisera les messages, etc. Pour que cette injection fonctionne, ce code doit sexcuter dans un conteneur (de servlet, dEJB ou de client dapplication) : dans le cas contraire, on ne peut pas utiliser lannotation @WebServiceRef et il faut alors passer par la programmation.

496

Java EE 6 et GlassFish 3

Le lasses produites par loutil faon suivante:

wsimport

peuvent tre directement utilises de la

CardValidatorService cardValidatorService = new CardValidatorService(); CardValidator cardValidator = cardValidatorService.getCardValidatorPort(); cardValidato r.validate(creditCard);

Ce code cre une instance de reste est identique.


INFO

CardValidatorService

laide du mot-cl

new;

le

Les outils wsimport et wsgen sont fournis avec le JDK 1.6. wsimport prend en entre un fichier WSDL et produit les artfacts JAX-WS une SEI notamment. wsgen lit une classe extrmit de service web et produit le WSDL. Vous pouvez accder directement ces outils ou passer par linterface en ligne de commande de GlassFish, par une tche Ant ou par une extension Maven.

Rcapitulatif
Nous allons rassembler tous ces concepts en crivant un service web et un consommateur, puis en les dployant et en les testant avec GlassFish. Nous utiliserons pour cela les annotations JAXB et JAX-WS et produirons une SEI avec le but wsimport de Maven. Plusieurs tapes sont ncessaires pour crire un service web: nous les prsenterons en mme temps que le service CardValidator. Le service web CardValidator teste si une carte de crdit est valide. Il na quune seule mthode qui prend un objet CreditCard en paramtre, applique un algorithme et renvoie true si cette carte est valide, false sinon. Une fois que ce service a t dploy dans GlassFish, on utilise wsimport pour produire les artfacts ncessaires au consommateur. Celui-ci peut ensuite invoquer le service web pour valider des cartes de crdit. Nous utiliserons deux projets Maven: lun pour assembler le service web dans un fichier war (chapter14- service-1.0.war), lautre pour assembler le consommateur dans un fichier jar (chapter14-consumer- 1.0.jar).

Chapitre 14

Services web SOAP 497

La classe CreditCard

La classe CreditCard prsente dans le Listing14.23 est le POJO pass en paramtre la mthode validate() du service web. Les services web changeant des messages XML et non des objets Java, cette classe est annote par lannotation @XmlRootElement de JAXB afin dtre srialise en XML pour tre envoye dans une requte SOAP. Un objet CreditCard a quelques attributs de base comme le numro de la carte, la date dexpiration (au format MM/AA), le type de la carte (Visa, Master Card, American Express, etc.) et un code de contrle.
Listing14.23: La classe CreditCard avec une annotation JAXB
@XmlRootElement public class CreditCard { private private private private String number; String expiryDate; Integer controlNumber; String type;

// Constructeurs, getters, setters }

Le service web CardValidator


CardValidator (voir Listing14.24) est galement un POJO, mais annot par lannotation @WebService de JAX-WS. Ce nest pas une extrmit EJB car elle na pas dannotation @Stateless: cest donc une extrmit servlet qui sera dploye dans un fichier war (chapter14-service-1.0.war). Elle possde une mthode validate() qui prend un objet CreditCard en paramtre et dont lalgorithme teste si la carte est valide en fonction de son numro: les numros impairs sont considrs comme valides, les pairs, comme non valides. Cette mthode renvoie un boolen.

Listing14.24: Le service web CardValidator


@WebService public class CardValidator { public boolean validate(CreditCard creditCard) { String lastDigit = creditCard.getNumber().substring( creditCard.getNumber().length() - 1, creditCard.getNumber().length()); if (Integer.parseInt(lastDigit) % 2 != 0) {

498

Java EE 6 et GlassFish 3

return true; } else { return false; } }

Pour rester simple, nous nutilisons pas dautres associations Java vers WSDL: il ny a donc pas dannotation @WebMethod, @WebResult ou @WebParam, ce qui nous permet de constater la simplicit dcriture dun service web avec les rgles dassociation par dfaut.
Compilation et assemblage avec Maven

Le service web CardValidator doit tre compil et assembl dans un fichier war (<packaging>war</packaging>). Le fichier pom.xml du Listing 14.25 dclare la dpendance jaxws-rt pour utiliser la version2.2 de JAX-WS. Java SE6 est fourni avec une implmentation de JAXB et JAX-WS: prciser la version1.6 dans llment maven-compiler-plugin de Maven devrait suffire indiquer explicitement que vous voulez utiliser Java SE6 (<source>1.6</source>) mais, si vous voulez contrler les versions des dpendances et que vous exigez absolument la version2.2 de JAX-WS, il est prfrable dajouter explicitement la dpendance jaxws-rt.
Listing14.25: Fichier pom.xml pour compiler et assembler le service web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter14-service</artifactId> <version>1.0</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins>

Chapitre 14

Services web SOAP 499

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Les descripteurs de dploiement tant facultatifs avec Java EE6, nous navons pas besoin de fichier web.xml ou webservices.xml. Cependant, Maven nous obligeant quand mme ajouter un fichier web.xml dans un war, nous devons modifier lattribut failOnMissingWebXml de maven-war-plugin en le mettant false ; sinon Maven chouera lors de la compilation. Pour compiler et assembler le service web, il suffit douvrir une ligne de commande dans le rpertoire contenant le fichier pom.xml et dentrer la commande Maven suivante:
mvn package

Le rpertoire target devrait maintenant contenir un fichier chapter14-service1.0.war. Si nous louvrons, nous pouvons constater que les classes CardValidator. class et CreditCard.class se trouvent toutes les deux sous le rpertoire WEB-INF\ classes et que larchive ne contient rien dautre, pas mme un fichier WSDL.
Dploiement dans GlassFish

Lorsque le service web a t assembl dans le fichier war, il faut le dployer dans GlassFish. Ouvrez une fentre de commande, placez-vous dans le rpertoire target o se trouve le fichier chapter14-service-1.0.war, assurez-vous que GlassFish sexcute et lancez la commande suivante:
asadmin deploy chapter14-service-1.0.war

500

Java EE 6 et GlassFish 3

Si le dploiement a russi, la commande qui suit devrait renvoyer le nom des composants dploys et leurs types:
asadmin list-components chapter14-service-1.0 <web-module> chapter14-service-1.0#CardValidator <webservice>

Il est intressant de noter que GlassFish reconnat le module web (le fichier war aurait pu contenir des pages web, des servlets, etc.) comme tant un service web. Si vous utilisez la console dadministration de GlassFish prsente la Figure14.7 (http:// localhost:4848/), vous constaterez que chapter14-service-1.0 est dploy sous Applications -> Web Applications et que le service web CardValidator est dploy sous le nud Web Services.

Figure14.7 Services web dploys dans la console dadministration de GlassFish.

Si vous cliquez sur le lien View WSDL de cette page, une fentre du navigateur affichera lURL suivante, qui prsente le WSDL du service web (voir Figure14.8):
http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl

Il est intressant de noter que vous navez pas cr ce WSDL et que vous ne lavez pas non plus dploy dans le fichier war: cest la pile Metro qui le produit automatiquement en se servant des annotations contenues dans le service web.

Chapitre 14

Services web SOAP 501

Figure14.8 Le WSDL produit par Metro.

Le consommateur du service web

Le service web est dsormais en ligne et nous savons o trouver son WSDL. Grce lui et laide de loutil wsimport, le consommateur pourra produire les artfacts ncessaires lappel du service. Le Listing14.26 montre le code du consommateur.
Listing14.26: La classe Main invoque le service web laide dune injection
public class Main { @WebServiceRef private static CardValidatorService cardValidatorService; public static void main(String[] args) { CreditCard creditCard = new CreditCard(); creditCard.setNumber("12341234"); creditCard.setExpiryDate("10/10"); creditCard.setType("VISA");

502

Java EE 6 et GlassFish 3

creditCard.setControlNumber(1234); CardValidator cardValidator = cardValidatorService.getCardValidatorPort(); System.out.println(cardValidator.validate(creditCard)); } }

Cette classe Main cre une instance de CreditCard, initialise quelques donnes, obtient une rfrence au service web, invoque sa mthode validate() et affiche le rsultat (true ou false selon que la carte est valide ou non). Le point important est que le consommateur na aucune de ces classes: CardValidatorService, Card Validator et CreditCard lui sont totalement inconnues. Ce code ne pourra donc pas tre compil tant que ces classes nont pas t gnres.
Cration des artefacts du consommateur et assemblage avec Maven

Avant de compiler la classe Main du consommateur, nous devons crer les artfacts avec loutil wsimport. Heureusement, Maven possde un but wsimport qui est excut automatiquement au cours de la phase generate-sources du cycle de vie comme on la expliqu au Chapitre1, cette phase sert produire le code et sexcute avant la compilation. La seule chose faire consiste donc informer le but wsimport de lemplacement du document WSDL. Lorsque nous avons dploy le web service dans GlassFish et que nous avons demand laffichage du WSDL, nous avons pu constater que ce document se trouvait lURL suivante:
http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl

Le fichier pom.xml du Listing14.27 prcise galement les dpendances ncessaires, la version de jaxws-rt (2.2) et celle du JDK (1.6). La classe Main est assemble dans un fichier jar qui, comme tout fichier jar, contient un fichier META-INF\MANIFEST.MF : celui-ci peut servir dfinir des mtadonnes de larchive cest le but de lextension maven-jar : on ajoute un lment mainClass au MANIFEST, pointant vers la classe Main du consommateur. Cette information permettra lexcution du fichier jar.
Listing14.27: Le fichier pom.xml produit les artfacts et les paquetages pour le consommateur
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

Chapitre 14

Services web SOAP 503

<groupId>com.apress.javaee6</groupId> <artifactId>chapter14-consumer</artifactId> <packaging>jar</packaging> <version>1.0</version> <dependencies> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.2</version> <configuration> <archive> <manifest> <mainClass>com.apress.javaee6.chapter14.Main</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxws-maven-plugin</artifactId> <executions> <execution> <goals> <goal>wsimport</goal> </goals> <configuration> <wsdlUrls> <wsdlUrl> http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl </wsdlUrl> </wsdlUrls> <keep>true</keep> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited>

504

Java EE 6 et GlassFish 3

<configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Pour mieux comprendre ce qui se passe en coulisse, commenons par crer les artfacts laide de la commande Maven suivante:
mvn clean generate-sources

Celle-ci excute la phase generate-sources du cycle de vie de Maven et donc le but wsimport qui lui est attach. wsimport se connecte lURL du WSDL du service web, le tlcharge et produit les artfacts. Voici la sortie de cette commande:
[INFO] [clean:clean] [INFO] [jaxws:wsimport {execution: default}] [INFO] Processing: http://localhost:8080/chapter14-service-1.0/ CardValidatorService?wsdl [INFO] jaxws:wsimport args: [-s, D:\Chapter14-Consumer\target\ jaxws\wsimport\java, -d, D:\Chapter14-Consumer\target\ classes, -Xnocompile, http://localhost:8080/chapter14 service-1.0/CardValidatorService?wsdl] parsing WSDL... generating code... [INFO]---------------------------------------[INFO] BUILD SUCCESSFUL [INFO]----------------------------------------

Si vous tes curieux, vous pouvez vous rendre dans le rpertoire target\ jaxws\wsimport\java et vrifier les classes qui ont t produites. Vous y trouverez bien sr les classes CardValidator, CardValidatorService et CreditCard mais galement une classe pour la requte SOAP (Validate) et une autre pour la rponse (ValidateResponse). Ces classes sont remplies dannotations JAXB et JAXW car elles srialisent lobjet CreditCard et se connectent au service web distant. Ne vous souciez pas du code produit. Le fichier jar peut tre compil et assembl:
mvn package

Cette commande crera le fichier chapter14-consumer-1.0.jar contenant la classe Main que nous avons crite, plus toutes les autres classes que nous venons de produire. Ce jar est autonome et peut dsormais tre lanc pour invoquer le service web.

Chapitre 14

Services web SOAP 505

Excution de la classe Main

Noubliez pas que la classe Main du consommateur utilise lannotation @WebServiceRef pour obtenir une rfrence la SEI du web service par injection. Ceci signifie que le code doit tre excut dans le conteneur de client dapplication (ACC). Rappelez-vous galement que le fichier chapter14-consumer-1.0.jar est excutable car nous lui avons ajout un lment mainClass dans le fichier MANIFEST.MF. La seule chose faire consiste donc appeler loutil appclient de GlassFish en lui passant le fichier jar:
appclient -client chapter14-consumer-1.0.jar

Cette commande appellera le service web via HTTP et obtiendra une rponse indiquant si la carte de crdit est valide ou non.

Rsum
Les services web sont utiliss par lintgration B2B (business-to-business) et sont une technologie essentielle pour SOA. Amazon, eBay, Google, Yahoo! fournissent des services web leurs utilisateurs et de nombreuses socits les utilisent beaucoup en interne. Dans ce chapitre, nous avons prsent plusieurs standards (UDDI, WSDL, SOAP, XML, etc.) et nous nous sommes intresss aux spcifications de JavaEE qui les prennent en charge (JAX-WS, JAXB, WS-Metadata, etc.). JAXB (Java Architecture for XML Binding) dfinit un standard pour lier les reprsentations Java XML et vice versa. Elle fournit un haut niveau dabstraction car elle repose sur les annotations. Bien que JAXB puisse tre utilise avec nimporte quel type dapplication Java, elle sintgre particulirement bien dans lespace des services web car les informations changes sont crites en XML. Puis nous avons tudi WSDL et SOAP. Ces spcifications sont vitales pour les services web car elles dcrivent, respectivement, leurs interfaces et les messages changs. JAX-WS suit un modle de dveloppement simple et nutilise quun petit nombre dannotations pour ajuster la traduction de Java en WSDL. Lcriture dun service web (extrmit de servlet ou dEJB) ou dun consommateur en est donc dautant plus simple puisquil suffit dun POJO annot avec, ventuellement, des descripteurs de dploiement.

506

Java EE 6 et GlassFish 3

Ce chapitre sest termin en tudiant lcriture dun web service, sa compilation et son assemblage avec Maven et la production des artfacts du consommateur avec loutil wsimport.

15
Services web REST
La pile des services web (SOAP, WSDL, WS-*) dcrite au chapitre prcdent fournit la fois une interoprabilit pour linjection des messages et le style RPC. Avec lavnement du Web 2.0, des frameworks web comme Rails sont apparus et un nouveau type de services web est devenu la mode: les services web REST. De nombreux acteurs essentiels du Web, comme Amazon, Google et Yahoo!, ont abandonn leurs services SOAP en faveur de services REST orients ressources. Lorsquil sagit de choisir entre des services SOAP et REST, de nombreux facteurs entrent en ligne de compte. REST (Representational State Transfer) est un type darchitecture reposant sur le fonctionnement mme du Web, quil applique aux services web. Pour concevoir un service REST, il faut connatre le protocole HTTP (Hypertext Transfer Protocol), le principe des URI (Uniform Resource Identifiers) et respecter quelques rgles. Il faut raisonner en termes de ressources. REST est intgr Java EE 6 via JAX-RS (Java API for RESTful Web Services), que nous utiliserons dans ce chapitre.

Prsentation des services web REST


Dans larchitecture REST, toute information est une ressource et chacune delles est dsigne par une URI (Uniform Resource Identifier) gnralement un lien sur le Web. Les ressources sont manipules par un ensemble doprations simples et bien dfinies. Larchitecture client-serveur de REST est conue pour utiliser un protocole de communication sans tat le plus souvent HTTP. Avec REST, les clients et les serveurs changent des reprsentations des ressources en utilisant une interface et un protocole bien dfinis. Ces principes encouragent la simplicit, la lgret et lefficacit des applications.

508

Java EE 6 et GlassFish 3

Ressources

Les ressources jouent un rle central dans les architectures REST. Une ressource est tout ce que peut dsigner ou manipuler un client, toute information pouvant tre rfrence dans un lien hypertexte. Elle peut tre stocke dans une base de donnes, un fichier, etc. On vite autant que possible dexposer des concepts abstraits sous forme de ressources et lon privilgie les objets simples. Certaines ressources de lapplication CD-BookStore pourraient donc tre:

une liste des livres publis par Apress et consacrs Java; louvrage Ruby On Rails; le rsum dOla Bini. les donnes mtorologiques de Paris en 2008; vos informations de contact; le centime nombre de Fibonacci; une transaction en cours dexcution dans un gestionnaire de transactions; le nombre damis communs de Joe et Bill; les photos intressantes postes sur Flickr au cours des 24 dernires heures; les photos intressantes postes sur Flickr le 01/01/2009.

Dautres exemples de ressources sont:


URI

Une ressource web est identifie par une URI, qui est un identifiant unique form dun nom et dune adresse indiquant o trouver la ressource. Il existe diffrents types dURI: les adresses web, les UDI (Universal Document Identifiers), les URI (Universal Resource Identifiers) et, enfin, les combinaisons dURL (Uniform Resource Locators) et dURN (Uniform Resource Names). Voici quelques exemples dURI:

http://www.pearson.fr/catalogue/programmation/ http://www.pearson.fr/Resources/titles/27440100325790/Images /27440100325790L.gif http://www.pearson.fr/contacts/ http://www.weather.com/weather/2008?location=Paris,France http://www.flickr.com/explore/interesting/2009/01/01

Chapitre 15

Services web REST 509

http://www.flickr.com/explore/interesting/24hours http://www.movies.com/catalog/titles/movies/123456 http://www.movies.com/categories/aventure

Les URI devraient tre aussi descriptives que possible et ne dsigner quune seule ressource, bien que des URI diffrentes qui identifient des ressources diffrentes puissent mener aux mmes donnes: un instant donn, la liste des photos intressantes publies sur Flickr le 01/01/2009 tait la mme que la liste des photos dposes au cours des 24 dernires heures, bien que linformation convoye par les deux URI ne ft pas la mme.
Reprsentations

On peut vouloir obtenir la reprsentation dun objet sous forme de texte, de XML, de PDF ou sous un autre format. Un client traite toujours une ressource au travers de sa reprsentation; la ressource elle-mme reste sur le serveur. La reprsentation contient toutes les informations utiles propos de ltat dune ressource. La liste des ouvrages sur Java mentionne prcdemment, par exemple, a au moins deux reprsentations:

La page HTML telle quelle est affiche par un navigateur, http://www.apress. com/book/catalog?category=32; Le fichier CSV tlcharg pour calculer le nombre de livres, apress.com/resource/csv/bookcategory?cat=32.
http://www.

Deux solutions sont possibles pour choisir entre les diffrentes reprsentations dune ressource. La premire consiste proposer une URI par reprsentation: cest ce que fait le site dApress pour la liste des livres sur Java. Cependant, en ce cas, les deux URI sont diffrentes et ne semblent pas directement lies. Voici un ensemble dURI mieux organis:

http://www.apress.com/book/catalog/java http://www.apress.com/book/catalog/java.csv http://www.apress.com/book/catalog/java.xml

La premire URI est la reprsentation par dfaut de la ressource, les reprsentations supplmentaires lui ajoutent simplement lextension de leur format : .csv, .xml, .pdf, etc.

510

Java EE 6 et GlassFish 3

Lautre solution consiste nexposer quune seule URI pour toutes les reprsentations (http://www. apress.com/book/catalog/java, par exemple) et utiliser un mcanisme appel ngociation du contenu, que nous prsenterons un peu plus loin.
WADL

Alors que les services web SOAP utilisent WSDL pour dcrire le format des requtes possibles, WADL (Web Application Description Language) sert indiquer les interactions possibles avec un service web REST. Il facilite le dveloppement des clients, qui peuvent ainsi charger et interagir directement avec les ressources. Nous ne prsenterons pas WADL dans ce livre car il nest pas obligatoire pour les services REST et parce quil est peu utilis.
HTTP

HTTP, un protocole pour les systmes dinformations distribus, collaboratifs et hypermdias, a conduit, avec les URI, HTML et les premiers navigateurs la mise en place du World Wide Web. Gr par le W3C (World Wide Web Consortium) et lIETF (Internet Engineering Task Force), HTTP est le rsultat de plusieurs RFC (Requests For Comment), notamment la RFC216, qui dfinit http1.1. Il repose sur des requtes et des rponses changes entre un client et un serveur.
Requtes et rponses

Un client envoie une requte un serveur afin dobtenir une rponse (voir Figure15.1). Les messages utiliss pour ces changes sont forms dune enveloppe et dun corps galement appel document ou reprsentation.
Figure15.1 Requte et rponse http.
Client GET /index.xhtml
requte

Serveur

200 OK <html><head>...
rponse

Voici, par exemple, un type de requte envoye un serveur:


$> curl -v http://www.apress.com/book/catalog?category=32 > GET /book/catalog?category=32 HTTP/1.1 > User-Agent: curl/7.19.3 (i586-pc-mingw32msvc) libcurl/7.19.3 zlib/1.2.3 > Host: www.apress.com > Accept: */*

Chapitre 15

Services web REST 511

Cette requte contient plusieurs informations envoyes par le client:


la mthode HTTP: GET; le chemin: /book/catalog?category=32; plusieurs autres en-ttes de requte.


GET

Vous remarquerez que la requte na pas de corps (un En rponse, le serveur renverra le message suivant:

na jamais de corps).

< HTTP/1.1 200 OK < Date: Mon, 23 Feb 2009 07:28:09 GMT < Server: Apache/2.0.63 (Unix) PHP/5.2.6 < X-Powered-By: PHP/5.2.6 < Set-Cookie: PHPSESSID=b9ae64b800781d9761670fcf6067a317; path=/ < Expires: Thu, 19 Nov 1981 08:52:00 GMT < Cache-Control: no-store, no-cache, must-revalidate, post-check=0, precheck=0 < Pragma: no-cache < Transfer-Encoding: chunked < Content-Type: text/html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title> ...

Cette rponse est forme des parties suivantes:


Un code de rponse. Ici, ce code est 200

OK.

Plusieurs en-ttes de rponse. Notamment Date, Server, Content-Type. Ici, le type du contenu est text/html, mais il pourrait sagir de nimporte quel format comme du XML (application/xml) ou une image (image/jpeg). Un corps ou reprsentation. Ici, il sagit du contenu de la page web renvoye (dont nous navons reproduit quun fragment).
INFO

Nous utilisons ici cURL (http://curl.haxx.se/) comme client, mais nous aurions pu galement utiliser Firefox ou nimporte quel autre client web. CURL est un outil en ligne de commande permettant de transfrer des fichiers par HTTP, FTP, Gopher, SFTP, FTPS, SCP, TFTP et bien dautres protocoles encore en utilisant une URL. Avec lui, vous pouvez envoyer des commandes HTTP, modifier les en-ttes HTTP, etc.: cest donc un outil parfait pour simuler les actions dun utilisateur dans un navigateur web.

512

Java EE 6 et GlassFish 3

Mthodes de HTTP

Le Web est form de ressources bien identifies, relies ensemble et auxquelles accder au moyen de requtes HTTP simples. Les requtes principales de HTTP sont de type GET, POST, PUT et DELETE. Ces types sont appels verbes ou mthodes. HTTP dfinit quatre autres verbes plus rarement utiliss, HEAD, TRACE, OPTIONS et CONNECT.
GET
GET est une mthode de lecture demandant une reprsentation dune ressource. Elle doit tre implmente de sorte ne pas modifier ltat de la ressource. En outre, GET doit tre idempotente, ce qui signifie quelle doit laisser la ressource dans le mme tat, quel que soit le nombre de fois o elle est appele. Ces deux caractristiques garantissent une plus grande stabilit: si un client nobtient pas de rponse ( cause dun problme rseau, par exemple), il peut renouveler sa requte et sattendre la mme rponse que celle quil aurait obtenue initialement, sans corrompre ltat de la ressource sur le serveur.

POST

partir dune reprsentation texte, XML, etc., POST cre une nouvelle ressource subordonne une ressource principale identifie par lURI demande. Des exemples dutilisation de POST sont lajout dun message un fichier journal, dun commentaire un blog, dun livre une liste douvrages, etc. POST modifie donc ltat de la ressource et nest pas idempotente (envoyer deux fois la mme requte produit deux nouvelles ressources subordonnes). Si une ressource a t cre sur le serveur dorigine, le code de la rponse devrait tre 201 (Created). La plupart des navigateurs modernes ne produisent que des requtes GET ou POST.
PUT

Une requte PUT est conue pour modifier ltat de la ressource stocke une certaine URI. Si lURI de la requte fait rfrence une ressource inexistante, celle-ci sera cre avec cette URI. PUT modifie donc ltat de la ressource, mais elle est idempotente: mme si lon envoie plusieurs fois la mme requte PUT, ltat de la ressource finale restera inchang.
DELETE

Une requte DELETE supprime une ressource. La rponse DELETE peut tre un message dtat dans le corps de la rponse ou aucun code du tout. DELETE est idempotente, mais elle modifie videmment ltat de la ressource.

Chapitre 15

Services web REST 513

Autres

Comme on la voqu, il existe dautres actions HTTP, plus rarement employes:


HEAD ressemble GET sauf que le serveur ne renvoie pas de corps dans sa rponse.
HEAD permet par exemple de vrifier la validit dun client ou la taille dune entit sans avoir besoin de la transfrer.

TRACE

retrace la requte reue.

OPTIONS est une demande dinformation sur les options de communication dispo-

nibles pour la chane requte/rponse identifie par lURI. Cette mthode permet au client de connatre les options et/ou les exigences associes une ressource, ou les possibilits dun serveur sans demander daction sur une ressource et sans rcuprer aucune ressource.
CONNECT

est utilis avec un proxy pouvant se transformer dynamiquement en tunnel (une technique grce laquelle le protocole HTTP sert denveloppe diffrents protocoles rseau).

Ngociation du contenu

La ngociation du contenu, dcrite dans la section12 du standard HTTP, est dfinie comme "le fait de choisir la meilleure reprsentation pour une rponse donne lorsque plusieurs reprsentations sont disponibles". Les besoins, les souhaits et les capacits des clients varient: la meilleure reprsentation pour lutilisateur dun tlphone portable au Japon peut, en effet, ne pas tre la plus adapte un lecteur de flux RSS en France. La ngociation du contenu utilise entre autres les en-ttes HTTP Accept, AcceptCharset, Accept-Encoding, Accept-Language et User-Agent. Pour obtenir, par exemple, la reprsentation CSV de la liste des livres sur Java publis par Apress, lapplication cliente (lagent utilisateur) demandera http://www.apress.com/book/ catalog/java avec un en-tte Accept initialis text/csv. Vous pouvez aussi imaginer que, selon la valeur de len-tte Accept-Language, le contenu de ce document CSV pourrait tre traduit par le serveur dans la langue correspondante.
Types de contenu

HTTP utilise des types de supports Internet (initialement appels types MIME) dans les en-ttes Content-Type et Accept afin de permettre un typage des donnes et une ngociation du contenu ouverts et extensibles. Les types de support Internet sont

514

Java EE 6 et GlassFish 3

diviss en cinq catgories: text, image, audio, video et application. Ces types sont leur tour diviss en sous-types (text/plain, text/xml, text/xhtml, etc.). Voici quelques-uns des plus utiliss:
text/html.

HTML est utilis par linfrastructure dinformation du World Wide Web depuis 1990 et sa spcification a t dcrite dans plusieurs documents informels. Le type de support text/html a t initialement dfini en 1995 par le groupe de travail IETF HTML. Sa dfinition complte se trouve dans la RFC2854. Il sagit du type de contenu par dfaut car il est utilis pour les -messages textuels simples. Le type de support image exige la prsence dun dispositif daffichage (un cran ou une imprimante graphique, par exemple) permettant de visualiser linformation.

text/plain.

image/gif, image/jpeg, image/png.

text/xml, application/xml. XML 1.0 a t publi par le W3C en fvrier 1998.

La dfinition complte de ce support est dcrite dans la RFC3023.


application/json. JSON (JavaScript Object Notation) est un format textuel lger

pour lchange de donnes. Il est indpendant des langages de programmation.


Codes dtat

Un code HTTP est associ chaque rponse. La spcification dfinit environ 60codes dtats; llment Status-Code est un entier de trois chiffres qui dcrit le contexte dune rponse et qui est intgr dans lenveloppe de celle-ci. Le premier chiffre indique lune des cinq classes de rponses possibles:

1xx: Information. La requte a t reue et le traitement se poursuit. 2xx: Succs. Laction a bien t reue, comprise et accepte. 3xx: Redirection. Une autre action est requise pour que la requte seffectue. 4xx: Erreur du client. La requte contient des erreurs de syntaxe ou ne peut pas tre excute. 5xx: Erreur du serveur. Le serveur na pas russi excuter une requte pourtant apparemment valide. 200 OK. La requte a russi. Le corps de lentit, si elle en possde un, contient la reprsentation de la ressource.

Voici quelques codes dtat que vous avez srement dj d rencontrer:

Chapitre 15

Services web REST 515

301Moved Permanently. La ressource demande a t affecte une autre URI permanente et toute rfrence future cette ressource devrait utiliser lune des URI renvoyes. 404 Not Found. Le serveur na rien trouv qui corresponde lURL demande. 500 Internal Server Error. Le serveur sest trouv dans une situation inattendue qui la empch de rpondre la requte.

Mise en cache et requtes conditionnelles

La mise en cache est un lment crucial pour la plupart des systmes distribus. Elle a pour but damliorer les performances en vitant les requtes inutiles et en rduisant le volume des donnes des rponses. HTTP dispose de mcanismes permettant la mise en cache et la vrification de lexactitude des donnes du cache. Si le client dcide de ne pas utiliser ce cache, il devra toujours demander les donnes, mme si elles nont pas t modifies depuis la dernire requte. La rponse une requte de type GET peut contenir un en-tte Last-Modified indiquant la date de dernire modification de la ressource. La prochaine fois que lagent utilisateur demandera cette ressource, il passera cette date dans len-tte If-Modified-Since: le serveur web (ou le proxy) la comparera alors la date de dernire modification. Si celle envoye par lagent utilisateur est gale ou plus rcente, le serveur renverra une rponse sans corps, avec un code dtat 304 Not Modified. Sinon lopration demande sera ralise ou transfre. Les dates peuvent tre difficiles manipuler et impliquent que les agents concerns soient, et restent, synchroniss: cest le but de len-tte de rponse ETag, qui peut tre considr comme un hachage MD5 ou SHA1 de tous les octets dune reprsentation si un seul octet est modifi, la valeur dETag sera diffrente. La valeur ETag reue dans une rponse une requte GET peut, ensuite, tre affecte un en-tte If-Match dune requte. La Figure 15.2 montre un exemple dutilisation dETag. Pour obtenir une ressource livre, on utilise une action GET en lui indiquant lURI de la ressource (GET / book/12345). Le serveur rpondra par une reprsentation XML du livre, un code 200 OK et un ETag. La seconde fois que lon demandera la mme ressource et que lon passera la valeur de cet ETag dans la requte, le serveur ne renverra pas la reprsentation de la ressource, mais uniquement une rponse avec un code 304 Not Modified afin dinformer le client que cette ressource na pas t modifie depuis son dernier accs.

516

Java EE 6 et GlassFish 3

Figure15.2 Utilisation du cache et du code 304.

Client

GET /book/12345 200 OK <book>...</book> ETag: 328987 GET/book/12315 If-Ncn-Match: 328987 301 Not modified

Serveur

Les requtes qui utilisent les en-ttes HTTP If-Modified-Since, If-UnmodifiedSince, If-Match, If-None-Match et If-Range sont appeles requtes conditionnelles. Elles permettent dconomiser la bande passante et le temps de calcul ( la fois sur le serveur et sur le client) en vitant des allers et retours ou des transferts de donnes inutiles. Gnralement, ces en-ttes If-* sont surtout utiliss avec les requtes GET et PUT.

Spcification des services web REST


Contrairement SOAP et la pile WS-*, qui reposent sur les standards du W3C; REST nest pas un standard: cest uniquement un style darchitecture respectant certains critres de conception. Les applications REST, cependant, dpendent fortement dautres standards:

HTTP; URI, URL; XML, JSON, HTML, GIF, JPEG, etc. (reprsentation des ressources).

Sa prise en compte par Java a t spcifie par JAX-RS, mais REST est comme un patron de conception : cest une solution rutilisable dun problme courant, qui peut tre implmente en diffrents langages.
Historique rapide de REST

Le terme REST a t employ pour la premire fois par Roy Thomas Fielding dans le Chapitre5 de sa thse de doctorat, Architectural Styles and the Design of Networkbased Software Architectures (universit de Californie, Irvine, 2000, http://www. ics.uci.edu/~fielding/pubs/dissertation/top.htm). Sa dissertation est une rtrospective des architectures choisies pour dvelopper le Web.

Chapitre 15

Services web REST 517

Dans sa thse, Fielding tudie les parties du Web qui fonctionnent trs bien et en extrait les principes de conception qui pourraient rendre aussi efficaces les autres systmes hypermdias distribus quils soient ou non lis au Web. La raison initiale qui a pouss au dveloppement de REST tait donc de crer un modle architectural du Web. Roy T. Fielding tant galement lun des auteurs de la spcification HTTP, il nest pas surprenant que ce protocole corresponde particulirement bien larchitecture dcrite dans sa dissertation.
JAX-RS 1.1

Pour crire des services web REST, il suffit dun client et dun serveur reconnaissant le protocole HTTP. Nimporte quel navigateur et un conteneur de servlet HTTP pourraient donc faire laffaire, au prix dun peu de configuration XML et dajustement du code. Mais, au final, ce code pourrait devenir peu lisible et difficile maintenir: cest l que JAX-RS vole notre secours. Sa premire version, finalise en octobre 2008, dfinit un ensemble dAPI mettant en avant une architecture REST. Cest le standard JCP pour les services web REST (JSR311). Au moyen dannotations, il simplifie limplmentation de ces services et amliore la productivit. La spcification ne couvre que la partie serveur de REST.
Nouveauts de JAX-RS 1.1

JAX-RS 1.1 est une version de maintenance axe sur lintgration avec Java EE6 et ses nouvelles fonctionnalits. Les nouveauts principales de JAX-RS1.1 sont les suivantes:

Le support de beans de session sans tat comme ressources racine. Il est dsormais possible dinjecter des ressources externes (gestionnaire de persistance, sources de donnes, EJB, etc.) dans une ressource REST. Les annotations JAX-RS peuvent sappliquer linterface locale dun bean ou directement un bean sans interface.

Implmentation de rfrence

Jersey est limplmentation de rfrence officielle de JAX-RS par Sun. Cest un projet open-source couvert la fois par les licences CDDL et GPL. Jersey est extensible au moyen de son API et fournit galement une API cliente (qui nest pas couverte par la spcification).

518

Java EE 6 et GlassFish 3

Il existe galement dautres implmentations de JAX-RS, comme CXF (Apache), RESTEasy (JBoss) et Restlet (un ancien projet qui existait avant que JAX-RS ne soit finalise).

Lapproche REST
Comme on la dj mentionn, REST est un ensemble de contraintes de conceptions gnrales reposant sur HTTP. Ce chapitre sintressant aux services web et REST drivant du Web, nous commencerons par une navigation relle passant en revue les principes du Web. Ce dernier est devenu une source essentielle dinformations et fait dsormais partie de nos outils quotidiens: vous le connaissez donc srement trs bien et cette familiarit vous aidera donc comprendre les concepts et les proprits de REST.
Du Web aux services web

Nous savons comment fonctionne le Web: pourquoi les services web devraient-ils se comporter diffremment? Aprs tout, ils changent souvent uniquement des ressources bien identifies, lies dautres au moyen de liens hypertextes. Larchitecture du Web ayant prouv sa tenue en charge au cours du temps, pourquoi rinventer la roue? Pour crer, modifier et supprimer une ressource livre, pourquoi ne pas utiliser les verbes classiques de HTTP? Par exemple:

Utiliser POST sur des donnes (au format XML, JSON ou texte) afin de crer une ressource livre avec lURI http://www.apress.com/books/. Le livre cr, la rponse renvoie lURI de la nouvelle ressource : http://www.apress.com/ books/123456. Utiliser GET pour lire la ressource (et les ventuels liens vers dautres ressources partir du corps de lentit) lURI http://www.apress.com/books/123456. Utiliser PUT pour modifier la ressource situe lURI http://www.apress.com/ books/123456. Utiliser
DELETE

pour supprimer la ressource lURI

http://www.apress.com/

books/123456.

En se servant de verbes HTTP, nous pouvons donc effectuer toutes les actions CRUD (Create, Read, Update, Delete) sur une ressource.

Chapitre 15

Services web REST 519

Pratique de la navigation sur le Web

Comment obtenir la liste des livres sur Java publis par Apress? En faisant pointer son navigateur sur le site web dApress: http://www.apress.com. Mme si cette page ne contiendra srement pas les informations exactes que lon recherche, on sattend ce quelle donne accs, par un moyen ou un autre, la liste des livres consacrs Java. La page daccueil offre un moteur de recherche de tous les livres dApress, mais galement un rpertoire de livres classs par technologies. Si lon clique sur le nud Java, la magie de lhypertexte opre et lon obtient la liste complte des livres sur Java publis par Apress. Elle est assez longue et, bien que le nombre douvrages ne soit pas indiqu, le lien vers le format CSV et nos comptences en shell permettront de faire ce calcul en quelques cycles dhorloge (il y a ici 144livres dans la catgorie Java):
curl http://www.apress.com/resource/csv/bookcategory?cat=32 | sed -n 2~1p | wc -l (..) 144

Supposons que nous ayons sauvegard le lien dans notre gestionnaire des favoris et quau cours du parcours de la liste louvrage The Definitive Guide to Grails, de Graeme Rocher et Jeff Brown, attire notre attention: le lien hypertexte sur le titre de ce livre nous mnera la page contenant le rsum, la biographie des auteurs, etc. et nous pourrons noter quun des livres mentionns dans la section Related titles est galement susceptible de nous intresser. Nous voudrons comparer louvrage de Graeme Rocher avec Practical JRuby on Rails Web2.0 Projects: Bringing Ruby on Rails to Java, dOla Bini. Les pages des livres dApress nous donnent accs une reprsentation plus concrte sous la forme de prvisualisations: nous pouvons alors ouvrir une prvisualisation, lire la table des matires et faire notre choix. Voici ce que lon fait quotidiennement avec nos navigateurs. REST applique les mmes principes vos services o les livres, les rsultats des recherches, une table des matires ou la couverture dun livre peuvent tre dfinis comme des ressources.
Interface uniforme

Lune des principales contraintes constituant une architecture REST est lutilisation dune interface uniforme pour la gestion des ressources. Nous pouvons choisir linterface que nous voulons, du moment que nous lutilisons de la mme faon partout, dune ressource une autre, dun service un autre. Il ne faut jamais sen carter ou

520

Java EE 6 et GlassFish 3

modifier sa signification initiale. En utilisant une interface uniforme, "larchitecture globale du systme est simplifie et la visibilit des interactions, amliore" (Roy Thomas Fielding, Architectural Styles and the Design of Network-based Software Architectures). Nos services font alors partie dune communaut de services utilisant exactement la mme smantique. Le protocole de facto du Web est HTTP un protocole standardis de requte/ rponse entre un client et un serveur , et cest linterface uniforme des services web REST. Les services reposant sur SOAP, WSDL et les autres standards WS-* lutilisent galement au niveau de la couche transport, mais ils ne se servent que dune infime partie de ses possibilits. Sil faut analyser le WSDL pour dcouvrir la smantique dun service SOAP puis appeler les bonnes mthodes, les services web REST, en revanche, ont une interface uniforme (les verbes HTTP et les URI): ds que lon connat lemplacement dune ressource (son URI), il suffit dinvoquer les verbes HTTP (GET, POST, etc.). Outre lavantage de sa familiarit, une interface web met en avant linteroprabilit entre les applications: HTTP est largement rpandu et le nombre de bibliothques HTTP clientes garantit que nous naurons pas besoin de nous soucier des problmes de communication.
Accessibilit

Le second principe de la conception des services REST est laccessibilit. Un service web doit rendre une application aussi accessible que possible, ce qui signifie que chaque fragment dinformation intressante de cette application doit tre une ressource et possder une URI afin de pouvoir tre aisment atteinte. Cest la seule information quil faut publier pour rendre la ressource accessible, afin que le partenaire nait pas besoin de jouer aux devinettes pour latteindre. Supposons, par exemple, quun bogue se soit gliss dans notre application et que nos investigations nous amnent la ligne42 de la classe CreditCardValidator.java. Comme nous ne sommes pas responsables de cette partie de lapplication, nous remplissons un rapport de bogue que nous envoyons la personne qualifie. Comment lui montrer la ligne incrimine? Nous pourrions crire "Allez la ligne42 de la classe CreditCardValidator" ou, si notre code source est accessible via un navigateur, placer lURI de cette ligne dans le rapport de bogue. Ceci pose donc le problme de la granularit des ressources REST dune application: elle peut intervenir au niveau dune ligne, dune mthode, dune classe, etc.

Chapitre 15

Services web REST 521

Les URI uniques permettent de crer des liens vers les ressources et, comme elles sont exposes via une interface uniforme, chacun sait exactement comment interagir avec elles: ceci permet dutiliser une application de faon insouponne.
Connectivit

En thorie des graphes, on dit quun graphe est connexe lorsque tous les sommets peuvent se connecter les uns aux autres via un chemin quelconque. Un graphe est dit fortement connexe sil existe un chemin direct de u v et un chemin direct de v u pour toute paire de sommets (u, v). REST demande ce que les ressources soient les plus connectes possible. Cest le fameux principe "Lhypermdia est le moteur de ltat de lapplication". Ce principe provient, l encore, dun examen du succs du Web. Les pages contiennent des liens pour passer de page en page de faon logique et simple, et lon peut donc considrer que le Web est trs bien connect. Sil existe une relation forte entre deux ressources, celles-ci doivent tre connectes. REST prcise que les services web devraient galement tirer parti de lhypermdia pour informer le client de ce qui est disponible et de la faon de sy rendre. Il met en avant la dcouverte des services. partir dune seule URI, un agent utilisateur accdant un service bien connect pourra dcouvrir toutes les actions et ressources disponibles, leurs diffrentes reprsentations, etc. Lorsquun agent utilisateur recherche, par exemple, la reprsentation dun CD (voir Listing15.1), celle-ci peut contenir non seulement le nom de lartiste, mais galement un lien une URI vers sa biographie. Cest ensuite lagent utilisateur de la rcuprer ou non. La reprsentation pourrait galement contenir des liens vers dautres reprsentations de la ressource ou vers les actions possibles.
Listing15.1: Reprsentation dun CD connecte dautres services
<cd> <title>Ella and Louis</title> <year ref=http://music.com/year/1956>1956</year> <artist ref=http://music.com/artists/123> Ella Fitzgerald </artist> <artist ref=http://music.com/artists/456> Louis Armstrong </artist> <link rel="self" type="text/json" href="http://music.com/album/789"/>

522

Java EE 6 et GlassFish 3

<link rel="self" type="text/xml" href="http://music.com/album/789"/> <link rel="http://music.com/album/comments" type="text/xml" href="http://music.com/album/789/comments"/> </cd>

Un autre aspect essentiel du principe de lhypermdia est quil doit piloter ltat de lapplication. Pour rsumer, le fait que le service web fournisse un ensemble de liens permet au client de faire passer lapplication dun tat lautre en suivant simplement un lien. Dans lextrait XML prcdent, le client pouvait ainsi changer ltat de lapplication en apportant un commentaire sur lalbum. La liste des commentaires de cet album est une ressource accessible par lURI http://music.com/album/789/comments. Ce service web utilisant une interface uniforme, tout client connaissant le format de lURI, les types de contenus disponibles et le format des donnes savent exactement comment interagir avec lui: un GET rcuprera la liste des commentaires existants, un POST crera un nouveau commentaire, etc. partir de cette requte initiale simple, le client peut raliser de nombreuses actions, et cest la raison pour laquelle lhypermdia pilote ltat de lapplication.
Sans tat

La dernire fonctionnalit de REST est labsence dtat, ce qui signifie que toute requte HTTP est totalement indpendante puisque le serveur ne mmorisera jamais les requtes qui ont t effectues. Pour plus de clart, ltat dune ressource et celui de lapplication sont gnralement diffrencis: ltat de la ressource doit se trouver sur le serveur et tre partag par tous, tandis que celui de lapplication doit rester chez le client et tre sa seule proprit. Si lon revient lexemple du Listing15.1, ltat de lapplication est que le client a rcupr une reprsentation de lalbum Ella and Louis, mais le serveur ne mmorise pas cette information. Ltat de la ressource, quant lui, est linformation sur lalbum: le serveur doit videmment la mmoriser et le client peut le modifier. Si le panier virtuel est une ressource dont laccs est rserv un seul client, lapplication doit stocker lidentifiant de ce panier dans la session du client. Labsence dtat a de nombreux avantages, notamment une meilleure adaptation la charge: aucune information de session grer, pas besoin de router les requtes suivantes vers le mme serveur, gestion des erreurs, etc. Si vous devez mmoriser ltat, le client devra faire un travail supplmentaire pour le stocker.

Chapitre 15

Services web REST 523

JAX-RS: Java API for RESTful Web Services


Vous pouvez vous demander quoi ressemblera le code qui sappuie sur des concepts daussi bas niveau que le protocole HTTP. En ralit, vous navez pas besoin dcrire des requtes HTTP ni de crer manuellement des rponses car JAX-RS est une API trs lgante qui permet de dcrire une ressource laide de quelques annotations seulement. Comme le montre le Listing15.2, une ressource est alors un POJO possdant au moins une mthode annote par @javax.ws.rs.Path.
Listing15.2: Une ressource livre simple
@Path("/book") public class BookResource { @GET @Produces("text/plain") public String getBookTitle() { return "H2G2"; } }

BookResource

tant une classe Java annote par @Path, la ressource sera hberge lURI /book. La mthode getBookTitle() est annote par @GET afin dindiquer quelle traitera les requtes HTTP GET et quelle produit du texte (le contenu est identifi par le type MIME text/plain). Pour accder la ressource, il suffit dun client HTTP, un navigateur, par exemple, pouvant envoyer une requte GET vers lURL http://www.myserver.com/book.

Le modle JAX-RS

Le Listing15.2 montre que le service REST nimplmente aucune interface et ntend aucune classe: @Path est la seule annotation obligatoire pour transformer un POJO en service REST. JAX-RS utilisant la configuration par exception, un ensemble dannotations permettent de modifier son comportement par dfaut. Les exigences que doit satisfaire une classe pour devenir un service REST sont les suivantes:

Elle doit tre annote par @javax.ws.rs.Path. Pour ajouter les fonctionnalits des EJB au service REST, la classe doit tre annote par @javax.ejb.Stateless. Elle doit tre publique et ne pas tre finale ni abstraite.

524

Java EE 6 et GlassFish 3

Les classes ressources racine (celles ayant une annotation @Path) doivent avoir un constructeur par dfaut public. Les classes ressources non racine nexigent pas ce constructeur. La classe ne doit pas dfinir la mthode finalize().

Par nature, JAX-RS repose sur HTTP et dispose dun ensemble de classes et dannotations clairement dfinies pour grer HTTP et les URI. Une ressource pouvant avoir plusieurs reprsentations, lAPI permet de grer un certain nombre de types de contenu et utilise JAXB pour srialiser et dsrialiser les reprsentations XML et JSON en objets. JAX-RS tant indpendant du conteneur, les ressources peuvent tre videmment dployes dans GlassFish, mais galement dans un grand nombre dautres conteneurs de servlets.
criture dun service REST

Les services REST peuvent tre des beans de session sans tat, ce qui permet dutiliser des transactions pour accder une couche de persistance (entits JPA), comme le montre le Listing15.3.
Listing15.3: Une ressource livre qui cre et rcupre des livres dans une base de donnes
@Path("books") @Stateless @Produces({"application/xml", "application/json"}) @Consumes({"application/xml", "application/json"}) public class BookResource { @Context private UriInfo uriInfo; @PersistenceContext(unitName = "chapter15PU") private EntityManager em; @GET public List<Book> getAllBooks() { Query query = em.createNamedQuery("findAllBooks"); List<Book> books = query.getResultList(); return books; } @POST public Response createNewBook(JAXBElement<Book> bookJaxb) { Book book = bookJaxb.getValue(); em.persist(book); URI bookUri = uriInfo.getAbsolutePathBuilder().path( book.getId().toString()).build(); return Response.created(bookUri).build(); } }

Chapitre 15

Services web REST 525

Le code du Listing15.3 reprsente un service REST pouvant consommer et produire les reprsentations XML et JSON dun livre. La mthode getAllBooks() rcupre la liste des livres partir dune base de donnes et renvoie sa reprsentation XML ou JSON (en utilisant la ngociation du contenu); elle est appele par une requte GET. La mthode createNewBook() prend une reprsentation XML ou JSON dun livre et le stocke dans la base de donnes. Cette mthode est invoque par une requte POST et renvoie lURI du nouveau livre.
Dfinition des URI

La valeur de lannotation @Path est relative un chemin dURI. Lorsquelle est utilise sur des classes, celles-ci sont considres comme des ressources racine, car elles fournissent la racine de larborescence des ressources et laccs aux sous-ressources.
@Path("/customers") public class CustomersResource { @GET public List getCustomers() { // ... }

Vous pouvez galement intgrer dans la syntaxe de lURI des templates de chemins dURI: ces variables seront values lexcution. La documentation Javadoc de lannotation @Path dcrit la syntaxe des templates:
@Path("/customers/{customername}")

peut galement sappliquer aux mthodes des ressources racine, ce qui permet de regrouper les fonctionnalits communes plusieurs ressources, comme le montre la Figure15.4 (ignorez pour linstant les annotations @GET, @POST et @DELETE car nous les dcrirons plus loin, dans la section "Mthodes ou interface uniforme").
@Path

Listing15.4: Une ressource article avec plusieurs annotations @Path


@Path("/items") public class ItemsResource { @GET public List<Item> getListOfItems() { // ... } @GET @Path("{itemid}")

526

Java EE 6 et GlassFish 3

public Item getItem(@PathParam("itemid") String itemid) { // ... } @PUT @Path("{itemid}") public void putItem(@PathParam("itemid") String itemid, Item item) { // ... } @DELETE @Path("{itemid}") public void deleteItem(@PathParam("itemid") String itemid) { // ... } @GET @Path("/books/") public List<Book> getListOfBooks() { // ... } @GET @Path("/books/{bookid}") public Book getBook(@PathParam("bookid") String bookid) { // ... } }

Si @Path est applique la fois sur la classe et une mthode, le chemin relatif de la ressource produite par cette mthode est la concatnation de la classe et de la mthode. Pour obtenir un livre par son identifiant, par exemple, le chemin sera /items/books/1234. Si lon demande la ressource racine /items, seule la mthode sans annotation @Path sera slectionne (getListOfItems(), ici). Si lon demande /items/books, cest la mthode getListOfBooks() qui sera invoque. Si @Path ("/items") nannotait que la classe et aucune mthode, le chemin daccs de toutes les mthodes serait le mme et il faudrait utiliser le verbe HTTP (GET, PUT) et la ngociation du contenu (texte, XML, etc.) pour les diffrencier.
Extraction des paramtres

Nous avons besoin dextraire des informations sur les URI et les requtes lorsquon les manipule. Le Listing 15.4 a dj montr comment extraire un paramtre du chemin laide de @javax.ws.rs.PathParam et JAX-RS fournit tout un ensemble

Chapitre 15

Services web REST 527

dannotations pour extraire les diffrents paramtres quune requte peut envoyer (@QueryParam, @MatrixParam, @CookieParam, @HeaderParam et @FormParam). Lannotation @PathParam permet dextraire la valeur dun paramtre template dune URI. Le code suivant, par exemple, extraira lidentifiant du client (98342) de lURI http://www.myserver. com/customers/98342:
@Path("/customers") public class CustomersResource { @GET public Customer getCustomer(@PathParam("customerid") customerid) { // ... } }

Lannotation @QueryParam extrait la valeur du paramtre dune requte. Le code suivant extraira le code postal (19870) de lURI http://www.myserver.com/ customers?zip=19870:
@Path("/customers") public class CustomersResource { @GET public Customer getCustomerByZipCode(@QueryParam("zip") Long zip) { // ... } }

Lannotation @MatrixParam agit comme @QueryParam, sauf quelle extrait la valeur dun paramtre matrice dune URI (le dlimiteur est; au lieu de?). Elle permet, par exemple, dextraire le nom de lauteur (Goncalves) de cette URI: http://www. myserver.com/products/ books;author=Goncalves. Deux autres annotations sont lies aux dtails internes de HTTP, ce que lon ne voit pas directement dans les URI: les cookies et les en-ttes. @CookieParam extrait la valeur dun cookie, tandis que @HeaderParam permet dobtenir la valeur dun entte. Le code suivant, par exemple, extrait lidentifiant de session du cookie:
@Path("/products") public class ItemsResource { @GET public Book getBook(@CookieParam("sessionid") int sessionid) { // ... } }

528

Java EE 6 et GlassFish 3

Lannotation @FormParam prcise que la valeur dun paramtre doit tre extraite dun formulaire situ dans le corps de la requte. On peut ajouter @DefaultValue toutes ces annotations pour dfinir une valeur par dfaut pour le paramtre que lon attend. Cette valeur sera utilise si les mtadonnes correspondantes sont absentes de la requte. Si le paramtre age ne se trouve pas dans la requte, par exemple, le code suivant utilisera la valeur 50 par dfaut:
@Path("/customers") public class CustomersResource { @GET public Response getCustomers(@DefaultValue("50") @QueryParam("age") int age) { // ... } }

Consommation et production des types de contenus

Avec REST, une ressource peut avoir plusieurs reprsentations: un livre peut tre reprsent comme une page web, un document XML ou une image affichant sa couverture. Les annotations @javax.ws.rs.Consumes et @javax.ws.rs.Produces peuvent sappliquer une ressource qui propose plusieurs reprsentations: elle dfinit les types de mdias changs entre le client et le serveur. Lutilisation de lune de ces annotations sur une mthode ressource redfinit celle qui sappliquait sur la classe de la ressource pour un paramtre dune mthode ou une valeur de retour. En leur absence, on suppose que la ressource supporte tous les types de mdias (*/*). Dans le Listing 15.5, CustomerResource produit par dfaut une reprsentation en texte brut, sauf pour certaines mthodes.
Listing15.5: Une ressource customer avec plusieurs reprsentations
@Path("/customers") @Produces("text/plain") public class CustomersResource { @GET public String getAsPlainText() { // ... } @GET @Produces("text/html")

Chapitre 15

Services web REST 529

public String getAsHtml() { // ... } @GET @Produces("application/json") public List<Customer> getAsJson() { // ... } @PUT @Consumes("text/plain") public void putBasic(String customer) { // ... } @POST @Consumes("application/xml") public Response createCustomer(InputStream is) { // ... } }

Si une ressource peut produire plusieurs types de mdias Internet, la mthode choisie correspondra au type qui convient le mieux len-tte Accept de la requte HTTP du client. Si, par exemple, cet en-tte est:
Accept: text/plain

et que lURI est /customers, cest la mthode getAsPlainText() qui sera invoque. Le client aurait galement pu utiliser len-tte suivant:
Accept: text/plain; q=0.8, text/html

Cet en-tte annonce que le client peut accepter les types text/plain et text/html mais quil prfre le dernier avec un facteur de qualit de 0.8 ("je prfre huit fois plus le text/html que le text/plain"). En incluant cet en-tte dans une requte adresse /customers, cest la mthode getAsHtml() qui sera invoque. Dans le Listing15.5, la mthode getAsJson() renvoie une reprsentation de type application/json. JSON est un format lger pour lchange de donnes: comme vous pourrez le constater en tudiant le Listing15.6, il est moins verbeux et plus lisible que XML.
Listing15.6: Reprsentation JSON dune liste de clients
{"customers": { "id": "0",

530

Java EE 6 et GlassFish 3

"version": "1", "customer": [ { "firstName": "Alexis", "lastName": "Midon", "address": { "streetAddress": "21 2nd Street", "city": "New York", "postalCode": 10021 }, "phoneNumbers": [ "212 555-1234", "646 555-4567" ] }, { "firstName": "Sebastien", "lastName": "Auvray", "address": { "streetAddress": "2 Rue des Acacias", "city": "Paris", "postalCode": 75015 }, "phoneNumbers": [ "+33 555-1234", ] } ] })

Jersey convertira pour vous la liste des clients au format JSON en utilisant les fournisseurs dentits prdfinis MessageBodyReader et MessageBodyWriter.
INFO Jersey ajoute une association BadgerFish de JAXB XML vers JSON. BadgerFish (http:// badgerfish.ning.com/) est une convention de traduction dun document XML en objet JSON afin quil soit plus facile manipuler avec JavaScript.

Fournisseurs dentits

Lorsque les entits sont reues dans des requtes ou envoyes dans des rponses, limplmentation JAX-RS doit pouvoir convertir les reprsentations en Java et vice versa: cest le rle des fournisseurs dentits. JAXB, par exemple, traduit un objet en reprsentation Java et rciproquement. Il existe deux variantes de fournisseurs dentits: MessageBodyReader et MessageBodyWriter.

Chapitre 15

Services web REST 531

Pour traduire le corps dune requte en Java, une classe doit implmenter linterface javax.ws.rs.ext.MessageBodyReader et tre annote par @Provider. Par dfaut, limplmentation est cense consommer tous les types de mdias (*/*), mais lannotation @Consumes permet de restreindre les types supports. Le code du Listing15.7, par exemple, prend un flux XML en entre (reprsent par un objet javax.xml. bind.Element) et le convertit en objet Customer.
Listing15.7: Fournisseur lisant un flux XML et crant une entit Customer
@Provider @Consumes("application/xml") public class CustomerReader implements MessageBodyReader<Element> { public boolean isReadable(Class<?> type) { return Element.class.isAssignableFrom(type); } public Element readFrom(Class<Element> customer, MediaType mediaType, MultivaluedMap<String, String> headers, InputStream inputStream) throws IOException { Document doc = getParser().parse(inputStream); Element node = doc.getDocumentElement(); if (node.getLocalName().equals("customer")) { return el; } else { throw new IOException("Unexpected payload!"); } } }

De la mme faon, un type Java peut tre traduit en corps de rponse. Une classe Java voulant effectuer ce traitement doit implmenter linterface javax.ws.rs. ext.MessageBodyWriter et tre annote par linterface @Provider. Lannotation @Produces indique les types de mdias supports. Le code du Listing15.8 montre comment convertir une entit Customer en un corps HTTP en texte brut.
Listing15.8: Fournisseur produisant une reprsentation en texte brut dune entit Customer
@Provider @Produces("text/plain") public class CustomerWriter implements MessageBodyWriter<Customer> { public boolean isWriteable(Class<?> type) {

532

Java EE 6 et GlassFish 3

return Customer.class.isAssignableFrom(type);

public void writeTo(Customer customer, MediaType mediaType, MultivaluedMap<String, Object> headers, OutputStream outputStream) throws IOException { outputStream.write(customer.get(id).getBytes()); // ... }

Les implmentations de MessageBodyReader et MessageBodyWriter peuvent lever une exception WebApplicationException lorsquelles ne peuvent produire aucune reprsentation. Limplmentation de JAX-RS offre un ensemble de fournisseurs dentits par dfaut convenant aux situations courantes (voir Tableau15.1).
Tableau15.1: Fournisseurs par dfaut de limplmentation de JAX-RS

Type
byte[] java.lang.String java.io.InputStream java.io.Reader java.io.File javax.activation.DataSource javax.xml.transform.Source javax.xml.bind.JAXBElement MultivaluedMap<String,String> javax.ws.rs.core StreamingOutput

Description Tous types de mdias (*/*) Tous types de mdias (*/*) Tous types de mdias (*/*) Tous types de mdias (*/*) Tous types de mdias (*/*) Tous types de mdias (*/*) Types XML (text/xml, application/xml et application/-*+xml) Types de mdias XML de JAXB (text/-xml, application/ xml et application/*+xml) Contenu de formulaire (application/x-wwwform-urlencoded) Tous types de mdias (*/*), uniquement
MessageBodyWriter

Mthodes ou interface uniforme

Nous avons vu comment le protocole HTTP grait ses requtes, ses rponses et ses mthodes dactions (GET, POST, PUT, etc.). JAX-RS dfinit ces mthodes HTTP

Chapitre 15

Services web REST 533

laide dannotations: @GET, @POST, @PUT, @DELETE, @HEAD et @OPTIONS. Seules les mthodes publiques peuvent tre exposes comme des mthodes de ressources. Le Listing15.9, par exemple, montre une ressource customer exposant les mthodes CRUD.
Listing15.9: Ressource customer exposant les oprations CRUD
@Path("/customers") public class CustomersResource { @GET public List<Customer> getListOfCustomers() { // ... } @POST @Consumes("application/xml") public Response createCustomer(InputStream is) { // ... } @PUT @Path("{customerid}") @Consumes("application/xml") public Response updateCustomer(@PathParam("customerid") String customerId, InputStream is) { // ... } @DELETE @Path("{customerid}") public void deleteCustomer(@PathParam("customerid") String customerId) { // ... } }

Lorsquune mthode ressource est invoque, les paramtres annots par lune des annotations dextraction vues prcdemment sont initialiss. La valeur dun paramtre non annot (appel paramtre entit) est obtenue partir du corps de la requte et convertie par un fournisseur dentits. Les mthodes peuvent renvoyer void, Response ou un autre type Java. Response indique quil faudra fournir dautres mtadonnes lorsque lon cre un nouveau client, par exemple, il faudra renvoyer son URI.

534

Java EE 6 et GlassFish 3

Informations contextuelles

Le fournisseur de ressources a besoin dinformations contextuelles pour traiter correctement une requte. Lannotation @javax.ws.rs.core.Context sert injecter les classes suivantes dans un attribut ou dans le paramtre dune mthode: HttpHeaders, UriInfo, Request, SecurityContext et Providers. Le code du Listing15.10, par exemple, reoit par injection une UriInfo afin de pouvoir construire des URI.
Listing15.10: Ressource customer obtenant une UriInfo
@Path("/customers") public class CustomersResource { @Context UriInfo uriInfo; @GET @Produces("application/json") public JSONArray getListOfCustomers() { JSONArray uriArray = new JSONArray(); for (Customer customer : customerDAO.findAll()) { UriBuilder ub = uriInfo.getAbsolutePathBuilder(); URI userUri = ub.path(userEntity.getCustomerId()).build(); uriArray.put(userUri.toASCIIString()); } return uriArray; } }

En-ttes

Comme on la vu prcdemment, les informations transportes entre le client et le serveur sont formes non pas uniquement du corps dune entit, mais galement den-ttes (Date, Server, Content-Type, etc.). Les en-ttes HTTP font partie de linterface uniforme et les services web REST les utilisent. La classe javax. ws.rs.core.HttpHeaders permet aux dveloppeurs de ressources dy accder: une instance de HttpHeaders peut tre injecte dans un attribut ou dans un paramtre de mthode au moyen de lannotation @Context afin de permettre daccder aux valeurs des en-ttes sans tenir compte de leur casse. Si le service fournit des ressources localises, par exemple, le code suivant permettra dextraire la valeur de len-tte Accept-Language:
@GET @Produces{"text/plain"} public String get(@Context HttpHeaders headers) {

Chapitre 15

Services web REST 535

List<java.util.Locale> locales = headers.getAcceptableLanguages() // ou headers.getRequestHeader("accept-language") ... }

Construction dURI

Les liens hypertextes sont un lment central des applications REST. Pour voluer travers les tats de lapplication, les services web REST doivent grer les transitions et la construction des URI. Pour ce faire, JAX-RS fournit une classe javax.ws.rs. core.UriBuilder destine remplacer java.net.URI et faciliter la construction dURI. UriBuilder dispose dun ensemble de mthodes permettant de construire de nouvelles URI ou den fabriquer partir dURI existantes. Le code du Listing15.11 sen sert pour renvoyer un tableau dURI.
Listing15.11: Mthode renvoyant un tableau dURI
@Path("/customers/") public class CustomersResource { @Context UriInfo uriInfo; @GET @Produces("application/json") public JSONArray getCustomersAsJsonArray() { JSONArray uriArray = new JSONArray(); for (UserEntity userEntity : getUsers()) { UriBuilder ub = uriInfo.getAbsolutePathBuilder(); URI userUri = ub.path(userEntity.getUserid()).build(); uriArray.put(userUri.toASCIIString()); } return uriArray; } }

peut galement servir traduire des templates dURI de faon simple. LURI http://www.myserver.com/products/books?author=Goncalves peut, par exemple, tre obtenue de la faon suivante:
UriBuilder
UriBuilder.fromUri("http://www.myserver.com/") .path("{a}/{b}").queryParam("author", "{value}") .build("products", "books", "Goncalves");

536

Java EE 6 et GlassFish 3

Gestion des exceptions

Les codes que nous avons prsents jusqu maintenant sexcutaient dans un monde parfait, o tout se passe bien et o il ny a pas besoin de traiter les exceptions. Malheureusement, ce monde nexiste pas et, tt ou tard, une ressource nous explosera en plein visage parce que les donnes reues ne sont pas valides ou que des parties du rseau ne sont pas fiables. Comme le montre le Listing15.12, nous pouvons lever tout instant une exception WebApplicationException ou lune de ses sous-classes dans un fournissseur de ressources. Cette exception sera capture par limplmentation de JAX-RS et convertie en rponse HTTP. Lerreur par dfaut est un code 500 avec un message vide, mais la classe javax.ws.rs.WebApplicationException offre diffrents constructeurs permettant de choisir un code dtat spcifique (dfini dans lnumration javax. ws.rs.core.Response.Status) ou une entit.
Listing15.12: Mthode levant des exceptions
@Path("customers/{id}") public Customer getCustomer(@PathParam("id") int id) { if(id < 1) throw new WebApplicationException(Response.status(400) .entity("Id must be a positive integer!")); Item i = em.find(Item.class, id); if (i == null) throw new WebApplicationException(Response.Status.NOT_FOUND); return i; }

Pour crire un code DRY (acronyme de Dont Repeat Yourself), nous pouvons fournir des fournisseurs de traduction dexception, qui traduisent une exception en un objet Response. Si cette exception est lance, limplmentation JAX-RS la capturera et appellera le fournisseur de traduction dexception appropri. Comme le montre le Listing15.13, ces fournisseurs sont des implmentations dExceptionMapper<E extends java.lang.Throwable> annotes par @Provider.
Listing15.13: Traduction dexception
@Provider public class EntityNotFoundMapper implements ExceptionMapper<javax.persistence.EntityNotFoundException> { public Response toResponse( javax.persistence.EntityNotFoundException ex) {

Chapitre 15

Services web REST 537

return Response.status(404).entity(ex.getMessage()). type("text/plain").build(); } }

Les exceptions non contrles et les erreurs qui nentrent pas dans les deux cas prcdents seront relances comme toute exception Java non contrle.
Cycle de vie

Lorsquune requte arrive, la ressource cible est rsolue et une nouvelle instance de la classe ressource racine correspondante est cre. Le cycle de vie dune classe ressource racine dure donc le temps dune requte, ce qui implique que la classe ressource na pas soccuper des problmes de concurrence et quelle peut donc utiliser les variables dinstance en toute scurit. Sils sont dploys dans un conteneur Java EE (servlet ou EJB), les classes ressources et les fournisseurs JAX-RS peuvent galement utiliser les annotations de gestion du cycle de vie et de la scurit dfinies dans la JSR250: @PostConstruct, @PreDestroy, @RunAs, @RolesAllowed, @PermitAll, @DenyAll et @DeclareRoles. Le cycle de vie dune ressource peut se servir de @PostConstruct et de @PreDestroy pour ajouter de la logique mtier respectivement aprs la cration dune ressource et avant sa suppression. La Figure15.3 montre que ce cycle de vie est quivalent celui de tous les composants sans tat de JavaEE.
Figure15.3 Cycle de vie dune ressource.
@PostConstruct @PreDestroy N'existe pas

Prt

Appel de mthode

Rcapitulatif
Rassemblons tous ces concepts pour crire une ressource livre, lassembler et la dployer dans GlassFish puis la tester avec cURL. Lide consiste avoir une entit

538

Java EE 6 et GlassFish 3

associe une base de donnes et une classe BookResource produisant une reprsentation REST du livre. BookResource est galement un bean de session sans tat, ce qui autorise une dmarcation des transactions avec le gestionnaire dentits pour les oprations CRUD. Ces classes dployes, nous pourrons crer, rcuprer ou supprimer des livres laide des mthodes HTTP avec cURL et obtenir la fois des reprsentations XML et JSON.
Book

Cet exemple respecte la structure de rpertoires de Maven et assemble toutes les classes dans un fichier war (chapter15-resource-1.0.war). Les classes doivent se trouver dans les rpertoires suivants:
src/main/java.
BookResource.

Rpertoire pour lentit

Book

et le service web REST

src/main/resources.

Contient le fichier persistence.xml utilis par la ressource pour faire correspondre lentit Book la base de donnes Derby. src/webapp/WEB-INF. Contient le fichier web.xml qui dclare la servlet Jersey. pom.xml. Le fichier POM (Project Object Model) de Maven, qui dcrit le projet et ses dpendances.
Lentit Book

Vous connaissez dj le code de lentit Book mais, comme le montre la Figure15.14, un lment trs important a t ajout ici: lannotation @XmlRootElement de JAXB, qui permet dobtenir une reprsentation XML dun livre.
Listing15.14: Entit Book avec une annotation JAXB
@Entity @XmlRootElement @NamedQuery(name = "findAllBooks", query = "SELECT b FROM Book b") public class Book { @Id @GeneratedValue private Long id; @Column(nullable = false) private String title; private Float price; @Column(length = 2000) private String description; private String isbn; private Integer nbOfPage; private Boolean illustrations; } // Constructeurs, getters, setters

Chapitre 15

Services web REST 539

Cette entit doit galement tre assemble avec un fichier nous ne prsentons pas ici).
Le service REST BookResource

persistence.xml

(que

BookResource est un service web REST implment comme un bean de session sans

tat et qui utilise un gestionnaire dentits pour crer, supprimer et obtenir des livres.
En-tte

Len-tte de BookResource est important car il utilise plusieurs annotations. Avec JAX-RS, les utilisateurs peuvent accder aux ressources en publiant une URI. Lannotation @Path("books") indique le chemin de la ressource (cest--dire lURL pour latteindre) ici, elle est de la forme http:// localhost:8080/rs/books. Les annotations @Produces et @Consumes dfinissent les types de contenus par dfaut que produit ou consomme cette ressource XML et JSON, ici. Vous pouvez redfinir ce type de contenu mthode par mthode. Enfin, lannotation @Stateless que nous avons tudie au Chapitre7 informe le conteneur que cette ressource doit galement tre considre comme un EJB et quelle autorise la dmarcation des transactions lors des accs la base de donnes. Cette ressource reoit par injection une rfrence un gestionnaire dentits et une UriInfo:
@Path("books") @Produces({"application/xml", "application/json"}) @Consumes({"application/xml", "application/json"}) @Stateless public class BookResource { @PersistenceContext(unitName = "chapter15PU") private EntityManager em; @Context private UriInfo uriInfo;

Cration dun livre

Pour respecter la smantique de REST, nous utilisons une mthode POST pour crer une nouvelle ressource en XML ou JSON, comme lindique lannotation @Consumes de len-tte. Par dfaut, chaque mthode (et notamment createNewBook()) consomme du XML et du JSON. Comme nous lavons vu avec le Listing15.3, cette mthode prend un JAXBElement en paramtre noubliez pas que lentit Book est

540

Java EE 6 et GlassFish 3

galement un objet JAXB. Lorsque le XML a t srialis en objet Book (bookJaxb. getValue()), on utilise le gestionnaire dentits pour rendre le livre persistant. Cette mthode renvoie un objet Response reprsentant lURI du nouveau livre. Nous pourrions renvoyer le code dtat 200 OK pour indiquer que la cration a russi mais, selon les principes de REST, la mthode devrait renvoyer un code 201 (ou 204) pour indiquer que la requte a t traite et quelle a produit une nouvelle ressource, dsigne par lURI renvoye dans la rponse.
@POST public Response createNewBook(JAXBElement<Book> bookJaxb) { Book book = bookJaxb.getValue(); em.persist(book); URI bookUri = uriInfo.getAbsolutePathBuilder(). path(book.getId().toString()).build(); return Response.created(bookUri).build(); }

Pour crer une ressource avec ce code, nous avons le choix entre envoyer un document XML ou un document JSON ce dernier est plus lger. La commande cURL utilise une mthode POST en lui passant les donnes dans un format JSON qui doit respecter lassociation JSON/XML utilise par Jersey (noubliez pas que le XML est son tour obtenu partir de lobjet Book laide des rgles JAXB):
curl -X POST --data-binary "{ \"title\":\"H2G2\", \"description\":\"Scifi IT book\", \"illustrations\":\"false\",\"isbn\":\"134-234\", \"nbOfPage\":\"241\", \"price\":\"24.0\" } " -H "Content-Type: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books v

Le mode verbeux de cURL (activ par loption -v) affiche la requte et la rponse HTTP. Vous pouvez voir dans la rponse lURI de la ressource livre qui a t cre (son identifiant est 601):
> > > > > > > < < < < < < POST /chapter15-resource-1.0/rs/books HTTP/1.1 User-Agent: curl/7.19.0 (i586-pc-mingw32msvc) libcurl/7.19.0 zlib/1.2.3 Host: localhost:8080 Accept: */* Content-Type: application/json Content-Length: 127 HTTP/1.1 201 Created X-Powered-By: Servlet/3.0 Server: GlassFish/v3 Location: http://localhost:8080/chapter15-resource-1.0/rs/books/601 Content-Type: application/xml Content-Length: 0

Chapitre 15

Services web REST 541

Obtention dun livre par son identifiant

Pour rcuprer une ressource livre par son identifiant, la requte doit porter sur une URI de la forme /books/{identifiant du livre}. Lidentifiant sert de paramtre pour retrouver le livre dans la base de donnes. Avec une requte GET adresse /books/601, la mthode getBookById() renverra une reprsentation XML ou JSON du livre qui a lidentifiant 601:
@GET @Path("{id}/") public Book getBookById(@PathParam("id") Long id) { Book book = em.find(Book.class, id); return book; }

Lannotation @Path indique le sous-chemin dans le chemin dj indiqu au niveau de la classe. La syntaxe {id} lie llment de lURL au paramtre de la mthode. La commande cURL suivante permet daccder la ressource601 et dobtenir sa reprsentation JSON:
curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books/601 {"description":"Scifi IT book","illustrations":"false", "isbn":"1234-234","nbOfPage":"241","price":"24.0","title":"H2G2"}

En changeant simplement la valeur de len-tte Accept, le mme code permet dobtenir la reprsentation XML du livre 601:
curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-resource-1.0/rs/books/601 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <book><description>Scifi IT book</description> <illustrations>false</illustrations><isbn>1234-234</isbn> <nbOfPage>241</nbOfPage><price>24.0</price><title>H2G2</title></book>

Suppression dun livre

La mthode deleteBook() a le mme format que celui de getBookById() car elle utilise un sous-chemin et un identifiant de livre en paramtre la seule diffrence est la requte HTTP utilise. Ici, nous nous servons de la mthode DELETE, qui supprime le contenu associ lURL indique:
@DELETE @Path("{id}/") public void deleteBook(@PathParam("id") Long id) { Book book = em.find(Book.class, id); em.remove(book); }

542

Java EE 6 et GlassFish 3

Avec le mode verbeux de cURL, on voit la requte DELETE qui est envoye et le code dtat 204 No Content qui apparat dans la rponse pour indiquer que la ressource nexiste plus:
curl -X DELETE http://localhost:8080/chapter15-resource 1.0/rs/books/61 -v > DELETE /chapter15-resource-1.0/rs/books/61 HTTP/1.1 > User-Agent: curl/7.19.0 (i586-pc-mingw32msvc) libcurl/7.19.0 zlib/1.2.3 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 204 No Content < X-Powered-By: Servlet/3.0 < Server: GlassFish/v3

Configuration avec web.xml

Avant de dployer la classe BookResource et lentit Book, nous devons enregistrer Jersey dans le fichier web.xml (voir Listing15.15) afin que les requtes envoyes au chemin /rs soient interceptes par Jersey. Le paramtre com.sun.jersey.config. property.packages indiquera ce dernier o rechercher les classes Java annotes par JAX-RS.
Listing15.15: Fichier web.xml dclarant Jersey
<?xml version=1.0 encoding=UTF-8?> <web-app version="2.5" 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"> <servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name> com.sun.jersey.config.property.packages </param-name> <param-value>com.apress.javaee6.chapter15</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>

Chapitre 15

Services web REST 543

<servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/rs/*</url-pattern> </servlet-mapping> </web-app>

Le descripteur de dploiement associe les requtes au motif dURL /rs/*, ce qui signifie que toutes les URL commenant par /rs/ seront traites par Jersey. Nos exemples avec cURL ont, bien sr, manipul des URL commenant par /rs:
curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books curl -X DELETE http://localhost:8080/chapter15-resource 1.0/rs/books/61

Compilation et assemblage avec Maven

Lapplication web doit tre compile et dploye dans un fichier war (<packaging>war</packaging>). Le fichier pom.xml du Listing15.16 dclare toutes les dpendances ncessaires la compilation du code (jsr311-api, javax.ejb et javax.persistence). Noubliez pas que JAXB fait partie de JavaSE et quil ny a donc pas besoin dajouter cette dpendance.
Listing15.16: Le fichier pom.xml de Maven pour compiler et assembler lapplication web
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.apress.javaee6</groupId> <artifactId>chapter15-resource</artifactId> <packaging>war</packaging> <version>1.0</version> <name>Chapter 15 - REST Resource</name> <parent> <groupId>com.apress.javaee6</groupId> <artifactId>chapters</artifactId> <version>1.0</version> </parent> </dependencies> <dependency> <groupId>javax.ws.rs</groupId>

544

Java EE 6 et GlassFish 3

<artifactId>jsr311-api</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version3.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>

Pour compiler et assembler les classes, ouvrez une fentre de commandes et placez-vous dans le rpertoire contenant le fichier pom.xml, puis entrez la commande suivante:
mvn package

Le rpertoire target devrait dsormais contenir un fichier chapter15-resource1.0.war. Ouvrez-le et vous constaterez que les fichiers Book.class et BookResource. class se trouvent dans le rpertoire WEB-INF\classes. Ce fichier war contient galement les fichiers web.xml et persistence.xml.
Dploiement dans GlassFish

Une fois le code assembl et lorsque GlassFish et Derby sexcutent, nous pouvons dployer le fichier war laide de la commande asadmin. Ouvrez une fentre de

Chapitre 15

Services web REST 545

commande, placez-vous dans le rpertoire target o se trouve le fichier chapter15resource-1.0.war et lancez la commande suivante:
asadmin deploy chapter15-resource-1.0.war

Si le dploiement russit, la commande suivante devrait afficher le nom et le type du composant dploy:
asadmin list-components chapter15-resource-1.0 <ejb, web>

Excution de lexemple

Lorsque lapplication a t dploye, vous pouvez utiliser cURL en ligne de commande pour crer des ressources livres laide de requtes POST ou obtenir des ressources spcifiques avec des requtes GET. Pour supprimer des livres, utilisez la requte DELETE:
// Cration dun livre curl -X POST --data-binary "{ \"title\":\"H2G2\", \"description\":\"Scifi IT book\", \"illustrations\":\"false\",\"isbn\":\"134-234\", \"nbOfPage\":\"241\", \"price\":\"24.0\" }" -H "Content-Type: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books // Rcupration de tous les livres curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books // Rcupration du livre 601 au format JSON curl -X GET -H "Accept: application/json" http://localhost:8080/chapter15-resource-1.0/rs/books/601 {"description":"Scifi IT book","illustrations":"false", "isbn":"1234-234","nbOfPage":"241","price":"24.0","title":"H2G2"} // Rcupration du livre 601 au format XML curl -X GET -H "Accept: application/xml" http://localhost:8080/chapter15-resource-1.0/rs/books/601 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <book><description>Scifi IT book</description> <illustrations>false</illustrations><isbn>1234-234</isbn> <nbOfPage>241</nbOfPage><price>24.0</price><title>H2G2</title> </book> // Suppression du livre 601 curl -X DELETE http://localhost:8080/chapter15-resource 1.0/rs/books/601

546

Java EE 6 et GlassFish 3

Rsum
Le chapitre prcdent a prsent les services web SOAP. Ceux-ci utilisent une enveloppe spciale, sont dcrits avec WSDL et peuvent tre invoqus par diffrents protocoles, dont HTTP. Ce chapitre a dcrit les services web REST, et vous connaissez maintenant la diffrence entre une implmentation de JAX-WS et JAX-RS. Nous avons commenc par prsenter REST. laide de verbes simples (GET, POST, PUT, etc.) et dune interface uniforme, nous avons vu comment accder nimporte quelle ressource dploye sur le Web. Puis nous nous sommes intresss HTTP, un protocole reposant sur un mcanisme de requtes/rponses utilisant une ngociation pour choisir le type de contenu appropri en rponse une requte. La mise en cache permet doptimiser le trafic rseau grce des requtes conditionnelles et des en-ttes ETag. Nous avons vu que REST pouvait tirer parti de cette optimisation car, comme lui, elle repose sur HTTP. JAX-RS est une API Java fournie avec Java EE 6 qui simplifie le dveloppement des services web REST. laide dannotations, nous pouvons dfinir le chemin et les sous-chemins permettant daccder une ressource, extraire diffrents types de paramtres ou traduire du code Java en mthodes HTTP (@GET, @POST, etc.). Lorsque lon dveloppe des services web REST, il est important de raisonner en termes de ressources, dtudier la faon dont elles sont relies et de savoir grer leur tat avec HTTP.

Index
Symboles
@Access, annotation 94 @AccessTimeout, annotation 238 @ActivationConfigProperty, annotation 448, 449, 453, 454, 459 @AfterClass, annotation 69 @ApplicationException, annotation 295, 296 @ApplicationScoped, annotation 393, 394, 395 @AroundInvoke, annotation 275, 277, 279, 280, 281 @Asynchronous, annotation 252 @AttributeOverride, annotation 133 @Basic, annotation 86 @Before, annotation 69 @BeforeClass, annotation 69 @CollectionTable, annotation 96 @Column, annotation 57, 61, 87 @ConcurrencyManagement, annotation 236 @Consumes, annotation 524, 528, 529, 531, 533, 534, 539 @Context, annotation 524, 534, 535, 539 @CookieParam, annotation 527, 528 @DeclareRoles, annotation 301, 302, 303, 304, 537 @DefaultValue, annotation 528, 529 @DELETE, annotation 525, 526, 533, 541 @DenyAll, annotation 301, 302, 303, 304, 537 @DependsOn, annotation 235, 267 @DiscriminatorColumn, annotation 129 @DiscriminatorValue, annotation 130 @EJB, annotation 203, 249, 250, 266, 268, 315, 321, 322, 452 @ElementCollection, annotation 96 @Embeddable, annotation 102, 103 @Embedded, annotation 102 @EmbeddedId, annotation 83 @Entity, annotation 57, 61, 74 <entity-listener>, marqueur 197 @EntityListeners, annotation 193 @Enumerated, annotation 90 @ExcludeClassInterceptors, annotation 278, 279, 281, 282 @ExcludeDefaultInterceptors, annotation 281, 282 @ExcludeDefaultListeners, annotation 197 @ExcludeSuperclassListeners, annotation 195 @FacesConverter, annotation 406, 407 @FacesValidator, annotation 409 @FormParam, annotation 527, 528 @GeneratedValue, annotation 57, 61, 81 @GET, annotation 523, 524, 525, 526, 527, 528, 533, 534, 535, 541, 546 @HEAD, annotation 533 @HeaderParam, annotation 527, 528 @Id, annotation 57, 61, 74, 81 @IdClass, annotation 84 @Inheritance, annotation 127 @Interceptors, annotation 277, 278, 279, 280, 281, 282 @JoinColumn, annotation 114 @JoinTable, annotation 116 @Lob, annotation 87 @Local, annotation 241 @LocalBean, annotation 245 @Lock, annotation 237 @ManagedBean, annotation 314, 315, 321, 322, 391, 392, 393, 394, 395, 397, 398, 399 @ManagedProperty, annotation 392, 395, 396 @ManytoMany, annotation 119 @MapKeyColumn, annotation 98 @MappedSuperclass, annotation 137 @MatrixParam, annotation 527, 528 @MessageDriven, annotation 447, 448, 449, 453, 454, 459

548

Java EE 6 et GlassFish 3

@NamedNativeQuery, annotation 179 @NamedQuery, annotation 59, 61, 176 @NoneScoped, annotation 394 @OnetoMany, annotation 115 @OneToOne, annotation 112, 156 @OneWay, annotation 487, 490 @OPTIONS, annotation 533 @OrderBy, annotation 123 @OrderColumn, annotation 124 @Path, annotation 523, 524, 525, 526, 527, 528, 533, 534, 535, 536, 539, 540, 541, 543 @PathParam, annotation 526, 527, 533, 536, 537, 541, 542, 543 @PermitAll, annotation 301, 303, 537 @PersistenceContext, annotation 148, 244, 249, 250, 266, 268 @PersistenceUnit, annotation 249 <persistence-unit-defaults>, marqueur 197 <persistence-unit-metadata>, marqueur 197 @PostActivate, annotation 270, 271, 272, 278, 279 @POST, annotation 524, 525, 529, 530, 533, 540, 546 @PostConstruct, annotation 266, 268, 269, 270, 271, 272, 278, 279, 280, 282, 394, 395, 452, 454, 493, 537 @PostLoad, annotation 190 @PostPersist, annotation 189 @PostRemove, annotation 189 @PostUpdate, annotation 189 @PreDestroy, annotation 269, 270, 271, 272, 278, 279, 280, 282, 396, 397, 452, 454, 493, 537 @PrePassivate, annotation 270, 271, 272, 278, 279 @PrePersist, annotation 189 @PreRemove, annotation 189 @PreUpdate, annotation 189 @Produces, annotation 523, 524, 528, 531, 534, 535, 536, 539 @Provider, annotation 531, 536, 537 @QueryParam, annotation 527, 528, 529 @Remote, annotation 216, 241 @Remove, annotation 232, 269, 270, 271, 272 @RequestScoped, annotation 321, 322, 392, 394 @Resource, annotation 249, 250, 266, 268, 293, 297, 298, 306, 431, 432, 438, 440, 441, 451, 452, 453, 454, 458, 493, 494, 495

@RolesAllowed, annotation 301, 302, 303, 304, 305, 537 @RunAs, annotation 301, 304, 305, 537 @Schedule, annotation 257, 260 @SecondaryTable, annotation 79 @SessionScoped, annotation 393 @Singleton, annotation 234, 267 @SOAPBinding, annotation 487 @SOAPMessageHandler, annotation 487 @Startup, annotation 235, 267 @Stateful, annotation 232, 267 @StatefulTimeout, annotation 232 @Stateless, annotation 203, 228, 229, 267, 273, 274, 277, 278, 279, 280, 281, 282, 497, 498, 524, 539 @Table, annotation 78 @Temporal, annotation 89 @Test, annotation 69 @Timeout, annotation 257 @TransactionAttribute, annotation 292, 293, 302, 456 @TransactionManagement, annotation 297, 302 @Transient, annotation 90, 480 @Version, annotation 182 @ViewScoped, annotation 393 @WebMethod, annotation 487, 488, 489, 490, 491, 498 @WebParam, annotation 487, 489, 490, 491, 498 @WebResult, annotation 487, 489, 490, 498 @WebService, annotation 486, 488, 489, 490, 491, 497, 498 @WebServiceRef, annotation 249, 250, 495, 501, 505 @XmlAccessorType, annotation 478, 479 @XmlAttribute, annotation 478, 479, 480 @XmlElement, annotation 478, 479, 480 @XmlEnum, annotation 480 @XmlEnumValue, annotation 480 @XmlID, annotation 480 @XmlIDREF, annotation 480 @XmlList, annotation 480 @XmlMimeType, annotation 480 @XmlNs, annotation 480 @XmlRootElement, annotation 472, 478, 480, 481, 497, 498, 538 @XmlSchema, annotation 479, 480

Index 549

@XmlTransient, annotation 480 @XmlType, annotation 480 @XmlValue, annotation 480

A
ABS(), fonction JPQL 167 Accept, en-tte HTTP 510, 513, 529, 534, 540, 541, 542, 543, 545 actionSource (JSF) 377 Activation 230, 268 AND, oprateur JPQL 168 appclient, programme 221, 462, 505 application, objet implicite 381 applicationScope, objet implicite 381 asadmin commande 48, 329 programme 221, 430, 461, 499, 500 assert, mot-cl 38 AsyncResult, classe 253 attribute action JSP 345 (JSF) 376 AVG(), fonction JPQL 167

B
Backing bean 386, 391, 392, 404 BETWEEN, oprateur JPQL 168 body action JSP 345 (JSF) 371 bundle (JSTL) 352

C
Cache, interface 163 Cardinalit d'une relation 107 cascade, attribut d'annotation 161 catch (JSTL) 350 CDATA, section XML 344 choose (JSTL) 350, 355 Classe de cl primaire 82 clear(), mthode d'EntityManager 151, 158, 189

column (JSF) 369 commandButton composant JSF 396, 397, 398, 413, 416 (JSF) 363 commandLink composant JSF 393, 396, 397 (JSF) 363 component (Facelets) 359 objet implicite 381 Composants graphiques de JSF 324 compositeComponent, objet implicite 381 composition (Facelets) 359 (JSF) 372 CONCAT(), fonction JPQL 167 ConnectionFactory, classe 430, 432, 433, 438, 439, 440, 441, 451, 452, 454, 455, 458, 459, 461, 462 Connection, interface 431, 432, 433, 434, 438, 439, 440, 441, 442, 454, 455, 458, 459 containsIgnoreCase (JSTL) 358 contains (JSTL) 358 contains(), mthode d'EntityManager 151, 158, 188 Content-Type, en-tte HTTP 513, 534, 540, 542, 545, 546 convertDateTime, composant JSF 405 Converter composant JSF 407 interface 406 ConverterException, exception 406 convertNumber, composant JSF 405 cookie, objet implicite 381 CORBA (Common Object Request Broker Architecture) 469 COUNT(), fonction JPQL 167 createNamedQuery(), mthode d'EntityManager 172, 177 createNativeQuery(), mthode d'EntityManager 178, 179 createQuery(), mthode d'EntityManager 172, 174 cURL, programme 511, 537, 538, 540, 541, 542, 543, 545 CURRENT_DATE(), fonction JPQL 167 CURRENT_ TIME(), fonction JPQL 167 CURRENT_TIMESTAMP(), fonction JPQL 167

550

Java EE 6 et GlassFish 3

D
dataTable (JSF) 369 dateParam (JSTL) 354 dblook, programme 40 DCOM (Distributed Component Object Model) 469 debug (Facelets) 359 dclaration (JSP) 343 decorate (Facelets) 359 (JSF) 372 define (Facelets) 359 (JSF) 372 DELETE, instruction JPQL 170 Destination, interface 431, 435, 453, 454 detach(), mthode d'EntityManager 158 Direction d'une relation 106 DISTINCT, clause JPQL 166 DOM, API 13

exception d'application 294 systme 294 executeUpdate(), mthode de Query 173 expression (JSP) 344

F
faces-config.xml, fichier 311, 318, 328, 390, 391, 392, 395, 396, 398, 399, 400, 401, 406, 407, 409 FacesContext classe 387, 389, 401, 406, 408, 409 objet implicite 381, 389 FacesMessage, classe 389, 401, 402, 408, 409 FacesServlet, classe 386, 387, 388, 390, 391, 392, 398, 399 facet (JSF) 369, 376 find(), mthode d'EntityManager 151, 154, 188 flush(), mthode d'EntityManager 151, 156 forEach (JSTL) 350, 355 formatDate (JSTL) 352 formatNumber (JSTL) 352 form (JSF) 371 forTokens (JSTL) 350 forward, action JSP 345 fragment (Facelets) 359 (JSF) 372 FROM, clause JPQL 167 Future, interface 253

E
EAI (Enterprise Application Integration) 465 ear archive 15 fichier archive 208 EclipseLink, framework 54 ECMA (European Computer Manufacturers Association) 339 editableValueHolder (JSF) 377 EJBContext, interface 247 EJBException, exception 291, 294 ejb-jar.xml, fichier 208, 249, 281, 292, 297 EJBTransactionRequiredException, exception 291 element, action JSP 345 endsWith (JSTL) 358 EntityManager classe 62 gestionnaire d'entits 59 interface 142, 146 EntityManagerFactory, classe 62, 144 EntityNotFoundException, exception 154 EntityTransaction, classe 152 escapeXml (JSTL) 358 ETag, en-tte HTTP 515, 546

G
getProperty, action JSP 345 getReference(), mthode d'EntityManager 151, 154 getResultList(), mthode de Query 173 getSingleResult(), mthode de Query 173 graphicImage (JSF) 368 GROUP BY, clause JPQL 169

H
HAVING, clause JPQL 170 headerValues, objet implicite 381

Index 551

head (JSF) 371 Hibernate, ORM 53 HttpHeaders, classe 534 HTTP, protocole 14 HTTPS, protocole 14

I
Idempotence 512, 513 IDL, protocole 14 if (JSTL) 350, 355 If-Match, en-tte HTTP 515, 516 If-Modified-Since, en-tte HTTP 515, 516 If-None-Match, en-tte HTTP 516 If-Range, en-tte HTTP 516 If-Unmodified-Since, en-tte HTTP 516 ij, programme 40, 66 implementation (JSF) 376 import (JSTL) 350 include action JSP 345 directive JSP 342 (Facelets) 359 INDEX(), fonction JPQL 167 indexOf (JSTL) 358 InitialContext, classe 423, 430, 431 initParam, objet implicite 382 IN, oprateur JPQL 168 inputHidden (JSF) 365 inputSecret (JSF) 365 inputTextarea (JSF) 365 inputText (JSF) 365 insert (Facelets) 359 (JSF) 372 insertChildren (JSF) 376 insertFacet (JSF) 376 interface (JSF) 376 InvocationContext, interface 275, 276, 277, 279, 280 IS EMPTY, oprateur JPQL 168 IS NULL, oprateur JPQL 168

JACC, API 14 JAF, API 13 jar, archive 15 JavaMail, API 13 javax.faces.component.UIComponent (JSF) 363 javax.faces.convert convertisseurs JSF 314 paquetage 404 javax.faces.validator, paquetage 407 javax.persistence, paquetage 57 javax.servlet.Servlet, interface 387 JAXBContext, classe 476, 477, 478 JAXP, API 13 JAX-RPC, API 14 JAX-RS, API 14 JAX-WS, API 14 JCA, API 13 JDBC, API 52 Jersey, implmentation 46 JMS, API 13 JMSException, exception 454, 456, 458, 459, 460 JMX, API 14, 16 JNDI, API 13 join (JSTL) 358 JPA, API 13 JRMP, protocole 14 jsf.js, fichier 412 JTA, API 13, 53

L
Last-Modified, en-tte HTTP 515 LENGTH(), fonction JPQL 167 length (JSTL) 358 LengthValidator, validateur JSF 314 Lifecycle, classe 387 LIKE, oprateur JPQL 168 lock(), mthode d'EntityManager 181 LOWER(), fonction JPQL 167

J
JAAS, API 13

M
MANIFEST.MF, fichier 502, 505 Marshaller, classe 476, 477, 478, 479

552

Java EE 6 et GlassFish 3

MAX(), fonction JPQL 167 MEMBER, oprateur JPQL 168 merge(), mthode d'EntityManager 151, 160, 189 message composant JSF 401, 402 (JSF) 370 (JSTL) 352 MessageBodyReader, interface 530, 531, 532, 533 MessageBodyWriter, interface 530, 531, 532 MessageConsumer, interface 424, 425, 432, 433, 438, 440, 441, 442 MessageDrivenContext, interface 451, 455 MessageListener, interface 439, 441, 447, 448, 449, 452, 453, 454, 455, 459 MessageProducer, interface 424, 437, 438, 443, 444, 445, 446, 454, 455, 458, 459 Metro implmentation 45 de rfrence 471 MIN(), fonction JPQL 167 MOD(), fonction JPQL 167 Mojarra implmentation 46 de JSF 2.0 318 MOM (Middleware Orient Messages) 419 MVC (Modle-Vue-Contrleur) 311 mvn commande Maven 329 programme 65, 499, 504

out (JSTL) 350, 355 outputLabel (JSF) 366 outputLink (JSF) 366 outputScript composant JSF 412, 414, 415 (JSF) 371 outputStylesheet (JSF) 371 outputText (JSF) 366

P
page, directive JSP 342 panelGrid (JSF) 369 panelGroup (JSF) 369 param action JSP 345 (Facelets) 359 (JSTL) 350, 352, 354, 355 objet implicite 382 paramValues, objet implicite 382 parseDate (JSTL) 352 parse (JSTL) 355 parseNumber (JSTL) 352 PartialViewContext, classe 413 Passivation 230, 268 Persistence, classe 62, 148 persistence.xml, fichier 63, 145, 197, 538, 539, 544 persist(), mthode d'EntityManager 151, 188 PessimisticLockException, exception 185 plugin, action JSP 345 pom.xml, fichier 64, 328, 498, 499, 502, 538, 543, 544 Principal 305, 306 Programmation oriente aspect 272 Propritaire d'une relation 107 Providers, classe 534

N
NOT, oprateur JPQL 168

O
OASIS (Organization for the Advancement of Structured Information Standards) 469 OpenMQ (Open Message Queue) 423 implmentation 46 OptimisticLockException, exception 183 ORDER BY, clause JPQL 169 ORM, Object-Relational Mapping 52 OR, oprateur JPQL 168 otherwise (JSTL) 350, 355

Q
Query interface 172 (JSTL) 353 Queue classe 424 interface 427, 431

Index 553

QueueConnectionFactory, interface 430, 431 QueueConnection, interface 433, 434 QueueReceiver, interface 427, 439 QueueSender, interface 437, 438 QueueSession, interface 426, 434, 435

R
redirect (JSTL) 350 refresh(), mthode d'EntityManager 151, 156, 157, 188 RegexValidator, validateur JSF 314 remove (Facelets) 359 (JSTL) 350 remove(), mthode d'EntityManager 154, 155, 188 repeat (Facelets) 359 replace (JSTL) 358 request classe 534 objet implicite 382 requestEncoding (JSTL) 352 requestScope, objet implicite 382 Requtes nommes 58 resource, objet implicite 382 REST, protocole 14 RMI-IIOP, protocole 14 RMI (Remote Method Invocation) 469 RPC (Remote Procedure Call) 469

S
SAX, API 13 ScheduleExpression, classe 257 schemaGen, programme 477 scriptlet (JSP) 344 SecurityContext, classe 534 SecurityException, exception 306, 307 selectBooleanCheckbox (JSF) 366 SELECT, clause JPQL 165 selectManyCheckbox (JSF) 366 selectManyListbox (JSF) 367 selectManyMenu (JSF) 367 selectOneListbox (JSF) 367

selectOneMenu (JSF) 367 selectOneRadio (JSF) 367 Serializable, interface 189, 268 session interface 424, 425, 426, 427, 432, 433, 434, 438, 440, 441, 445, 450, 453, 454, 458 objet implicite 382 SessionContext classe 293, 294, 297, 305, 306 interface 247 sessionScope, objet implicite 382 setDataSource (JSTL) 353 setFirstResult(), mthode de Query 174 set (JSTL) 350, 355 setLocale (JSTL) 352 setLockMode(), mthode de Query 174 setMaxResults(), mthode de Query 174 setParameter(), mthode de Query 173, 174 setProperty, action JSP 345 setTimeZone (JSTL) 352 SGML (Standard Generalized Markup Language) 332 SIZE(), fonction JPQL 167 SOAP, protocole 14 SOA (Service-Oriented Architecture) 465 split (JSTL) 358 SQRT(), fonction JPQL 167 startsWith (JSTL) 358 StAX, API 13 Struts, framework web 317 substringAfter (JSTL) 358 substringBefore (JSTL) 358 SUBSTRING(), fonction JPQL 167 substring (JSTL) 358 SUM(), fonction JPQL 167 SVG (Scalable Vector Graphics) 313 synchronized, mot-cl 238, 393 sysinfo, programme 40

T
Table primaire 79 secondaire 79 taglib, directive JSP 342

554

Java EE 6 et GlassFish 3

Timer, classe 262 TimerService, interface 261 timeZone (JSTL) 352 toLowerCase (JSTL) 358 TopicConnectionFactory, interface 430, 431 TopicConnection, interface 433, 434 Topic, interface 431, 438, 440, 441, 447, 449, 453, 454, 455 TopicPublisher, interface 437, 438, 439 TopicSession, interface 426, 434, 435 TopicSubscriber, interface 427, 439, 440 TopLink, ORM 53 toUpperCase (JSTL) 358 Transaction (JSTL) 353 transform (JSTL) 355 TRIM(), fonction JPQL 167 trim (JSTL) 358 Types MIME 513

V
validate, message SOAP 472, 473, 481, 482, 483, 485, 489, 490, 491, 495, 496, 502, 504 validateResponse, message SOAP 482, 483, 484, 485, 504 validateResponse, rponse SOAP 481 validator composant JSF 409 interface 408, 409 ValidatorException, exception 408 valueHolder (JSF) 377 view, objet implicite 382 viewScope, objet implicite 382 volatile, mot-cl 238

W
WebApplicationException, exception 532, 536 WebServiceContex, interface 493 webservices.xml, fichier 499, 500 web.xml, fichier 327, 387, 538, 542, 544 when (JSTL) 350, 355 WHERE, clause JPQL 167 WML (Wireless Markup Language) 313 wsgen, programme 496 wsimport, programme 494, 496, 501, 502, 503, 504, 506

U
UIViewRoot (JSF) 363 Unmarshaller, classe 476, 477 UPDATE instruction JPQL 171 (JSTL) 353 UPPER(), fonction JPQL 167 UriBuilder, classe 534, 535 UriInfo, classe 524, 534, 535, 539 url (JSTL) 350 useBean, action JSP 345 User-Agent, en-tte HTTP 510, 513, 540, 542 UserTransaction, interface 297, 298

X
xjc, programme 477 XMLHttpRequest, objet 410, 411, 412

Java EE 6 et GlassFish 3

Rfrence

Aujourdhui, les applications doivent accder des donnes, appliquer une logique mtier, ajouter des couches de prsentation et communiquer avec des systmes externes. Les entreprises tentent de raliser toutes ces oprations moindre cot, en se servant de technologies standard et robustes supportant des charges importantes. Si vous tes dans cette situation, ce livre est fait pour vous. Il explore les innovations de Java EE 6, la dernire version de la plate-forme Java Enterprise, et examine les diffrentes spcications et la faon de les assembler pour dvelopper des applications. Conu un peu comme un tutoriel approfondi (construction progressive dune application), tous les aspects de la plate-forme sont prsents et illustrs par des exemples que le lecteur peut tester. Vous apprendrez comment associer des objets des bases de donnes avec JPA 2.0, crire une couche mtier transactionnelle avec EJBTM 3.1, ajouter une couche prsentation avec JSFTM 2.0 et interagir avec dautres systmes tels que JMSTM et les services web SOAP et REST. En outre, tous les exemples de ce livre ont t crits spciquement pour GlassFish 3, la toute dernire version de limplmentation de rfrence pour la plate-forme Java EE. Code source tlchargeable sur www.pearson.fr Dveloppement web

TABLE DES MATIRES

Tour dhorizon de Java EE 6 Persistance en Java ORM : Object-Relational Mapping Gestion des objets persistants Mthodes de rappel et couteurs Enterprise Java Beans Beans de session et service timer Mthodes de rappel et intercepteurs Transactions et scurit JavaServer Faces Pages et composants Traitement et navigation Envoi de messages Services web SOAP Services web REST

propos de lauteur : Antonio Goncalves est un architecte logiciel. Membre expert de plusieurs JSR (Java Specication Requests) et co-fondateur du JUG (Java User Group) de Paris, il participe aux grandes confrences internationales consacres Java EE, dont JavaOne, le Server Side Symposium, Devoxx et Jazoon. Son premier livre Java EE 5 est paru en 2007 aux ditions Eyrolles.

Niveau : Tous niveaux Conguration : Multiplate-forme

Pearson Education France 47 bis, rue des Vinaigriers 75010 Paris Tl. : 01 72 74 90 00 Fax : 01 42 05 22 17 www.pearson.fr

ISBN : 978-2-7440-4157-0

customer 27921 at Fri Mar 11 19:07:20 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

You might also like