Vous êtes sur la page 1sur 341

Introduction Java EE 5 avec Netbeans 6.

8 et le serveur d'applications Glassfish V3

serge.tahe at istia.univ-angers.fr juin 2010

http://tahe.developpez.com/java/javaee

1/341

INTRODUCTION
Ce document reprend un prcdent document crit en 2007 et intitul "Introduction Java EE avec Netbeans 5.5.1". Celui-ci intitul "Introduction Java EE 5 avec Netbeans 6.8 et le serveur Glassfish v3" adapte le document prcdent Netbeans 6.8. Les copies d'cran ne convenaient plus et Netbeans 6.8 amne avec lui des assistants qui n'existaient pas avec Netbean 5.5.1. En-dehors du changement d'IDE, les deux documents sont semblables. Java EE signifie Java Enterprise Edition. J2EE (Java 2 Enterprise Edition) tait le terme prcdent. J2EE dsigne les technologies Java utilises pour crer des applications d'entreprise avec le JDK 1.4 ou antrieur. En mme temps que le JDK 1.5 amenait de nombreuses nouveauts dans le langage Java, Sun introduisait de nouvelles technologies s'appuyant sur ce langage amlior afin de remdier des lacunes de ces mmes technologies dans J2EE. Le terme Java EE 5 a alors t utilis pour dsigner l'ensemble des technologies qui concourent crer une application d'entreprise avec la plate-forme Java. Au moment de la mise jour de ce document, la dernire version de Java EE est Java EE 6. Les livres d'Antonio Goncalves :

Java EE 5 aux ditions Eyrolles Beginning Java EE 6 Platform with Glassfish 3 aux ditions Apress

sont d'excellents livres pour dcouvrir les technologies de Java EE 5 et Java EE 6. Toutes les technologies importantes de Java EE y sont passes en revue dans le contexte d'tudes de cas ralistes. L'auteur a un site [http://www.antoniogoncalves.org] que le lecteur est invit visiter. Le document prsent tudie certaines des technologies de Java EE 5. Nous y crons une application basique trois couches [prsentation, mtier, accs aux donnes] dcline en plusieurs versions : Une application web avec les technologies suivantes : JavaServer Faces : pour la couche web Ejb3 ou Spring : pour la couche mtier Ejb3 ou Spring, Jpa/Hibernate, Jpa/EclipseLink : pour crer diffrentes couches d'accs aux donnes Une application client / serveur avec les technologies suivantes : Swing : pour la couche graphique cliente avec un support Spring Ejb3 ou service web : pour la couche serveur Certaines technologies Java EE ne sont pas prsentes telles les MDB (Message Driven Bean) ou les Ejb3 stateful. Pour les dcouvrir, on lira les livres d'Antonio Goncalves. Il existe d'autres technologies Open Source disponibles pour crer des applications trois couches. Une tandem trs populaire est Spring (http://www.springframework.org/) / Hibernate (http://www.hibernate.org/). Afin de permettre au lecteur de comparer les technologies Ejb3 et Spring, l'application prcdente a des versions o Spring remplace les Ejb3. Le document a deux parties bien distinctes :

la premire partie est un TD utilis en 5ime anne de l'cole d'ingnieurs ISTIA de l'universit d'Angers [http://www.istia.univ-angers.fr]. Un TD est un Travail Dirig. Ce TD dcrit l'application construire, les technologies Java utiliser, les endroits o trouver de l'information. La solution propose est trs cadre. Le TD pose des questions dont il ne donne pas les rponses. C'est l'tudiant de les trouver. la seconde partie est un cours sur JSF (JavaServer Faces). Il sert d'appui pour crire la couche web de l'application exemple.

L'apprentissage Java EE propos ici ncessite un investissement du lecteur estim entre 50 et 100 heures. Le document contient beaucoup de code rendant possible le copier / coller. Par ailleurs, tous les projets Netbeans sont dcrits dans le dtail. Globalement, le document donne les squelettes des solutions et il est demand l'tudiant d'en donner certains dtails. Le document peut tre utile mme quelqu'un ne pouvant ou ne voulant pas s'investir autant. On peut s'intresser uniquement aux architectures dcrites et dlaisser la partie code qui fait l'objet des questions. Pour dvelopper et excuter l'application, nous utilisons l'IDE Netbeans 6.8. Netbeans est un produit assez lourd : prvoir 1 Go de Ram pour travailler confortablement. On peut le tlcharger l'url [http://www.netbeans.org/].

http://tahe.developpez.com/java/javaee

2/341

Le document fait rfrence aux cours suivants : 1. 2. Persistance Java 5 par la pratique : [http://tahe.developpez.com/java/jpa] - donne les outils pour construire la couche d'accs aux donnes avec Jpa (Java Persistence Api) Introduction au langage Java [http://tahe.developpez.com/java/cours] - pour les dbutants

Ces supports de cours sont par la suite rfrencs [ref1] et [ref2]. Serge Tah, juin 2010.

http://tahe.developpez.com/java/javaee

3/341

1 Architecture d'une application Java en couches


Une application java est souvent dcoupe en couches chacune ayant un rle bien dfini. Considrons une architecture courante, celle trois couches :

utilisateur

Couche interface utilisateur [ui] 1

Couche mtier [metier] 2

Couche d'accs aux donnes [dao] 3

Donnes

la couche [1], appele ici [ui] (User Interface) est la couche qui dialogue avec l'utilisateur, via une interface graphique Swing, une interface console ou une interface web. Elle a pour rle de fournir des donnes provenant de l'utilisateur la couche [2] ou bien de prsenter l'utilisateur des donnes fournies par la couche [2]. la couche [2], appele ici [metier] est la couche qui applique les rgles dites mtier, c.a.d. la logique spcifique de l'application, sans se proccuper de savoir d'o viennent les donnes qu'on lui donne, ni o vont les rsultats qu'elle produit. la couche [3], appele ici [dao] (Data Access Object) est la couche qui fournit la couche [2] des donnes pr-enregistres (fichiers, bases de donnes, ...) et qui enregistre certains des rsultats fournis par la couche [2].

Il existe diffrentes possibilits pour implmenter la couche [dao]. Examinons-en quelques-unes :

utilisateur

Couche ui [ui] 1

Couche mtier [metier] 2

Couche d'accs aux donnes [dao] 3

Couche [JDBC]

Base de Donnes

La couche [JDBC] ci-dessus est la couche standard utilise en Java pour accder des bases de donnes. Elle isole la couche [dao] du SGBD qui gre la base de donnes. On peut thoriquement changer de SGBD sans changer le code de la couche [dao]. Malgr cet avantage, l'API JDBC prsente certains inconvnients : toutes les oprations sur le SGBD sont susceptibles de lancer l'exception contrle (checked) SQLException. Ceci oblige le code appelant (la couche [dao] ici) les entourer par des try / catch rendant ainsi le code assez lourd. la couche [dao] n'est pas compltement insensible au SGBD. Ceux-ci ont par exemple des mthodes propritaires quant la gnration automatique de valeurs de cls primaires que la couche [dao] ne peut ignorer. Ainsi lors de l'insertion d'un enregistrement : avec Oracle, la couche [dao] doit d'abord obtenir une valeur pour la cl primaire de l'enregistrement puis insrer celui-ci. avec SQL Server, la couche [dao] insre l'enregistrement qui se voit donner automatiquement une valeur de cl primaire par le SGBD, valeur rendue la couche [dao]. Ces diffrences peuvent tre gommes via l'utilisation de procdures stockes. Dans l'exemple prcdent, la couche [dao] appellera une procdure stocke dans Oracle ou SQL Server qui prendra en compte les particularits du SGBD. Celles-ci seront caches la couche [dao]. Nanmoins, si changer de SGBD n'impliquera pas de rcrire la couche [dao], cela implique quand mme de rcrire les procdures stockes. Cela peut ne pas tre considr comme rdhibitoire. De multiples efforts ont t faits pour isoler la couche [dao] des aspects propritaires des SGBD. Une solution qui a eu un vrai succs dans ce domaine ces dernires annes, est celle d'Hibernate :

Couche d'accs aux donnes [dao] 3

4 Objets image de la BD

Couche [Hibernate] 5

Couche [JDBC] 6

Base de Donnes 7

http://tahe.developpez.com/java/javaee

4/341

La couche [Hibernate] vient se placer entre la couche [dao] crite par le dveloppeur et la couche [Jdbc]. Hibernate est un ORM (Object Relational Mapping), un outil qui fait le pont entre le monde relationnel des bases de donnes et celui des objets manipuls par Java. Le dveloppeur de la couche [dao] ne voit plus la couche [Jdbc] ni les tables de la base de donnes dont il veut exploiter le contenu. Il ne voit que l'image objet de la base de donnes, image objet fournie par la couche [Hibernate]. Le pont entre les tables de la base de donnes et les objets manipuls par la couche [dao] est fait principalement de deux faons : par des fichiers de configuration de type XML par des annotations Java dans le code, technique disponible seulement depuis le JDK 1.5 La couche [Hibernate] est une couche d'abstraction qui se veut la plus transparente possible. L'idal vis est que le dveloppeur de la couche [dao] puisse ignorer totalement qu'il travaille avec une base de donnes. C'est envisageable si ce n'est pas lui qui crit la configuration qui fait le pont entre le monde relationnel et le monde objet. La configuration de ce pont est assez dlicate et ncessite une certaine habitude. La couche [4] des objets, image de la BD est appele "contexte de persistance". Une couche [dao] s'appuyant sur Hibernate fait des actions de persistance (CRUD, create - read - update - delete) sur les objets du contexte de persistance, actions traduites par Hibernate en ordres SQL excuts par la couche Jdbc. Pour les actions d'interrogation de la base (le SQL Select), Hibernate fournit au dveloppeur, un langage HQL (Hibernate Query Language) pour interroger le contexte de persistance [4] et non la BD ellemme. Hibernate est populaire mais complexe matriser. La courbe d'apprentissage souvent prsente comme facile est en fait assez raide. Ds qu'on a une base de donnes avec des tables ayant des relations un--plusieurs ou plusieurs--plusieurs, la configuration du pont relationnel / objets n'est pas la porte du premier dbutant venu. Des erreurs de configuration peuvent conduire des applications peu performantes. Devant le succs des produits ORM, Sun le crateur de Java, a dcid de standardiser une couche ORM via une spcification appele JPA (Java Persistence Api) apparue en mme temps que Java 5. La spcification JPA a t implmente par divers produits : Hibernate, Toplink, EclipseLink, OpenJpa, .... Avec JPA, l'architecture prcdente devient la suivante : 4 Objets image de la BD

Couche d'accs aux donnes [dao] 3

Interface [JPA]

Implmentation JPA [Hibernate / ...] 5

Couche [JDBC] 6

Base de Donnes 7

La couche [dao] dialogue maintenant avec la spcification JPA, un ensemble d'interfaces. Le dveloppeur y a gagn en standardisation. Avant, s'il changeait sa couche ORM, il devait galement changer sa couche [dao] qui avait t crite pour dialoguer avec un ORM spcifique. Maintenant, il va crire une couche [dao] qui va dialoguer avec une couche JPA. Quelque soit le produit qui implmente celle-ci, l'interface de la couche JPA prsente la couche [dao] reste la mme. Dans ce document, nous utiliserons une couche [dao] s'appuyant sur une couche JPA/Hibernate ou JPA/EclipseLink. Par ailleurs nous utiliserons le framework Spring 2.8 pour lier ces couches entre-elles. 4

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface Implmentation JPA [EclipseLink [JPA] / Hibernate] 5

Couche [JDBC] 6

Spring

Le grand intrt de Spring est qu'il permet de lier les couches par configuration et non dans le code. Ainsi si l'implmentation JPA / Hibernate doit tre remplace par une implmentation Hibernate sans JPA, parce que par exemple l'application s'excute dans un environnement JDK 1.4 qui ne supporte pas JPA, ce changement d'implmentation de la couche [dao] n'a pas d'impact sur le code de la couche [mtier]. Seul le fichier de configuration Spring qui lie les couches entre elles doit tre modifi. Avec Java EE 5, une autre solution existe : implmenter les couches [metier] et [dao] avec des Ejb3 (Enterprise Java Bean version 3) :

http://tahe.developpez.com/java/javaee

5/341

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface Implmentation JPA [EclipseLink [JPA] / Hibernate] 5

Couche [JDBC] 6

conteneur Ejb3

Nous verrons que cette solution n'est pas trs diffrente de celle utilisant Spring. L'environnement Java EE5 est disponible au sein de serveurs dits serveurs d'applications tels que Sun Application Server 9.x (Glassfish), Jboss Application Server, Oracle Container for Java (OC4J), ... Un serveur d'applications est essentiellement un serveur d'applications web. Il existe galement des environnements EE 5 dits "stand-alone", c.a.d. pouvant tre utiliss en-dehors d'un serveur d'applications. C'est le cas de JBoss EJB3 ou OpenEJB. Dans un environnement EE5, les couches sont implmentes par des objets appels EJB (Enterprise Java Bean). Dans les prcdentes versions d'EE, les EJB (EJB 2.x) taient rputs difficiles mettre en oeuvre, tester et parfois peu-performants. On distingue les EJB2.x "entity" et les EJB2.x "session". Pour faire court, un EJB2.x "entity" est l'image d'une ligne de table de base de donnes et EJB2.x "session" un objet utilis pour implmenter les couches [metier], [dao] d'une architecture multi-couches. L'un des principaux reproches faits aux couches implmentes avec des EJB est qu'elles ne sont utilisables qu'au sein de conteneurs EJB, un service dlivr par l'environnement EE. Cet environnement, plus complexe mettre en oeuvre qu'un environnement SE (Standard Edition), peut dcourager le dveloppeur faire frquemment des tests. Nanmoins, il existe des environnements de dveloppement Java qui facilitent l'utilisation d'un serveur d'application en automatisant le dploiement des Ejb sur le serveur : Eclipse, Netbeans, JDeveloper, IntelliJ IDEA. Nous utiliserons ici Netbeans 6.8 et le serveur d'application Glassfish v3. Le framework Spring est n en raction la complexit des EJB2. Spring fournit dans un environnement SE un nombre important des services habituellement fournis par les environnements EE. Ainsi dans la partie "Persistance de donnes", Spring fournit les pools de connexion et les gestionnaires de transactions dont ont besoin les applications. L'mergence de Spring a favoris la culture des tests unitaires, devenus plus faciles mettre en oeuvre dans le contexte SE que dans le contexte EE. Spring permet l'implmentation des couches d'une application par des objets Java classiques (POJO, Plain Old/Ordinary Java Object), permettant la rutilisation de ceux-ci dans un autre contexte. Enfin, il intgre de nombreux outils tiers de faon assez transparente, notamment des outils de persistance tels que Hibernate, EclipseLink, Ibatis, ... Java EE5 a t conu pour corriger les lacunes de la spcification EJB2. Les EJB 2.x sont devenus les EJB3. Ceux-ci sont des POJOs tagus par des annotations qui en font des objets particuliers lorsqu'ils sont au sein d'un conteneur EJB3. Dans celui-ci, l'EJB3 va pouvoir bnficier des services du conteneur (pool de connexions, gestionnaire de transactions, ...). En-dehors du conteneur EJB3, l'EJB3 devient un objet Java normal. Ses annotations EJB sont ignores. Ci-dessus, nous avons reprsent Spring et un conteneur EJB3 comme infrastructure (framework) possible de notre architecture multi-couches. C'est cette infrastructure qui dlivrera les services dont nous avons besoin : un pool de connexions et un gestionnaire de transactions. avec Spring, les couches seront implmentes avec des POJOs. Ceux-ci auront accs aux services de Spring (pool de connexions, gestionnaire de transaction) par injection de dpendances dans ces POJOs : lors de la construction de ceux-ci, Spring leur injecte des rfrences sur les services dont il vont avoir besoin. avec le conteneur Ejb3, les couches seront implmentes avec des Ejb. Une architecture en couches implmentes avec des Ejb3 est peu diffrente de celles implmentes avec des POJO instancis par Spring. Nous trouverons beaucoup de ressemblances.

pour terminer, nous prsenterons un exemple d'application web multi-couches : 4

Couche [web] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface Implmentation JPA [EclipseLink [JPA] / Hibernate] 5

Couche [JDBC] 6

Spring ou Ejb3

http://tahe.developpez.com/java/javaee

6/341

2 PAM - Version 1
On se propose dcrire une application console ainsi qu'une application graphique permettant dtablir le bulletin de salaire des assistantes maternelles employes par la "Maison de la petite enfance" d'une commune.

2.1

La base de donnes

Les donnes statiques utiles pour construire la fiche de paie seront places dans une base de donnes que nous dsignerons par la suite dbpam. Cette base de donnes pourrait avoir les tables suivantes : Table EMPLOYES : rassemble des informations sur les diffrentes assistantes maternelles Structure :
SS NOM PRENOM ADRESSE VILLE CODEPOSTAL INDICE

numro de scurit sociale de l'employ - cl primaire nom de l'employ son prnom son adresse sa ville son code postal son indice de traitement - cl trangre sur le champ [INDICE] de la table [INDEMNITES]

Son contenu pourrait tre le suivant :

Table COTISATIONS : rassemble des pourcentages ncessaires au calcul des cotisations sociales Structure :
CSGRDS CSGD SECU RETRAITE

pourcentage : contribution sociale gnralise + contribution au remboursement de la dette sociale pourcentage : contribution sociale gnralise dductible pourcentage : scurit sociale, veuvage, vieillesse pourcentage : retraite complmentaire + assurance chmage

Son contenu pourrait tre le suivant :

Les taux des cotisations sociales sont indpendants du salari. La table prcdente n'a qu'une ligne. Table INDEMNITES : rassemble les lments permettant le calcul du salaire payer.
INDICE BASEHEURE ENTRETIENJOUR REPASJOUR INDEMNITESCP

indice de traitement - cl primaire prix net en euro dune heure de garde indemnit dentretien en euro par jour de garde indemnit de repas en euro par jour de garde indemnit de congs pays. C'est un pourcentage appliquer au salaire de base.

Son contenu pourrait tre le suivant :

http://tahe.developpez.com/java/javaee

7/341

On notera que les indemnits peuvent varier d'une assistante maternelle une autre. Elles sont en effet associes une assistante maternelle prcise via l'indice de traitement de celle-ci. Ainsi Mme Marie Jouveinal qui a un indice de traitement de 2 (table EMPLOYES) a un salaire horaire de 2,1 euro (table INDEMNITES).

2.2

Mode de calcul du salaire d'une assistante maternelle

Nous prsentons maintenant le mode de calcul du salaire mensuel d'une assistante maternelle. Il ne prtend pas tre celui utilis dans la ralit. Nous prenons pour exemple, le salaire de Mme Marie Jouveinal qui a travaill 150 h sur 20 jours pendant le mois payer. Les lments suivants sont pris en compte :
[TOTALHEURES]: total des heures travailles dans le mois [TOTALJOURS]: total des jours travaills dans le mois [TOTALHEURES]=150 [TOTALJOURS]= 20

Le salaire de base de l'assistante maternelle est donn par la formule suivante : Un certain nombre de cotisations sociales doivent tre prleves sur ce salaire de base :

[SALAIREBASE]=([TOTALHEURES]*[BAS EHEURE])*(1+[INDEMNITESCP]/100)

[SALAIREBASE]=(150*[2.1])*(1+0.15 )= 362,25

Contribution sociale gnralise et contribution au remboursement de la dette sociale : [SALAIREBASE]*[CSGRDS/100] Contribution sociale gnralise dductible : [SALAIREBASE]*[CSGD/100] Scurit sociale, veuvage, vieillesse : [SALAIREBASE]*[SECU/100] Retraite Complmentaire + AGPF + Assurance Chmage : [SALAIREBASE]*[RETRAITE/100]

CSGRDS : 12,64 CSGD : 22,28 Scurit sociale : 34,02 Retraite : 28,55

Total des cotisations sociales : Par ailleurs, l'assistante maternelle a droit, chaque jour travaill, une indemnit d'entretien ainsi qu' une indemnit de repas. A ce titre elle reoit les indemnits suivantes : Au final, le salaire net payer l'assistante maternelle est le suivant :

[COTISATIONSSOCIALES]=[SALAIREBAS E]*(CSGRDS+CSGD+SECU+RETRAITE)/10 0 [INDEMNITS]=[TOTALJOURS]*(ENTRET IENJOUR+REPASJOUR)

[COTISATIONSSOCIALES]=97,48

[INDEMNITES]=104

[SALAIREBASE][COTISATIONSSOCIALES]+ [INDEMNITS]

[salaire NET]=368,77

2.3

Fonctionnement de l'application console

Voici un exemple de ce qui est attendu. L'ensemble des excutables et des fichiers de configuration ncessaires l'application console sont rassembls dans un rpertoire :

http://tahe.developpez.com/java/javaee

8/341

[pam-spring-ui-metier-dao-jpa-eclipselink.jar] (1) contient l'application que nous allons crire le dossier [lib] (2) contient les bibliothques de classes ncessaires au projet

Voici un exemple d'excution de l'application console dans une fentre Dos :


1. dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150 20 2. 3. Valeurs saisies : 4. N de scurit sociale de l'employ : 254104940426058 5. Nombre d'heures travailles : 150 6. Nombre de jours travaills : 20 7. 8. Informations Employ : 9. Nom : Jouveinal 10. Prnom : Marie 11. Adresse : 5 rue des Oiseaux 12. Ville : St Corentin 13. Code Postal : 49203 14. Indice : 2 15. 16. Informations Cotisations : 17. CSGRDS : 3.49 % 18. CSGD : 6.15 % 19. Retraite : 7.88 % 20. Scurit sociale : 9.39 % 21. 22. Informations Indemnits : 23. Salaire horaire : 2.1 euro 24. Entretien/jour : 2.1 euro 25. Repas/jour : 3.1 euro 26. Congs Pays : 15.0 % 27. 28. Informations Salaire : 29. Salaire de base : 362.25 euro 30. Cotisations sociales : 97.48 euro 31. Indemnits d'entretien : 42.0 euro 32. Indemnits de repas : 62.0 euro 33. Salaire net : 368.77 euro

On crira un programme qui recevra les informations suivantes : 1. 2. 3. n de scurit sociale de l'assistante maternelle ( 254104940426058 dans l'exemple - ligne 1) nombre total d'heures travailles (150 dans l'exemple - ligne 1) nombre total de jours travaills (20 dans l'exemple - ligne 1)

On voit que :

lignes 9-14 : affichent les informations concernant l'employ dont on a donn le n de scurit sociale lignes 17-20 : affichent les taux des diffrentes cotisations lignes 23-26 : affichent les indemnits associes l'indice de traitement de l'employ (ici l'indice 2) lignes 29-33 : affichent les lments constitutifs du salaire payer

L'application signale les erreurs ventuelles : Appel sans paramtres :

http://tahe.developpez.com/java/javaee

9/341

dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar Syntaxe : pg num_securite_sociale nb_heures_travailles nb_jours_travaills

Appel avec des donnes errones :


dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar Le nombre d'heures travailles [150x] est erron Le nombre de jours travaills [20x] est erron 254104940426058 150x 20x

Appel avec un n de scurit sociale erron :


dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar xx 150 20 L'erreur suivante s'est produite : L'employ de n[xx] est introuvable

2.4

Fonctionnement de l'application graphique

L'application graphique permet le calcul des salaires des assistantes maternelles au travers d'un formulaire Swing :

6 1 2 3 4

les informations passes en paramtres au programme console, sont maintenant saisies au moyen des champs de saisie [1, 2, 3]. le bouton [4] demande le calcul du salaire le formulaire affiche les diffrents lments du salaire jusqu'au salaire net payer [5]

La liste droulante [1, 6] ne prsente pas les ns SS des employs mais les noms et prnoms de ceux-ci. On fait ici l'hypothse qu'il n'y a pas deux employs de mmes nom et prnom.

3 Implmentation JPA de la couche de persistance des donnes


3.1 Les entits JPA

Nous adopterons l'architecture suivante pour notre application console :

http://tahe.developpez.com/java/javaee

10/341

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink/ Hibernate] 5

Couche [JDBC] 6

Spring

La persistance des donnes en base de donnes sera assure par une implmentation JPA / Hibernate ou JPA / EclipseLink. La couche JPA fera le pont entre les tables [COTISATIONS, INDEMNITES, EMPLOYES] et les entits [Cotisation, Indemnite, Employe]. Question : Donnez un code possible pour les entits [Cotisation, Indemnite, Employe]. Lectures conseilles : [ref1], paragraphe 2.4. Notes :

les entits feront partie d'un paquetage nomm [jpa] chaque entit aura un n de version si deux entits sont lies par une relation, seule la relation principale sera construite. La relation inverse ne le sera pas.

La structure de la base de donnes MySQL sera la suivante :


1. 2. -- -------------------------------------------------------3. 4. -5. -- Structure de la table 'cotisations' 6. -7. 8. CREATE TABLE cotisations ( 9. ID bigint(20) NOT NULL AUTO_INCREMENT, 10. SECU double NOT NULL, 11. RETRAITE double NOT NULL, 12. CSGD double NOT NULL, 13. CSGRDS double NOT NULL, 14. VERSION int(11) NOT NULL, 15. PRIMARY KEY (ID) 16. ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 17. 18. -- -------------------------------------------------------19. 20. -21. -- Structure de la table 'employes' 22. -23. 24. CREATE TABLE employes ( 25. ID bigint(20) NOT NULL AUTO_INCREMENT, 26. PRENOM varchar(20) NOT NULL, 27. SS varchar(15) NOT NULL, 28. ADRESSE varchar(50) NOT NULL, 29. CP varchar(5) NOT NULL, 30. VILLE varchar(30) NOT NULL, 31. NOM varchar(30) NOT NULL, 32. VERSION int(11) NOT NULL, 33. INDEMNITE_ID bigint(20) NOT NULL, 34. PRIMARY KEY (ID), 35. UNIQUE KEY SS (SS), 36. KEY FK_EMPLOYES_INDEMNITE_ID (INDEMNITE_ID) 37. ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 38. 39. -- -------------------------------------------------------40. 41. -42. -- Structure de la table 'indemnites' 43. -44. 45. CREATE TABLE indemnites ( 46. ID bigint(20) NOT NULL AUTO_INCREMENT, 47. ENTRETIEN_JOUR double NOT NULL, 48. REPAS_JOUR double NOT NULL,

http://tahe.developpez.com/java/javaee

11/341

49. INDICE int(11) NOT NULL, 50. INDEMNITES_CP double NOT NULL, 51. BASE_HEURE double NOT NULL, 52. VERSION int(11) NOT NULL, 53. PRIMARY KEY (ID), 54. UNIQUE KEY INDICE (INDICE) 55. ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 56. 57. -58. -- Contraintes pour les tables exportes 59. -60. 61. -62. -- Contraintes pour la table `employes` 63. -64. ALTER TABLE `employes` 65. ADD CONSTRAINT FK_EMPLOYES_INDEMNITE_ID FOREIGN KEY (INDEMNITE_ID) REFERENCES indemnites (ID);

3.2

Configuration de la couche JPA

Une fois les entits JPA crites, il est possible de faire une premire srie de tests en gnrant la base de donnes image des entits l'aide d'un script ant. Nous ferons ces tests l'intrieur du projet Netbeans [pam-jpa-hibernate-tools] suivant :

4 1 5 3 2 9 6 8 7

en [1] : le projet Java. On y notera : [2] : le paquetage [jpa] et les trois entits [Cotisation, Employe, Indemnite] [3] : le fichier [persistence.xml] qui configure la couche JPA en [4] : le dossier complet du projet : [5, 7] : le dossier [conf-bd] contient les fichiers [persistence.xml] pour divers SGBD [6, 8] : le dossier [conf-ddl] contient le schma de la base de donnes gnre par la couche JPA pour chacun des SGBD prcdents en [9] : le script [ant-hibernate.xml] est le script ant qui va gnrer la base de donnes partir des entits JPA.

Question : donner le contenu du fichier [persistence.xml] qui connecterait la couche JPA la base de donnes ayant les caractristiques suivantes : [SGBD : MySQL5, url : //localhost:3306/dbpam_hibernate, propritaire de la connexion : dbpam, mot de passe : dbpam]. Notes : suivre l'exemple du paragraphe 2.1.5 de [ref1]

4 Mise en oeuvre des tests de la couche JPA


Nous mettons en place l'environnement de tests de la couche JPA dveloppe prcdemment. Pour cela, nous utilisons l'IDE Netbeans 6.8 (http://www.netbeans.org/). L'objectif de ces tests est de gnrer la base de donnes MySQL partir de :

la dfinition des entits JPA la dfinition du fichier [persistence.xml] qui dfinit l'implmentation JPA utilise ainsi que l'url de la base de donnes dont la couche JPA est l'image. Nous utiliserons deux implmentations JPA : Hibernate et EclipseLink.

http://tahe.developpez.com/java/javaee

12/341

4.1

Implmentation JPA / Hibernate

Nous allons utiliser l'architecture suivante pour crer la base de donnes partir des entits JPA :

Script ant de gnration de la base de donnes

Interface [JPA]

Implmentation [Hibernate]

Couche [JDBC]

Les entits JPA et le fichier [persistence.xml] qui configure l'implmentation JPA et l'accs la base de donnes sont ceux dfinis prcdemment. Pour crer l'environnement de test, on pourra procder comme suit :

crer le projet Netbeans :

1 3 2

4 5

8 6 9

11

10

http://tahe.developpez.com/java/javaee

13/341

[1] : option [File / New Project] [2,3] : choisir la catgorie [General] et le type de projet [Java Application] [4] : explique le type du projet [5] : on passe l'tape suivante [6,7] : l'aide de [7], on dsigne le dossier [6] dans lequel sera cr le sous-dossier du projet. [8] : on donne un nom au projet [9] : prcise le dossier qui va tre cr pour le projet [10] : par dfaut, l'option [Main Class] est coche. Elle entrane la cration d'une classe dite principale avec la mthode statique void main (String[] args). Une classe principale est ncessaire pour avoir une gnration complte du projet. Nous l'appelons [Main] et la plaons dans un paquetage [main]. Nous terminons l'assistant de cration du projet avec le bouton [Finish] non reprsent. [11] : le projet a t cr. La classe [Main] cre est la suivante :

1. package main; 2. 3. public class Main { 4. 5. /** Creates a new instance of Main */ 6. public Main() { 7. } 8. 9. /** 10. * @param args the command line arguments 11. */ 12. public static void main(String[] args) { 13. // TODO code application logic here 14. } 15. 16. }

associer au projet cr les bibliothques de classes dont il va avoir besoin. Celles-ci ont t rassembles dans divers dossiers :

2 3

5 6 7

en [1] : l'emplacement du dossier [lib] qui contient les classes externes ncessaires au projet. Il est au mme niveau que les dossiers des projets Netbeans qui vont tre crs dans ce document. en [2] : les six sous-dossiers du dossier [lib] en [3] : le dossier [divers] contient les pilotes Jdbc des SGBD tests, l'archive log4j qui gre les logs de l'application en [4] : le dossier [hibernate-tools] contient les classes de l'implmentation JPA / Hibernate

http://tahe.developpez.com/java/javaee

14/341

en [5] : le dossier [eclipselink] contient les classes de l'implmentation JPA / EclipseLink 2.0 en [6] : le dossier [openejb] contient les classes du conteneur Ejb OpenEJB 3.1 en [7] : le dossier [spring] contient les classes du framework Spring 2.8 en [8] : le dossier [client-glassfish-v3] contient les classes ncessaires la communication entre un client distant et un EJB dploy sur un serveur Glassfish v3 Pour associer des bibliothques de classes (.jar) au projet Netbeans, on procde de la faon suivante :

2 5 1 3

en [1] : clic droit sur [Libraries] puis slectionner l'option [Add JAR/Folder] qui permet d'ajouter des archives Java (.jar) au Classpath du projet. en [2] : naviguer jusqu'au dossier qui contient les .jar ajouter en [3] : slectionner les . jar dsirs. Ils apparaissent en [4] en [5] : on prcise que les bibliothques du projet doivents tre rfrences par leur chemin relatif au chemin du projet plutt que par leur chemin absolu. Cela permet de dplacer le projet et ses bibliothques ailleurs dans le systme de fichiers. en [5] : valider la slection on fait ce processus pour les dossiers [divers, hibernate-tools]. [divers] nous apporte les pilotes Jdbc dont nous aurons besoin pour travailler avec divers SGBD. [hibernate-tools] nous apporte les archives de l'implmentation JPA / Hibernate ainsi que l'archive [hibernate-tools] dont nous aurons besoin dans un script ant. Le rsultat obtenu est le suivant (vue partielle) [1] : 1 2

[2] : insrer dans le projet le code des entits JPA et celui du fichier de configuration [persistence.xml] une fois disponibles les sources Java des entits JPA, le fichier de configuration [persistence.xml], les bibliothques de classes ncessaires la compilation et l'excution du projet, on peut gnrer celui-ci :

http://tahe.developpez.com/java/javaee

15/341

2 1b 1 3a

3b 6

4 5

[1] : la gnration du projet [2] : le projet est gnr dans le dossier [dist] de l'onglet [Files]. On y trouve trois lments : le dossier <lib> qui contient les bibliothques de classes tierces associes au projet (cf [7]). Ce dossier n'est pas gnr si le projet n'a pas de classe principale. C'est pourquoi nous avons laiss l'assistant crer une classe principale dont nous n'avions pas besoin pour les tests ant que nous voulons faire. l'archive .jar [3a, 3b] rsultat de la compilation de la branche [Source Packages] du projet. Sous forme compresse, on retrouve l'arborescence et le contenu de la branche [Source Packages] [1b] la diffrence que les .java ont t compils (4,5). Le .jar [3a,3b] porte le nom du projet. [6] un fichier [MANIFEST.MF] qui configure l'excution du .jar. Son contenu est le suivant :

1. 2. 3. 4. 5.

Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.1 Created-By: 10.0-b23 (Sun Microsystems Inc.) Main-Class: main.Main Class-Path: lib/derbyclient.jar lib/hsqldb.jar lib/jaybird-full-2.1.1. jar lib/log4j-1.2.13.jar lib/mysql-connector-java-5.0.5-bin.jar lib/o 6. jdbc14.jar lib/postgresql-8.2-505.jdbc3.jar lib/sqljdbc.jar lib/antlr -2.7.6.jar ... 7. X-COMMENT: Main-Class will be added automatically by build

Le fichier .jar peut tre excut dans une fentre Dos / Windows avec la commande suivante :
dos>java -jar "<dist>\pam-jpa-hibernate-tools.jar"

o <dist> reprsente le chemin du dossier <dist> dans la copie d'cran [2]. La machine virtuelle Java (JVM) utilise le fichier [META-INF / MANIFEST.MF] pour savoir comment excuter le .jar : ligne 4 : l'attribut [Main-Class] dsigne la classe qui contient la mthode statique void main(String[] args) que la JVM doit excuter pour lancer l'application. ligne 5 : l'attribut [Class-Path] dsigne la liste des .jar explorer par la JVM lorsque l'application rclame une classe qui n'est pas dans le .jar excut. L'environnement ncessaire la cration de la base de donnes, image des entits JPA, est dsormais prt. Nous allons utiliser un script ant pour faire cette gnration. Cette technique est dcrite au paragraphe [2.1.6] de [ref1].

http://tahe.developpez.com/java/javaee

16/341

10 9

Le script [ant-hibernate.xml] (8) va permettre de gnrer la base de donnes image de la couche JPA. Il mettra galement le schma de la base de donnes gnre dans le fichier [ddl/schema.sql]. Pour cela, nous crons le dossier [ddl] [9]. Le script [ant-hibernate.xml] est le suivant :
1. <project name="jpa-hibernate" default="DDL" basedir="."> 2. 3. <!-- nom du projet et version --> 4. <property name="proj.name" value="jpa-hibernate" /> 5. <property name="proj.shortname" value="jpa-hibernate" /> 6. <property name="version" value="1.0" /> 7. 8. <!-- Proprit globales --> 9. <property name="src.java.dir" value="src" /> 10. <property name="dist.dir" value="dist" /> 11. 12. <!-- le Classpath du projet --> 13. <path id="project.classpath"> 14. <fileset dir="${dist.dir}"> 15. <include name="**/*.jar" /> 16. </fileset> 17. </path> 18. 19. <!-- Hibernate Tools --> 20. <taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="project.classpath"/> 21. 22. <!-- Gnrer le schma de la base --> 23. <target name="DDL" description="Gnration DDL base"> 24. <hibernatetool destdir="${basedir}"> 25. <!-- Utiliser META-INF/persistence.xml --> 26. <jpaconfiguration /> 27. <!-- export --> 28. <hbm2ddl drop="true" create="true" export="false" outputfilename="ddl/schema.sql" delimiter=";" format="true" /> 29. </hibernatetool> 30. </target> 31. 32. <!-- Gnrer la base --> 33. <target name="BD" description="Gnration BD"> 34. <hibernatetool destdir="${basedir}"> 35. <!-- Utiliser META-INF/persistence.xml --> 36. <jpaconfiguration /> 37. <!-- export --> 38. <hbm2ddl drop="true" create="true" export="true" outputfilename="ddl/schema.sql" delimiter=";" format="true" /> 39. </hibernatetool> 40. </target> 41. </project>

ligne 1 : le projet [ant] s'appelle "jpa-hibernate". Il rassemble un ensemble de tches dont l'une est la tche par dfaut : ici la tche nomme "DDL". Un script ant est appel pour excuter une tche T. Si celle-ci n'est pas prcise, c'est la tche par dfaut qui est excute. basedir="." indique que pour tous les chemins relatifs trouvs dans le script, le point de dpart du chemin est le dossier dans lequel se trouve le script ant. lignes 3-10 : dfinissent des variables de script avec la balise <property name="nomVariable" value="valeurVariable"/>. La variable peut ensuite tre utilise dans le script avec la notation ${nomVariable}. Les noms peuvent tre quelconques. Attardons-nous sur les variables dfinies aux lignes 9-10 :

http://tahe.developpez.com/java/javaee

17/341

ligne 9 : dfinit une variable nomme "src.java.dir" (le nom est libre) qui va, dans la suite du script, dsigner le dossier qui contient les codes source Java. Sa valeur est "src", un chemin relatif au dossier dsign par l'attribut basedir (ligne 1). Il s'agit donc du chemin "./src" o . dsigne ici le dossier qui contient le script ant. C'est bien dans le dossier src que se trouvent les codes source Java (cf [9] plus haut). ligne 10 : dfinit une variable nomme "dist.dir" qui va, dans la suite du script, dsigner le dossier qui contient les archives jar dont ont besoin les tches Java du script. Sa valeur <dist> dsigne le dossier (3) qui, on le sait, contient une arborescence avec les archives .jar et les fichiers .class du projet. lignes 13-17 : la balise <path> sert dfinir des lments du classpath que devront utiliser les tches ant. Ici, le path "project.classpath" (le nom est libre) rassemble les archives .jar du dossier <dist>, c.a.d. les jars du dossier <dist>/lib et celui du projet [swing-metier-dao-jpa-spring-hibernate.jar] (3a, 3b). ligne 20 : dfinition d'une tche l'aide de la balise <taskdef>. Une telle tche a vocation tre rutilise ailleurs dans le script. C'est une facilit de codage. Parce que la tche est utilise divers endroits du script, on la dfinit une fois avec la balise <taskdef> et on la rutilise ensuite via son nom, lorsqu'on en a besoin. la tche s'appelle hibernatetool (attribut name). sa classe est dfinie par l'attribut classname. Ici, la classe dsigne sera trouve dans l'archive [hibernate-tools.jar] du dosssier <dist>/lib. l'attribut classpathref indique ant o chercher la classe prcdente les lignes 25-30 concernent la tche qui nous intresse ici, celle de la gnration du schma de la base de donnes image des objets @Entity de notre projet Netbeans. Une tche ant est dfinie au moyen de la balise <target>. ligne 23 : la tche s'appelle DDL (comme Data Definition Language, le SQL associ la cration des objets d'une base de donnes). lignes 24-29 : la tche [hibernatetool] dfinie ligne 20 est appele. On lui passe de nombreux paramtes, outre ceux dj dfinis ligne 20 : ligne 24 : le dossier de sortie des rsultats produits par la tche sera le dossier courant . ligne 26 : indique la tche [hibernatetool] comment elle peut connatre son environnement d'excution : la balise <jpaconfiguration/> lui indique qu'elle est dans un environnement JPA et qu'elle doit donc utiliser le fichier [META-INF/persistence.xml] qu'elle trouvera ici dans son classpath, c.a.d dans le dossier <dist>. la ligne 28 fixe les conditions de gnration de la base de donnes : drop=true indique que des ordres SQL drop table doivent tre mis avant la cration des tables, create=true indique que le fichier texte des ordres SQL de cration de la base doit tre cr, outputfilename indique le nom de ce fichier SQL - ici schema.sql dans le dossier <ddl> du projet Eclipse, export=false indique que les ordres SQL gnrs ne doivent pas tre jous dans une connexion au SGBD. Ce point est important : il implique que pour excuter la tche, le SGBD cible n'a pas besoin d'tre lanc. delimiter fixe le caractre qui spare deux ordres SQL dans le schma gnr, format=true demande ce qu'un formatage de base soit fait sur le texte gnr. les lignes 33-39 dfinissent la tche nomme BD. Elle est identique la tche DDL prcdente, si ce n'est que cette fois elle gnre la base de donnes (export="true" de la ligne 38). La tche ouvre une connexion sur le SGBD avec les informations trouves dans [persistence.xml], pour y jouer le schma SQL et gnrer la base de donnes. Pour excuter la tche BD, il faut donc que le SGBD soit lanc.

Nous testons maintenant la couche JPA [entits, persistence.xml] avec le SGBD MySQL5. Le mode opratoire est le suivant : 1. 2. 3. lancer MySQL5 avec les outils d'administration de MySQL5, crer une base de donnes appele [dbpam_hibernate] ainsi qu'un utilisateur [dbpam / dbpam] ayant tous les droits sur la base [dbpam_hibernate] (cf paragraphe 5.5 de [ref1]) compiler le projet Netbeans :

4.

dans l'onglet [Files] de Netbeans, lancer la tche BD du script [ant-hibernate.xml] :

On obtient les rsultats suivants dans la console de Netbeans :

http://tahe.developpez.com/java/javaee

18/341

1. BD: 2. Executing Hibernate Tool with a JPA Configuration 3. 1. task: hbm2ddl (Generates database schema) 4. alter table employes 5. drop 6. foreign key FK4722E6BC73F24A67; 7. drop table if exists cotisations; 8. drop table if exists employes; 9. drop table if exists indemnites; 10. create table cotisations ( 11. ID bigint not null auto_increment, 12. SECU double precision not null, 13. RETRAITE double precision not null, 14. CSGD double precision not null, 15. CSGRDS double precision not null, 16. VERSION integer not null, 17. primary key (ID) 18. ) ENGINE=InnoDB; 19. create table employes ( 20. ID bigint not null auto_increment, 21. PRENOM varchar(20) not null, 22. SS varchar(15) not null, 23. ADRESSE varchar(50) not null, 24. CP varchar(5) not null, 25. VILLE varchar(30) not null, 26. NOM varchar(30) not null, 27. VERSION integer not null, 28. INDEMNITE_ID bigint not null, 29. primary key (ID), 30. unique (SS) 31. ) ENGINE=InnoDB; 32. create table indemnites ( 33. ID bigint not null auto_increment, 34. ENTRETIEN_JOUR double precision not null, 35. REPAS_JOUR double precision not null, 36. INDICE integer not null, 37. INDEMNITES_CP double precision not null, 38. BASE_HEURE double precision not null, 39. VERSION integer not null, 40. primary key (ID), 41. unique (INDICE) 42. ) ENGINE=InnoDB; 43. alter table employes 44. add index FK4722E6BC73F24A67 (INDEMNITE_ID), 45. add constraint FK4722E6BC73F24A67 46. foreign key (INDEMNITE_ID) 47. references indemnites (ID); 48. BUILD SUCCESSFUL (total time: 3 seconds)

Les logs de la console montrent le schma de la base gnre. Ce schma peut galement tre retrouv dans le fichier [ddl/schema.sql] :

lignes 10-18 : la table [COTISATIONS] lignes 19-31 : la table [EMPLOYES] lignes 32-42 : la table [INDEMNITES] lignes 43-47 : la cl trangre (INDEMNITE_ID) de la table [EMPLOYES] sur la colonne ID de la table [INDEMNITES].

Pour vrifier que la base MySQL5 [dbpam_hibernate] a t gnre, nous procdons de la faon suivante :

http://tahe.developpez.com/java/javaee

19/341

dans l'onglet [1] [Services], la branche [Databases] liste les connexions des SGBD configures par l'utilisateur. Pour crer une connexion un SGBD, il faut donner deux informations Netbeans : o trouver le pilote Jdbc du SGBD les coordonnes de l'url de la base de donnes laquelle on veut se connecter. Ces informations se donnent en deux temps : d'abord le pilote Jdbc s'il n'est pas dj dans la liste des drivers disponibles [Databases / Drivers] [2], puis l'url de la base de donnes [3].

Netbeans 6.8 est livr avec le pilote Jdbc de MySQL dj configur dans la branche [Databases / Drivers]. Pour l'exemple, ajoutons cette branche le pilote Jdbc du SGBD Firebird. Celui-ci se trouve dans le dossier [lib / divers]. 1 4 2 3

5 6

slectionner la feuille [Databases / Drivers] de l'arborescence [2], cliquer droit et prendre l'option [New Driver] l'aide de [3], dsigner l'emplacement du pilote Jdbc de MySQL5. Dans ce TD, on le trouvera dans le dossier [lib / divers] (cf page 14). L'archive du pilote Jdbc apparat en [4] le nom de la classe du pilote Jdbc apparat normalement en [5]. Si rien n'apparat ou si vous connaissez la classe et que celle affiche n'est pas la bonne, utiliser le bouton [6] pour trouver la classe. ceci fait, cliquer [OK]. Le pilote Jdbc de Firebird est ajout la liste des pilotes Jdbc disponibles :

Maintenant, crons une connexion vers la base MySQL [dbpam_hibernate] dans laquelle le script [ant-hibernate.xml] a du crer des tables issues de la configuration JPA du projet :

http://tahe.developpez.com/java/javaee

20/341

2 1 3 4 5 6

en [1] : cliquer droit sur le pilote JDBC MySQL et choisir [Connect Using...] pour crer la connexion la base MySQL5 / dbpam_hibernate. en [2] : le nom de la machine sur laquelle se trouve le SGBD MySQL en [3] : le port d'coute du SGBD en [4] : le nom de la base de donnes laquelle on veut se connecter en [5] : le propritaire de la connexion en [6] : son mot de passe [dbpam] en [7] : l'url Jdbc de la base de donnes faire [OK]

La connexion est alors ajoute la liste des connexions configures : 1 3 2 1 5

en [1] : les 3 tables gnres par le script [ant-hibernate.xml] en [2] : la structure de la table [COTISATIONS] en [3] : la structure de table [EMPLOYES] avec en [4] sa cl trangre sur la colonne INDEMNITES(ID) en [5] : la structure de table [INDEMNITES]

Pour voir le contenu d'une table :

http://tahe.developpez.com/java/javaee

21/341

2 3

4 1

en [1] : on demande voir le contenu de la table [employes] en [3] : la requte SQL mise en [2] : la connexion sur laquelle elle est mise en [4] : le rsultat de la requte la table est vide.

Travail pratique : Refaire le travail prcdent avec certains des SGBD suivants : Oracle XE, SQL Server Express, Firebird, Apache Derby, HSQL. A chaque fois, sauvegarder le fichier [schema.sql] gnr par le script [ant-hibernate.xml] dans le dossier [ddl / sgbd] o sgbd dsigne le SGBD utilis pour le test. Aprs avoir chang le SGBD dans le fichier [persistence.xml], rgnrer le projet (option Build Project) avant d'excuter le script ant.

4.2

Implmentation JPA / EclipseLink

L'architecture de notre application devient la suivante :

Programme principal [Main]

Interface [JPA]

Implmentation [EclipseLink]

Couche [JDBC]

Nous crons un nouveau projet [pam-jpa-eclipselink] [1, 2, 3, 4] : 1

2 3

http://tahe.developpez.com/java/javaee

22/341

5 4

En [5], nous ajoutons au projet les entits JPA du projet prcdent. Des erreurs apparaissent parce que notre projet n'a pas dfini de bibliothques pour l'implmentation JPA. en [6], nous ajoutons les bibliothques ncessaires au projet : celles de l'implmentation JPA EclipseLink qu'on trouve dans le dossier [lib/eclipselink] celle du pilote Jdbc du SGBD MySQL qu'on trouve dans le dossier [lib/divers]

Le projet volue alors de la faon suivante [7] :

Il ne prsente plus d'erreurs. Rappelons qu'une couche JPA est dfinie par deux lments : des entits JPA - nous venons de les dfinir un fichier [META-INF/persistence.xml] qui dfinit l'implmentation JPA utilise et les proprits de celle-ci. Parmi ces dernires, il y a les paramtres de connexion la base de donnes. Netbeans peut nous aider dfinir le fichier [persistence.xml] en procdant de la faon suivante :

crer une base de donnes MySQL [dbpam_eclipselink] de propritaire [dbpam] avec le mot de passe [dbpam]. On procdera comme il a t fait pour la base [dbpam_hibernate].

Netbeans permet de gnrer le fichier [persistence.xml] pour une base existante et une implmentation JPA donne. Tout d'abord, nous crons dans l'onglet [Services] de Netbeans, une connexion la base MySQL [dbpam_eclipselink]. On procdera comme il a t vu page 20 pour la connexion la base [dbpam_hibernate]. On obtient le rsultat suivant :

http://tahe.developpez.com/java/javaee

23/341

Pour l'instant, la base [dbpam_eclipselink] est sans tables. Une fois la connexion la base dfinie, il est possible de gnrer le fichier [persistence.xml] :

en [2], clic droit sur le projet / New / Other

5 6 7 4 3 8

choisir la catgorie [Persistence] [3] et le type [Persistence Unit] [4] - faire [Next] en [5] le nom de l'unit de persistence. Ici, il est gnr automatiquement par Netbeans. On peut le changer. en [6] choisir l'implmentation JPA dsire. Ici, on choisit EclipseLink en [7] dsigner la connexion Netbeans relie la base de donnes cible de la couche JPA. Ici, nous dsignons la connexion la base [dbpam_eclipselink] que nous venons de crer. en [8] indiquer la stratgie de gnration des tables de la base de donnes lorsque la couche JPA est construite : None : rien n'est fait Drop and Create : la couche JPA supprime les tables existantes (Drop) et les recre (Create) Create : la couche JPA cre les tables (Create). Si elles existent dj, les ordres SQL CREATE chouent mais ne provoquent pas de plantage. Ici, nous choisissons le mode [Create].

Lorsqu'on termine l'assistant de cration du fichier [persistence.xml], le projet volue de la faon suivante :

http://tahe.developpez.com/java/javaee

24/341

3 2

en [1], le fichier [persistence.xml] gnr en [2], les bibliothques d'EclipseLink ont t rajoutes. Elles sont dsormais prsentes deux fois. On peut supprimer l'une des deux versions. Nous gardons celle rfrenant les bibliothques du dossier [lib/eclipselink] [3].

Double-cliquons sur le fichier [persistence.xml]. Celui-ci peut tre vu selon deux modes : 1 2

[1] : mode texte [XML] [2] : mode [Design]

Prenons le mode [XML]. Le fichier [persistence.xml] est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL"> 4. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 5. <properties> 6. <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/> 7. <property name="javax.persistence.jdbc.password" value="dbpam"/> 8. <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> 9. <property name="javax.persistence.jdbc.user" value="dbpam"/> 10. <property name="eclipselink.ddl-generation" value="create-tables"/> 11. </properties> 12. </persistence-unit> 13. </persistence>

ligne 3 : le nom de l'unit de persistance et son mode de transaction. Ici RESOURCE_LOCAL indique que c'est le code de l'utilisateur qui a la charge de les grer. ligne 4 : l'implmentation JPA utilise, ici EclipseLink lignes 5-11 : les proprits (paramtres) de l'implmentation JPA ligne 6 : l'url Jdbc de la base de donnes cible ligne 7 : le propritaire des connexions qui seront faites sur la base ligne 9 : son mot de passe ligne 8 : le pilote Jdbc du SGBD cible MySQL ligne 10 : la stratgie de cration des tables de la base lorsque la couche JPA est instancie

On rajoutera manuellement la proprit suivante :


<property name="eclipselink.target-database" value="MySQL"/>

http://tahe.developpez.com/java/javaee

25/341

qui fixe le type de la base de donnes cible. En mode [Design], on a la vue suivante :

1 2

Pa dfaut, la couche JPA grera toutes les classes ayant l'annotation @Entity [1]. On peut aussi dclarer explicitement ces classes avec le bouton [2] :

en [3], on dsigne les entits JPA qui apparaissent alors en [4]

Le fichier [persistence.xml] volue de la faon suivante dans la vue texte :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL"> 4. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 5. <class>jpa.Cotisation</class> 6. <class>jpa.Employe</class> 7. <class>jpa.Indemnite</class> 8. <properties> 9. ... 10. </properties> 11. </persistence-unit> 12. </persistence>

Les entits JPA gres par la couche JPA ont t dfinies aux lignes 5-7 par une balise <class>.

http://tahe.developpez.com/java/javaee

26/341

La couche JPA tant dfinie, nous allons l'instancier partir de la classe principale du projet [main.Main] :

La classe [Main.java] sera la suivante :


1. package main; 2. 3. import javax.persistence.EntityManager; 4. import javax.persistence.EntityManagerFactory; 5. import javax.persistence.Persistence; 6. 7. public class Main { 8. 9. public static void main(String[] args){ 10. // crer l'Entity Manager suffit construire la couche JPA 11. EntityManagerFactory emf=Persistence.createEntityManagerFactory("pam-jpa-eclipselinkPU"); 12. EntityManager em=emf.createEntityManager(); 13. // libration des ressources 14. em.close(); 15. emf.close(); 16. // fin 17. System.out.println("termin"); 18. } 19. }

ligne 11 : on cre un EntityManagerFactory partir de l'unit de persistance [pam-jpa-eclipselinkPU] qui vient d'tre cr. Ce nom est celui de l'attribut name de la balise <persistence-unit>. ligne 12 : on cre l'objet EntityManager qui est le gestionnaire des entits JPA. La cration de cet objet instancie la couche JPA. Le fichier [persistence.xml] est exploit. Parce que nous avons crit :
<property name="eclipselink.ddl-generation" value="create-tables"/>

la couche JPA va crer les tables images des entits JPA. Ainsi aprs excution devrions-nous voir des tables dans la base de donnes. Essayons.

2 1

en [1], la classe [Main] est excute en [2], dans l'onglet [Services], nous rafrachissons la base de donnes [dbpam_eclipselink] en [3], ce rafrachissement fait apparatre 4 tables : une pour chacune des entits JPA et une quatrime appele [sequence] utilise pour gnrer automatiquement les cls primaires des trois autres tables. On dcouvre ainsi, que selon l'implmentation Hibernate ou EclipseLink, la base de donnes gnre n'a pas t la mme.

http://tahe.developpez.com/java/javaee

27/341

Travail pratique : Refaire le travail prcdent avec d'autres SGBD : Oracle XE, SQL Server Express, Firebird, Apache Derby, HSQL, ... Revenons l'architecture dsire pour le projet final : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink] 5

Couche [JDBC] 6

Spring

Les entits JPA [4] sont dsormais crites. Nous abordons maintenant l'criture de la couche [dao] [3] mais auparavant nous allons dfinir les interfaces prsentes par les couches [mtier] et [dao].

5 Les interfaces des couches [metier] et [dao]


Dans l'architecture ci-dessus, quelle interface doit offrir la couche [dao] la couche [metier] et quelle interface doit offrir la couche [metier] la couche [ui] ? Une premire approche pour dfinir les interfaces des diffrentes couches est d'examiner les diffrents cas d'usage (use cases) de l'application. Ici nous en avons deux, selon l'interface utilisateur choisie : console ou formulaire graphique. Examinons le mode d'utilisation de l'application console :
1. dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150 20 2. 3. Valeurs saisies : 4. N de scurit sociale de l'employ : 254104940426058 5. Nombre d'heures travailles : 150 6. Nombre de jours travaills : 20 7. 8. Informations Employ : 9. Nom : Jouveinal 10. ... 11. 12. Informations Cotisations : 13. CSGRDS : 3.49 % 14. ... 15. 16. Informations Indemnits : 17. ... 18. 19. Informations Salaire : 20. Salaire de base : 362.25 euro 21. Cotisations sociales : 97.48 euro 22. Indemnits d'entretien : 42.0 euro 23. Indemnits de repas : 62.0 euro 24. Salaire net : 368.77 euro

L'application reoit trois informations de l'utilisateur (cf ligne 1 ci-dessus) le n de scurit sociale de l'assistante maternelle le nombre d'heures travailles dans le mois le nombre de jours travaills dans le mois A partir de ces information et d'autres enregistres dans des fichiers de configuration, l'application affiche les informations suivantes :

lignes 4-6 : les valeurs saisies lignes 8-10 : les informations lies l'employ dont on a donn le n de scurit sociale lignes 12-14 : les taux des diffrentes cotisations sociales lignes 16-17 : les diffrentes indemnits verses l'assistante maternelle lignes 19-24 : les lments de la feuille de salaire de l'assistante maternelle

Un certain nombre d'informations doivent tre fournies par la couche [metier] la couche [ui] :

http://tahe.developpez.com/java/javaee

28/341

1. 2. 3. 4.

les informations lies une assistante maternelle identifie par son n de scurit sociale. On trouve ces informations dans la table [EMPLOYES]. Cela permet d'afficher les lignes 6-8. les montants des divers taux de cotisations sociales prlever sur le salaire brut. On trouve ces informations dans la table [COTISATIONS]. Cela permet d'afficher les lignes 10-12. les montants des diverses indemnits lies la fonction d'assistante maternelle. On trouve ces informations dans la table [INDEMNITES]. Cela permet d'afficher les lignes 14-15. les lments constitutifs du salaire affichs lignes 18-22.

De ceci, on pourrait dcider d'une premire criture de l'interface [IMetier] prsente par la couche [metier] la couche [ui] :
1. 2. 3. 4. 5. 6. package metier; public interface IMetier { // obtenir la feuille de salaire public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills ); }

ligne 1 : les lments de la couche [metier] sont mis dans le paquetage [metier] ligne 5 : la mthode [ calculerFeuilleSalaire ] prend pour paramtres les trois informations acquises par la couche [ui] et rend un objet de type [FeuilleSalaire] contenant les informations que la couche [ui] affichera sur la console. La classe [FeuilleSalaire] pourrait tre la suivante :

1. package metier; 2. 3. import jpa.Cotisation; 4. import jpa.Employe; 5. import jpa.Indemnite; 6. 7. public class FeuilleSalaire { 8. // champs privs 9. private Employe employe; 10. private Cotisation cotisation; 11. private Indemnite indemnite; 12. private ElementsSalaire elementsSalaire; 13. 14. ... 15. }

ligne 9 : l'employ concern par la feuille de salaire - information n 1 affiche par la couche [ui] ligne 10 : les diffrents taux de cotisation - information n 2 affiche par la couche [ui] ligne 11 : les diffrentes indemnits lies l'indice de l'employ - information n 3 affiche par la couche [ui] ligne 12 : les lments constitutifs de son salaire - information n 4 affiche par la couche [ui]

Un second cas d'usage de la couche [mtier] apparat avec l'interface graphique :

2 1

On voit ci-dessus, que la liste droulante [1, 2] prsente tous les employs. Cette liste doit tre demande la couche [mtier]. L'interface de celle-ci volue alors de la faon suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. package metier; import java.util.List; import jpa.Employe; public interface IMetier { // obtenir la feuille de salaire public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills ); // liste des employs public List<Employe> findAllEmployes(); }

http://tahe.developpez.com/java/javaee

29/341

ligne [10] : la mthode qui va permettre la couche [ui] de demander la liste de tous les employs la couche [mtier].

La couche [metier] ne peut initialiser les champs [Employe, Cotisation, Indemnite] de l'objet [FeuilleSalaire] ci-dessus qu'en questionnant la couche [dao] car ces informations sont dans les tables de la base de donnes. Il en est de mme pour obtenir la liste de tous les employs. On peut crer une interface [dao] unique grant l'accs aux trois entits [Employe, Cotisation, Indemnite]. Nous dcidons plutt ici de crer une interface [dao] par entit. L'interface [dao] pour les accs aux entits [Cotisation] de la table [COTISATIONS] sera la suivante :
1. package dao; 2. 3. import java.util.List; 4. import jpa.Cotisation; 5. 6. public interface ICotisationDao { 7. // crer une nouvelle cotisation 8. public Cotisation create(Cotisation cotisation); 9. // modifier une cotisation existante 10. public Cotisation edit(Cotisation cotisation); 11. // supprimer une cotisation existante 12. public void destroy(Cotisation cotisation); 13. // chercher une cotisation particulire 14. public Cotisation find(Long id); 15. // obtenir tous les objets Cotisation 16. public List<Cotisation> findAll(); 17. 18. }

ligne 6, l'interface [ICotisationDao] gre les accs l'entit [Cotisation] et donc la table [COTISATIONS] de la base de donnes. Notre application n'a besoin que de la mthode [findAll] de la ligne 16 qui permet de retrouver tout le contenu de la table [COTISATIONS]. On a voulu ici se mettre dans un cas plus gnral o toutes les oprations CRUD (Create, Read, Update, Delete) sont effectues sur l'entit. ligne 8 : la mthode [create] cre une nouvelle entit [Cotisation] ligne 10 : la mthode [edit] modifie une entit [Cotisation] existante ligne 12 : la mthode [destroy] supprime une entit [Cotisation] existante ligne 14 : la mthode [find] permet de retrouver une entit [Cotisation] existante via son identifiant id ligne 16 : la mthode [findAll] rend dans une liste toutes les entits [Cotisation] existantes

Attardons-nous sur la signature de la mthode [create] :


1. // crer une nouvelle cotisation 2.Cotisation create(Cotisation cotisation);

La mthode create a un paramtre cotisation de type Cotisation. Le paramtre cotisation doit tre persist, c.a.d. ici mis dans la table [COTISATIONS]. Avant cette persistance, le paramtre cotisation a un identifiant id sans valeur. Aprs la persistance, le champ id a une valeur qui est la cl primaire de l'enregistrement ajout la table [COTISATIONS]. Le paramtre cotisation est donc un paramtre d'entre / sortie de la mthode create. Il ne semble pas ncessaire que mthode create rende de plus le paramtre cotisation comme rsultat. La mthode appelante dtenant une rfrence sur l'objet [Cotisation cotisation], si celui-ci est modifi, elle a accs l'objet modifi puisqu'elle a une rfrence dessus. Elle peut donc connatre la valeur que la mthode create a donn au champ id de l'objet [Cotisation cotisation]. La signature de la mthode pourrait donc tre plus simplement :
1. // crer une nouvelle cotisation 2. void create(Cotisation cotisation);

Lorsqu'on crit une interface, il est bon de se rappeler qu'elle peut tre utilise dans deux contextes diffrents : local et distant. Dans le contexte local, la mthode appelante et la mthode appele sont excutes dans la mme Jvm :

utilisateur

Couche interface utilisateur [ui]

Couche mtier [metier] JVM

Couche d'accs aux donnes [dao]

Donnes

Si la couche [metier] fait appel la mthode create de la couche [dao], elle a bien une rfrence sur le paramtre [Cotisation cotisation] qu'elle passe la mthode.

http://tahe.developpez.com/java/javaee

30/341

Dans le contexte distant, la mthode appelante et la mthode appele sont excutes dans des Jvm diffrentes : 1 utilisateur Couche interface utilisateur [ui] JVM 1 Couche mtier [metier] 2 3 Rseau tcp /ip Couche d'accs aux donnes [dao] JVM 2 Donnes

Ci-dessus, la couche [metier] s'excute dans la JVM 1 et la couche [dao] dans la JVM 2 sur deux machines diffrentes. Les deux couches ne communiquent pas directement. Entre-elles s'intercale une couche qu'on appellera couche de communication [1]. Celleci est compose d'une couche d'mission [2] et d'une couche de rception [3]. Le dveloppeur n'a en gnral pas crire ces couches de communication. Elles sont gnres automatiquement par des outils logiciels. La couche [metier] est crite comme si elle s'excutait dans la mme Jvm que la couche [dao]. Il n'y a donc aucune modification de code. Le mcanisme de communication entre la couche [metier] et la couche [dao] est le suivant :

la couche [metier] fait appel la mthode create de la couche [dao] en lui passant le paramtre [Cotisation cotisation1] ce paramtre est en fait pass la couche d'mission [2]. Celle-ci va transmettre sur le rseau, la valeur du paramtre cotisation1 et non sa rfrence. La forme exacte de cette valeur dpend du protocole de communication utilis. la couche de rception [3] va rcuprer cette valeur et reconstruire partir d'elle un objet [Cotisation cotisation2] image du paramtre initial envoy par la couche [metier]. On a maintenant deux objets identiques (au sens de contenu) dans deux Jvm diffrentes : cotisation1 et cotisation2. la couche de rception va passer l'objet cotisation2 la mthode create de la couche [dao] qui va le persister en base de donnes. Aprs cette opration, le champ id de l'objet cotisation2 a t initialis par la cl primaire de l'enregistrement ajout la table [COTISATIONS]. Ce n'est pas le cas de l'objet cotisation1 sur lequel la couche [metier] a une rfrence. Si on veut que la couche [metier] ait une rfrence sur l'objet cotisation2, il faut le lui envoyer. Aussi est-on amens changer la signature de la mthode create de la couche [dao] :

1. // crer une nouvelle cotisation 2. Cotisation create(Cotisation cotisation);

avec cette nouvelle signature, la mthode create va rendre comme rsultat l'objet persist cotisation2. Ce rsultat est rendu la couche de rception [3] qui avait appel la couche [dao]. Celle-ci va rendre la valeur (et non la rfrence) de cotisation2 la couche d'mission [2]. la couche d'mission [2] va rcuprer cette valeur et reconstruire partir d'elle un objet [Cotisation cotisation3] image du rsultat rendu par la mthode create de la couche [dao]. l'objet [Cotisation cotisation3] est rendu la mthode de la couche [metier] dont l'appel la mthode create de la couche [dao] avait initi tout ce mcanisme. La couche [metier] peut donc connatre la valeur de cl primaire donn l'objet [Cotisation cotisation1] dont elle avait demand la persistance : c'est la valeur du champ id de cotisation3.

L'architecture prcdente n'est pas la plus courante. On trouve plus frquemment les couches [metier] et [dao] dans la mme Jvm : 1 utilisateur 2 Couche interface utilisateur [ui] JVM 1 3 Rseau tcp /ip Couche mtier [metier] Couche d'accs aux donnes [dao] JVM 2 Donnes

Dans cette architecture, ce sont les mthodes de la couche [metier] qui doivent rendre des rsultats et non celles de la couche [dao]. Nanmoins la signature suivante de la mthode create de la couche [dao] :
1. // crer une nouvelle cotisation 2. Cotisation create(Cotisation cotisation);

http://tahe.developpez.com/java/javaee

31/341

nous permet de ne pas faire d'hypothses sur l'architecture rellement mise en place. Utiliser des signatures qui fonctionneront quelque soit l'architecture retenue, locale ou distante, implique que dans le cas o une mthode appele modifie certains de ses paramtres : ceux-ci doivent faire galement partie du rsultat de la mthode appele la mthode appelante doit utiliser le rsultat de la mthode appele et non les rfrences des paramtres modifis qu'elle a transmis la mthode appele. On se laisse ainsi la possibilit de passer une couche d'une architecture locale une architecture distante sans modification de code. Rexaminons, cette lumire, l'interface [ICotisationDao] :
1. package dao; 2. 3. import java.util.List; 4. import jpa.Cotisation; 5. 6. public interface ICotisationDao { 7. // crer une nouvelle cotisation 8. public Cotisation create(Cotisation cotisation); 9. // modifier une cotisation existante 10. public Cotisation edit(Cotisation cotisation); 11. // supprimer une cotisation existante 12. public void destroy(Cotisation cotisation); 13. // chercher une cotisation particulire 14. public Cotisation find(Long id); 15. // obtenir tous les objets Cotisation 16. public List<Cotisation> findAll(); 17. 18. }

ligne 8 : le cas de la mthode create a t trait ligne 10 : la mthode edit utilise son paramtre [Cotisation cotisation1] pour mettre jour l'enregistrement de la table [COTISATIONS] ayant la mme cl primaire que l'objet cotisation. Elle rend comme rsultat l'objet cotisation2 image de l'enregistrement modifi. Le paramtre cotisation1 n'est lui pas modifi. La mthode doit rendre cotisation2 comme rsultat qu'on soit dans le cadre d'une architecture distante ou locale. ligne 12 : la mthode destroy supprime l'enregistrement de la table [COTISATIONS] ayant la mme cl primaire que l'objet cotisation pass en paramtre. Celui-ci n'est pas modifi. Il n'a donc pas tre rendu. ligne 14 : le paramtre id de la mthode find n'est pas modifi par la mthode. Il n'a pas faire partie du rsultat. ligne 16 : la mthode findAll n'a pas de paramtres. On n'a donc pas l'tudier.

Au final, seule la signature de la mthode create doit tre adapte pour tre utilisable dans le cadre d'une architecture distante. Les raisonnements prcdents seront valables pour les autres interfaces [dao]. Nous ne les rpterons pas et utiliserons directement des signatures utilisables aussi bien dans le cadre d'une architecture distante que locale. L'interface [dao] pour les accs aux entits [Indemnite] de la table [INDEMNITES] sera la suivante :
1. package dao; 2. 3. import java.util.List; 4. import jpa.Indemnite; 5. 6. public interface IIndemniteDao { 7. // crer une entit Indemnite 8. public Indemnite create(Indemnite indemnite); 9. // modifier une entit Indemnite 10. public Indemnite edit(Indemnite indemnite); 11. // supprimer une entit Indemnite 12. public void destroy(Indemnite indemnite); 13. // rechercher une entit Indemnite via son identifiant 14. public Indemnite find(Long id); 15. // obtenir toutes les entits Indemnite 16. public List<Indemnite> findAll(); 17. 18. }

ligne 6, l'interface [IIndemniteDao] gre les accs l'entit [Indemnite] et donc la table [INDEMNITES] de la base de donnes. Notre application n'a besoin que de la mthode [findAll] de la ligne 16 qui permet de retrouver tout le contenu de la table [INDEMNITES]. On a voulu ici se mettre dans un cas plus gnral o toutes les oprations CRUD (Create, Read, Update, Delete) sont effectues sur l'entit. ligne 8 : la mthode [create] cre une nouvelle entit [Indemnite] ligne 10 : la mthode [edit] modifie une entit [Indemnite] existante ligne 12 : la mthode [destroy] supprime une entit [Indemnite] existante ligne 14 : la mthode [find] permet de retrouver une entit [Indemnite] existante via son identifiant id

http://tahe.developpez.com/java/javaee

32/341

ligne 16 : la mthode [findAll] rend dans une liste toutes les entits [Indemnite] existantes

L'interface [dao] pour les accs aux entits [Employe] de la table [EMPLOYES] sera la suivante :
1. package dao; 2. 3. import java.util.List; 4. import jpa.Employe; 5. 6. public interface IEmployeDao { 7. // crer une nouvelle entit Employe 8. public Employe create(Employe employe); 9. // modifier une entit Employe existante 10. public Employe edit(Employe employe); 11. // supprimer une entit Employe 12. public void destroy(Employe employe); 13. // chercher une entit Employe via son identifiant id 14. public Employe find(Long id); 15. // chercher une entit Employe via son n SS 16. public Employe find(String SS); 17. // obtenir toutes les entits Employe 18. public List<Employe> findAll(); 19. }

ligne 6, l'interface [IEmployeDao] gre les accs l'entit [Employe] et donc la table [EMPLOYES] de la base de donnes. Notre application n'a besoin que de la mthode [findAll] de la ligne 16 qui permet de retrouver tout le contenu de la table [EMPLOYES]. On a voulu ici se mettre dans un cas plus gnral o toutes les oprations CRUD (Create, Read, Update, Delete) sont effectues sur l'entit. ligne 8 : la mthode [create] cre une nouvelle entit [Employe] ligne 10 : la mthode [edit] modifie une entit [Employe] existante ligne 12 : la mthode [destroy] supprime une entit [Employe] existante ligne 14 : la mthode [find] permet de retrouver une entit [Employe] existante via son identifiant id ligne 16 : la mthode [find(String SS)] permet de retrouver une entit [Employe] existante via son n SS. Nous avons vu que cette mthode tait ncessaire l'application console. ligne 18 : la mthode [findAll] rend dans une liste toutes les entits [Employe] existantes. Nous avons vu que cette mthode tait ncessaire l'application graphique.

6 La classe [PamException]
La couche [dao] va travailler avec l'API JDBC de Java. Cette API lance des exceptions contrles de type [SQLException] qui prsentent deux inconvnients : elles alourdissent le code qui doit obligatoirement grer ces exceptions avec des try / catch. elles doivent tre dclares dans la signature des mthodes de l'interface [IDao] par un "throws SQLException". Ceci a pour consquence d'empcher l'implmentation de cette interface par des classes qui lanceraient une exception contrle d'un type diffrent de [SQLException]. Pour remdier ce problme, la couche [dao] ne "remontera" que des exceptions non contrles de type [PamException].

PamException
Couche [ui] Couche [metier]

[Jpa]Exception

SQLException
Couche [JDBC]

Couche [dao]

Implmentation [Jpa / EclipseLink]

Spring

la couche [Jdbc] lance des exceptions de type [SQLException] la couche [Jpa] lance des exceptions propres l'implmentation Jpa utilise la couche [dao] lance des exceptions de type [PamException] non contrles

Ceci a deux consquences :

http://tahe.developpez.com/java/javaee

33/341

la couche [metier] n'aura pas l'obligation de grer les exceptions de la couche [dao] avec des try / catch. Elle pourra simplement les laisser remonter jusqu' la couche [ui]. les mthodes de l'interface [IDao] n'ont pas mettre dans leur signature la nature de l'exception [PamException], ce qui laisse la possiblit d'implmenter cette interface avec des classes qui lanceraient un autre type d'exception non contrle.

La classe [PamException] sera place dans le paquetage [exception] du projet Netbeans :

Son code est le suivant :


1. package exception; 2. 3. @SuppressWarnings("serial") 4. public class PamException extends RuntimeException { 5. 6. // code d'erreur 7. private int code; 8. 9. public PamException(int code) { 10. super(); 11. this.code = code; 12. } 13. 14. public PamException(String message, int code) { 15. super(message); 16. this.code = code; 17. } 18. 19. public PamException(Throwable cause, int code) { 20. super(cause); 21. this.code = code; 22. } 23. 24. public PamException(String message, Throwable cause, int code) { 25. super(message, cause); 26. this.code = code; 27. } 28. 29. // getter et setter 30. 31. public int getCode() { 32. return code; 33. } 34. 35. public void setCode(int code) { 36. this.code = code; 37. } 38. 39. }

ligne 4 : [PamException] drive de [RuntimeException]. C'est donc un type d'exceptions que le compilateur ne nous oblige pas grer par un try / catch ou mettre dans la signature des mthodes. C'est pour cette raison, que [PamException] n'est pas dans la signature des mthodes de l'interface [IDao]. Cela permet cette interface d'tre implmente par une classe lanant un autre type d'exceptions, pourvu que celui-ci drive galement de [RuntimeException]. pour diffrencier les erreurs qui peuvent se produire, on utilise le code erreur de la ligne 7. Les trois constructeurs des lignes 14, 19 et 24 sont ceux de la classe parente [RuntimeException] auxquels on a rajout un paramtre : celui du code d'erreur qu'on veut donner l'exception.

Le fonctionnement de l'application, du point de vue des exceptions, sera le suivant :

la couche [dao] encapsulera toute exception rencontre, dans une exception de type [PamException], et relancera cette dernire pour la couche [mtier].

http://tahe.developpez.com/java/javaee

34/341

la couche [mtier] laissera remonter les exceptions lances par la couche [dao]. Elle encapsulera toute exception survenant dans la couche [mtier], dans une exception de type [PamException] et relancera cette dernire pour la couche [ui]. la couche [ui] intercepte toutes les exceptions qui remontent des couches [mtier] et [dao]. Elle se contentera d'afficher l'exception sur la console ou l'interface graphique.

Examinons maintenant successivement l'implmentation des couches [dao] et [metier].

7 La couche [dao] de l'application [PAM]


Nous nous plaons dans le cadre de l'architecture suivante : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

7.1

Implmentation

Lectures conseilles : paragraphe 3.1.3 de [ref1] Question : En utilisant l'intgration Spring / JPA, crire les classes [CotisationDao, IndemniteDao, EmployeDao] d'implmentation des interfaces [ICotisationDao, IIndemniteDao, IEmployeDao]. Chaque mthode de classe interceptera une ventuelle exception et l'encapsulera dans une exception de type [PamException] avec un code d'erreur propre l'exception intercepte. Les classes d'implmentation feront partie du paquetage [dao] :

7.2

Configuration

Lectures conseilles : paragraphe 3.1.5 de [ref1] L'intgration Dao / JPA est configure par le fichier Spring [spring-config-dao.xml] et le fichier JPA [persistence.xml] :

http://tahe.developpez.com/java/javaee

35/341

Question : crire le contenu de ces deux fichiers. On supposera que la base de donnes utilise est la base MySQL5 utilise page 18. Le fichier Spring dfinira les trois beans suivants : employeDao de type EmployeDao, indemniteDao de type IndemniteDao, cotisationDao de type CotisationDao. Par ailleurs, l'implmentation JPA utilise sera Hibernate.

7.3

Tests

Maintenant que la couche [dao] est crite et configure, nous pouvons la tester. L'architecture des tests sera la suivante : 4

Couche [tests] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

7.3.1
7.3.1.1

Tests 1
InitDB

Lectures conseilles : paragraphes 3.1.6 et 3.1.7 de [ref1]

Nous allons crer deux programmes de tests de la couche [dao]. Ceux-ci seront placs dans le paquetage [dao] [2] de la branche [Test Packages] [1] du projet Netbeans. Cette branche n'est pas incluse dans le projet gnr par l'option [Build project], ce qui nous assure que les programmes de tests que nous y plaons ne seront pas inclus dans le .jar final du projet.

1 2 3

Les classes places dans la branche [Test Packages] ont connaissance des classes prsentes dans la branche [Source Packages] ainsi que des bibliothques de classes du projet. Si les tests ont besoin de bibliothques autres que celles du projet, celles-ci doivent tre dclares dans la branche [Test Libraries] [3]. Ici la bibliothque JUnit est prsente par dfaut en deux versions. Les classes de tests utilisent l'outil de tests unitaires JUnit : [JUnitInitDB] ne fait aucun test. Elle remplit la base de donnes avec quelques enregistrements et affiche ensuite ceux-ci sur la console. [JUnitDao] fait une srie de tests dont elle vrifie le rsultat. Le squelette de la classe [JUnitInitDB] est le suivant :

http://tahe.developpez.com/java/javaee

36/341

1. package dao; 2. 3. ... 4. 5. public class JUnitInitDB { 6. 7. private IEmployeDao employeDao = null; 8. private ICotisationDao cotisationDao = null; 9. private IIndemniteDao indemniteDao = null; 10. 11. @BeforeClass 12. public void init(){ 13. // configuration de l'application 14. ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-dao.xml"); 15. // couches dao 16. employeDao = (IEmployeDao) ctx.getBean("employeDao"); 17. cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao"); 18. indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao"); 19. } 20. 21. @Test 22. public void initDB(){ 23. // on remplit la base 24. ... 25. // on affiche le contenu de la base 26. ... 27. } 28. 29. @Before() 30. public void clean(){ 31. // on vide la base 32. ... 33. } 34. }

la mthode [init] est excute avant le dbut de la srie des tests (annotation @BeforeClass). Elle instancie la couche [dao]. la mthode [clean] est excute avant chaque test (annotation @Before). Elle vide la base de donnes. la mthode [initDB] est un test (annotation @Test). C'est le seul. Un test doit contenir des instructions d'assertion Assert.assertCondition. Ici il n'y en aura aucune. La mthode est donc un faux test. Elle a pour rle de remplir la base de donnes avec quelques lignes puis d'afficher le contenu de la base sur la console. Ce sont les mthodes create et findAll des couches [dao] qui sont ici utilises.

Question : complter le code de la classe [JUnitInitDB]. On s'aidera de l'exemple du paragraphe 3.1.6 de [ref1]. Le code gnrera le contenu prsent au paragraphe 2.1, page 7.

7.3.1.2

Mise en oeuvre des tests

Nous sommes dsormais prts pour excuter [InitDB]. Nous dcrivons la procdure avec le SGBD MySQL5 :

http://tahe.developpez.com/java/javaee

37/341

4 2 3 5 1 7

6 8 9

les classes [1] et les fichiers de configuration [2, 3] de la couche [dao] sont mis en place en [4], on ajoute les bibliothques suivantes au projet : celles d'Hibernate qu'on trouve dans [lib/hibernate-tools] celles de Spring qu'on trouve dans [lib/spring] celle du pilote Jdbc de MySQL qu'on trouve dans [lib/divers] en [5] les bibliothques rajoutes au projet le SGBD MySQL5 est lanc. Il doit avoir la base [dbpam_hibernate] avec un utilisateur [dbpam/dbpam] ayant tous les privilges dessus. le projet est construit [6] la classe [JUnitInitDB] est excute [7] la fentre [Test Results] [8] dit que les tests ont t russis. Ce message n'est pas significatif ici, car le programme [JUnitInitDB] ne contient aucune instruction d'assertion Assert.assertCondition, qui pourrait provoquer l'chec du test. Nanmoins, cela montre qu'il n'y a pas eu d'exception l'excution du test.

La fentre [Output] [9] contient les logs de l'excution, ceux de Spring et ceux du test lui-mme. Les affichages faits par la classe JUnitInitDB sont les suivants :
1. ------------- Standard Output --------------2. Employs ---------------------3. jpa.Employe[id=5,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue des oiseaux,ville=St Corentin,code postal=49203,indice=2] 4. jpa.Employe[id=6,version=0,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La brlerie,ville=St Marcel,code postal=49014,indice=1] 5. Indemnits ---------------------6. jpa.Indemnite[id=5,version=0,indice=1,base heure=1.93,entretien jour2.0,repas jour=3.0,indemnits CP=12.0] 7. jpa.Indemnite[id=6,version=0,indice=2,base heure=2.1,entretien jour2.1,repas jour=3.1,indemnits CP=15.0] 8. Cotisations ---------------------9. jpa.Cotisation[id=3,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retraite=7.88] 10. ------------- ---------------- ---------------

Les tables [EMPLOYES, INDEMNITES, COTISATIONS] ont t remplies. On peut le vrifier avec la connexion Netbeans la base [dbpam_hibernate] dcrite page 20.

http://tahe.developpez.com/java/javaee

38/341

en [1], dans l'onglet [services], on visualise les donnes de la table [employes] de la connexion [dbpam_hibernate] [2] en [3] le rsultat

7.3.2
7.3.2.1

Tests 2
JUnitDao

Nous nous intressons maintenant une seconde classe de tests [JUnitDao] :

Le squelette de la classe sera le suivant :


1. package dao; 2. 3. import exception.PamException; 4. ... 5. 6. public class JUnitDao { 7. 8. // couches dao 9. static private IEmployeDao employeDao; 10. static private IIndemniteDao indemniteDao; 11. static private ICotisationDao cotisationDao; 12. 13. @BeforeClass 14. public static void init() { 15. // log 16. log("init"); 17. // configuration de l'application 18. ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-dao.xml"); 19. // couches dao 20. employeDao = (IEmployeDao) ctx.getBean("employeDao"); 21. indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao"); 22. cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao"); 23. } 24. 25. @AfterClass 26. public static void terminate() { 27. } 28. 29. @Before() 30. public void clean() { 31. ... 32. } 33.

http://tahe.developpez.com/java/javaee

39/341

34. // logs 35. private static void log(String message) { 36. System.out.println("----------- " + message); 37. } 38. 39. // tests 40. @Test 41. public void test01() { 42. log("test01"); 43. // liste des cotisations 44. List<Cotisation> cotisations = cotisationDao.findAll(); 45. int nbCotisations = cotisations.size(); 46. // on ajoute une cotisation 47. Cotisation cotisation = cotisationDao.create(new Cotisation(3.49, 6.15, 9.39, 7.88)); 48. // on la demande 49. cotisation = cotisationDao.find(cotisation.getId()); 50. // vrification 51. Assert.assertNotNull(cotisation); 52. Assert.assertEquals(3.49, cotisation.getCsgrds(), 1e-6); 53. Assert.assertEquals(6.15, cotisation.getCsgd(), 1e-6); 54. Assert.assertEquals(9.39, cotisation.getSecu(), 1e-6); 55. Assert.assertEquals(7.88, cotisation.getRetraite(), 1e-6); 56. // on la modifie 57. cotisation.setCsgrds(-1); 58. cotisation.setCsgd(-1); 59. cotisation.setRetraite(-1); 60. cotisation.setSecu(-1); 61. Cotisation cotisation2 = cotisationDao.edit(cotisation); 62. // vrifications 63. Assert.assertEquals(cotisation.getVersion() + 1, cotisation2.getVersion()); 64. Assert.assertEquals(-1, cotisation2.getCsgrds(), 1e-6); 65. Assert.assertEquals(-1, cotisation2.getCsgd(), 1e-6); 66. Assert.assertEquals(-1, cotisation2.getRetraite(), 1e-6); 67. Assert.assertEquals(-1, cotisation2.getSecu(), 1e-6); 68. // on demande l'lment modifi 69. Cotisation cotisation3 = cotisationDao.find(cotisation2.getId()); 70. // vrifications 71. Assert.assertEquals(cotisation3.getVersion(), cotisation2.getVersion()); 72. Assert.assertEquals(-1, cotisation3.getCsgrds(), 1e-6); 73. Assert.assertEquals(-1, cotisation3.getCsgd(), 1e-6); 74. Assert.assertEquals(-1, cotisation3.getRetraite(), 1e-6); 75. Assert.assertEquals(-1, cotisation3.getSecu(), 1e-6); 76. // on supprime l'lment 77. cotisationDao.destroy(cotisation3); 78. // vrifications 79. Cotisation cotisation4 = cotisationDao.find(cotisation3.getId()); 80. Assert.assertNull(cotisation4); 81. cotisations = cotisationDao.findAll(); 82. Assert.assertEquals(nbCotisations, cotisations.size()); 83. } 84. 85. 86. @Test 87. public void test02(){ 88. log("test02"); 89. // on demande la liste des indemnits 90. ... 91. // on ajoute une Indemnite indemnite 92. .. 93. // on va chercher indemnite en base on rcupre indemnite1 94. .. 95. // on vrifie que indemnite1 = indemnite 96. ... 97. // on modifie l'indemnit obtenue et on persiste la modification en BD. On obtient indemnite2 98. ... 99. // on vrifie la version de indemnite2 100. ... 101. // on va chercher indemnite2 en base on obtient indemnite3 102. ... 103. // on vrifie que indemnite3 = indemnite2 104. ... 105. // on supprime en base l'image de indemnite3 106. ... 107. // on va chercher indemnite3 en base 108. ... 109. // on vrifie qu'on a obtenu une rfrence null 110. ... 111. } 112. 113. @Test 114. public void test03(){ 115. log("test03"); 116. // on rpte un test analogue aux prcdents pour Employe

http://tahe.developpez.com/java/javaee

40/341

117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187.

... } @Test public void test04(){ log("test04"); // on teste la mthode [IEmployeDao].find(String SS) // d'abord avec un employ existant // puis avec un employ inexistant ... } @Test public void test05(){ log("test05"); // on cre deux indemnits avec le mme indice // enfreint la contrainte d'unicit de l'indice // on vrifie qu'une exception de type PamException se produit // et qu'elle a le n d'erreur attendu ... } @Test public void test06(){ log("test06"); // on cre deux employs avec le mme n SS // enfreint la contrainte d'unicit sur le n SS // on vrifie qu'une exception de type PamException se produit // et qu'elle a le n d'erreur attendu ... } @Test public void test07(){ log("test07"); // on cre deux employs avec le mme n SS, le 1er avec create, le 2me avec edit // enfreint la contrainte d'unicit sur le n SS // on vrifie qu'une exception de type PamException se produit // et qu'elle a le n d'erreur attendu ... } @Test public void test08(){ log("test08"); // supprimer un employ qui n'existe pas ne provoque pas d'exception // il est ajout puis dtruit on le vrifie ... } @Test public void test09(){ log("test09"); // modifier un employ sans avoir la bonne version doit provoquer une exception // on le vrifie ... } @Test public void test10(){ log("test10"); // supprimer un employ sans avoir la bonne version doit provoquer une exception // on le vrifie ... } // getters et setters ...

Dans la classe de tests prcdente, la base est vide avant chaque test. Question : crire les mthodes suivantes : - test02 : on s'inspirera de test01 - test03 : un employ a un champ de type Indemnite. Il faut donc crer une entit Indemnite et une entit Employe - test04.

http://tahe.developpez.com/java/javaee

41/341

7.3.2.2

Mise en oeuvre des tests

En procdant de la mme faon que pour la classe de tests [JUnitInitDB], on obtient les rsultats suivants :

en [1], on excute la classe de tests en [2], les rsultats des tests dans la fentre [Test Results]

Provoquons une erreur pour voir comment cela est signal dans la page web des rsultats :
1. @Test 2. public void test01() { 3. log("test01"); 4. // liste des cotisations 5. List<Cotisation> cotisations = cotisationDao.findAll(); 6. int nbCotisations = cotisations.size(); 7. // on ajoute une cotisation 8. Cotisation cotisation = cotisationDao.create(new Cotisation(3.49, 6.15, 9.39, 7.88)); 9. // on la demande 10. cotisation = cotisationDao.find(cotisation.getId()); 11. // vrification 12. Assert.assertNotNull(cotisation); 13. Assert.assertEquals(0, cotisation.getCsgrds(), 1e-6); 14. Assert.assertEquals(6.15, cotisation.getCsgd(), 1e-6); 15. Assert.assertEquals(9.39, cotisation.getSecu(), 1e-6); 16. Assert.assertEquals(7.88, cotisation.getRetraite(), 1e-6); 17. // on la modifie 18. .... 19. }

Ligne 13, l'assertion va provoquer une erreur, la valeur de Csgrds tant 3.49 (ligne 8). L'excution de la classe de tests donne les rsultats suivants :

1 2

la page des rsultats [1] montre maintenant qu'il y a eu des tests non russis [2]. en [3], un rsum de l'exception qui a fait chouer le test. On y trouve le n de la ligne du code Java o s'est produite l'exception.

http://tahe.developpez.com/java/javaee

42/341

8 La couche [metier] de l'application [PAM]


Maintenant que la couche [dao] a t crite, nous passons l'tude de la couche mtier [2] : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

8.1

L'interface Java [IMetier]

Celle-ci a t dcrite au paragraphe 5, page 29. Nous la rappelons ci-dessous :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. package metier; import java.util.List; import jpa.Employe; public interface IMetier { // obtenir la feuille de salaire public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills ); // liste des employs public List<Employe> findAllEmployes(); }

L'implmentation de la couche [metier] sera faite dans un paquetage [metier] :

Le paquetage [metier] comprendra, outre l'interface [IMetier] et son implmentation [Metier], deux autres classes [FeuilleSalaire] et [ElementsSalaire]. La classe [FeuilleSalaire] a t brivement prsente au paragraphe 5, page 29. Nous revenons dessus maintenant.

8.2

La classe [FeuilleSalaire]

La mthode [calculerFeuilleSalaire] de l'interface [IMetier] rend un objet de type [FeuilleSalaire] qui reprsente les diffrents lments d'une feuille de salaire. Sa dfinition est la suivante :
1. package metier; 2. 3. import jpa.Cotisation; 4. import jpa.Employe; 5. import jpa.Indemnite; 6. 7. public class FeuilleSalaire implements Serializable{ 8. // champs privs 9. private Employe employe; 10. private Cotisation cotisation; 11. private ElementsSalaire elementsSalaire; 12. 13. // constructeurs 14. public FeuilleSalaire() { 15. 16. } 17. 18. public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) { 19. setEmploye(employe);

http://tahe.developpez.com/java/javaee

43/341

20. setCotisation(cotisation); 21. setElementsSalaire(elementsSalaire); 22. } 23. 24. // toString 25. public String toString() { 26. return "[" + employe + "," + cotisation + "," + elementsSalaire + "]"; 27. } 28. 29. // accesseurs 30. ... 31. }

ligne 7 : la classe implmente l'interface Serializable parce que ses instances sont susceptibles d'tre changes sur le rseau. ligne 9 : l'employ concern par la feuille de salaire ligne 10 : les diffrents taux de cotisation ligne 11 : les diffrentes indemnits lies l'indice de l'employ ligne 12 : les lments constitutifs de son salaire lignes 14-22 : les deux constructeurs de la classe lignes 25-27 : mthode [toString] identifiant un objet [FeuilleSalaire] particulier lignes 29 et au-del : les accesseurs publics aux champs privs de la classe

La classe [ElementsSalaire] rfrence ligne 11 de la classe [FeuilleSalaire] ci-dessus, rassemble les lments constituant une fiche de paie. Sa dfinition est la suivante :
1. package metier; 2. 3. public class ElementsSalaire implements Serializable{ 4. 5. // champs privs 6. private double salaireBase; 7. private double cotisationsSociales; 8. private double indemnitesEntretien; 9. private double indemnitesRepas; 10. private double salaireNet; 11. 12. // constructeurs 13. public ElementsSalaire() { 14. 15. } 16. 17. public ElementsSalaire(double salaireBase, double cotisationsSociales, 18. double indemnitesEntretien, double indemnitesRepas, 19. double salaireNet) { 20. setSalaireBase(salaireBase); 21. setCotisationsSociales(cotisationsSociales); 22. setIndemnitesEntretien(indemnitesEntretien); 23. setIndemnitesRepas(indemnitesRepas); 24. } 25. 26. // toString 27. public String toString() { 28. return "[salaire base=" + salaireBase + ",cotisations sociales=" + cotisationsSociales + ",indemnits d'entretien=" 29. + indemnitesEntretien + ",indemnits de repas=" + indemnitesRepas + ",salaire net=" 30. + salaireNet + "]"; 31. } 32. 33. // accesseurs publics 34. ... 35. }

ligne 3 : la classe implmente l'interface Serializable parce qu'elle est un composant de la classe FeuilleSalaire qui doit tre srialisable. ligne 6 : le salaire de base ligne 7 : les cotisations sociales payes sur ce salaire de base ligne 8 : les indemnits journalires d'entretien de l'enfant ligne 9 : les indemnits journalires de repas de l'enfant ligne 10 : le salaire net payer l'assistante maternelle lignes 12-24 : les constructeurs de la classe lignes 27-31 : mthode [toString] identifiant un objet [ElementsSalaire] particulier lignes 34 et au-del : les accesseurs publics aux champs privs de la classe

http://tahe.developpez.com/java/javaee

44/341

8.3

La classe d'implmentation [Metier] de la couche [metier]

La classe d'implmentation [Metier] de la couche [metier] pourrait tre la suivante :


1. package metier; 2. 3. ... 4. 5. @Transactional 6. public class Metier implements IMetier { 7. 8. // rfrence sur la couche [dao] 9. private ICotisationDao cotisationDao = null; 10. private IEmployeDao employeDao=null; 11. 12. 13. // obtenir la feuille de salaire 14. public FeuilleSalaire calculerFeuilleSalaire(String SS, 15. double nbHeuresTravailles, int nbJoursTravaills) { 16. ... 17. } 18. 19. // liste des employs 20. public List<Employe> findAllEmployes() { 21. ... 22. } 23. 24. // getters et setters 25. ... 26. }

ligne 5 : l'annotation Spring @Transactional fait que chaque mthode de la classe se droulera au sein d'une transaction. lignes 9-11 : les rfrence sur les couches [dao] des entits [Cotisation, Employe, Indemnite] lignes 14-17 : la mthode [calculerFeuilleSalaire] lignes 20-22 : la mthode [findAllEmployes] ligne 24 et au-del : les accesseurs publics des champs privs de la classe

Question : crire le code de la mthode [findAllEmployes]. Question : crire le code de la mthode [calculerFeuilleSalaire]. On notera les points suivants : le mode de calcul du salaire a t expliqu au paragraphe 2.2, page 8. si le paramtre [SS] ne correspond aucun employ (la couche [dao] a renvoy un pointeur null), la mthode lancera une exception de type [PamException] avec un code d'erreur appropri.

8.4

Tests de la couche [metier]

Nous crons deux programmes de test : 1

2 3 Les classes de tests [3] sont crs dans un paquetage [metier] [2] de la branche [Test Packages] [1] du projet. La classe [JUnitMetier_1] pourrait tre la suivante :
1. package metier; 2. 3. ... 4. 5. public class JUnitMetier_1 { 6.

http://tahe.developpez.com/java/javaee

45/341

7. // couche mtier 8. private IMetier metier; 9. 10. @BeforeClass 11. public void init(){ 12. // log 13. log("init"); 14. // configuration de l'application 15. // instanciation couche [metier] 16. ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metier-dao.xml"); 17. metier = (IMetier) ctx.getBean("metier"); 18. // couches dao 19. IEmployeDao employeDao=(IEmployeDao) ctx.getBean("employeDao"); 20. ICotisationDao cotisationDao=(ICotisationDao) ctx.getBean("cotisationDao"); 21. IIndemniteDao indemniteDao=(IIndemniteDao) ctx.getBean("indemniteDao"); 22. // on vide la base 23. for(Employe employe:employeDao.findAll()){ 24. employeDao.destroy(employe); 25. } 26. for(Cotisation cotisation:cotisationDao.findAll()){ 27. cotisationDao.destroy(cotisation); 28. } 29. for(Indemnite indemnite : indemniteDao.findAll()){ 30. indemniteDao.destroy(indemnite); 31. } 32. // on la remplit 33. Indemnite indemnite1=indemniteDao.create(new Indemnite(1,1.93,2,3,12)); 34. Indemnite indemnite2=indemniteDao.create(new Indemnite(2,2.1,2.1,3.1,15)); 35. Employe employe2=employeDao.create(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",indemnite2)); 36. Employe employe1=employeDao.create(new Employe("260124402111742","Laverti","Justine","La brlerie","St Marcel","49014",indemnite1)); 37. Cotisation cotisation1=cotisationDao.create(new Cotisation(3.49,6.15,9.39,7.88)); 38. } 39. 40. // logs 41. private void log(String message) { 42. System.out.println("----------- " + message); 43. } 44. 45. // test 46. @Test 47. public void test01(){ 48. // calcul de feuilles de salaire 49. System.out.println(metier.calculerFeuilleSalaire("260124402111742",30, 5)); 50. System.out.println(metier.calculerFeuilleSalaire("254104940426058",150, 20)); 51. try { 52. System.out.println(metier.calculerFeuilleSalaire("xx", 150, 20)); 53. } catch (PamException ex) { 54. System.err.println(String.format("PamException[Code=%d, message=%s]",ex.getCode(), ex.getMessage())); 55. } 56. } 57. }

Il n'y a pas d'assertion Assert.assertCondition dans la classe. On cherche simplement calculer quelques salaires afin de les vrifier ensuite la main. L'affichage cran obtenu par l'excution de la classe prcdente est le suivant :
1. 2. 3. 4. Testsuite: metier.JUnitMetier_1 ----------- init .... [jpa.Employe[id=22,version=0,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La brlerie,ville=St Marcel,code postal=49014,indice=1],jpa.Cotisation[id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retraite=7.88 ],jpa.Indemnite[id=29,version=0,indice=1,base heure=1.93,entretien jour2.0,repas jour=3.0,indemnits CP=12.0],[salaire base=64.85,cotisations sociales=17.45,indemnits d'entretien=10.0,indemnits de repas=15.0,salaire net=72.4]] 5. [jpa.Employe[id=21,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue des oiseaux,ville=St Corentin,code postal=49203,indice=2],jpa.Cotisation[id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retraite=7.88 ],jpa.Indemnite[id=30,version=0,indice=2,base heure=2.1,entretien jour2.1,repas jour=3.1,indemnits CP=15.0],[salaire base=362.25,cotisations sociales=97.48,indemnits d'entretien=42.0,indemnits de repas=62.0,salaire net=368.77]] 6. PamException[Code=101, message=L'employ de n[xx] est introuvable] 7. Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,234 sec

ligne 4 : la feuille de salaire de Justine Laverti ligne 5 : la feuille de salaire de Marie Jouveinal ligne 6 : l'exception due au fait que l'employ de n SS 'xx' n'existe pas.

http://tahe.developpez.com/java/javaee

46/341

Question : la ligne 17 de [JUnitMetier_1] utilise le bean Spring nomm metier. Donner la dfinition de ce bean dans le fichier [spring-config-metier-dao.xml]. La classe [JUnitMetier_2] pourrait tre la suivante :
1. package metier; 2. 3. ... 4. public class JUnitMetier_2 { 5. 6. // couche mtier 7. private IMetier metier; 8. 9. @BeforeClass 10. public void init(){ 11. ... 12. } 13. 14. // logs 15. private void log(String message) { 16. System.out.println("----------- " + message); 17. } 18. 19. // test 20. @Test 21. public void test01(){ 22. ... 23. } 24. }

La classe [JUnitMetier_2] est une copie de la classe [JUnitMetier_1] o cette fois, des assertions ont t places dans la mthode test01. Question : crire la mthode test01. Lors de l'excution de la classe [JUnitMetier_2], on obtient les rsultats suivants si tout va bien :

9 La couche [ui] de l'application [PAM] version console


Maintenant que la couche [metier] a t crite, il nous reste crire la couche [ui] [1] : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

Nous crerons deux implmentations diffrentes de la couche [ui] : une version console et une version graphique swing :

http://tahe.developpez.com/java/javaee

47/341

9.1

La classe [ui.console.Main]

Nous nous intressons tout d'abord l'application console implmente par la classe [ui.console.Main] ci-dessus. Son fonctionnement a t dcrit au paragraphe 2.3, page 8. Le squelette de la classe [Main] pourrait tre le suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. package ui.console; import exception.PamException; import metier.FeuilleSalaire; import metier.IMetier; import java.util.ArrayList; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { /** * @param args */ public static void main(String[] args) { // donnes locales final String syntaxe = "pg num_securite_sociale nb_heures_travailles nb_jours_travaills"; // on vrifie le nombre de paramtres ... // liste des erreurs ArrayList erreurs = new ArrayList(); // le second paramtre doit tre un nombre rel >0 ... // erreur ? if (...) { erreurs.add("Le nombre d'heures travailles [" + args[1] + "] est erron"); } // le troisime paramtre doit tre un nombre entier >0 ... // erreur ? if (...) { erreurs.add("Le nombre de jours travaills [" + args[2] + "] est erron"); } // des erreurs ? if (erreurs.size() != 0) { for (int i = 0; i < erreurs.size(); i++) { System.err.println(erreurs.get(i)); } return; } // c'est bon - on peut demander la feuille de salaire FeuilleSalaire feuilleSalaire = null; try { // instanciation couche [metier] ... // calcul de la feuille de salaire ... } catch (PamException ex) { System.err.println("L'erreur suivante s'est produite : "+ ex.getMessage()); return; } catch (Exception ex) { System.err.println("L'erreur suivante s'est produite : "+ ex.toString()); return; } // affichage dtaill String output = "Valeurs saisies :\n"; output += ajouteInfo("N de scurit sociale de l'employ", args[0]);

http://tahe.developpez.com/java/javaee

48/341

62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85.

86. 87. 88. System.out.println(output); 89. } 90. 91. static String ajouteInfo(String message, String valeur) { 92. return message + " : " + valeur + "\n"; 93. } 94. }

output += ajouteInfo("Nombre d'heures travailles", args[1]); output += ajouteInfo("Nombre de jours travaills", args[2]); output += ajouteInfo("\nInformations Employ", ""); output += ajouteInfo("Nom", feuilleSalaire.getEmploye().getNom()); output += ajouteInfo("Prnom", feuilleSalaire.getEmploye().getPrenom()); output += ajouteInfo("Adresse", feuilleSalaire.getEmploye().getAdresse()); output += ajouteInfo("Ville", feuilleSalaire.getEmploye().getVille()); output += ajouteInfo("Code Postal", feuilleSalaire.getEmploye().getCodePostal()); output += ajouteInfo("Indice", ""+ feuilleSalaire.getEmploye().getIndemnite().getIndice()); output += ajouteInfo("\nInformations Cotisations", ""); output += ajouteInfo("CSGRDS", ""+ feuilleSalaire.getCotisation().getCsgrds() + " %"); output += ajouteInfo("CSGD", ""+ feuilleSalaire.getCotisation().getCsgd() + " %"); output += ajouteInfo("Retraite", ""+ feuilleSalaire.getCotisation().getRetraite() + " %"); output += ajouteInfo("Scurit sociale", ""+ feuilleSalaire.getCotisation().getSecu() + " %"); output += ajouteInfo("\nInformations Indemnits", ""); output += ajouteInfo("Salaire horaire", ""+ feuilleSalaire.getEmploye().getIndemnite().getBaseHeure() + " euro"); output += ajouteInfo("Entretien/jour", ""+ feuilleSalaire.getEmploye().getIndemnite().getEntretienJour() + " euro"); output += ajouteInfo("Repas/jour", ""+ feuilleSalaire.getEmploye().getIndemnite().getRepasJour() + " euro"); output += ajouteInfo("Congs Pays", ""+ feuilleSalaire.getEmploye().getIndemnite().getIndemnitesCP()+ " %"); output += ajouteInfo("\nInformations Salaire", ""); output += ajouteInfo("Salaire de base", ""+ feuilleSalaire.getElementsSalaire().getSalaireBase()+ " euro"); output += ajouteInfo("Cotisations sociales", ""+ feuilleSalaire.getElementsSalaire().getCotisationsSociales()+ " euro"); output += ajouteInfo("Indemnits d'entretien", ""+ feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ " euro"); output += ajouteInfo("Indemnits de repas", ""+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas()+ " euro"); output += ajouteInfo("Salaire net", ""+ feuilleSalaire.getElementsSalaire().getSalaireNet() + " euro");

Question : complter le code ci-dessus.

9.2

Excution

Pour excuter la classe [ui.console.Main], on procdera de la faon suivante :

http://tahe.developpez.com/java/javaee

49/341

6 7 3

1 4 5

en [1], slectionner les proprits du projet en [2], slectionner la proprit [Run] du projet utiliser le bouton [3] pour dsigner la classe (dite classe principale) excuter slectionner la classe [4] et valider la slection [5] la classe apparat en [6]. Celle-ci a besoin de trois arguments pour s'excuter (n SS, nombre d'heures travailles, nombre de jours travaills). Ces arguments sont placs en [7]. ceci fait, on peut excuter le projet [8]. La configuration prcdente fait que c'est la classe [ui.console.Main] qui va tre excute.

Les rsultats de l'excution sont obtenus dans la fentre [output] :

http://tahe.developpez.com/java/javaee

50/341

10 La couche [ui] de l'application [PAM] version graphique


Nous implmentons maintenant la couche [ui] avec une interface graphique : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

http://tahe.developpez.com/java/javaee

51/341

en [1], la classe [PamJFrame] de l'interface graphique en[2] : l'interface graphique

10.1

Un rapide tutoriel

Pour crer l'interface graphique, on pourra procder de la faon suivante :

2 3

[1] : on cre un nouveau fichier avec le bouton [1] [New File...] [2] : on choisit la catgorie du fichier [Swing GUI Forms], c.a.d. formulaires graphiques [3] : on choisit le type [JFrame Form], un type de formulaire vide [4] : on passe l'tape suivante

5 8 6 9

[5] : on donne un nom au formulaire qui sera aussi une classe [6] : on place le formulaire dans un paquetage [7] : on valide la configuration du formulaire [8] : le formulaire est ajout l'arborescence du projet [9] : le formulaire est accessible selon deux perspectives : [Design] [9] qui permet de dessiner les diffrents composants du formulaire, [Source] [10 ci-dessous] qui donne accs au code Java du formulaire. Au final, un formulaire est une classe Java comme une autre. La perspective [Design] est une facilit pour dessiner le formulaire. A chaque ajout de composant en mode [Design], du code Java est ajout dans la perspective [Source] pour le prendre en compte.

http://tahe.developpez.com/java/javaee

52/341

11 10

12

[11] : la liste des composants Swing disponibles pour un formulaire est trouve dans la fentre [Palette]. [12] : la fentre [Inspector] prsente l'arborescence des composants du formulaire. Les composants ayant une reprsentation visuelle se retrouveront dans la branche [JFrame], les autres dans la branche [Other Components]. 15

14 13

en [13], nous slectionnons un composant [JLabel] par un clic simple en [14], nous le dposons sur le formulaire en mode [Design] en [15], nous dfinissons les proprits du JLabel (text, font). 17 16

18

en [16], le rsultat obtenu. en [17], on demande la prvisualisation du formulaire en [18], le rsultat en [19], le label [JLabel1] a t ajout l'arborescence des composants dans la fentre [Inspector]

http://tahe.developpez.com/java/javaee

53/341

19

20

21

en [20] et [21] : dans la perspective [Source] du formulaire, du code Java a t ajout pour grer le JLabel ajout.

Un tutoriel sur la construction de formulaires avec Netbeans est disponible l'url [http://www.netbeans.org/kb/trails/matisse.html].

10.2

L'interface graphique [PamJFrame]

On construira l'interface graphique suivante :

http://tahe.developpez.com/java/javaee

54/341

1 2

en [1], l'interface graphique en [2], l'arborescence de ses composants : un JLabel et six conteneurs JPanel

JLabel1

JPanel1

JPanel2

http://tahe.developpez.com/java/javaee

55/341

JPanel3

JPanel4

JPanel5

Travail pratique : construire l'interface graphique prcdente en s'aidant du tutoriel [http://www.netbeans.org/kb/trails/matisse.html].

10.3

Les vnements de l'interface graphique

Lectures conseilles : chapitre [Interfaces graphiques] de [ref2]. Nous grerons le clic sur le bouton [jButtonSalaire]. Pour crer la mthode de gestion de cet vnement, on pourra procder comme suit :

http://tahe.developpez.com/java/javaee

56/341

Le gestionnaire du clic sur le bouton [JButtonSalaire] est gnr :


1. 2. 3. } private void jButtonSalaireActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here:

Le code Java qui associe la mthode prcdente au clic sur le bouton [JButtonSalaire] est lui aussi gnr :
1. jButtonSalaire.setText("Salaire"); 2. jButtonSalaire.addActionListener(new java.awt.event.ActionListener() { 3. public void actionPerformed(java.awt.event.ActionEvent evt) { 4. jButtonSalaireActionPerformed(evt); 5. } 6. });

Ce sont les lignes 2-5 qui indiquent que le clic (evt de type ActionPerformed) sur le bouton [jButtonSalaire] (ligne 2) doit tre gr par la mthode [jButtonSalaireActionPerformed] (ligne 4). Nous grerons galement, l'vnement [caretUpdate] (dplacement du curseur de saisie) sur le champ de saisie [jTextFieldHT]. Pour crer le gestionnaire de cet vnement, nous procdons comme prcdemment :

Le gestionnaire de l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est gnr :


private void jTextFieldHTCaretUpdate(javax.swing.event.CaretEvent evt) { ... }

Le code Java qui associe la mthode prcdente l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est lui aussi gnr :
1. jTextFieldHT.addCaretListener(new javax.swing.event.CaretListener() { 2. public void caretUpdate(javax.swing.event.CaretEvent evt) { 3. jTextFieldHTCaretUpdate(evt); 4. } 5. });

Les lignes 1-4 indiquent que l'vnement [caretUpdate] (ligne 2) sur le bouton [jTextFieldHT] (ligne 1) doit tre gr par la mthode [ jTextFieldHTCaretUpdate] (ligne 3).

10.4

Initialisation de l'interface graphique

Revenons l'architecture de notre application :

http://tahe.developpez.com/java/javaee

57/341

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

La couche [ui] a besoin d'une rfrence sur la couche [metier]. Rappelons comment cette rfrence avait t obtenue dans l'application console :
1. // instanciation couche [metier] 2. ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metier-dao.xml"); 3. IMetier metier = (IMetier) ctx.getBean("metier");

La mthode est la mme dans l'application graphique. Il faut que lorsque celle-ci s'initialise, la rfrence [IMetier metier] de la ligne 3 ci-dessus soit galement initialise. Le code gnr pour l'interface graphique est pour l'instant le suivant :
1. package ui.swing; 2. 3. ... 4. public class PamJFrame extends javax.swing.JFrame { 5. 6. /** Creates new form PamJFrame */ 7. public PamJFrame() { 8. initComponents(); 9. } 10. 11. /** This method is called from within the constructor to 12. * initialize the form. 13. * WARNING: Do NOT modify this code. The content of this method is 14. * always regenerated by the Form Editor. 15. */ 16. // <editor-fold defaultstate="collapsed" desc=" Generated Code "> 17. private void initComponents() { 18. ... 19. }// </editor-fold> 20. 21. private void jTextFieldHTCaretUpdate(javax.swing.event.CaretEvent evt) { 22. ... 23. } 24. 25. private void jButtonSalaireActionPerformed(java.awt.event.ActionEvent evt) { 26. ... 27. } 28. 29. public static void main(String args[]) { 30. java.awt.EventQueue.invokeLater(new Runnable() { 31. public void run() { 32. new PamJFrame().setVisible(true); 33. } 34. }); 35. } 36. 37. // Variables declaration - do not modify 38. private javax.swing.JButton jButtonSalaire; 39. ... 40. // End of variables declaration 41. 42. }

lignes 29-35 : la mthode statique [main] qui lance l'application ligne 32 : une instance de l'interface graphique [PamJFrame] est cre et rendue visible. lignes 7-9 : le constructeur de l'interface graphique. ligne 8 : appel la mthode [initComponents] dfinie ligne 17. Cette mthode est auto-gnre partir du travail fait en mode [Design]. On ne doit pas y toucher. ligne 21 : la mthode qui va grer le dplacement du curseur de saisie dans le champ [jTextFieldHT] ligne 25 : la mthode qui va grer le clic sur le bouton [jButtonSalaire]

Pour ajouter au code prcdent nos propres initialisations, nous pouvons procder comme suit :

http://tahe.developpez.com/java/javaee

58/341

1. /** Creates new form PamJFrame */ 2. public PamJFrame() { 3. initComponents(); 4. doMyInit(); 5. } 6. 7. ... 8. 9. // variables d'instance 10. private IMetier metier=null; 11. private List<Employe> employes=null; 12. private String[] employesCombo=null; 13. private double heuresTravailles=0; 14. 15. // initialisations propritaires 16. public void doMyInit(){ 17. // init contexte 18. try{ 19. // instanciation couche [metier] 20. ... 21. // liste des employs 22. ... 23. }catch (PamException ex){ 24. // le message de l'exception est plac dans [jTextAreaStatus] 25. ... 26. // retour 27. return; 28. } 29. // bouton salaire inhib 30. ... 31. // jScrollPane1 cach 32. ... 33. // spinner jours travaills 34. jSpinnerJT.setModel(new SpinnerNumberModel(0,0,31,1)); 35. // combobox employs 36. employesCombo=new String[employes.size()]; 37. int i=0; 38. for(Employe employe : employes){ 39. employesCombo[i++]=employe.getPrenom()+" "+employe.getNom(); 40. } 41. jComboBoxEmployes.setModel(new DefaultComboBoxModel(employesCombo)); 42. }

ligne 4 : on appelle une mthode propritaire pour faire nos propres initialisations. Celles-ci sont dfinies par le code des lignes 10-42

Question : en vous aidant des commentaires, complter le code de la procdure [doMyInit].

10.5

Gestionnaires d'vnements

Question : crire la mthode [jTextFieldHTCaretUpdate]. Cette mthode doit faire en sorte que si la donne prsente dans le champ [jTextFieldHT] n'est pas un nombre rel >=0, alors le bouton [jButtonSalaire] doit tre inactif. Question : crire la mthode [jButtonSalaireActionPerformed] qui doit afficher la feuille de salaire de l'employ slectionn dans [jComboBoxEmployes].

10.6

Excution de l'interface graphique

Pour excuter l'interface graphique, on modifiera la configuration [Run] du projet :

http://tahe.developpez.com/java/javaee

59/341

en [1], mettre la classe de l'interface graphique

Le projet doit tre complet avec ses fichiers de configuration (persistence.xml, spring-config-metier-dao.xml) et la classe de l'interface graphique. On lancera Le SGBD cible avant d'excuter le projet.

http://tahe.developpez.com/java/javaee

60/341

11 Implmentation de la couche JPA avec EclipseLink


Nous nous intressons l'architecture suivante o la couche JPA est dsormais implmente par EclipseLink : 4

Couche [ui] 1

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink] 5

Couche [JDBC] 6

Spring

11.1

Le projet Netbeans

Le nouveau projet Netbeans est obtenu par recopie du projet prcdent :

4 3 5

http://tahe.developpez.com/java/javaee

61/341

en [1] : aprs un clic droit sur le projet Hibernate, choisir Copy l'aide du bouton [2], choisir le dossier parent du nouveau projet. Le nom du dossier apparat en [3]. en [4], donner un nom au nouveau projet en [5], le nom du dossier du projet avec [6], valider la copie du projet

1 3

en [1], le nouveau projet a t cr en [2], on en fait le projet principal

Le projet doit tre modifi en deux points pour l'adapter la nouvelle couche JPA / EclipseLink : 1. 2. en [3], les fichiers de configuration de Spring doivent tre modifis. On y trouve en effet la configuration de la couche JPA. en [4], les bibliothques du projet doivent tre modifies : celles d'Hibernate doivent tre remplaces par celles de EclipseLink.

Commenons par ce dernier point. On pourra procder de la faon suivante :

1 2 3

en [1], demander les proprits de la bibliothque [Libraries] en [2], slectionner tous les .jar et les supprimer [3]. On gardera les bibliothques ncessaires l'interface graphique [Absolute Layout, Swing Layout Extensions].

http://tahe.developpez.com/java/javaee

62/341

4 7 5 6

en [4], passer la phase d'ajout des nouveaux .jar en [5], slectionner le .jar du pilote Jdbc de MySQL dans [divers] et tous les .jar des dossiers [spring, eclipselink]. Le contenu de ces dossiers a t expliqu au paragraphe 4.1, page 14. en [6], la liste finale des .jar avec en [7], les 2 archives de l'implmentation Jpa / EclipseLink.

Les fichiers de configuration de Spring doivent tre modifis pour indiquer que l'implmentation JPA a chang. Dans les deux fichiers, seule la section configurant la couche JPA change. Par exemple dans [spring-config-metier-dao.xml] on a :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xmlns:tx="http://www.springframework.org/schema/tx" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx2.0.xsd"> 5. 6. <!-- couches applicatives --> 7. <!-- dao --> 8. <bean id="employeDao" class="dao.EmployeDao" /> 9. <bean id="indemniteDao" class="dao.IndemniteDao" /> 10. <bean id="cotisationDao" class="dao.CotisationDao" /> 11. <!-- mtier --> 12. <bean id="metier" class="metier.Metier"> 13. <property name="employeDao" ref="employeDao"/> 14. <property name="indemniteDao" ref="indemniteDao"/> 15. <property name="cotisationDao" ref="cotisationDao"/> 16. </bean> 17. 18. <!-- configuration JPA --> 19. <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 20. <property name="dataSource" ref="dataSource" /> 21. <property name="jpaVendorAdapter"> 22. <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 23. <!-24. <property name="showSql" value="true" /> 25. --> 26. <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> 27. <property name="generateDdl" value="true" /> 28. <!-29. <property name="generateDdl" value="true" /> 30. --> 31. </bean> 32. </property> 33. <property name="loadTimeWeaver"> 34. <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> 35. </property> 36. </bean> 37. 38. <!-- la source de donnees DBCP --> 39. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 40. <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 41. <property name="url" value="jdbc:mysql://localhost:3306/dbpam_hibernate" /> 42. <property name="username" value="dbpam" /> 43. <property name="password" value="dbpam" /> 44. </bean> 45. .... 46. </beans>

http://tahe.developpez.com/java/javaee

63/341

Les lignes 19-36 configurent la couche JPA. L'implmentation JPA utilise est Hibernate (ligne 22). Par ailleurs, la base de donnes cible est [dbpam_hibernate] (ligne 41). Pour passer une implmentation JPA / EclipseLink, les lignes 19-35 ci-dessus sont remplaces par les lignes ci-dessous :
1. 2. <!-- configuration JPA --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 3. <property name="dataSource" ref="dataSource" /> 4. <property name="jpaVendorAdapter"> 5. <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"> 6. <!-7. <property name="showSql" value="true" /> 8. --> 9. <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" /> 10. <!-11. <property name="generateDdl" value="true" /> 12. --> 13. </bean> 14. </property> 15. <property name="loadTimeWeaver"> 16. <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> 17. </property> 18. </bean>

ligne 5 : l'implmentation JPA utilise est EclipseLink ligne 9 : la proprit databasePlatform fixe le SGBD cible, ici MySQL ligne 11 : pour gnrer les tables de la base de donnes lorsque la couche JPA est instancie. Ici, la proprit est en commentaires. ligne 7 : pour visualiser sur la console les ordres SQL mis par la couche JPA. Ici, la proprit est en commentaires.

Par ailleurs, la base de donnes cible devient [dbpam_eclipselink] (ligne 4 ci-dessous) :


1. <!-- la source de donnees DBCP --> 2. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 3. <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 4. <property name="url" value="jdbc:mysql://localhost:3306/dbpam_eclipselink" /> 5. <property name="username" value="dbpam" /> 6. <property name="password" value="dbpam" /> 7. </bean>

11.2

Mise en oeuvre des tests

Avant de tester l'application entire, il est bon de vrifier si les tests JUnit passent avec la nouvelle implmentation JPA. Avant de les faire, on commencera par supprimer les tables de la base de donnes. Pour cela, dans l'onglet [Runtime] de Netbeans, si besoin est, on crera une connexion sur la base dbpam_eclipselink / MySQL5 comme expliqu au paragraphe 4.1, page 20. Une fois connect la base dbpam_eclipselink / MySQL5, on pourra procder la suppression des tables comme montr ci-dessous :

[1] : avant la suppression [2] : aprs la suppression

http://tahe.developpez.com/java/javaee

64/341

Ceci fait, on peut excuter le premier test sur la couche [dao] : InitDB qui remplit la base. Pour que les tables dtruites prcdemment soient recres par l'application, il faut s'assurer que dans la configuration Jpa / EclipseLink de Spring la ligne :
<property name="generateDdl" value="true" />

existe et n'est pas mise en commentaires. Nous construisons le projet (Build) puis nous excutons le test [JUnitInitDB] : 2

en [1], le test InitDB est excut. en [2], il choue. L'exception est lance par Spring et non par un test qui aurait chou. Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [spring-config-dao.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation. Spring indique qu'il y a un problme de configuration. Le message n'est pas clair. La raison de l'exception a t explique au paragraphe 3.1.9 de [ref1]. Pour que la configuration Spring / EclipseLink fonctionne, la Jvm qui excute l'application doit tre lance avec un paramtre particulier, un agent Java. La forme de ce paramtre est la suivante :
-javaagent:C:\...\lib\spring\spring-agent.jar

[spring-agent.jar] est l'agent Java dont a besoin la Jvm pour grer la configuration Spring / EclipeLink. Il a t plac dans le dossier [lib / spring]. Lorsqu'on excute un projet, il est possible de passer des arguments la Jvm :

http://tahe.developpez.com/java/javaee

65/341

2 3

en [1], on accde aux proprits du projet en [2], les proprit du Run en [3], on passe le paramtre -javaagent la Jvm

11.3
11.3.1

Tests de la couche [dao]


InitDB

Maintenant, nous sommes prts pour tester de nouveau [InitDB]. Cette fois-ci les rsultats obtenus sont les suivants :

en [1], le test a t russi en [2], dans l'onglet [Services], on rafrachit la connexion qu'a Netbeans avec la base [dbpam_eclipselink] en [3], quatre tables ont t cres

http://tahe.developpez.com/java/javaee

66/341

5 6

en [5], on visualise le contenu de la table [employes] en [6], le rsultat.

11.3.2

JUnitDao

L'excution de la classe de tests [JUnitDao] peut chouer, mme si avec l'implmentation Jpa / Hibernate, elle avait russi. Pour comprendre pourquoi, analysons un exemple. La mthode teste est la mthode IndemniteDao.create suivante :
1. package dao; 2. 3. ... 4. @Transactional(propagation=Propagation.REQUIRED) 5. public class IndemniteDao implements IIndemniteDao{ 6. 7. @PersistenceContext 8. private EntityManager em; 9. 10. // constructeur 11. public IndemniteDao() { 12. } 13. 14. // crer une indemnit 15. public Indemnite create(Indemnite indemnite) { 16. try{ 17. em.persist(indemnite); 18. }catch(Throwable th){ 19. throw new PamException(th,31); 20. } 21. return indemnite; 22. } 23. 24. ... 25. }

lignes 15-22 : la mthode teste

La mthode de test est la suivante :


1. package dao; 2. 3. ... 4. 5. public class JUnitDao { 6. 7. // couches dao 8. static private IEmployeDao employeDao; 9. static private IIndemniteDao indemniteDao; 10. static private ICotisationDao cotisationDao; 11. 12. @BeforeClass 13. public static void init() { 14. // log 15. log("init"); 16. // configuration de l'application 17. ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-dao.xml"); 18. // couches dao 19. employeDao = (IEmployeDao) ctx.getBean("employeDao"); 20. indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao"); 21. cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao"); 22. } 23. 24. @Before()

http://tahe.developpez.com/java/javaee

67/341

25. public void clean() { 26. // on vide la base 27. for (Employe employe : employeDao.findAll()) { 28. employeDao.destroy(employe); 29. } 30. for (Cotisation cotisation : cotisationDao.findAll()) { 31. cotisationDao.destroy(cotisation); 32. } 33. for (Indemnite indemnite : indemniteDao.findAll()) { 34. indemniteDao.destroy(indemnite); 35. } 36. } 37. 38. // logs 39. private static void log(String message) { 40. System.out.println("----------- " + message); 41. } 42. 43. // tests 44. . 45. @Test 46. public void test05() { 47. log("test05"); 48. // on cre deux indemnits avec le mme indice 49. // enfreint la contrainte d'unicit de l'indice 50. boolean erreur = true; 51. Indemnite indemnite1 = null; 52. Indemnite indemnite2 = null; 53. Throwable th = null; 54. try { 55. indemnite1 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12)); 56. indemnite2 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12)); 57. erreur = false; 58. } catch (PamException ex) { 59. th = ex; 60. // vrifications 61. Assert.assertEquals(31, ex.getCode()); 62. } catch (Throwable th1) { 63. th = th1; 64. } 65. // vrifications 66. Assert.assertTrue(erreur); 67. // chane des exceptions 68. System.out.println("Chane des exceptions --------------------------------------"); 69. System.out.println(th.getClass().getName()); 70. while (th.getCause() != null) { 71. th = th.getCause(); 72. System.out.println(th.getClass().getName()); 73. } 74. // la 1re indemnit a du tre persiste 75. Indemnite indemnite = indemniteDao.find(indemnite1.getId()); 76. // vrification 77. Assert.assertNotNull(indemnite); 78. Assert.assertEquals(1, indemnite.getIndice()); 79. Assert.assertEquals(1.93, indemnite.getBaseHeure(), 1e-6); 80. Assert.assertEquals(2, indemnite.getEntretienJour(), 1e-6); 81. Assert.assertEquals(3, indemnite.getRepasJour(), 1e-6); 82. Assert.assertEquals(12, indemnite.getIndemnitesCP(), 1e-6); 83. // la seconde indemnit n'a pas du tre persiste 84. List<Indemnite> indemnites = indemniteDao.findAll(); 85. int nbIndemnites = indemnites.size(); 86. Assert.assertEquals(nbIndemnites, 1); 87. } 88. 89. ... 90. }

Question : expliquer ce que fait le test test05 et indiquer les rsultats attendus. Les rsultats obtenus avec une couche Jpa / Hibernate sont les suivants :
1. 2. 3. 4. 5. 6. 7. ----------- test05 4 juin 2010 16:45:43 org.hibernate.util.JDBCExceptionReporter logExceptions ATTENTION: SQL Error: 1062, SQLState: 23000 4 juin 2010 16:45:43 org.hibernate.util.JDBCExceptionReporter logExceptions GRAVE: Duplicate entry '1' for key 2 Chane des exceptions -------------------------------------exception.PamException

http://tahe.developpez.com/java/javaee

68/341

8. javax.persistence.EntityExistsException 9. org.hibernate.exception.ConstraintViolationException 10. com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException

Le test passe, c.a.d. que les assertions sont vrifies et il n'y a pas d'exception qui sort de la mthode de test. Question : expliquer ce qui s'est pass. Les rsultats obtenus avec une couche Jpa / EclipseLink sont les suivants :
1. ----------- test05 2. [EL Warning]: 2010-06-04 16:48:26.421--UnitOfWork(749304)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DatabaseException 3. Internal Exception: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 2 4. Error Code: 1062 5. Call: INSERT INTO INDEMNITES (ID, ENTRETIEN_JOUR, REPAS_JOUR, INDICE, INDEMNITES_CP, BASE_HEURE, VERSION) VALUES (?, ?, ?, ?, ?, ?, ?) 6. bind => [108, 2.0, 3.0, 1, 12.0, 1.93, 1] 7. Query: InsertObjectQuery(jpa.Indemnite[id=108,version=1,indice=1,base heure=1.93,entretien jour2.0,repas jour=3.0,indemnits CP=12.0]) 8. Chane des exceptions -------------------------------------9. org.springframework.transaction.TransactionSystemException 10. javax.persistence.RollbackException 11. org.eclipse.persistence.exceptions.DatabaseException 12. com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException

Comme prcdemment avec Hibernate, le test passe, c.a.d. que les assertions sont vrifies et il n'y a pas d'exception qui sort de la mthode de test. Question : expliquer ce qui s'est pass. Question : de ces deux exemples, que peut-on conclure de l'interchangeabilit des implmentations Jpa ? Est-elle totale ici ?

11.4

Les autres tests

Une fois la couche [dao] teste et considre correcte, on pourra passer aux tests de la couche [metier] et ceux du projet lui-mme dans sa version console ou graphique. Le changement d'implmentation Jpa n'influe en rien sur les couches [metier] et [ui] et donc si ces couches fonctionnaient avec Hibernate, elles fonctionneront avec EclipseLink quelques exceptions prs : l'exemple prcdent montre en effet que les exceptions lances par les couches [dao] peuvent diffrer. Ainsi dans le cas d'utilisation du test, Spring / Jpa / Hibernate lance une exception de type [PamException], une exception propre l'application [pam] alors que Spring / Jpa / EclipseLink lui, lance une exception de type [TransactionSystemException], une exception du framework Spring. Si dans le cas d'usage du test, la couche [ui] attend une exception de type [PamException] parce qu'elle a t construite avec Hibernate, elle ne fonctionnera plus lorsqu'on passera EclipseLink. Travail pratique : refaire les tests des applications console et swing avec diffrents SGBD : MySQL5, Oracle XE, SQL Server.

http://tahe.developpez.com/java/javaee

69/341

12 Portage d'une architecture Spring / Jpa vers une architecture OpenEJB / Jpa
12.1 Introduction aux principes du portage

Nous prsentons ici les principes qui vont gouverner le portage d'une application Jpa / Spring / Hibernate vers une application Jpa / OpenEJB / EclipseLink. La mise en oeuvre pratique du portage est dcrite au paragraphe 12.2, page 75.

12.1.1

Les deux architectures

L'implmentation actuelle avec Spring / Hibernate

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 2

Couche [JDBC]

Spring

L'implmentation construire avec OpenEjb / Eclipselink

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink] 2

Couche [JDBC]

OpenEjb

12.1.2

Les bibliothques des projets


les couches [dao] et [metier] ne sont plus instancies par Spring. Elles le sont par le conteneur OpenEjb. les bibliothques du conteneur Spring et sa configuration disparaissent au profit des bibliothques du conteneur OpenEjb et sa configuration. les bibliothques de la couche JPA / Hibernate sont remplaces par celles de la couche JPA / EclipseLink

12.1.3

Configuration de la couche JPA / EclipseLink / OpenEjb


le fichier [META-INF/persistence.xml] configurant la couche JPA devient le suivant :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-openejb-ui-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> 4. <!-- entits Jpa --> 5. <class>jpa.Cotisation</class> 6. <class>jpa.Employe</class> 7. <class>jpa.Indemnite</class> 8. <!-- le fournisseur JPA est EclipseLink --> 9. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 10. <!-- proprits provider --> 11. <properties>

http://tahe.developpez.com/java/javaee

70/341

12. <property name="eclipselink.ddl-generation" value="create-tables"/> 13. </properties> 14. </persistence-unit> 15. </persistence>

ligne 3 : les transactions dans un conteneur Ejb sont de type JTA (Java Transaction Api). Elles taient de type RESOURCE_LOCAL avec Spring. ligne 9 : l'implmentation JPA utilise est EclipseLink lignes 5-7 : les entits gres par la couche Jpa lignes 11-13 : proprits du provider Eclipselink ligne 12 : chaque excution, les tables seront cres Les caractristiques Jdbc de la source de donnes JTA utilise par le conteneur OpenEjb seront prcises par le fichier de configuration [conf/openejb.conf] suivant :

1. <?xml version="1.0"?> 2. <openejb> 3. <Connector id="Default JDBC Database"> 4. JdbcDriver com.mysql.jdbc.Driver 5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink 6. UserName dbpam 7. Password dbpam 8. </Connector> 9. </openejb>

ligne 3 : on utilise l'id Default JDBC Database lorsqu'on travaille avec un conteneur OpenEjb embarqu (embedded) dans l'application elle-mme. ligne 5 : nous utilisons une base MySQL [dbpam_eclipselink]

12.1.4

Implmentation de la couche [dao] par des Ejb


Les classes implmentant la couche [dao] deviennent des Ejb. Prenons l'exemple de la classe [CotisationDao] : L'interface [ICotisationDao] dans la version Spring tait la suivante :

1. package dao; 2. 3. import java.util.List; 4. import jpa.Cotisation; 5. 6. public interface ICotisationDao { 7. // crer une nouvelle cotisation 8. Cotisation create(Cotisation cotisation); 9. // modifier une cotisation existante 10. Cotisation edit(Cotisation cotisation); 11. // supprimer une cotisation existante 12. void destroy(Cotisation cotisation); 13. // chercher une cotisation particulire 14. Cotisation find(Long id); 15. // obtenir tous les objets Cotisation 16. List<Cotisation> findAll(); 17. 18. }

L'Ejb va implmenter cette mme interface sous deux formes diffrentes : une locale et une distante. L'interface locale peut tre utilise par un client s'excutant dans la mme JVM, l'interface distante par un client s'excutant dans une autre JVM. L'interface locale :
1. 2. 3. 4. 5. 6. 7. package dao; import javax.ejb.Local; @Local public interface ICotisationDaoLocal extends ICotisationDao{ }

ligne 6 : l'interface [ICotisationDaoLocal] hrite de l'interface [ICotisationDao] pour en reprendre toutes les mthodes. Elle n'en ajoute pas de nouvelles. ligne 5 : l'annotation @Local en fait une interface locale pour l'Ejb qui l'implmentera.

http://tahe.developpez.com/java/javaee

71/341

L'interface distante :
1. 2. 3. 4. 5. 6. 7. package dao; import javax.ejb.Remote; @Remote public interface ICotisationDaoRemote extends ICotisationDao{ }

ligne 6 : l'interface [ICotisationDaoRemote] hrite de l'interface [ICotisationDao] pour en reprendre toutes les mthodes. Elle n'en ajoute pas de nouvelles. ligne 5 : l'annotation @Remote en fait une interface distante pour l'Ejb qui l'implmentera. La couche [dao] est implmente par un Ejb implmentant les deux interfaces (ce n'est pas obligatoire) :

1. @Stateless() 2. @TransactionAttribute(TransactionAttributeType.REQUIRED) 3. public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote { 4. 5. @PersistenceContext 6. private EntityManager em;

ligne 1 : l'annotation @Stateless qui fait de la classe un Ejb ligne 2 : l'annotation @TransactionAttribute qui fait que chaque mthode de la classe s'excutera au sein d'une transaction. ligne 5 : l'annotation @PersistenceContext qui injecte dans la classe [CotisationDao] l'EntityManager de la couche JPA. Elle est identique ce qu'on avait dans la version Spring. Lorsque l'interface locale de la couche [dao] est utilise, le client de cette interface s'excute dans la mme JVM.

utilisateur

Couche interface utilisateur [ui]

Couche mtier [metier] JVM

Couche d'accs aux donnes [dao]

Donnes

Ci-dessus, les couches [metier] et [dao] changent des objets par rfrence. Lorsqu'une couche change l'objet partag, l'autre couche voit ce changement. Lorsque l'interface distante de la couche [dao] est utilise, le client de cette interface s'excute habituellement dans une autre JVM. 1 utilisateur Couche interface utilisateur [ui] JVM 1 Couche mtier [metier] 2 3 Rseau tcp /ip Couche d'accs aux donnes [dao] JVM 2 Donnes

Ci-dessus, les couches [metier] et [dao] changent des objets par valeur (srialisation de l'objet chang). Lorsqu'une couche change un objet partag, l'autre couche ne voit ce changement que si l'objet modifi lui est renvoy.

12.1.5

Implmentation de la couche [metier] par un Ejb


La classe implmentant la couche [metier] devient elle aussi un Ejb implmentant une interface locale et distante. L'interface initiale [IMetier] tait la suivante :

1. package metier;

http://tahe.developpez.com/java/javaee

72/341

2. 3. import java.util.List; 4. import jpa.Employe; 5. 6. public interface IMetier { 7. // obtenir la feuille de salaire 8. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills ); 9. // liste des employs 10. List<Employe> findAllEmployes(); 11. }

On cre une interface locale et une interface distante partir de l'interface prcdente :
1. 2. 3. 4. 5. 6. 7. 1. 2. 3. 4. 5. 6. 7. package metier; import javax.ejb.Local; @Local public interface IMetierLocal extends IMetier{ } package metier; import javax.ejb.Remote; @Remote public interface IMetierRemote extends IMetier{ }

L'Ejb de la couche [metier] implmente ces deux interfaces :


1. @Stateless() 2. @TransactionAttribute(TransactionAttributeType.REQUIRED) 3. public class Metier implements IMetierLocal, IMetierRemote { 4. 5. // rfrence sur les couches [dao] locales 6. @EJB 7. private ICotisationDaoLocal cotisationDao = null; 8. @EJB 9. private IEmployeDaoLocal employeDao = null; 10. @EJB 11. private IIndemniteDaoLocal indemniteDao = null;

lignes 1-2 : dfinissent un Ejb dont chaque mthode s'excute dans une transaction. ligne 7 : une rfrence sur l'interface locale de l'Ejb [CotisationDao]. ligne 6 : l'annotation @EJB demande ce que le conteneur Ejb injecte une rfrence sur l'interface locale de l'Ejb [CotisationDao]. lignes 8-11 : on refait la mme chose pour les interfaces locales des Ejb [EmployeDao] et [IndemniteDao]. Au final, lorsque l'Ejb [Metier] sera instanci, les champs des lignes 7, 9 et 11 seront initialiss avec des rfrences sur les interfaces locales des trois Ejb de la couche [dao]. On fait donc ici l'hypothse que les couches [metier] et [dao] s'excuteront dans la mme JVM. 1 utilisateur 2 Couche interface utilisateur [ui] JVM 1 3 Rseau tcp /ip Couche mtier [metier] Couche d'accs aux donnes [dao] JVM 2 Donnes

12.1.6

Les clients des Ejb

http://tahe.developpez.com/java/javaee

73/341

1 utilisateur 2 Couche interface utilisateur [ui] JVM 1 3 Rseau tcp /ip Couche mtier [metier] Couche d'accs aux donnes [dao] JVM 2 Donnes

Dans le schma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une rfrence sur l'interface distante de l'Ejb de la couche [metier].

utilisateur

Couche interface utilisateur [ui]

Couche mtier [metier] JVM

Couche d'accs aux donnes [dao]

Donnes

Dans le schma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une rfrence sur l'interface locale de l'Ejb de la couche [metier]. La mthode pour obtenir ces rfrences diffre d'un conteneur l'autre. Pour le conteneur OpenEjb, on pourra procder comme suit : Rfrence sur l'interface locale :
1. 2. 3. 4. 5. 6. 7. 8. 9. // on configure le conteneur Open Ejb embarqu Properties properties = new Properties(); properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); // initialisation du contexte JNDI avec les proprits prcdentes InitialContext initialContext = new InitialContext(properties); // instanciation couches dao employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal"); cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal"); indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");

lignes 2-5 : le conteneur OpenEjb est initialis. ligne 5 : on a un contexte JNDI (Java Naming and Directory Interface) qui permet d'obtenir des rfrences sur les Ejb. Chaque Ejb est dsign par un nom JNDI : pour l'interface locale on ajoute Local au nom de l'Ejb (lignes 7-9) pour l'interface distante on ajoute Remote au nom de l'Ejb Avec Java EE 5, ces rgles changent selon le conteneur Ejb. C'est une difficult. Java EE 6 a introduit une notation JNDI portable sur tous les serveurs d'applications.

Ci-dessous, le code pour avoir une rfrence sur l'interface distante de l'Ejb [Metier] :
1. 2. 3. // on configure le conteneur Open Ejb embarqu Properties properties = new Properties(); properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); // initialisation du contexte JNDI du conteneur Ejb InitialContext initialContext = new InitialContext(properties);

4. 5. 6. 7. // instanciation couche mtier distante 8. metier = (IMetierRemote) initialContext.lookup("MetierRemote");

http://tahe.developpez.com/java/javaee

74/341

12.2

Travail pratique

On se propose de porter l'application Netbeans Spring / Hibernate vers une architecture OpenEjb / EclipseLink. L'implmentation actuelle avec Spring / Hibernate

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 2

Couche [JDBC]

1 L'implmentation construire avec OpenEjb / Eclipselink

Spring

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink]

Couche [JDBC]

OpenEjb

12.2.1

Mise en place de la base de donnes [dbpam_eclipselink]

Si elle n'existe pas, crez la base MySQL [dbpam_eclipselink]. Si elle existe, supprimez toutes ses tables. Crez une connexion Netbeans vers cette base comme il a t dcrit page 75.

12.2.2

Configuration initiale du projet Netbeans


charger le projet [pam-spring-ui-metier-dao-jpa-hibernate] crer un nouveau projet Java [pam-openejb-ui-metier-dao-jpa-eclipselink] dcocher la proprit [compile on save] [1] dans les proprits du projet. Si cette proprit est coche, le conteneur OpenEjb trouve deux contextes de persistence au lieu d'un et il refuse de se lancer. 2 1 3

dans l'onglet [Files] [2], crer un dossier [conf] [3] sous la racine du projet placer dans ce dossier, le fichier [openejb.conf] [4] suivant :

1. <?xml version="1.0"?> 2. <openejb> 3. <Connector id="Default JDBC Database">

http://tahe.developpez.com/java/javaee

75/341

4. JdbcDriver com.mysql.jdbc.Driver 5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink 6. UserName dbpam 7. Password dbpam 8. </Connector> 9. </openejb>

5 4

dans [Source Packages], crer le dossier [META-INF] [5] y mettre le fichier [persistence.xml] [6] suivant :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-openejb-ui-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> 4. <!-- entits Jpa --> 5. <class>jpa.Cotisation</class> 6. <class>jpa.Employe</class> 7. <class>jpa.Indemnite</class> 8. <!-- le fournisseur JPA est EclipseLink --> 9. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 10. <!-- proprits provider --> 11. <properties> 12. <property name="eclipselink.ddl-generation" value="create-tables"/> 13. </properties> 14. </persistence-unit> 15. </persistence>

ajouter les bibliothques OpenEjb, EclipseLink au projet, le pilote Jdbc de MySQL, la bibliothque log4j.jar.

12.2.3

Portage de la couche [dao]

Nous allons faire le portage de la couche [dao] par copie de packages du projet [pam-spring-ui-metier-dao-jpa-hibernate] vers le projet [pam-openejb-ui-metier-dao-jpa-eclipselink].

copier les packages [dao, exception, jpa]

http://tahe.developpez.com/java/javaee

76/341

Les erreurs signales ci-dessus viennent du fait que la couche [dao] copie utilise Spring et que les bibliothques Spring ne font plus partie du projet.

12.2.3.1

L'Ejb [CotisationDao]

Nous crons les interfaces locale et distante du futur Ejb [CotisationDao] : L'interface locale ICotisationDaoLocal :
1. 2. 3. 4. 5. 6. 7. package dao; import javax.ejb.Local; @Local public interface ICotisationDaoLocal extends ICotisationDao{ }

Pour avoir les bons import de paquetages, faire [clic droit sur le code / Fix Imports]. L'interface distante ICotisationDaoRemote :
1. 2. 3. 4. 5. 6. 7. package dao; import javax.ejb.Remote; @Remote public interface ICotisationDaoRemote extends ICotisationDao{ }

Puis nous modifions la classe [CotisationDao] pour en faire un Ejb :


1. ... 2. import javax.persistence.PersistenceContext; 3. import jpa.Cotisation; 4. 5. @Stateless 6. @TransactionAttribute(TransactionAttributeType.REQUIRED) 7. public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote { 8. 9. @PersistenceContext 10. private EntityManager em; 11. ...

L'import que faisait cette classe sur le framework Spring disparat. Faire un [Clean and Build] du projet :

http://tahe.developpez.com/java/javaee

77/341

1 2

En [1], il n'y a plus d'erreurs sur la classe [CotisationDao].

12.2.3.2

Les Ejb [EmployeDao] et [IndemniteDao]

On rpte la mme dmarche pour les autres lments de la couche [dao] :


interfaces IEmployeDaoLocal, IEmployeDaoRemote drives de IEmployeDao Ejb EmployeDao implmentant ces deux interfaces interfaces IIndemniteDaoLocal, IIndemniteDaoRemote drives de IIndemniteDao Ejb IndemniteDao implmentant ces deux interfaces

Ceci fait, il n'y a plus d'erreurs dans le projet [2].

12.2.3.3

La classe [PamException]

La classe [PamException] reste ce qu'elle tait un dtail prs :


1. package exception; 2. 3. import javax.ejb.ApplicationException; 4. 5. @ApplicationException(rollback=true) 6. public class PamException extends RuntimeException { 7. 8. // code d'erreur 9. private int code; 10. ...

La ligne 5 est ajoute. Pour avoir les bons import faire [Fix imports]. Pour comprendre l'annotation de la ligne 5, il faut se rappeler que chaque mthode des Ejb de notre couche [dao] : s'excute dans une transaction dmarre et termine par le conteneur Ejb lance une exception de type [PamException] ds que quelque chose se passe mal

utilisateur

Couche interface utilisateur [ui]

Couche mtier [metier]

Proxy Ejb

Couche d'accs aux donnes [dao]

Donnes

Lorsque la couche [metier] appelle une mthode M de la couche [dao], cet appel est intercept par le conteneur Ejb. Tout se passe comme s'il y avait une classe intermdiaire entre la couche [metier] et la couche [dao], ici appele [Proxy Ejb], interceptant tous les

http://tahe.developpez.com/java/javaee

78/341

appels vers la couche [dao]. Lorsque l'appel la mthode M de la couche [dao] est intercepte, le Proxy Ejb dmarre une transaction puis passe la main la mthode M de la couche [dao] qui s'excute alors dans cette transaction. La mthode M se termine avec ou sans exception.

si la mthode M se termine sans exception, l'excution revient au proxy Ejb qui termine la transaction en la validant par un commit. Le flux d'excution est ensuite rendu la mthode appelante de la couche [metier] si la mthode M se termine avec exception, l'excution revient au proxy Ejb qui termine la transaction en l'invalidant par un rollback. De plus il encapsule cette exception dans un type EjbException. Le flux d'excution est ensuite rendu la mthode appelante de la couche [metier] qui reoit donc une EjbException. L'annotation de la ligne 5 ci-dessus empche cette encapsulation. La couche [metier] recevra donc une PamException. De plus, l'attribut rollback=true indique au proxy Ejb que lorsqu'il reoit une PamException, il doit invalider la transaction.

12.2.3.4

Test de la couche [dao]

Notre couche [dao] implmente par des Ejb peut tre teste. Nous commenons par copier le package [dao] de [Test Packages] du projet [pam-spring-ui-metier-dao-jpa-hibernate] dans le projet en cours de construction [1] :

3 1 2

Nous ne conservons que le test [JUnitInitDB] qui initialise la base avec quelques donnes [2]. Nous renommons la classe [ JUnitInitDbLocal] [3]. La classe [JUnitInitDBLocal] utilisera l'interface locale des Ejb de la couche [dao]. Nous modifions tout d'abord la classe [JUnitInitDBLocal] de la faon suivante :
1. public class JUnitInitDBLocal { 2. 3. static private IEmployeDaoLocal employeDao = null; 4. static private ICotisationDaoLocal cotisationDao = null; 5. static private IIndemniteDaoLocal indemniteDao = null; 6. 7. @BeforeClass 8. public static void init() throws Exception { 9. // on configure le conteneur Open Ejb embarqu 10. Properties properties = new Properties(); 11. properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 12. // initialisation du contexte JNDI avec les proprits prcdentes 13. InitialContext initialContext = new InitialContext(properties); 14. // instanciation couches dao locales 15. employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal"); 16. cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal"); 17. indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal"); 18. } 1. 2. ...

lignes 3-5 : des rfrences sur les interfaces locales des Ejb de la couche [dao] ligne 7 : @BeforeClass annote la mthode excute au dmarrage du test JUnit lignes 10-13 : initialisation du conteneur OpenEjb. Cette initialisation est propritaire et change avec chaque conteneur Ejb. ligne 13 : on a un contexte JNDI (Java Naming and Directory Interface) qui permet d'accder aux Ejb via des noms. Avec OpenEjb, l'interface locale d'un Ejb E est dsigne par ELocal et l'interface distante par ERemote. lignes 15-17 : on demande au contexte JNDI, une rfrence sur les interfaces locales des Ejb [EmployeDao, CotisationDao, IndemniteDao].

http://tahe.developpez.com/java/javaee

79/341

Ceci fait, on peut compiler la classe de test [JUnitInitDBLocal] : clic droit sur la classe / compile

En [1], le projet ne prsente pas d'erreur. En [2], dans l'onglet [Files], on place dans le dossier [conf] du projet, un fichier [logging.properties] qui permet d'avoir des logs sur la console. Ce fichier est le suivant :
1. ## --------------------------------------------------2. ## Nice alternate configuration for embedded testing 3. ## 4. ## Output is slightly more terse and sent to System.out 5. ## 6. ## Simply comment out the above declarations and 7. ## uncomment the configuration below. 8. ## 9. # 10. log4j.rootLogger = fatal,C 11. log4j.category.OpenEJB = warn 12. log4j.category.OpenEJB.server = info 13. log4j.category.OpenEJB.startup = info 14. log4j.category.OpenEJB.startup.service = warn 15. log4j.category.OpenEJB.startup.config = info 16. log4j.category.OpenEJB.hsql = info 17. log4j.category.CORBA-Adapter = info 18. log4j.category.Transaction = warn 19. log4j.category.org.apache.activemq = error 20. log4j.category.org.apache.geronimo = error 21. log4j.category.openjpa = error 22. 23. log4j.appender.C = org.apache.log4j.ConsoleAppender 24. log4j.appender.C.layout = org.apache.log4j.SimpleLayout

Ce fichier utilise la bibliothque [log4j.jar]. Celle-ci doit tre prsente dans les bibliothques du projet. Lorsque tout ceci a t fait, on construit le projet (Build), on lance le serveur MySQL si besoin est, on excute le test JUnitInitDBLocal. On rappelle que le fichier [persistence.xml] a t configur pour recrer les tables chaque excution. Avant l'excution du test, il est prfrable de supprimer les ventuelles tables de la base MySQL [dbpam_eclipselink].

en [1], dans l'onglet [Services], on supprime les tables de la connexion Netbeans tablie au paragraphe 12.2.1. en [2], la base [dbpam_eclipselink] n'a plus de tables en [3], le projet est construit en [4], les test JUnitInitDBLocal est excut

http://tahe.developpez.com/java/javaee

80/341

en [5], le test a t russi en [6], on rafrachit la connexion Netbeans en [7], on voit les 4 tables cres par la couche JPA. Le test avait pour but de les remplir. On visualise le contenu de l'une d'entre-elles

en [8], le contenu de la table [EMPLOYES]

Grce au fichier [logging.properties], le conteneur OpenEjb a affich des logs dans la console :
1. 2. 3. 4. 5. Apache OpenEJB 3.1 build: 20081009-03:31 http://openejb.apache.org/ INFO - openejb.home = C:\temp\pam\02\pam-openejb-ui-metier-dao-jpa-eclipselink INFO - openejb.base = C:\temp\pam\02\pam-openejb-ui-metier-dao-jpa-eclipselink INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service) 6. INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, providerid=Default Transaction Manager) 7. INFO - Configuring Service(id=Default JDBC Database, type=Resource, provider-id=Default JDBC Database) 8. INFO - Found PersistenceModule in classpath: C:\temp\pam\02\pam-openejb-ui-metier-dao-jpaeclipselink\build\classes 9. INFO - Beginning load: C:\temp\pam\02\pam-openejb-ui-metier-dao-jpa-eclipselink\build\classes 10. INFO - Configuring enterprise application: classpath.ear 11. INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container) 12. INFO - Auto-creating a container for bean CotisationDao: Container(type=STATELESS, id=Default Stateless Container) 13. INFO - Configuring PersistenceUnit(name=pam-openejb-ui-metier-dao-jpa-eclipselinkPU, provider=org.eclipse.persistence.jpa.PersistenceProvider) 14. INFO - Auto-creating a Resource with id 'Default JDBC DatabaseNonJta' of type 'DataSource for 'pam-openejb-ui-metier-dao-jpa-eclipselinkPU'. 15. INFO - Configuring Service(id=Default JDBC DatabaseNonJta, type=Resource, provider-id=Default JDBC Database) 16. INFO - Adjusting pam-openejb-ui-metier-dao-jpa-eclipselinkPU <jta-data-source> to 'Default JDBC Database' 17. INFO - Adjusting pam-openejb-ui-metier-dao-jpa-eclipselinkPU <non-jta-data-source> to 'Default JDBC DatabaseNonJta' 18. INFO - Enterprise application "classpath.ear" loaded. 19. INFO - Assembling app: classpath.ear 20. INFO - PersistenceUnit(name=pam-openejb-ui-metier-dao-jpa-eclipselinkPU, provider=org.eclipse.persistence.jpa.PersistenceProvider) 21. ERROR - JAVA AGENT NOT INSTALLED. The JPA Persistence Provider requested installation of a ClassFileTransformer which requires a JavaAgent. See http://openejb.apache.org/3.0/javaagent.html 22. INFO - Jndi(name=CotisationDaoLocal) --> Ejb(deployment-id=CotisationDao) 23. INFO - Jndi(name=CotisationDaoRemote) --> Ejb(deployment-id=CotisationDao) 24. INFO - Jndi(name=EmployeDaoLocal) --> Ejb(deployment-id=EmployeDao) 25. INFO - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao) 26. INFO - Jndi(name=IndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao) 27. INFO - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao) 28. INFO - Created Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container) 29. INFO - Created Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container) 30. INFO - Created Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container) 31. INFO - Deployed Application(path=classpath.ear) 32. [EL Info]: 2010-06-04 18:04:00.562--ServerSession(5263041)--EclipseLink, version: Eclipse Persistence Services - 2.0.0.v20091127-r5931

http://tahe.developpez.com/java/javaee

81/341

33. [EL Info]: 2010-06-04 18:04:01.078--ServerSession(5263041)--file:/C:/temp/pam/02/pam-openejb-uimetier-dao-jpa-eclipselink/build/classes/_pam-openejb-ui-metier-dao-jpa-eclipselinkPU login successful 34. [EL Warning]: 2010-06-04 18:04:01.125--ServerSession(5263041)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DatabaseException 35. Internal Exception: com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Table 'employes' already exists 36. Error Code: 1050 37. Call: CREATE TABLE EMPLOYES (ID BIGINT NOT NULL, PRENOM VARCHAR(20) NOT NULL, SS VARCHAR(15) NOT NULL UNIQUE, ADRESSE VARCHAR(50) NOT NULL, CP VARCHAR(5) NOT NULL, VILLE VARCHAR(30) NOT NULL, NOM VARCHAR(30) NOT NULL, VERSION INTEGER NOT NULL, INDEMNITE_ID BIGINT NOT NULL, PRIMARY KEY (ID)) 38. .... 39. Employs ---------------------40. jpa.Employe[id=353,version=1,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue des oiseaux,ville=St Corentin,code postal=49203,indice=2] 41. jpa.Employe[id=354,version=1,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La brlerie,ville=St Marcel,code postal=49014,indice=1] 42. Indemnits ---------------------43. jpa.Indemnite[id=351,version=1,indice=1,base heure=1.93,entretien jour2.0,repas jour=3.0,indemnits CP=12.0] 44. jpa.Indemnite[id=352,version=1,indice=2,base heure=2.1,entretien jour2.1,repas jour=3.1,indemnits CP=15.0] 45. Cotisations ---------------------46. jpa.Cotisation[id=355,version=1,csgrds=3.49,csgd=6.15,secu=9.39,retraite=7.88] 47. Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,813 sec

lignes 22-27 : les noms JNDI des six interfaces prsentes par les trois Ejb. ligne 32 : dbutent les logs d'EclipseLink ligne 33 : EclipseLink a russi se connecter la base cible lignes 34-37 : une exception se produit car Eclipselink essaie de crer la table [EMPLOYES] qui existe dj. L'erreur est signale dans les logs mais ce n'est pas une erreur fatale. Le code continue s'excuter. lignes 39-46 : les affichages du test JUnitInitDBLocal

Nous refaisons le mme test, en utilisant cette fois-ci l'interface distante des Ejb.

En [1], la classe [JUnitInitDBLocal] a t duplique (copy / paste) dans [JUnitInitDBRemote]. Dans cette classe, nous remplaons les interfaces locales par les interfaces distantes :
1. public class JUnitInitDBRemote { 2. 3. static private IEmployeDaoRemote employeDao = null; 4. static private ICotisationDaoRemote cotisationDao = null; 5. static private IIndemniteDaoRemote indemniteDao = null; 6. 7. @BeforeClass 8. public static void init() throws Exception { 9. // on configure le conteneur Open Ejb embarqu 10. Properties properties = new Properties(); 11. properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 12. // initialisation du contexte JNDI avec les proprits prcdentes 13. InitialContext initialContext = new InitialContext(properties); 14. // instanciation couches dao distantes 15. employeDao = (IEmployeDaoRemote) initialContext.lookup("EmployeDaoRemote"); 16. cotisationDao = (ICotisationDaoRemote) initialContext.lookup("CotisationDaoRemote"); 17. indemniteDao = (IIndemniteDaoRemote) initialContext.lookup("IndemniteDaoRemote"); 18. }

http://tahe.developpez.com/java/javaee

82/341

Ceci fait, la nouvelle classe de test peut tre excute. Auparavant, avec la connexion Netbeans [dbpam_eclipselink], supprimez les tables de la base [dbpam_eclipselink].

Avec la connexion Netbeans [dbpam_eclipselink], vrifiez que la base a t remplie.

12.2.4

Portage de la couche [metier]

Nous allons faire le portage de la couche [metier] par copie de packages du projet [pam-spring-ui-metier-dao-jpa-hibernate] vers le projet [pam-openejb-ui-metier-dao-jpa-eclipselink]..

2 3

Les erreurs signales ci-dessus [1] viennent du fait que la couche [metier] copie utilise Spring et que les bibliothques Spring ne font plus partie du projet.

12.2.4.1

L'Ejb [Metier]

Nous suivons la mme dmarche que celle dcrite pour l'Ejb [CotisationDao]. Nous crons tout d'abord en [2] les interfaces locale et distante du futur Ejb [Metier]. Elles drivent toutes deux de l'interface initiale [IMetier].
1. 2. 3. 4. 5. 6. 7. 8. 1. 2. 3. 4. 5. 6. 7. 8. package metier; import javax.ejb.Local; @Local public interface IMetierLocal extends IMetier{ } package metier; import javax.ejb.Remote; @Remote public interface IMetierRemote extends IMetier{ }

Ceci fait, en [3] nous modifions la classe [Metier] afin qu'elle devienne un Ejb :
1. @Stateless 2. @TransactionAttribute(TransactionAttributeType.REQUIRED) 3. public class Metier implements IMetierLocal, IMetierRemote { 4. 5. // rfrences sur la couche [dao] locale 6. @EJB

http://tahe.developpez.com/java/javaee

83/341

7. private ICotisationDaoLocal cotisationDao = null; 8. @EJB 9. private IEmployeDaoLocal employeDao = null; 10. @EJB 11. private IIndemniteDaoLocal indemniteDao = null; 12. 13. // obtenir la feuille de salaire 14. public FeuilleSalaire calculerFeuilleSalaire(String SS, 15. double nbHeuresTravailles, int nbJoursTravaills) { 16. // on rcupre les informations lies l'employ 17. ...

ligne 1 : l'annotation @Stateless fait de la classe un Ejb ligne 2 : chaque mthode de la classe s'excutera dans une transaction ligne 3 : l'Ejb [Metier] implmente les deux interfaces locale et distante que nous venons de dfinir ligne 7 : l'Ejb [Metier] va utiliser l'Ejb [CotisationDao] via l'interface locale de celui-ci. Cela signifie que les couches [metier] et [dao] doivent s'excuter dans la mme Jvm. ligne 6 : l'annotation @EJB fait en sorte que le conteneur Ejb injecte lui-mme la rfrence sur l'interface locale de l'Ejb [CotisationDao]. L'autre faon que nous avons rencontre est d'utiliser un contexte JNDI. lignes 8-11 : le mme mcanisme est utilis pour les deux autres Ejb de la couche [dao].

12.2.4.2

Test de la couche [metier]

Notre couche [metier] implmente par un Ejb peut tre teste. Nous commenons par copier le package [metier] de [Test Packages] du projet [pam-spring-ui-metier-dao-jpa-hibernate] dans le projet en cours de construction [1] :

en [1], le rsultat de la copie en [2], on supprime le 1er test en [3], le test restant est renomm [JUnitMetierLocal]

La classe [JUnitMetierLocal] devient la suivante :


1. public class JUnitMetierLocal { 2. 3. // couche mtier locale 4. static private IMetierLocal metier; 5. 6. @BeforeClass 7. public static void init() throws NamingException { 8. // on configure le conteneur Open Ejb embarqu 9. Properties properties = new Properties(); 10. properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 11. // initialisation du contexte JNDI avec les proprits prcdentes 12. InitialContext initialContext = new InitialContext(properties); 13. 14. // instanciation couches dao locales 15. IEmployeDaoLocal employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal"); 16. ICotisationDaoLocal cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal"); 17. IIndemniteDaoLocal indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal"); 18. // instanciation couche mtier locale 19. metier = (IMetierLocal) initialContext.lookup("MetierLocal"); 20. 21. // on vide la base 22. ... 23. }

ligne 4 : une rfrence sur l'interface locale de l'Ejb [Metier] lignes 8-12 : configuration du conteneur OpenEjb identique celle faite dans le test de la couche [dao]

http://tahe.developpez.com/java/javaee

84/341

lignes 15-19 : on demande au contexte JNDI de la ligne 12, des rfrences sur les 3 Ejb de la couche [dao] et sur l'Ejb de la couche [metier]. Les Ejb de la couche [dao] vont servir initialiser la base, l'Ejb de la couche [metier] faire des tests de calculs de salaire.

L'excution du test [JUnitMetierLocal] donne le rsultat suivant [1] :

En [2], on duplique [JUnitMetierLocal] en [JUnitMetierRemote] pour tester cette fois-ci l'interface distante de l'Ejb [Metier]. Le code de [JUnitMetierRemote] est modifi pour utiliser cette interface distante. Le reste ne change pas.
1. public class JUnitMetierRemote { 2. 3. // couche mtier distante 4. static private IMetierRemote metier; 5. 6. @BeforeClass 7. public static void init() throws NamingException { 8. // on configure le conteneur Open Ejb embarqu 9. Properties properties = new Properties(); 10. properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 11. // initialisation du contexte JNDI avec les proprits prcdentes 12. InitialContext initialContext = new InitialContext(properties); 13. 14. // instanciation couches dao distantes 15. IEmployeDaoRemote employeDao = (IEmployeDaoRemote) initialContext.lookup("EmployeDaoRemote"); 16. ICotisationDaoRemote cotisationDao = (ICotisationDaoRemote) initialContext.lookup("CotisationDaoRemote"); 17. IIndemniteDaoRemote indemniteDao = (IIndemniteDaoRemote) initialContext.lookup("IndemniteDaoRemote"); 18. // instanciation couche mtier distante 19. metier = (IMetierRemote) initialContext.lookup("MetierRemote"); 20. 21. // on vide la base 22. for(Employe employe:employeDao.findAll()){ 23. employeDao.destroy(employe); 24. } 25. for(Cotisation cotisation:cotisationDao.findAll()){ 26. cotisationDao.destroy(cotisation); 27. } 28. for(Indemnite indemnite : indemniteDao.findAll()){ 29. indemniteDao.destroy(indemnite); 30. } 31. // on la remplit 32. Indemnite indemnite1=new Indemnite(1,1.93,2,3,12); 33. Indemnite indemnite2=new Indemnite(2,2.1,2.1,3.1,15); 34. indemnite1=indemniteDao.create(indemnite1); 35. indemnite2=indemniteDao.create(indemnite2); 36. employeDao.create(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",indemnite2)); 37. employeDao.create(new Employe("260124402111742","Laverti","Justine","La brlerie","St Marcel","49014",indemnite1)); 38. cotisationDao.create(new Cotisation(3.49,6.15,9.39,7.88)); 39. } 40. }

lignes 4 et 19 : on utilise l'interface distante de l'Ejb [Metier]. lignes 15-17 : on utilise les interfaces distantes de la couche [dao] lignes 34-35 : parce qu'avec les interfaces distantes, les objets changs entre le client et le serveur le sont par valeur, il faut rcuprer le rsultat rendu par la mthode create(Indemnite i). Ce n'tait pas obligatoire avec les interfaces locales o l les objets sont passs par rfrence.

Ceci fait, le projet peut tre construit et le test [JUnitMetierRemote] excut :

http://tahe.developpez.com/java/javaee

85/341

12.2.5

Portage de la couche [console]

Nous allons faire le portage de la couche [console] par copie de packages du projet [pam-spring-ui-metier-dao-jpa-hibernate] vers le projet [pam-openejb-ui-metier-dao-jpa-eclipselink].

2 3 1

Les erreurs signales ci-dessus [1] viennent du fait que la couche [metier] copie utilise Spring et que les bibliothques Spring ne font plus partie du projet. En [2], la classe [Main] est renomme [MainLocal]. Elle utilisera l'interface locale de l'Ejb [Metier]. Le code de la classe [MainLocal] volue de la faon suivante :
1.
public static void main(String[] args) { 2. // donnes locales 3. final String syntaxe = "pg num_securite_sociale nb_heures_travailles nb_jours_travaills"; 4. ... 5. // des erreurs ? 6. if (erreurs.size() != 0) { 7. for (int i = 0; i < erreurs.size(); i++) { 8. System.err.println(erreurs.get(i)); 9. } 10. return; 11. } 12. // c'est bon - on peut demander la feuille de salaire la couche [mtier] 13. IMetierLocal metier = null; 14. FeuilleSalaire feuilleSalaire = null; 15. try { 16. // on configure le conteneur Open Ejb embarqu 17. Properties properties = new Properties(); 18. properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 19. // initialisation du contexte JNDI avec les proprits prcdentes 20. InitialContext initialContext = new InitialContext(properties); 21. // instanciation couche mtier locale 22. metier = (IMetierLocal) initialContext.lookup("MetierLocal"); 23. // calcul de la feuille de salaire 24. feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); 25. } catch (PamException ex) { 26. System.err.println("L'erreur suivante s'est produite : " + ex.getMessage()); 27. return; 28. } catch (Exception ex) { 29. System.err.println("L'erreur suivante s'est produite : " + ex.toString()); 30. return; 31. } 32. // affichage dtaill 33. String output = "Valeurs saisies :\n"; 34. output += ajouteInfo("N de scurit sociale de l'employ", args[0]); 35. ....

Les modifications se situent dans les lignes 13-25. C'est la faon d'avoir une rfrence sur la couche [metier] qui change (lignes 1722). Nous n'expliquons pas le nouveau code qui a dj t vu dans des exemples prcdents. Une fois ces modifications faites, le projet ne prsente plus d'erreurs (cf [3]).

http://tahe.developpez.com/java/javaee

86/341

Nous configurons le projet pour qu'il soit excut avec des arguments [1] :

Pour que l'application console s'excute normalement, il faut qu'il y ait des donnes dans la base. Pour cela, il faut modifier le fichier [META-INF/persistence.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 3. <persistence-unit name="jpa" transaction-type="JTA"> 4. <!-- le fournisseur JPA est EclipseLink --> 5. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 6. <!-- entits Jpa --> 7. <class>jpa.Cotisation</class> 8. <class>jpa.Employe</class> 9. <class>jpa.Indemnite</class> 10. <!-- proprits provider --> 11. <properties> 12. <!-13. <property name="eclipselink.ddl-generation" value="create-tables"/> 14. --> 15. </properties> 16. </persistence-unit> 17. </persistence>

La ligne 13 qui faisait que les tables de la base de donnes taient recres chaque excution est mise en commentaires. Le projet doit tre reconstruit (Clean and Build) pour que cette modification soit prise en compte. Ceci fait, on peut excuter le programme. Si tout va bien, on obtient un affichage console analogue au suivant :
1. ....... 2. INFO - Created Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container) 3. INFO - Deployed Application(path=classpath.ear) 4. [EL Info]: 2009-09-30 15:09:21.109--ServerSession(16658781)--EclipseLink, version: Eclipse Persistence Services - 1.1.2.v20090612-r4475 5. [EL Info]: 2009-09-30 15:09:21.937--ServerSession(16658781)--file:/C:/temp/09-09-28/pam-consolemetier-dao-openejb-eclipselink-0910/build/classes/-jpa login successful 6. Valeurs saisies : 7. N de scurit sociale de l'employ : 254104940426058 8. Nombre d'heures travailles : 150 9. Nombre de jours travaills : 20 10. 11. Informations Employ : 12. Nom : Jouveinal 13. Prnom : Marie 14. Adresse : 5 rue des oiseaux 15. Ville : St Corentin 16. Code Postal : 49203 17. Indice : 2 18. 19. Informations Cotisations : 20. CSGRDS : 3.49 % 21. CSGD : 6.15 % 22. Retraite : 7.88 % 23. Scurit sociale : 9.39 % 24. 25. Informations Indemnits : 26. Salaire horaire : 2.1 euro 27. Entretien/jour : 2.1 euro

http://tahe.developpez.com/java/javaee

87/341

28. Repas/jour : 3.1 euro 29. Congs Pays : 15.0 % 30. 31. Informations Salaire : 32. Salaire de base : 362.25 euro 33. Cotisations sociales : 97.48 euro 34. Indemnits d'entretien : 42.0 euro 35. Indemnits de repas : 62.0 euro 36. Salaire net : 368.77 euro 37. 38. BUILD SUCCESSFUL (total time: 4 seconds)

Nous avons utilis ici l'interface locale de la couche [metier]. Nous utilisons maintenant son interface distante dans une seconde classe console :

En [1], la classe [MainLocal] a t duplique dans [MainRemote]. Le code de [MainRemote] est modifi pour utiliser l'interface distante de la couche [metier] :
1. // c'est bon - on peut demander la feuille de salaire la couche [metier] 2. IMetierRemote metier = null; 3. FeuilleSalaire feuilleSalaire = null; 4. try { 5. // on configure le conteneur Open Ejb embarqu 6. ... 7. // instanciation couche mtier distante 8. metier = (IMetierRemote) initialContext.lookup("MetierRemote"); 9. // calcul de la feuille de salaire 10. feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); 11. } catch (PamException ex) { 12. ... 13. } catch (Exception ex) { 14. ... 15. }

Les modifications sont faites aux lignes 2 et 8. Le projet est configur [2] pour excuter la classe [MainRemote]. Son excution donne les mmes rsultats que prcdemment.

12.3

Conclusion

Nous avons montr comment porter une architecture Spring / Hibernate vers une architecture OpenEjb / EclipseLink. L'architecture Spring / Hibernate

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate]

Couche [JDBC]

Spring

http://tahe.developpez.com/java/javaee

88/341

L'architecture OpenEjb / Eclipselink

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink] 2

Couche [JDBC]

OpenEjb

Le portage a pu se faire sans trop de difficults parce que l'application initiale avait t structure en couches. Ce point est important comprendre.

http://tahe.developpez.com/java/javaee

89/341

13 Portage de l'application PAM sur un serveur d'applications Glassfish


On se propose de placer les Ejb des couches [metier] et [dao] de l'architecture OpenEjb / Eclipselink dans le conteneur d'un serveur d'applications Glassfish. L'implmentation actuelle avec OpenEjb / Eclipselink

Couche [ui]

Couche [metier]

Couche [dao]

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink]

Couche [JDBC]

OpenEjb
Ci-dessus, la couche [ui] utilise l'interface distante de la couche [metier]. Nous avons test deux contextes d'excution : local et distant. Dans ce dernier mode, la couche [ui] tait cliente de la couche [metier], couche implmente par des Ejb. Pour fonctionner en mode client / serveur, dans lequel le client et le serveur s'excutent dans deux Jvm diffrentes, nous allons placer les couches [metier, dao, jpa] sur le serveur Java EE Glassfish. Ce serveur est livr avec Netbeans. L'implmentation construire avec le serveur Glassfish C

Couche [ui] 1

Couche [metier] 2 7

Couche [dao] 3

Couche [JPA / EclipseLink]

Couche [JDBC] 6

Jvm1 - Java SE

Jvm2 Java EE - serveur Glassfish v3

la couche [ui] s'excutera dans un environnement Java SE (Standard Edition) les couches [metier, dao, jpa] s'excuteront dans un environnement Java EE (Enterprise Edition) sur un serveur Glassfish v3 le client communiquera avec le serveur via un rseau tcp-ip. Les changes rseau sont transparents pour le dveloppeur, si ce n'est qu'il doit tre quand mme conscient que le client et le serveur s'changent des objets srialiss pour communiquer et non des rfrences d'objets. Le protocole rseau utilis pour ces changes s'appelle RMI (Remote Method Invocation), un protocole utilisable uniquement entre deux applications Java. l'implmentation Jpa utilise sur le serveur Glassfish sera EclipseLink.

13.1
13.1.1

La partie serveur de l'application client / serveur PAM


L'architecture de l'application

Nous tudions ici la partie serveur qui sera hberge par le conteneur Ejb3 du serveur Glassfish :

http://tahe.developpez.com/java/javaee

90/341

Client Java SE

Conteneur Ejb3 serveur Java EE

Jpa / Toplink

Donnes

Il s'agit de faire un portage vers le serveur Glassfish de ce qui a dj t fait et test avec le conteneur OpenEjb. C'est l l'intrt de OpenEjb et en gnral des conteneurs Ejb embarqus : il nous permet de tester l'application dans un environnement d'excution simplifi. Lorsque l'application a t teste, il ne reste plus qu' la porter sur un serveur cible, ici le serveur Glassfish.

13.1.1.1

Le projet Netbeans

Commenons par crer un nouveau projet Netbeans : 1 3 2

en [1], nouveau projet en [2], choisir la catgorie Java EE et en [3] le type EJB Module. Il s'agit en effet de construire un projet qui sera hberg et excut par un conteneur Ejb, celui du serveur Glassfish.

5 4b 4a 7 6

avec le bouton [4a], choisir le dossier parent du dossier du projet ou taper son nom directement en [4b]. en [5], donner un nom au projet en [6], choisir le serveur d'application sur lequel il sera excut. Celui choisi ici est l'un de ceux visibles dans l'onglet [Runtime / Servers], ici Glassfish v3. en [7], choisir la version de Java ncessaire : Java EE 5 supporte les Ejb3, J2EE les Ejb2. On choisira donc Java EE5. On notera que cela dpend du serveur d'application choisi. Java EE 5 n'est propos dans la liste droulante [7] que si le serveur choisi en [6] le supporte. Glassfish v3 supporte Java EE 6 mais dans ce document cette version de Java EE n'est pas dveloppe.

http://tahe.developpez.com/java/javaee

91/341

1 4 2 3 5

en [1], le nouveau projet. Il diffre d'un projet Java classique par quelques points : une branche [Configuration Files] [2] dans laquelle nous allons trouver les fichiers qui configurent le module Ejb. Nous dcouvrirons qu'il y en a peu et que de plus certains sont gnrs automatiquement par Netbeans. une branche [Server Resources] [3]. Cette branche permet de crer des ressources sur le serveur. Nous aurons l'utiliser pour crer une source de donnes Jdbc. une branche [Libraries] [4] dans laquelle on trouve les bibliothques du serveur Glassfish [5] :

13.1.1.2

Configuration de la couche de persistance

Par configuration de la couche de persistance, nous entendons l'criture du fichier [persistence.xml] qui dfinit : l'implmentation Jpa utiliser la dfinition de la source de donnes exploite par la couche Jpa. Celle-ci sera une source Jdbc gre par le serveur Glassfish. C

Couche [ui]

Couche [metier]

Couche [dao]

Couche [JPA / Toplink]

Couche [JDBC] 6

SGBD
7

BD

Java SE

Java EE - serveur Glassfish

On pourra procder comme suit. Tout d'abord, dans l'onglet [Runtime / Databases], on crera [1] une connexion sur la base MySQL5 / dbpam_eclipselink :

Ceci fait, on peut passer la cration de la ressource Jdbc utilise par le module Ejb :

http://tahe.developpez.com/java/javaee

92/341

2 4 1

en [1], crer un nouveau fichier on s'assurera que le projet Ejb est slectionn avant de faire cette opration en [2], le projet Ejb en [3], on slectionne la catgorie [Glassfish] en [4], on veut crer une ressource Jdbc 7 8 5

6 9

en [5], indiquer que la ressource Jdbc va utiliser un nouveau pool de connexions. On rappelle qu'un pool de connexions est un pool de connexions ouvertes qui sert acclrer les changes de l'application avec la base de donnes. en [6], donner un nom JNDI la ressource Jdbc cre. Ce nom peut tre quelconque mais il a souvent la forme jdbc/nom. Ce nom JNDI sera utilis dans le fichier [persistence.xml] pour dsigner la source de donnes que l'implmentation Jpa doit utiliser. en [7], donner un nom qui peut tre quelconque au pool de connexions qui va tre cr dans la liste droulante [8], choisir la connexion Jdbc cre prcdemment sur la base MySQL / dbpam_eclipselink. en [9], un rcapitulatif des proprits du pool de connexions - on ne touche rien

http://tahe.developpez.com/java/javaee

93/341

11

10

en [10], on peut prciser plusieurs des proprits du pool de connexions - on laisse les valeurs par dfaut en [11], l'issue de l'assistant de cration d'une ressource Jdbc pour le module Ejb, un fichier [sun-resources.xml] a t cre dans la branche [Server Resources]. Le contenu de ce fichier est le suivant :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd"> 3. <resources> 4. <jdbc-resource enabled="true" jndi-name="jdbc/dbpam_eclipselink" object-type="user" poolname="dbpamEclipseLinkConnectionPool"> 5. <description/> 6. </jdbc-resource> 7. <jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validationmethod="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" failall-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connectionenlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="dbpamEclipseLinkConnectionPool" non-transactionalconnections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-inseconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbcobjects="false"> 8. <property name="URL" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/> 9. <property name="User" value="dbpam"/> 10. <property name="Password" value="dbpam"/> 11. </jdbc-connection-pool> 12. </resources>

Le fichier [sun-resources.xml] est un fichier Xml qui reprend toutes les donnes collectes par l'assistant. Il va tre utilis par Netbeans pour, lors du dploiement du module Ejb sur le serveur Glassfish, demander la cration de la ressource Jdbc dont a besoin ce module. On peut dsormais crer le fichier [persistence.xml] qui va configurer la couche JPA du module Ejb :

http://tahe.developpez.com/java/javaee

94/341

4 1 3

en [1], crer un nouveau fichier on s'assurera que le projet Ejb est slectionn avant de faire cette opration en [2], le projet Ejb en [3], on slectionne la catgorie [Persistence] en [4], on veut crer une unit de persistance

5 6 8 7 9 10

en [5], donner un nom l'unit de persistance en [6], plusieurs implmentations Jpa sont proposes. On choisira ici [EclipseLink]. D'autres implmentations sont utilisables condition de mettre les bibliothques qui les implmentent avec celles du serveur Glassfish. dans la liste droulante [7], choisir la source de donnes Jdbc [jdbc/dbpam_eclipselink] qui vient d'tre cre. en [8], indiquer que les transactions sont gres par le conteneur Ejb en [9], indiquer qu'aucune opration ne doit tre faite sur la source de donnes lors du dploiement du module Ejb sur le serveur. En effet, le module Ejb va utiliser une base [dbpam_eclipselink] dj cre. la fin de l'assistant, un fichier [persistence.xml] a t cr [10]. Son contenu est le suivant :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-serveur-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> 4. <jta-data-source>jdbc/dbpam_eclipselink</jta-data-source> 5. <exclude-unlisted-classes>false</exclude-unlisted-classes> 6. <properties/> 7. </persistence-unit> 8. </persistence>

ligne 3 : le nom de l'unit de persistance (pam-serveur-metier-dao-jpa-eclipselinkPU) et le type de transactions (JTA pour un conteneur Ejb) ligne 5 : le nom JNDI de la source de donnes utilise par la couche de persistance : jdbc/dbpam_eclipselink ligne 6 : les entits Jpa ne sont pas prcises. Elles seront cherches dans le Classpath du module Ejb. le nom de l'implmentation JPA (Hibernate, EclipseLink, ...) utilise n'est pas indique. Dans ce cas, Glassfish v3 utilise par dfaut EclipseLink.

13.1.1.3

Insertion des couches [jpa, dao, metier]

Maintenant que le fichier [persistence.xml] a t dfini, nous pouvons passer l'insertion dans le projet des couches [metier, dao, jpa] de l'application d'entreprise [pam] :

http://tahe.developpez.com/java/javaee

95/341

Couche [ui]

Couche [metier]

Couche [dao]

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Java SE

Java EE - serveur Glassfish

Ces trois couches sont identiques ce qu'elles taient avec OpenEjb. On peut procder un simple copier / coller entre les deux projets. C'est ce que nous faisons maintenant :

en [1], le rsultat de la copie des paquetages [jpa, dao, metier, exception] du projet [pam-openejb-ui-metier-dao-jpaeclipselink] dans le module Ejb [pam-serveur-metier-dao-jpa-eclipselink] en [2], dans la branche [Enterprise Beans], les trois Ejb de la couche [dao] et l'Ejb de la couche [metier] ont t reconnus.

13.1.1.4

Configuration du serveur Glassfish

Il nous reste configurer le serveur Glassfish sur deux points :


la couche Jpa est implmente par EclipseLink. Il faut nous assurer que le serveur Glassfish a les bibliothques de cette implmentation Jpa. la source de donnes est une base MySQL. Il faut nous assurer que le serveur Glassfish a le pilote Jdbc de ce SGBD.

On peut dcouvrir l'absence de ces bibliothques lors du dploiement du module Ejb. Voici une faon de procder parmi d'autres pour ajouter des bibliothques manquantes au serveur Glassfish :

http://tahe.developpez.com/java/javaee

96/341

en [1], visualiser les proprits du serveur Glassfish en [2], noter le dossier des domaines du serveur. Nous le notons par la suite <domains> dans le dossier <domains>\domain1\lib, mettre les bibliothques manquantes. Dans l'exemple, les bibliothques d'Hibernate (lib / hibernate-tools) et le pilote Jdbc de MySQL (lib / divers) ont t rajouts. Par dfaut, le serveur Glassfish a les bibliothques d'EclipseLink.

en [1], dans l'onglet [Services], nous lanons le serveur Glassfish v3 en [2], il est actif

13.1.1.5

Dploiement du module Ejb

Nous dployons maintenant le module Ejb sur le serveur Glassfish :

2 1 4

en [1], le module Ejb est dploy en [2], l'arborescence du serveur Glassfish est rafrachie en [3], aprs dploiement, le module Ejb apparat dans la branche [Applications] du serveur Glassfish en [4], la ressource Jdbc [jdbc / dbpam_eclipselink] a t cre sur le serveur Glassfish. On rappelle que nous l'avions dfinie page 92.

Lors du dploiement, le serveur Glassfish logue dans la console des informations intressantes :
1. INFO: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created. 2. .... 3. INFO: Portable JNDI names for EJB IndemniteDao : [java:global/pam-serveur-metier-dao-jpaeclipselink/IndemniteDao!dao.IIndemniteDaoLocal, java:global/pam-serveur-metier-dao-jpaeclipselink/IndemniteDao!dao.IIndemniteDaoRemote] 4. INFO: Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao : [dao.IIndemniteDaoRemote#dao.IIndemniteDaoRemote, dao.IIndemniteDaoRemote] 5. ... 6. INFO: Portable JNDI names for EJB CotisationDao : [java:global/pam-serveur-metier-dao-jpaeclipselink/CotisationDao!dao.ICotisationDaoLocal, java:global/pam-serveur-metier-dao-jpaeclipselink/CotisationDao!dao.ICotisationDaoRemote] 7. INFO: Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao : [dao.ICotisationDaoRemote, dao.ICotisationDaoRemote#dao.ICotisationDaoRemote] 8. INFO: Portable JNDI names for EJB Metier : [java:global/pam-serveur-metier-dao-jpaeclipselink/Metier!metier.IMetierRemote, java:global/pam-serveur-metier-dao-jpaeclipselink/Metier!metier.IMetierLocal] 9. INFO: Glassfish-specific (Non-portable) JNDI names for EJB Metier : [metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote] 10. ...

http://tahe.developpez.com/java/javaee

97/341

11. INFO: Portable JNDI names for EJB EmployeDao : [java:global/pam-serveur-metier-dao-jpaeclipselink/EmployeDao!dao.IEmployeDaoLocal, java:global/pam-serveur-metier-dao-jpaeclipselink/EmployeDao!dao.IEmployeDaoRemote] 12. INFO: Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao : [dao.IEmployeDaoRemote#dao.IEmployeDaoRemote, dao.IEmployeDaoRemote] 13. INFO: pam-serveur-metier-dao-jpa-eclipselink was successfully deployed in 12 891 milliseconds.

On notera aux lignes 3, 6, 8 et 11 les noms portables JNDI des Ejb dploys. Java EE 6 a introduit la notion de nom JNDI portable. Cela dnote un nom JNDI reconnu par tous les serveurs Java EE 6. Avec Java EE 5, les noms JNDI sont spcifiques au serveur utilis. 4, 7, 9, 12 : les noms JNDI des Ejb dploys sous une forme spcifique Glassfish v3. Ces noms seront utiles l'application console que nous allons crire pour utiliser le module Ejb dploy.

13.2

Client console - version 1

Maintenant que nous avons dploy la partie serveur de notre application client / serveur, nous en venons tudier la partie client [1] :

Couche [ui] 1

Couche [metier]

Couche [dao]

Couche [JPA / Toplink]

Couche [JDBC]

SGBD

BD

Java SE

Java EE - serveur Glassfish

13.2.1

Le projet du client

Nous crons un nouveau projet Netbeans de type [Java Application] nomm [pam-client-metier-dao-jpa-eclipselink-mysql] :

2 4 3

en [1], le projet du client en [2], les paquetages [ui.console, metier, jpa, exception] copis partir du projet [pam-openejb-ui-metier-dao-jpaeclipselink]. Le client va changer avec le serveur des entits Jpa ainsi que des objets de la couche [metier] (FeuilleSalaire, ElementsSalaire). Aussi a-t-on besoin des paquetages [jpa] et [metier]. Par ailleurs, le module Ejb peut lancer des exceptions de type [PamException] que le client va grer. Aussi a-t-on besoin du paquetage [exception]. Les erreurs signales viennent du fait que les classes ajoutes ont besoin de bibliothques qui n'ont pas encore t intgres au projet. en [3], le client ne peut tre ici qu'un client distant : il va utiliser une couche [metier] qui s'excute dans une autre Jvm (celle du serveur Glassfish) que celle du client. On supprime la classe [MainLocal].

http://tahe.developpez.com/java/javaee

98/341

en [4], le client n'a pas besoin de l'interface locale [IMetierLocal] de la couche [metier], ni de l'implmentation [Metier] de cette dernire. On supprime ces deux classes.

13.2.2

Suppression des annotations Java de persistence et d'EJB

Parmi les classes copies, nous trouvons des annotations JPA dans le package [jpa] des annotations EJB dans le package [metier] et [Exception] Les classes copies existent ct serveur avec les notations JPA et EJB adquates. Ct client, nous n'avons pas besoin de ces annotations. On pourrait les garder mais cela nous oblige alors intgrer au projet les bibliothques dfinissant ces annotations. Nous dcidons de les enlever. Les nouvelles classes sont les suivantes : Notations EJB Interface IMetierRemote 1. package metier; 2. 3. public interface IMetierRemote extends IMetier{ 4. 5. } En enlevant les notations EJB de l'interface IMetierRemote, cette interface devient identique l'interface IMetier dont elle drive. On pourrait donc supprimer cette interface pour ne garder que l'interface IMetier. Classe PamException 1. package exception; 2. 3. public class PamException extends RuntimeException { 4. 5. // code d'erreur 6. private int code; 7. 8. public PamException(int code) { 9. super(); 10. this.code = code; 11. } 12. ... Dans la classe PamException, nous enlevons l'annotation : @ApplicationException(rollback = true) Notations JPA Classe Employe 1. package jpa; 2. 3. import java.io.Serializable; 4. 5. public class Employe implements Serializable { 6. 7. private Long id; 8. private int version; 9. private String SS; 10. private String nom; 11. private String prenom; 12. private String adresse;

http://tahe.developpez.com/java/javaee

99/341

13. private String ville; 14. private String codePostal; 15. private Indemnite indemnite; 16. ... La classe [Employe] a perdu toutes ses annotations JPA (@Entity, @Column, ...). Nous faisons de mme pour les classes [Cotisation] et [Indemnite].

13.2.3

Les bibliothques du client

Le client du module Ejb a besoin de certaines bibliothques pour communiquer avec le serveur Glassfish. Celles-ci se trouvent dans les bibliothques du serveur Glassfish lui-mme :

en [1,2], on dtermine l'emplacement d'installation du serveur Glassfish. Nous l'appelerons <glassfish> et ci-dessus, c'est le dossier [C:\devjava\glassfish-v3\sges-v3\glassfish]. en [3], on clique droit sur la branche [Libraries] du projet pour ajouter des archives .jar

1 2 4

dans <glassfish>/modules [1], on trouve une archive mettre dans le Classpath du client : gf-client.jar [2,3]. Selon la version de Glassfish, cette archive peut se trouver dans le dossier <glassfish>/lib au lieu du dossier <glassfish>/modules. Cette archive fait elle-mme rfrence un nombre important d'autres archives du dossier <glassfish>/modules. La liste de celles-ci peut tre trouve dans le fichier [MANIFEST.MF] [4] : Manifest-Version: 1.0 Ant-Version: Apache Ant 1.6.5 Created-By: Apache Maven Archiver-Version: Plexus Archiver Built-By: java_re Build-Jdk: 1.6.0_10

1. 2. 3. 4. 5. 6.

http://tahe.developpez.com/java/javaee

100/341

7. 8. 9. 10.

Package: org.glassfish.appclient.client.acc Main-Class: org.glassfish.appclient.client.AppClientFacade PreMain-Class: org.glassfish.appclient.client.acc.agent.AppClientConta inerAgent Class-Path: ../modules/woodstox-osgi.jar ../modules/tools.jar ../modul es/glassfish-corba-asm.jar ../modules/glassfishcorba-codegen.jar ../ modules/glassfish-corba-csiv2-idl.jar ../modules/glassfish-corba-newt imer.jar ../modules/glassfishcorba-omgapi.jar ../modules/glassfish-c orba-orb.jar ../modules/glassfish-corba-orbgeneric.jar ../modules/aut odepends.jar ../modules/config.jar ../modules/config-types.jar ../mo dules/hk2.jar ../modules/hk2-core.jar ../modules/osgi-adapter.jar ../ modules/tiger-types-osgi.jar ../modules/grizzly-comet.jar ../modules/ grizzlyconfig.jar ../modules/grizzly-framework.jar ../modules/grizzl y-http.jar ../modules/grizzly-http-servlet.jar ../modules/grizzly-por tunif.jar ../modules/grizzly-rcm.jar ../modules/grizzly-utils.jar ../ modules/pkg-client.jar ../modules/jaxb-osgi.jar ../modules/webservice s-osgi.jar ../modules/activation.jar ../modules/el-api.jar ../modules .....................................................

Plusieurs dizaines d'archives sont rfrences ligne 10 et seront automatiquement intgres dans le ClassPath du client. Pour la plupart, elles se trouvent dans le dossier [modules] de Glassfish.

en [5], le pilote Jdbc du SGBD MySQL a t ajout aux bibliothques du client. On peut s'tonner de sa prsence ct client. En effet, toute la persistance se fait ct serveur et non ct client. Cependant lorsque la couche de persistance ct serveur rencontre des problmes, un serveur MySQL non lanc par exemple, elle lance une exception appartenant aux bibliothques d'EclipseLink, exception encapsulant l'exception lance par le pilote Jdbc de MySQL. Si ct client, on veut avoir accs aux messages de ces exceptions, il faut que le client ait accs aux classes de celles-ci. D'o la ncessit des archives d'EclipseLink et de celle du pilote Jdbc de MySQL. La bibliothque [gf-client.jar] rfrence les bibliothques d'EclipseLink donc celles-ci seront intgres automatiquement au projet.

La classe [MainRemote] doit obtenir une rfrence sur l'Ejb de la couche [metier]. Le code de la classe [MainRemote] volue de la faon suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. .... // c'est bon - on peut demander la feuille de salaire FeuilleSalaire feuilleSalaire = null; IMetierRemote metier=null; try { // contexte JNDI du serveur Glassfish InitialContext initialContext = new InitialContext(); // instanciation couche mtier metier = (IMetierRemote) initialContext.lookup("java:global/pam-serveur-metier-dao-jpaeclipselink/Metier!metier.IMetierRemote"); 10. // calcul de la feuille de salaire 11. feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); 12. } catch (PamException ex) { 13. System.err.println("L'erreur suivante s'est produite : " 14. + ex.getMessage()); 15. return; 16. } catch (Exception ex) { 17. System.err.println("L'erreur suivante s'est produite : " 18. + ex.toString()); 19. return; 20. } 21. ....

ligne 7 : initialisation du contexte JNDI du serveur Glassfish. ligne 9 : on demande ce contexte JNDI une rfrence sur l'interface distante de la couche [metier]. D'aprs les logs de Glassfish, on sait que l'interface distante de la couche [metier] a deux noms possibles :

1. INFO: Portable JNDI names for EJB Metier : [java:global/pam-serveur-metier-dao-jpa-eclipselink/Metier! metier.IMetierRemote, java:global/pam-serveur-metier-dao-jpa-eclipselink/Metier!metier.IMetierLocal]

http://tahe.developpez.com/java/javaee

101/341

2. INFO: Glassfish-specific (Non-portable) JNDI names for EJB Metier : [metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote] Ligne 1, le nom JNDI utilisable avec tout serveur d'applications JAVA EE 6. Ligne 2, le nom JNDI spcifique Glassfish. Dans le code, ligne 9, nous utilisons le nom JNDI portable.

le reste du code ne change pas

En [1], on configure le projet pour qu'il excute la classe [MainRemote] avec des arguments. Si tout va bien, l'excution du projet donne le rsultat suivant :
1. run: 2. Valeurs saisies : 3. N de scurit sociale de l'employ : 254104940426058 4. Nombre d'heures travailles : 150 5. Nombre de jours travaills : 20 6. 7. Informations Employ : 8. Nom : Jouveinal 9. Prnom : Marie 10. Adresse : 5 rue des oiseaux 11. Ville : St Corentin 12. Code Postal : 49203 13. Indice : 2 14. 15. Informations Cotisations : 16. CSGRDS : 3.49 % 17. CSGD : 6.15 % 18. Retraite : 7.88 % 19. Scurit sociale : 9.39 % 20. 21. Informations Indemnits : 22. Salaire horaire : 2.1 euro 23. Entretien/jour : 2.1 euro 24. Repas/jour : 3.1 euro 25. Congs Pays : 15.0 % 26. 27. Informations Salaire : 28. Salaire de base : 362.25 euro 29. Cotisations sociales : 97.48 euro 30. Indemnits d'entretien : 42.0 euro 31. Indemnits de repas : 62.0 euro 32. Salaire net : 368.77 euro 33. 34. BUILD SUCCESSFUL (total time: 2 seconds)

Si dans les proprits, on met un n de scurit sociale incorrect, on obtient le rsultat suivant :
1. run: 2. L'erreur suivante s'est produite : L'employ de n[254104940426058x] est introuvable 3. BUILD SUCCESSFUL (total time: 2 seconds)

Lorsqu'on construit le projet, Netbeans produit une archive excutable. Celle-ci est place dans le sous-dossier [dist] du dossier du projet :

http://tahe.developpez.com/java/javaee

102/341

Ci-dessus, l'archive excutable est [pam-client-metier-dao-jpa-eclipselink.jar]. On notera que dossier [dist / lib] contient les bibliothques faisant partie de la branche [Libraries] du projet. Netbeans indique dans les logs de la console comment excuter l'archive excutable [pam-client-metier-dao-jpa-eclipselink.jar] :
1. To run this application from the command line without Ant, try: 2. java -jar "C:\temp\pam\02\pam-client-metier-dao-jpa-eclipselink\dist\pam-client-metier-dao-jpaeclipselink.jar"

La ligne 2 indique la commande excuter dans une fentre Dos pour excuter le jar cr par Netbeans. Ouvrons une fentre Dos et tapons la commande indique plus haut en lui ajoutant les trois paramtres attendus par la classe principale :

Ci-dessus, la commande a chou. Cela vient du fait que les bibliothques rfrences par l'archive [gf-client.jar] n'ont pas t trouves. En effet, celles-ci sont rfrences dans le fichier [MANIFEST.MF] de [gf-client.jar] de la faon suivante :
11. Class-Path:

../modules/woodstox-osgi.jar ../modules/tools.jar ../modules/glassfish-corba-asm.jar ../modules/glassfishcorba-codegen.jar ../ modules/glassfish-corba-csiv2-idl.jar ../modules/glassfish-corba-newt imer.jar ../modules/glassfishcorba-omgapi.jar ../modules/glassfish-c orba-orb.jar ../modules/glassfish-corba-orbgeneric.jar ../modules/autodepends.jar ../modules/config.jar ../modules/config-types.jar ../modules/hk2.jar ../modules/hk2-core.jar ../modules/osgi-adapter.jar ../ modules/tiger-types-osgi.jar ../modules/grizzly-comet.jar ../modules/grizzlyconfig.jar ../modules/grizzly-framework.jar ../modules/grizzl y-http.jar ../modules/grizzly-http-servlet.jar ../modules/grizzly-por tunif.jar ../modules/grizzly-rcm.jar ../modules/grizzly-utils.jar ../ modules/pkg-client.jar ../modules/jaxb-osgi.jar ../modules/webservice s-osgi.jar ../modules/activation.jar ../modules/el-api.jar ../modules .....................................................

Or nous avons vu que l'archive [gf-client.jar] se trouvait dans le dossier [dist / lib] du projet. Les archives ci-dessus sont alors cherches dans un dossier [dist / modules] qui n'existe pas.

13.3

Client console - version 2

Nous crons un nouveau projet Java par copie du prcdent [pam-client-metier-dao-jpa-eclipselink] :

http://tahe.developpez.com/java/javaee

103/341

1 12 3

en [1], le nouveau projet en [2], on enlve du projet la bibliothque [gf-link.jar] en [3], on ajoute de nouvelles bibliothques au projet

en [4,5], on prend toutes archives du dossier [lib / client-glassfish-v3]. Ce dossier a t cr par copie du dossier [<glassfish>/modules]. Il contient donc toutes les archives du dossier [<glassfish>/modules]. en [6], les nouvelles bibliothques du projet.

Aprs avoir construit le nouveau projet (Clean and Build), le dossier [dist] a le contenu suivant :

en [1], le dossier [dist] du projet en [2], le contenu du dossier [dist / lib]. Les archives qui y sont contenues font automatiquement partie du Classpath du projet.

Dans les logs issus du Build, Netbeans indique comment excuter l'application dans une fentre DOS :
1. To run this application from the command line without Ant, try: 2. java -jar "C:\temp\pam\02\pam-client-metier-dao-jpa-eclipselink-v2\dist\pam-client-metier-dao-jpaeclipselink-v2.jar"

Ouvrons une fentre Dos et tapons la commande indique plus haut en lui ajoutant les trois paramtres attendus par la classe principale :

http://tahe.developpez.com/java/javaee

104/341

Cette fois-ci l'application fonctionne et elle fonctionne en-dehors de Netbeans.

13.4

Client console - version 3

Dans les versions prcdentes, l'environnement JNDI du serveur Glassfish tait configur partir d'un fichier [jndi.properties] trouv quelque part dans les archives du projet. Son contenu par dfaut est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. # accs JNDI Sun Application Server java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.factory.url.pkgs=com.sun.enterprise.naming # Required to add a javax.naming.spi.StateFactory for CosNaming that # supports dynamic RMI-IIOP. java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl org.omg.CORBA.ORBInitialHost=localhost org.omg.CORBA.ORBInitialPort=3700

Les lignes 7 et 8 dsignent la machine du service JNDI et le port d'coute de celui-ci. Ce fichier ne permet pas d'interroger un serveur JNDI autre que localhost ou travaillant sur un port autre que le port 3700. Si on veut changer ces deux paramtres, on peut construire son propre fichier [jndi.properties]. Pour montrer cette technique, nous dupliquons la premire version du client console [pam-client-metier-dao-jpa-eclipselink] en un nouveau projet Netbeans [pam-openejb-ui-metier-dao-jpa-eclipselink-v3] :

4 3 1 2

Dans l'onglet [Files], nous crons le fichier [jndi.properties - 1] dans le dossier [src] du projet avec le contenu dcrit prcdemment. Lorsque le projet est compil, ce fichier est recopi [2] dans le jar [3] produit par le build du projet dans le dossier dist [4]. Il existe maintenant deux fichiers [jndi.properties] :

http://tahe.developpez.com/java/javaee

105/341

celui qui tait utilis auparavant et qui se trouve dans l'une des archives du dossier [lib] celui dans l'archive [dist/pam-openejb-ui-metier-dao-jpa-eclipselink-v3.jar]

Quel sera celui qui sera utilis par l'application ? Le fichier [jndi.properties] est cherch dans le Classpath du projet. Ce dernier est dfini dans le fichier [MANIFEST.MF] [5]. Ce fichier est gnr partir du fichier [manifest.mf] [6] qu'on trouve la racine du dossier du projet Netbeans.

5 6

Regardons le contenu du fichier [manifest.mf] [6] :


1. Manifest-Version: 1.0 2. X-COMMENT: Main-Class will be added automatically by build

Regardons maintenant le contenu du fichier [MANIFEST.MF] [5] :


1. 2. 3. 4. 5. 6. Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.1 Created-By: 10.0-b23 (Sun Microsystems Inc.) Main-Class: ui.console.MainRemote Class-Path: lib/gf-client.jar lib/mysql-connector-java-5.1.6-bin.jar X-COMMENT: Main-Class will be added automatically by build

Le fichier [MANIFEST.MF] a repris le contenu du fichier [manifest.mf] mais a ajout de nouvelles lignes. Le ClassPath du projet a ainsi t dfini ligne 5. Il reprend les lments de la branche [Libraries] du projet. Si on laisse le ClassPath tel quel, le fichier [jndi.properties] sera trouv dans l'une des archives rfrences par l'archive [gf-client.jar] (ligne 5). Le fichier [jndi.properties] qui se trouve dans l'archive [dist/pam-client-metier-dao-jpa-eclipselink-v3.jar] ne sera pas exploit. Une solution est d'inclure le dossier courant dans le ClassPath. Le Classpath du fichier [manifest.mf] est dfini comme suit :
1. Manifest-Version: 1.0 2. X-COMMENT: Main-Class will be added automatically by build 3. Class-Path: . lib/gf-client.jar lib/mysql-connector-java-5.1.6-bin.jar

Ligne 3, on dfinit explicitement le ClassPath. Dans celui-ci, comme premier lment, on met le dossier courant, not . Le fichier [jndi.properties] [2] est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. # accs JNDI Sun Application Server java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.factory.url.pkgs=com.sun.enterprise.naming # Required to add a javax.naming.spi.StateFactory for CosNaming that # supports dynamic RMI-IIOP. java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl org.omg.CORBA.ORBInitialHost=localhost org.omg.CORBA.ORBInitialPort=3700

Les lignes 7 et 8 dsignent la machine du service JNDI et le port d'coute de celui-ci. Lorsqu'on excute l'application, elle fonctionne mais on peut avoir des doutes sur le fichier [jndi.properties] rellement utilis, le ntre ou celui contenu dans l'une des archives du dossier [lib]. Pour le savoir, on change le port de la ligne 8 ci-dessus, en 3701, puis on compile (build) et on excute l'application. Les logs de la console sont les suivants :
1. L'erreur suivante s'est produite : javax.naming.NamingException: Lookup failed for 'java:global/pam-serveur-metier-dao-jpa-eclipselink/Metier!metier.IMetierRemote' in SerialContext targetHost=localhost,targetPort=3701,orb'sInitialHost=localhost,orb'sInitialPort=3700 [Root exception is javax.naming.NamingException: Unable to acquire SerialContextProvider for SerialContext targetHost=localhost,targetPort=3701,orb'sInitialHost=localhost,orb'sInitialPort=3700 [Root exception is org.omg.CORBA.COMM_FAILURE: vmcid: SUN minor code: 201 completed: No]]

http://tahe.developpez.com/java/javaee

106/341

Le message d'erreur indique que le port 3701 a t utilis (targetPort). C'est donc bien notre fichier [jndi.properties] qui a t utilis. On remettra le port 3700 dans [jndi.properties].

13.5

Client console - version 4

Dans les versions prcdentes, l'environnement JNDI du serveur tait configur partir du fichier [jndi.properties] trouv dans le Classpath du projet. Dans cette nouvelle version, nous utilisons Spring pour dfinir l'environnement JNDI du serveur. Nous commenons par crer un nouveau projet partir du projet [pam-client-metier-dao-jpa-eclipselink] initial.

3 1 2

en [1], le nouveau projet en [2], les trois archives ncessaires Spring (lib / spring) ont t ajoutes au projet en [3], le fichier [spring-config-ui.xml] de configuration de Spring. Son contenu est le suivant :

Le fichier de configuration de Spring est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xmlns:jee="http://www.springframework.org/schema/jee" 4. xsi:schemaLocation=" 5. http://www.springframework.org/schema/beans 6. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 7. http://www.springframework.org/schema/jee 8. http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> 9. 10. <!-- mtier --> 11. <jee:jndi-lookup id="metier" jndi-name="java:global/pam-serveur-metier-dao-jpaeclipselink/Metier!metier.IMetierRemote"> 12. <jee:environment> 13. java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory 14. java.naming.factory.url.pkgs=com.sun.enterprise.naming 15. java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl 16. org.omg.CORBA.ORBInitialHost=localhost 17. org.omg.CORBA.ORBInitialPort=3700 18. </jee:environment> 19. </jee:jndi-lookup> 20. </beans>

Nous utilisons ici une balise <jee> (ligne 11) apparue avec Spring 2.0. L'usage de cette balise ncessite la dfinition du schma auquel elle appartient, lignes 3, 7 et 8.

ligne 11 : la balise <jee:jndi-lookup> permet d'obtenir la rfrence d'un objet auprs d'un service JNDI. Ici, on associe le bean nomm " metier " la ressource JNDI associe l'EJB [Metier]. Le nom JNDI utilis ici est le nom portable (Java EE 6) de l'EJB. le contenu du fichier [jndi.properties] utilis dans les deux versions prcdentes devient le contenu de la balise <jee:environment> (ligne 12) qui sert dfinir les paramtres de connexion au service JNDI.

La classe principale [MainRemote] volue de la faon suivante :


1. ... 2. // c'est bon - on peut demander la feuille de salaire 3. FeuilleSalaire feuilleSalaire = null; 4. IMetierRemote metier=null; 5. try {

http://tahe.developpez.com/java/javaee

107/341

6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20.

Lignes 7-8, la rfrence de type [IMetierRemote] sur la couche [metier] est demande Spring. Cette solution amne de la souplesse dans notre architecture. En effet, si l'Ejb de la couche [metier] devenait local, c.a.d. excut dans la mme Jvm que notre client [MainRemote], le code de celui-ci ne changerait pas. Seul le contenu du fichier [spring-config-ui.xml] changerait. On retrouverait alors une configuration analogue l'architecture Spring / Jpa tudie au paragraphe 9. Le lecteur est invit tester cette nouvelle version.

// instanciation couche [metier] ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-ui.xml"); metier = (IMetierRemote) ctx.getBean("metier"); // calcul de la feuille de salaire feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); } catch (PamException ex) { System.err.println("L'erreur suivante s'est produite : " + ex.getMessage()); return; } catch (Exception ex) { System.err.println("L'erreur suivante s'est produite : " + ex.toString()); return; } ...

13.6

Un client console de type [Enterprise Client Application]

Nous dcrivons maintenant un nouveau client qui ncessite ce qu'on appelle un ACC (Application Client Container) pour tre excut. Un client excut dans ce contexte peut se voir injecter des EJB via l'annotation @EJB ce qui vite les appels JNDI faits au serveur d'applications. Nous crons un nouveau projet (File / New Project) :

4 3 1 2

en [1], choisir la catgorie [Java EE] en [2], choisir un projet de type [Enterprise Application Client] en [3], choisir le dossier parent du projet en [4], donner un nom au projet

7 8

en [5], indiquer que l'application sera une application client du serveur Glassfish v3 en [6], donner un nom la classe principale en [7], le projet Netbeans ainsi cr

http://tahe.developpez.com/java/javaee

108/341

en [8], on peut voir que Netbeans a adjoint automatiquement au projet un certain nombre d'archives lies au serveur Glassfish. Elles vont permettre au client de communiquer avec le serveur.

Nous allons modifier le projet gnr de la faon suivante :

En [1], nous supprimons les packages existants de [Source Packages] pour les remplacer en [2] par les packages copis partir client initial [pam-client-metier-dao-jpa-eclipselink]. En [3], nous configurons l'environnement d'excution du projet comme pour les versions prcdentes. Puis nous modifions le code de la classe principale [MainRemote] de la faon suivante :
1. .... 2. public class MainRemote { 3. 4. // EJB distant 5. @EJB 6. static IMetierRemote metier; 7. 8. public static void main(String[] args) { 9. .... 10. // c'est bon - on peut demander la feuille de salaire la couche [metier] 11. FeuilleSalaire feuilleSalaire = null; 12. try { 13. // calcul de la feuille de salaire 14. feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); 15. } catch (PamException ex) { 16. ... 17. } catch (Exception ex) { 18. ... 19. } 20. // affichage dtaill 21. ...

Parce que le client est excut dans un environnement manag, l'Application Client Container (ACC), il est possible d'injecter l'EJB [Metier] distant en lignes 5-6. C'est le conteneur ACC qui fait cette injection. Ligne 14, l'EJB de la couche [metier] est utilis pour calculer le salaire. Le lecteur est invit excuter ce nouveau client. On rappelle que le serveur EJB doit tre actif, c.a.d. que l'application [pamserveur-metier-dao-jpa-eclipselink] doit tre dploye sur le serveur Glassfish v3. La construction du projet produit plusieurs archives jar [1] dans le dossier [dist] du projet (onglet Files). Par ailleurs, l'application cliente est dploye sur le serveur d'applications [2].

1 2

L'archive [pam-acc-client-metier-dao-jpa-eclipselink.jar] ci-dessus [1] peut tre excute en-dehors de Netbeans l'aide d'un script fourni avec le serveur Glassfish.

http://tahe.developpez.com/java/javaee

109/341

Appelons <glassfish> le dossier d'installation du serveur Glassfish. Ce dossier [1] peut tre trouv dans les proprits du serveur (onglet Services, clic droit sur le serveur Glassfish, option Proprits) :

Dans <glassfish>/bin, on trouve le script [appclient.bat] qui permet d'excuter l'archive [pam-acc-client-metier-dao-jpaeclipselink.jar] selon la syntaxe suivante :
dos>"<glassfish>\bin\appclient.bat" -client <chemin>/pam-acc-client-metier-dao-jpa-eclipselink.jar 254104940426058 150 20

La commande prcdente est excute dans une fentre Dos. Il faut mettre le chemin du .jar excuter ou se placer dans le dossier de l'archive excuter. Le rsultat obtenu est le suivant :

Au final, comme dans le cas de la version 2 du client console, on a bien un client capable de s'excuter en-dehors de Netbeans.

13.7

Le client Swing

Nous construisons maintenant le client swing de notre application client / serveur Ejb. Pour cela nous crons un nouveau projet.

1 3

http://tahe.developpez.com/java/javaee

110/341

en [1], le nouveau projet [pam-clientswing-metier-dao-jpa-eclipselink] est obtenue par recopie du projet console [pamclient-metier-dao-jpa-eclipselink-v4]. en [2], le package [ui.swing] est obtenu par recopie du package du mme nom du projet [pam-spring-ui-metier-dao-jpahibernate]. Des erreurs apparaissent parce qu'il manque des bibliothques au projet. on ajoute aux bibliothques du projet [3], celles ncessaires aux interfaces swing [4], comme montr ci-dessous :

Aprs l'ajout de ces bibliothques, il n'y a plus d'erreur dans le projet :

Ci-dessus, la classe [PamJFrame] avait t crite initialement pour s'excuter dans un environnement Spring / JPA : 4

Couche [ui] swing

Couche [metier] 2

Couche [dao] 3 7

Objets image de la BD

Interface [JPA]

Implmentation [Hibernate] 5

Couche [JDBC] 6

Spring

Maintenant cette classe doit devenir le client distant d'un EJB dploy sur le serveur Glassfish. C

Couche [ui] swing

Couche [metier] 2 7

Couche [dao] 3

Couche [JPA / EclipseLink]

Couche [JDBC] 6

Jvm1 - Java SE

Jvm2 Java EE - serveur Glassfish v3

Travail pratique : en suivant l'exemple du client console [ui.console.MainRemote] du projet, modifier la faon utilise par la mthode [doMyInit] (cf page 59) de la classe [PamJFrame] pour acqurir une rfrence sur la couche [metier] qui est maintenant distante.

http://tahe.developpez.com/java/javaee

111/341

13.8

Les tests unitaires JUnit

Nous souhaitons refaire les tests JUnit distants raliss avec le conteneur OpenEJB. Nous voulons vrifier l'interchangeabilit des conteneurs Ejb. S'ils sont rellement interchangeables, les tests dj raliss avec OpenEJB devraient russir ici galement. Nous crons un nouveau projet par recopie du projet client console [pam-client-metier-dao-jpa-eclipselink-v4] :

en [1], le nouveau projet obtenu par recopie du projet client console [pam-client-metier-dao-jpa-eclipselink-v4] en [2], dans la branche [Test Packages], on copie les tests JUnit du projet OpenEJB [pam-openejb-ui-metier-dao-jpaeclipselink]. On ne garde que les tests des interfaces distantes des Ejb. Des erreurs apparaissent parce que ces tests rfrencent des classes d'un package [dao] qui n'existe pas dans le projet [1]. en [3], dans la branche [Source Packages], on copie le package [dao] du projet OpenEJB [pam-openejb-ui-metier-dao-jpaeclipselink]. On ne garde que les interfaces distantes des Ejb. Des erreurs apparaissent parce que ces interfaces utilisent des notations propres aux Ejb et que la bibliothque grant ces annotations n'a pas t incluse dans le projet. Nous faisons en sorte que les interfaces distantes n'utilisent plus d'annotations EJB :

package dao; public interface ICotisationDaoRemote extends ICotisationDao{ } package dao; public interface IEmployeDaoRemote extends IEmployeDao{ } package dao; public interface IIndemniteDaoRemote extends IIndemniteDao{ } Ceci fait, le projet ne prsente plus d'erreurs.

http://tahe.developpez.com/java/javaee

112/341

Les classes JUnit testent les versions distantes des Ejb des couches [metier] et [dao]. Nous aurons ainsi deux architectures de tests : Tests de la couche [metier] C

Couche [tests] 1

Couche [metier] 2

Couche [dao] 3

Couche [JPA / EclipseLink]

Couche [JDBC] 6

SGBD
7

BD

Java SE
Tests de la couche [dao] C

Java EE 5 - serveur Glassfish

Couche [tests] 1

Couche [dao] 3

Couche [JPA / EclipseLink]

Couche [JDBC] 6

SGBD
7

BD

Java SE
Le fichier [spring-ui.xml] configure les Ejb distants :

Java EE 5 - serveur Glassfish

1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xmlns:jee="http://www.springframework.org/schema/jee" 4. xsi:schemaLocation=" 5. http://www.springframework.org/schema/beans 6. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 7. http://www.springframework.org/schema/jee 8. http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> 9. 10. <!-- dao --> 11. <jee:jndi-lookup id="employeDao" jndi-name="java:global/pam-serveur-metier-dao-jpaeclipselink/EmployeDao!dao.IEmployeDaoRemote"> 12. <jee:environment> 13. java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory 14. java.naming.factory.url.pkgs=com.sun.enterprise.naming

http://tahe.developpez.com/java/javaee

113/341

15. 16. 17. 18. 19. 20. 21.

java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl org.omg.CORBA.ORBInitialHost=localhost org.omg.CORBA.ORBInitialPort=3700 </jee:environment> </jee:jndi-lookup>

<jee:jndi-lookup id="indemniteDao" jndi-name="java:global/pam-serveur-metier-dao-jpaeclipselink/IndemniteDao!dao.IIndemniteDaoRemote"> 22. <jee:environment> 23. ... 24. </jee:environment> 25. </jee:jndi-lookup> 26. 27. <jee:jndi-lookup id="cotisationDao" jndi-name="java:global/pam-serveur-metier-dao-jpaeclipselink/CotisationDao!dao.ICotisationDaoRemote"> 28. <jee:environment> 29. ... 30. </jee:environment> 31. </jee:jndi-lookup> 32. 33. <!-- mtier --> 34. <jee:jndi-lookup id="metier" jndi-name="java:global/pam-serveur-metier-dao-jpaeclipselink/Metier!metier.IMetierRemote"> 35. <jee:environment> 36. ... 37. </jee:environment> 38. </jee:jndi-lookup> 39. </beans>

L'environnement JNDI est celui dj utilis, par exemple dans le projet [pam-client-metier-dao-jpa-eclipselink-v4]. Il est peut-tre possible d'viter sa rptition (lignes 12-18, 22-24, 28-30, 35-37). Travail pratique : modifier la mthode d'initialisation (mthode init) de chacun des tests afin qu'elles acquirent des rfrences sur les Ejb distants [metier] et [*Dao] via le fichier [spring-config-ui.xml] prcdent et excuter les tests.

Nous ajoutons maintenant un nouveau test de la couche [dao] : 2 1

en [1], la classe [JUnitDao] peut tre trouve dans le projet Spring / Jpa [pam-spring-ui-metier-dao-jpa-hibernate]. en [2], cette classe est renomme [JUnitDaoRemote]

Travail pratique : - modifier la mthode d'initialisation (mthode init) du test afin qu'elle acquire des rfrences sur les Ejb distants [*Dao] via le fichier [spring-config-ui.xml] - si besoin est modifier les mthodes de test afin qu'elles utilisent les Ejb distants Pour comprendre le second point, il faut se souvenir que dans le projet Spring / Jpa, la classe de test et la couche [dao] s'excutaient dans la mme Jvm. Donc, les paramtres passs aux mthodes de la couche [dao] l'taient par rfrence. Ici, la classe de test et la couche [dao] s'excutent dans deux Jvm diffrentes. Aussi, les paramtres passs aux mthodes de la couche [dao] le sont par valeur.

http://tahe.developpez.com/java/javaee

114/341

Soit la mthode de test suivante :


1. @Test 2. public void test06() { 3. log("test06"); 4. // on cre deux employs avec le mme n SS 5. // enfreint la contrainte d'unicit sur le n SS 6. boolean erreur = true; 7. Employe employe1 = null; 8. Employe employe2 = null; 9. Indemnite indemnite1 = null; 10. Indemnite indemnite2 = null; 11. Throwable th = null; 12. try { 13. indemnite1 = new Indemnite(1, 1.93, 2, 3, 12); 14. indemnite2 = new Indemnite(2, 2.1, 2.1, 3.1, 15); 15. indemniteDao.create(indemnite1); 16. indemniteDao.create(indemnite2); 17. employeDao.create(employe1 = new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", indemnite1)); 18. employeDao.create(employe2 = new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", indemnite2)); 19. erreur = false; 20. } catch (PamException ex) { 21. th = ex; 22. // vrifications 23. Assert.assertEquals(21, ex.getCode()); 24. } catch (Throwable th1) { 25. th = th1; 26. } 27. // vrifications 28. Assert.assertTrue(erreur); 29. // chane des exceptions 30. System.out.println("Chane des exceptions --------------------------------------"); 31. System.out.println(th.getClass().getName()); 32. while (th.getCause() != null) { 33. th = th.getCause(); 34. System.out.println(th.getClass().getName()); 35. } 36. // le 1er employ a du tre persist 37. Employe employe = employeDao.find(employe1.getId()); 38. // vrification 39. Assert.assertNotNull(employe); 40. Assert.assertEquals("254104940426058", employe.getSS()); 41. Assert.assertEquals("Jouveinal", employe.getNom()); 42. Assert.assertEquals("Marie", employe.getPrenom()); 43. Assert.assertEquals("5 rue des oiseaux", employe.getAdresse()); 44. Assert.assertEquals("St Corentin", employe.getVille()); 45. Assert.assertEquals("49203", employe.getCodePostal()); 46. Assert.assertEquals(indemnite1, employe.getIndemnite()); 47. // le 2me employ n'a pas du tre persist 48. List<Employe> employes = employeDao.findAll(); 49. int nbEmployes = employes.size(); 50. Assert.assertEquals(1, nbEmployes); 51. }

Ci-dessus, la ligne 15
indemniteDao.create(indemnite1);

cre un objet Indemnite. Aprs cette cration, l'objet cr indemnite1 a une cl primaire Id qu'il n'avait pas avant sa cration. Si le programme de test et la couche [dao] s'excutent dans la mme Jvm, l'objet indemnite1 a t pass par rfrence et est partag par la mthode appelante et la mthode appele. La classe de test voit donc bien l'objet indemnite1 avec sa cl primaire. Si le programme de test et la couche [dao] s'excutent dans deux Jvm, l'objet indemnite1 a t pass par valeur et n'est pas partag par la mthode appelante et la mthode appele. La classe de test ne voit donc pas l'objet indemnite1 avec sa cl primaire. Seule la couche [dao] le voit. Il faut alors crire :
indemnite1=indemniteDao.create(indemnite1);

c'est dire utiliser le rsultat renvoy par la mthode create. Le test prcdent doit alors tre rcrit comme suit :
1. @Test 2. public void test06() { 3. log("test06"); 4. // on cre deux employs avec le mme n SS 5. // enfreint la contrainte d'unicit sur le n SS 6. boolean erreur = true;

http://tahe.developpez.com/java/javaee

115/341

7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.

Employe employe1 = null; Employe employe2 = null; Indemnite indemnite1 = null; Indemnite indemnite2 = null; Throwable th = null; try { indemnite1 = new Indemnite(1, 1.93, 2, 3, 12); indemnite2 = new Indemnite(2, 2.1, 2.1, 3.1, 15); indemnite1=indemniteDao.create(indemnite1); indemnite2=indemniteDao.create(indemnite2); employe1=employeDao.create(employe1 = new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", indemnite1)); 18. employe2=employeDao.create(employe2 = new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", indemnite2)); 19. erreur = false; 20. } catch (PamException ex) { 21. th = ex; 22. // vrifications 23. Assert.assertEquals(21, ex.getCode()); 24. } catch (Throwable th1) { 25. th = th1; 26. } 27. // vrifications 28. Assert.assertTrue(erreur); 29. // chane des exceptions 30. System.out.println("Chane des exceptions --------------------------------------"); 31. System.out.println(th.getClass().getName()); 32. while (th.getCause() != null) { 33. th = th.getCause(); 34. System.out.println(th.getClass().getName()); 35. } 36. // le 1er employ a du tre persist 37. Employe employe = employeDao.find(employe1.getId()); 38. // vrification 39. Assert.assertNotNull(employe); 40. Assert.assertEquals("254104940426058", employe.getSS()); 41. Assert.assertEquals("Jouveinal", employe.getNom()); 42. Assert.assertEquals("Marie", employe.getPrenom()); 43. Assert.assertEquals("5 rue des oiseaux", employe.getAdresse()); 44. Assert.assertEquals("St Corentin", employe.getVille()); 45. Assert.assertEquals("49203", employe.getCodePostal()); 46. Assert.assertEquals(indemnite1, employe.getIndemnite()); 47. // le 2me employ n'a pas du tre persist 48. List<Employe> employes = employeDao.findAll(); 49. int nbEmployes = employes.size(); 50. Assert.assertEquals(1, nbEmployes); 51. }

Ceci fait, les tests russissent :

http://tahe.developpez.com/java/javaee

116/341

14 Version 4 client / serveur dans une architecture de service web


Dans cette nouvelle version, l'application [Pam] va s'excuter en mode client / serveur dans une architecture de service web. Revenons sur l'architecture de l'application prcdente :

Couche [ui]

C
1

RMI

S Couche
2 [metier]

Couche [dao]

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Java SE

Java EE - serveur Glassfish

Ci-dessus, une couche de communication [C, RMI, S] permettait une communication transparente entre le client [ui] et la couche distante [metier]. Nous allons utiliser une architecture analogue, o la couche de communication [C, RMI, S] sera remplace par une couche [C, HTTP / SOAP, S] :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Java EE - serveur Glassfish

Le protocole HTTP / SOAP a l'avantage sur le protocole RMI / EJB prcdent d'tre multi-plateformes. Ainsi le service web peut tre crit en Java et dploy sur le serveur Glassfish alors que le client lui, pourrait tre un client .NET ou PHP. Nous allons dvelopper cette architecture selon trois modes diffrents : 1. 2. 3. le service web sera assur par l'EJB [Metier] le service web sera assur par une application web utilisant l'EJB [Metier] le service web sera assur par une application web utilisant Spring

Un service web peut tre implment de diverses faons au sein d'un serveur Java EE :

par une classe annote @WebService qui s'excute dans un conteneur web

Client du service web tcp-ip

Conteneur web

Conteneur Ejb3

Jpa

Donnes

serveur Java EE

par un Ejb annot @WebService qui s'excute dans un conteneur Ejb

Client du service web


Nous comenons par cette dernire architecture.

Conteneur Ejb3 serveur Java EE

Jpa

Donnes

http://tahe.developpez.com/java/javaee

117/341

14.1
14.1.1
14.1.1.1

Service web implment par un EJB


La partie serveur
Le projet Netbeans

Commenons par crer un nouveau projet Netbeans copie du projet EJB [pam-serveur-metier-dao-jpa-eclipselink] :

Dans l'architecture suivante :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Java EE - serveur Glassfish

la couche [metier] va tre le service web contact par la couche [ui]. Cette classe n'a pas besoin d'implmenter une interface. Ce sont des annotations qui transforment un POJO (Plain Ordinary Java Object) en service web. La classe [Metier] qui implmente la couche [metier] ci-dessus, est transforme de la faon suivante :
1. package metier; 2. 3. ... 4. @WebService 5. @Stateless() 6. @TransactionAttribute(TransactionAttributeType.REQUIRED) 7. public class Metier implements IMetierLocal,IMetierRemote { 8. 9. // rfrences sur les couches [dao] 10. @EJB 11. private ICotisationDaoLocal cotisationDao = null; 12. @EJB 13. private IEmployeDaoLocal employeDao=null; 14. @EJB 15. private IIndemniteDaoLocal indemniteDao=null; 16. 17. 18. // obtenir la feuille de salaire 19. @WebMethod 20. public FeuilleSalaire calculerFeuilleSalaire(String SS, 21. ... 22. } 23. 24. // liste des employs 25. @WebMethod 26. public List<Employe> findAllEmployes() { 27. ... 28. } 29. // pas de getters et setters

http://tahe.developpez.com/java/javaee

118/341

30. }

ligne 4, l'annotation @WebService fait de la classe [Metier] un service web. Un service web expose des mthodes ses clients. Celles-ci doivent tre annotes par l'attribut @WebMethod. lignes 19 et 25 : les deux mthodes de la classe [Metier] deviennent des mthodes du service web. ligne 29 : il est important que les getters et setters soient supprims.

L'ajout de ces annotation est dtect par Netbeans qui fait alors voluer la nature du projet :

1 3 2

En [1], une arborescence [Web Services] est apparue dans le projet. On y trouve le service web Metier et ses deux mthodes. L'application serveur peut tre dploye [2]. Le serveur MySQL soit tre lanc et sa base [dbpam_eclipselink] exister et tre remplie. Il peut tre ncessaire auparavant de supprimer [3] les Ejb du projet client / serveur EJB tudi prcdemment pour viter des conflits de noms. En effet, notre nouveau projet amne avec lui les mmes Ejb que ceux du projet prcdent. 1

En [1], nous voyons notre application serveur dploye sur le serveur Glassfish. Une fois le service web dploy, il peut tre test : 2

3 1

en [1], dans le projet courant, nous testons le service web [Metier] le service web est accessible via diffrentes URL. L'URL [2] permet de tester le service web

http://tahe.developpez.com/java/javaee

119/341

en [3], un lien sur le fichier XML dfinissant le service web. Les clients du service web ont besoin de connatre l'url de ce fichier. C'est partir de lui qu'est gnre la couche cliente (stubs) du service web. en [4,5], un formulaire permettant de tester les mthodes exposes par le service web. Celles-ci sont prsentes avec leurs paramtres que l'utilisateur peut dfinir.

Par exemple, testons la mthode [findAllEmployes] qui n'a besoin d'aucun paramtre :

Ci-dessus, nous testons la mthode. Nous recevons alors la rponse ci-dessous (vue partielle). Nous y retrouvons bien les deux employs avec leurs indemnits. Le lecteur est invit tester de la mme faon la mthode [4] en lui passant les trois paramtres qu'elle attend.

14.1.2

La partie cliente

http://tahe.developpez.com/java/javaee

120/341

14.1.2.1

Le projet Netbeans du client console

Nous crons maintenant un projet Java de type [Java Application] pour la partie client de l'application. Une fois le projet cr, nous indiquons qu'il sera client du service web que nous venons de dployer sur le serveur Glassfish :

en [1], le projet du client du service web en [2], nous slectionnons le nouveau projet et activons le bouton [New File] en [3], nous indiquons que nous voulons crer un client de service web

avec [4], nous allons dsigner le projet Netbeans du service web dans la fentre [5], sont lists tous les projets ayant une branche [Web Services], ici uniquement le projet [pam-serveurwsmetier-dao-jpa-eclipselink]. un projet peut dployer plusieurs services web. En [6], nous dsignons le service web auquel on veut se connecter.

7 10

8 9 11

http://tahe.developpez.com/java/javaee

121/341

en [7], est affiche l'Url de dfinition du service web. Cette url est utilise par les outils logiciels qui gnrent la couche cliente qui va s'interfacer avec le service web.

Couche [ui] 3

C
1

S Couche
2 [metier] 4

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / Toplink]

Couche [JDBC]

SGBD

BD

Java EE - serveur Glassfish

la couche cliente [C] [1] qui va tre gnre est constitue d'un ensemble de classes Java qui vont tre mises dans un mme paquetage. Le nom de celui-ci est fix en [8]. en [9], on indique le type de client souhait. On garde la valeur par dfaut [JAX-WS]. une fois l'assistant de cration du client du service web termin avec le bouton [Finish], la couche [C] ci-dessus est cre.

Ceci est reflt par un certain nombre de changements dans le projet :

en [10] ci-dessus, apparat une arborescence [Generated Sources] qui contient les classes de la couche [C] qui permettent au client [3] de communiquer avec le service web. Cette couche permet au client [3] de communiquer avec la couche [metier] [4] comme si elle tait locale et non distante. en [11], apparat une arborescence [Web Service References] qui liste les services web pour lesquels une couche cliente a t gnre.

On notera que dans la couche [C] [10] gnre, nous retrouvons des classes qui ont t dployes ct serveur : Indemnite, Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier est le service web et les autres classes sont des classes ncessaires ce service. On pourra avoir la curiosit de consulter leur code. On verra que la dfinition des classes qui, instancies, reprsentent des objets manipuls par le service, consiste en la dfinition des champs de la classe et de leurs accesseurs ainsi qu' l'ajout d'annotations permettant la srialisation de la classe en flux Xml. La classe Metier est devenue une interface avec dedans les deux mthodes qui ont t annotes @WebMethod. Chacune de celles-ci donne naissance deux classes, par exemple [CalculerFeuilleSalaire.java] et [CalculerFeuilleSalaireResponse.java], o l'une encapsule l'appel la mthode et l'autre son rsultat. Enfin, la classe MetierService est la classe qui permet au client d'avoir une rfrence sur le service web Metier distant :
1. 2. 3. 4. } @WebEndpoint(name = "MetierPort") public Metier getMetierPort() { return super.getPort(new QName("http://metier/", "MetierPort"), Metier.class);

La mthode getMetierPort de la ligne 2 permet d'obtenir une rfrence sur le service web Metier distant.

14.1.2.2

Le client console du service web Metier

Il ne nous reste plus qu' crire le client du service web Metier. Nous recopions la classe [MainRemote] du projet [pam-clientmetier-dao-jpa-eclipselink] qui tait un client d'un serveur Ejb, dans le nouveau projet et nous la renommons [MainClientWs].

http://tahe.developpez.com/java/javaee

122/341

en [1], la classe du client du service web. La classe [MainClientWs] prsente des erreurs. Pour les corriger, on commencera par supprimer toutes les instructions [import] existantes dans la classe et on les rgnerera par l'option [Fix Imports]. En effet, certaines des classes utilises par la classe [MainClientWs] font dsormais partie du package [clientws.metier] [2] gnr. en [3], le morceau de code o la couche [metier] est instancie [3]. Elle est avec du code JNDI pour obtenir une rfrence sur un Ejb distant.

Nous faisons voluer le code de la faon suivante :


le code JNDI est supprim la classe [PamException] n'existant pas ct client, nous supprimons le catch associ pour ne garder que le catch sur la classe mre [Exception].

en [4], il nous reste obtenir une rfrence sur la service web distant [Metier] afin de pouvoir appeler sa mthode [calculerFeuilleSalaire]. en [5], avec la souris, nous tirons (drag) la mthode [calculerFeuilleSalaire] du service web [Metier] pour la dposer (drop) en [4]. Du code est gnr [6]. Ce code gnrique peut tre ensuite adapt par le dveloppeur.

http://tahe.developpez.com/java/javaee

123/341

ligne 71, on voit que [calculerFeuilleSalaire] est une mthode de la classe [clientws.metier.Metier] (ligne 65). Maintenant que nous savons comment obtenir la couche [metier], le code prcdent peut tre rcrit de la faon suivante :

1. ... 2. // c'est bon - on peut demander la feuille de salaire 3. FeuilleSalaire feuilleSalaire = null; 4. Metier metier = null; 5. try { 6. // instanciation couche [metier] 7. metier = new MetierService().getMetierPort(); 8. // calcul de la feuille de salaire 9. feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles, nbJoursTravaills); 10. } catch (Throwable th) { 11. // chane des exceptions 12. System.out.println("Chane des exceptions --------------------------------------"); 13. System.out.println(th.getClass().getName() + ":" + th.getMessage()); 14. while (th.getCause() != null) { 15. th = th.getCause(); 16. System.out.println(th.getClass().getName() + ":" + th.getMessage()); 17. } 18. System.exit(1); 19. } 20. // affichage rapide 21. ...

La ligne 7 rcupre une rfrence sur le service web Metier. Ceci fait, le code de la classe ne change pas, si ce n'est quand mme qu'en ligne 7, ce n'est pas l'exception de type [Exception] qui est gre mais le type plus gnral Throwable, la classe parent de la classe Exception. S'il y a exception, nous affichons toutes les causes imbriques de celle-ci jusqu' la cause originelle. Nous sommes prts pour les tests :

s'assurer que le SGBD MySQL5 est lanc, que la base dbpam_eclipselink est cre et initialise s'assurer que le service web est dploy sur le serveur Glassfish construire le client (Clean and Build) configurer l'excution du client

http://tahe.developpez.com/java/javaee

124/341

excuter le client

Les rsultats dans la console sont les suivants :


1. ... 2. Valeurs saisies : 3. N de scurit sociale de l'employ : 254104940426058 4. Nombre d'heures travailles : 150 5. Nombre de jours travaills : 20 6. 7. Informations Employ : 8. Nom : Jouveinal 9. Prnom : Marie 10. Adresse : 5 rue des oiseaux 11. ...

Avec la configuration suivante :

on obtient les rsultats suivants :


1. 2. 3. 4. Chane des exceptions -------------------------------------javax.xml.ws.soap.SOAPFaultException:L'employ de n[xx] est introuvable com.sun.xml.internal.ws.developer.ServerSideException:L'employ de n[xx] est introuvable Java Result: 1

On notera qu'alors que le service web [Metier] envoie une exception de type [PamException] l'exception reue par le client de type [SOAPFaultException]. Mme dans la chane des exceptions, on ne voit pas apparatre le type [PamException].

14.1.3

Le client swing du service web Metier

Travail faire : porter le client swing du projet [pam-clientswing-metier-dao-jpa-eclipselink] dans le nouveau projet afin que lui aussi soit client du service web dploy sur le serveur Glassfish.

14.2

Service web implment par une application web

Nous nous plaons maintenant dans le cadre de l'architecture suivante :

Client du service web tcp-ip

Conteneur web

Conteneur Ejb3

Jpa

Donnes

serveur Java EE

http://tahe.developpez.com/java/javaee

125/341

Le service web est assur par une application web excute au sein du conteneur web du serveur Glassfish. Ce service web va s'appuyer sur l'Ejb [Metier] dploy lui dans le conteneur Ejb3.

14.2.1

La partie serveur

Nous crons une application web :

1 2

en [1], nous crons un nouveau projet en [2], ce projet est de type [Web Application] en [3], nous lui donnons le nom [pam-webservice-metier-dao-jpa-eclipselink]

en [4], nous choisissons la version Java EE 5 en [5], nous n'utilisons aucun des frameworks proposs en [6], le projet cr

Sur le schma ci-dessous, l'application web cre va s'excuter dans le conteneur web. Elle va utiliser l'Ejb [Metier] qui lui sera dploy dans le conteneur Ejb du serveur.

Client du service web tcp-ip

Conteneur web

Conteneur Ejb3

Jpa

Donnes

serveur Java EE

Pour que l'application web cre ait accs aux classes associes l'Ejb [Metier], nous ajoutons aux bibliothques de l'application web [pam-webservice-metier-dao-jpa-eclipselink], l'archive jar du serveur Ejb [pam-serveur-metier-dao-jpa-eclipselink] dj tudi.

http://tahe.developpez.com/java/javaee

126/341

1 4

5 3

en [1], on ajoute un projet aux bibliothques du projet web en [2,3], on slectionne le projet [pam-serveur-metier-dao-jpa-eclipselink] en [4], c'est l'archive .jar du projet qui sera ajout aux bibliothques du projet web en [5], le jar du projet Ejb a t ajoute aux bibliothques du projet web.

Pour crer le mme service web que prcdemment, il nous faut :


crer une classe tague @Webservice avec deux mthodes calculerFeuilleSalaire et findAllEmployes tagues @WebMethod

Nous crons une classe [PamWsEjbMetier] dans un package [pam.ws] :

La classe [PamWsEjbMetier] est la suivante :


1. package pam.ws; 2. 3. import java.util.List; 4. import javax.ejb.EJB; 5. import javax.jws.WebMethod; 6. import javax.jws.WebService; 7. import jpa.Employe; 8. import metier.FeuilleSalaire; 9. import metier.IMetier; 10. import metier.IMetierLocal; 11. 12. @WebService 13. public class PamWsEjbMetier implements IMetier{ 14. 15. @EJB 16. private IMetierLocal metier; 17. 18. @WebMethod

http://tahe.developpez.com/java/javaee

127/341

19.

public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills) { 20. return metier.calculerFeuilleSalaire(SS, nbHeuresTravailles, nbJoursTravaills); 21. } 22. 23. @WebMethod 24. public List<Employe> findAllEmployes() { 25. return metier.findAllEmployes(); 26. } 27. 28. }

lignes 7-10 : la classe importe des classes du module Ejb [pam-serveurws-metier-dao-jpa-eclipselink] dont l'archive .jar a t ajoute aux bibliothques du projet. ligne 12 : la classe est un service web ligne 13 : elle implmente l'interface IMetier dfinie dans le module Ejb lignes 18-19 : la mthode calculerFeuilleSalaire est expose comme mthode du service web lignes 23-24 : la mthode findAllEmployes est expose comme mthode du service web lignes 15-16 : l'interface locale de l'Ejb [Metier] est injecte dans le champ de la ligne 16. Nous utilisons l'interface locale car l'application web et le module Ejb s'excutent dans la mme Jvm. lignes 20 et 25 : les mthodes calculerFeuilleSalaire et findAllEmployes dlguent leur traitement aux mthodes de mme nom de l'Ejb [Metier]. La classe ne sert donc qu' exposer des clients distants les mthodes de l'Ejb [Metier] comme des mthodes d'un service web.

Dans Netbeans, l'application web est reconnue comme exposant un service web :

2 1 3

Pour dployer le service web sur le serveur Glassfish, il nous faut la fois dployer :

le module web dans le conteneur web du serveur le module Ejb dans le conteneur Ejb du serveur

Pour cela, nous avons besoin de crer une application de type [Enterprise Application] qui va dployer les deux modules en mme temps. Pour ce faire, il faut que les deux projets soient chargs dans Netbeans [2]. Ceci fait, nous crons un nouveau projet [3].

en [4], nous choisissons un projet de type [Enterprise Application]. en [5], nous donnons un nom au projet

http://tahe.developpez.com/java/javaee

128/341

7 8

en [6], nous configurons le projet. La version de Java EE sera Java EE 5. Un projet d'entreprise peut tre cr avec trois modules : un module Ejb, un module Web, un module Client d'Application. Ici, le projet d'entreprise va encapsuler le module Web et le module Ejb dj crs et chargs dans Netbeans. Donc nous ne demandons pas la cration de nouveaux modules. en [7], le projet d'entreprise ainsi cr en [8], nous ajoutons des modules au projet d'entreprise

10

en [9], les modules Web et Ejb chargs dans Netbeans sont prsents. Nous slectionnons les deux modules ci-dessus. en [10], le projet d'entreprise inclut dsormais les modules Web et Ejb que nous devons dployer sur le serveur Glassfish.

Nous construisons le projet d'entreprise par un Clean and Build. Nous sommes quasiment prts le dployer sur le serveur Glassfish. Auparavant il peut tre ncessaire de dcharger les applications dj charges sur le serveur afin d'viter d'ventuels conflits de noms d'Ejb [11] :

11 13

12

Le serveur MySQL doit tre lanc et la base [dbpam_eclipselink] disponible et remplie. Ceci fait, l'application d'entreprise peut tre dploye [12]. En [13], on peut voir qu'elle a bien t dploye sur le serveur Glassfish. Nous pouvons tester le service web qui vient d'tre dploy :

http://tahe.developpez.com/java/javaee

129/341

1 2

en [1], nous demandons tester le service web [PamWsEjbMetier] en [2], la page de test. Nous laissons au lecteur le soin de conduire les tests.

14.2.2

La partie cliente

Travail faire : en suivant la dmarche dcrite au paragraphe 14.1.2.1, page 121, construire un client console du service web prcdent.

14.2.3

Service web implment avec Spring

Voir paragraphe 20, page 208.

http://tahe.developpez.com/java/javaee

130/341

15 Version 5 - Application PAM web / JSF


Dans cette partie, nous abordons la construction d'une interface web avec JSF (JavaServer Faces). Le lecteur ne connaissant pas JSF trouvera en page 224, un cours prsentant les bases indispensables connatre et qui seront utilises ici.

15.1

Architecture de l'application

L'architecture de l'application web PAM sera la suivante :

Couche [web / jsf]

Couche Ejb3 [metier]

Couche Ejb3 [dao] 7

Objets image de la BD

Interface [JPA]

Implmentation [EclipseLink]

Couche [JDBC]

Serveur Glassfish v3

Dans cette version, le serveur Glassfish hbergera la totalit des couches de l'application : la couche [web] est hberge par le conteneur de servlets du serveur (1 ci-dessous) les autres couches [metier, dao, jpa] sont hberges par le conteneur Ejb3 du serveur (2 ci-dessous)

Navigateur HTTP

Jpa 3 Conteneur web Conteneur 2 [web / jsf] Ejb3 [metier, dao] EclipseLink
1

SGBD

serveur Java EE

Les lments [metier, dao] de l'application s'excutant dans le conteneur Ejb3 ont dj t crits dans l'application client / serveur tudie au paragraphe 13.1, page 90 et dont l'architecture tait la suivante : client Couche [ui] serveur Couche [metier] Couche [dao] Couche [JPA / EclipseLink] Couche [JDBC]

Jvm1 - Java SE

Jvm2 Java EE - serveur Glassfish

Les couches [metier, dao] s'excutaient dans le conteneur Ejb3 du serveur Glassfish et la couche [ui] dans une application console ou swing sur une autre machine : client serveur

Client Java SE
Dans l'architecture de la nouvelle application :

Conteneur Ejb3

Jpa / EclipseLink

Donnes

serveur Java EE

http://tahe.developpez.com/java/javaee

131/341

Navigateur HTTP

Jpa 3 Conteneur web Conteneur 2 [web / jsf] Ejb3 [metier, dao] EclipseLink
1

SGBD

serveur Java EE

seule la couche [web / jsf] est crire. Les autres couches [metier, dao, jpa] sont acquises. Dans le document [jsf], il est montr qu'une application web o la couche web est implmente avec JavaServer Faces a une architecture similaire la suivante :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

couche [metier, dao, jpa]

Donnes

Modles

Cette architecture implmente le Design Pattern MVC (Modle, Vue, Contrleur). Le traitement d'une demande d'un client se droule de la faon suivante : Si la demande est faite avec un GET, les deux tapes suivantes sont excutes : 1. demande - le client navigateur fait une demande au contrleur [Faces Servlet]. Celui-ci voit passer toutes les demandes des clients. C'est la porte d'entre de l'application. C'est le C de MVC. 2. rponse - le contrleur C demande la page JSP choisie de s'afficher. C'est la vue, le V de MVC. La page JSP utilise un modle M pour initialiser les parties dynamiques de la rponse qu'elle doit envoyer au client. Ce modle est une classe Java qui peut faire appel la couche [mtier] [4a] pour fournir la vue V les donnes dont elle a besoin. Si la demande est faite avec un POST, deux tapes suppmentaires s'insrent entre la demande et la rponse : 1. demande - le client navigateur fait une demande au contrleur [Faces Servlet]. 2. traitement - le contrleur C traite cette demande. En effet, une demande POST est accompagne de donnes qu'il faut traiter. Pour ce faire, le contrleur se fait aider par des gestionnaires d'vnements spcifiques l'application crite [2a]. Ces gestionnaires peuvent avoir besoin de la couche mtier [2b]. Le gestionnaire de l'vnement peut tre amen mettre jour certains modles M [2c]. Une fois la demande du client traite, celle-ci peut appeler diverses rponses. Un exemple classique est : une page d'erreurs si la demande n'a pu tre traite correctement une page de confirmation sinon Le gestionnaire d'vnement rend au contrleur [Faces Servlet] un rsultat de type chane de caractres appele une cl de navigation. 3. navigation - le contrleur choisit la page JSP (= vue) envoyer au client. Ce choix se fait partir de la cl de navigation rendue par le gestionnaire d'vnement. 4. rponse - la page JSP choisie va envoyer la rponse au client. Elle utilise son modle M pour initialiser ses parties dynamiques. Ce modle peut lui aussi faire appel la couche [mtier] [4a] pour fournir la page JSP les donnes dont elle a besoin. Dans un projet JSF : le contrleur C est la servlet [javax.faces.webapp.FacesServlet]. On trouve celle-ci dans la bibliothque [jsf-api.jar]. les vues V sont implmentes par des pages JSP. les modles M et les gestionnaires d'vnements sont implments par des classes Java souvent appeles "backing beans". les rgles de navigation sont dfinies dans le fichier [faces-config.xml]. On y trouve la liste des vues et les rgles de transition de l'une l'autre.

http://tahe.developpez.com/java/javaee

132/341

15.2

Fonctionnement de l'application

Lorsque l'application est demande la premire fois, on obtient la page suivante : A

On remplit alors le formulaire puis on demande le salaire : B

On obtient le rsultat suivant : C

http://tahe.developpez.com/java/javaee

133/341

Cette version calcule un salaire fictif. Il ne faut pas prter attention au contenu de la page mais sa mise en forme. Lorsqu'on utilise le bouton [Raz], on revient la page [A]. Les saisies errones sont signales, comme le montre l'exemple suivant : D

15.3

Le projet Netbeans

Nous allons construire une premire version de l'application o la couche [mtier] sera simule. Nous aurons l'architecture suivante :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

Modles

couche [metier] simule

Lorsque les gestionnaires d'vnement ou les modles demanderont des donnes la couche [mtier] [2b, 4a], celle-ci leur donnera des donnes fictives. Le but est d'obtenir une couche web rpondant correctement aux sollicitations de l'utilisateur. Lorsque ceci sera atteint, il ne nous restera qu' installer la couche serveur dveloppe au paragraphe 13.1, page 90 :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

couche [metier, dao, jpa]

Donnes

Modles

Ce sera la version 2 de la version web notre application PAM. Le projet Netbeans de la version 1 sera le suivant :

http://tahe.developpez.com/java/javaee

134/341

4 6 2 5

en [1], les fichiers de configuration en [2], les pages Jsp et la feuille de style en [3], les classes de la couche [web] en [4], le fichier des messages pour l'internationalisation de l'application en [5], les objets changs entre la couche [web] et la couche [mtier] et la couche [mtier] elle-mme en [6], les bibliothques de l'application

Nous passons en revue certains de ces lments.

15.3.1

Les fichiers de configuration

Le fichier [web.xml] est celui gnr par dfaut par Netbeans avec de plus la configuration d'une page d'exception :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <servlet> 4. <servlet-name>Faces Servlet</servlet-name> 5. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 6. <load-on-startup>1</load-on-startup> 7. </servlet> 8. <servlet-mapping> 9. <servlet-name>Faces Servlet</servlet-name> 10. <url-pattern>/faces/*</url-pattern> 11. </servlet-mapping> 12. <session-config> 13. <session-timeout> 14. 30 15. </session-timeout> 16. </session-config> 17. <welcome-file-list> 18. <welcome-file>faces/form.jsp</welcome-file> 19. </welcome-file-list> 20. <error-page> 21. <error-code>500</error-code> 22. <location>/faces/exception.jsp</location> 23. </error-page> 24. <error-page> 25. <exception-type>java.lang.Exception</exception-type> 26. <location>/faces/exception.jsp</location> 27. </error-page> 28. 29. </web-app>

ligne 18 : [form.jsp] est la page d'accueil de l'application lignes 20-27 : configuration de la page d'exception

http://tahe.developpez.com/java/javaee

135/341

La page [exception.jsp] reprend la technique dveloppe dans l'exemple du paragraphe 21.5.4 "Gestion des exceptions" du cours [jsf]. Son code est le suivant :
1. <%@page contentType="text/html" pageEncoding="UTF-8"%> 2. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 4. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 5. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 6. "http://www.w3.org/TR/html4/loose.dtd"> 7. 8. <% 9. // on change le code Http de la page pour certaines versions d'IE 10. response.setStatus(HttpServletResponse.SC_OK); 11. %> 12. 13. 14. <f:view> 15. <html> 16. <head> 17. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 18. <title>Erreur</title> 19. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 20. </head> 21. <body background="<c:url value="/ressources/standard.jpg"/>"> 22. 23. <h3><h:outputText value="#{msg['exception.header']}"/></h3> 24. <h:outputText value="#{requestScope['javax.servlet.error.message']}"/> 25. </body> 26. </html> 27. </f:view>

Toute exception non explicitement gre par le code de l'application web provoquera l'affichage de la page ci-dessus. Le fichier [faces-config.xml] qui configure la navigation et les beans manags de l'application Jsf sera le suivant :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <managed-bean> 5. <managed-bean-name>form</managed-bean-name> 6. <managed-bean-class>web.forms.Form</managed-bean-class> 7. <managed-bean-scope>request</managed-bean-scope> 8. </managed-bean> 9. <managed-bean> 10. <managed-bean-name>locale</managed-bean-name> 11. <managed-bean-class>web.utils.ChangeLocale</managed-bean-class> 12. <managed-bean-scope>application</managed-bean-scope> 13. </managed-bean> 14. <application> 15. <resource-bundle> 16. <base-name> 17. messages 18. </base-name> 19. <var>msg</var> 20. </resource-bundle> 21. <message-bundle>messages</message-bundle> 22. </application> 23. </faces-config>

On notera les points suivants :


lignes 4-8 : la classe [web.forms.Form] est le modle de la page [form.jsp]. Sa dure de vie est la requte du client. Elle sera accessible dans les pages Jsp via la cl form. lignes 9-13 : la classe [web.utils.ChangeLocale] permet de changer la langue des pages. Cette fonctionnalit n'est pas utilise ici mais nous crirons l'application afin qu'elle puisse facilement tre internationalise. lignes 15-20 : le fichier [messages.properties] sera utilis pour l'internationalisation des pages. Il sera accessible dans les pages Jsp via la cl msg. ligne 21 : dfinit le fichier [messages.properties] comme devant tre explor en priorit pour les messages d'erreur affichs par les balises <h:messages> et <h:message>. Cela permet de redfinir certains messages d'erreur par dfaut de Jsf. Cette possibilit n'est pas utilise ici.

http://tahe.developpez.com/java/javaee

136/341

15.3.2

La feuille de style

Le fichier [styles.css] est le suivant :


1. .libelle{ 2. background-color: #ccffff; 3. font-family: 'Times New Roman',Times,serif; 4. font-size: 14px; 5. font-weight: bold 6. } 7. body{ 8. background-color: #ffccff 9. } 10. 11. .error{ 12. color: #ff3333 13. } 14. 15. .info{ 16. background-color: #99cc00 17. } 18. 19. .titreInfos{ 20. background-color: #ffcc00 21. }

Voici des exemples de code Jsp utilisant ces styles :


<h:outputText value="#{msg['form.infos.employ']}" styleClass="titreInfos"/> <h:panelGrid columns="3" rowClasses="libelle,info">

<h:message for="heuresTravailles" styleClass="error"/>

15.3.3

Le fichier des messages

Le fichier des messages [messages_fr.properties] est le suivant :


1. form.titre=Feuille de salaire 2. form.comboEmployes.libell=Employ 3. form.heuresTravailles.libell=Heures travailles 4. form.joursTravaills.libell=Jours travaills 5. form.heuresTravailles.required=Indiquez le nombre d'heures travailles 6. form.heuresTravailles.validation=Donne incorrecte 7. form.joursTravaills.required=Indiquez le nombre de jours travaills 8. form.joursTravaills.validation=Donne incorrecte 9. form.btnSalaire.libell=Salaire 10. form.btnRaz.libell=Raz 11. exception.header=L'exception suivante s'est produite : 12. form.infos.employ=Informations Employ 13. form.employe.nom=Nom 14. form.employe.prnom=Prnom 15. form.employe.adresse=Adresse 16. form.employe.ville=Ville 17. form.employe.codePostal=Code postal 18. form.employe.indice=Indice 19. form.infos.cotisations=Informations Cotisations sociales 20. form.cotisations.csgrds=CSGRDS 21. form.cotisations.csgd=CSGD 22. form.cotisations.retraite=Retraite 23. form.cotisations.secu=Scurit sociale 24. form.infos.indemnites=Informations Indemnits 25. form.indemnites.salaireHoraire=Salaire horaire 26. form.indemnites.entretienJour=Entretien / Jour 27. form.indemnites.repasJour=Repas / Jour 28. form.indemnites.congsPays=Congs pays 29. form.infos.salaire=Informations Salaire 30. form.salaire.base=Salaire de base

http://tahe.developpez.com/java/javaee

137/341

31. form.salaire.cotisationsSociales=Cotisations sociales 32. form.salaire.entretien=Indemnits d'entretien 33. form.salaire.repas=Indemnits de repas 34. form.salaire.net=Salaire net

Ces messages sont tous utiliss dans la page [form.jsp] l'exception de celui de la ligne 11 utilis dans la page [exception.jsp].

15.3.4

La couche [mtier]

La couche [mtier] implmente l'interface IMetierLocal suivante :


1. package metier; 2. 3. import java.util.List; 4. import javax.ejb.Local; 5. import jpa.Employe; 6. 7. @Local 8. public interface IMetierLocal { 9. // obtenir la feuille de salaire 10. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills ); 11. // liste des employs 12. List<Employe> findAllEmployes(); 13. }

Cette interface est celle utilise dans la partie serveur de l'application client / serveur dcrite au paragraphe 13.1, page 90. La classe Metier que nous allons utiliser pour tester la couche [web] implmente cette interface de la faon suivante :
1. package metier; 2. 3. ... 4. public class Metier implements IMetierLocal { 5. 6. // dictionnaire des employes index par le n SS 7. private Map<String,Employe> hashEmployes=new HashMap<String,Employe>(); 8. // liste des employs 9. private List<Employe> listEmployes; 10. 11. // obtenir la feuille de salaire 12. public FeuilleSalaire calculerFeuilleSalaire(String SS, 13. double nbHeuresTravailles, int nbJoursTravaills) { 14. // on rcupre l'employ de n SS 15. Employe e=hashEmployes.get(SS); 16. // on rend une feuille de salaire fiictive 17. return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),new ElementsSalaire(100,100,100,100,100)); 18. } 19. 20. // liste des employs 21. public List<Employe> findAllEmployes() { 22. if(listEmployes==null){ 23. // on cre une liste de deux employs 24. listEmployes=new ArrayList<Employe>(); 25. listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15))); 26. listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12))); 27. // dictionnaire des employes index par le n SS 28. for(Employe e:listEmployes){ 29. hashEmployes.put(e.getSS(),e); 30. } 31. } 32. // on rend la liste des employs 33. return listEmployes; 34. } 35. }

Nous laissons au lecteur le soin de dcrypter ce code. On notera la mthode utilise : afin de ne pas avoir mettre en place la partie Ejb de l'application, nous simulons la couche [mtier]. Lorsque la couche [web] sera dclare correcte, nous pourrons alors la remplacer par la vritable couche [mtier].

http://tahe.developpez.com/java/javaee

138/341

15.4

Le formulaire [form.jsp] et son modle [Form.java]

Nous construisons maintenant la page Jsp du formulaire ainsi que son modle. Lectures conseilles : exemple n 3 (intro-03), page 253, pour la listes des balises utilisables dans un formulaire exemple n 4 (intro-04), page 287, pour les listes droulantes remplies par le modle exemple n 6 (intro-06), page 305, pour la validation des saisies exemple n 7 (intro-07), page 327, pour la gestion du bouton [Raz]

15.4.1

tape 1

Question : Construire le formulaire [form.jsp] et son modle [Form.java] ncessaires pour obtenir la page suivante :

4 1 2 3

Les composants de saisie sont les suivants :


n 1 2 3 4 5

id

type Jsf

modle

rle

comboEmployes <h:selectOneMenu> String comboEmployesValue contient la liste des employs sous la forme SelectItem[] getEmployesItems() "prnom nom". String heuresTravailles heuresTravailles <h:inputText> nombre d'heures travailles - nombre rel String joursTravaills joursTravaills <h:inputText> nombre de jours travaills - nombre entier btnSalaire btnRaz <h:commandButton > <h:commandButton > lance le calcul du salaire remet le formulaire dans son tat premier

la mthode getEmployesItems rendra une liste d'employs qu'elle obtiendra auprs de la couche [mtier]. Les objets de type SelectItem construits par la mthode getEmployesItems auront pour attribut value, le n SS de l'employ et pour attribut label, une chane constitue du prnom et du nom de l'employ. les boutons [Salaire] et [Raz] ne seront pour l'instant pas connects des gestionnaires d'vnement. la validit des saisies sera vrifie.

Testez cette version. Vrifiez notamment que les erreurs de saisie sont bien signales.

http://tahe.developpez.com/java/javaee

139/341

15.4.2

tape 2

Question : complter le formulaire [form.jsp] et son modle [Form.java] ncessaires pour obtenir la page suivante une fois que le bouton [Salaire] a t cliqu :

Le bouton [Salaire] sera connect au gestionnaire d'vnement calculerSalaire du modle. Cette mthode utilisera la mthode calculerFeuilleSalaire de la couche [mtier]. Cette feuille de salaire sera faite pour l'employ slectionn en [1]. Dans le modle, la feuille de salaire sera reprsente par le champ priv suivant :
private FeuilleSalaire feuilleSalaire;

disposant des mthodes get et set. Pour obtenir les informations contenues dans cet objet, on pourra crire dans la page Jsp, des expressions comme la suivante :
<h:outputText value="#{form.feuilleSalaire.employe.nom}"/>

L'expression de l'attribut value sera value comme suit : [form].getFeuilleSalaire().getEmploye().getNom() o [form] reprsente une instance de la classe [Form.java]. Le lecteur pourra vrifier que les mthodes get utilises ici existent bien respectivement dans les classes [Form], [FeuilleSalaire] et [Employe]. Si ce n'tait pas le cas, une exception serait lance lors de l'valuation de l'expression. Testez cette nouvelle version.

15.4.3

tape 3

Question : complter le formulaire [form.jsp] et son modle [Form.java] pour obtenir les informations supplmentaires suivantes :

http://tahe.developpez.com/java/javaee

140/341

On suivra la mme dmarche que prcdemment. Il y a une difficult pour le signe montaire euro que l'on a en [1] par exemple. Dans le cadre d'une application internationalise, il serait prfrable d'avoir le format d'affichage et le signe montaire de la locale utilise (en, de, fr, ...). Cela peut s'obtenir de la faon suivante :
1. <h:outputFormat value="{0,number,currency}"> 2. <f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/> 3. </h:outputFormat>

On aurait pu crire :
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.entretienJour} ">

mais avec la locale en_GB (Anglais GB) on continuerait avoir un affichage en euros alors qu'il faudrait utiliser la livre . La balise <h:outputFormat> permet d'afficher des informations en fonction de la locale de la page Jsp affiche :

ligne 1 : affiche le paramtre {0} qui est un nombre (number) reprsentant une somme d'argent (currency)* ligne 2 : la balise <f:param> donne une valeur au paramtre {0}. Une deuxime balise <f:param> donnerait une valeur au paramtre not {1} et ainsi de suite.

15.4.4

tape 4

Lectures conseilles : exemple n 7 (intro-07), paragraphe 21.7.5, page 332. Question : complter le formulaire [form.jsp] et son modle [Form.java] pour grer le bouton [Raz]. Le bouton [Raz] ramne le formulaire dans l'tat qu'il avait lorsqu'on l'a demand la premire fois par un GET. Il y a plusieurs difficults ici. Certaines ont t expliques dans [jsf] (voir lectures conseilles). Le formulaire rendu par le bouton [Raz] n'est pas tout le formulaire mais seulement la partie saisie de celui-ci :

Ce rsultat peut tre obtenu avec une balise <f:subview> utilise de la faon suivante :

http://tahe.developpez.com/java/javaee

141/341

1. <f:subview id="viewInfos" rendered="#{form.viewInfosIsRendered}"> 2. ... la partie du formulaire qu'on veut pouvoir ne pas afficher 3. </f:subview>

La balise <f:subview> encadre toute la partie du formulaire qui est susceptible d'tre affiche ou cache. Tout composant peut tre affich ou cach grce l'attribut rendered. Si rendered="true", le composant est affich, si rendered="false", il ne l'est pas. Si l'attribut rendered prend sa valeur dans le modle, alors l'affichage du composant peut tre contrl par programme. Ci-dessus, on contrlera l'affichage de la vue viewInfos avec le champ suivant :
private boolean viewInfosIsRendered;

accompagn de ses mthodes get et set. Les mthodes grant les clics sur les bouton [Salaire] [Raz] mettront jour ce boolen selon que la vue viewInfos doit tre affiche ou non.

http://tahe.developpez.com/java/javaee

142/341

16 Version 6 - Intgration de la couche web dans une architecture 3 couches Jsf / Ejb
16.1 Architecture de l'application

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

Modles

couche [metier] simule

Nous remplaons la couche [mtier] simule, par les couches [mtier, dao, jpa] implmentes par des Ejb au paragraphe 13.1, page 90 :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

couches [metier, dao, jpa]

Donnes

Modles

16.2

Le projet Netbeans de la couche web

Le projet Netbeans de la version web n 2 est obtenue par copie du projet prcdent :

http://tahe.developpez.com/java/javaee

143/341

Nous avons trs peu de modifications faire pour adapter cette couche web son nouvel environnement : la couche [metier] simule doit tre remplace par la couche [metier, dao, jpa] du serveur construit au paragraphe 13.1, page 90. Pour cela, nous faisons deux choses :

nous supprimons les paquetages [exception, metier, jpa] qui taient prsents dans le prcdent projet. nous ajoutons aux bibliothques du projet web, le projet du serveur Ejb construit au paragraphe 13.1, page 90.

en [1], ajout d'un projet aux bibliothques du projet courant en [2], choix du projet Ejb [pam-serveur-metier-dao-jpa-eclipselink]

4 3

http://tahe.developpez.com/java/javaee

144/341

en [3], l'archive a t ajoute aux bibliothques en [4], on a supprim les paquetages [exception, metier, jpa] du prcdent projet. Ceux-ci sont dsormais trouvs dans la bibliothque [pam-serveur-metier-dao-ejb-jpa-eclipselink.jar] [3] :

Il nous faut galement modifier le code du bean [Form.java] :


1.public class Form { 2. 3. public Form() { 4. } 5. 6. // couche mtier 7. private IMetierLocal metier=new Metier(); 8. 9. // champs du formulaire 10....

La ligne 7 instanciait la couche [mtier] simule. Dsormais elle doit rfrencer la couche [mtier] relle. Le code prcdent devient le suivant :
1. public class Form { 2. 3. public Form() { 4. } 5. 6. // couche mtier 7. @EJB 8. private IMetierLocal metier; 9. 10. // champs du formulaire

Ligne 7, l'annotation @EJB indique au conteneur de servlets qui va excuter la couche web, d'injecter dans le champ metier de la couche 8, l'Ejb qui implmente l'interface locale IMetierLocal. Pourquoi l'interface locale IMetierLocal plutt que l'interface IMetierRemote ? Parce que la couche web et la couche Ejb s'excutent dans la mme Jvm :

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts
2c 4a

couche [metier, dao, jpa]

Donnes

Modles

Conteneur de servlets

Conteneur Ejb

Les classes du conteneur de servlets peuvent rfrencer directement les classes Ejb du conteneur Ejb. C'est tout. Notre couche web est prte. La transformation a t simple parce qu'on avait pris soin de simuler la couche [mtier] par une classe qui respectait l'interface IMetierLocal implmente par la couche [mtier] relle.

16.3

Le projet Netbeans de l'application d'entreprise

Une application d'entreprise permet le dploiement simultan sur un serveur d'applications, de la couche [web] et de la couche [ejb] d'une application, respectivement dans le conteneur de servlets et dans le conteneur Ejb. Nous procdons de la faon suivante :

http://tahe.developpez.com/java/javaee

145/341

4 3 1 2

en [1], on cre un nouveau projet en [2], on choisit la catgorie [Enterprise] en [3], on choisit le type [ Enterprise Application] en [4], on donne un nom au projet

7 5 6

en [5], nous choisissons Java EE 5 en [6], un projet d'entreprise peut comprendre jusqu' trois types de modules : un module Ejb un module web un module dit client On peut demander en mme temps que la cration du projet d'entreprise, la cration de ces trois modules qui seront vides au dpart. Un projet d'entreprise ne sert qu'au dploiement des modules qui en font partie. En-dehors de a, c'est une coquille vide. Ici, nous voulons dployer : un module web existant (pam-jsf). Il est donc inutile de crer un nouveau module web. un module Ejb existant [pam-serveur-metier-dao-jpa-eclipselink]. L galement, il est inutile d'en crer un nouveau. En [6], nous crons un projet d'entreprise sans modules. Nous allons lui ajouter ses modules web et ejb ultrieurement. en [7], le projet d'entreprise. Sa branche [Java EE modules] est vide. en [8], nous chargeons dans Netbeans le module Ejb [pam-serveur-metier-dao-jpa-eclipselink] qui va tre intgr au module d'application d'entreprise.

Nous ajoutons le module web et le module Ejb au projet d'entreprise :

1 2

en [1], ajout d'un nouveau module en [2], choix du projet web [pam-jsf-ejb]et du projet Ejb [pam-serveur-metier-dao-jpa-eclipselink] en [3], le projet d'entreprise a deux modules

Ceci-fait, construisons le binaire de l'application d'entreprise :

http://tahe.developpez.com/java/javaee

146/341

2 1 3 4

en [1], construction du projet en [2], dans l'onglet [Files], le dossier [dist] contient l'archive [pam-ea-jsf-ejb.ear] du projet. On notera son suffixe particulier : ear [Enterprise ARchive]. en [3] : [pam-jsf-ejb.war] est l'archive du projet web [pam-jsf-ejb]. Le suffixe war signifie Web ARchive. On trouve galement en [3], l'archive [pam-serveur-metier-dao-jpa-eclipselink.jar]. en [4] : le fichier [persistence.xml] qui configure la couche de persistance du module Ejb. Son contenu est le suivant :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 3. <persistence-unit name="pam-serveur-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> 4. <jta-data-source>jdbc/dbpam_eclipselink</jta-data-source> 5. <exclude-unlisted-classes>false</exclude-unlisted-classes> 6. <properties/> 7. </persistence-unit> 8. </persistence>

Avant le dploiement de l'application d'entreprise [pam-ea-jsf-ejb], on s'assurera que la base MySQL [dbpam_eclipselink] existe et est remplie. Ceci fait, nous pouvons dployer l'application d'entreprise [pam-ea-jsf-ejb] :

3 1

en [1], l'application d'entreprise est dploye en [2], l'application d'entreprise [pam-ea-jsf-ejb] a bien t dploye. en [3], elle est excute

Si l'application est excute sans que le serveur MySQL soit lanc, on obtient la page d'exception :

http://tahe.developpez.com/java/javaee

147/341

Si nous lanons MySQL et rexcutons l'application, on obtient la page suivante :

en [1], l'Url affiche aprs la redirection faite par [index.jsp] en [2], la liste des employs a t remplie avec les lments de la table [Employes] de la base dbpam.

Le lecteur est invit refaire les tests de la version web n 1. Voici un exemple d'excution :

http://tahe.developpez.com/java/javaee

148/341

http://tahe.developpez.com/java/javaee

149/341

17 Version 7 - Application web PAM multi-vues / mono-page


Nous revenons ici l'architecture initiale o la couche [mtier] tait simule. Nous savons dsormais que celle-ci peut tre aisment remplace par la couche [mtier] relle. La couche [mtier] simule facilite les tests.

Application web
couche [web]
1

Faces Servlet
4b

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaires d'vts
2c 4a

Modles

couche [metier] simule

Une application JSF est de type MVC (Modle Vue Contrleur) : la servlet [Faces Servlet] est le contrleur gnrique fourni par JSF. Ce contrleur est tendu par les gestionnaires d'vnements spcifiques l'application. Les gestionnaires d'vnements rencontrs jusqu'ici taient des mthodes des classes servant de modles aux pages JSP les pages JSP envoient les rponses au navigateur client. Ce sont les vues de l'application. les pages JSP comportent des lments dynamiques qu'on appelle le modle de la page. On rappelle que pour certains auteurs, le modle recouvre les entits manipules par l'application, telles par exemple les classes FeuilleSalaire ou Employe. Pour distinguer ces deux modles, on pourra parler de modle de l'application et modle d'une page JSP. Dans l'architecture JSF, le passage d'une page JSPi une page JSPj est un peu problmatique. la page JSPi a t affiche. A partir de cette page, l'utilisateur provoque un POST par un vnement quelconque [1] en JSF, ce POST sera trait [2a,2b] en gnral par une mthode C du modle M i de la page JSP i. On peut dire que la mthode C est un contrleur secondaire. si l'issue de cette mthode, la page JSPj doit tre affiche, le contrleur C doit : 1. mettre jour [2c] le modle Mj de la page JSPj 2. rendre [2a] au contrleur principal, la cl de navigation qui permettra l'affichage de la page JSP j L'tape 1 ncessite que le modle Mi de la page JSPi ait une rfrence sur modle Mj de la page JSPj. Cela complique un peu les choses rendant les modles Mi dpendant les uns des autres. En effet, le gestionnaire C du modle M i qui met jour le modle Mj doit connatre celui-ci. Si on est amen changer le modle M j, on sera alors amen changer le gestionnaire C du modle Mi. Il existe un cas o la dpendance des modles entre-eux peut tre vite : celui o il y a un unique modle M qui sert toutes les pages JSP. Cette architecture n'est utilisable que dans les applications n'ayant que quelques vues mais elle se rvle alors trs simple d'usage. C'est celle que nous utilisons maintenant. Nous nous plaons dans le contexte suivant : les vues V seront gnres partir d'une unique page JSF par le mcanisme des vues internes <f:subView>. une unique classe servira de modle M la page JSF et de contrleur C qui traitera les vnements des vues V avec des mthodes internes la classe. Dans ce contexte, l'architecture de l'application est la suivante :

http://tahe.developpez.com/java/javaee

150/341

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couche [metier] simule

Pour la demande GET initiale : [Faces Servlet] reoit une demande GET d'affichage de la page [form.jsp] en [1] il fait afficher celle-ci en [3]. La page utilise son modle / contrleur [Form.java] pour initialiser ses parties dynamiques. En [4], une vue Vi parmi d'autres est affiche, les autres restant caches. Pour les demandes POST qui suivent : [Faces Servlet] reoit une demande POST pour la page [form.jsp] en [1] l'vnement qui a provoqu le POST est trait par l'une des mthodes du modle / contrleur [Form.java] [2a, 2b]. Celleci met jour le modle M afin qu'une vue Vi particulire soit affiche et les autres caches. La mthode rend comme cl de navigation la cl null. [Faces Servlet] ayant reu la cl null, fait afficher de nouveau [form.jsp] en [3]. La page utilise son modle [Form.java] pour initialiser ses parties dynamiques. En [4], la vue Vi est affiche, les autres restant caches.

17.1

Les vues de l'application

Les diffrentes vues prsentes l'utilisateur seront les suivantes : - la vue [VueSaisies] qui prsente le formulaire de simulation

- la vue [VueSimulation] utilise pour afficher le rsultat dtaill de la simulation :

http://tahe.developpez.com/java/javaee

151/341

- la vue [VueSimulations] qui donne la liste des simulations faites par le client

- la vue [VueSimulationsVides] qui indique que le client n'a pas ou plus de simulations :

la vue [VueErreur] qui indique une ou plusieurs erreurs :

http://tahe.developpez.com/java/javaee

152/341

17.2

Le projet Netbeans de la couche [web]

Le projet Netbeans de cette version sera le suivant :

4 5 2 3

en [1], les fichiers de configuration en [2], les pages Jsp et la feuille de style en [3], les classes de la couche [web] en [4], le fichier des messages pour l'internationalisation de l'application en [5], les objets changs entre la couche [web] et la couche [mtier], et la couche [mtier] elle-mme en [6], les bibliothques de l'application

Nous passons en revue certains de ces lments.

17.2.1

Les fichiers de configuration

Le fichier [web.xml] est celui des versions prcdentes. Le fichier [faces-config.xml] volue de la faon suivante :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <managed-bean> 5. <managed-bean-name>applicationData</managed-bean-name> 6. <managed-bean-class>web.beans.application.ApplicationData</managed-bean-class> 7. <managed-bean-scope>application</managed-bean-scope> 8. </managed-bean> 9. <managed-bean> 10. <managed-bean-name>form</managed-bean-name>

http://tahe.developpez.com/java/javaee

153/341

11. <managed-bean-class>web.beans.session.Form</managed-bean-class> 12. <managed-bean-scope>session</managed-bean-scope> 13. <managed-property> 14. <property-name>applicationData</property-name> 15. <value>#{applicationData}</value> 16. </managed-property> 17. </managed-bean> 18. <managed-bean> 19. <managed-bean-name>locale</managed-bean-name> 20. <managed-bean-class>web.utils.ChangeLocale</managed-bean-class> 21. <managed-bean-scope>application</managed-bean-scope> 22. </managed-bean> 23. <application> 24. <resource-bundle> 25. <base-name> 26. messages 27. </base-name> 28. <var>msg</var> 29. </resource-bundle> 30. <message-bundle>messages</message-bundle> 31. </application> 32. </faces-config>

Les lignes 4-8 dclarent un bean nomm applicationData (ligne 5) de porte application (ligne 7). Nous mettrons dans ce bean, les donnes qui peuvent tre partages par toutes les requtes de tous les navigateurs clients. Dans cette application, il s'agit de la liste des employs. Dans la version prcdente, cette liste tait maintenue dans le bean form (ligne 10), de porte session (ligne 12). Elle tait obtenue en dbut de session par appel la mthode [metier].findAllEmployes(). dans la chane de couches [mtier, dao] l'une des couches peut mmoriser la liste des employs en mmoire une fois qu'elle a t obtenue depuis la base de donnes. La mthode [metier].findAllEmployes() ne rend alors qu'une rfrence sur cet emplacement mmoire. Les beans form des diffrentes sessions utilisateurs ont alors une rfrence de plus sur ce mme emplacement. si aucune des couches [mtier, dao] n'a mmoris la liste des employs, la mthode [metier].findAllEmployes() fait alors chaque fois qu'elle est excute un accs la base de donnes. On a alors un accs par session utilisateur. Si par ailleurs, chaque bean form mmorise la liste obtenue, on a, un moment donn, la liste des employs prsente n fois en mmoire. On a alors des performances dgrades vis vis de la solution prcdente. La couche [web] ne sait pas ncessairement comment la mthode [metier].findAllEmployes() obtient la liste des employs. Elle cherchera optimiser les performances quelque soit la solution adopte par les couches [mtier, dao], mmorisation ou non de la liste des employs. Aussi il est de l'intrt de la couche [web] de mmoriser elle-mme cette liste et de la partager entre toutes les sessions utilisateurs. Ce sera fait dans le bean applicationData de porte application. si cette liste d'employs est dj mmorise par l'une des couches [mtier,dao], le bean applicationData ne fera qu'avoir une rfrence de plus sur la liste. La liste n'est pas duplique. si cette liste d'employs n'tait mmorise par aucune des couches, alors la liste temporaire cre par la mthode [metier].findAllEmployes reste en mmoire parce que le bean applicationData garde une rfrence dessus. Les lignes 9-17 de [faces-config.xml] dfinissent le bean form (ligne 10) de porte session (ligne 12). Ce bean sera peu diffrent de ce qu'il tait dans les versions prcdentes. C'est lui notamment qui alimente la liste droulante des employs dans le formulaire. Aussi a-t-il toujours besoin de la liste des employs. Celle-ci tant dsormais maintenue dans le bean nomm applicationData (ligne 5), le bean form devra avoir une rfrence sur ce bean. C'est ce qui sera fait dans [Form.java] :
1.package web.beans.session; 2. 3.... 4.public class Form { 5. 6. // autres beans 7. private ApplicationData applicationData; 8. 9. // les vues 10....

Ligne 7, le bean [Form] a une rfrence sur le bean [ApplicationData]. Cette rfrence sera initialise par injection d'une rfrence opre par le conteneur de servlets grce la configuration du bean [Form] faite dans [faces-config.xml] :
1. 2. 3. 4. 5. 6. 7. 8. 9. <managed-bean> <managed-bean-name>applicationData</managed-bean-name> <managed-bean-class>web.beans.application.ApplicationData</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>form</managed-bean-name> <managed-bean-class>web.beans.session.Form</managed-bean-class> <managed-bean-scope>session</managed-bean-scope>

http://tahe.developpez.com/java/javaee

154/341

10. <managed-property> 11. <property-name>applicationData</property-name> 12. <value>#{applicationData}</value> 13. </managed-property> 14.</managed-bean>

lignes 10-13 : initialisation d'une proprit du bean [Form] ligne 11 : nom de la proprit sera accde via getApplicationData et setApplicationData ligne 12 : valeur injecter dans la proprit la notation #{applicationData} dsigne le bean dfini ligne 2.

17.2.2

La feuille de style

Le fichier [styles.css] dfinit quelques styles supplmentaires pour la vue [VueSimulations] et la vue [VueErreur] :
1. .simulationsHeader { 2. text-align: center; 3. font-style: italic; 4. color: Snow; 5. background: Teal; 6. } 7. 8. .simuNum { 9. height: 25px; 10. text-align: center; 11. background: MediumTurquoise; 12. } 13. .simuNom { 14. text-align: left; 15. background: PowderBlue; 16. } 17. .simuPrenom { 18. width: 6em; 19. text-align: left; 20. color: Black; 21. background: MediumTurquoise; 22. } 23. .simuHT { 24. width: 3em; 25. text-align: center; 26. color: Black; 27. background: PowderBlue; 28. } 29. .simuJT { 30. width: 3em; 31. text-align: center; 32. color: Black; 33. background: MediumTurquoise; 34. } 35. .simuSalaireBase { 36. width: 9em; 37. text-align: center; 38. color: Black; 39. background: PowderBlue; 40. } 41. .simuIndemnites { 42. width: 3em; 43. text-align: center; 44. color: Black; 45. background: MediumTurquoise; 46. } 47. .simuCotisationsSociales { 48. width: 6em; 49. text-align: center; 50. background: PowderBlue; 51. } 52. 53. .simuSalaireNet { 54. width: 10em; 55. text-align: center; 56. background: MediumTurquoise; 57. } 58. 59. .erreursHeaders { 60. background: Teal; 61. background-color: #ff6633; 62. color: Snow; 63. font-style: italic; 64. text-align: center

http://tahe.developpez.com/java/javaee

155/341

65. 66. } 67. 68. .erreurClasse { 69. background: MediumTurquoise; 70. background-color: #ffcc66; 71. height: 25px; 72. text-align: center 73. } 74. 75. .erreurMessage { 76. background: PowderBlue; 77. background-color: #ffcc99; 78. text-align: left 79. }

Voici des exemples de code Jsp utilisant ces styles : Vue Simulations
<h:dataTable value="#{form.simulations}" var="simulation" headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSoc iales,simuSalaireNet">

Le rsultat obtenu est le suivant :

Vue Erreur
<h:dataTable value="#{form.erreurs}" var="erreur" headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">

17.2.3

Le fichier des messages

Le fichier des messages [messages_fr.properties] s'enrichit de nouveaux messages pour les vues [VueSimulations] et [VueErreur] :
1. simulations.headers.nom=Nom 2. simulations.headers.prenom=Prnom 3. simulations.headers.heuresTravaillees=Heures travailles 4. simulations.headers.joursTravailles=Jours Travaills 5. simulations.headers.salaireBase=Salaire de base 6. simulations.headers.indemnites=Indemnits 7. simulations.headers.cotisationsSociales=Cotisations sociales 8. simulations.headers.salaireNet=SalaireNet 9. simulations.headers.numero=N 10. erreur.titre=Une erreur s'est produite. 11. erreur.exceptions=Chane des exceptions 12. exception.type=Type de l'exception 13. exception.message=Message associ

17.2.4

La couche [mtier]

http://tahe.developpez.com/java/javaee

156/341

La couche [mtier] simule est modifie de la faon suivante :


1. package metier; 2. 3. ... 4. public class Metier implements IMetierLocal { 5. 6. // liste des employes 7. private Map<String,Employe> hashEmployes=new HashMap<String,Employe>(); 8. private List<Employe> listEmployes; 9. 10. // obtenir la feuille de salaire 11. public FeuilleSalaire calculerFeuilleSalaire(String SS, 12. double nbHeuresTravailles, int nbJoursTravaills) { 13. // on rcupre l'employ 14. Employe e=hashEmployes.get(SS); 15. // on rend une exception si l'employ n'existe pas 16. if(e==null){ 17. throw new PamException(String.format("L'employ de n SS [%s] n'existe pas",SS),1); 18. } 19. // on rend une feuille de salaire fictive 20. return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100)); 21. } 22. 23. // liste des employs 24. public List<Employe> findAllEmployes() { 25. if(listEmployes==null){ 26. // on cre une liste de trois employs 27. listEmployes=new ArrayList<Employe>(); 28. listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15))); 29. listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12))); 30. // dictionnaire des employes 31. for(Employe e:listEmployes){ 32. hashEmployes.put(e.getSS(),e); 33. } 34. // on ajoute un employ qui n'existera pas dans le dictionnaire 35. listEmployes.add(new Employe("X","Y","Z","La brlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12))); 36. } 37. // on rend la liste des employs 38. return listEmployes; 39. } 40. }

ligne 8 : la liste des employs ligne 7 : la mme liste sous forme de dictionnaire index par le n SS des employs lignes 24-39 : la mthode findAllEmployes qui rend la liste des employs. Cette mthode cre une liste en dur et la rfrence par le champ employs de la ligne 8. lignes 27-33 : une liste et un dictionnaire de deux employs sont crs ligne 35 : un employ est ajout la liste employes (ligne 8) mais pas au dictionnaire hashEmployes (ligne 7). Ceci pour qu'il apparaisse dans le combo des employs mais qu'ensuite il ne soit pas reconnu par la mthode calculerFeuilleSalaire (ligne 14) afin que celle-ci lance une exception (ligne 17). lignes 11-21 : la mthode calculerFeuilleSalaire ligne 14 : l'employ est cherch dans le dictionnaire hashEmployes via son n SS. S'il n'est pas trouv, une exception est lance (lignes 16-18). Ainsi aurons-nous une exception pour l'employ de n SS X ajout ligne 35 dans la liste employs mais pas dans le dictionnaire hashEmployes. ligne 20, une feuille de salaire fictive est cre et rendue.

17.3

Le bean [ApplicationData]

Le bean [ApplicationData] a pour but premier d'avoir une rfrence sur : la liste des employs afin qu'elle soit disponible toutes les requtes de tous les navigateurs clients. Ceci est possible parce que la liste est en lecture seule. la couche [mtier]. En effet, dans l'architecture de l'application, celle-ci est partage par toutes les requtes de toutes les applications. Comme elle est implmente par une classe sans tat (stateless, sans champs privs mmmorisant des informations propres un utilisateur donn), elle peut tre partage.

http://tahe.developpez.com/java/javaee

157/341

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couche [metier] simule

Dans le schma ci-dessus, on voit la couche [mtier] qui peut tre partage par toutes les requtes de toutes les applications. La porte du bean est l'aplication elle-mme. Il est cr une fois, puis partag par toutes les requtes de tous les utilisateurs. Son code est le suivant :
1. package web.beans.application; 2. 3. ... 4. public class ApplicationData { 5. 6. public ApplicationData() { 7. } 8. 9. // couche mtier 10. private IMetierLocal metier=new Metier(); 11. // employs 12. private SelectItem[] employesItems; 13. 14. @PostConstruct 15. private void init(){ 16. // la liste des employs est demande la couche mtier 17. List<Employe>employes=metier.findAllEmployes(); 18. // on gnre la liste des lments du combo 19. setEmployesItems(new SelectItem[employes.size()]); 20. for(int i=0;i<employes.size();i++){ 21. getEmployesItems()[i]=new SelectItem(employes.get(i).getSS(),employes.get(i).getPrenom()+" "+employes.get(i).getNom()); 22. } 23. } 24. 25. // getters et setters 26. .... 27. }

ligne 10 : rfrence sur la couche [mtier] simule ligne 12 : le tableau d'lments SelectItem qui sert remplir le combo des employs. En effet, plutt que de mmoriser la liste des employs, il est prfrable de mmoriser le tableau d'lements SelectItem qui alimente le combo des pages [form.jsp] affiches par les diffrents navigateurs clients. ligne 15 : une mthode annote par @PostConstruct. Cette annotation dsigne la mthode qui doit tre excute juste aprs l'excution du constructeur de la classe. Nous utilisons la mthode init pour initialiser le champ employesItems de la ligne 12. ligne 17 : la liste des employs est demande la couche [mtier] lignes 19-22 : le tableau employesItems d'lments de type SelectItem est initialis. Les lments gnrs dans le combo auront pour attribut value, le n SS de l'employ et pour attribut label (ce qui est affich) le prnom et le nom de l'employ. Cela signifie que lorsque la valeur du combo sera poste, c'est un n SS qui sera envoy au serveur, celui de l'employ slectionn.

Une fois le bean ApplicationData cr, le bean [Form] qui a une rfrence nomme applicationData sur ce bean, aura accs la liste des lments du combo via la mthode applicationData.getEmployesItems() la couche [mtier] via la mthode applicationData.getMetier() On se rappellera que le bean ApplicationData est disponible pour toutes les requtes de tous les clients.

http://tahe.developpez.com/java/javaee

158/341

17.4
17.4.1

Implmentation du modle MVC


Le formulaire [form.jsp] et ses vues

Le squelette du formulaire [form.jsp] est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. <script language="javascript"> 11. function raz(){ 12. // on change les valeurs postes 13. document.forms['formulaire'].elements['formulaire:vueSaisies:comboEmployes'].value="0"; 14. document.forms['formulaire'].elements['formulaire:vueSaisies:heuresTravailles'].value="0"; 15. document.forms['formulaire'].elements['formulaire:vueSaisies:joursTravaills'].value="0"; 16. } 17. </script> 18. 19. <f:view> 20. <html> 21. <head> 22. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 23. <title><h:outputText value="#{msg['form.titre']}"/></title> 24. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 25. </head> 26. <body background="<c:url value="/ressources/standard.jpg"/>"> 27. <h:form id="formulaire"> 28. <!-- entete --> 29. ... 30. <!-- saisie --> 31. <f:subview id="vueSaisies" rendered="#{form.vueSaisiesIsRendered}"> 32. ... 33. </f:subview> 34. <!-- simulation --> 35. <f:subview id="vueSimulation" rendered="#{form.vueSimulationIsRendered}"> 36. ... 37. </f:subview> 38. <f:subview id="vueSimulations" rendered="#{form.vueSimulationsIsRendered}"> 39. ... 40. </f:subview> 41. <f:subview id="vueSimulationsVides" rendered="#{form.vueSimulationsVidesIsRendered}"> 42. <h2>Votre liste de simulations est vide.</h2> 43. </f:subview> 44. <!-- vue Erreur --> 45. <f:subview id="vueErreur" rendered="#{form.vueErreurIsRendered}"> 46. ... 47. </f:subview> 48. </h:form> 49. </body> 50. </html> 51. </f:view>

Le formulaire [form.jsp] est compos d'un certain nombre de vues ligne 28 : l'entte prsent dans toutes les vues

lignes 31-33 : la vue [vueSaisies]

http://tahe.developpez.com/java/javaee

159/341

lignes 35-37 : la vue [vueSimulation] (vue partielle ci-dessous)

lignes 38-40 : la vue [vueSimulations]

lignes 41-43 : la vue [vueSimulationsVides]

lignes 45-47 : la vue [vueErreur]

Nous examinerons ces vues les unes aprs les autres en liaison avec le contrleur / modle [Form.java].

17.4.2

Le contrleur / modle [Form.java]

La page JSP [form.jsp] est contrle par la classe [Form.java] suivante :


1. package web.beans.session; 2. 3. ... 4. public class Form { 5. 6. public Form() { 7. } 8. 9. // autres beans 10. private ApplicationData applicationData; 11. 12. // les vues 13. private boolean vueSaisiesIsRendered=true; 14. private boolean vueSimulationIsRendered=false; 15. private boolean vueSimulationsIsRendered=false; 16. private boolean vueSimulationsVidesIsRendered=false; 17. private boolean vueErreurIsRendered=false;

http://tahe.developpez.com/java/javaee

160/341

18. 19. // les menus 20. private boolean menuFaireSimulationIsRendered=true; 21. private boolean menuEffacerSimulationIsRendered=true; 22. private boolean menuEnregistrerSimulationIsRendered=false; 23. private boolean menuVoirSimulationsIsRendered=false; 24. private boolean menuRetourSimulateurIsRendered=false; 25. private boolean menuTerminerSessionIsRendered=true; 26. 27. // le modle des vues 28. private String comboEmployesValue=""; 29. private String heuresTravailles=""; 30. private String joursTravaills=""; 31. private FeuilleSalaire feuilleSalaire; 32. private SelectItem[] employesItems; 33. private Integer numSimulationToDelete; 34. private List<Erreur> erreurs=new ArrayList<Erreur>(); 35. private List<Simulation> simulations=new ArrayList<Simulation>(); 36. 37. // actions du menu 38. public String faireSimulation(){ 39. ... 40. } 41. 42. public String enregistrerSimulation(){ 43. ... 44. } 45. 46. public String effacerSimulation(){ 47. ... 48. } 49. 50. public String voirSimulations(){ 51. ... 52. } 53. 54. public String retourSimulateur(){ 55. ... 56. } 57. public String terminerSession(){ 58. ... 59. } 60. 61. public String retirerSimulation(){ 62. ... 63. } 64. 65. // gestion des vues 66. private void setVues(boolean vueSaisiesIsRendered, boolean vueSimulationIsRendered, boolean vueSimulationsIsRendered, boolean vueSimulationsVidesIsRendered, boolean vueErreurIsRendered){ 67. this.vueSaisiesIsRendered=vueSaisiesIsRendered; 68. this.vueSimulationIsRendered=vueSimulationIsRendered; 69. this.vueSimulationsIsRendered=vueSimulationsIsRendered; 70. this.vueSimulationsVidesIsRendered=vueSimulationsVidesIsRendered; 71. this.vueErreurIsRendered=vueErreurIsRendered; 72. } 73. 74. // gestion des menus 75. private void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered){ 76. this.menuFaireSimulationIsRendered=menuFaireSimulationIsRendered; 77. this.menuEnregistrerSimulationIsRendered=menuEnregistrerSimulationIsRendered; 78. this.menuVoirSimulationsIsRendered=menuVoirSimulationsIsRendered; 79. this.menuEffacerSimulationIsRendered=menuEffacerSimulationIsRendered; 80. this.menuRetourSimulateurIsRendered=menuRetourSimulateurIsRendered; 81. this.menuTerminerSessionIsRendered=menuTerminerSessionIsRendered; 82. } 83. 84. // getters et setters 85. .. 86. }

lignes 13-17 : les champs qui contrlent l'affichage ou non des vues lignes 66-72 : une mthode utilitaire setVues qui permet de contrler les champs prcdents. lignes 20-25 : les champs qui contrlent l'affichage des options du menu lignes 38-63 : les mthodes qui traitent l'vnement clic sur les options du menu lignes 75-82 : une mthode utilitaire setMenu qui permet de contrler l'affichage ou non des 6 options du menu.

http://tahe.developpez.com/java/javaee

161/341

17.4.3

La vue [vueEntete]

La vue [vueEntete] est la suivante :

et son code JSP :


1. 2. 3. 4. 5. 6. 7. 8. <!-- entete --> <h:panelGrid columns="2"> <h:panelGroup> <h2><h:outputText value="#{msg['form.titre']}"/></h2> </h:panelGroup> <h:panelGroup> <h:panelGrid columns="1"> <h:commandLink value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{form.menuFaireSimulationIsRendered}"/> 9. <h:commandLink id="cmdEffacerSimulation" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{form.menuEffacerSimulationIsRendered}" onclick="raz()"/> 10. <h:commandLink value="#{msg['form.menu.enregistrerSimulation']}" immediate="true" action="#{form.enregistrerSimulation}" rendered="#{form.menuEnregistrerSimulationIsRendered}"/> 11. <h:commandLink value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" immediate="true" rendered="#{form.menuVoirSimulationsIsRendered}"/> 12. <h:commandLink value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{form.menuRetourSimulateurIsRendered}"/> 13. <h:commandLink value="#{msg['form.menu.terminerSession']}" immediate="true" action="#{form.terminerSession}" rendered="#{form.menuTerminerSessionIsRendered}"/> 14. </h:panelGrid> 15. </h:panelGroup> 16. </h:panelGrid> 17. <hr/>

On notera que : chaque lien peut tre affich ou non via l'attribut rendered contrl par un champ du modle [Form] chaque lien est li une mthode du contrleur secondaire [Form]. le lien [Effacer la simulation] de la ligne 9 est lie la fonction Javascript raz() dont on peut voir le code dans le listing de [form.jsp] page 159. La classe [Form] a les lments suivants lis l'entte :
1. ... 2. public class Form { 3. 4. ... 5. 6. // les menus 7. private boolean menuFaireSimulationIsRendered=true; 8. private boolean menuEffacerSimulationIsRendered=true; 9. private boolean menuEnregistrerSimulationIsRendered=false; 10. private boolean menuVoirSimulationsIsRendered=false; 11. private boolean menuRetourSimulateurIsRendered=false; 12. private boolean menuTerminerSessionIsRendered=true; 13. 14. // actions du menu 15. public String faireSimulation(){ 16. ... 17. } 18. 19. public String enregistrerSimulation(){ 20. ... 21. } 22. 23. public String effacerSimulation(){ 24. ...

http://tahe.developpez.com/java/javaee

162/341

25. } 26. 27. public String voirSimulations(){ 28. ... 29. } 30. 31. public String retourSimulateur(){ 32. ... 33. } 34. public String terminerSession(){ 35. ... 36. } 37. 38. public String retirerSimulation(){ 39. ... 40. } 41. 42. 43. // gestion des menus 44. private void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered){ 45. this.menuFaireSimulationIsRendered=menuFaireSimulationIsRendered; 46. this.menuEnregistrerSimulationIsRendered=menuEnregistrerSimulationIsRendered; 47. this.menuVoirSimulationsIsRendered=menuVoirSimulationsIsRendered; 48. this.menuEffacerSimulationIsRendered=menuEffacerSimulationIsRendered; 49. this.menuRetourSimulateurIsRendered=menuRetourSimulateurIsRendered; 50. this.menuTerminerSessionIsRendered=menuTerminerSessionIsRendered; 51. } 52. 53. // getters et setters 54. .. 55. }

lignes 7-12 : les champs qui contrlent l'affichage des options du menu lignes 15-40 : les mthodes qui traitent l'vnement clic sur les options du menu lignes 44-51 : une mthode utilitaire setMenu qui permet de contrler l'affichage ou non des 6 options du menu.

17.4.4

La vue [vueSaisies]

La vue [vueSaisies] est la suivante :

en [1], l'entte, en [2], la vue [vueSaisies]

Son code JSP :


1. <!-- saisie --> 2. <f:subview id="vueSaisies" rendered="#{form.vueSaisiesIsRendered}"> 3. <h:panelGrid columns="3"> 4. <!-- ligne 1 --> 5. <h:outputText value="#{msg['form.comboEmployes.libell']}"/> 6. <h:outputText value="#{msg['form.heuresTravailles.libell']}"/> 7. <h:outputText value="#{msg['form.joursTravaills.libell']}"/> 8. <!-- ligne 2 --> 9. <h:selectOneMenu id="comboEmployes" value="#{form.comboEmployesValue}"> 10. <f:selectItems ..."/> 11. </h:selectOneMenu> 12. <h:inputText id="heuresTravailles" value="#{form.heuresTravailles}" required="true" requiredMessage="#{msg['form.heuresTravailles.required']}" validatorMessage="#{msg['form.heuresTravailles.validation']}">

http://tahe.developpez.com/java/javaee

163/341

13. 14. 15.

<f:validateDoubleRange minimum="0" maximum="300"/> </h:inputText> <h:inputText id="joursTravaills" value="#{form.joursTravaills}" required="true" requiredMessage="#{msg['form.joursTravaills.required']}" validatorMessage="#{msg['form.joursTravaills.validation']}"> 16. <f:validateLongRange minimum="0" maximum="31"/> 17. </h:inputText> 18. <!-- ligne 3 --> 19. <h:panelGroup></h:panelGroup> 20. <h:message for="heuresTravailles" styleClass="error"/> 21. <h:message for="joursTravaills" styleClass="error"/> 22. </h:panelGrid> 23. <hr/> 24. </f:subview>

Question : complter la ligne 10. La liste des lments du combo des employs est fournie par une mthode d'un des beans de l'application. Indiquer lequel et crire la mthode. Celle-ci devra fournir un tableau d'lments de type SelectItem dont la proprit value aura pour valeur le n SS d'un employ et la proprit label une chane forme du prnom et du nom de celui-ci.

Question : comment doit tre initialis le modle [Form.java] pour que lors de la requte GET initiale faite au formulaire, la page ci-dessus soit envoye au navigateur client ?

17.5
17.5.1

Les actions du contrleur


L'action [faireSimulation]

Le code JSP de l'action [faireSimulation] est le suivant :


<h:commandLink value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{form.menuFaireSimulationIsRendered}"/>

L'action [faireSimulation] calcule une feuille de salaire. Elle provoque les changes client / serveur suivants :

A partir de la page prcdente, on obtient le rsultat suivant :

http://tahe.developpez.com/java/javaee

164/341

Question : crire la mthode [faireSimulation] de la classe [Form]. On reprendra le code de la version prcdente en prcisant ce qui change.

17.5.2

L'action [effacerSimulation]

L'action [effacerSimulation] permet l'utilisateur de retrouver un formulaire vide :

Le code JSP du lien [effacerSimulation] est le suivant :


1. .... 2. <script language="javascript"> 3. function raz(){ 4. // on change les valeurs postes

http://tahe.developpez.com/java/javaee

165/341

5. document.forms['formulaire'].elements['formulaire:vueSaisies:comboEmployes'].value="0"; 6. document.forms['formulaire'].elements['formulaire:vueSaisies:heuresTravailles'].value="0"; 7. document.forms['formulaire'].elements['formulaire:vueSaisies:joursTravaills'].value="0"; 8. } 9. </script> 10. 11. <f:view> 12. <html> 13. <head> 14. ... 15. </head> 16. <body background="<c:url value="/ressources/standard.jpg"/>"> 17. <h:form id="formulaire"> 18. <!-- entete --> 19. ... 20. <h:panelGrid columns="1"> 21. ... 22. <h:commandLink id="cmdEffacerSimulation" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{form.menuEffacerSimulationIsRendered}" onclick="raz()"/> 23. .... 24. </h:panelGrid> 25. </h:panelGroup> 26. </h:panelGrid> 27. ...

Ligne 22, le code du lien [EffacerSimulation]. Un clic dessus provoque d'abord l'appel de la fonction Javascript raz() (ligne 22). Cette mthode dfinie lignes 3-8 change les valeurs postes. On notera que les valeurs postes sont des valeurs valides, c.a.d. qu'elles passeront les tests de validation des champs de saisie heuresTravailles et joursTravaills. la fonction raz ne poste pas le formulaire. En effet, celui-ci va tre post par le lien cmdEffacerSimulation de la ligne 22. Ce post se fera aprs excution de la fonction Javascript raz. Au cours du post, les valeurs postes vont suivre un cheminement normal : validation puis affectation aux champs du modle. Ceuxci sont les suivants dans la classe [Form] :
// le modle des vues private String comboEmployesValue; private String heuresTravailles; private String joursTravaills; ...

Ces trois champs vont recevoir les trois valeurs postes {"0","0","0"}. Une fois cette affectation opre, la mthode effacerSimulation va tre excute. Question : crire la mthode [effacerSimulation] de la classe [Form]. On fera en sorte que : - seule la zone des saisies soit affiche - le combo soit positionn sur son 1er lment - les zones de saisie heuresTravailles et joursTravaills affichent des chanes vides

17.5.3

L'action [enregistrerSimulation]

Le code JSP du lien [enregistrerSimulation] est le suivant :


<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{form.menuEnregistrerSimulationIsRendered}"/>

L'action [enregistrerSimulation] associe au lien permet d'enregistrer la simulation courante dans une liste de simulations maintenue dans la classe [Form] :
1. // le modle des vues 2.... 3.private List<Simulation> simulations=new ArrayList<Simulation>();

La classe Simulation est la suivante :


1. package web.entities; 2. 3. import metier.FeuilleSalaire;

http://tahe.developpez.com/java/javaee

166/341

4. 5. public class Simulation { 6. 7. public Simulation() { 8. } 9. 10. // champs d'une simulation 11. private Integer num; 12. private FeuilleSalaire feuilleSalaire; 13. private String heuresTravailles; 14. private String joursTravaills; 15. 16. // constructeur 17. public Simulation(Integer num,String heuresTravailles, String joursTravaills, FeuilleSalaire feuilleSalaire){ 18. this.setNum(num); 19. this.setFeuilleSalaire(feuilleSalaire); 20. this.setHeuresTravailles(heuresTravailles); 21. this.setJoursTravaills(joursTravaills); 22. } 23. 24. public double getIndemnites(){ 25. return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas(); 26. } 27. 28. // getters et setters 29. ... 30. }

Cette classe permet de mmoriser une simulation faite par l'utilisateur : ligne 11 : le n de la simulation ligne 12 : la feuille de salaire qui a t calcule ligne 13 : le nombre d'heures travailles ligne 14 : le nombre de jours travaills Voici un exemple d'enregistrement :

A partir de la page prcdente, on obtient le rsultat qui suit :

http://tahe.developpez.com/java/javaee

167/341

Le n de la simulation est un nombre incrment chaque nouvel enregistrement. La logique voudrait que ce n soit maintenu dans la classe [Form] avec la liste des simulations :
1. // simulations 2. private List<Simulation> simulations=new ArrayList<Simulation>(); 3.private int numDerniereSimulation=0;

Le champ numDerniereSimulation serait incrment chaque nouvel enregistrement. Notons que nous ne pouvons pas utiliser la taille de la liste des simulations pour numroter les simulations successives puisque l'utilisateur peut retirer des simulations de la liste (cf ci-dessous). Ainsi, s'il retire la simulation n 2 d'une liste de trois simulations, nous aurons le tableau suivant :

Le n de la prochaine simulation doit tre 4 mme si la liste n'a que deux simulations. Comme la classe [Form] est de porte session, le champ numDerniereSimulation serait correctement conserv au fil des requtes. Comme exercice, nous stockerons le n de la dernire simulation faite, non pas comme un champ de la classe [Form] mais comme attribut de la session de l'utilisateur. Cela nous permettra de dcouvrir comment dans un bean JSF on peut avoir accs la session de l'utilisateur. Cela peut tre utile si l'application web mlange servlets, pages JSP et JSF qui peuvent alors utiliser la session comme mmoire commune. Dans une mthode de la classe [Form], la session de l'utilisateur est accessible de la faon suivante :
1. 2. // on rcupre la requte courante HttpServletRequest request=(HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest(); 3. // on rcupre dans la session le n de la dernire simulation 4. Integer numDerniereSimulation=(Integer) request.getSession().getAttribute("numDerniereSimulation");

ligne 2 : rcupre l'objet HttpServletRequest request qui encapsule la requte faite par le navigateur client ligne 4 : la session de l'utilisateur est rcupre par request.getSession().

On peut stocker des informations dans une session via ses attributs. On dispose pour cela de deux mthodes :

[session].setAttribute(String cl, Object objet) : pour stocker objet associ cl. [session].getAttribute(String cl) : pour rcuprer l'objet associ cl. On rcupre la valeur null, si la cl n'existe pas dans les attributs de la session.

Dans l'exemple ci-dessus, le n de la dernire simulation faite est associe la cl numDerniereSimulation. La mthode [enregistrerSimulation] peut procder ainsi :

1. 2.

rcuprer le n de la dernire simulation dans la session. Si ce n n'existe pas, l'y mettre avec une valeur 0. incrmenter le n le remettre dans la session (si besoin est) ajouter la nouvelle simulation la liste des simulations maintenue par la classe [Form] :
// simulations private List<Simulation> simulations;

faire afficher le tableau des simulations :

http://tahe.developpez.com/java/javaee

168/341

Le tableau des simulations peut tre affich avec une balise <h:dataTable> :
1. <f:subview id="vueSimulations" rendered="#{form.vueSimulationsIsRendered}"> 2. <!-- tableau des simulations --> 3. <h:dataTable value="#{form.simulations}" var="simulation" 4. headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisa tionsSociales,simuSalaireNet"> 5. <h:column> 6. <f:facet name="header"> 7. <h:outputText value="#{msg['simulations.headers.numero']}"/> 8. </f:facet> 9. <h:outputText value="#{simulation.num}"/> 10. </h:column> 11. <h:column> 12. <f:facet name="header"> 13. <h:outputText value="#{msg['simulations.headers.nom']}"/> 14. </f:facet> 15. <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/> 16. </h:column> 17. <h:column> 18. <f:facet name="header"> 19. <h:outputText value="#{msg['simulations.headers.prenom']}"/> 20. </f:facet> 21. <h:outputText value="#{simulation.feuilleSalaire.employe.prenom}"/> 22. </h:column> 23. <h:column> 24. <f:facet name="header"> 25. <h:outputText value="#{msg['simulations.headers.heuresTravaillees']}"/> 26. </f:facet> 27. <h:outputText value="#{simulation.heuresTravailles}"/> 28. </h:column> 29. <h:column> 30. <f:facet name="header"> 31. <h:outputText value="#{msg['simulations.headers.joursTravailles']}"/> 32. </f:facet> 33. <h:outputText value="#{simulation.joursTravaills}"/> 34. </h:column> 35. <h:column> 36. <f:facet name="header"> 37. <h:outputText value="#{msg['simulations.headers.salaireBase']}"/> 38. </f:facet> 39. <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireBase}"/> 40. </h:column> 41. <h:column> 42. <f:facet name="header"> 43. <h:outputText value="#{msg['simulations.headers.indemnites']}"/> 44. </f:facet> 45. <h:outputText value="#{simulation.indemnites}"/> 46. </h:column> 47. <h:column> 48. <f:facet name="header"> 49. <h:outputText value="#{msg['simulations.headers.cotisationsSociales']}"/> 50. </f:facet> 51. <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.cotisationsSociales}"/> 52. </h:column> 53. <h:column> 54. <f:facet name="header"> 55. <h:outputText value="#{msg['simulations.headers.salaireNet']}"/> 56. </f:facet> 57. <h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireNet}"/> 58. </h:column> 59. <h:column> 60. <h:commandLink value="Retirer" action="#{form.retirerSimulation}">

http://tahe.developpez.com/java/javaee

169/341

61.

<f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/> 62. </h:commandLink> 63. </h:column> 64. </h:dataTable> 65. </f:subview>

ligne 3, la balise <h:dataTable> utilise le champ #{form.simulations} comme source de donnes, c.a.d. le champ suivant :
1. // simulations 2.private List<Simulation> simulations;

- l'attribut var="simulation" fixe le nom de la variable reprsentant la simulation courante l'intrieur de la balise <h:datatable> - l'attribut headerClass="simulationsHeaders" fixe le style des titres des colonnes du tableau. - l'attribut columnClasses="...." fixe le style de chacune des colonnes du tableau Examinons l'une des colonnes du tableau et voyons comment elle est construite :

Le code JSP de la colonne Nom est le suivant :


1. <h:column> 2. <f:facet name="header"> 3. <h:outputText value="#{msg['simulations.headers.nom']}"/> 4. </f:facet> 5. <h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/> 6. </h:column>

lignes 2-4 : la balise <f:facet name="header"> dfinit le titre de la colonne ligne 5 : le nom de l'employ est crit : simulation fait rfrence l'attribut var de la balise <h:dataTable ...> : <h:dataTable value="#{form.simulations}" var="simulation" ...>

simulation dsigne la simulation courante de la liste des simulations : d'abord la 1re, puis la 2me, ... simulation.feuilleSalaire fait rfrence au champ feuilleSalaire de la simulation courante simulation.feuilleSalaire.employe fait rfrence au champ employe du champ feuilleSalaire simulation.feuilleSalaire.employe.nom fait rfrence au champ nom du champ employe

La mme technique est rpte pour toutes les colonnes du tableau. Il y a une difficult pour la colonne Indemnits qui est gnre avec le code suivant :
1. <h:column> 2. <f:facet name="header"> 3. <h:outputText value="#{msg['simulations.headers.indemnites']}"/> 4. </f:facet> 5. <h:outputText value="#{simulation.indemnites}"/> 6. </h:column>

Ligne 5, on affiche la valeur de simulation.indemnites. Or la classe Simulation n'a pas de champ indemnites. Il faut se rappeler ici que le champ indemnites n'est pas utilis directement mais via la mthode simulation.getIndemnites(). Il suffit donc que cette mthode existe. Le champ indemnites peut lui ne pas exister. La mthode getIndemnites doit rendre le total des indemnits de l'employ. Cela ncessite un calcul intermdiaire car ce total n'est pas disponible directement dans la feuille de salaire. La mtode getIndemnites est donne page 166.

http://tahe.developpez.com/java/javaee

170/341

Question : crire la mthode [enregistrerSimulation] de la classe [Form].

17.5.4

L'action [retourSimulateur]

Le code JSP du lien [retourSimulateur] est le suivant :


<h:commandLink id="cmdRetourSimulateur" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{form.menuRetourSimulateurIsRendered}"/>

L'action [retourSimulateur] associe au lien permet l'utilisateur de revenir de la vue [vueSimulations] la vue [vueSaisies] :

Le rsultat obtenu :

Question : crire la mthode [retourSimulateur] de la classe [Form]. Le formulaire de saisie prsent doit tre vide comme cidessus.

17.5.5

L'action [voirSimulations]

Le code JSP du lien [voirSimulations] est le suivant :


<h:commandLink id="cmdVoirSimulations" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" immediate="true" rendered="#{form.menuVoirSimulationsIsRendered}"/>

L'action [voirSimulations] associe au lien permet l'utilisateur d'avoir le tableau des simulations, ceci quelque soit l'tat de ses saisies :

http://tahe.developpez.com/java/javaee

171/341

Le rsultat obtenu :

Question : crire la mthode [voirSimulations] de la classe [Form]. On fera en sorte que si la liste des simulations est vide, la vue affiche soit [vueSimulationsVides] :

17.5.6

L'action [retirerSimulation]

L'utilisateur peut retirer des simulations de sa liste :

Le rsultat obtenu est le suivant :

http://tahe.developpez.com/java/javaee

172/341

Si ci-dessus, on retire la dernire simulation, on obtiendra le rsultat suivant :

Le code JSP de la colonne [Retirer] du tableau des simulations est le suivant :


1. 2. 3. 4. <f:subview id="vueSimulations" rendered="#{form.vueSimulationsIsRendered}"> <!-- tableau des simulations --> <h:dataTable value="#{form.simulations}" var="simulation" headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisa tionsSociales,simuSalaireNet"> 5. ... 6. <h:column> 7. <h:commandLink value="Retirer" action="#{form.retirerSimulation}"> 8. <f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/> 9. </h:commandLink> 10. </h:column> 11. </h:dataTable> 12. </f:subview>

ligne 7 : le lien [Retirer] est associ la mthode [retirerSimulation] de la classe [Form]. Cette mthode a besoin de connatre le n de la simulation retirer. Celui-ci lui est fourni par la balise <f:setPropertyActionListener> de la ligne 8. Cette balise a deux attributs target et value : l'attribut target dsigne un champ du modle auquel la valeur de l'attribut value sera affecte. Ici le n de la simulation retirer #{simulation.num} sera affecte au champ numSimulationToDelete de la classe [Form] :
1. 2. 3. // le modle des vues ... private Integer numSimulationToDelete;

Lorsque la mthode [retirerSimulation] de la classe [Form] s'excutera, elle pourra utiliser la valeur qui aura t stocke auparavant dans le champ numSimulationToDelete. Question : crire la mthode [retirerSimulation] de la classe [Form].

17.5.7

L'action [terminerSession]

Le code JSP du lien [Terminer la session] est le suivant :


1. <h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}" rendered="#{form.menuTerminerSessionIsRendered}"/>

L'action [terminerSession] associe au lien permet l'utilisateur d'abandonner sa session et de revenir au formulaire de saisies vide :

http://tahe.developpez.com/java/javaee

173/341

Si l'utilisateur avait une liste de simulations, celle-ci est vide. Par ailleurs, la numrotation des simulations repart de 1. Question : crire la mthode [terminerSession] de la classe [Form].

17.6

La vue [vueErreur]

On veut pouvoir grer proprement les exceptions qui peuvent survenir lors du calcul d'une simulation. Pour cela le code de la mthode [faireSimulation] utilisera un try / catch :
1. // action du menu 2. public String faireSimulation(){ 3. try{ 4. // on calcule la feuille de salaire 5. feuilleSalaire= ... 6. // on affiche la simulation 7. ... 8. // on met jour le menu 9. ... 10. }catch(Throwable th){ 11. // on vide la liste des erreurs prcdentes 12. ... 13. // on cre la nouvelle liste des erreurs 14. ... 15. // on affiche la vue vueErreur 16. ... 17. // on met jour le menu 18. ... 19. } 20. // on rend la mme page 21. return null; 22.}

La liste des erreurs cre ligne 14 est la suivante :


1. // le modle des vues 2. ... 3. private List<Erreur> erreurs=new ArrayList<Erreur>(); 4....

La classe Erreur est dfinie comme suit :


1. package web.entities; 2. 3. public class Erreur {

http://tahe.developpez.com/java/javaee

174/341

4. 5. public Erreur() { 6. } 7. 8. // champ 9. private String classe; 10. private String message; 11. 12. // constructeur 13. public Erreur(String classe, String message){ 14. this.setClasse(classe); 15. this.message=message; 16. } 17. 18. // getters et setters 19. ... 20. }

Les erreurs seront des exceptions dont on mmorise le nom de la classe dans le champ classe et le message dans le champ message. La liste des erreurs construite dans la mthode [faireSimulation] est constitue de : l'exception initiale th de type Throwable qui s'est produite de sa cause th.getCause() si elle en a une de la cause de la cause h.getCause().getCause() si elle existe ... Voici un exemple de liste d'erreurs :

Ci-dessus, l'employ [Z Y] n'existe pas dans le dictionnaire des employs utilis par la couche [mtier] simule. Dans ce cas, la couche [mtier] simule lance une exception :
1. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills) { 2. // on rcupre l'employ 3. Employe e=hashEmployes.get(SS); 4. // on rend une exception si l'employ n'existe pas 5. if(e==null){ 6. throw new PamException(String.format("L'employ de n SS [%s] n'existe pas",SS),1); 7. } 8.... 9.}

Le rsultat obtenu est le suivant :

Pour obtenir cette page, le code JSP suivant a t utilis :

http://tahe.developpez.com/java/javaee

175/341

1. <!-- vue Erreur --> 2. <f:subview id="vueErreur" rendered="#{form.vueErreurIsRendered}"> 3. <h3><h:outputText value="#{msg['erreur.titre']}"/></h3> 4. <h:dataTable value="#{form.erreurs}" var="erreur" 5. headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage"> 6. <f:facet name="header"> 7. <h:outputText value="#{msg['erreur.exceptions']}"/> 8. </f:facet> 9. <h:column> 10. <f:facet name="header"> 11. <h:outputText value="#{msg['exception.type']}"/> 12. </f:facet> 13. <h:outputText value="#{erreur.classe}"/> 14. </h:column> 15. <h:column> 16. <f:facet name="header"> 17. <h:outputText value="#{msg['exception.message']}"/> 18. </f:facet> 19. <h:outputText value="#{erreur.message}"/> 20. </h:column> 21. </h:dataTable> 22. </f:subview>

Question : complter la mthode [faireSimulation] afin que lors d'une exception, elle fasse afficher la vue [vueErreur].

17.7

Intgration de la couche web dans une architecture 3 couches Jsf / Ejb

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couche [metier] simule

Nous remplaons la couche [mtier] simule, par les couches [mtier, dao, jpa] implmentes par des Ejb au paragraphe 13.1, page 90 :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couches [metier, dao, jpa]

Travail pratique : raliser l'intgration des couches Jsf et Ejb en suivant la mthodologie du paragraphe 16, page 143.

http://tahe.developpez.com/java/javaee

176/341

18 Version 8 - Application web PAM multi-vues / mono-page - 2


Nous reprenons la mme architecture que prcdemment : 1 page JSP, 1 modle / contrleur pour cette page, des vues toutes issues de la page JSP.

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couche [metier] simule

Pour la demande GET initiale : [Faces Servlet] reoit une demande GET d'affichage de la page [form.jsp] en [1] il fait afficher celle-ci en [3]. La page utilise son modle / contrleur [Form.java] pour initialiser ses parties dynamiques. En [4], une vue Vi parmi d'autres est affiche, les autres restant caches. Pour les demandes POST qui suivent : [Faces Servlet] reoit une demande POST pour la page [form.jsp] en [1] l'vnement qui a provoqu le POST est trait par l'une des mthodes du modle / contrleur [Form.java] [2a, 2b]. Celleci met jour le modle M afin qu'une vue Vi particulire soit affiche et les autres caches. La mthode rend comme cl de navigation la cl null. [Faces Servlet] ayant reu la cl null, fait afficher de nouveau [form.jsp] en [3]. La page utilise son modle [Form.java] pour initialiser ses parties dynamiques. En [4], la vue Vi est affiche, les autres restant caches. Le projet Netbeans prcdent avait l'allure suivante :

4 5 2 3

en [1], les fichiers de configuration en [2], les pages Jsp et la feuille de style en [3], les classes de la couche [web] en [4], le fichier des messages pour l'internationalisation de l'application en [5], les objets changs entre la couche [web] et la couche [mtier] et la couche [mtier] elle-mme en [6], les bibliothques de l'application

http://tahe.developpez.com/java/javaee

177/341

La nouvelle version amne des modifications uniquement dans le paquetage [web.beans] [3] qui regroupe les beans de l'application JSF. Les deux beans ApplicationData et Form de [3] ci-dessus taient dfinis de la faon suivante dans le fichier [faces-config.xml] :
33. <?xml version="1.0" encoding="UTF-8"?> 34. <!-- =========== FULL CONFIGURATION FILE ================================== --> 35. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 36. <managed-bean> 37. <managed-bean-name>applicationData</managed-bean-name> 38. <managed-bean-class>web.beans.application.ApplicationData</managed-bean-class> 39. <managed-bean-scope>application</managed-bean-scope> 40. </managed-bean> 41. <managed-bean> 42. <managed-bean-name>form</managed-bean-name> 43. <managed-bean-class>web.beans.session.Form</managed-bean-class> 44. <managed-bean-scope>session</managed-bean-scope> 45. <managed-property> 46. <property-name>applicationData</property-name> 47. <value>#{applicationData}</value> 48. </managed-property> 49. </managed-bean> 50. ... 51. </faces-config>

lignes 36-40 : dfinition du bean applicationData (ligne 37) de porte application (ligne 39) lignes 41-49 : dfinition du bean form (ligne 42) de porte session (ligne 44)

Le bean [Form] dfini ligne 43 tait le suivant :


1. package web.beans.session; 2. 3. ... 4. public class Form { 5. 6. public Form() { 7. } 8. 9. // autres beans 10. private ApplicationData applicationData; 11. 12. // les vues 13. ... 14. 15. // les menus 16. ... 17. 18. // le modle des vues 19. private String comboEmployesValue=""; 20. private String heuresTravailles=""; 21. private String joursTravaills=""; 22. private FeuilleSalaire feuilleSalaire; 23. private SelectItem[] employesItems; 24. private Integer numSimulationToDelete; 25. private List<Erreur> erreurs=new ArrayList<Erreur>(); 26. private List<Simulation> simulations=new ArrayList<Simulation>(); 27. ..

La classe [Form] maintenait au fil des requtes d'un utilisateur donn, la liste de simulations de celui-ci (ligne 26). Pour cette raison, le bean [Form] devait rester dans la session de l'utilisateur et c'est pourquoi il a la porte session dans [faces-config.xml]. Dans cette version, nous allons isoler tout ce qui doit rester dans la session dans un bean appel sessionData et allons passer la porte du bean form de session request.

18.1

Le projet Netbeans

Le projet Netbeans de cette version est obtenu d'abord par recopie du projet prcdent puis par modification de certains de ces lments :

http://tahe.developpez.com/java/javaee

178/341

4 5 2 3

5 1 3 2 6

en [1], les fichiers de configuration en [2], les pages Jsp et la feuille de style en [3], les classes de la couche [web]. Le changement se produit ici : la classe [Form] qui tait auparavant dans le paquetage [web.beans.session] passe dans le paquetage [web.beans.request] pour reflter le fait que sa porte passe de session request. la classe [SessionData] qui va nous servir stocker les informations de porte session apparat dans le paquetage [web.beans.session]. en [4], le fichier des messages pour l'internationalisation de l'application en [5], les objets changs entre la couche [web] et la couche [mtier] et la couche [mtier] elle-mme en [6], les bibliothques de l'application

18.2

Le fichier [faces-config.xml]

Le fichier [faces-config.xml] volue de la faon suivante :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <managed-bean> 5. <managed-bean-name>form</managed-bean-name> 6. <managed-bean-class>web.beans.request.Form</managed-bean-class>

http://tahe.developpez.com/java/javaee

179/341

7. <managed-bean-scope>request</managed-bean-scope> 8. <managed-property> 9. <property-name>applicationData</property-name> 10. <value>#{applicationData}</value> 11. </managed-property> 12. <managed-property> 13. <property-name>sessionData</property-name> 14. <value>#{sessionData}</value> 15. </managed-property> 16. </managed-bean> 17. <managed-bean> 18. <managed-bean-name>locale</managed-bean-name> 19. <managed-bean-class>web.utils.ChangeLocale</managed-bean-class> 20. <managed-bean-scope>application</managed-bean-scope> 21. </managed-bean> 22. <managed-bean> 23. <managed-bean-name>applicationData</managed-bean-name> 24. <managed-bean-class>web.beans.application.ApplicationData</managed-bean-class> 25. <managed-bean-scope>application</managed-bean-scope> 26. </managed-bean> 27. <managed-bean> 28. <managed-bean-name>sessionData</managed-bean-name> 29. <managed-bean-class>web.beans.session.SessionData</managed-bean-class> 30. <managed-bean-scope>session</managed-bean-scope> 31. </managed-bean> 32. <application> 33. <resource-bundle> 34. <base-name> 35. messages 36. </base-name> 37. <var>msg</var> 38. </resource-bundle> 39. <message-bundle>messages</message-bundle> 40. </application> 41. </faces-config>

lignes 22-26 : le bean applicationData de porte application lignes 27-31 : le bean sessionData de porte session lignes 4-16 : le bean form de porte request. Ce bean a une rfrence sur le bean applicationData (lignes 8-11) et sur le bean sessionData (lignes 12-15).

18.3

Le bean SessionData

Le bean SessionData mmorise toutes les informations qui doivent tre maintenues dans la session de l'utilisateur. Sa classe est la suivante :
1. package web.beans.session; 2. 3. import java.util.ArrayList; 4. import java.util.List; 5. import web.entities.Simulation; 6. 7. public class SessionData { 8. 9. public SessionData() { 10. } 11. 12. // simulations 13. private List<Simulation> simulations=new ArrayList<Simulation>(); 14. private int numDerniereSimulation=0; 15. private Simulation simulation; 16. 17. // les vues 18. private boolean vueSaisiesIsRendered=true; 19. private boolean vueSimulationIsRendered; 20. private boolean vueSimulationsIsRendered; 21. private boolean vueSimulationsVidesIsRendered; 22. private boolean vueErreurIsRendered; 23. 24. // menus 25. private boolean menuFaireSimulationIsRendered=true; 26. private boolean menuEffacerSimulationIsRendered=true; 27. private boolean menuEnregistrerSimulationIsRendered; 28. private boolean menuVoirSimulationsIsRendered; 29. private boolean menuRetourSimulateurIsRendered; 30. private boolean menuTerminerSessionIsRendered=true;

http://tahe.developpez.com/java/javaee

180/341

31. 32. 33.

// gestion des vues public void setVues(boolean vueSaisiesIsRendered, boolean vueSimulationIsRendered, boolean vueSimulationsIsRendered, boolean vueSimulationsVidesIsRendered, boolean vueErreurIsRendered){ 34. // tat des vues 35. this.setVueSaisiesIsRendered(vueSaisiesIsRendered); 36. this.setVueSimulationIsRendered(vueSimulationIsRendered); 37. this.setVueSimulationsIsRendered(vueSimulationsIsRendered); 38. this.setVueSimulationsVidesIsRendered(vueSimulationsVidesIsRendered); 39. this.setVueErreurIsRendered(vueErreurIsRendered); 40. } 41. // gestion des menus 42. public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered){ 43. this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered); 44. this.setMenuEnregistrerSimulationIsRendered(menuEnregistrerSimulationIsRendered); 45. this.setMenuVoirSimulationsIsRendered(menuVoirSimulationsIsRendered); 46. this.setMenuEffacerSimulationIsRendered(menuEffacerSimulationIsRendered); 47. this.setMenuRetourSimulateurIsRendered(menuRetourSimulateurIsRendered); 48. this.setMenuTerminerSessionIsRendered(menuTerminerSessionIsRendered); 49. } 50. 51. // getters et setters 52. ... 53. }

Si la prsence des champs simulations, numDerniereSimulation, simulation (lignes 13-15) dans la session se comprend, celle des champs vues (lignes 18-22) et menu (lignes 25-30) est plus difficile expliquer. Tout d'abord, on constate que si on laisse ces options de menu dans le bean Form et que celui est de porte request, alors l'application ne fonctionne pas. Si on change la porte du bean Form de request session, alors l'application fonctionne de nouveau. C'est le point de dpart de notre rflexion. Essayons d'imaginer ce qui peut se passer. Lorsque le formulaire est envoy la premire fois, l'utilisateur reoit la page suivante :

L'utilisateur fait une simulation (vue partielle) :

Si le bean Form est de porte request et qu'il contient les vues et options de menu, l'utilisation du bouton [Enregistrer la simulation] donne le rsultat suivant :

http://tahe.developpez.com/java/javaee

181/341

On est ramens la page initiale tout en gardant les valeurs saisies. Tout semble indiquer que la procdure [Form].enregistrerSimulation n'est pas excute. Cela peut tre confirm en insrant des logs dans le code. La question est donc de savoir pourquoi elle n'est pas excute. Le cycle de traitement du post JSF provoqu par le clic [Enregistrer la simulation] est le cycle standard suivant : A B C

en [A] l'arbre des composants de la page envoye lors du cycle prcdent est reconstitu. Cet arbre tait celui de la simulation o : les vues [vueSaisie, vueSimulation] taient visibles, les autres pas les options de menu [Effacer la simulation, Enregistrer la simulation, Terminer la session] taient visibles, les autres pas. On sait que la page embarque un champ cach nomm [javax.faces.ViewState] dont la valeur est une chane de caractres code reprsentant l'arbre des composants envoy au navigateur client. C'est le champ [javax.faces.ViewState] qui post lui aussi par le navigateur lors du POST, permet de reconstituer l'arbre des composants en A. Dans notre exemple, le champ [javax.faces.ViewState] code l'tat des vues [vueSaisie, vueSimulation] et des options de menu visibles dans ces vues. Il ne code pas l'tat des vues qui ont l'attribut rendered="false". en [B], les valeurs postes sont affectes l'arbre des composants. Dans cette phase, des conversions peuvent tre faites. Ainsi si une valeur poste est associe un champ de type int, une conversion String -> int a lieu. Une conversion peut chouer. Dans ce cas, une erreur est associe au composant erron mais on passe quand mme la phase [C]. en [C], les valeurs postes qui doivent tre valides le sont. Si l'une des conversions ou des validations a chou, le cycle JSF s'arrte et la page est renvoye telle qu'elle a t poste. en [D], les valeurs postes sont affectes aux champs du modle auxquels elles sont associes. en [E], la mthode associe l'vnement qui a provoqu le POST est excute et rend une cl de navigation au contrleur [Faces Servlet] en [F], le contrleur [Faces Servlet] envoie la page correspondant la cl de navigation reue.

Ce que ne dit pas l'explication ci-dessus, c'est quand le bean ou modle mis jour dans la phase [D] est cr. Dans notre cas, le bean [Form] est de porte request. Il est donc recr chaque nouvelle requte par une opration new Form(). A quel moment, on ne le sait donc pas. Toujours est-il que la phase [D] met jour un bean [Form] tout neuf. Cette mise jour a bien lieu comme le montre l'exprience suivante : On change le code JSP pour faire afficher les valeurs du modle sous les trois champs de saisie :
1. <h:panelGrid columns="3"> 2. <!-- ligne 1 --> 3. <h:outputText value="#{msg['form.comboEmployes.libell']}"/> 4. <h:outputText value="#{msg['form.heuresTravailles.libell']}"/>

http://tahe.developpez.com/java/javaee

182/341

5. 6. 7. 8. 9. 10.

<h:outputText value="#{msg['form.joursTravaills.libell']}"/> <!-- ligne 2 --> <h:selectOneMenu id="comboEmployes" value="#{form.comboEmployesValue}"> <f:selectItems value="#{applicationData.employesItems}"/> </h:selectOneMenu> <h:inputText id="heuresTravailles" value="#{form.heuresTravailles}" required="true" requiredMessage="#{msg['form.heuresTravailles.required']}" validatorMessage="#{msg['form.heuresTravailles.validation']}"> 11. <f:validateDoubleRange minimum="0" maximum="300"/> 12. </h:inputText> 13. <h:inputText id="joursTravaills" value="#{form.joursTravaills}" required="true" requiredMessage="#{msg['form.joursTravaills.required']}" validatorMessage="#{msg['form.joursTravaills.validation']}"> 14. <f:validateLongRange minimum="0" maximum="31"/> 15. </h:inputText> 16. <!-- ligne 3 --> 17. <h:panelGroup> 18. <h:outputText value="#{form.comboEmployesValue}"/> 19. </h:panelGroup> 20. <h:panelGroup> 21. <h:outputText value="#{form.heuresTravailles}"/> 22. <h:message for="heuresTravailles" styleClass="error"/> 23. </h:panelGroup> 24. <h:panelGroup> 25. <h:outputText value="#{form.joursTravaills}"/> 26. <h:message for="joursTravaills" styleClass="error"/> 27. </h:panelGroup> 28. </h:panelGrid>

Les lignes 18, 21 et 25 affichent les valeurs du modle. On fait l'exprience suivante sur deux cycles demande / rponse : Cycle 1 : on fait une simulation

On obtient la page suivante :

1 2

En [1], on voit que le modle [Form] a t correctement mis jour. En [2], la prsence de la vue [vueSimulation] montre que la mthode [Form].faireSimulation a t excute. Cycle 2 : on utilise le lien [Enregistrer la simulation] en changeant les valeurs saisies :

http://tahe.developpez.com/java/javaee

183/341

En [1], les valeurs saisies sont changes. En [2], elles sont postes par le lien [Enregistrer la simulation]. On obtient alors le rsultat suivant :

En [1], les valeurs du modles sont bien les valeurs postes. Ce qui montre que la phase [D] du cycle JSF a t excute : A B C

La phase [E], elle, n'est pas excute pour une raison inconnue. Avec des logs, on peut montrer que le flux d'excution ne passe pas par la mthode [Form].enregistrerSimulation. On passe alors la phase [F]. Celle-ci se droule normalement : elle affiche le modle [Form]. Comme la mthode [Form].enregistrerSimulation n'a pas t excute, le modle [Form] est celui issu de l'opration new Form() excute pour recrer le bean de porte request. Or ce bean est configur par dfaut de la faon suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. // les vues private boolean private boolean private boolean private boolean private boolean vueSaisiesIsRendered=true; vueSimulationIsRendered=false; vueSimulationsIsRendered=false; vueSimulationsVidesIsRendered=false; vueErreurIsRendered=false;

// menus private boolean menuFaireSimulationIsRendered=true; private boolean menuEffacerSimulationIsRendered=true; private boolean menuEnregistrerSimulationIsRendered=false; private boolean menuVoirSimulationsIsRendered=false; private boolean menuRetourSimulateurIsRendered=false;

http://tahe.developpez.com/java/javaee

184/341

14.private boolean menuTerminerSessionIsRendered=true;

Il affiche donc : la vue [vueSaisies] les liens [Faire la simulation, Effacer la simulation, Terminer la session] Ce qui explique la page reue. Toute la question est donc de savoir pourquoi la mthode [Form].enregistrerSimulation. associe au lien [Enregistrer la simulation] n'a pas t excute. Une hypothse pourrait tre la suivante :

dans le modle [Form] issu de l'opration new Form(), le champ menuEnregistrerSimulationIsRendered est initialis false: dans la phase [E] l'vnement "il y a eu un clic sur le lien Enregistrer la simulation" est peut-tre ignor parce que dans le modle [Form], ce lien est dsactiv.

De faon plus gnrale, le modle [Form] issu de l'opration new Form() n'est pas celui qui a t utilis pour gnrer la page envoye prcdemment au navigateur client : les vues caches / montres ne sont pas les mmes les liens actifs / inactifs ne sont pas les mmes Ds qu'on redonne la porte session au modle [Form], l'application fonctionne correctement. Cette fois-ci, le modle Form utilis au dbut du cycle n, est bien le mme que celui utilis la fin du cycle n-1 pour gnrer la rponse au navigateur. Pour reproduire ce fonctionnement tout en donnant une porte request au modle [Form], on dcide donc de mettre en session l'tat des vues et des liens du modle [Form] dans le bean #{sessionData}. Ce bean qui mmorise les tats des vues et des liens leur donne une valeur par dfaut correspondant la valeur qu'ils doivent avoir pour la requte initiale :
1.// les vues 2. private boolean 3. ... 4. 5. // menus 6. private boolean 7. private boolean 8. ... 9. private boolean vueSaisiesIsRendered=true;

menuFaireSimulationIsRendered=true; menuEffacerSimulationIsRendered=true; menuTerminerSessionIsRendered=true;

ligne 2 : dans la page initiale, seule la vue [vueSaisie] est affiche lignes 6, 7 et 9 : dans la page initiale, seuls les liens [Faire la simulation, Effacer la simulation, Terminer la session] sont actifs.

18.4

Le bean Form

Le bean [Form] dsormais de porte request volue de la faon suivante :


1. package web.beans.request; 2. 3. ... 4. public class Form { 5. 6. public Form() { 7. } 8. 9. // autres beans 10. private ApplicationData applicationData; 11. private SessionData sessionData; 12. 13. // le modle des vues 14. private String comboEmployesValue=""; 15. private String heuresTravailles=""; 16. private String joursTravaills=""; 17. private Integer numSimulationToDelete; 18. private List<Erreur> erreurs=new ArrayList<Erreur>(); 19. 20. 21. // action du menu 22. public String faireSimulation(){ 23. ... 24. } 25.

http://tahe.developpez.com/java/javaee

185/341

26. public String enregistrerSimulation(){ 27. // on rcupre dans la session le n de la dernire simulation 28. getSessionData().setNumDerniereSimulation(getSessionData().getNumDerniereSimulation()+1); 29. // on l'affecte la simulation qu'on va enregistrer 30. getSessionData().getSimulation().setNum(getSessionData().getNumDerniereSimulation()); 31. // on ajoute aux simulations la dernire simulation que l'utilisateur a faite 32. getSessionData().getSimulations().add(getSessionData().getSimulation()); 33. // on affiche le formulaire des simulations 34. return voirSimulations(); 35. } 36. 37. public String effacerSimulation(){ 38. ... 39. } 40. 41. public String voirSimulations(){ 42. // affichage vue simulations 43. getSessionData().setVues(false,false,getSessionData().getSimulations().size()>0,getSessionData ().getSimulations().size()==0,false); 44. // menus 45. getSessionData().setMenu(false,false,false,false,true,true); 46. // navigation 47. return null; 48. } 49. 50. public String retourSimulateur(){ 51. ... 52. } 53. 54. public String terminerSession(){ 55. ... 56. } 57. 58. public String retirerSimulation(){ 59. ... 60. } 61. 62. 63. // getters et setters 64. ... 65. }

lignes 10-11 : les rfrences sur les beans de type ApplicationData (ligne 10) et SessionData (ligne 11). Rappelons que ces deux champs sont initialiss aprs instanciation de la classe Form, par injection de dpendances par le framework JSF luimme. Ceci est d la configuration faite dans [faces-config.xml] :
1. ... 2. <faces-config ...> 3. <managed-bean> 4. <managed-bean-name>form</managed-bean-name> 5. <managed-bean-class>web.beans.request.Form</managed-bean-class> 6. <managed-bean-scope>request</managed-bean-scope> 7. <managed-property> 8. <property-name>applicationData</property-name> 9. <value>#{applicationData}</value> 10. </managed-property> 11. <managed-property> 12. <property-name>sessionData</property-name> 13. <value>#{sessionData}</value> 14. </managed-property> 15. </managed-bean>

16.

Les lignes 7-10 ci-dessus provoquent l'initialisation du champ applicationData avec une rfrence sur le bean #{applicationData} et les lignes 11-14, celles du champ sessionData avec une rfrence sur le bean #{sessionData}. La mthode enregistrerSimulation des lignes 26-35 montre le rle jou par le bean #{sessionData} :

ligne 28 : incrmente le n de la dernire simulation - cette information est maintenue par le bean #{sessionData} ligne 30 : ce n est affect la simulation qui va tre enregistre. Celle-ci est dans le bean #{sessionData}. Elle y a t place par la mthode faireSimulation des lignes 22-24. ligne 32 : la simulation qui doit tre enregistre est mise dans la liste des simulations, elle aussi gre par le bean #{sessionData}. ligne 34 : le flux d'excution est pass la mthode voirSimulations car une fois que la simulation a t enregistre, l'application montre la liste des simulations.

http://tahe.developpez.com/java/javaee

186/341

ligne 43 : les vues qui doivent tre affiches / caches sont gres. Cette information est maintenue par le bean #{sessionData}. ligne 45 : il est fait de mme avec les options du menu ligne 47 : la cl de navigation null rendue au contrleur [Faces Servlet] fait que celui-ci va renvoyer le bean Form comme rponse au client.

Question : complter le code du bean [Form].

Travail pratique : tester cette nouvelle version.

18.5

Intgration de la couche web dans une architecture 3 couches Jsf / Ejb

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couche [metier] simule

Nous remplaons la couche [mtier] simule, par les couches [mtier, dao, jpa] implmentes par des Ejb au paragraphe 13.1, page 90 :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

2b

[MC] Form.java Modle M Gestionnaires d'vts

[V] JSP form

couches [metier, dao, jpa]

Travail pratique : raliser l'intgrations des couches Jsf et Ejb en suivant la mthodologie du paragraphe 16, page 143.

http://tahe.developpez.com/java/javaee

187/341

19 Version 9 - Application web PAM multi-vues / multi-pages


L'architecture prcdente atteint vite ses limites : ds qu'une application a plus de quelques vues, la page [form.jsp] devient complexe avec ses nombreuses vues <f:subview> et son modle [Form.java] s'alourdit la fois par ajout de champs pour le modle et de mthodes pour la gestion des vnements. elle est viable tant qu'un unique dveloppeur est suffisant mais se prte mal au travail en quipe puisque les dveloppeurs doivent se partager le mme modle [Form.java]. Nanmoins, elle est bien adapte aux projets de quelques vues. Nous prsentons maintenant une architecture offrant davantage de possibilits d'volution o :

la page envoye au client sera gnre par une unique page JSP, appele page matre. Nous l'appelerons ici masterPage.jsp. la page masterPage.jsp sera constitue de parties gnrs par d'autres pages JSP. Ici, la page aura deux parties : 1

la partie [1] de la page sera gnre par la page [entete.jsp]. Elle sera toujours prsente. la partie [2] de la page va changer selon ce qu'on veut prsenter l'utilisateur. C'est la partie variable de la page matre. Elle sera gnre par les pages [simulation.jsp, simulations.jsp, simulationsvides.jsp, erreur.jsp, saisies.jsp]. Nous dirons que la page masterPage.jsp est constitue de deux vues V1 et Vi, mme si pour l'utilisateur, la page reue ne forme qu'une vue. Nous prenons ici un point de vue de dveloppeur. La vue V1 sera toujours gnre par [entete.jsp]. La vue Vi dpendra du contexte. une vue V sera associe une page JSP [V.jsp] et un modle M et contrleur C runis dans une mme classe (bean) MC. Le bean MC contiendra la fois le modle de la vue V et les gestionnaires des vnements se produisant partir de celleci.

Les associations vue V (page Jsp), bean MC (classe Java) seront les suivantes : vue V
entete.jsp saisies.jsp simulation.jsp simulations.jsp simulationsvides erreur.jsp

bean MC BeanMasterPage BeanSimulation BeanSimulation BeanSimulations prsente les options de menu prsente les saisies

rle

prsente la simulation faite partir des saisies prsente la liste des simulations prsente une page statique avec le message indiquant que la liste des simulations est vide prsente une page d'erreur

BeanErreur

L'architecture propose est la suivante :

http://tahe.developpez.com/java/javaee

188/341

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couche [metier] simule

Pour la demande GET initiale : [Faces Servlet] reoit une demande GET d'affichage de la page [masterpage.jsp] en [1] il fait afficher celle-ci en [3]. La page [masterpage.jsp] sera forme des vues [entete.jsp] et [saisies.jsp]. Chacune de ces vues utilisera son propre modle MC pour s'initialiser. Pour les demandes POST qui suivent : [Faces Servlet] reoit une demande POST pour la page [masterpage.jsp] en [1] l'vnement qui a provoqu le POST est trait par une mthode de l'un des contrleurs MC. La page qui provoque le POST sera toujours constitu de deux vues [V1=entete.jsp,Vi]. si l'vnement peut tre rattach la vue Vi, alors on fera traiter l'vnement par le modle / contrleur MC de la vue Vi. Par exemple, si la vue tait [simulations.jsp] qui prsente le tableau des simulations, et si l'vnement provient d'un clic sur l'un des liens "Retirer" du tableau, alors cet vnement sera gr par le bean BeanSimulations qui est associ la vue [simulations.jsp]. si l'vnement ne vise qu' changer de vue Vi, alors il sera trait par le bean BeanMasterPage associ la page matre [masterPage.jsp]. C'est le cas par exemple du clic sur le lien "Voir les simulations" qui a pour effet de passer de la vue Vi=simulation.jsp qui prsente l'utilisateur la simulation qu'il vient de faire la vue Vj=simulations.jsp qui prsente la liste de toutes les simulations qu'il a faites. Nous parlerons alors d'action de navigation. une fois que l'vnement ayant provoqu le POST a t trait par un bean MC, celui-ci mettra jour le modle MCj de la vue Vj qui doit faire partie de la page [entete.jsp, Vj] qui va tre envoye en rponse au navigateur client mettra jour le modle BeanMasterPage de [masterPage.jsp] afin que celle-ci soit constitue des vues [entete.jsp, Vj] rendra comme cl de navigation la cl "masterpage" qui forcera le raffichage de la page masterpage.jsp. [Faces Servlet] ayant reu la cl "masterpage", fera afficher de nouveau [masterpage.jsp] en [3]. La page utilise son modle [BeanMasterPage] pour initialiser ses parties dynamiques. En [4], la page constitue des parties [entete.jsp, Vj] sera envoye au client.

19.1

Le projet Netbeans

Le projet Netbeans de cette version est le suivant :

http://tahe.developpez.com/java/javaee

189/341

4 1 5 2 6

en [1], les fichiers de configuration en [2], les pages Jsp et la feuille de style en [3], les classes de la couche [web]. en [4], le fichier des messages pour l'internationalisation de l'application en [5], les objets changs entre la couche [web] et la couche [mtier] et la couche [mtier] elle-mme en [6], les bibliothques de l'application

19.2

La configuration de l'application

On se souvient que dans la version prcdente, le fichier [faces-config.xml] dfinissait trois beans :

applicationData qui maintenait les informations de porte application sessionData qui maintenait les informations de porte session Form qui servait de modle et de contrleur la page [form.jsp]. Sa dure de vie tait celle de la requte.

Dans la nouvelle version, nous avons plusieurs pages Jsp et nous associons chacune d'elles un bean qui leur sert de modle et de contrleur. Le fichier [faces-config.xml] est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <managed-bean> 5. <managed-bean-name>beanSimulation</managed-bean-name> 6. <managed-bean-class>web.beans.request.BeanSimulation</managed-bean-class> 7. <managed-bean-scope>request</managed-bean-scope> 8. <managed-property> 9. <property-name>applicationData</property-name> 10. <value>#{applicationData}</value> 11. </managed-property> 12. <managed-property> 13. <property-name>beanSimulations</property-name> 14. <value>#{beanSimulations}</value> 15. </managed-property> 16. <managed-property> 17. <property-name>beanMasterPage</property-name> 18. <value>#{beanMasterPage}</value> 19. </managed-property> 20. <managed-property> 21. <property-name>beanErreur</property-name> 22. <value>#{beanErreur}</value> 23. </managed-property> 24. </managed-bean> 25. 26. <managed-bean>

http://tahe.developpez.com/java/javaee

190/341

27. <managed-bean-name>beanSimulations</managed-bean-name> 28. <managed-bean-class>web.beans.session.BeanSimulations</managed-bean-class> 29. <managed-bean-scope>session</managed-bean-scope> 30. <managed-property> 31. <property-name>applicationData</property-name> 32. <value>#{applicationData}</value> 33. </managed-property> 34. <managed-property> 35. <property-name>beanMasterPage</property-name> 36. <value>#{beanMasterPage}</value> 37. </managed-property> 38. </managed-bean> 39. 40. <managed-bean> 41. <managed-bean-name>beanMasterPage</managed-bean-name> 42. <managed-bean-class>web.beans.session.BeanMasterPage</managed-bean-class> 43. <managed-bean-scope>session</managed-bean-scope> 44. </managed-bean> 45. 46. <managed-bean> 47. <managed-bean-name>beanErreur</managed-bean-name> 48. <managed-bean-class>web.beans.request.BeanErreur</managed-bean-class> 49. <managed-bean-scope>request</managed-bean-scope> 50. </managed-bean> 51. 52. <managed-bean> 53. <managed-bean-name>locale</managed-bean-name> 54. <managed-bean-class>web.utils.ChangeLocale</managed-bean-class> 55. <managed-bean-scope>application</managed-bean-scope> 56. </managed-bean> 57. 58. <managed-bean> 59. <managed-bean-name>applicationData</managed-bean-name> 60. <managed-bean-class>web.beans.application.ApplicationData</managed-bean-class> 61. <managed-bean-scope>application</managed-bean-scope> 62. </managed-bean> 63. 64. 65. <application> 66. <resource-bundle> 67. <base-name> 68. messages 69. </base-name> 70. <var>msg</var> 71. </resource-bundle> 72. <message-bundle>messages</message-bundle> 73. </application> 74. 75. <navigation-rule> 76. <from-view-id>/masterPage.jsp</from-view-id> 77. <navigation-case> 78. <from-outcome>masterpage</from-outcome> 79. <to-view-id>/masterPage.jsp</to-view-id> 80. </navigation-case> 81. </navigation-rule> 82. </faces-config>

lignes 58-62 : le bean applicationData tel qu'il existait dans la version prcdente. Il n'a pas chang. Il est de porte application. lignes 40-44 : le bean beanMasterPage est le modle / contrleur de la page masterPage.jsp. Il est de porte session. lignes 4-24 : le bean beanSimulation est le modle / contrleur de la page simulation.jsp. Il est de porte request. lignes 26-38 : le bean beanSimulations est le modle / contrleur de la page simulations.jsp. Il est de porte session. lignes 46-50 : le bean beanErreur est le modle / contrleur de la page erreur.jsp. Il est de porte request.

Certains de ces beans ont des rfrences sur d'autres beans. Nous y reviendrons lorsque nous les examinerons plus en dtail.

lignes 75-81 : il n'y a qu'une rgle de navigation : chaque gestionnaire d'vnement rendra au contrleur [Faces Servlet] la cl masterpage afin de faire afficher la page masterPage.jsp. On peut se demander si cette rgle de navigation est bien ncessaire. Aprs tout, on sait que si un gestionnaire d'vnement rend la cl null au contrleur [Faces Servlet], celui-ci raffiche la page qui a provoqu le POST, donc masterPage.jsp. Nanmoins ce mode de navigation ne convient pas ici. Nous essaierons de comprendre pourquoi.

19.3

Les vues Jsp et leurs beans modles / contrleurs

http://tahe.developpez.com/java/javaee

191/341

19.3.1

La page matre [masterPage.jsp]

La page matre est la suivante :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. 11. <f:view> 12. <html> 13. <head> 14. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 15. <title><h:outputText value="#{msg['form.titre']}"/></title> 16. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 17. </head> 18. <body background="<c:url value="/ressources/standard.jpg"/>"> 19. <h:form id="formulaire"> 20. <!-- entete --> 21. <c:import url="entete.jsp"/> 22. <!-- contenu --> 23. <c:import url="${beanMasterPage.vue}"/> 24. </h:form> 25. </body> 26. </html> 27. </f:view>

La page matre est compose de deux vues : ligne 21 : l'entte de la page gnr par [entete.jsp]. Elle est insre dans la page par une directive < c:import>. La bibliothque de balises jstl/core ncessaire est dclare ligne 6. ligne 23 : une vue dont le nom est dfini par le champ vue du bean beanMasterPage. On notera la syntaxe de l'expression ${beanMasterPage.vue}. C'est une expression JSTL et non JSF. En changeant la valeur de l'expression ${beanMasterPage.vue} on change la nature de la page envoye au navigateur client. Ainsi si la page est constitue des parties [entete.jsp, saisies.jsp], le navigateur affiche la page suivante :

alors que si elle est constitue des parties [entete.jsp, erreur.jsp], le navigateur affiche la page suivante :

http://tahe.developpez.com/java/javaee

192/341

Le bean BeanMasterPage qui sert de modle et de contrleur la page [masterPage.jsp] est le suivant :
1. package web.beans.session; 2. 3. ... 4. public class BeanMasterPage { 5. 6. public BeanMasterPage() { 7. } 8. 9. 10. // menus 11. private boolean menuFaireSimulationIsRendered = true; 12. private boolean menuEffacerSimulationIsRendered = true; 13. private boolean menuEnregistrerSimulationIsRendered; 14. private boolean menuVoirSimulationsIsRendered; 15. private boolean menuRetourSimulateurIsRendered; 16. private boolean menuTerminerSessionIsRendered = true; 17. 18. // la vue JSP afficher 19. private String vue = "saisies.jsp"; 20. 21. // gestion des menus 22. public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered) { 23. this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered); 24. ... 25. } 26. 27. 28. public String retourSimulateur() { 29. ... 30. // navigation 31. setVue("saisies.jsp"); 32. return "masterpage"; 33. } 34. 35. public String terminerSession() { 36. ... 37. // navigation 38. setVue("saisies.jsp"); 39. return "masterpage"; 40. } 41. 42. // voir la liste des simulations 43. public String voirSimulations() { 44. ... 45. // navigation 46. setVue("simulations.jsp"); 47. return "masterpage"; 48. } 49. 50. // getters et setters 51. ... 52. }

Le bean BeanMasterPage sert deux choses :


mmoriser l'tat des menus de la vue [entete.jsp]. C'est pour cette raison que la porte du bean est la session dans [facesconfig.xml] (cf page 190). La mmorisation de l'tat des menus est faite par les champs des lignes 11-16. fixer la vue V qui doit tre affiche par la page [masterPage.jsp] compose de deux parties [entete.jsp, V]. Le nom de la vue V est fix par le champ vue de la ligne 19. La premire vue V tre affiche, au moment du GET initial, est la vue [saisies.jsp].

Le bean BeanMasterPage traite trois vnements :


ligne 28 : le clic sur le lien [Retour au simulateur] : ligne 35 : le clic sur le lien [Terminer la session] :

http://tahe.developpez.com/java/javaee

193/341

ligne 43 : le clic sur le lien [Voir les simulations] :

Ces trois vnements ont pour but premier de faire apparatre une autre vue. C'est ce que nous avons appel prcdemment une action de navigation. Nous faisons traiter ce type d'actions par le bean BeanMasterPage. Nous aurions pu faire d'autres choix.

19.3.2

La page [entete.jsp]

La page [entete.jsp] est celle qui gnre les options du menu. Celles-ci sont prsentes sur toutes les pages envoyes au navigateur.

Son code est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. <script language="javascript"> 11. function raz(){ 12. // on change les valeurs postes 13. document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0"; 14. document.forms['formulaire'].elements['formulaire:heuresTravailles'].value="0"; 15. document.forms['formulaire'].elements['formulaire:joursTravaills'].value="0"; 16. } 17. </script> 18. 19. <!-- entete --> 20. <h:panelGrid columns="2"> 21. <h:panelGroup> 22. <h2><h:outputText value="#{msg['form.titre']}"/></h2> 23. </h:panelGroup> 24. <h:panelGroup> 25. <h:panelGrid columns="1"> 26. <h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{beanSimulation.faireSimulation}" rendered="#{beanMasterPage.menuFaireSimulationIsRendered}"/>

http://tahe.developpez.com/java/javaee

194/341

27.

<h:commandLink id="cmdEffacerSimulation" value="#{msg['form.menu.effacerSimulation']}" action="#{beanSimulation.effacerSimulation}" rendered="#{beanMasterPage.menuEffacerSimulationIsRendered}" onclick="raz()"/> 28. <h:commandLink id="cmdEnregistrerSimulation" value="#{msg['form.menu.enregistrerSimulation']}" action="#{beanSimulations.enregistrerSimulation}" rendered="#{beanMasterPage.menuEnregistrerSimulationIsRendered}"/> 29. <h:commandLink id="cmdVoirSimulations" value="#{msg['form.menu.voirSimulations']}" action="#{beanMasterPage.voirSimulations}" immediate="true" rendered="#{beanMasterPage.menuVoirSimulationsIsRendered}"/> 30. <h:commandLink id="cmdRetourSimulateur" value="#{msg['form.menu.retourSimulateur']}" action="#{beanMasterPage.retourSimulateur}" rendered="#{beanMasterPage.menuRetourSimulateurIsRendered}"/> 31. <h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{beanMasterPage.terminerSession}" rendered="#{beanMasterPage.menuTerminerSessionIsRendered}"/> 32. </h:panelGrid> 33. </h:panelGroup> 34. </h:panelGrid> 35. <hr/>

C'est un code qui tait dj prsent dans la page [form.jsp] de la version prcdente. On notera les points suivants :

[entete.jsp] est insre dans une autre page Jsp, savoir [masterPage.jsp] :

1. ... 2. <f:view> 3. <html> 4. <head> 5. ... 6. </head> 7. <body background="<c:url value="/ressources/standard.jpg"/>"> 8. <h:form id="formulaire"> 9. <!-- entete --> 10. <c:import url="entete.jsp"/> 11. <!-- contenu --> 12. <c:import url="${beanMasterPage.vue}"/> 13. </h:form> 14. </body> 15. </html> 16. </f:view>

Ligne 10, le contenu de [entete.jsp] est insr dans une balise <h:form>. C'est pourquoi dans [entete.jsp], ne trouvonsnous pas les balises <f:view>,<html>,<body>,<h:form> dj prsentes dans [masterPage.jsp]. chaque lien dfinit son modle et son contrleur, par exemple :
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{beanSimulation.faireSimulation}" rendered="#{beanMasterPage.menuFaireSimulationIsRendered}"/>

19.3.3

La page [saisies.jsp]

La page [saisies.jsp] est celle qui gnre le panneau des saisies :

Son code est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>

http://tahe.developpez.com/java/javaee

195/341

5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. <!-- saisie --> 11. <h:panelGrid columns="3"> 12. <!-- ligne 1 --> 13. <h:outputText value="#{msg['form.comboEmployes.libell']}"/> 14. <h:outputText value="#{msg['form.heuresTravailles.libell']}"/> 15. <h:outputText value="#{msg['form.joursTravaills.libell']}"/> 16. <!-- ligne 2 --> 17. <h:selectOneMenu id="comboEmployes" value="#{beanSimulation.comboEmployesValue}"> 18. <f:selectItems value="#{applicationData.employesItems}"/> 19. </h:selectOneMenu> 20. <h:inputText id="heuresTravailles" value="#{beanSimulation.heuresTravailles}" required="true" requiredMessage="#{msg['form.heuresTravailles.required']}" validatorMessage="#{msg['form.heuresTravailles.validation']}"> 21. <f:validateDoubleRange minimum="0" maximum="300"/> 22. </h:inputText> 23. <h:inputText id="joursTravaills" value="#{beanSimulation.joursTravaills}" required="true" requiredMessage="#{msg['form.joursTravaills.required']}" validatorMessage="#{msg['form.joursTravaills.validation']}"> 24. <f:validateLongRange minimum="0" maximum="31"/> 25. </h:inputText> 26. <!-- ligne 3 --> 27. <h:panelGroup> 28. </h:panelGroup> 29. <h:panelGroup> 30. <h:message for="heuresTravailles" styleClass="error"/> 31. </h:panelGroup> 32. <h:panelGroup> 33. <h:message for="joursTravaills" styleClass="error"/> 34. </h:panelGroup> 35. </h:panelGrid> 36. <hr/>

Ce code est celui de la version prcdente. Il n'y a qu'un seul changement : le modle qui tait le bean #{form} est dsormais le bean #{beanSimulation}. Au POST, les valeurs saisies seront enregistres dans les champs #{beanSimulation.comboEmployesValue} (ligne 17), #{beanSimulation.heuresTravailles} (ligne 20), #{beanSimulation.joursTravaills} (ligne 23). Le modle / contrleur #{beanSimulation} est le suivant :
1. package web.beans.request; 2. 3. ... 4. public class BeanSimulation { 5. 6. public BeanSimulation() { 7. } 8. // autres beans 9. private ApplicationData applicationData; 10. private BeanMasterPage beanMasterPage; 11. private BeanErreur beanErreur; 12. private BeanSimulations beanSimulations; 13. 14. // le modle des vues 15. private String comboEmployesValue = ""; 16. private String heuresTravailles = ""; 17. private String joursTravaills = ""; 18. 19. // actions du menu 20. public String faireSimulation() { 21. try { 22. // on calcule la feuille de salaire 23. ... 24. // on met la simulation dans le bean #{beanSimulations} 25. ... 26. // on met jour le menu 27. getBeanMasterPage().setMenu(...); 28. // on affiche la simulation 29. getBeanMasterPage().setVue("simulation.jsp"); 30. } catch (Throwable th) { 31. // on cre la liste des erreurs dans le bean #{beanErreur} 32. ... 33. // on affiche la vue Erreur 34. getBeanMasterPage().setVue("erreur.jsp"); 35. // on met jour le menu 36. getBeanMasterPage().setMenu(...);

http://tahe.developpez.com/java/javaee

196/341

37. } 38. // on rend la mme page 39. return "masterpage"; 40. } 41. 42. public String effacerSimulation() { 43. // on rend la page de saisies vide 44. comboEmployesValue=""; 45. heuresTravailles=""; 46. joursTravaills=""; 47. // mise jour du menu dans le bean #{beanMasterPage} 48. getBeanMasterPage().setMenu(...); 49. // on affiche la vue des saisies 50. getBeanMasterPage().setVue("saisies.jsp"); 51. return "masterpage"; 52. } 53. 54. // getters et setters 55. ...

56.}

lignes 15-17 : le modle de la page [saisies.jsp] lignes 20-40 : la mthode qui traite l'vnement clic sur le lien "Faire la simulation" de [entete.jsp] (cf attribut action cidessous):
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{beanSimulation.faireSimulation}" rendered="#{beanMasterPage.menuFaireSimulationIsRendered}"/>

lignes 42-52 : la mthode qui traite l'vnement clic sur le lien "Effacer la simulation" de [entete.jsp] (cf attribut action cidessous):
<h:commandLink id="cmdEffacerSimulation" value="#{msg['form.menu.effacerSimulation']}" action="#{beanSimulation.effacerSimulation}" rendered="#{beanMasterPage.menuEffacerSimulationIsRendered}" onclick="raz()"/>

lignes 9-12 : rfrencent les beans ncessaires aux mthodes de la classe : le bean #{applicationData} est ncessaire la mthode [faireSimulation] pour obtenir une rfrence sur la couche [mtier] le bean #{beanErreur} est ncessaire la mthode [faireSimulation] s'il se produit une erreur lors du calcul de la simulation. le bean #{beanSimulations} est ncessaire la mthode faireSimulation pour y placer la simulation qui aura t faite afin qu'elle reste dans la session de l'utilisateur enregistrerSimulation pour placer la simulation prcdente dans la liste des simulations enregistres le bean #{beanMasterPage} est ncessaire toutes les mthodes pour grer l'tat du menu. Ces rfrences sur les beans sont instancies par le moteur JSF cause de la configuration du bean #{beanSimulation} faite dans [faces-config.xml] :
1. <managed-bean> 2. <managed-bean-name>beanSimulation</managed-bean-name> 3. <managed-bean-class>web.beans.request.BeanSimulation</managed-bean-class> 4. <managed-bean-scope>request</managed-bean-scope> 5. <managed-property> 6. <property-name>applicationData</property-name> 7. <value>#{applicationData}</value> 8. </managed-property> 9. <managed-property> 10. <property-name>beanSimulations</property-name> 11. <value>#{beanSimulations}</value> 12. </managed-property> 13. <managed-property> 14. <property-name>beanMasterPage</property-name> 15. <value>#{beanMasterPage}</value> 16. </managed-property> 17. <managed-property> 18. <property-name>beanErreur</property-name> 19. <value>#{beanErreur}</value> 20. </managed-property> 21.</managed-bean>

http://tahe.developpez.com/java/javaee

197/341

19.3.4

La page [simulation.jsp]

La page [simulation.jsp] est celle qui gnre la vue de la simulation :

Son code est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. <!-- saisies --> 11. <c:import url="saisies.jsp"/> 12. 13. <!-- simulation --> 14. <!-- informations Employ --> 15. <h:outputText value="#{msg['form.infos.employ']}" styleClass="titreInfos"/> 16. <br/><br/> 17. <h:panelGrid columns="3" rowClasses="libelle,info"> 18. <!-- ligne 1 --> 19. <h:outputText value="#{msg['form.employe.nom']}"/> 20. <h:outputText value="#{msg['form.employe.prnom']}"/> 21. <h:outputText value="#{msg['form.employe.adresse']}"/> 22. <!-- ligne 2 --> 23. <h:outputText value="#{beanSimulations.simulation.feuilleSalaire.employe.nom}"/> 24. <h:outputText value="#{beanSimulations.simulation.feuilleSalaire.employe.prenom}"/> 25. <h:outputText value="#{beanSimulations.simulation.feuilleSalaire.employe.adresse}"/> 26. </h:panelGrid> 27. ... 28. <!-- informations Cotisations --> 29. ... 30. <!-- informations Indemnits -->

http://tahe.developpez.com/java/javaee

198/341

31. ... 32. <!-- informations Salaire --> 33. ... 34. <!-- Salaire net--> 35. ...

ligne 11 : la vue [simulation.jsp] inclut la vue [saisies.jsp] afin que l'utilisateur continue voir ses saisies pour ventuellement les modifier. le code Jsf qui affiche la simulation est celui de la version prcdente au changement prs suivant : le modle #{form} a t remplac par le modle #{beanSimulations} (lignes 23-25 par exemple) qui contient la simulation affiche par la vue (ligne 15 ci-dessous) :

1. package web.beans.session; 2. 3. ... 4. public class BeanSimulations { 5. 6. public BeanSimulations() { 7. } 8. 9. // autres beans 10. private BeanMasterPage beanMasterPage; 11. 12. // champs modle formulaire 13. private List<Simulation> simulations = new ArrayList<Simulation>(); 14. private int numDerniereSimulation = 0; 15. private Simulation simulation; 16. private Integer numSimulationToDelete; 17. 18. // retirer une simulation 19. public String retirerSimulation() { 20. .. 21. } 22. 23. // getters et setters 24. ... 25. }

19.3.5

La page [simulations.jsp]

La page [simulations.jsp] est celle qui gnre la vue prsentant la liste des simulations :

Son code est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. <!-- tableau des simulations --> 11. <h:dataTable value="#{beanSimulations.simulations}" var="simulation" 12. headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisat ionsSociales,simuSalaireNet"> 13. <h:column> 14. <f:facet name="header">

http://tahe.developpez.com/java/javaee

199/341

15. <h:outputText value="#{msg['simulations.headers.numero']}"/> 16. </f:facet> 17. <h:outputText value="#{simulation.num}"/> 18. </h:column> 19. ... 20. <h:column> 21. <h:commandLink value="Retirer" action="#{beanSimulations.retirerSimulation}"> 22. <f:setPropertyActionListener target="#{beanSimulations.numSimulationToDelete}" value="#{simulation.num}"/> 23. </h:commandLink> 24. </h:column> 25. </h:dataTable>

Ce code est celui de la version prcdente au dtail prs suivant : le bean #{form} a t remplac par le bean #{beanSimulations}. Celui-ci est le suivant :
26. package web.beans.session; 27. 28. ... 29. public class BeanSimulations { 30. 31. public BeanSimulations() { 32. } 33. 34. // autres beans 35. private BeanMasterPage beanMasterPage; 36. 37. // champs modle formulaire 38. private List<Simulation> simulations = new ArrayList<Simulation>(); 39. private int numDerniereSimulation = 0; 40. private Simulation simulation; 41. private Integer numSimulationToDelete; 42. 43. // retirer une simulation 44. public String retirerSimulation() { 45. // on retire la simulation de n numSimulationToDelete 46. ... 47. // navigation 48. getBeanMasterPage().setVue(simulations.size() == 0 ? "simulationsvides.jsp" : "simulations.jsp"); 49. return "masterpage"; 50. } 51. 52. // enregistrer une simulation 53. public String enregistrerSimulation() { 54. // on incrmente le n de la dernire simulation 55. ... 56. // on l'affecte la simulation qu'on va enregistrer 57. ... 58. // on ajoute la simulation la liste actuelle des simulations 59. ... 60. // option de menu 61. getBeanMasterPage().setMenu(false, false, false, false, true, true); 62. // on affiche le formulaire des simulations 63. getBeanMasterPage().setVue("simulations.jsp"); 64. return "masterpage"; 65. } 66. 67. // getters et setters 68. ... 69. }

Rappelons que ce bean est de porte session (cf configuration page 190).

ligne 13 : la liste des simulations faites par l'utilisateur ligne 14 : le n de la dernire simulation faite - est incrment chaque nouvelle simulation ligne 15 : la dernire simulation faite par l'utilisateur - est place l par la mthode #{beanSimulation}.faireSimulation. ligne 16 : le n de la simulation retirer - est plac l cause du code Jsf des liens [Retirer] du tableau des simulations :
1. 2. 3. <h:column> <h:commandLink value="Retirer" action="#{beanSimulations.retirerSimulation}"> <f:setPropertyActionListener target="#{beanSimulations.numSimulationToDelete}" value="#{simulation.num}"/> 4. </h:commandLink>

http://tahe.developpez.com/java/javaee

200/341

http://tahe.developpez.com/java/javaee

201/341

5.

</h:column>

La ligne 3 ci-dessus indique le n de la simulation #{simulation.num} doit tre plac dans le champ #{beanSimulations.numSimulationToDelete} lorsque le lien est cliqu.

http://tahe.developpez.com/java/javaee

202/341

ligne 10 : une rfrence sur le bean #{beanMasterPage}. Celui-ci est ncessaire pour la gestion du menu. Ce champ est initialis par le moteur Jsf cause de la configuration faite pour le bean #{beanSimulations} dans [faces-config.xml] :
1. <managed-bean> 2. <managed-bean-name>beanSimulations</managed-bean-name> 3. <managed-bean-class>web.beans.session.BeanSimulations</managed-bean-class> 4. <managed-bean-scope>session</managed-bean-scope> 5. <managed-property> 6. <property-name>beanMasterPage</property-name> 7. <value>#{beanMasterPage}</value> 8. </managed-property> 9.</managed-bean>

19.3.6

La page [erreur.jsp]

La page [erreur.jsp] est celle qui gnre la vue prsentant une erreur :

Son code est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. 7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. <!-- vue Erreur --> 10. <h3><h:outputText value="#{msg['erreur.titre']}"/></h3> 11. <h:dataTable value="#{beanErreur.erreurs}" var="erreur" 12. headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage"> 13. ... 14. </h:dataTable>

Ce code est celui de la version prcdente si ce n'est que le bean #{form} a t remplac par le bean #{beanErreur} (ligne 11). Ce bean est le suivant :
1. package web.beans.request; 2. 3. ... 4. public class BeanErreur { 5. 6. // liste des erreurs afficher 7. 8. private List<Erreur> erreurs = new ArrayList<Erreur>(); 9. 10. // getters et setters 11. ... 12. }

ligne 8 : la liste des objets Erreur affiche par la page [erreur.jsp].

19.3.7

La page [simulationsvides.jsp]

La page [simulationsvides.jsp] est celle qui gnre la vue indiquant que la liste des simulations est vide :

http://tahe.developpez.com/java/javaee

203/341

Son code est le suivant :


1. 2. 3. 4. 5. 6. <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <h2>Votre liste de simulations est vide.</h2>

Cette vue est statique et n'a donc pas de modle associ.

19.4

Les actions

Revenons l'architecture de notre application :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couche [metier] simule

Chaque vue V associe une page Jsp a t associe un bean MC jouant la fois le rle de modle M et de contrleur C de la page Jsp. Les vues V et leurs modles M ont t dtaills prcdemment. Nous prsentons ici la partie contrleur des beans de l'application au travers des actions que ceux-ci excutent.

19.4.1

L'action [faireSimulation]

Le code JSP de l'action [faireSimulation] dans [entete.jsp] est le suivant :


<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{beanSimulation.faireSimulation}" rendered="#{beanMasterPage.menuFaireSimulationIsRendered}"/>

L'action [faireSimulation] calcule une simulation. Elle a t prsente page 164. Question : crire la mthode [faireSimulation] du bean #{beanSimulation} prsent page 196. On s'inspirera du code de la version prcdente.

19.4.2

L'action [effacerSimulation]

Le code JSP de l'action [effacerSimulation] dans [entete.jsp] est le suivant :

http://tahe.developpez.com/java/javaee

204/341

<h:commandLink id="cmdEffacerSimulation" value="#{msg['form.menu.effacerSimulation']}" action="#{beanSimulation.effacerSimulation}" rendered="#{beanMasterPage.menuEffacerSimulationIsRendered}" onclick="raz()"/>

L'action [effacerSimulation] a t prsente page 165. Question : crire la mthode [effacerSimulation] du bean #{beanSimulation} prsent page 196. On s'inspirera du code de la version prcdente.

19.4.3

L'action [enregistrerSimulation]

Le code JSP de l'action [enregistrerSimulation] dans [entete.jsp] est le suivant :


<h:commandLink id="cmdEnregistrerSimulation" value="#{msg['form.menu.enregistrerSimulation']}" action="#{beanSimulations.enregistrerSimulation}" rendered="#{beanMasterPage.menuEnregistrerSimulationIsRendered}"/>

L'action [enregistrerSimulation] a t prsente page 166. Question : crire la mthode [enregistrerSimulation] du bean #{beanSimulations} prsent page 200. On s'inspirera du code de la version prcdente.

19.4.4

L'action [retourSimulateur]

Le code JSP de l'action [retourSimulateur] dans [entete.jsp] est le suivant :


<h:commandLink id="cmdRetourSimulateur" value="#{msg['form.menu.retourSimulateur']}" action="#{beanMasterPage.retourSimulateur}" rendered="#{beanMasterPage.menuRetourSimulateurIsRendered}"/>

L'action [retourSimulateur] a t prsente page 171. Question : crire la mthode [retourSimulateur] du bean #{beanMasterPage} prsent page 193. On s'inspirera du code de la version prcdente. On peut rencontrer une difficult ici. Pour savoir si le menu doit afficher ou non le lien "Voir les simulations", on peut vouloir savoir si la liste des simulations est vide ou non. On n'affichera le lien que si la liste est non vide. Pour cela, il nous faut avoir accs aux simulations qui sont dans le bean #{beanSimulations}. Nous sommes dans le bean #{beanMasterPage}. Une premire ide est de demander JSF, par configuration, d'injecter une rfrence du bean #{beanSimulations} dans le bean #{beanMasterPage} :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config ..."> 4. 5. <managed-bean> 6. <managed-bean-name>beanSimulations</managed-bean-name> 7. <managed-bean-class>web.beans.session.BeanSimulations</managed-bean-class> 8. <managed-bean-scope>session</managed-bean-scope> 9. <managed-property> 10. <property-name>beanMasterPage</property-name> 11. <value>#{beanMasterPage}</value> 12. </managed-property> 13. </managed-bean> 14. 15. <managed-bean> 16. <managed-bean-name>beanMasterPage</managed-bean-name> 17. <managed-bean-class>web.beans.session.BeanMasterPage</managed-bean-class> 18. <managed-bean-scope>session</managed-bean-scope> 19. <managed-property> 20. <property-name>beanSimulations</property-name> 21. <value>#{beanSimulations}</value> 22. </managed-property> 23. </managed-bean> 24. 25. .... 26. </faces-config>

http://tahe.developpez.com/java/javaee

205/341

lignes 19-22 : on injecte le bean #{beanSimulations} dans le bean #{beanMasterPage}

Ici, il faut connatre les rgles observer dans l'injection des beans manags :

on ne peut injecter dans un bean B1 de porte p1, un bean B2 de porte p2 infrieure p1. La hirarchie des dures de vie (ou portes) est la suivante : application > session > request. Ici, on injecte un bean B2 (#{beanSimulations} ligne 20) de porte session (ligne 8) dans un bean B1 (#{beanMasterPage}, ligne 16) galement de porte session (ligne 18). Ceci est possible. on ne peut crer de rfrences circulaires entre beans : on ne peut injecter un bean B2 dans un bean B1, si le bean B1 est lui-mme inject dans le bean B2. Ici, cette proprit n'est pas vrifie. Le bean #{beanMasterPage} tant inject dans le bean #{beanSimulations} (ligne 10), ce dernier ne peut, son tour, tre inject dans le bean #{beanMasterPage} (ligne 20).

Il nous faut trouver une autre solution. On peut alors utiliser du code. Le bean #{beanSimulations} peut tre rcupr avec le code suivant :
// on rcupre le bean des simulations BeanSimulations beanSimulations = (BeanSimulations) FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance( ), "#{beanSimulations}", BeanSimulations.class);

C'est une expression complexe que nous ne commenterons pas. Nous la prendrons telle quelle. De faon gnrale pour un bean #{bean} de type Bean, on crira :
Bean bean = (Bean) FacesContext.getCurrentInstance().getApplication() .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{bean}", Bean.class);

Ce code pourrait remplacer systmatiquement le systme d'injections par configuration si on le souhaitait.

19.4.5

L'action [voirSimulations]

Le code JSP de l'action [voirSimulations] dans [entete.jsp] est le suivant :


<h:commandLink id="cmdVoirSimulations" value="#{msg['form.menu.voirSimulations']}" action="#{beanMasterPage.voirSimulations}" immediate="true" rendered="#{beanMasterPage.menuVoirSimulationsIsRendered}"/>

L'action [voirSimulations] a t prsente page 171. Question : crire la mthode [voirSimulations] du bean #{beanMasterPage} prsent page 193. On s'inspirera du code de la version prcdente.

19.4.6

L'action [retirerSimulation]

Le code JSP de l'action [retirerSimulation] dans la page [simulations.jsp] est le suivant :


<h:commandLink value="Retirer" action="#{beanSimulations.retirerSimulation}"> <f:setPropertyActionListener target="#{beanSimulations.numSimulationToDelete}" value="#{simulation.num}"/> </h:commandLink>

L'action [retirerSimulation] a t prsente page 172. Question : crire la mthode [retirerSimulation] du bean #{beanSimulations} prsent page 200. On s'inspirera du code de la version prcdente.

19.4.7

L'action [terminerSession]

Le code JSP de l'action [terminerSession] dans la page [entete.jsp] est le suivant :


<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{beanMasterPage.terminerSession}" rendered="#{beanMasterPage.menuTerminerSessionIsRendered}"/>

http://tahe.developpez.com/java/javaee

206/341

L'action [terminerSession] a t prsente page 173. Question : crire la mthode [terminerSession] du bean #{beanMasterPage} prsent page 193. On s'inspirera du code de la version prcdente. Travail pratique : tester cette nouvelle version.

19.5

Intgration de la couche web dans une architecture 3 couches avec Jsf - Ejb

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couche [metier] simule

Nous remplaons la couche [mtier] simule, par les couches [mtier, dao, jpa] implmentes par des Ejb au paragraphe 13.1, page 90 :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couches [mtier,dao,jpa]

Travail pratique : raliser l'intgration des couches Jsf et Ejb en suivant la mthodologie du paragraphe 16, page 143.

http://tahe.developpez.com/java/javaee

207/341

20 Intgration Spring / Glassfish


Nous nous intressons ici l'intgration de Spring et Glassfish. Les couches [metier] et [dao] ne seront plus implmentes par des Ejb. Ce seront des classes normales (POJO) lies ensemble par un fichier de configuration Spring. Nous allons porter deux applications dveloppes avec des Ejb :

le service web dvelopp avec des Ejb page 125. la version 9 de l'application web [pam] page 188.

Nous commenons par le service web.

20.1

Service web implment par une application web Spring / Glassfish

Nous nous plaons dans le cadre de l'architecture suivante :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Spring et Serveur Glassfish

Le service web est assur par une application web excute au sein du conteneur web du serveur Glassfish. La couche [metier] sera comme prcdemment expose comme un service web. L'intgration des couches [metier], [dao] et [jpa] sera assure par un conteneur Spring. La gestions des transactions sera dvolue au gestionnaire de transactions du serveur Glassfish. L'application qui va tre dcrite est propre au serveur Glassfish. Ses fondements ont t trouvs dans l'article "How to use Glassfish Managed JPA EntityManager in Spring" [http://java.dzone.com/tips/how-use-glassfish-managed-jpa]. Certains points resteront vagues car vrai dire, je n'ai pas tout compris dans l'article. Nanmoins comme il y a peu d'exemples d'intgration Spring / Glassfish sur le web, il m'a sembl intressant d'inclure cet exemple dans ce document.

20.1.1

La partie serveur

Nous crons une application web. Pour cela, nous suivons la procdure dcrite page 125.

1 2

en [1], nous crons un nouveau projet en [2], ce projet est de type [Web Application] en [3], nous lui donnons le nom [pam-springws-metier-dao-jpa-eclipselink]

http://tahe.developpez.com/java/javaee

208/341

5 en [4], nous choisissons la version Java EE 5 en [5], nous n'utilisons aucun des frameworks proposs en [6], le projet cr

Comme nous allons utiliser Spring, nous ajoutons ses bibliothques (lib / spring) au projet. Nous ajoutons galement l'archive du pilote Jdbc de MySQL : 3 1 2

en [1], les bibliothques actuelles du projet en [2], on en ajoute de nouvelles en [3], les nouvelles bibliothques

Revenons l'architecture de notre projet :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Spring et Serveur Glassfish

Les couches [metier], [dao] et [jpa] sont celles qui ont t utilises dans le projet initial Spring / Jpa [pam-spring-ui-metier-dao-jpahibernate]. Nous chargeons ce projet dans Netbeans [1] et nous en copions les packages [META-INF, jpa, dao, exception, metier] dans le nouveau projet [2] :

http://tahe.developpez.com/java/javaee

209/341

3 1 2

En [3], le fichier [persistence.xml] configure une couche JPA gre par Spring :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 3. <persistence-unit name="pam-spring-ui-metier-dao-jpa-eclipselinkPU" transactiontype="RESOURCE_LOCAL"> 4. <class>jpa.Cotisation</class> 5. <class>jpa.Employe</class> 6. <class>jpa.Indemnite</class> 7. </persistence-unit> 8. </persistence>

Ligne 3, l'attribut transaction-type="RESOURCE_LOCAL" indique que la source de donnes et les transactions qui sont faites dessus sont gres par le code utilisateur (Spring) et non par un conteneur Ejb. Dans le nouveau projet, nous voulons utiliser la ressource Jdbc [jdbc/dbpam_eclipselink] du serveur Glassfish et le gestionnaire de transactions de Glassfish. Le fichier [persistence.xml] volue alors comme suit :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 3. <persistence-unit name="pam-spring-ui-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> 4. <jta-data-source>jdbc/dbpam_eclipselink</jta-data-source> 5. <class>jpa.Cotisation</class> 6. <class>jpa.Employe</class> 7. <class>jpa.Indemnite</class> 8. </persistence-unit> 9. </persistence>

ligne 3, l'attribut transaction-type="JTA" indique que la source de donnes et les transactions qui sont faites dessus sont gres par le serveur Java EE Glassfish. ligne 4 : le nom JNDI de la source de donnes. C'est la source de donnes dj utilise dans les exemples prcdents d'applications dployes sur Glassfish.

Il nous faut maintenant configurer Spring. Dans l'application Spring / Jpa [pam-spring-ui-metier-dao-jpa-hibernate], le fichier de configuration de Spring tait lu au dmarrage de l'application par une mthode main de la couche [ui]. Ici, nous sommes dans une application web o une telle mthode main n'existe pas. Il est possible de configurer l'application web pour que le fichier de configuration de spring soit lu au chargement de l'application web. Cela se fait dans le fichier [web.xml] qui configure l'application web [1] :

http://tahe.developpez.com/java/javaee

210/341

Le fichier [web.xml] sera le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <listener> 4. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 5. </listener> 6. <persistence-unit-ref> 7. <persistence-unit-ref-name>persistence/pam-springws-metier-dao-jpa-eclipselinkPU</persistenceunit-ref-name> 8. <persistence-unit-name>pam-springws-metier-dao-jpa-eclipselinkPU</persistence-unit-name> 9. </persistence-unit-ref> 10. </web-app>

lignes 3-5 : dfinissent un listener. Un listener est une classe charge au dmarrage de l'application web. ligne 4 : le listener charg est un listener fourni par Spring. Il a pour fonction d'exploiter le fichier de configuration de Spring et de crer avec un objet de type WebApplicationContext. Cet objet contiendra les beans dfinis dans le fichier de configuration de Spring. Il est accessible partir de chaque requte faite l'application web. Lorsque le nom du fichier de configuration n'est pas indiqu, c'est le fichier [WEB-INF/applicationContext.xml] qui est utilis. Il faut alors le crer.

lignes 6-9 : dfinissent les units de persistance que l'on trouve dans le fichier [META-INF/persistence.xml]. Il peut y en avoir plusieurs. ligne 8 : le nom de l'unit de persistence. C'est l'attribut name de la balise <persistence-unit> du fichier [METAINF/persistence.xml] : <persistence-unit name="pam-springws-metier-dao-jpa-eclipselinkPU" transaction-type="JTA"> ligne 7 : le nom JNDI de l'unit de persistance. Pour le serveur Glassfish, ce nom est de la forme persistence/nom_unite_de_persistance. Ce nom JNDI change avec chaque serveur Java EE.

Une fois, le fichier [web.xml] dfini, nous pouvons crire le fichier [applicationContext.xml] [1] qui configure Spring. Son contenu sera le suivant :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xmlns:tx="http://www.springframework.org/schema/tx" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx2.0.xsd"> 5. 6. <!-- couches applicatives --> 7. <!-- dao --> 8. <bean id="employeDao" class="dao.EmployeDao" /> 9. <bean id="indemniteDao" class="dao.IndemniteDao" /> 10. <bean id="cotisationDao" class="dao.CotisationDao" /> 11. 12. <!-- persistence --> 13. <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > 14. <property name="persistenceUnits"> 15. <map> 16. <entry key="pam-springws-metier-dao-jpa-eclipselinkPU" value="persistence/pam-springwsmetier-dao-jpa-eclipselinkPU"/> 17. </map> 18. </property> 19. </bean> 20. 21. <!-- le gestionnaire de transactions --> 22. <tx:annotation-driven transaction-manager="txManager" /> 23. 24. <!-- gestionnaire de transactions JTA -->

http://tahe.developpez.com/java/javaee

211/341

25. <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> 26. 27. </beans>

lignes 8-10 : les beans de la couche [dao] ligne 13 : la classe Spring qui gre les annotations de persistance, par exemple @PersistenceContext. lignes 14-18 : dfinissent toutes les units de persistance du fichier [persistence.xml]. On retrouve l les mmes dfinitions que celles trouves pour les units de persistance du fichier [web.xml] ligne 22 : dfinit le bean charg de grer les annotations de transaction telle l'annotation @Transactional. ligne 25 : le gestionnaire de transactions est un gestionnaire JTA. Ce sera le gestionnaire de transactions du serveur sur lequel la couche JPA sera dploye.

Revenons l'architecture de notre application :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Spring et Serveur Glassfish

Pour l'instant, nous avons configur l'intgration Spring / Glassfish pour grer la couche Jpa [persistence.xml, web.xml, applicationContext.xml]. La couche [dao] volue lgrement.

Prenons par exemple la classe [CotisationDao] ci-dessus. Elle pourrait tre crite comme suit :
1. package dao; 2. 3. import exception.PamException; 4. import java.util.List; 5. import javax.persistence.EntityManager; 6. import javax.persistence.PersistenceContext; 7. import jpa.Cotisation; 8. import org.springframework.transaction.annotation.Transactional; 9. 10. @Transactional 11. public class CotisationDao implements ICotisationDao { 12. 13. @PersistenceContext 14. private EntityManager em; 15. 16. // constructeur 17. public CotisationDao() { 18. } 19. ........

ligne 10 : l'annotation Spring qui fait que toutes les mthodes vont tre excutes dans une transaction. Nous avons vu que par configuration, ce sera une transaction JTA du serveur Glassfish.

http://tahe.developpez.com/java/javaee

212/341

ligne 13 : l'annotation qui injecte un EntityManager pour grer la couche Jpa. Cette annotation est traite la fois par Spring et le serveur Glassfish. Il peut y avoir conflit. Avec la configuration qui a t faite, Spring utilisera le mme EntityManager que Glassfish. Dans le fichier [applicationContext.xml] qui configure Spring, nous avons crit :

1. 2. 3. 4. 5.

<!-- persistence --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > <property name="persistenceUnits"> <map> <entry key="pam-springws-metier-dao-jpa-eclipselinkPU" value="persistence/pam-springwsmetier-dao-jpa-eclipselinkPU"/> 6. </map> 7. </property> 8. </bean>

En ligne 5, le nom d'une unit de persistance (key) est associ son nom JNDI (value) sur le serveur Glassfish. C'est via ce nom JNDI que Spring utilisera le mme contexte de persistance que Glassfish. Pour cela, tout contexte de persistance doit tre identifi par son nom (key) lorsqu'il est utilis dans une classe. La classe [CotisationDao] doit donc tre rcrite comme suit :
1. package dao; 2. 3. import exception.PamException; 4. import java.util.List; 5. import javax.persistence.EntityManager; 6. import javax.persistence.PersistenceContext; 7. import jpa.Cotisation; 8. import org.springframework.transaction.annotation.Transactional; 9. 10. @Transactional 11. public class CotisationDao implements ICotisationDao { 12. 13. @PersistenceContext(unitName="pam-springws-metier-dao-jpa-eclipselinkPU") 14. private EntityManager em; 15. 16. // constructeur 17. public CotisationDao() { 18. } 19. ...

En ligne 13, on prcise l'unit de persistance utiliser. On fait de mme pour les deux autres classes de la couche [dao], [EmployeDao] et [IndemniteDao]. Revenons de nouveau sur l'architecture de l'application :

Couche [ui]

C
1

S Couche
2 [metier]

Couche [dao]

Java SE

HTTP / SOAP

Couche [JPA / EclipseLink]

Couche [JDBC]

SGBD

BD

Spring et Serveur Glassfish

Ci-dessus, la couche [metier] va tre expose comme un service web. La couche [metier] est implmente par la classe [Metier] cidessous.

http://tahe.developpez.com/java/javaee

213/341

Pour exposer la classe [Metier] ci-dessus comme un service web, nous suivons une dmarche qui prsente des analogies avec celle utilise pour exposer un Ejb comme un service web. Le code de la classe [Metier] volue comme suit :
1. package metier; 2. 3. ... 4. import org.springframework.transaction.annotation.Transactional; 5. import org.springframework.web.context.support.WebApplicationContextUtils; 6. 7. @WebService 8. @Transactional 9. public class Metier implements IMetier { 10. 11. // rfrences sur la couche [dao] 12. private ICotisationDao cotisationDao = null; 13. private IEmployeDao employeDao = null; 14. private IIndemniteDao indemniteDao = null; 15. 16. // contexte du service web 17. @Resource() 18. private WebServiceContext wsContext; 19. 20. // accs aux beans de la couche [dao] 21. private void getDao() { 22. .... 23. } 24. 25. // obtenir la feuille de salaire 26. @WebMethod 27. public FeuilleSalaire calculerFeuilleSalaire(String SS, 28. double nbHeuresTravailles, int nbJoursTravaills) { 29. // couche [dao] 30. getDao(); 31. // on rcupre les informations lies l'employ 32. Employe employe = employeDao.find(SS); 33. ... 34. } 35. 36. // liste des employs 37. @WebMethod 38. public List<Employe> findAllEmployes() { 39. // couche [dao] 40. getDao(); 41. // rsultat 42. return employeDao.findAll(); 43. } 44. }

ligne 7 : la classe est tague @Webservice. C'est donc un service web. ligne 8 : chaque mthode de la classe s'excutera au sein d'une transaction Spring. Nous savons que Spring va dlguer la gestion de la transaction au serveur Glassfish. lignes 12-14 : des rfrences sur la couche [dao]. Ces rfrences seront initialises par la mthode prive getDao() des lignes 21-23. lignes 26 et 37 : l'annotation @WebMethod dsigne les mthodes exposes par le service web.

Pour comprendre le reste du code, il faut savoir qu'un service web est instanci chaque requte. Ainsi la classe [Metier] ci-dessus est-elle instancie de faon rpte par le conteneur web du serveur Glassfish. C'est la raison pour laquelle, on ne trouve pas la dfinition de la classe [Metier] dans le fichier de configuration de Spring [applicationContext.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" ...> 3.

http://tahe.developpez.com/java/javaee

214/341

4. <!-- couches applicatives --> 5. <!-- dao --> 6. <bean id="employeDao" class="dao.EmployeDao" /> 7. <bean id="indemniteDao" class="dao.IndemniteDao" /> 8. <bean id="cotisationDao" class="dao.CotisationDao" /> 9. 10. <!-- persistence --> 11. ... 12. 13. </beans>

lignes 6-8 : les beans de la couche [dao] seront instancis lorsque Spring exploitera son fichier de configuration. D'aprs le fichier [web.xml] que nous avons crit, ce sera ds le chargement de l'application web dans le conteneur web de Glassfish. Le bean de la couche [Metier] ne peut pas, lui, tre instanci par Spring. Il doit l'tre par le conteneur web lorsqu'une requte pour le service web [Metier] arrive.

La classe [Metier] a besoin de connatre les beans de la couche [dao] pour traiter la requte du client :
1. // obtenir la feuille de salaire 2. @WebMethod 3. public FeuilleSalaire calculerFeuilleSalaire(String SS, 4. double nbHeuresTravailles, int nbJoursTravaills) { 5. // couche [dao] 6. getDao(); 7. // on rcupre les informations lies l'employ 8. Employe employe = employeDao.find(SS); 9. ... 10. } 11. 12. // liste des employs 13. @WebMethod 14. public List<Employe> findAllEmployes() { 15. // couche [dao] 16. getDao(); 17. // rsultat 18. return employeDao.findAll();

19. }

Lignes 8 et 18 ci-dessus, les mthodes du service web [Metier] utilisent des lments de la couche [dao]. Ces derniers ont t instancis par Spring au chargement initial de l'application. La question est donc de retrouver les rfrences des beans instancis par Spring. Ci-dessus, celles-ci sont rerouves par l'appel la mthode getDao(). Celle-ci est la suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. @WebService @Transactional public class Metier implements IMetier { // rfrences sur la couche [dao] private ICotisationDao cotisationDao = null; private IEmployeDao employeDao = null; private IIndemniteDao indemniteDao = null; // contexte du service web @Resource() private WebServiceContext wsContext; // accs aux beans de la couche [dao] private void getDao() { // on rcupre le contexte de l'application ServletContext servletContext = (ServletContext) wsContext.getMessageContext().get(MessageContext.SERVLET_CONTEXT); // on rcupre les beans de la couche [dao] cotisationDao = (ICotisationDao) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("cotisationDao"); employeDao = (IEmployeDao) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("employeDao"); indemniteDao = (IIndemniteDao) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("indemniteDao"); } .......

Un service web est une application web. Une application web est excute par une classe spciale appele Servlet. Les informations lies cette servlet forment le contexte de la servlet ou le contexte de l'application web. Via ce contexte, on a notamment accs aux informations de configuration de l'application. Les lignes de code ci-dessus reposent sur l'accs ce contexte reprsent par le type ServletContext (ligne 17).

lignes 11-12 : demande au serveur Java EE d'injecter dans la variable wsContext (elle doit s'appeler comme a) un objet de type WebServiceContext. C'est un objet qui va nous donner accs au contexte de l'application web. A chaque instanciation du service web (donc chaque requte) ce champ sera initialis par le conteneur web du serveur. ligne 17 : la mthode getMessageContext() donne accs diverses informations lies la requte courante au service web. Ces informations sont disponibles sous la forme d'un dictionnaire. Ici, nous demandons l'objet de type ServletContext li la requte courante. une fois l'objet servletContext obtenu, nous avons accs aux beans instancis par Spring au chargement de l'application par l'instruction suivante : WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("idDuBean").

http://tahe.developpez.com/java/javaee

215/341

lignes 19-21 : instancient les champs des lignes 6-8. En conclusion, aprs excution de la mthode prive getDao, les rfrences des lignes 6-8 sur la couche [dao] sont initialises. Les mthodes du service web [Metier] peuvent alors les utiliser.

Nous sommes dsormais prts dployer notre service web. Nous le construisons (Clean and Build) puis le dployons. Il faut que le serveur MySQL soit lanc et la base [dbpam_eclipselink] cre et remplie.

en [1], le service web a t dploy sur le serveur Glassfish v3 en [2], nous le testons

en [3], la page de tests. Nous invitons le lecteur s'assurer que le service web qu'il a dploy fonctionne correctement.

Le service web tant dploy, il nous reste construire un client.

20.1.2

La partie cliente

Travail faire : en suivant la dmarche dcrite au paragraphe 14.1.2.1, page 121, construire un client console du service web prcdent.

20.2

Portage de la version 9 de l'application web [pam]

http://tahe.developpez.com/java/javaee

216/341

20.2.1

Architecture Jsf / Spring

L'architecture de la version 9 l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couches [mtier,dao,jpa]

Conteneur de servlets

Conteneur Ejb3

la couche [web] s'excutait dans un conteneur de servlets les couches [mtier, dao, jpa] s'excutaient dans un conteneur Ejb3

Nous utilisons maintenant la nouvelle architecture suivante :

Application web
couche [web]
1

Faces Servlet V1 V2 Vn

2a 3

MC1 MC2 MCn

2b

[V] JSP masterpage

couches [mtier,dao,jpa/eclipselink]

Conteneur de servlets / Spring / Glassfish

L'intgration des couches [metier], [dao] et [jpa] sera assure par un conteneur Spring. La gestions des transactions sera dvolue au gestionnaire de transactions du serveur Glassfish.

20.2.2

Le projet Netbeans Jsf / Spring

Le projet [pam-spring-jsf-metier-dao-jpa-eclipselink-mysql] utilise des lments de deux projets dj construits :


le service web prcdent l'application web multivues / multipages dcrite au paragraphe 19, page 188

Nous chargeons ces deux projets dans Netbeans. Le projet [pam-spring-jsf-metier-dao-jpa-eclipselink-mysql] est d'abord obtenu par recopie du projet [pam-jsf-alone-multivuesmultipages] :

http://tahe.developpez.com/java/javaee

217/341

Dans une seconde tape, les paquetages [exception, jpa, metier] du nouveau projet [1] sont remplacs par les paquetages [METAINF, exception, jpa, dao, metier] du projet prcdemment tudi [pam-springws-metier-dao-jpa-eclipselink].

en [1], nous avons des erreurs dus au fait que les paquetages copis font rfrence Spring en [2], nous ajoutons les bibliothques de Spring au projet [2] ainsi que le pilote Jdbc de MySQL

A ce stage, seule une classe prsente des erreurs, la classe [ApplicationData] [3] :

http://tahe.developpez.com/java/javaee

218/341

20.2.3

Fichiers de configuration

L'application en cours de construction est configure par divers fichiers : META-INF / persistence.xml qui configure la couche de persistance qui sera gre par le serveur Glassfish comme dans le service web tudi prcdemment - dj prsent - issu du service web - reste inchang WEB-INF / faces-config.xml qui configure la couche web / JSF - dj prsent - issu de l'application web version 9 reste inchang WEB-INF / web.xml qui configure l'application web - dj prsent - issu de l'application web version 9 - doit voluer WEB-INF / applicationContext.xml qui configure Spring comme il a t fait dans le service web prcdent - inclure dans le projet - doit voluer Nous copions maintenant le fichier [WEB-INF / applicationContext.xml] partir du projet [pam-springws-metier-dao-jpaeclipselink] :

Ce fichier est actuellement le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xmlns:tx="http://www.springframework.org/schema/tx" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx2.0.xsd"> 5. 6. <!-- couches applicatives --> 7. <!-- dao --> 8. <bean id="employeDao" class="dao.EmployeDao" /> 9. <bean id="indemniteDao" class="dao.IndemniteDao" /> 10. <bean id="cotisationDao" class="dao.CotisationDao" /> 11. 12. <!-- persistence --> 13. <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > 14. <property name="persistenceUnits"> 15. <map> 16. <entry key="pam-springws-metier-dao-jpa-eclipselinkPU" value="persistence/pam-springwsmetier-dao-jpa-eclipselinkPU"/> 17. </map> 18. </property> 19. </bean> 20. 21. <!-- le gestionnaire de transactions --> 22. <tx:annotation-driven transaction-manager="txManager" /> 23. 24. <!-- gestionnaire de transactions JTA --> 25. <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> 26. 27. </beans>

Il avait t crit pour une application o la couche [metier] tait un service web. La classe [Metier] qui l'implmentait tait instancie chaque appel fait au service web. Ici, la classe [Metier], comme les classes de la couche [dao] doit tre instancie une seule fois, au dmarrage de l'application. Aussi le fichier [applicationContext.xml] volue-t-il comme suit :
1. <!-- couches applicatives --> 2. <!-- dao --> 3. <bean id="employeDao" class="dao.EmployeDao" /> 4. <bean id="indemniteDao" class="dao.IndemniteDao" /> 5. <bean id="cotisationDao" class="dao.CotisationDao" /> 6. <!-- mtier --> 7. <bean id="metier" class="metier.Metier"> 8. <property name="employeDao" ref="employeDao"/> 9. <property name="indemniteDao" ref="indemniteDao"/> 10. <property name="cotisationDao" ref="cotisationDao"/>

http://tahe.developpez.com/java/javaee

219/341

11. 12. 13. 14. 15. 16. 17.

</bean>

<!-- persistence --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > <property name="persistenceUnits"> <map> <entry key="pam-springws-metier-dao-jpa-eclipselinkPU" value="persistence/pam-springwsmetier-dao-jpa-eclipselinkPU"/> 18. </map> 19. </property> 20. </bean> 21. ............

lignes 7-11 : dfinition du bean "metier" de la couche [metier]

Le fichier [WEB-INF / web.xml] qui configure l'application web fusionne les informations des fichiers [web.xml] du service web et l'application web :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. 4. <!-- configuration Spring --> 5. <listener> 6. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 7. </listener> 8. <persistence-unit-ref> 9. <persistence-unit-ref-name>persistence/pam-springws-metier-dao-jpa-eclipselinkPU</persistenceunit-ref-name> 10. <persistence-unit-name>pam-springws-metier-dao-jpa-eclipselinkPU</persistence-unit-name> 11. </persistence-unit-ref> 12. 13. <!-- configuration Jsf --> 14. <servlet> 15. <servlet-name>Faces Servlet</servlet-name> 16. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 17. <load-on-startup>1</load-on-startup> 18. </servlet> 19. <servlet-mapping> 20. <servlet-name>Faces Servlet</servlet-name> 21. <url-pattern>/faces/*</url-pattern> 22. </servlet-mapping> 23. <session-config> 24. <session-timeout> 25. 30 26. </session-timeout> 27. </session-config> 28. <welcome-file-list> 29. <welcome-file>faces/masterPage.jsp</welcome-file> 30. </welcome-file-list> 31. <error-page> 32. <error-code>500</error-code> 33. <location>/faces/exception.jsp</location> 34. </error-page> 35. <error-page> 36. <exception-type>java.lang.Exception</exception-type> 37. <location>/faces/exception.jsp</location> 38. </error-page> 39. 40. </web-app>

20.2.4

Mise jour du code Java

La classe [ApplicationData] actuelle est issue de l'application web version 9 :


1. package web.beans.application; 2. 3. ... 4. public class ApplicationData { 5. 6. /** Creates a new instance of ApplicationData */ 7. public ApplicationData() { 8. } 9. 10. // couche mtier 11. private IMetierLocal metier=new Metier();

http://tahe.developpez.com/java/javaee

220/341

12.

...

Dans la version 9 de l'application web, nous utilisions une couche [metier] simule implmente par une classe [Metier] implmentant l'interface [IMetierLocal]. Ci-dessous, nous pouvons voir que cette interface n'existe plus. Dans la version Spring, l'interface s'appelle [IMetier] :

Par ailleurs, la classe [Metier] issue du projet [pam-springws-metier-dao-jpa-eclipselink] est un service web. Pour en faire une classe normale gre par Spring, nous enlevons les annotations et le code qui en faisaient un service web :
1. package metier; 2. 3. ... 4. 5. @Transactional 6. public class Metier implements IMetier { 7. 8. // rfrences sur la couche [dao] 9. private ICotisationDao cotisationDao = null; 10. private IEmployeDao employeDao = null; 11. private IIndemniteDao indemniteDao = null; 12. 13. // obtenir la feuille de salaire 14. public FeuilleSalaire calculerFeuilleSalaire(String SS, 15. double nbHeuresTravailles, int nbJoursTravaills) { 16. // on rcupre les informations lies l'employ 17. Employe employe = employeDao.find(SS); 18. .... 19. // on rend le rsultat 20. return new FeuilleSalaire(employe, cotisation, indemnite, elementsSalaire); 21. } 22. 23. // liste des employs 24. public List<Employe> findAllEmployes() { 25. // rsultat 26. return employeDao.findAll(); 27. } 28. }

Le code de la classe [ApplicationData] volue comme suit :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. public class ApplicationData { /** Creates a new instance of ApplicationData */ public ApplicationData() { } // couche mtier private IMetier metier; ... @PostConstruct private void init(){ // on rcupre le contexte Spring partir de JSF ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); // instanciation couche [metier] metier = (IMetier) ctx.getBean("metier"); // la liste des employs est demande la couche mtier List<Employe>employes=getMetier().findAllEmployes(); .... } // getters et setters .....

ligne 8 : la couche [metier] initialise par la mthode [init] de la ligne 12 ligne 14 : on rcupre le contexte Spring issu de l'exploitation du fichier de configuration [applicationContext.xml]. La classes [FacesContextUtils] est fournie par Spring. Elle permet de rcuprer un contexte Spring dans une application Jsf. ligne 16 : initilaisation du champ de la ligne 8 avec le bean Spring "metier"

http://tahe.developpez.com/java/javaee

221/341

Ces modifications faites, le nouveau projet [pam-spring-jsf-metier-dao-jpa-eclipselink-mysql] doit se compiler sans erreurs. Avant de le dployer, on procdera aux prliminaires suivants :

on lancera le SGBD MySQL5 on s'assurera que la base de donnes [dbpam_eclipselink] existe et est remplie.

Ceci fait, nous pouvons excuter le projet. Nous obtenons la chose suivante :

Le lecteur est invit tester l'application. Voici un exemple de simulation :

http://tahe.developpez.com/java/javaee

222/341

Le lecteur est invit faire des tests supplmentaires.

http://tahe.developpez.com/java/javaee

223/341

JavaServer Faces (JSF) par l'exemple avec Netbeans 6.8 et le serveur Java EE Glassfish V3

http://tahe.developpez.com/java/javaee

224/341

21 JSF - JavaServer Faces


Objectif : Introduction JSF via des exemples Netbeans. Pour approfondir :

Java EE5 Tutorial [http://java.sun.com/javaee/5/docs/tutorial/information/download.html]. La rfrence Sun pour dcouvrir Java EE5. On lira la partie II [Web Tier] pour dcouvrir la technologie web et notamment JSF. Les exemples du tutoriel peuvent tre tlchargs. Ils viennent sous forme de projets Netbeans qu'on peut charger et excuter. Java EE5 de Antonio Goncalves aux ditions Eyrolles. Ce livre, qui montre l'utilisation de diffrentes technologies Java EE5 dans le dveloppement d'une application de commerce lectronique est particulirement conseill. JavaServer Faces de Chris Schalk et Ed Burns aux ditions Mc Graw-Hill Introduction la programmation web en Java [http://tahe.developpez.com/java/web/] : donne les bases de la programmation web en Java : servlets et pages JSP. Les bases du dveloppement web MVC en Java [http://tahe.developpez.com/java/baseswebmvc] : prconise le dveloppement d'applications web avec des architectures trois couches, o la couche web implmente le modle de conception (Design Pattern) MVC (Modle, Vue, Contrleur). L'introduction JSF sera faite dans ce contexte [1] :

Utilisateur

Couche [Jsf/web] 1

Couche [metier]

Couche [dao]

Interface [JPA]

Implmentation [EclipseLink]

Couche [JDBC]

SGBD

Nous utiliserons l'IDE Netbeans 6.8 pour construire les exemples et le serveur Glassfish v3 pour les excuter. L'application en couches ci-dessus peut tre excute dans deux contextes diffrents :

1- totalement dans le conteneur de servlets du serveur :

Navigateur HTTP

Conteneur de servlets [web, metier, dao, jpa] serveur Java EE ou conteneur de servlets

SGBD

2 - la couche [web] dans le conteneur de servlets du serveur, les autres couches dans le conteneur Ejb :

Navigateur HTTP

Conteneur web [web]

Conteneur Ejb3 [metier, dao, jpa]

SGBD

serveur Java EE

Dans le contexte 1, l'intgration des couches peut tre faite avec Spring et l'application excute dans un simple conteneur de servlets comme Tomcat. Dans le contexte 2, l'intgration des couches est faite par le serveur Java EE. Dans ce document, nous crirons des applications le plus souvent rduites la seule couche [web]. Une telle application s'excutera alors dans le conteneur de servlets du serveur Glassfish. Nous prsenterons quelques application avec les couches [web, metier, dao, jpa] et o les couches [metier, dao] seront implmentes avec des Ejb. Dans ce cas, l'application s'excutera dans le contexte 2.

21.1
21.1.1

Exemple n 1
Gnration du projet

http://tahe.developpez.com/java/javaee

225/341

en [1], crer un nouveau projet en [2], choisir la catgorie [web] et le type de projet [Web Application].

4 3 6 7 5 8

en [3], dsigner le dossier parent du dossier du nouveau projet en [4], donner un nom au projet en [5], le contexte du projet, c.a.d. le nom sous lequel il sera connu par les navigateurs clients. Le nom du projet est propos par dfaut. Dans l'exemple ci-dessus, cela signifie que les pages du projet seront accessibles via des url de la forme [http://machine:port/intro-01/page]. en [6], on choisit le serveur Glassfish v3 en [7], on choisit la version Java EE 5 en [8], on demande le support du framework JavaServer Faces, version 1.2 [9]. Cela va gnrer les fichiers de configuration ncessaires aux projets de type JSF 1.2.

Examinons les lments du projet ainsi gnr et explicitons le rle de chacun :

en [1] : les diffrentes branches du projet : [Web Pages] : contiendra les pages web (.html, .jsp), les ressources (images, documents divers), la configuration de la couche web. [Source packages] : les classes Java du projet [Libraries] : les archives .jar ncessaires au projet [Test packages] : les classes Java des tests de l'application. Restera vide dans nos exemples. [Test Libraries] : les archives .jar ncessaires aux classes de test et non dj comprises dans la branche [Libraries] [Configuration Files] : reprend les fichiers de configuration de la couche [web] dj prsents dans la branche [Web Pages].

http://tahe.developpez.com/java/javaee

226/341

en [2], on trouve des pages JSP (Java Server Pages) et des fichiers de configuration : [welcomeJSF.jsp] : une page JSP gnre automatiquement par Netbeans. Nous la commenterons ultrieurement. [WEB-INF/web.xml] : le fichier de configuration de l'application web - existe dans toute application web [WEB-INF/faces-config.xml] : le fichier de configuration du projet JSF - n'existe que dans un projet JSF [WEB-INF/sun-web.xml] : fichier de dploiement sur le serveur Glassfish - est propre celui-ci et sera remplac par un autre si on change de serveur. en [3] : les bibliothques du projet.

21.1.2
21.1.2.1

Configuration d'un projet JSF


web.xml

Un projet JSF est un projet web et en tant que tel, doit tre configur par un fichier [WEB-INF/web.xml].

Le fichier [web.xml] gnr par Netbeans est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <servlet> 4. <servlet-name>Faces Servlet</servlet-name> 5. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 6. <load-on-startup>1</load-on-startup> 7. </servlet> 8. <servlet-mapping> 9. <servlet-name>Faces Servlet</servlet-name> 10. <url-pattern>/faces/*</url-pattern> 11. </servlet-mapping> 12. <session-config> 13. <session-timeout> 14. 30 15. </session-timeout> 16. </session-config> 17. <welcome-file-list> 18. <welcome-file>faces/welcomeJSF.jsp</welcome-file> 19. </welcome-file-list> 20. </web-app>

les lignes 3-7 dfinissent une servlet, c.a.d. une classe Java capable de traiter les demandes des clients. Une application JSF fonctionne de la faon suivante :

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

http://tahe.developpez.com/java/javaee

227/341

Cette architecture implmente le Design Pattern MVC (Modle, Vue, Contrleur). Le traitement d'une demande d'un client se droule selon les quatre tapes suivantes : 1. demande - le client navigateur fait une demande au contrleur [Faces Servlet]. Celui-ci voit passer toutes les demandes des clients. C'est la porte d'entre de l'application. C'est le C de MVC . 2. traitement - le contrleur C traite cette demande. Pour ce faire, il se fait aider par des gestionnaires d'vnements spcifiques l'application crite [2a]. Ces gestionnaires peuvent avoir besoin de l'aide de la couche mtier [2b]. Une fois la demande du client traite, celle-ci peut appeler diverses rponses. Un exemple classique est : une page d'erreurs si la demande n'a pu tre traite correctement une page de confirmation sinon 3. navigation - le contrleur choisit la rponse (= vue) envoyer au client. Choisir la rponse envoyer au client ncessite plusieurs tapes : choisir la page JSP qui va gnrer la rponse. C'est ce qu'on appelle la vue V, le V de MVC. Ce choix dpend en gnral du rsultat de l'excution de l'action demande par l'utilisateur. fournir cette page Jsp les donnes dont elle a besoin pour gnrer cette rponse. En effet, celle-ci contient le plus souvent des informations calcules par le contrleur. Ces informations forment ce qu'on appelle le modle M de la vue, le M de MVC. L'tape 3 consiste donc en le choix d'une vue V et en la construction du modle M ncessaire celle-ci. 4. rponse - le contrleur C demande la page JSP choisie de s'afficher. Celle-ci utilise le modle M prpar par le contrleur C pour initialiser les parties dynamiques de la rponse qu'elle doit envoyer au client. La forme exacte de celle-ci peut tre diverse : ce peut tre un flux HTML, PDF, Excel, ... Dans un projet JSF : le contrleur C est la servlet [javax.faces.webapp.FacesServlet]. On trouve celle-ci dans la bibliothque [javaee.jar]. les vues V sont implmentes par des pages JSP. les modles M et les gestionnaires d'vnements sont implments par des classes Java souvent appeles "backing beans". les rgles de navigation sont dfinies dans le fichier [faces-config.xml]. On y trouve la liste des vues et les rgles de transition de l'une l'autre. Revenons sur le contenu du fichier [web.xml) :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <servlet> 4. <servlet-name>Faces Servlet</servlet-name> 5. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 6. <load-on-startup>1</load-on-startup> 7. </servlet> 8. <servlet-mapping> 9. <servlet-name>Faces Servlet</servlet-name> 10. <url-pattern>/faces/*</url-pattern> 11. </servlet-mapping> 12. <session-config> 13. <session-timeout> 14. 30 15. </session-timeout> 16. </session-config> 17. <welcome-file-list> 18. <welcome-file>faces/welcomeJSF.jsp</welcome-file> 19. </welcome-file-list> 20. </web-app>

lignes 8-11 : la balise <servlet-mapping> sert associer une servlet une Url demande par le navigateur client. Ici, il est indiqu que les Url de la forme [/faces/*] doivent tre traites par la servlet de nom [Faces Servlet]. Celle-ci est dfinie lignes 3-7. Comme il n'y a pas d'autre balise <servlet-mapping> dans le fichier, cela signifie que la servlet [Faces Servlet] ne traitera que les url de la forme [/faces/*]. Nous avons vu que le contexte de l'application s'appelait [/intro-01]. Les Url des clients traites par la servlet [Faces Servlet] auront donc la forme [http://machine:port/intro-01/faces/*]. Les pages .html et .jsp seront traites par dfaut par le conteneur de servlets lui-mme, et non par une servlet particulire. En effet, le conteneur de servlets sait comment les grer. lignes 3-7 : dfinissent la servlet [Faces Servlet]. Comme toutes les Url acceptes sont diriges vers elle, elle est le contrleur C du modle MVC. ligne 6 : indique que la servlet doit tre charge en mmoire ds le dmarrage du serveur web. Par dfaut, une servlet n'est charge qu' rception de la 1re demande qui lui est faite.

http://tahe.developpez.com/java/javaee

228/341

lignes 13-15 : dure en minutes d'une session. Un client dialogue avec l'application par une suite de cycles demande / rponse o chaque cycle se droule comme il a t dcrit prcdemment. Chaque cycle utilise une connexion tcp-ip qui lui est propre, nouvelle chaque nouveau cycle. Aussi, si un client C fait deux demandes D1 et D2, le serveur S n'a pas les moyens de savoir que les deux demandes appartiennent au mme client C. Le serveur S n'a pas la mmoire du client. C'est le protocole HTTP utilis (HyperText Transport Protocol) qui veut a : le client dialogue avec le serveur par une succession de cycles demande client / rponse serveur utilisant chaque fois une nouvelle connexion tcp-ip. On parle de protocole sans tat. Dans d'autres protocoles, comme par exemple FTP (File Transfer Protocol), le client C utilise la mme connexion pendant la dure de son dialogue avec le serveur S. Une connexion est donc lie un client particulier. Le serveur S sait toujours qui il a affaire. Afin de pouvoir reconnatre qu'une demande appartient un client donn, le serveur web peut utiliser la technique de la session : lors de la 1re demande d'un client, le serveur S lui envoie la rponse attendue plus un jeton, une suite de caractres alatoire, unique ce client. lors de chaque demande suivante, le client C renvoie au serveur S le jeton qu'il a reu, permettant ainsi au serveur S de le reconnatre. L'application a dsormais la possibilit de demander au serveur de mmoriser des informations associes un client donn. On parle de session client. La ligne 26 indique que la dure de vie d'une session est de 30 mn. Cela signifie que si un client C ne fait pas de nouvelle demande pendant 30 mn, sa session est dtruite et les informations qu'elle contenait, perdues. Lors de sa prochaine demande, tout se passera comme s'il tait un nouveau client et une nouvelle session dmarrera. lignes 17-19 : la liste des pages afficher lorsque l'utilisateur demande le contexte sans prciser de page, par exemple ici [http://machine:port/intro-01]. Dans ce cas, le serveur web (pas la servlet) recherche si l'application a dfini une balise <welcome-file-list>. Si oui, il affiche la 1re page trouve dans la liste. Si elle n'existe pas, la deuxime page, et ainsi de suite jusqu' trouver une page existante. Ici, lorsque le client demande l'url [http://machine:port/intro-01], c'est l'url [http://machine:port/intro-01/welcomeJSF.jsp] qui lui sera servie.

21.1.2.2

faces-config.xml

Le fichier [faces-config.xml] gnr par Netbeans est le suivant :


1. <?xml version='1.0' encoding='UTF-8'?> 2. 3. <!-- =========== FULL CONFIGURATION FILE ================================== --> 4. 5. <faces-config version="1.2" 6. xmlns="http://java.sun.com/xml/ns/javaee" 7. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 8. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/webfacesconfig_1_2.xsd"> 9. 10. 11. </faces-config>

Revenons sur l'architecture d'une application Jsf :

http://tahe.developpez.com/java/javaee

229/341

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

On trouve dans le fichier [faces-config.xml] : la liste des gestionnaires d'vnements [2a] ainsi que les modles [3] de l'application. Ces lments sont des classes Java qui suivent la norme JavaBean : prsence d'un constructeur sans paramtres prsence de mthodes get / set pour chaque champ priv Ces beans sont tous dclars dans [faces-config.xml]. Un bean est souvent li une page Jsp particulire qui il sert la fois de modle et de gestionnaire d'vnements : les champs du bean servent de modle et ses mthodes de gestionnaires d'vnements. les rgles de navigation. Les gestionnaires d'vnements doivent rendre au contrleur [Faces Servlet] ci-dessus, un rsultat sous forme de chanes de caractres. Les rgles de navigation consistent dfinir pour tous les vnements de l'application, trois paramtres [jsp1, res1, jsp2] : jsp1 est la page Jsp dont un vnement a t gr res1 est l'une des chanes de caractres que peut rendre le gestionnaire de cet vnement jsp2 est la page Jsp qui doit tre renvoye l'utilisateur lorsque le gestionnaire d'vnements rend la valeur res1 Si l'ensemble des gestionnaires des vnements de la page jsp1 peut rendre N rsultats diffrents, alors on aura N rgles de navigation dans le fichier [faces-config.xml] pour la page jsp1. Dans l'exemple tudi, il n'y a ni modle, ni gestionnaire d'vnements, aussi le fichier est-il prsent mais sans configuration. 21.1.3 Excution du projet

Avant d'excuter le projet, regardons ces proprits : 3 1 4

en [1], clic droit sur le projet puis option Properties en [2], option Run en [3], le serveur sur lequel va tre dploye l'application en [4], son contexte (son nom)

http://tahe.developpez.com/java/javaee

230/341

7 8

en [6], on excute le projet. Ceci va avoir pour effet de lancer le serveur Glassfish, si ce n'tait fait [7] en [8], l'application [intro-01] apparat dans la branche [Applications] du serveur Glassfish

L'excution ouvre un navigateur et demande la page d'accueil de l'application [http://localhost:8080/intro-01/] [9] :

10

en [10], la page [welcomeJSF.jsp]. En [9], on a demand le contexte /intro-01/ sans mentionner de page. C'est donc la page [http://localhost:8080/intro-01/welcomeJSF.jsp] qui a t servie (cf <welcome-file-list> dans [web.xml]). La page [welcomeJSF.jsp]

21.1.4

Examinons la page [welcomeJSF.jsp] gnre par l'assistant de cration du projet web :


1. <%@page contentType="text/html" pageEncoding="UTF-8"%> 2. 3. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 4. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 5. 6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 7. "http://www.w3.org/TR/html4/loose.dtd"> 8. 9. <%-10. This file is an entry point for JavaServer Faces application. 11. --%> 12. <f:view> 13. <html> 14. <head> 15. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 16. <title>JSP Page</title> 17. </head> 18. <body> 19. <h1><h:outputText value="JavaServer Faces"/></h1> 20. </body> 21. </html> 22. </f:view>

On a l, une page JSP qui contient des balises appartenant au framework JSF. Aussi par la suite, appellera-t-on ce type de page, page JSF. Deux balises n'appartiennent pas au standard HTML :

<f:view> (ligne 12) : balise racine de la partie JSF d'une page JSP. Un code situ l'extrieur de cette balise ne fait l'objet d'aucun traitement de la part du contrleur JSF. <h:outputText> (ligne 19) : est remplac dans le flux HTML de la rponse par le texte de son attribut value.

Le code HTML gnr par cette page est le suivant :

http://tahe.developpez.com/java/javaee

231/341

1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 2. "http://www.w3.org/TR/html4/loose.dtd"> 3. 4. <html> 5. <head> 6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 7. <title>JSP Page</title> 8. </head> 9. <body> 10. <h1>JavaServer Faces</h1> 11. </body> 12. </html>

On voit que toute balise non HTML a disparu. Les balises <f:view> et <h:outputText> font partie de bibliothques de balises dfinies lignes 3 et 4 de la page [welcomeJSF.jsp] :
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

Considrons la 1re des deux balises. Elle a un attribut uri qui dsigne une bibliothque prcise. Deux bibliothques diffrentes doivent avoir des attributs uri diffrents. A la rencontre de cette attribut, le serveur web va explorer les dossiers [META-INF] du Classpath de l'application, la recherche de fichiers avec le suffixe .tld (TagLib Definition). Ici, il va les trouver dans l'archive [jsfimpl.jar] [1,2] : 3 2

Examinons [3] le fichier [jsf_core.tld] :


1. <taglib xmlns="http://java.sun.com/xml/ns/javaee" 2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/webjsptaglibrary_2_1.xsd" 4. version="2.1"> 5. 6. 7. <!-- ========== Tag Library Description Elements ========================= --> 8. 9. <description> 10. The core JavaServer Faces custom actions that are independent of 11. any particular RenderKit. 12. </description> 13. <tlib-version>1.2</tlib-version> 14. <short-name>f</short-name> 15. <uri>http://java.sun.com/jsf/core</uri>

en ligne 15, l'uri de la bibliothque de balises en ligne 14, son nom court

Revenons au code de la page JSF :


1. ... 2. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 3. ... 4. <f:view> 5. ... 6. </f:view>

7. ...

http://tahe.developpez.com/java/javaee

232/341

En ligne 4, le parseur de la page JSP trouve la balise <f:view>. La ligne 2 lui indique que toute balise commenant par f dsigne une balise de la bibliothque d'uri [http://java.sun.com/jsf/core]. Il trouve la dfinition de cette bibliothque dans [jsf-impl.jar/METAINF/jsf_core.tld]. Dans ce fichier, il trouve galement les classes capable de traiter la balise <f:view> 1.
<tag> 2. 3. 4. 5. 6. 7. 8. 9. .... 10. </tag> <description> Container for all JavaServer Faces core and custom component actions used on a page. </description> <name>view</name> <tag-class>com.sun.faces.taglib.jsf_core.ViewTag</tag-class> <tei-class>com.sun.faces.taglib.FacesTagExtraInfo</tei-class> <body-content>JSP</body-content>

lignes 1, 10 : chaque balise JSF fait l'objet d'une dfinition l'intrieur d'une balise <tag> ligne 5 : nom de la balise lignes 6-7 : les classes capables de grer la balise. Ces classes sont recherches dans le Classpath de l'application. Elles seront trouves, l encore, dans l'archive [jsf-impl.jar] :

Les prfixes dfinis pour les balises du framework JSF (cf ci-dessous) sont libres. Les prfixes f et h sont des valeurs qu'on retrouve dans la plupart des pages JSF et il est prfrable de les garder.
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

21.1.5

Tutoriels Netbeans

Il existe de nombreux tutoriels Java pour Netbeans et notamment pour le dveloppement web. Pour y accder, on procdera ainsi : 1 2 3

dans l'option [Help] [1], prendre l'option [2] qui va ouvrir un navigateur et charger la page d'accueil de l'aide Netbeans en [3], un ensemble de liens. On suivra le lien [Java EE & Java Web Applications].

Les tutoriels Netbeans sont de prcieux outils. Ils sont courts, une vingtaine de minutes souvent, et rsolument pratiques. L'utilisateur est constamment guid dans l'criture du code Java et dans l'utilisation de Netbeans.

http://tahe.developpez.com/java/javaee

233/341

21.2

Exemple n 2

Mots cls : gestionnaire d'vnements, internationalisation, navigation entre pages.

21.2.1

L'application

L'application est la suivante : 4 1 2 5

en [1], la page d'accueil vue prcdemment [1] a t agrmente de liens en [2], deux liens pour changer la langue des pages de l'application en [3], un lien de navigation vers une autre page lorsqu'on clique sur [3], la page [4] est affiche le lien [5] permet de revenir la page d'accueil 1 2 3

sur la page d'accueil [1], les liens [2] permettent de changer de langue en [3], la page d'accueil en anglais

21.2.2

Le projet Netbeans

On gnrera un nouveau projet Jsf comme expliqu au paragraphe 21.1.1, page 225. On le nommera intro-02 :

http://tahe.developpez.com/java/javaee

234/341

4 5

en [1], le projet gnr en [2], le projet final. Il comportera deux pages JSF [3], des fichiers de messages pour internationaliser l'application [4] et du code Java [5] pour grer certains vnements de la page [welcomeJSF].

21.2.3
21.2.3.1

Les pages Jsf du projet


[welcomeJSF.jsp]

Le nouveau fichier [welcomeJSF.jsp] envoie la page suivante au navigateur client :

Le code qui produit cette page est le suivant :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. 7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. 10. <f:view> 11. <html> 12. <head> 13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14. <title><h:outputText value="#{msg['welcome.titre']}" /></title> 15. </head> 16. <body> 17. <h:form id="formulaire"> 18. <h:panelGrid columns="2"> 19. <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> 20. <h:commandLink value="#{msg['welcome.langue2']}" action="#{locale.setEnglishLocale}"/> 21. </h:panelGrid> 22. <h1><h:outputText value="#{msg['welcome.titre']}" /></h1> 23. <h:commandLink value="#{msg['welcome.page1']}" action="page1"/> 24. </h:form> 25. </body>

http://tahe.developpez.com/java/javaee

235/341

26. </html> 27. </f:view>

ligne 10 : la balise <f:view> sert dlimiter le code que le moteur Jsf doit traiter. En ligne 14, une expression de la forme #{expression} doit tre value par le moteur Jsf. Aussi a-t-on remont la balise <f:view> afin qu'elle englobe l'ensemble du code de la page Jsp. ligne 14 : la balise <h:outputText> affiche la valeur d'une expression, #{msg['welcome.titre']}. La syntaxe #{expression} est standard. La forme de expression peut tre diverse. Nous l'exprimerons le plus souvent sous la forme bean['cl'] ou bean.champ. bean doit tre un objet dclar dans le fichier de configuration [faces-config.xml]. Celui-ci doit donc dfinir l'objet msg utilis ligne 14 :

1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. ... 13. </faces-config>

lignes 4-11 : la balise <application> sert configurer l'application Jsf lignes 5-10 : la balise <resource-bundle> sert dfinir des ressources pour l'application, ici un fichier de messages. lignes 6-8 : la balise <base-name> dfinit le nom du fichier de messages. ligne 7 : le fichier s'appellera messages[_CodeLangue][_CodePays].properties. La balise <base-name> ne dfinit que la premire partie du nom. Le reste est implicite. Il peut exister plusieurs fichiers de messages, un par langue : 1 2 3

en [1], on voit quatre fichiers de messages correspondant au nom de base messages dfini dans [faces-config.xml]. messages_fr.properties : contient les messages en franais (code fr) messages_en.properties : contient les messages en anglais (code en) messages_es_ES.properties : contient les messages en espagnol (code es) de l'Espagne (code ES). Il existe d'autres types d'espagnol, par exemple celui de Bolivie (es_BO) messages.properties : est utilis par le serveur lorsque la langue de la machine sur laquelle il s'excute n'a aucun fichier de messages qui lui est associ. Il serait utilis par exemple, si l'application s'excutait sur une machine en Allemagne o la langue par dfaut serait l'allemand (de). Comme il n'existe pas de fichier [messages_de.properties], l'application utiliserait le fichier [messages.properties]. en [2] : les codes des langues font l'objet d'un standard international. en [3] : idem pour les codes des pays.

Dans notre exemple, le fichier de messages en franais [messages_fr.properties] contiendra les lments suivants :
1. 2. 3. 4. 5. 6. 7. welcome.titre=Tutoriel JSF (JavaServer Faces) welcome.langue1=Franais welcome.langue2=Anglais welcome.page1=Page 1 page1.titre=page1 page1.entete=Page 1 page1.welcome=Page d'accueil

http://tahe.developpez.com/java/javaee

236/341

Revenons au fichier [faces-config.xml] qui dclare le fichier des messages :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. ... 13. </faces-config>

La ligne 9 indique qu'une ligne du fichier des messages sera rfrence par l'identificateur msg dans les pages Jsf. Cet identificateur est utilis dans le fichier [welcomeJSF.jsp] tudi :
1. <f:view> 2. <html> 3. <head> 4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5. <title><h:outputText value="#{msg['welcome.titre']}" /></title> 6. </head> 7. <body> 8. ... 9. </body> 10. </html> 11. </f:view>

La balise <h:outputText> de la ligne 5, va afficher la valeur du message (prsence de l'identificateur msg) de cl welcome.titre. Ce message est cherch et trouv dans le fichier [messages.properties] de la langue active du moment. Par exemple, pour le franais :
welcome.titre=Tutoriel JSF (JavaServer Faces)

Un message est de la forme cl=valeur. La ligne 5 du fichier [welcomeJSF.jsp] devient la suivante aprs valuation de l'expression #{msg['welcome.titre']} :
<title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>

Nous verrons que ce mcanisme des fichiers de messages permet de changer facilement la langue des pages d'un projet Jsf. On parle d'internationalisation du projet ou plus souvent de son abrviation i18n, parce que le mot internationalisation commence par i et finit par n et qu'il y a 18 lettres entre le i et le n. Continuons explorer le contenu du fichier [welcomeJSF.jsp] :
1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. 7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. 10. <f:view> 11. <html> 12. <head> 13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14. <title><h:outputText value="#{msg['welcome.titre']}" /></title> 15. </head> 16. <body> 17. <h:form id="formulaire"> 18. <h:panelGrid columns="2"> 19. <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> 20. <h:commandLink value="#{msg['welcome.langue2']}" action="#{locale.setEnglishLocale}"/> 21. </h:panelGrid> 22. <h1><h:outputText value="#{msg['welcome.titre']}" /></h1> 23. <h:commandLink value="#{msg['welcome.page1']}" action="page1"/> 24. </h:form>

http://tahe.developpez.com/java/javaee

237/341

25. </body> 26. </html> 27. </f:view>

lignes 17-24 : la balise <h:form> introduit un formulaire. Un formulaire est gnralement constitu de : balises de champs de saisie (texte, boutons radio, cases cocher, listes d'lments, ...) balises de validation du formulaire (boutons, liens). C'est via un bouton ou un lien que l'utilisateur envoie ses saisies au serveur qui les traitera. Toute balise Jsf peut tre identifie par un attribut id. Le plus souvent, on peut s'en passer et c'est ce qui a t fait dans la plupart des balises Jsf utilises ici. Nanmoins, cet attribut est utile dans certains cas. Ligne 17, le formulaire est identifi par l'id formulaire. Dans cet exemple, l'id du formulaire ne sera pas utilis et aurait pu tre omis. lignes 18-21 : la balise <h:panelGrid> dfinit ici un tableau Html deux colonnes. Elle donne naissance la balise Html <table>. le formulaire dispose de trois liens dclenchant son traitement, en lignes 19, 20 et 23. La balise <h:commandLink> a au moins deux attributs : value : le texte du lien action : soit une chane de caractres C, soit la rfrence d'une mthode qui aprs excution rend la chane de caractres C. Cette chane de caractres C doit tre dfinie dans les rgles de navigation du fichier [facesconfig.xml]. Elle permet au contrleur [Faces Servlet] de dterminer la page qui doit tre affiche, une fois que l'action dfinie par l'attribut action a t excute.

Examinons la mcanique du traitement des formulaires avec l'exemple du lien de la ligne 19 :


<h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/>

Tout d'abord, le fichier des messages est exploit pour remplacer l'expression #{msg['welcome.langue1']} par sa valeur. Aprs valuation, la balise devient :
<h:commandLink value="Franais" action="#{locale.setFrenchLocale}"/>

La traduction Html de cette balise Jsf va tre la suivante :


<a href="#" onclick="mojarra.jsfcljs(document.getElementById('formulaire'), {'formulaire:j_id_id21':'formulaire:j_id_id21'},'');return false">Franais</a></td>

ce qui donnera l'apparence visuelle qui suit :

On notera l'attribut onclick de la balise Html <a>. Lorsque l'utilisateur va cliquer sur le lien [Franais], du code Javascript va tre excut. Celui-ci est embarqu dans la page que le navigateur a reue et c'est le navigateur qui l'excute. Le code Javascript est largement utilis dans les technologies Jsf et Ajax (Asynchronous Javascript And Xml). Il a en gnral pour but d'amliorer l'ergonomie et la ractivit des applications web. Il est le plus souvent gnr de faon automatique par des outils logiciels et il n'est pas alors utile de le comprendre. Mais parfois un dveloppeur peut tre amen ajouter du code Javascript dans ses pages Jsf. La connaissance de Javascript est alors ncessaire. Il est inutile ici de comprendre le code Javascript gnr pour la balise Jsf <h:commandLink>. On peut cependant noter deux points : le code Javascript utilise l'identifiant formulaire que nous avons donn la balise Jsf <h:form> Jsf gnre des identifiants automatiques pour toutes les balises o l'attribut id n'a pas t dfini. On en voit un exemple ici : j_id_id21. Donner un identifiant clair aux balises permet de mieux comprendre le code Javascript gnr si cela devient ncessaire. C'est notamment le cas lorsque le dveloppeur doit lui-mme ajouter du code Javascript qui manipule les composants de la page. Il a alors besoin de connatre les identifiants id de ces composants. Que va-t-il se passer lorsque l'utilisateur va cliquer sur le lien [Franais] de la page ci-dessus ? Considrons l'architecture d'une application Jsf :

http://tahe.developpez.com/java/javaee

238/341

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Modles

Le contrleur [Faces Servlet] va recevoir la requte du navigateur client sous la forme HTTP suivante :
1. 2. 3. 4. 5. 6. 7. POST /intro-02/faces/welcomeJSF.jsp HTTP/1.1 Host: localhost:8080 ... Content-Type: application/x-www-form-urlencoded Content-Length: 1854 formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_id_id21=formulaire%3Aj_id_id21

lignes 1-2 : le navigateur demande l'Url [http://localhost:8080/intro-02/faces/welcomeJSF.jsp]. C'est toujours ainsi : les saisies faites dans un formulaire Jsf obtenu avec l'Url urlFormulaire sont envoyes cette mme url. Le navigateur a deux moyens pour envoyer les valeurs saisies : GET et POST. Avec la mthode GET, les valeurs saisies sont envoyes par le navigateur dans l'Url qui est demande. Ci-dessus, le navigateur aurait pu envoyer la premire ligne suivante :

GET /intro-02/faces/welcomeJSF.jsp?formulaire=formulaire&javax.faces.ViewState=...&formulaire %3Aj_id_id21=formulaire%3Aj_id_id21 HTTP/1.1

Avec la mthode POST utilise ici, le navigateur envoie au serveur les valeurs saisies au moyen de la ligne 7. ligne 4 : indique la forme d'encodage des valeurs du formulaire ligne 5 : indique la taille en octets de la ligne 7 ligne 6 : ligne vide qui indique la fin des enttes HTTP et le dbut des 1854 octets des valeurs du formulaire ligne 7 : les valeurs du formulaire sous la forme element1=valeur1&element2=valeur2& ..., la forme d'encodage dfinie par la ligne 4. Dans cette forme de codage, certains caractres sont remplacs par leur valeur hexadcimale. C'est le cas dans le dernier lment :
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_id_id21=formulaire%3Aj_id_id21

o %3A reprsente le caractre :. C'est donc la chane formulaire:j_id_id21=formulaire:j_id_id21 qui est envoye au serveur. On se rappelle peut-tre que nous avons dj rencontr l'identifiant j_id_id21 lorsque nous avons examin le code Html gnr pour la balise <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> Il avait t gnr de faon automatique par Jsf. Ce qui nous importe ici, c'est que la prsence de cet identifiant dans la chane des valeurs envoyes par le navigateur client permet Jsf de savoir que le lien [Franais] a t cliqu. Il va alors utiliser l'attribut action ci-dessus, pour dcider comment traiter la chane reue. L'attribut action="#{locale.setFrenchLocale}" indique Jsf que la requte du client doit tre traite par la mthode [setFrenchLocale] d'un objet appel locale. Cet objet, ou bean, doit tre dfini dans le fichiers [faces-config.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. <managed-bean>

http://tahe.developpez.com/java/javaee

239/341

13. <description> 14. change la langue des pages JSF 15. </description> 16. <managed-bean-name>locale</managed-bean-name> 17. <managed-bean-class>utils.ChangeLocale</managed-bean-class> 18. <managed-bean-scope>request</managed-bean-scope> 19. </managed-bean> 20. 21. <!-- navigation --> 22. ... 23. </faces-config>

lignes 4-11 : configuration dj explique, celle du fichier des messages de l'application lignes 12-19 : la balise <managed-bean> sert dfinir un objet manipul par les pages Jsf. On a l'habitude d'appeler ces objets des beans. lignes 13-15 : une description facultative du bean ligne 16 : le nom sous lequel le bean sera connu dans les pages Jsf. Rappelons la balise <h:commandLink> tudie :

<h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/>

ligne 17 : la classe qu'il faut instancier lorsqu'une page Jsf rclame le bean locale. Cette classe sera ajoute prochainement notre projet Netbeans [3] :

ligne 18 : la dure de vie du bean. Il y en a trois : request : la dure de vie du bean est celle du cycle demande navigateur / rponse serveur. Si pour traiter une nouvelle requte du mme navigateur ou d'un autre, ce bean est de nouveau ncessaire, il sera instanci de nouveau. session : la dure de vie du bean est celle de la session d'un client particulier. Le bean est cr initialement pour les besoins de l'une des requtes de ce client. Il restera ensuite en mmoire dans la session de ce client. Un tel bean mmorise en gnral des donnes propres un client donn. Il sera dtruit lorsque la session du client sera dtruite. application : la dure de vie du bean est celle de l'application elle-mme. Un bean avec cette dure de vie est le plus souvent partag par tous les clients de l'application. Il est en gnral initialis au dbut de l'application. Netbeans gnre par dfaut une dure de vie gale request, toujours utilisable, alors que les dures session et application ncessitent des conditions particulires. Nous verrons ultrieurement que le bean locale peut avoir une dure de vie gale application.

Revenons la requte du navigateur :

http://tahe.developpez.com/java/javaee

240/341

Application web
couche [web]
1

Faces Servlet JSP1 JSP2 JSPn


3

2a

2b

Gestionnaire d'vts

couche [metier]

couche [dao]

Modles

et la balise <h:commandLink> qui a gnr le lien [Franais] sur lequel ci-dessus on a cliqu : <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> Le contrleur va transmettre la requte du navigateur au gestionnaire d'vnements dfini par l'attribut action de la balise <h:commandLink>. Le gestionnaire d'vnements M rfrenc par l'attribut action d'une commande <h:commandLink> doit avoir la signature suivante : public String M();

il ne reoit aucun paramtre. Nous verrons qu'il peut nanmoins avoir accs la requte du client il doit rendre un rsultat C de type String. La chane C doit tre dclare dans les rgles de navigation du fichier [facesconfig.xml]. Dans l'architecture Jsf ci-dessus, le contrleur [Faces Servlet] utilisera la chane C rendue par le gestionnaire d'vnements et son fichier de configuration [faces-config.xml] pour dterminer quelle page Jsf, il doit envoyer en rponse au client [4].

On trouve dans le fichier [faces-config.xml] : la liste des gestionnaires d'vnements [2a] ainsi que les modles [3] de l'application. Ces lments sont des classes Java qui suivent la norme JavaBean : prsence d'un constructeur sans paramtres prsence de mthodes get / set pour chaque champ priv Ces beans sont tous dclars dans [faces-config.xml]. Un bean est souvent li une page Jsp particulire qui il sert la fois de modle et de gestionnaire d'vnements : les champs du bean servent de modle et ses mthodes de gestionnaires d'vnements. les rgles de navigation. Les gestionnaires d'vnements doivent rendre au contrleur [Faces Servlet] ci-dessus, un rsultat sous forme de chanes de caractres. Les rgles de navigation consistent dfinir pour tous les vnements de l'application, trois paramtres [jsp1, res1, jsp2] : jsp1 est la page Jsp dont un vnement a t gr res1 est l'une des chanes de caractres que peut rendre le gestionnaire de cet vnement jsp2 est la page Jsp qui doit tre renvoye l'utilisateur lorsque le gestionnaire d'vnements rend la valeur res1 Si l'ensemble des gestionnaires des vnements de la page jsp1 peut rendre N rsultats diffrents, alors on aura N rgles de navigation dans le fichier [faces-config.xml] pour la page jsp1. Dans la balise <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> le gestionnaire de l'vnement clic sur le lien [Franais] est la mthode [locale.setFrenchLocale] o locale est une instance de la classe [utils.ChangeLocale] suivante : 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. package utils; import java.util.Locale; import javax.faces.context.FacesContext; public class ChangeLocale { /** Creates a new instance of ChangeLocale */ public ChangeLocale() { }

http://tahe.developpez.com/java/javaee

241/341

11. 12. public String setFrenchLocale(){ 13. changeLocale(new Locale("fr")); 14. return null; 15. } 16. 17. public String setEnglishLocale(){ 18. changeLocale(new Locale("en")); 19. return null; 20. } 21. 22. private void changeLocale(Locale locale){ 23. FacesContext.getCurrentInstance().getViewRoot().setLocale(locale); 24. } 25. } La mthode setFrenchLocale a bien la signature des gestionnaires d'vnements. Rappelons-nous que le gestionnaire d'vnements doit traiter la requte du client. Puisqu'il ne reoit pas de paramtres, comment peut-il avoir accs celle-ci ? Il existe diverses faons de faire :

le bean B qui contient le gestionnaire d'vnements de la page Jsf P est aussi souvent celui qui contient le modle M de cette page. Cela signifie que le bean B contient des champs qui seront initialiss par les valeurs saisies dans la page P. Cela sera fait par le contrleur [Faces Servlet] avant que le gestionnaire d'vnements du bean B ne soit appel. Ce gestionnaire aura donc accs, via les champs du bean B auquel il appartient, aux valeurs saisies par le client dans le formulaire et pourra les traiter. la mthode statique [FacesContext.getCurrentInstance()] de type [FacesContext] donne accs au contexte d'excution de la requte Jsf courante qui est un objet de type [FacesContext]. Le contexte d'excution de la requte ainsi obtenu, permet d'avoir accs aux paramtres posts au serveur par le navigateur client avec la mthode suivante : Map FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap() Si les paramtres posts (POST) par le navigateur client sont les suivants :
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_id_id21=formulaire%3Aj_id_id21

la mthode getRequestParameterMap() rendra le dictionnaire suivant : cl formulaire javax.faces.ViewState formulaire:j_id_id21 Dans la balise <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> qu'attend-on du gestionnaire d'vnements locale.setFrenchLocale ? On veut qu'il fixe la langue utilise par l'application. Dans le jargon Java, on appelle cela " localiser " l'application. Cette localisation est obtenue partir du contexte d'excution de la requte, de la faon suivante :
FacesContext.getCurrentInstance().getViewRoot().setLocale(Locale locale);

valeur formulaire ... formulaire:j_id_id21

FacesContext FacesContext.getCurrentInstance() : donne le contexte d'excution Jsf courant UIViewRoot FacesContext.getCurrentInstance().getViewRoot() : donne le composant racine de l'arbre des composants Jsf de la page en cours de traitement. Une page Jsf est un ensemble de balises qui forment ensemble un arbre de balises. Dans la terminologie Jsf, on parle de composants et d'arbre de composants. void FacesContext.getCurrentInstance().getViewRoot().setLocale(Locale locale) : fixe la langue d'affichage de l'arbre des composants de la page courante.

Un objet de type java.util.Locale a un constructeur qui admet pour paramtre un type String reprsentant un code Langue et / ou un code Pays tels ceux qui ont t prsents lors de l'tude du fichier des messages [messages.properties], page 236.

http://tahe.developpez.com/java/javaee

242/341

Ceci expliqu, la mthode setFrenchLocale pourrait tre la suivante : 1. public String setFrenchLocale(){ 2. FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("fr")); 3. return null; 4.} Nous avons expliqu qu'un gestionnaire d'vnements devait rendre une chane de caractres C qui serait recherche dans les rgles de navigation du fichier [faces-config.xml] afin de trouver la page Jsf envoyer en rponse au navigateur client. Si la page renvoyer est la mme que celle en cours de traitement, le gestionnaire d'vnements peut se contenter de renvoyer la valeur null. C'est ce qui est fait ici ligne 3 : on veut renvoyer la mme page [welcomeJSF.jsp] mais dans une langue diffrente. Revenons l'architecture de traitement de la requte :

Application web
1

couche [web]

Faces Servlet
4

2a 3

2b

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Modles

Le gestionnaire d'vnements locale.setFrenchLocale a t excut et a rendu la valeur null au contrleur [Faces Servlet]. Celui-ci va donc rafficher la page [welcomeJSF.jsp] et son arbre de composants. Revoyons celui-ci :
1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. 7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. 10. <f:view> 11. <html> 12. <head> 13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14. <title><h:outputText value="#{msg['welcome.titre']}" /></title> 15. </head> 16. <body> 17. <h:form id="formulaire"> 18. <h:panelGrid columns="2"> 19. <h:commandLink value="#{msg['welcome.langue1']}" action="#{locale.setFrenchLocale}"/> 20. <h:commandLink value="#{msg['welcome.langue2']}" action="#{locale.setEnglishLocale}"/> 21. </h:panelGrid> 22. <h1><h:outputText value="#{msg['welcome.titre']}" /></h1> 23. <h:commandLink value="#{msg['welcome.page1']}" action="page1"/> 24. </h:form> 25. </body> 26. </html> 27. </f:view>

A chaque fois qu'une valeur de type #{msg['...']} est value, l'un des fichiers des messages [messages.properties] est utilis. Celui utilis est celui qui correspond la " localisation " de l'arbre des composants. Le gestionnaire d'vnements locale.setFrenchLocale dfinissant cette localisation fr, c'est le fichier [messages_fr.properties] qui sera utilis. Un clic sur le lien [Anglais] (ligne 20) changera la localisation en en (cf mthode locale.setEnglishLocale). Ce sera alors le fichier [messages_en.properties] qui sera utilis et la page apparatra en anglais :

http://tahe.developpez.com/java/javaee

243/341

Pour une raison qui n'apparat pas clairement ici, cette localisation faite sur une page est conserve pour les pages suivantes. Il nous reste un dernier lment de la page [welcomeJSF.jsp] tudier, la line 23 du code :
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>

On a de nouveau une balise <h:commandLink> avec un attribut action gal une chane de caractres. Dans ce cas, aucun gestionnaire d'vnements n'est appel pour traiter la page. On passe tout de suite la page dsigne par la rgle de navigation suivante :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. ... 6. </application> 7. <managed-bean> 8. ... 9. </managed-bean> 10. 11. <!-- navigation --> 12. <navigation-rule> 13. <description> 14. 15. </description> 16. <from-view-id>/welcomeJSF.jsp</from-view-id> 17. <navigation-case> 18. <from-outcome>page1</from-outcome> 19. <to-view-id>/page1.jsp</to-view-id> 20. </navigation-case> 21. </navigation-rule> 22. ... 23. </faces-config>

lignes 12-21 : une rgle de navigation ligne 16 : la vue de dpart lignes 17-19 : une rgle de navigation pour la vue de dpart de la ligne 16 ligne 18 : la cl de navigation celle-ci peut tre le rsultat d'un gestionnaire d'vnements ou directement la valeur d'un attribut action d'une balise <h:commandLink> ou <h:commandButton>. C'est le cas que nous tudions actuellement. ligne 19 : la page vers laquelle il faut naviguer lorsque, partir de la vue de la ligne 16 on obtient la cl de navigation de la ligne 18. pour la vue de la ligne 16, on peut avoir plusieurs cas de navigation. Chacun d'eux sera reprsent par une balise <navigation-case> l'intrieur de la balise <navigation-rule> des lignes 12-21.

Examinons le fonctionnement de l'application dans ce cas d'utilisation :

http://tahe.developpez.com/java/javaee

244/341

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Modles

L'utilisateur clique sur le lien [Page 1]. Le formulaire est post au contrleur [Faces Servlet]. Celui reconnat dans la requte qu'il reoit, le fait que le lien [Page 1] a t cliqu. Il examine la balise correspondante :
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>

Il n'y a pas de gestionnaire d'vnements associ au lien. Le contrleur [Faces Servlet] passe tout de suite l'tape [3] ci-dessus de navigation. Il exploite le fichier [faces-config.xml] et dcouvre qu'il doit afficher la page [/page1.jsp] :

21.2.3.2

[page1.jsp]

La fichier [page1.jsp] envoie la page suivante au navigateur client :

Le code qui produit cette page est le suivant :


1. 2. 3. 4. 5. 6. <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

http://tahe.developpez.com/java/javaee

245/341

7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. 10. <f:view> 11. <html> 12. <head> 13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14. <title><h:outputText value="#{msg['page1.titre']}"/></title> 15. </head> 16. <body> 17. <h1><h:outputText value="#{msg['page1.entete']}"/></h1> 18. <h:form> 19. <h:commandLink value="#{msg['page1.welcome']}" action="welcome"/> 20. </h:form> 21. </body> 22. </html> 23. </f:view>

Il n'y a dans cette page rien qui n'ait dj t expliqu. Le lecteur fera la correspondance entre le code Jsf et la page envoye au navigateur client. Le lien de retour la page d'accueil :
<h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>

ncessite une rgle de navigation qui est ajoute au fichier de configuration [faces-config.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. ... 5. <navigation-rule> 6. <description> 7. 8. </description> 9. <from-view-id>/page1.jsp</from-view-id> 10. <navigation-case> 11. <from-outcome>welcome</from-outcome> 12. <to-view-id>/welcomeJSF.jsp</to-view-id> 13. </navigation-case> 14. </navigation-rule> 15. 16. </faces-config>

lignes 5-14 : les rgles de navigation partir de la page /page1.jsp dclare ligne 9. lignes 10-13 : lorsqu' l'issue du traitement d'un vnement de la page [/page1.jsp], le contrleur [Faces Servlet] reoit la chane de caractres welcome (ligne 11), il affiche la page [/welcomeJSF.jsp] (ligne 12).

21.2.4

Le fichier des messages du projet

Rappelons comment est dclar le fichier des messages du projet Jsf dans [faces-config.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. ... 13. </faces-config>

Le nom du fichier des messages est dfini ligne 7. Il sera cherch dans le classpath du projet. S'il est l'intrieur d'un paquetage, celuici doit tre dfini ligne 7, par exemple ressources.messages, si le fichier [messages.properties] se trouve dans le dossier [ressources] du classpath. Le nom, ligne 7, ne comportant pas de paquetage, le fichier [messages.properties] doit tre plac la racine du dossier des codes source :

http://tahe.developpez.com/java/javaee

246/341

En [1], dans l'onglet [Projects] du projet Netbeans, le fichier [messages.properties] est prsent comme une liste des diffrentes versions de messages dfinies. Les versions sont identifies par une suite d'un trois codes [codeLangue_codePays_codeVariante]. En [1], seul le code [codeLangue] a t utilis : en pour l'anglais, fr pour le franais. Chaque version fait l'objet d'un fichier spar dans le systme de fichiers [2]. Pour notre projet, nous construirons trois fichiers de messages avec les contenus suivants : [messages_fr.properties]
welcome.titre=Tutoriel JSF (JavaServer Faces) welcome.langue1=Fran9ais welcome.langue2=Anglais welcome.page1=Page 1 page1.titre=page1 page1.entete=Page 1 page1.welcome=Page d'accueil

[messages_en.properties]
welcome.titre=JSF (JavaServer Faces) Tutorial welcome.langue1=French welcome.langue2=English welcome.page1=Page 1 page1.titre=page1 page1.entete=Page 1 page1.welcome=Welcome page

Le fichier [messages.properties] aura le contenu du fichier [messages_fr.properties].

21.2.5

Les classes Java du projet

Notre projet ne contient qu'une classe, celle qui traite les vnements " clic " sur les liens [Franais] et [Anglais] :

1 2

Dans la branche [Source Packages], la classe [ChangeLocale] a t place dans le paquetage [utils]. Cette classe a dj t dcrite page 241.

21.2.6

Le fichier de configuration [faces-config.xml]

Nous avons dcrit les diffrentes lments du fichier de configuration du projet Jsf [faces-config.xml]. Son code est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>

http://tahe.developpez.com/java/javaee

247/341

2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. <managed-bean> 13. <description> 14. change la langue des pages JSF 15. </description> 16. <managed-bean-name>locale</managed-bean-name> 17. <managed-bean-class>utils.ChangeLocale</managed-bean-class> 18. <managed-bean-scope>application</managed-bean-scope> 19. </managed-bean> 20. 21. <!-- navigation --> 22. <navigation-rule> 23. <description> 24. 25. </description> 26. <from-view-id>/welcomeJSF.jsp</from-view-id> 27. <navigation-case> 28. <from-outcome>page1</from-outcome> 29. <to-view-id>/page1.jsp</to-view-id> 30. </navigation-case> 31. </navigation-rule> 32. 33. <navigation-rule> 34. <description> 35. 36. </description> 37. <from-view-id>/page1.jsp</from-view-id> 38. <navigation-case> 39. <from-outcome>welcome</from-outcome> 40. <to-view-id>/welcomeJSF.jsp</to-view-id> 41. </navigation-case> 42. </navigation-rule> 43. 44. 45. </faces-config>

Le contenu de ce fichier a dj t dcrit. Notons simplement un changement sur la dure de vie (ligne 18) du bean locale dfini lignes 12-19. Nous avons indiqu qu'il y avait trois dures de vie possibles : request, session, application et nous avons expliqu ce que chacune d'elles recouvrait. La valeur par dfaut est request, une valeur qui convient tous les beans. Si on examine le code de la classe [utils.ChangeLocale] page 241, on verra que rien ne s'oppose ce que le bean ait une dure de vie gale application. En effet, il ne contient aucun champ priv, ce qui en fait un bean sans tat. Un tel bean peut alors tre partag par toutes les requtes de tous les clients sans risque de conflit entre elles. Netbeans offre des facilits pour construire le fichier [faces-config.xml] que nous examinons maintenant. Il est cr vide lors de la cration initiale du projet. La dfinition du fichier des messages est faite la main avec l'diteur de texte de Netbeans :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. </faces-config>

L'ajout des beans " manags ", ces classes Java qui vont servir de modles aux pages Jsf et traiter leurs vnements, peut tre fait avec l'assistance de Netbeans :

http://tahe.developpez.com/java/javaee

248/341

2 3

Dans le code source de [faces-config.xml] en [1], on clique droit pour prendre l'option |Insert] [2], puis l'option [Managed Bean] [3] :

3 2 4 5 1

utiliser [1] pour indiquer le bean de la classe. L'assistant est peu performant ici et il faut taper les premires lettres du nom de la classe pour qu'elle soit propose la slection. Il peut tre plus rapide de taper directement son nom en [2]. en [3], donner un nom au nouveau bean en [4], prciser sa dure de vie en [5], donner une description falcutative

Ceci fait, le fichier [faces-config.xml] est mis jour :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. <managed-bean> 13. <description> 14. sert changer la langue 15. des pages 16. </description> 17. <managed-bean-name>locale</managed-bean-name> 18. <managed-bean-class>utils.ChangeLocale</managed-bean-class> 19. <managed-bean-scope>application</managed-bean-scope> 20. </managed-bean> 21. </faces-config>

Netbeans offre galement un assistant pour l'ajout des rgles de navigation. Cela se fait en deux tapes :

ajout d'une rgle de navigation qui consiste dfinir la page pour laquelle on va dfinir des cas de navigation. pour cette page, dfinir tous les cas de navigation possibles

http://tahe.developpez.com/java/javaee

249/341

Dfinissons les cas de navigation pour la page [welcomeJSF.jsp]. On dfinit d'abord la page pour laquelle on va dfinir ces cas :

5 6 1 4

dans le code de [faces-config.xml], cliquer droit pour choisir l'option [Insert] [2] puis l'option [Navigation Rule] [3] utiliser le bouton [4] pour dsigner la page pour laquelle on va dcrire les cas de navigation. On peut galement taper son nom en [5]. En [6], une description facultative de la rgle de navigation.

Ceci fait, le fichier [faces-config.xml] est mis jour :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. <managed-bean> 13. <description> 14. sert changer la langue 15. des pages 16. </description> 17. <managed-bean-name>locale</managed-bean-name> 18. <managed-bean-class>utils.ChangeLocale</managed-bean-class> 19. <managed-bean-scope>application</managed-bean-scope> 20. </managed-bean> 21. <navigation-rule> 22. <description> 23. page d'accueil 24. </description> 25. <from-view-id>/welcomeJSF.jsp</from-view-id> 26. </navigation-rule> 27. </faces-config>

Les cas de navigation pour la page [welcomeJSF.jsp] sont ajouts de la faon suivante :

3 6 7

4 5

8 9

http://tahe.developpez.com/java/javaee

250/341

en [1], cliquer droit sur le code de la rgle de navigation choisir [Insert] en [2], puis [Add Navigation Case] en [3] en [4] doit apparatre la page sur la rgle de navigation de laquelle on a cliqu en [1]. Sinon utiliser le bouton [5] pour la dsigner. en [7], la page vers laquelle on veut naviguer. On peut la slectionner avec le bouton [8]. en [6], le code de transition de la page [/welcomeJSF.jsp] vers la page [/page1.jsp]. Ce code est obtenu par le contrleur [Faces Servlet] de deux faons : l'vnement de la page [welcomeJSF.jsp] trait est celui d'un clic sur un bouton ou un lien de la page [welcomeJSF.jsp] qui avait un attribut action="page1" l'vnement de la page [welcomeJSF.jsp] a t trait par un gestionnaire d'vnements qui a rendu la cl page1. en [9], une description facultative.

Ceci fait, le fichier [faces-config.xml] est de nouveau mis jour :


1. <navigation-rule> 2. <description> 3. page d'accueil 4. </description> 5. <from-view-id>/welcomeJSF.jsp</from-view-id> 6. <navigation-case> 7. <from-outcome>page1</from-outcome> 8. <to-view-id>/page1.jsp</to-view-id> 9. </navigation-case> 10.</navigation-rule>

On fait de mme pour les cas de navigation partir de la page [/page1.jsp] :


1. <navigation-rule> 2. <from-view-id>/page1.jsp</from-view-id> 3. <navigation-case> 4. <from-outcome>welcome</from-outcome> 5. <to-view-id>/welcomeJSF.jsp</to-view-id> 6. </navigation-case> 7.</navigation-rule>

21.2.7

Excution du projet

Notre projet est dsormais complet. Nous pouvons le construire (Clean and Build) :

5 6

dans le dossier [dist] [1] est place l'archive [intro-02.war] [2] du projet. C'est cette archive qui est dploye sur le serveur.

http://tahe.developpez.com/java/javaee

251/341

dans [WEB-INF / classes], on trouve les classes compiles du dossier [Source Packages] du projet ainsi que les autres fichiers qui se trouvaient dans ce mme dossier, ici les fichiers de messages. dans [WEB-INF / lib] [4], on trouve les bibliothques du projet la racine de [WEB-INF] [5], on trouve les fichiers de configuration du projet la racine de l'archive [intro-02.war] [6], on trouve les pages JSF qui taient dans la branche [Web Pages] du projet une fois le projet construit, il peut tre excut [7]. Il va tre excut selon sa configuration d'excution [8]. le serveur Glassfish va tre lanc s'il n'tait pas dj lanc l'archive [intro-02.war] va tre charge sur le serveur. On appelle cela le dploiement du projet sur le serveur d'application. l'application va tre excute selon les proprits de celle-ci :

1 2 8 3

en [1], le serveur sur lequel a t dploye l'application Jsf en [2], le nom de cette application ou contexte en [3], il est demand de lancer un navigateur l'excution. Celui-ci va demander le contexte de l'application [2], c.a.d. l'Url [http://localhost:8080/intro-02]. D'aprs les rgles du fichier [web.xml] (cf page 227), c'est le fichier [faces/welcomeJSF.jsp] qui va tre servi au navigateur client. Puisque l'url est de la forme [/faces/*], elle va tre traite par le contrleur [Faces Servlet] (cf [web.xml] page 227). Celui-ci va traiter la page et envoyer le flux Html suivant :

le contrleur [Faces Servlet] traitera les vnements qui vont se produire partir de cette page.

21.2.8

Conclusion

Revenons sur le projet Netbeans que nous avons crit :

http://tahe.developpez.com/java/javaee

252/341

D A

Ce projet recouvre l'architecture suivante :

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

Dans chaque projet Jsf, nous trouverons les lments suivants : des pages Jsf [A] qui sont envoyes [4] aux navigateurs clients par le contrleur [Faces Servlet] [3] des fichiers de messages [B] qui permettent de changer la langue des pages Jsf des classes Java [C] qui traitent les vnements qui se produisent sur le navigateur client [2a, 2b] et / ou qui servent de modles aux pages Jsf [3]. Le plus souvent, les couches [metier] et [dao] sont dveloppes et testes sparment. La couche [web] est alors teste avec une couche [metier] fictive. Si les couches [metier] et [dao] sont disponibles, on travaille le plus souvent avec leurs archives .jar. des fichiers de configuration [D] pour lier ces divers lments entre-eux. Le fichier [web.xml] a t dcrit page 227, et sera peu souvent modifi. Le principal fichier de configuration est [faces-config.xml] qui dfinit : le fichier des messages les beans (classes Java) qui servent de modles aux pages Jsf ou traitent leurs vnements les rgles de navigation entre pages Jsf

21.3

Exemple n 3

Mots cls : formulaire de saisie composants Jsf

21.3.1

L'application

L'application a une unique page :

http://tahe.developpez.com/java/javaee

253/341

2 3

L'application prsente les principaux composants Jsf utilisables dans un formulaire de saisies : la colonne [1] indique le nom de la balise Jsf / Html utilise la colonne [2] prsente un exemple de saisie pour chacune des balises rencontres la colonne [3] affiche les valeurs du bean servant de modle la page les saisies faites en [2] sont valides par le bouton [4]. Cette validation ne fait que mettre jour le bean modle de la page. La mme page est ensuite renvoye. Aussi aprs validation, la colonne [3] prsente-t-elle les nouvelles valeurs du bean modle permettant ainsi l'utilisateur de vrifier l'impact de ses saisies sur le modle de la page.

21.3.2

Le projet Netbeans

Le projet Netbeans de l'application est le suivant :

http://tahe.developpez.com/java/javaee

254/341

1 2 3 4

en [1], les fichiers de configuration du projet Jsf. en [2], l'unique page Jsp du projet : form.jsp en [3], une feuille de style [styles.css] pour configurer l'aspect de la page [form.jsp] en [4], les classes Java du projet. en [5], le fichier des messages de l'application en deux langues : franais et anglais.

21.3.3

Le fichier [web.xml]

Le fichier [web.xml] a t configur pour que la page [form.jsp] soit la page d'accueil du projet :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <servlet> 4. <servlet-name>Faces Servlet</servlet-name> 5. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 6. <load-on-startup>1</load-on-startup> 7. </servlet> 8. <servlet-mapping> 9. <servlet-name>Faces Servlet</servlet-name> 10. <url-pattern>/faces/*</url-pattern> 11. </servlet-mapping> 12. <session-config> 13. <session-timeout> 14. 30 15. </session-timeout> 16. </session-config> 17. <welcome-file-list> 18. <welcome-file>faces/form.jsp</welcome-file> 19. </welcome-file-list> 20. </web-app>

21.3.4

Le fichier [faces-config.xml]

Le fichier [faces-config.xml] de l'application est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <managed-bean> 5. <managed-bean-name>form</managed-bean-name> 6. <managed-bean-class>forms.Form</managed-bean-class> 7. <managed-bean-scope>request</managed-bean-scope> 8. </managed-bean> 9. <managed-bean> 10. <managed-bean-name>locale</managed-bean-name> 11. <managed-bean-class>utils.ChangeLocale</managed-bean-class>

http://tahe.developpez.com/java/javaee

255/341

12. <managed-bean-scope>application</managed-bean-scope> 13. </managed-bean> 14. <application> 15. <resource-bundle> 16. <base-name> 17. messages 18. </base-name> 19. <var>msg</var> 20. </resource-bundle> 21. </application> 22. </faces-config>

lignes 14-22 : le fichier des messages de l'application lignes 9-13 : le bean locale qui permet de changer la langue des pages. Ce bean est celui de l'application prcdente. lignes 4-9 : le bean form qui va servir de modle la page Jsf form.jsp. L'application se consacre essentiellement l'tude des liens qui unissent une page son bean modle.

21.3.5

Le fichier des messages [messages.properties]

Les fichiers des messages (cf [5] dans la copie d'cran du projet) sont les suivants : [messages_fr.properties]
1. form.langue1=Franais 2. form.langue2=Anglais 3. form.titre=Java Server Faces - les tags 4. form.headerCol1=Type 5. form.headerCol2=Champs de saisie 6. form.headerCol3=Valeurs du modle de la page 7. form.loginPrompt=login : 8. form.passwdPrompt=mot de passe : 9. form.descPrompt=description : 10. form.selectOneListBox1Prompt=choix unique : 11. form.selectOneListBox2Prompt=choix unique : 12. form.selectManyListBoxPrompt=choix multiple : 13. form.selectOneMenuPrompt=choix unique : 14. form.selectManyMenuPrompt=choix multiple : 15. form.selectBooleanCheckboxPrompt=mari(e) : 16. form.selectManyCheckboxPrompt=couleurs prfres : 17. form.selectOneRadioPrompt=moyen de transport prfr : 18. form.submitText=Valider 19. form.buttonRazText=Raz

Ces messages sont affichs aux endroits suivants de la page :

http://tahe.developpez.com/java/javaee

256/341

2 3 4 7 8 9 11 6 5

12 19 13 14 19 15 16 17

18 La version anglaise des messages est la suivante : [messages_en.properties]


1. form.langue1=French 2. form.langue2=English 3. form.titre=Java Server Faces - the tags 4. form.headerCol1=Input Type 5. form.headerCol2=Input Fields 6. form.headerCol3=Page Model Values 7. form.loginPrompt=login : 8. form.passwdPrompt=password : 9. form.descPrompt=description : 10. form.selectOneListBox1Prompt=unique choice : 11. form.selectOneListBox2Prompt=unique choice : 12. form.selectManyListBoxPrompt=multiple choice : 13. form.selectOneMenuPrompt=unique choice : 14. form.selectManyMenuPrompt=multiple choice : 15. form.selectBooleanCheckboxPrompt=married : 16. form.selectManyCheckboxPrompt=preferred colors : 17. form.selectOneRadioPrompt=preferred transport means : 18. form.submitText=Submit 19. form.buttonRazText=Reset

21.3.6

Le modle [Form.java] de la page [form.jsp]

http://tahe.developpez.com/java/javaee

257/341

Dans le projet ci-dessus, la classe [Form.java] va servir de modle ou backing bean la page Jsf [form.jsp]. Illustrons cette notion de modle avec un exemple tir de la page [form.jsp] :
1. <!-- ligne 2 --> 2. <h:outputText value="inputText" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.loginPrompt']}"/> 5. <h:inputText id="inputText" value="#{form.inputText}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputText}"/>

A la demande initiale de la page [form.jsp], le code ci-dessus gnre la ligne 2 du tableau des saisies :

La ligne 2 affiche la zone [1], les lignes 3-6 : la zone [2], la ligne 7 : la zone [3]. Les lignes 5 et 7 utilisent une expression faisant intervenir le bean form dfini dans le fichier [faces-config.xml] de la faon suivante :
1. <managed-bean> 2. <managed-bean-name>form</managed-bean-name> 3. <managed-bean-class>forms.Form</managed-bean-class> 4. <managed-bean-scope>request</managed-bean-scope> 5.</managed-bean>

Le bean nomm form est une instance de la classe forms.Form que nous allons dcouvrir bientt et sa dure de vie est celle de la requte. Cela signifie que dans un cycle demande client / rponse serveur, il est instanci lorsque la requte en a besoin et supprim lorsque la rponse au client a t rendue. Dans le code ci-dessous de la page [form.jsp] :
1. <!-- ligne 2 --> 2. <h:outputText value="inputText" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.loginPrompt']}"/> 5. <h:inputText id="inputText" value="#{form.inputText}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputText}"/>

les lignes 5 et 7 utilisent la valeur inputText du bean form. Pour comprendre les liens qui unissent une page P son modle M, il faut revenir au cycle demande client / rponse serveur qui caractrise une application web :

http://tahe.developpez.com/java/javaee

258/341

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

Il faut distinguer le cas o la page P est envoye en rponse au navigateur (tape 4), par exemple lors de la demande initiale de la page, du cas o l'utilisateur ayant provoqu un vnement sur la page P, celui-ci est trait par le contrleur [Faces Servlet] (tape 1). On peut distinguer ces deux cas en les regardant du point de vue du navigateur : 1. 2. lors de la demande initiale de la page, le navigateur fait une opration GET sur l'Url de la page lors de la soumission des valeurs saisies dans la page, le navigateur fait une opration POST sur l'Url de la page

Dans les deux cas, c'est la mme Url qui est demande. Selon la nature de la demande GET ou POST du navigateur, le traitement de la requte va diffrer. [cas 1 demande initiale de la page P] Le navigateur demande l'Url de la page avec un GET. Le contrleur [Faces Servlet] va passer directement l'tape [4] de rendu de la rponse et la page [form.jsp] va tre envoye au client. Le contrleur Jsf va demander chaque balise de la page de s'afficher. Prenons l'exemple de la ligne 5 du code de [form.jsp] :
<h:inputText id="inputText" value="#{form.inputText}"/>

La balise Jsf <h:inputText value= "valeur "/> donne naissance la balise Html <input type= "text " value= "valeur "/>. La classe charge de traiter cette balise rencontre l'expression #{form.inputText} qu'elle doit valuer : si le bean form n'existe pas encore, il est cr par instanciation de la classe forms.Form, comme indiqu dans [facesconfig.xml]. l'expression #{form.inputText} est value par appel la mthode form.getInputText(). le texte <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /> est insr dans le flux Html qui va tre envoy au client si on imagine que la mthode form.getInputText() a rendu la chane "texte". Jsf va par ailleurs donner un nom (name) au composant Html mis dans le flux. Ce nom est construit partir des identifiants id du composant Jsf analys et ceux de ses composants parents, ici la balise <h:form id= "formulaire "/>. On retiendra que si dans une page P, on utilise l'expression #{M.champ} o M est le bean modle de la page P, celui-ci doit disposer de la mthode publique getChamp(). Le type rendu par cette mthode doit pouvoir tre converti en type String. Un modle M possible et frquent est le suivant :
1. private T champ; 2. public T getChamp(){ 3. return champ; 4. }

o T est un type qui peut tre converti en type String, ventuellement avec une mthode toString. Toujours dans le cas de l'affichage de la page P, le traitement de la ligne :
<h:outputText value="#{form.inputText}"/>

sera analogue et le flux Html suivant sera cr :


texte

De faon interne au serveur, la page P est reprsente comme un arbre de composants, image de l'arbre des balises de la page envoye au client. Nous appellerons vue ou tat de la page, cet arbre. Cet tat est mmoris. Il peut l'tre de deux faons selon une configuration faite dans le fichier [web.xml] de l'application :

http://tahe.developpez.com/java/javaee

259/341

1. <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/webapp_2_5.xsd"> 2. ... 3. <context-param> 4. <param-name>javax.faces.STATE_SAVING_METHOD</param-name> 5. <param-value>client</param-value> 6. </context-param> 7. <servlet> 8. <servlet-name>Faces Servlet</servlet-name> 9. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 10. <load-on-startup>1</load-on-startup> 11. </servlet> 12. ... 13. </web-app>

Les lignes 7-11 dfinissent le contrleur [Faces Servlet]. Celui-ci peut tre configur par diffrentes balises <context-param> dont celle des lignes 3-6 qui indique que l'tat d'une page doit tre sauvegard sur le client (le navigateur). L'autre valeur possible, ligne 5, est server pour indiquer une sauvegarde sur le serveur. Lorsque l'tat d'une page est sauvegard sur le client, le contrleur Jsf ajoute chaque page Html qu'il envoie, un champ cach dont la valeur est l'tat actuel de la page. Ce champ cach a la forme suivante :
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />

Sa valeur reprsente sous forme code, l'tat de la page envoye au client. Ce qu'il est important de comprendre, c'est que ce champ cach fait partie du formulaire de la page et fera donc partie des valeurs postes par le navigateur lors de la validation du formulaire. A partir de ce champ cach, le contrleur Jsf est capable de restaurer la vue telle qu'elle a t envoye au client. Lorsque l'tat d'une page est sauvegard sur le serveur, l'tat de la page envoye au client est sauvegard dans la session de celui-ci. Lorsque le navigateur client va poster les valeurs saisies dans le formulaire, il va envoyer galement son jeton de session. A partir de celui-ci, le contrleur Jsf retrouvera l'tat de la page envoye au client et la restaurera. [cas 2 traitement de la page P]

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

On est l'tape [1] ci-dessus o le contrleur [Faces Servlet] va recevoir une requte POST du navigateur client qui il a envoy prcdemment la page [form.jsp]. On est en prsence du traitement d'un vnement de la page. Plusieurs tapes vont se drouler avant mme que l'vnement ne puisse tre trait en [2a]. Le cycle de traitement d'une requte POST par le contrleur Jsf est le suivant :

http://tahe.developpez.com/java/javaee

260/341

en [A], grce au champ cach javax.faces.ViewState la vue initialement envoye au navigateur client est reconstitue. Ici, les composants de la page retrouvent la valeur qu'ils avaient dans la page envoye. Notre composant inputText retrouve sa valeur "texte". en [B], les valeurs postes par le navigateur client sont utilises pour mettre jour les composants de la vue. Ainsi si dans le champ de saisie html nomm inputText, l'utilisateur a tap "jean", la valeur "jean" remplace la valeur "texte". Dsormais la vue reflte la page telle que l'a modifie l'utilisateur et non plus telle qu'elle a t envoye au navigateur. en [C], les valeurs postes sont vrifies. Supposons que le composant inputText prcdent soit le champ de saisie d'un ge. Il faudra que la valeur saisie soit un nombre entier. Les valeurs postes par le navigateur sont toujours de type String. Leur type final dans le modle M associ la page P peut tre tout autre. Il y a alors conversion d'un type String vers un autre type T. Cette conversion peut chouer. Dans ce cas, le cycle demande / rponse est termin et la page P construite en [B] est renvoye au navigateur client avec des messages d'erreur si l'auteur de la page P les a prvus. On notera que l'utilisateur retrouve la page telle qu'il l'a saisie, sans effort de la part du dveloppeur. Dans une autre technologie, telle que Jsp, le dveloppeur doit reconstruire lui-mme la page P avec les valeurs saisies par l'utilisateur. La valeur d'un composant peut subir galement un processus de validation. Toujours avec l'exemple du composant inputText qui est le champ de saisie d'un ge, la valeur saisie devra tre non seulement un nombre entier mais un nombre entier compris dans un intervalle [1,N]. Si la valeur saisie passe l'tape de la conversion, elle peut ne pas passer l'tape de la validation. Dans ce cas, l galement le cycle demande / rponse est termin et la page P construite en [B] est renvoye au navigateur client. en [D], si tous les composants de la page P passent l'tape de conversion et de validation, leurs valeurs vont tre affectes au modle M de la page P. Si la valeur du champ de saisie gnr partir de la balise suivante :
<h:inputText value="#{form.inputText}"/>

est "jean", alors cette valeur sera affecte au modle form de la page par excution du code form.setInputText("jean"). On retiendra que dans le modle M de la page P, les champs privs de M qui mmorisent la valeur d'un champ de saisie de P doivent avoir une mthode set. une fois le modle M de la page P mis jour par les valeurs postes, l'vnement qui a provoqu le POST de la page P peut tre trait. C'est l'tape [E]. On notera que si le gestionnaire de cet vnement appartient au bean M, il a accs aux valeurs du formulaire P qui ont t stockes dans les champs de ce mme bean. l'tape [E] va rendre au contrleur Jsf, une cl de navigation. Cette cl sera recherche dans le fichier [faces-config.xml] et une page Jsf sera choisie pour tre envoye en rponse au navigateur client. C'est l'tape [F].

Nous retiendrons de ce qui prcde que :


une page P affiche les champs C de son modle M avec des mthodes [M].getC() les champs C du modle M d'une page P sont initialiss avec les valeurs saisies dans la page P l'aide des mthodes [M].setC(saisie). Dans cette tape, peuvent intervenir des processus de conversion et de validation susceptibles d'chouer. Dans ce cas, l'vnement qui a provoqu le POST de la page P n'est pas trait et la page est renvoye de nouveau au client telle que celui-ci l'a saisie.

Le modle [Form.java] de la page [form.jsp] sera le suivant :


1. package forms; 2. 3. public class Form { 4. 5. /** Creates a new instance of Form */ 6. public Form() { 7. } 8.

http://tahe.developpez.com/java/javaee

261/341

9. // champs du formulaire 10. private String inputText="texte"; 11. private String inputSecret="secret"; 12. private String inputTextArea="ligne1\nligne2\n"; 13. private String selectOneListBox1="2"; 14. private String selectOneListBox2="3"; 15. private String[] selectManyListBox=new String[]{"1","3"}; 16. private String selectOneMenu="1"; 17. private String[] selectManyMenu=new String[]{"1","2"}; 18. private String inputHidden="initial"; 19. private boolean selectBooleanCheckbox=true; 20. private String[] selectManyCheckbox=new String[]{"1","3"}; 21. private String selectOneRadio="2"; 22. 23. // vnements 24. public String submit(){ 25. return null; 26. } 27. 28. // getters et setters 29. ...

30.}

Les champs des lignes 10-21 sont utiliss aux endroits suivants du formulaire :

10 11 12 13 14 15

10 11 12 13 14 15

16 17

16 17 18

19 20 21 21 20

19

21.3.7

La page [form.jsp]

http://tahe.developpez.com/java/javaee

262/341

La page [form.jsp] qui gnre la vue prcdente est la suivante :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. 11. <f:view> 12. <html> 13. <head> 14. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 15. <title>JSF</title> 16. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 17. </head> 18. <body background="<c:url value="/ressources/standard.jpg"/>"> 19. <h:form id="formulaire"> 20. <h:panelGrid columns="2"> 21. <h:commandLink value="#{msg['form.langue1']}" action="#{locale.setFrenchLocale}"/> 22. <h:commandLink value="#{msg['form.langue2']}" action="#{locale.setEnglishLocale}"/> 23. </h:panelGrid> 24. <h1><h:outputText value="#{msg['form.titre']}"/></h1> 25. <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1"> 26. <!-- ligne 1 --> 27. <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/> 28. <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/> 29. <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/> 30. <!-- ligne 2 --> 31. <h:outputText value="inputText" styleClass="info"/> 32. <h:panelGroup> 33. <h:outputText value="#{msg['form.loginPrompt']}"/> 34. <h:inputText id="inputText" value="#{form.inputText}"/> 35. </h:panelGroup> 36. <h:outputText value="#{form.inputText}"/> 37. <!-- ligne 3 --> 38. <h:outputText value="inputSecret" styleClass="info"/> 39. <h:panelGroup> 40. <h:outputText value="#{msg['form.passwdPrompt']}"/> 41. <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/> 42. </h:panelGroup> 43. <h:outputText value="#{form.inputSecret}"/> 44. <!-- ligne 4 --> 45. <h:outputText value="inputTextArea" styleClass="info"/> 46. <h:panelGroup> 47. <h:outputText value="#{msg['form.descPrompt']}"/> 48. <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/> 49. </h:panelGroup> 50. <h:outputText value="#{form.inputTextArea}"/> 51. <!-- ligne 5 --> 52. <h:outputText value="selectOneListBox (size=1)" styleClass="info"/> 53. <h:panelGroup> 54. <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/> 55. <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> 56. <f:selectItem itemValue="1" itemLabel="un"/> 57. <f:selectItem itemValue="2" itemLabel="deux"/> 58. <f:selectItem itemValue="3" itemLabel="trois"/> 59. </h:selectOneListbox> 60. </h:panelGroup> 61. <h:outputText value="#{form.selectOneListBox1}"/> 62. <!-- ligne 6 --> 63. <h:outputText value="selectOneListBox (size=3)" styleClass="info"/> 64. <h:panelGroup> 65. <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/> 66. <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3"> 67. <f:selectItem itemValue="1" itemLabel="un"/> 68. <f:selectItem itemValue="2" itemLabel="deux"/> 69. <f:selectItem itemValue="3" itemLabel="trois"/> 70. <f:selectItem itemValue="4" itemLabel="quatre"/> 71. <f:selectItem itemValue="5" itemLabel="cinq"/> 72. </h:selectOneListbox> 73. </h:panelGroup> 74. <h:outputText value="#{form.selectOneListBox2}"/> 75. <!-- ligne 7 --> 76. <h:outputText value="selectManyListBox (size=3)" styleClass="info"/> 77. <h:panelGroup> 78. <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/> 79. <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3"> 80. <f:selectItem itemValue="1" itemLabel="un"/>

http://tahe.developpez.com/java/javaee

263/341

81. 82. 83. 84. 85. 86.

<f:selectItem itemValue="2" itemLabel="deux"/> <f:selectItem itemValue="3" itemLabel="trois"/> <f:selectItem itemValue="4" itemLabel="quatre"/> <f:selectItem itemValue="5" itemLabel="cinq"/> </h:selectManyListbox> <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}" />" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p> 87. </h:panelGroup> 88. <h:outputText value="#{form.selectManyListBoxValue}"/> 89. <!-- ligne 8 --> 90. <h:outputText value="selectOneMenu" styleClass="info"/> 91. <h:panelGroup> 92. <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/> 93. <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}"> 94. <f:selectItem itemValue="1" itemLabel="un"/> 95. <f:selectItem itemValue="2" itemLabel="deux"/> 96. <f:selectItem itemValue="3" itemLabel="trois"/> 97. <f:selectItem itemValue="4" itemLabel="quatre"/> 98. <f:selectItem itemValue="5" itemLabel="cinq"/> 99. </h:selectOneMenu> 100. </h:panelGroup> 101. <h:outputText value="#{form.selectOneMenu}"/> 102. <!-- ligne 9 --> 103. <h:outputText value="selectManyMenu" styleClass="info"/> 104. <h:panelGroup> 105. <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" /> 106. <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" > 107. <f:selectItem itemValue="1" itemLabel="un"/> 108. <f:selectItem itemValue="2" itemLabel="deux"/> 109. <f:selectItem itemValue="3" itemLabel="trois"/> 110. <f:selectItem itemValue="4" itemLabel="quatre"/> 111. <f:selectItem itemValue="5" itemLabel="cinq"/> 112. </h:selectManyMenu> 113. <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}"/>" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p> 114. </h:panelGroup> 115. <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/> 116. <!-- ligne 10 --> 117. <h:outputText value="inputHidden" styleClass="info"/> 118. <h:inputHidden id="inputHidden" value="#{form.inputHidden}"/> 119. <h:outputText value="#{form.inputHidden}"/> 120. <!-- ligne 11 --> 121. <h:outputText value="selectBooleanCheckbox" styleClass="info"/> 122. <h:panelGroup> 123. <h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" /> 124. <h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/> 125. </h:panelGroup> 126. <h:outputText value="#{form.selectBooleanCheckbox}"/> 127. <!-- ligne 12 --> 128. <h:outputText value="selectManyCheckbox" styleClass="info"/> 129. <h:panelGroup> 130. <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" /> 131. <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}"> 132. <f:selectItem itemValue="1" itemLabel="rouge"/> 133. <f:selectItem itemValue="2" itemLabel="bleu"/> 134. <f:selectItem itemValue="3" itemLabel="blanc"/> 135. <f:selectItem itemValue="4" itemLabel="noir"/> 136. </h:selectManyCheckbox> 137. </h:panelGroup> 138. <h:outputText value="#{form.selectManyCheckboxValue}"/> 139. <!-- ligne 13 --> 140. <h:outputText value="selectOneRadio" styleClass="info"/> 141. <h:panelGroup> 142. <h:outputText value="#{msg['form.selectOneRadioPrompt']}" /> 143. <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}"> 144. <f:selectItem itemValue="1" itemLabel="voiture"/> 145. <f:selectItem itemValue="2" itemLabel="vlo"/> 146. <f:selectItem itemValue="3" itemLabel="scooter"/> 147. <f:selectItem itemValue="4" itemLabel="marche"/> 148. </h:selectOneRadio> 149. </h:panelGroup> 150. <h:outputText value="#{form.selectOneRadio}"/> 151. </h:panelGrid> 152. <p> 153. <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/> 154. </p> 155. </h:form> 156. </body> 157. </html> 158.</f:view>

http://tahe.developpez.com/java/javaee

264/341

Nous allons tudier successivement les principaux composants de cette page. On notera la structure gnrale d'un formulaire Jsf :
1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. 7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 8. "http://www.w3.org/TR/html4/loose.dtd"> 9. 10. <f:view> 11. <html> 12. <head> 13. ... 14. </head> 15. <body> 16. <h:form id="formulaire"> 17. .... 18. <h:commandButton value="#{msg['form.submitText']}"/> 19. .... 20. </h:form> 21. </body> 22. </html> 23. </f:view>

Les composants d'un formulaire doivent tre l'intrieur d'une balise <h:form> (lignes 16-20), elle-mme l'intrieur d'une balise <f:view> (lignes 10-23). Par ailleurs, un formulaire doit disposer d'un moyen d'tre post (POST), souvent un lien ou un bouton comme dans la ligne 18. Il est peut tre post galement par de nombreux vnements (changement d'une slection dans une liste, changement de champ actif, frappe d'un caractre dans un champ de saisie, ...).

21.3.7.1

La feuille de style du formulaire

Afin de rendre plus lisibles les colonnes du tableau du formulaire, celui est accompagn d'une feuille de style :
1. ... 2. <f:view> 3. <html> 4. <head> 5. ... 6. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 7. </head> 8. <body background="<c:url value="/ressources/standard.jpg"/>"> 9. <h:form id="formulaire"> 10. ... 11. <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1"> 12. <!-- ligne 1 --> 13. <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/> 14. <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/> 15. <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/> 16. <!-- ligne 2 --> 17. <h:outputText value="inputText" styleClass="info"/> 18. <h:panelGroup> 19. <h:outputText value="#{msg['form.loginPrompt']}"/> 20. <h:inputText id="inputText" value="#{form.inputText}"/> 21. </h:panelGroup> 22. <h:outputText value="#{form.inputText}"/> 23. <!-- ligne 3 --> 24. 25. ... 26. </h:panelGrid> 27. <p> 28. <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/> 29. </p> 30. </h:form> 31. </body> 32. </html> 33. </f:view>

ligne 7 : la feuille de style de la page est dfinie l'intrieur de la balise Html <head>, par une balise <link ... rel= "stylesheet ">. Nous utilisons ici, la balise Jstl <c:url .../> pour dfinir l'url de la feuille de style. Celle-ci a t place l'endroit suivant dans le projet Netbeans :

http://tahe.developpez.com/java/javaee

265/341

Le fichier [styles.css] qui dfinit les diffrents styles utiliss dans le formulaire est la racine de la branche [Web Pages] et peut donc tre obtenue avec l'Url [/intro-03/styles.css]. On aurait pu crire la balise <link> de la faon suivante :
<link href="/intro-03/styles.css" rel="stylesheet" type="text/css"/>

La balise <c:url> permet de nous affranchir du nom /intro-03 de l'application. La balise


<c:url value="/styles.css"/>

va gnrer le texte /intro-03/styles.css. La balise <c:url> permet ainsi de changer le nom de l'application sans avoir repasser sur tous les liens contenant ce nom. La mme technique est utilise ligne 8 pour gnrer le lien de l'image de fond de la page.

lignes 11-26 : la balise <h:panelGrid columns="3"/> dfinit un tableau trois colonnes. L'attribut columnClasses permet de donner un style ces colonnes :
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">

Les valeurs col1,col2,col3 de l'attribut columnClasses dsignent les styles respectifs des colonnes 1, 2 et 3 du tableau. Ces styles sont cherchs dans le feuille de style de la page dfinie ligne 6 :
1. .info{ 2. font-family: Arial,Helvetica,sans-serif; 3. font-size: 14px; 4. font-weight: bold 5. } 6. 7. .col1{ 8. background-color: #ccccff 9. } 10. 11. .col2{ 12. background-color: #ffcccc 13. } 14. 15. .col3{ 16. background-color: #ffcc66 17. } 18. 19. .entete{ 20. font-family: 'Times New Roman',Times,serif; 21. font-size: 14px; 22. font-weight: bold 23. }

lignes 7-9 : le style nomm col1 lignes 11-13 : le style nomm col2 lignes 15-17 : le style nomm col3

Ces trois styles dfinissent la couleur de fond de chacune des colonnes.

lignes 19-23 : le style entete sert dfinir le style des textes de la 1re ligne du tableau. Il est utilis lignes 13-15 du code Jsp.

http://tahe.developpez.com/java/javaee

266/341

lignes 1-5 : le style info sert dfinir le style des textes de la 1re colonne du tableau. On en voit un exemple ligne 17 du code Jsp.

Nous insisterons peu sur l'utilisation des feuilles de style car celles-ci mritent elles seules un livre et que par ailleurs leur laboration en est souvent confie des spcialistes. Nanmoins, nous avons souhait en utiliser une, minimaliste, afin de rappeler que leur usage est indispensable.

21.3.7.2

Les deux cycles demande client / rponse serveur d'un formulaire

Revnons sur ce qui a dj t expliqu page 258 dans un cas gnral et appliquons-le au formulaire tudi. Celui-ci sera test dans l'environnement Jsf classique :

Application web
couche [web]
1

Faces Servlet
4

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles

Ici, il n'y aura pas de gestionnaires d'vnements ni de couche [metier]. Les tapes [2x] n'existeront donc pas. On distinguera le cas o le formulaire F est demande initialement par le navigateur du cas o l'utilisateur ayant provoqu un vnement dans le formulaire F, celui-ci est trait par le contrleur [Faces Servlet]. Il y a deux cycles demande client / rponse serveur qui sont diffrents.

le premier correspondant la demande initiale de la page est provoqu par une opration GET du navigateur sur l'Url du formulaire. le second correspondant la soumission des valeurs saisies dans la page est provoqu par une opration POST sur cette mme Url.

Selon la nature de la demande GET ou POST du navigateur, le traitement de la requte par le contrleur [Faces Servlet] diffre. [cas 1 demande initiale du formulaire F] Le navigateur demande l'Url de la page avec un GET. Le contrleur [Faces Servlet] va passer directement l'tape [4] de rendu de la rponse. Le formulaire [form.jsp] va tre initialis par son modle [Form.java] et tre envoy au client qui reoit la vue suivante :

http://tahe.developpez.com/java/javaee

267/341

Les changes HTTP client / serveur sont les suivants cette occasion : Demande HTTP du client :
1. GET /intro-03/faces/form.jsp HTTP/1.1 2. Host: localhost:8080 3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 4. Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0. 5 5. Accept-Language: fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3 6. Accept-Encoding: gzip,deflate 7. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 8. Keep-Alive: 300 9. Connection: keep-alive 10. Cookie: test_cookie=test cookie

Ligne 1, on voit le GET du navigateur. Rponse HTTP du serveur :


1. HTTP/1.x 200 OK 2. X-Powered-By: Servlet/2.5, JSP/2.1

http://tahe.developpez.com/java/javaee

268/341

3. 4. 5. 6. 7. 8.

Set-Cookie: JSESSIONID=6f57b611e610e3ec55cb3cbd76aeb; Path=/intro-03 Content-Type: text/html;charset=UTF-8 Content-Language: fr-FR Transfer-Encoding: chunked Date: Fri, 05 Oct 2007 08:46:00 GMT Server: Sun Java System Application Server Platform Edition 9.0_01

Non montr ici, la ligne 8 est suivie d'une ligne vide et du code Html du formulaire. C'est ce code que le navigateur interprte et affiche. [cas 2 traitement des valeurs saisies dans le formulaire F] L'utilisateur remplit le formulaire et le valide par le bouton [Valider]. Le navigateur demande alors l'Url du formulaire avec un POST. Le contrleur [Faces Servlet] traite cette requte, met jour le modle [Form.java] du formulaire [form.jsp], et renvoie de nouveau le formulaire [form.jsp] mis jour par ce nouveau modle. Examinons ce cycle sur un exemple :

Ci-dessus, l'utilisateur a fait ses saisies et les valide. Il reoit en rponse la vue suivante :

http://tahe.developpez.com/java/javaee

269/341

Les changes HTTP client / serveur sont les suivants cette occasion : Demande HTTP du client :
1. POST /intro-03/faces/form.jsp;jsessionid=6f57b611e610e3ec55cb3cbd76aeb HTTP/1.1 2. Host: localhost:8080 3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7 4. Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0. 5 5. Accept-Language: fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3 6. Accept-Encoding: gzip,deflate 7. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 8. Keep-Alive: 300 9. Connection: keep-alive 10. Referer: http://localhost:8080/intro-03/faces/form.jsp 11. Cookie: JSESSIONID=6f57b611e610e3ec55cb3cbd76aeb; test_cookie=test cookie 12. Content-Type: application/x-www-form-urlencoded 13. Content-Length: 6107 14. 15. formulaire=formulaire&javax.faces.ViewState=H4s...wAA&formulaire %3AinputText=nouveau+texte&formulaire%3AinputSecret=mdp&formulaire%3AinputTextArea=Tutoriel+JSF%0D %0Apartie+1%0D%0A&formulaire%3AselectOneListBox1=3&formulaire%3AselectOneListBox2=5&formulaire %3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire %3AselectManyListBox=5&formulaire%3AselectOneMenu=4&formulaire%3AselectManyMenu=4&formulaire %3AselectManyMenu=5&formulaire%3AinputHidden=initial&formulaire%3AselectManyCheckbox=2&formulaire %3AselectManyCheckbox=4&formulaire%3AselectOneRadio=4&formulaire%3Asubmit=Valider

En ligne 1, le POST fait par le navigateur. En ligne 15, les valeurs saisies par l'utilisateur. On peut par exemple y dcouvrir le texte mis dans le champ de saisie :

http://tahe.developpez.com/java/javaee

270/341

formulaire%3AinputText=nouveau+texte

Ligne 13, on dcouvre que l'ensemble des valeurs saisies occupe 6107 caractres. Cela est d la prsence du champ cach javax.faces.ViewState qui, lui tout seul, fait plusieurs milliers de caractres. Ce champ reprsente, sous forme code, l'tat du formulaire tel qu'il a t envoy initialement au navigateur lors de son GET initial. Rponse HTTP du serveur :
1. 2. 3. 4. 5. 6. 7. HTTP/1.x 200 OK X-Powered-By: Servlet/2.5, JSP/2.1 Content-Type: text/html;charset=UTF-8 Content-Language: fr-FR Transfer-Encoding: chunked Date: Fri, 05 Oct 2007 08:57:16 GMT Server: Sun Java System Application Server Platform Edition 9.0_01

Non montr ici, la ligne 7 est suivie d'une ligne vide et du code Html du formulaire mis jour par son nouveau modle isu du POST. Nous examinons maintenant les diffrentes composantes de ce formulaire.

21.3.7.3

Balise <h:inputText>

La balise <h:inputText> gnre une balise Html <input type="text" ...>. Considrons le code suivant :
1. <!-- ligne 2 --> 2. <h:outputText value="inputText" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.loginPrompt']}"/> 5. <h:inputText id="inputText" value="#{form.inputText}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputText}"/>

et son modle [Form.java] :


1. 2. 3. 4. 5. 6. 7. 8. 9.} private String inputText="texte"; public String getInputText() { return inputText; } public void setInputText(String inputText) { this.inputText = inputText;

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 1 2 3 4

la ligne 2 du code Jsp gnre [1] la balise <h:panelGroup> (lignes 3-6) permet de regrouper plusieurs lments dans une mme cellule du tableau gnr par la balise <h:panelGrid> de la ligne 24 du code complet de la page. Le texte [2] est gnr par la ligne 4. Le champ de saisie [3] est gnr par la ligne [5]. Ici, la mthode getInputText de [Form.java] (lignes 3-5 du code Java) a t utilise pour gnrer le texte du champ de saisie. la ligne 7 du code Jsp gnre [4]. C'est de nouveau la mthode getInputText de [Form.java] qui est utilise pour gnrer le texte [4].

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">inputText</span></td> 3. <td class="col2">login : <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /></td> 4. <td class="col3">texte</td> 5. </tr>

http://tahe.developpez.com/java/javaee

271/341

Les balises Html <tr> et <td> sont gnres par la balise <h:panelGrid> utilise pour gnrer le tableau du formulaire. Maintenant, ci-dessous, saisissons une valeur dans le champ de saisie [1] et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3, 4] :

1 2 3 4

La valeur du champ [1] est poste de la faon suivante :


formulaire%3AinputText=nouveau+texte

En [2], le formulaire est valid avec le bouton suivant :


<h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>

La balise <h:commandButton> n'a pas d'attribut action. Dans ce cas, aucun gestionnaire d'vnement n'est invoqu ni aucune rgle de navigation. Aprs traitement, la mme page est renvoye. Revoyons son cycle de traitement : A B C

en [A] la page P est restaure telle qu'elle avait t envoye. Cela signifie que le composant d'id inputText est restaur avec sa valeur initiale "texte". en [B], les valeurs postes par le navigateur (saisies par l'utilisateur) sont affectes aux composants de la page P. Ici, le composant d'id inputText reoit la valeur "un nouveau texte". en [C], les conversions et validations ont lieu. Ici, il n'y en a aucune. Dans le modle M, le champ associ au composant d'id inputText est le suivant :
private String inputText="texte";

Comme les valeurs saisies sont de type String, il n'y a pas de conversion faire. Par ailleurs, aucune rgle de validation n'a t cre. Nous en construirons ultrieurement. en [D], les valeurs saisies sont affectes au modle. Le champ inputText de [Form.java] reoit la valeur "un nouveau texte". en [E], rien n'est fait car aucun gestionnaire d'vnement n'a t associ au bouton [Valider]. en [F], la page P est de nouveau envoye au client car le bouton [Valider] n'a pas d'attribut action. Les lignes suivantes de [form.jsp] sont alors excutes :
1. 2. <!-- ligne 2 --> <h:outputText value="inputText" styleClass="info"/>

http://tahe.developpez.com/java/javaee

272/341

3. <h:panelGroup> 4. <h:outputText value="#{msg['form.loginPrompt']}"/> 5. <h:inputText id="inputText" value="#{form.inputText}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputText}"/>

Les lignes 5 et 7 utilisent la valeur du champ inputText du modle qui est dsormais "un nouveau texte". D'o l'affichage obtenu :

21.3.7.4

Balise <h:inputSecret>

La balise <h:inputSecret> gnre une balise Html <input type="password" ...>. C'est un champ de saisie analogue celui de la balise Jsf <h:inputText> si ce n'est que chaque caractre tap par l'utilisateur est remplac visuellement par un caractre *. Considrons le code suivant :
1. <!-- ligne 3 --> 2. <h:outputText value="inputSecret" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.passwdPrompt']}"/> 5. <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputSecret}"/>

et son modle dans [Form.java] :


1. private String inputSecret="secret";

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 1 2

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. Le champ de saisie [3] est gnr par la ligne [5]. Normalement, la mthode getInputSecret de [Form.java] aurait du tre utilise pour gnrer le texte du champ de saisie. Il y a une exception lorsque celui-ci est de type " mot de passe ". La balise <h:inputSecret> ne sert qu' lire une saisie, pas l'afficher. la ligne 7 du code Jsp gnre [4]. Ici la mthode getInputSecret de [Form.java] a t utilise pour gnrer le texte [4] (cf ligne 1 du code Java).

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">inputSecret</span></td> 3. <td class="col2">mot de passe : <input id="formulaire:inputSecret" type="password" name="formulaire:inputSecret" value="" /></td> 4. <td class="col3">secret</td> 5. </tr>

ligne 3 : la balise Html <input type= "password " .../> gnre par la balise Jsf <h:inputSecret>

Maintenant, ci-dessous, saisissons une valeur dans le champ de saisie [1] et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3] :

http://tahe.developpez.com/java/javaee

273/341

1 2

La valeur du champ [1] est poste de la faon suivante :


formulaire%3AinputSecret=mdp

La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. Le champ inputSecret de [Form.java] a reu alors la valeur mdp. Parce que le formulaire [form.jsp] n'a dfini aucune rgle de navigation, ni aucun gestionnaire d'vnement, il est raffich aprs la mise jour de son modle. On retombe alors dans l'affichage fait la demande initiale de la page [form.jsp] o simplement le champ inputSecret du modle a chang de valeur [3].

21.3.7.5

Balise <h:inputTextArea>

La balise <h:inputTextArea> gnre une balise Html <textarea ...>texte</textarea>. C'est un champ de saisie analogue celui de la balise Jsf <h:inputText> si ce n'est qu'ici, on peut taper plusieurs lignes de texte. Considrons le code suivant :
1. <!-- ligne 4 --> 2. <h:outputText value="inputTextArea" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.descPrompt']}"/> 5. <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.inputTextArea}"/>

et son modle dans [Form.java] :


1. private String inputTextArea="ligne1\nligne2\n";

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 1 2 3 4

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. Le champ de saisie [3] est gnr par la ligne [5]. Son contenu a t gnr par appel la mthode getInputTextArea du modle, qui a rendu la valeur dfinie en ligne 1 du code Java ci-dessus. la ligne 7 du code Jsp gnre [4]. Ici la mthode getInputTextArea de [Form.java] a t de nouveau utilise. La chane " ligne1\nligne2 " contenait des sauts de ligne \n. Ils y sont toujours. Mais insrs dans un flux Html, ils sont affichs comme des espaces par les navigateurs. La balise Html <textarea> qui affiche [3], elle, interprte correctement les sauts de ligne.

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">inputTextArea</span></td> 3. <td class="col2">description : <textarea id="formulaire:inputTextArea" name="formulaire:inputTextArea" rows="4">ligne1 4. ligne2 5. </textarea></td> 6. <td class="col3">ligne1 7. ligne2 8. </td>

http://tahe.developpez.com/java/javaee

274/341

9. </tr>

lignes 3-5 : la balise Html <textarea>...</textarea> gnre par la balise Jsf <h:inputSecret>

Maintenant, ci-dessous, saisissons une valeur dans le champ de saisie [1] et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3] :

La valeur du champ [1] poste est la suivante :


formulaire%3AinputTextArea=Tutoriel+Jsf%0D%0Apartie+1%0D%0A

La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. Le champ textArea de [Form.java] a reu alors la valeur " Tutoriel Jsf\npartie1 ". Le raffichage de [form.jsp] montre que champ textArea du modle a bien t mis jour [3].

21.3.7.6

Balise <h:selectOneListBox>

La balise <h:selectOneListBox> gnre une balise Html <select>...</select>. Visuellement, elle gnre une liste droulante ou une liste avec ascenseur. Considrons le code suivant :
1. 2. 3. 4. 5. <!-- ligne 5 --> <h:outputText value="selectOneListBox (size=1)" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/> <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}"

size="1"> 6. <f:selectItem itemValue="1" itemLabel="un"/> 7. <f:selectItem itemValue="2" itemLabel="deux"/> 8. <f:selectItem itemValue="3" itemLabel="trois"/> 9. </h:selectOneListbox> 10. </h:panelGroup> 11. <h:outputText value="#{form.selectOneListBox1}"/>

et son modle dans [Form.java] :


1. private String selectOneListBox1="2";

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 4 1

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. La liste droulante [3] est gnre par les lignes [5-9]. C'est la valeur de l'attribut size= "1" qui fait que la liste n'affiche qu'un lment. Si cet attribut est manquant, la valeur par dfaut de l'attribut size est 1. Les lments de la liste ont t gnrs par les balises <f:selectItem> des lignes 6-8. Ces balises ont la syntaxe suivante :

http://tahe.developpez.com/java/javaee

275/341

<f:selectItem itemValue="valeur" itemLabel="texte"/>

La valeur de l'attribut itemLabel est ce qui est affich dans la liste. La valeur de l'attribut itemValue est la valeur de l'lment. C'est cette valeur qui sera envoye au contrleur [Faces Servlet] si l'lment est slectionn dans la liste droulante. L'lment affich en [3] a t dtermin par appel la mthode getSelectOneListBox1() (ligne 5). Le rsultat " 2 " obtenu (ligne 1 du code Java) a fait que l'lment de la ligne 7 de la liste droulante a t affich, ceci parce que son attribut itemValue vaut " 2 ". la ligne 11 du code Jsp gnre [4]. Ici la mthode getSelectOneListBox1 de [Form.java] a t de nouveau utilise.

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">selectOneListBox (size=1)</span></td> 3. <td class="col2">choix unique : <select id="formulaire:selectOneListBox1" name="formulaire:selectOneListBox1" size="1"> 4. <option value="1">un</option> 5. <option value="2" selected="selected">deux</option> 6. <option value="3">trois</option> 7. </select></td> 8. <td class="col3">2</td> 9. </tr>

lignes 3 et 7 : la balise Html <select ...>...</select> gnre par la balise Jsf <h:selectOneListBox> lignes 4-6 : les balises Html <option ...> ... </option> gnres par les balises Jsf <f:selectItem> ligne 5 : le fait que l'lment de valeur= "2 " soit slectionn dans la liste se traduit par la prsence de l'attribut selected="selected".

Maintenant, ci-dessous, choisissons [1] une nouvelle valeur dans la liste et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3] :

1 2 4

La valeur du champ [1] poste est la suivante :


formulaire%3AselectOneListBox1=3

La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. L'lment Html
<option value="3">trois</option>

a t slectionn. Le navigateur a envoy la chane " 3 " comme valeur du composant Jsf ayant produit la liste droulante :
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">

Le contrleur Jsf va utiliser la mthode setSelectOneListBox1(" 3 ") pour mettre jour le modle de la liste droulante. Aussi aprs cette mise jour, le champ du modle [Form.java]
private String selectOneListBox1;

contient-il dsormais la valeur " 3 ". Lorsque la page [form.jsp] est raffiche l'issue de son traitement, cette valeur provoque l'affichage [3,4] ci-dessus : elle dtermine l'lment de la liste droulante qui doit tre affich [3] la valeur du champ selectOneListBox1 est affiche en [4].

http://tahe.developpez.com/java/javaee

276/341

Considrons une variante de la balise <h:selectOneListBox> :


1. 2. 3. 4. 5. <!-- ligne 6 --> <h:outputText value="selectOneListBox (size=3)" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/> <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}"

size="3"> 6. <f:selectItem itemValue="1" itemLabel="un"/> 7. <f:selectItem itemValue="2" itemLabel="deux"/> 8. <f:selectItem itemValue="3" itemLabel="trois"/> 9. <f:selectItem itemValue="4" itemLabel="quatre"/> 10. <f:selectItem itemValue="5" itemLabel="cinq"/> 11. </h:selectOneListbox> 12. </h:panelGroup> 13. <h:outputText value="#{form.selectOneListBox2}"/>

Le modle dans [Form.java] de la balise <h:selectOneListBox> de la ligne 5 est le suivant :


1. private String selectOneListBox2="3";

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 2 1

3 4

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. La liste avec ascenseur [3] est gnre par les lignes [5-11]. C'est la valeur de l'attribut size= "3 " qui fait qu'on a une liste avec ascenseur plutt qu'une liste droulante. Les lments de la liste ont t gnrs par les balises <f:selectItem> des lignes 6-8. L'lment slectionn en [3] a t dtermin par appel la mthode getSelectOneListBox2() (ligne 5). Le rsultat " 3 " obtenu (ligne 1 du code Java) a fait que l'lment de la ligne 8 de la liste a t affich, ceci parce que son attribut itemValue vaut " 3 ". la ligne 13 du code Jsp gnre [4]. Ici la mthode getSelectOneListBox2 de [Form.java] a t de nouveau utilise.

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">selectOneListBox (size=3)</span></td> 3. <td class="col2">choix unique : <select id="formulaire:selectOneListBox2" name="formulaire:selectOneListBox2" size="3"> 4. <option value="1">un</option> 5. <option value="2">deux</option> 6. <option value="3" selected="selected">trois</option> 7. <option value="4">quatre</option> 8. <option value="5">cinq</option> 9. </select></td> 10. <td class="col3">3</td> 11. </tr>

ligne 6 : le fait que l'lment de valeur="3" soit slectionn dans la liste se traduit par la prsence de l'attribut selected="selected".

Maintenant, ci-dessous, choisissons [1] une nouvelle valeur dans la liste et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3] :

http://tahe.developpez.com/java/javaee

277/341

2 4

La valeur poste pour le champ [1] est la suivante :


formulaire%3AselectOneListBox2=5

La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. L'lment Html
<option value="5">cinq</option>

a t slectionn. Le navigateur a envoy la chane " 5 " comme valeur du composant Jsf ayant produit la liste droulante :
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">

Le contrleur Jsf va utiliser la mthode setSelectOneListBox2(" 5 ") pour mettre jour le modle de la liste. Aussi aprs cette mise jour, le champ
private String selectOneListBox2;

contient-il dsormais la valeur " 5 ". Lorsque la page [form.jsp] est raffiche l'issue de son traitement, cette valeur provoque l'affichage [3,4] ci-dessus : elle dtermine l'lment de la liste qui doit tre slectionn [3] la valeur du champ selectOneListBox2 est affich en [4].

21.3.7.7

Balise <h:selectManyListBox>

La balise <h:selectmanyListBox> gnre une balise Html <select multiple= "multiple ">...</select> qui permet l'utilisateur de slectionner plusieurs lments dans une liste. Considrons le code suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. <!-- ligne 7 --> <h:outputText value="selectManyListBox (size=3)" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/> <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}"

<f:selectItem itemValue="1" itemLabel="un"/> <f:selectItem itemValue="2" itemLabel="deux"/> <f:selectItem itemValue="3" itemLabel="trois"/> <f:selectItem itemValue="4" itemLabel="quatre"/> <f:selectItem itemValue="5" itemLabel="cinq"/> </h:selectManyListbox> <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}" />" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p> 13. </h:panelGroup> 14. <h:outputText value="#{form.selectManyListBoxValue}"/>

size="3">

et son modle dans [Form.java] :


1. private String[] selectManyListBox=new String[]{"1","3"};

http://tahe.developpez.com/java/javaee

278/341

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 2 1 5 3 4

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. La liste [3] est gnre par les lignes [5-11]. L'attribut size= "3" fait que la liste affiche un moment donn trois de ces lments. Les lments slectionns dans la liste ont t dtermins par appel la mthode getSelectManyListBox() (ligne 5) du modle Java. Le rsultat {"1","3"} obtenu (ligne 1 du code Java) est un tableau d'lments de type String. Chacun de ces lments sert slectionner l'un des lments de la liste. Ici, les lments des lignes 6 et 10 ayant leur attribut itemValue dans le tableau {"1","3"} seront slectionns. C'est ce que montre [3]. la ligne 14 du code Jsp gnre [4]. Il est ici fait appel, non pas la mthode getSelectManyListBox du modle Java de la liste mais la mthode getSelectManyListBoxValue suivante :

1.

private String[] selectManyListBox=new String[]{"1","3"}; ... 2. public String getSelectManyListBoxValue(){ 3. return getValue(selectManyListBox); 4. } 5. 6. private String getValue(String[] chaines){ 7. String value="["; 8. for(String chaine : chaines){ 9. value+=" "+chaine; 10. } 11. return value+"]"; 12.}

Si on avait fait appel la mthode getSelectManyListBox, on aurait obtenu un tableau de String. Pour inclure cet lment dans le flux Html, le contrleur aurait appel sa mthode toString. Or celle-ci pour un tableau ne fait que rendre le " hashcode " de celui-ci et non pas la liste de ses lments comme nous le souhaitons. Aussi utilise-t-on la mthode getSelectManyListBoxValue ci-dessus pour obtenir une chane de caractres reprsentant le contenu du tableau.

la ligne 12 du code Jsp gnre le bouton [5]. Lorsque ce bouton est cliqu, le code Javascript de l'attribut onclick est excut. Il sera embarqu au sein de la page Html qui va tre gnre par le code Jsf. Pour le comprendre, nous avons besoin de connatre la nature exacte de celle-ci.

Le flux Html gnr par la page Jsp est le suivant :


1. <tr> 2. <td class="col1"><span class="info">selectManyListBox (size=3)</span></td> 3. <td class="col2">choix multiple : <select id="formulaire:selectManyListBox" name="formulaire:selectManyListBox" multiple="multiple" size="3"> 4. <option value="1" selected="selected">un</option> 5. <option value="2">deux</option> 6. <option value="3" selected="selected">trois</option> 7. <option value="4">quatre</option> 8. <option value="5">cinq</option> 9. </select> 10. <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p> 11. </td> 12. <td class="col3">[ 1 3]</td> 13. </tr>

lignes 3 et 9 : la balise Html <select multiple= "multiple "...>...</select> gnre par la balise Jsf <h:selectManyListBox>. C'est la prsence de l'attribut multiple qui indique qu'on a affaire une liste slection multiple. le fait que le modle de la liste soit le tableau de String {"1","3"} fait que les lments de la liste des lignes 4 (value= "1 ") et 6 (value= "3 ") ont l'attribut selected="selected". ligne 10 : lorsqu'on clique sur le bouton [Raz], le code Javascript de l'attribut onclick s'excute. La page est reprsente dans le navigateur par un arbre d'objets souvent appel DOM (Document Object Model). Chaque objet de l'arbre est accessible au code Javascript via son attribut name. La liste de la ligne 3 du code Html ci-dessus s'appelle formulaire:selectManyListBox. Le formulaire lui-mme peut tre dsign de diverses faons. Ici, il est dsign par la notation this.form o this dsigne le bouton [Raz] et this.form le formulaire dans lequel se trouve ce bouton. La liste

http://tahe.developpez.com/java/javaee

279/341

formulaire:selectManyListBox se trouve dans ce mme formulaire. Aussi la notation this.form['formulaire:selectManyListBox'] dsigne-t-elle l'emplacement de la liste dans l'arbre des composants du formulaire. L'objet reprsentant une liste a un attribut selectedIndex qui a pour valeur le n de l'lment slectionn dans la liste. Ce n commence 0 pour dsigner le 1er lment de la liste. La valeur -1 indique qu'aucun lment n'est slectionn dans la liste. Le code Javascript affectant la valeur -1 l'attribut selectedIndex a pour effet de dslectionner tous les lments de la liste s'il y en avait. Maintenant, ci-dessous, choisissons [1] de nouvelles valeurs dans la liste (pour slectionner plusieurs lments dans la liste, maintenir la touche Ctrl appuye en cliquant) et validons le formulaire avec le bouton [Valider] [2]. Nous obtenons en rponse la page [3,4] :

2 4

La valeur du champ [1] poste est la suivante :


formulaire%3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire%3AselectManyListBox=5

La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. Les lments Html
<option value="3">trois</option> <option value="4">quatre</option> <option value="5">cinq</option>

ont t slectionns. Le navigateur a envoy les trois chanes "3", "4", "5" comme valeurs du composant Jsf ayant produit la liste droulante :
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">

La mthode setSelectManyListBox du modle va tre utilise pour mettre ce jour ce modle avec les valeurs envoyes par le navigateur :
1. private String[] selectManyListBox; 2..... 3. public void setSelectManyListBox(String[] selectManyListBox) { 4. this.selectManyListBox = selectManyListBox; 5.}

Ligne 3, on voit que le paramtre de la mthode est un tableau de String. Ici ce sera le tableau {" 3 ", "4 ", "5 "}. Aprs cette mise jour, le champ
private String[] selectManyListBox;

contient dsormais le tableau {" 3 ", "4 ", "5 "}. Lorsque la page [form.jsp] est raffiche l'issue de son traitement, cette valeur provoque l'affichage [3,4] ci-dessus : elle dtermine les lments de la liste qui doivent tre slectionns [3] la valeur du champ selectManyListBox est affiche en [4].

http://tahe.developpez.com/java/javaee

280/341

21.3.7.8

Balise <h:selectOneMenu>

La balise <h:selectOneMenu> est identique la balise <h:selectOneListBox size= "1 ">. Dans l'exemple, le code Jsf excut est le suivant :
1. <!-- ligne 8 --> 2. <h:outputText value="selectOneMenu" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/> 5. <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}"> 6. <f:selectItem itemValue="1" itemLabel="un"/> 7. <f:selectItem itemValue="2" itemLabel="deux"/> 8. <f:selectItem itemValue="3" itemLabel="trois"/> 9. <f:selectItem itemValue="4" itemLabel="quatre"/> 10. <f:selectItem itemValue="5" itemLabel="cinq"/> 11. </h:selectOneMenu> 12. </h:panelGroup> 13. <h:outputText value="#{form.selectOneMenu}"/>

Le modle de la balise <h:selectOneMenu> dans [Form.java] est le suivant :


private String selectOneMenu="1";

A la demande initiale de la page [form.jsp], le code prcdent gnre la vue :

Un exemple d'excution pourrait tre celui qui suit :

1 2 3

La valeur poste pour le champ [1] est la suivante : formulaire%3AselectOneMenu=4

21.3.7.9

Balise <h:selectManyMenu>

La balise <h:selectManyMenu> est identique la balise <h:selectManyListBox size= "1 ">. Le code Jsf excut dans l'exemple est le suivant :
1. 2. 3. 4. 5. 6. 7. 8. <!-- ligne 9 --> <h:outputText value="selectManyMenu" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" /> <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" > <f:selectItem itemValue="1" itemLabel="un"/> <f:selectItem itemValue="2" itemLabel="deux"/> <f:selectItem itemValue="3" itemLabel="trois"/>

http://tahe.developpez.com/java/javaee

281/341

9. 10. 11. 12.

<f:selectItem itemValue="4" itemLabel="quatre"/> <f:selectItem itemValue="5" itemLabel="cinq"/> </h:selectManyMenu> <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}"/>" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p> 13. </h:panelGroup> 14. <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>

Le modle de la balise <h:selectManyMenu> dans [Form.java] est le suivant :


private String[] selectManyMenu=new String[]{"1","2"};

A la demande initiale de la page [form.jsp], le code prcdent gnre la page : 1

La liste [1] contient les textes " un ", ..., " cinq " avec les lments " un " et " deux " slectionns. Le code Html gnr est le suivant :
1. <tr> 2. <td class="col1"><span class="info">selectManyMenu</span></td> 3. <td class="col2"><span class="prompt">choix multiple : </span><select id="formulaire:selectManyMenu" name="formulaire:selectManyMenu" multiple="multiple" size="1"> 4. <option value="1" selected="selected">un</option> 5. <option value="2" selected="selected">deux</option> 6. <option value="3">trois</option> 7. <option value="4">quatre</option> 8. <option value="5">cinq</option> 9. </select> 10. 11. 12. <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p> 13. </td> 14. <td class="col3"><span class="prompt">[ 1 2]</span></td> 15. </tr>

On voit ci-dessus, en lignes 4 et 5 que les lments " un " et " deux " sont slectionns (prsence de l'attribut selected). Il est difficile de donner une copie d'cran d'un exemple d'excution car on ne peut pas montrer les lments slectionns dans le menu. Le lecteur est invit faire le test lui-mme (pour slectionner plusieurs lments dans la liste, maintenir la touche Ctrl appuye en cliquant).

21.3.7.10

Balise <h:inputHidden>

La balise <h:inputHidden> n'a pas de reprsentation visuelle. Elle ne sert qu' insrer une balise Html <input type= "hidden " value= "... "/> dans le flux Html de la page. Inclus l'intrieur d'une balise <h:form>, leurs valeurs font partie des valeurs envoyes au serveur lorsque le formulaire est post. Parce que ce sont des champs de formulaire et que l'utilisateur ne les voie pas, on les appelle des champs cachs. L'intrt de ces champs est de garder de la mmoire entre les diffrents cycles demande / rponse d'un mme client : le client demande un formulaire F. Le serveur le lui envoie et met une information I dans un champ cach C, sous la forme <h:inputHidden id= "C " value= "I "/> lorsque le client a rempli le formulaire F et qu'il le poste au serveur, la valeur I du champ C est renvoye au serveur. Celuici peut alors retrouver l'information I qu'il avait stocke dans la page. On a ainsi cr une mmoire entre les deux cycles demande / rponse Jsf utilise lui-mme cette technique. L'information I qu'il stocke dans le formulaire F est la valeur de tous les composants de celui-ci. Il utilise le champ cach suivant pour cela :
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />

Le champ cach s'appelle javax.faces.ViewState et sa valeur est une chane qui reprsente sous forme code la valeur de tous les composants de la page envoye au client. Lorsque celui-ci renvoie la page aprs avoir fait des saisies dans le formulaire, le champ cach javax.faces.ViewState est renvoy avec les valeurs saisies. C'est ce qui permet au contrleur

http://tahe.developpez.com/java/javaee

282/341

Jsf de reconstituer la page telle qu'elle avait t envoye initialement. Ce mcanisme a t expliqu au paragraphe 21.3.6 page 257. Le code Jsf de l'exemple est le suivant :
1. <!-- ligne 10 --> 2. <h:outputText value="inputHidden" styleClass="info"/> 3. <h:inputHidden id="inputHidden" value="#{form.inputHidden}"/> 4. <h:outputText value="#{form.inputHidden}"/>

Le modle de la balise <h:inputHidden> dans [Form.java] est le suivant :


private String inputHidden="initial";

Ce qui donne l'affichage suivant lors de la demande initiale de la page [form.jsp] : 1

la ligne 2 gnre [1], la ligne 4 [2]. La ligne 3 ne gnre aucun lment visuel.

Le code Html gnr est le suivant :


1. <tr> 2. <td class="col1"><span class="info">inputHidden</span></td> 3. <td class="col2"><input id="formulaire:inputHidden" type="hidden" name="formulaire:inputHidden" value="initial" /></td> 4. <td class="col3">initial</td> 5. </tr>

Au moment du POST du formulaire, la valeur " initial " du champ nomm formulaire:inputHidden de la ligne 3 sera post avec les autres valeurs du formulaire. Le champ
private String inputHidden;

sera mis jour avec cette valeur, qui est celle qu'il avait dj initialement. Cette valeur sera intgre dans la nouvelle page renvoye au client. On obtient donc toujours la copie d'cran ci-dessus. La valeur poste pour le champ cach est la suivante :
formulaire%3AinputHidden=initial

21.3.7.11

Balise <h:selectBooleanCheckBox>

La balise <h:selectBooleanCheckBox> gnre une balise Html <input type="checkbox" ...>. Considrons le code Jsf suivant :
1. 2. 3. 4. <!-- ligne 11 --> <h:outputText value="selectBooleanCheckbox" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" /> 5. <h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/> 6. </h:panelGroup> 7. <h:outputText value="#{form.selectBooleanCheckbox}"/>

Le modle de la balise <h:selectBooleanCheckbox> de ligne 5 ci-dessus dans [Form.java] est le suivant:


private boolean selectBooleanCheckbox=true;

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 3 1 2 4

http://tahe.developpez.com/java/javaee

283/341

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. La case cocher [3] est gnre par la ligne [5]. Ici, la mthode getSelectBooleanCheckbox de [Form.java] a t utilise pour cocher ou non la case. La mthode rendant le boolen true (cf code Java), la case a t coche. la ligne 7 du code Jsp gnre [4]. C'est de nouveau la mthode getSelectBooleanCheckbox de [Form.java] qui est utilise pour gnrer le texte [4].

Le flux Html gnr par le code Jsf prcdent est le suivant :


1. 2. 3. 4. <tr> <td class="col1"><span class="info">selectBooleanCheckbox</span></td> <td class="col2"><span class="prompt">mari&eacute;(e) : </span> <input id="formulaire:selectBooleanCheckbox" type="checkbox" name="formulaire:selectBooleanCheckbox" checked="checked" /></td> 5. <td class="col3">true</td> 6. </tr>

En [4], on voit la balise Html <input type= "checkbox "> qui a t gnre. La valeur true du modle associ a fait que l'attribut checked= "checked " a t ajout la balise. Ce qui fait que la case est coche. Maintenant, ci-dessous, dcochons la case [1], validons le formulaire [2] et regardons le rsultat obtenu [3] :

1 2 4 3

Parce que la case est dcoche, il n'y a pas de valeur poste pour le champ [1]. La validation du formulaire par [2] a provoqu la mise jour du modle [Form.java] par la saisie [1]. Le champ selectBooleanCheckbox de [Form.java] a reu alors la valeur false. Le raffichage de [form.jsp] montre que champ selectBooleanCheckbox du modle a bien t mis jour [3] et [4]. Il est intressant de noter ici que c'est grce au champ cach javax.faces.ViewState que Jsf a t capable de dire que la case cocher initialement coche avait t dcoche par l'utilisateur. En effet, la valeur d'une case dcoche ne fait pas partie des valeurs postes par le navigateur. Grce l'arbre des composants stock dans le champ cach javax.faces.ViewState Jsf retrouve le fait qu'il y avait une case cocher nomme "selectBooleanCheckbox" dans le formulaire et que sa valeur ne fait pas partie des valeurs postes par le navigateur client. Il peut en conclure qu'elle tait dcoche dans le formulaire post, ce qui lui permet d'affecter le boolen false au modle Java associ :
private boolean selectBooleanCheckbox;

21.3.7.12

Balise <h:selectManyCheckBox>

La balise <h:selectManyCheckBox> gnre un groupe de cases cocher et donc plusieurs balises Html <input type="checkbox" ...>. Cette balise est le pendant de la balise <h:selectManyListBox>, si ce n'est que les lments slectionner sont prsents sous forme de cases cocher contiges plutt que sous forme de liste. Ce qui a t dit pour la balise <h:selectManyListBox> reste valide ici. Considrons le code Jsf suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. <!-- ligne 12 --> <h:outputText value="selectManyCheckbox" styleClass="info"/> <h:panelGroup> <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}"> <f:selectItem itemValue="1" itemLabel="rouge"/> <f:selectItem itemValue="2" itemLabel="bleu"/> <f:selectItem itemValue="3" itemLabel="blanc"/> <f:selectItem itemValue="4" itemLabel="noir"/>

/>

http://tahe.developpez.com/java/javaee

284/341

10. </h:selectManyCheckbox> 11. </h:panelGroup> 12. <h:outputText value="#{form.selectManyCheckboxValue}"/>

Le modle de la balise <h:selectManyCheckbox> de ligne 5 ci-dessus dans [Form.java] est le suivant:


private String[] selectManyCheckbox=new String[]{"1","3"};

Lorsque la page [form.jsp] est demande la premire fois, la page obtenue est la suivante : 2 1

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. Les cases cocher [3] sont gnres par les lignes 5-10. Pour chacune d'elles : l'attribut itemLabel dfinit le texte affich prs de la case cocher l'attribut itemvalue dfinit la valeur qui sera poste au serveur si la case est coche. Le modle des quatre cases est le champ Java suivant :
private String[] selectManyCheckbox=new String[]{"1","3"};

Ce tableau dfinit : lorsque la page est affiche, les cases qui doivent tre coches. Ceci est fait via leur valeur, c.a.d. leur champ itemValue. Ci-dessus, les cases ayant leurs valeurs dans le tableau {"1","3"} seront coches. C'est ce qui est vu sur la copie d'cran ci-dessus. lorsque la page est poste, le modle selectManyCheckbox reoit le tableau des valeurs des cases que l'utilisateur a coches. C'est ce que nous allons voir prochainement. la ligne 12 du code Jsp gnre [4]. C'est la mthode getSelectManyCheckboxValue suivante qui a gnr [4] :
1. public String getSelectManyCheckboxValue(){ 2. return getValue(getSelectManyCheckbox()); 3. } 4. 5. private String getValue(String[] chaines){ 6. String value="["; 7. for(String chaine : chaines){ 8. value+=" "+chaine; 9. } 10. return value+"]"; 11.}

Le flux Html gnr par le code Jsf prcdent est le suivant :


1. <tr> 2. <td> 3. <input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:0" value="1" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:0"> rouge</label></td> 4. <td> 5. <input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:1" value="2" type="checkbox" /><label for="formulaire:selectManyCheckbox:1"> bleu</label></td> 6. <td> 7. <input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:2" value="3" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:2"> blanc</label></td> 8. <td> 9. <input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:3" value="4" type="checkbox" /><label for="formulaire:selectManyCheckbox:3"> noir</label></td> 10. </tr> 11. </table></td> 12. <td class="col3">[ 1 3]</td> 13. </tr>

Quatre balises Html <input type= "checkbox " ...> ont t gnres. Les balises des lignes 3 et 7 ont l'attribut checked= "checked " qui font qu'elles apparaissent coches. On notera qu'elles ont toutes le mme attribut name="formulaire:selectManyCheckbox", autrement dit les quatre champs Html ont le mme nom. Si les cases des lignes 5 et 9 sont coches par l'utilisateur, le navigateur enverra les valeurs des quatre cases cocher sous la forme :

http://tahe.developpez.com/java/javaee

285/341

formulaire:selectManyCheckbox=2&formulaire:selectManyCheckbox=4

et le modle des quatre cases


private String[] selectManyCheckbox=new String[]{"1","3"};

recevra le tableau {"2","4"}. Vrifions-le ci-dessous. En [1], on fait le changement, en [2] on valide le formulaire. En [3] le rsultat obtenu :

2 3

Les valeurs postes pour les champs [1] sont les suivantes : formulaire%3AselectManyCheckbox=2&formulaire%3AselectManyCheckbox=4

21.3.7.13

Balise <h:selectOneRadio>

La balise <h:selectOneRadio> gnre un groupe de boutons radio exclusifs les uns des autres. Considrons le code Jsf suivant :
1. <!-- ligne 13 --> 2. <h:outputText value="selectOneRadio" styleClass="info"/> 3. <h:panelGroup> 4. <h:outputText value="#{msg['form.selectOneRadioPrompt']}" /> 5. <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}"> 6. <f:selectItem itemValue="1" itemLabel="voiture"/> 7. <f:selectItem itemValue="2" itemLabel="vlo"/> 8. <f:selectItem itemValue="3" itemLabel="scooter"/> 9. <f:selectItem itemValue="4" itemLabel="marche"/> 10. </h:selectOneRadio> 11. </h:panelGroup> 12. <h:outputText value="#{form.selectOneRadio}"/>

Le modle de la balise <h:selectOneRadio> de la ligne 5 ci-dessus est le suivant dans [Form.java] :


private String selectOneRadio="2";

Lorsque la page [form.jsp] est demande la premire fois, la vue obtenue est la suivante : 2 1

4 3

la ligne 2 du code Jsp gnre [1] le texte [2] est gnr par la ligne 4. Les boutons radio [3] sont gnrs par les lignes 5-10. Pour chacun d'eux : l'attribut itemLabel dfinit le texte affich prs du bouton radio l'attribut itemvalue dfinit la valeur qui sera poste au serveur si le bouton est coch. Le modle des quatre boutons radio est le champ Java suivant :

http://tahe.developpez.com/java/javaee

286/341

private String selectOneRadio="2";

Ce modle dfinit : lorsque la page est affiche, l'unique bouton radio qui doit tre coch. Ceci est fait via leur valeur, c.a.d. leur champ itemValue. Ci-dessus, le bouton radio ayant la valeur " 2 " sera coche. C'est ce qui est vu sur la copie d'cran ci-dessus. lorsque la page est poste, le modle selectOneRadio reoit la valeur du bouton radio qui a t coch. C'est ce que nous allons voir prochainement. la ligne 12 du code Jsp gnre [4].

Le flux Html gnr par le code Jsf prcdent est le suivant :


1.<tr> 2.<td class="col1"><span class="info">selectOneRadio</span></td> 3.<td class="col2">moyen de transport pr&eacute;f&eacute;r&eacute; : <table id="formulaire:selectOneRadio"> 4. <tr> 5.<td> 6.<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:0" value="1" /><label for="formulaire:selectOneRadio:0"> voiture</label></td> 7.<td> 8.<input type="radio" checked="checked" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:1" value="2" /><label for="formulaire:selectOneRadio:1"> v&eacute;lo</label></td> 9.<td> 10.<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:2" value="3" /><label for="formulaire:selectOneRadio:2"> scooter</label></td> 11.<td> 12.<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:3" value="4" /><label for="formulaire:selectOneRadio:3"> marche</label></td> 13.</tr>

Quatre balises Html <input type= "radio" ...> ont t gnres. La balise de la ligne 8 a l'attribut checked= "checked " qui fait que le bouton radio correspondant apparat coch. On notera que les balises ont toutes le mme attribut name="formulaire:selectOneRadio", autrement dit les quatre champs Html ont le mme nom. C'est la condition pour avoir un groupe de boutons radio exclusifs : lorsque l'un est coch, les autres ne le sont pas. Ci-dessous, en [1], on coche un des boutons radio, en [2] on valide le formulaire, en [3] le rsultat obtenu :

2 3

La valeur poste pour le champ [1] est la suivante : formulaire%3AselectOneRadio=4

21.4

Exemple n 4

Thmes : formulaires dynamiques

21.4.1

L'application

L'application est la mme que prcdemment :

http://tahe.developpez.com/java/javaee

287/341

Les seuls changements proviennent de la faon dont sont gnrs les lments des listes des zones [1] et [2]. Ils sont ici gnrs dynamiquement par du code Java alors que dans la version prcdente ils taient crits " en dur " dans le code de la page Jsf.

21.4.2

Le projet Netbeans

Le projet Netbeans de l'application est le suivant :

3 1 2

http://tahe.developpez.com/java/javaee

288/341

Le projet [intro-04] est identique au projet [intro-03] aux diffrences prs suivantes : en [1], dans la page Jsf, les lments des listes ne vont plus tre crites " en dur " dans le code en [2], le modle de la page Jsf [1] va tre modifi en [3], l'un des messages va tre modifi

21.4.3

La page [form.jsp] et son modle [Form.java]

La page Jsf [form.jsp] devient la suivante :


1. ... 2. <f:view> 3. <html> 4. <head> 5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6. <title>JSF</title> 7. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 8. </head> 9. <body background="<c:url value="/ressources/standard.jpg"/>"> 10. <h:form id="formulaire"> 11. <h:panelGrid columns="2"> 12. <h:commandLink value="#{msg['form.langue1']}" action="#{locale.setFrenchLocale}"/> 13. <h:commandLink value="#{msg['form.langue2']}" action="#{locale.setEnglishLocale}"/> 14. </h:panelGrid> 15. <h1><h:outputText value="#{msg['form.titre']}"/></h1> 16. <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1"> 17. ... 18. <!-- ligne 5 --> 19. <h:outputText value="selectOneListBox (size=1)" styleClass="info"/> 20. <h:panelGroup> 21. <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/> 22. <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> 23. <f:selectItems value="#{form.selectOneListbox1Items}"/> 24. </h:selectOneListbox> 25. </h:panelGroup> 26. <h:outputText value="#{form.selectOneListBox1}"/> 27. <!-- ligne 6 --> 28. <h:outputText value="selectOneListBox (size=3)" styleClass="info"/> 29. <h:panelGroup> 30. <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/> 31. <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3"> 32. <f:selectItems value="#{form.selectOneListbox2Items}"/> 33. </h:selectOneListbox> 34. </h:panelGroup> 35. <h:outputText value="#{form.selectOneListBox2}"/> 36. <!-- ligne 7 --> 37. <h:outputText value="selectManyListBox (size=3)" styleClass="info"/> 38. <h:panelGroup> 39. <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/> 40. <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3"> 41. <f:selectItems value="#{form.selectManyListBoxItems}"/> 42. </h:selectManyListbox> 43. <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}" />" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p> 44. </h:panelGroup> 45. <h:outputText value="#{form.selectManyListBoxValue}"/> 46. <!-- ligne 8 --> 47. <h:outputText value="selectOneMenu" styleClass="info"/> 48. <h:panelGroup> 49. <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/> 50. <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}"> 51. <f:selectItems value="#{form.selectOneMenuItems}"/> 52. </h:selectOneMenu> 53. </h:panelGroup> 54. <h:outputText value="#{form.selectOneMenu}"/> 55. <!-- ligne 9 --> 56. <h:outputText value="selectManyMenu" styleClass="info"/> 57. <h:panelGroup> 58. <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" /> 59. <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" > 60. <f:selectItems value="#{form.selectManyMenuItems}"/> 61. </h:selectManyMenu> 62. <p><input type="button" value="<h:outputText value="#{msg['form.buttonRazText']}"/>" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p> 63. </h:panelGroup> 64. <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/> 65. <!-- ligne 10 --> 66. <h:outputText value="inputHidden" styleClass="info"/>

http://tahe.developpez.com/java/javaee

289/341

67. <h:inputHidden id="inputHidden" value="#{form.inputHidden}"/> 68. <h:outputText value="#{form.inputHidden}"/> 69. <!-- ligne 11 --> 70. ... 71. <!-- ligne 12 --> 72. <h:outputText value="selectManyCheckbox" styleClass="info"/> 73. <h:panelGroup> 74. <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" /> 75. <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}"> 76. <f:selectItems value="#{form.selectManyCheckboxItems}"/> 77. </h:selectManyCheckbox> 78. </h:panelGroup> 79. <h:outputText value="#{form.selectManyCheckboxValue}"/> 80. <!-- ligne 13 --> 81. <h:outputText value="selectOneRadio" styleClass="info"/> 82. <h:panelGroup> 83. <h:outputText value="#{msg['form.selectOneRadioPrompt']}" /> 84. <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}"> 85. <f:selectItems value="#{form.selectOneRadioItems}"/> 86. </h:selectOneRadio> 87. </h:panelGroup> 88. <h:outputText value="#{form.selectOneRadio}"/> 89. </h:panelGrid> 90. <p> 91. <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/> 92. </p> 93. </h:form> 94. </body> 95. </html> 96. </f:view>

Les modifications apportes sont illustres par les lignes 22 24. L o auparavant, on avait le code :
1. <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> 2. <f:selectItem itemValue="1" itemLabel="un"/> 3. <f:selectItem itemValue="2" itemLabel="deux"/> 4. <f:selectItem itemValue="3" itemLabel="trois"/> 5. </h:selectOneListbox>

on a dsormais celui-ci :
a) <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> b) <f:selectItems value="#{form.selectOneListbox1Items}"/> c) </h:selectOneListbox>

Les trois balises <f:selectItem> des lignes 2-4 ont t remplaces par l'unique balise <f:selectItems> de la ligne b. Cette balise a un attribut value dont la valeur est une collection d'lments de type javax.faces.model.SelectItem. Ci-dessus, la valeur de l'attribut value va tre obtenue par appel de la mthode [form].getSelectOneListbox1Items suivante :
1. 2. 3. public SelectItem[] getSelectOneListbox1Items() { return getItems("A",3); }

4.

5. private SelectItem[] getItems(String label, int qte) { 6. SelectItem[] items=new SelectItem[qte]; 7. for(int i=0;i<qte;i++){ 8. items[i]=new SelectItem(i,label+i); 9. } 10. return items; 11.}

ligne 1, la mthode getSelectOneListbox1Items rend un tableau d'lments de type javax.faces.model.SelectItem construit par la mthode prive getItems de la ligne 5. On notera que la mthode getSelectOneListbox1Items n'est pas le getter d'un champ priv selectOneListBox1Items. la classe javax.faces.model.SelectItem a divers constructeurs.

http://tahe.developpez.com/java/javaee

290/341

Nous utilisons ligne 8 de la mthode getItems, le constructeur SelectItem(Object value, String label) qui correspond la balise Jsf <f:selectItem itemValue= "value " labelValue= "label "/>

lignes 5-10 : la mthode getItems(String label, int qte) construit un tableau de qte lments de type SelectItem, o l'lment i est obtenu par le constructeur SelectItem(i, label+i).

Le code Jsf
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> <f:selectItems value="#{form.selectOneListbox1Items}"/> </h:selectOneListbox>

devient alors fonctionnellement quivalent au code Jsf suivant :


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1"> <f:selectItem itemValue="0" itemLabel="A0"/> <f:selectItem itemValue="1" itemLabel="A1"/> <f:selectItem itemValue="2" itemLabel="A2"/> </h:selectOneListbox>

Il est fait de mme pour toutes les autres listes de la page Jsf. On trouve ainsi dans le modle [Form.java] les nouvelles mthodes suivantes :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. public SelectItem[] getSelectOneListbox1Items() { return getItems("A",3); } public SelectItem[] getSelectOneListbox2Items() { return getItems("B",4); } public SelectItem[] getSelectManyListBoxItems() { return getItems("C",5); } public SelectItem[] getSelectOneMenuItems() { return getItems("D",3); } public SelectItem[] getSelectManyMenuItems() { return getItems("E",4); } public SelectItem[] getSelectManyCheckboxItems() { return getItems("F",3); } public SelectItem[] getSelectOneRadioItems() { return getItems("G",4); } private SelectItem[] getItems(String label, int qte) { SelectItem[] items=new SelectItem[qte]; for(int i=0;i<qte;i++){ items[i]=new SelectItem(i,label+i); } return items;

http://tahe.developpez.com/java/javaee

291/341

35.}

21.4.4

Le fichier des messages

Un seul message est modifi : [messages_fr.properties]


form.titre=Java Server Faces - remplissage dynamique des listes

[messages_en.properties]

form.titre=Java Server Faces - dynamic filling of lists of elements

21.4.5

Tests

Le lecteur est invit tester cette nouvelle version. Le plus souvent les lments dynamiques d'un formulaire sont les rsultats d'un traitement mtier ou proviennent d'une base de donnes :

Application web
couche [web]
1

Faces Servlet
5

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles
4

Etudions la demande initiale de la page Jsf [form.jsp] par un GET du navigateur : la page Jsf est demande [1] le contrleur [Faces Servlet] demande son affichage en [3]. Le moteur Jsf qui traite la page fait appel au modle [Form.java] de celle-ci, par exemple la mthode getSelectOneListBox1Items. Cette mthode pourrait trs bien rendre un tableau d'lments de type SelectItem, partir d'informations enregistres dans une base de donnes. Pour cela, elle ferait appel la couche [mtier] [4].

21.5

Exemple n 5

Thmes : navigation, session, pages Jsp, gestion des exceptions

21.5.1

L'application

L'application est la mme que prcdemment si ce n'est que le formulaire se prsente dsormais sous la forme d'un assistant plusieurs pages :

en [1], la page 1 du formulaire - peut tre obtenue galement par le lien 1 de [2]

http://tahe.developpez.com/java/javaee

292/341

en [2], un groupe de 5 liens. en [3], la page 2 du formulaire obtenue par le lien 2 de [2] 4

en [4], la page 3 du formulaire obtenue par le lien 3 de [2] en [5], la page obtenue par le lien Lancer une exception de [2]

http://tahe.developpez.com/java/javaee

293/341

en [6], la page obtenue par le lien 4 de [2]. Elle rcapitule les saisies faites dans les pages 1 3.

21.5.2

Le projet Netbeans

Le projet Netbeans de l'application est le suivant :

3 1

http://tahe.developpez.com/java/javaee

294/341

Le projet [intro-05] introduit deux nouveauts : 1. en [1], la page Jsf [form.jsp] est scinde en trois pages [form1.jsp, form2.jsp, form3.jsp] sur lesquelles ont t rparties les saisies. La page [form4.jsp] est une copie de la page [form.jsp] du projet prcdent. En [2], la classe [Form.java] reste inchange. Elle va servir de modle aux quatre pages Jsf prcdentes. 2. en [3], une page Jsf est ajoute : elle sera utilise lorsque se produira une exception dans l'application. Nous allons tout d'abord tudier le point 1 qui ne prsente pas de difficults. Nous tudierons ensuite le point 2 qui lui en prsente.

21.5.3
21.5.3.1

Les pages [form.jsp] et leur modle [Form.java]


Le code des pages Jsp

La page Jsf [form1.jsp] est la suivante :


1. ... 2. <f:view> 3. <html> 4. <head> 5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6. <title>JSF</title> 7. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 8. </head> 9. <body background="<c:url value="/ressources/standard.jpg"/>"> 10. <h:form id="formulaire"> 11. <h:panelGrid columns="2"> 12. <h:commandLink value="#{msg['form.langue1']}" action="#{locale.setFrenchLocale}"/> 13. <h:commandLink value="#{msg['form.langue2']}" action="#{locale.setEnglishLocale}"/> 14. </h:panelGrid> 15. <h1><h:outputText value="#{msg['form1.titre']}"/></h1> 16. <h:panelGrid columnClasses="col1,col2" columns="2" border="1"> 17. <!-- ligne 1 --> 18. <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/> 19. <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/> 20. <!-- ligne 2 --> 21. <h:outputText value="inputText" styleClass="info"/> 22. <h:panelGroup> 23. <h:outputText value="#{msg['form.loginPrompt']}"/> 24. <h:inputText id="inputText" value="#{form.inputText}"/> 25. </h:panelGroup> 26. <!-- ligne 3 --> 27. <h:outputText value="inputSecret" styleClass="info"/> 28. <h:panelGroup> 29. <h:outputText value="#{msg['form.passwdPrompt']}"/> 30. <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/> 31. </h:panelGroup> 32. <!-- ligne 4 --> 33. <h:outputText value="inputTextArea" styleClass="info"/> 34. <h:panelGroup> 35. <h:outputText value="#{msg['form.descPrompt']}"/> 36. <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/> 37. </h:panelGroup> 38. </h:panelGrid> 39. <!-- liens --> 40. <h:panelGrid columns="6"> 41. <h:commandLink value="1" action="form1"/> 42. <h:commandLink value="2" action="#{form.doAction2}"/> 43. <h:commandLink value="3" action="form3"/> 44. <h:commandLink value="4" action="#{form.doAction4}"/> 45. <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/> 46. <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/> 47. </h:panelGrid> 48. </h:form> 49. </body> 50. </html> 51. </f:view>

et correspond l'affichage suivant :

http://tahe.developpez.com/java/javaee

295/341

2 On notera les points suivants : ligne 16, le tableau qui avait auparavant 3 colonnes, n'en a plus que 2. La colonne 3 qui affichait les valeurs du modle a t supprime. C'est [form4.jsp] qui les affichera. lignes 40-46 : un tableau de 6 liens. Les liens des lignes 41et 43 ont une navigation statique : leur attribut action est cod en dur. Les autres liens ont une navigation dynamique : leur attribut action pointe sur une mthode du bean form charge de rendre la cl de navigation. Les mthodes rfrences dans [Form.java] sont les suivantes :
1.// vnements 2. public String doAction2(){ 3. return "form2"; 4. } 5. 6. public String doAction4(){ 7. return "form4"; 8. } 9. 10. public String doAlea(){ 11. // un nombre alatoire entre 1 et 3 12. int i=1+(int)(3*Math.random()); 13. // on rend la cl de navigation 14. return "form"+i; 15. } 16. 17. public String throwException() throws java.lang.Exception{ 18. throw new Exception("Exception test"); 19. }

Nous ignorerons pour l'instant la mthode throwException de la ligne 17. Nous y reviendrons ultrieurement. Les mthodes doAction2 et doAction4 se contentent de rendre la cl de navigation sans faire de traitement. On aurait donc tout ausi bien pu crire :
<!-- liens --> <h:panelGrid columns="6"> <h:commandLink value="1" action="form1"/> <h:commandLink value="2" action="form2"/> <h:commandLink value="3" action="form3"/> <h:commandLink value="4" action="form4"/> <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/> <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/> </h:panelGrid>

La mthode doAlea gnre, elle, une cl de navigation alatoire qui prend sa valeur dans l'ensemble {"form1","form2","form3"}. Les rgles de navigation correspondant aux 4 cls sont prsentes dans le fichier [facesconfig.xml] :
1. ... 2. <!-- rgles de navigation --> 3. <navigation-rule> 4. <from-view-id>*</from-view-id> 5. <navigation-case> 6. <from-outcome>form1</from-outcome> 7. <to-view-id>/form1.jsp</to-view-id> 8. </navigation-case>

http://tahe.developpez.com/java/javaee

296/341

9. <navigation-case> 10. <from-outcome>form2</from-outcome> 11. <to-view-id>/form2.jsp</to-view-id> 12. </navigation-case> 13. <navigation-case> 14. <from-outcome>form3</from-outcome> 15. <to-view-id>/form3.jsp</to-view-id> 16. </navigation-case> 17. <navigation-case> 18. <from-outcome>form4</from-outcome> 19. <to-view-id>/form4.jsp</to-view-id> 20. </navigation-case> 21. </navigation-rule> 22. </faces-config>

ligne 4, on indique que les cas de navigation qui suivent sont valables quelque soit la page de dpart de la navigation. C'est le sens de la chane *. lignes 5-8 : lorsque le contrleur [Faces Servlet] reoit la cl de navigation form1 (ligne 6), il doit afficher la page Jsf /form1.jsp. on retrouve des cas de navigation similaires lignes 9-21.

Le code des pages [form2.jsp, form3.jsp, form3.jsp] est analogue celle de la page [form1.jsp].

21.5.3.2

Dure de vie du modle [Form.java] des pages [form*.jsp]

Considrons la squence d'actions suivantes :

en [1], on remplit la page 1 et on passe la page 3 en [2], on remplit la page 3 et on revient la page 1

http://tahe.developpez.com/java/javaee

297/341

en [3], on retrouve la page 1 telle qu'on l'a saisie. On revient alors la page 3 en [4], la page 3 est retrouve telle qu'elle a t saisie.

Le mcanisme du champ cach [javax.faces.ViewState] ne suffit pas expliquer ce phnomne. Lors du passage de [1] [2], plusieurs tapes ont lieu : le modle [Form.java] est mis jour avec le POST de [form1.jsp]. Notamment, le champ inputText reoit la valeur "un autre texte". la cl de navigation "form3" provoque l'affichage de [form3.jsp]. Le ViewState embarqu dans [form3.jsp] est l'tat des seuls composants de [form3.jsp] pas ceux de [form1.jsp]. Lors du passage de [2] [3] : le modle [Form.java] est mis jour avec le POST de [form3.jsp]. Si la dure de vie du modle [Form.java] est request, un objet [Form.java] tout neuf est cr avant d'tre mis jour par le POST de [form3.jsp]. Dans ce cas, le champ inputText du modle retrouve sa valeur par dfaut :
private String inputText="texte";

et la garde : en effet dans le POST de [form3.jsp], rien ne vient mettre jour le champ inputText qui fait partie du modle de [form1.jsp] et non de celui de [form3.jsp]. la cl de navigation "form1" provoque l'affichage de [form1.jsp]. La page affiche son modle. Dans notre cas, le champ de saisie login li au modle inputText affichera texte et non pas la valeur "un autre texte" saisie en [1]. Pour que le champ inputText garde la valeur saisie en [1], la dure de vie du modle [Form.java] doit tre session et non request. Dans ce cas, l'issue du POST de [form1.jsp], il sera plac dans la session du client. Le champ inputText aura la valeur "un autre texte". au moment du POST de [form3.jsp], il sera recherch dans cette session et mis jour par le POST de [form3.jsp]. Le champ inputText ne sera pas mis jour par ce POST mais gardera la valeur "un autre texte" acquise l'issue du POST de [form1.jsp] [1].

Le fichier [faces-config.xml] volue donc comme suit :


1. ... 2. <faces-config version="1.2" 3. xmlns="http://java.sun.com/xml/ns/javaee" 4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"> 6. 7. .... 8. 9. <!-- beans manags --> 10. <managed-bean> 11. <managed-bean-name>form</managed-bean-name> 12. <managed-bean-class>forms.Form</managed-bean-class> 13. <managed-bean-scope>session</managed-bean-scope> 14. </managed-bean> 15. <managed-bean>

http://tahe.developpez.com/java/javaee

298/341

16. <managed-bean-name>locale</managed-bean-name> 17. <managed-bean-class>utils.ChangeLocale</managed-bean-class> 18. <managed-bean-scope>application</managed-bean-scope> 19. </managed-bean> 20. 21. <!-- rgles de navigation --> 22. <navigation-rule> 23. ... 24. </navigation-rule> 25. </faces-config>

ligne 13, la dure de vie session du bean form.

21.5.4

Gestion des exceptions

Revenons sur l'architecture gnrale d'une application Jsf :

Application web
couche [web]
1

Faces Servlet
5

2a 3

2b

Navigateur

JSP1 JSP2 JSPn

Gestionnaire d'vts

couche [metier]

couche [dao]

Donnes

Modles
4

Que se passe-t-il lorsque un gestionnaire d'vnement ou bien un modle rcupre une exception provenant de la couche mtier, une dconnexion imprvue d'avec une base de donnes par exemple ?

les gestionnaires d'vnements [2a] peuvent intercepter toute exception remontant de la couche [mtier] et rendre au contrleur [Faces Servlet] une cl de navigation vers une page d'erreur spcifique l'exception. pour les modles cette solution n'est pas utilisable car lorsqu'ils sont sollicits [3,4], on est dans la phase de rendu d'une page Jsp prcise et plus dans la phase de choix de celle-ci. Comment faire pour changer de page alors mme qu'on est dans la phase de rendu de l'une d'elles ? Une solution simple mais qui ne convient pas toujours est de ne pas grer l'exception qui va alors remonter jusqu'au conteneur de servlets qui excute l'application. Celle-ci peut tre configure pour afficher une page particulire lorsqu'une exception remonte jusqu'au conteneur de servlets. Cette solution est toujours utilisable et nous l'examinons maintenant.

21.5.4.1

Configuration de l'application web pour la gestion des exceptions

La configuration d'une application web pour la gestion des exceptions se fait dans son fichier [web.xml] :
1. <?xml version="1.0" encoding="UTF-8"?> 2. <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/webapp_2_5.xsd"> 3. <display-name>intro-05</display-name> 4. ... 5. <servlet> 6. <servlet-name>Faces Servlet</servlet-name> 7. <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 8. <load-on-startup>1</load-on-startup> 9. </servlet> 10. <servlet-mapping> 11. <servlet-name>Faces Servlet</servlet-name> 12. <url-pattern>/faces/*</url-pattern> 13. </servlet-mapping> 14. ... 15. <error-page> 16. <error-code>500</error-code> 17. <location>/faces/exception.jsp</location> 18. </error-page> 19. <error-page> 20. <exception-type>java.lang.Exception</exception-type>

http://tahe.developpez.com/java/javaee

299/341

21. <location>/faces/exception.jsp</location> 22. </error-page> 23. 24. </web-app>

Lignes 15-22, on trouve la dfinition de deux pages d'erreur. On peut avoir autant de balises <error-page> que ncessaires. La balise <location> indique la page afficher en cas d'erreur. Le type de l'erreur associe la page peut tre dfini de deux faons :

par la balise <exception-type> qui dfinit le type Java de l'exception gre. Ainsi la balise <error-page> des lignes 19-22 indique que si le conteneur de servlets rcupre une exception de type [java.lang.Exception] ou driv (ligne 20) au cours de l'excution de l'application, alors il doit faire afficher la page [/faces/exception.jsp] (ligne 21). En prenant ici, le type d'exception le plus gnrique [java.lang.Exception], on s'assure de grer toutes les exceptions. par la balise <error-code> qui dfinit un code Http d'erreur. Par exemple, si un navigateur demande l'Url [http://machine:port/contexte/P] et que la page P n'existe pas dans l'application contexte, celle-ci n'intervient pas dans la rponse. C'est le conteneur de servlets qui gnre cette rponse en envoyant une page d'erreur par dfaut. La premire ligne du flux Http de sa rponse contient un code d'erreur 404 indiquant que la page P demande n'existe pas. On peut vouloir gnrer une rponse qui par exemple respecte la charte graphique de l'application ou qui donne des liens pour rsoudre le problme. Dans ce cas, on utilisera une balise <error-page> avec une balise <error-code>404</errorcode>. Ci-dessus, le code Http d'erreur 500 est le code renvoy en cas de " plantage " de l'application. C'est le code qui serait renvoy si une exception remontait jusqu'au conteneur de servlets. Les deux balises <error-page> des lignes 15-22 sont donc probablement redondantes. On les a mises toutes les deux pour illustrer les deux faons de grer une erreur.

21.5.4.2

La simulation de l'exception

Une exception est produite artificiellement par le lien [Lancer une exception] :

2 1

Un clic sur le lien [Lancer une exception] [1] provoque l'affichage de la page [2]. Dans le code des pages [formx.jsp], le lien [Lancer une exception] est gnr de la faon suivante :
1. <!-- liens --> 2. <h:panelGrid columns="6"> 3. <h:commandLink value="1" action="form1"/> 4. ... 5. <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/> 6. </h:panelGrid>

Ligne 5, on voit que lors d'un clic sur le lien, la mthode [form].throwException va tre excute. Celle-ci est la suivante :
1. public String throwException() throws java.lang.Exception{ 2. throw new Exception("Exception test"); 3.}

On y lance une exception de type [java.lang.Exception]. Elle va remonter jusqu'au conteneur de servlets qui va alors afficher la page [/faces/exception.jsp].

http://tahe.developpez.com/java/javaee

300/341

21.5.4.3

Les informations lies une exception

Lorsqu'une exception remonte jusqu'au conteneur de servlets, celui-ci va faire afficher la page d'erreur correspondante en transmettant celles-ci des informations sur l'exception. Celles-ci sont places comme nouveaux attributs de la requte en cours de traitement. La requte d'un navigateur et la rponse qu'il va recevoir sont encapsules dans des objets Java de type [HttpServletRequest request] et [HttpServletResponse response]. Ces objets sont disponibles tous les tapes de traitement de la requte du navigateur.

request, response Navigateur Conteneur de servlets t1 t2 tn

A rception de la requte Http du navigateur, le conteneur de servlets encapsule celle-ci dans l'objet Java [HttpServletRequest request] et cre l'objet [HttpServletResponse response] qui va permettre de gnrer la rponse. Dans cet objet, on trouve notamment le canal tcp-ip utiliser pour le flux Http de la rponse. Tous les couches t1, t2, ..., tn qui vont intervenir dans le traitement de l'objet request, ont accs ces deux objets. Chacune d'elles peut avoir accs aux lments de la requte initiale request, et prparer la rponse en enrichissant l'objet response. Une couche de localisation pourra par exemple fixer la localisation de la rponse par la mthode response.setLocale(Locale l). Les diffrentes couches ti peuvent se passer des informations via l'objet request. Celui-ci a un dictionnaire d'attributs, vide sa cration, qui peut tre enrichi par les couches de traitement successives. Celles-ci peuvent mettre dans les attributs de l'objet request des informations ncessaires la couche de traitement suivante. Il existe deux mthodes pour grer les attributs de l'objet request : void setAttribute(String s, Object o) qui permet d'ajouter aux attributs un objet o identifi par la chane s Object getAttribute(String s) qui permet d'obtenir l'attribut o identifi par la chane s Lorsqu'une exception remonte jusqu'au conteneur de servlets, ce dernier met les attributs suivants dans la requte en cours de traitement : cl
javax.servlet.error.status_code javax.servlet.error.message javax.servlet.error.exception_type javax.servlet.error.request_uri javax.servlet.error.servlet_name

valeur le code d'erreur Http qui va tre renvoy au client le message li l'exception le type Java de l'exception l'Url demande lorsque l'exception s'est produite la servlet qui traitait la requte lorsque l'exception s'est produite

Nous utiliserons ces attributs de la requte dans la page [exception.jsp] pour les afficher.

21.5.4.4

La page d'erreur [exception.jsp]

Son contenu est le suivant :


1. <%@page contentType="text/html" pageEncoding="UTF-8"%> 2. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 4. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 5. <%@page import="java.util.Locale,javax.faces.context.FacesContext"%> 6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 7. "http://www.w3.org/TR/html4/loose.dtd"> 8. 9. <% 10. // on rcupre la locale dans la session 11. String codeLocale = (String) session.getAttribute("codeLocale"); 12. if (codeLocale == null) { 13. codeLocale = "fr"; 14. } 15. // on fixe la locale 16. FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale(codeLocale)); 17. // on change le code Http de la page pour certaines versions d'IE 18. response.setStatus(HttpServletResponse.SC_OK);

http://tahe.developpez.com/java/javaee

301/341

19. %> 20. 21. 22. <f:view> 23. <html> 24. <head> 25. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 26. <title>JSF</title> 27. <link href="<c:url value="/styles.css"/>" rel="stylesheet" type="text/css"/> 28. </head> 29. <body background="<c:url value="/ressources/standard.jpg"/>"> 30. 31. <h3><h:outputText value="#{msg['exception.header']}"/></h3> 32. <table border="1"> 33. <tr> 34. <td><h:outputText value="#{msg['exception.httpCode']}"/></td> 35. <td><h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/></td> 36. </tr> 37. <tr> 38. <td><h:outputText value="#{msg['exception.message']}"/></td> 39. <td><h:outputText value="#{requestScope['javax.servlet.error.message']}"/></td> 40. </tr> 41. <tr> 42. <td><h:outputText value="#{msg['exception.exceptionClassName']}"/></td> 43. <td><h:outputText value="#{requestScope['javax.servlet.error.exception_type']}"/></td> 44. </tr> 45. <tr> 46. <td><h:outputText value="#{msg['exception.requestUri']}"/></td> 47. <td><h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/></td> 48. </tr> 49. <tr> 50. <td><h:outputText value="#{msg['exception.servletName']}"/></td> 51. <td><h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/></td> 52. </tr> 53. </table> 54. <!-- liens --> 55. <table> 56. <tr> 57. <td><a href="<c:url value="/faces/form1.jsp"/>"/>1</td> 58. <td><a href="<c:url value="/faces/form2.jsp"/>"/>2</td> 59. <td><a href="<c:url value="/faces/form3.jsp"/>"/>3</td> 60. <td><a href="<c:url value="/faces/form4.jsp"/>"/>4</td> 61. </tr> 62. </table> 63. </body> 64. </html> 65. </f:view>

21.5.4.4.1

Les expressions de la page d'exception

Dans la chane de traitement de la requte du client, la page Jsp est normalement le dernier maillon de la chane :

request, response Navigateur Conteneur de servlets t1 t2 jsp

Tous les lments de la chane sont des classes Java, y compris la page Jsp. Celle-ci est en effet transforme en servlet par le conteneur de servlets, c.a.d. en une classe Java normale. Plus prcisment, la page Jsp est transforme en code Java qui s'excute au sein de la mthode suivante :
1. 2. 3. 4. 5. 6. 7. 8. 9. public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { JspFactory _jspxFactory = null; PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null;

http://tahe.developpez.com/java/javaee

302/341

10. Object page = this; 11. JspWriter _jspx_out = null; 12. PageContext _jspx_page_context = null; 13. ... 14. ...code de la page Jsp 15.

A partir de la ligne 14, on trouvera le code Java image de la page Jsp. Ce code va disposer d'un certain nombre d'objets initialiss par la mthode _jspService, ligne 1 ci-dessus :

ligne 1 : HttpServletRequest request : la requte en cours de traitement ligne 1 : HttpServletResponse response : la rponse qui va tre envoye au client ligne 7 : ServletContext application : un objet qui reprsente l'application web elle-mme. Comme l'objet request, l'objet application peut avoir des attributs. Ceux-ci sont partags par toutes les requtes de tous les clients. Ce sont en gnral des attributs en lecture seule. ligne 6 : HttpSession session : reprsente la session du client. Comme les objets request et application, l'objet session peut avoir des attributs. Ceux-ci sont partags par toutes les requtes d'un mme client. Nous utiliserons cet objet pour y placer le code langue de la page Jsp et des pages Jsf. ligne 9 : JspWriter out : un flux d'criture vers le navigateur client. Cet objet est utile pour le dbogage d'une page Jsp. Tout ce qui est crit via out.println(texte) sera affich dans le navigateur client.

Lorsque dans la page Jsf, on crit #{expression}, expression peut tre la cl d'un attribut des objets request, session ou application ci-dessus. L'attribut correspondant est cherch successivement dans ces trois objets. Ainsi #{cl} est value de la faon suivante : 1. 2. 3. request.getAttribute(cl) session.getAttribute(cl) application.getAttribute(cl)

Ds qu'une valeur non null est obtenue, l'valuation de #{cl} est arrte. On peut vouloir tre plus prcis en indiquant le contexte dans lequel l'attribut doit tre cherch :

#{requestScope['cl']} pour chercher l'attribut dans l'objet request #{sessionScope['cl']} pour chercher l'attribut dans l'objet session #{applicationScope['cl']} pour chercher l'attribut dans l'objet application

C'est ce qui a t fait dans la page [exception.jsp] page 301. Les attributs utiliss sont les suivants : cl
codeLocale javax.servlet.error.status_code javax.servlet.error.message javax.servlet.error.exception_type javax.servlet.error.request_uri javax.servlet.error.servlet_name

domaine session request idem idem idem idem

valeur le code langue du client est mis dans la session lorsque l'utilisateur utilise les liens de choix de langue cf paragraphe 21.5.4.3, page 301. idem idem idem idem

Pour terminer, on notera le dbut du code de la page [exception.jsp] :


1. 2. 3. 4. 5. 6. 7. 8. 9. ... <% .... // on change le code Http de la page pour certaines versions d'IE response.setStatus(HttpServletResponse.SC_OK); %> <fmt_rt:setLocale value="${sessionScope['codeLocale']}"/> ...

Le navigateur Internet Explorer n'affiche pas les rponses HTML accompagnes d'un code erreur Http 500. Mozilla Firefox lui les affiche. Afin que la page d'exception soit affiche par les deux navigateurs, on change le code erreur de la rponse Http en ligne 4. Le code HttpServletResponse.SC_OK est le code 200 qui indique que la page demande a t trouve. Dans ce cas, les navigateurs vont l'afficher.

http://tahe.developpez.com/java/javaee

303/341

21.5.4.4.2

Localisation de la page d'exception et des autres pages Jsf

Nous souhaitons avoir une page d'erreur localise, c.a.d. capable de s'afficher en plusieurs langues. Les tests montrent que si on garde le mcanisme de localisation utilis dans les exemples prcdents, la page [exception.jsp] s'affiche toujours en franais. Ci-dessous, la page [form1] est affiche en anglais [1]. Si on utilise le lien [Throw an exception] pour lancer une exception, alors la page d'exception n'apparat pas en anglais mais en franais [2].

Si dans la page [2] on utilise l'un des liens [3], la page demande sera affiche en franais. La raison de la perte de la locale des pages n'a pas t trouve. Pour palier ce problme, on dcide de mmoriser le code langue choisi, dans la session du client. Cela se fait dans les mthodes charges de grer les liens de choix de langue [3] :
1. package utils; 2. 3. import java.util.Locale; 4. import javax.faces.context.FacesContext; 5. import javax.servlet.http.HttpServletRequest; 6. import javax.servlet.http.HttpSession; 7. 8. public class ChangeLocale { 9. 10. /** Creates a new instance of ChangeLocale */ 11. public ChangeLocale() { 12. } 13. 14. public String setFrenchLocale(){ 15. changeLocale("fr"); 16. return null; 17. } 18. 19. public String setEnglishLocale(){ 20. changeLocale("en"); 21. return null; 22. } 23. 24. private void changeLocale(String codeLocale){ 25. // on mmorise la locale dans la session 26. HttpSession session=((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()). getSession(); 27. session.setAttribute("codeLocale",codeLocale); 28. } 29. }

Lignes 24-28, la mthode prive changeLocale mmorise dans la session du client, un attribut de cl codeLocale et de valeur le code langue choisi par l'utilisateur. Les pages Jsf [formi] retrouveront alors leur locale d'affichage de la faon suivante :
1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. <%@page import="java.util.Locale,javax.faces.context.FacesContext"%>

http://tahe.developpez.com/java/javaee

304/341

4. ... 5. <% 6. // on rcupre la locale dans la session 7. String codeLocale=(String)session.getAttribute("codeLocale"); 8. if(codeLocale==null){ 9. codeLocale="fr"; 10. } 11. // on fixe la locale 12. FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale(codeLocale)); 13. %> 14. 15. <f:view> 16. <html> 17. ...

lignes 5-13 : du code Java excut avant traitement de la vue <f:view> (ligne 15) de la page Jsf ligne 7 : on rcupre l'attribut de cl codeLocale dans la session lignes 8-10 : si cet attribut n'existe pas, on fixe la langue au franais ligne 12 : on fixe la langue de la vue Jsf qui va tre affiche. On retrouve l le code qui auparavant tait dans la mthode changeLocale de la classe ChangeLocale. ligne 3 : directive d'import des classes utilises dans le code des lignes 5-13

Les diffrents messages ncessaires la page Jsf [exception.jsp] ont t rajouts aux fichiers de messages dj existants : [messages_fr.properties]
exception.header=L'exception suivante s'est produite exception.httpCode=Code HTTP de l'erreur exception.message=Message de l'exception exception.exceptionClassName=Classe de l'exception exception.requestUri=Url demande lors de l'erreur exception.servletName=Nom de la servlet demande lorsque l'erreur s'est produite

[messages_en.properties]
exception.header=The following error occurred exception.httpCode=HTTP error code exception.message=Exception message exception.exceptionClassName=Exception class exception.requestUri=Url requested when error occurred exception.servletName=Servlet requested when error occurred

21.5.4.4.3

Internet Explorer

Pour terminer, on notera le dbut du code de la page [exception.jsp] :


10. ... 11. <% 12. ..... 13. // on change le code Http pour certaines versions d'IE 14. response.setStatus(HttpServletResponse.SC_OK); 15. %> 16. ...

Certaines versions d'Internet Explorer n'affiche pas les rponses HTML qui sont accompagnes d'un code erreur Http 500. Mozilla Firefox lui les affiche. Afin que la page d'exception soit affiche par les deux navigateurs, on change le code erreur de la rponse Http en ligne 5. Le code HttpServletResponse.SC_OK est le code 200 qui indique que la page demande a t trouve. Dans ce cas, les navigateurs vont l'afficher.

21.6

Exemple n 6

Thme : Validation et conversion des saisies d'un formulaire

21.6.1

L'application

L'application prsente un formulaire de saisies. A la validation de celui-ci, le mme formulaire est renvoy avec d'ventuels messages d'erreurs si les saisies ont t trouves incorrectes.

http://tahe.developpez.com/java/javaee

305/341

http://tahe.developpez.com/java/javaee

306/341

21.6.2

Le projet Netbeans

Le projet Netbeans de l'application est le suivant :

Le projet [intro-06] repose de nouveau sur une unique page [form.jsp] [1] et son modle [Form.java] [2]. Il continue utiliser des messages tirs de [messages.properties] mais uniquement en franais [3]. L'option de changement de langue n'est pas offerte.

21.6.3

La page [form.jsp] et son modle [Form.java]

La page [form.jsp] est la suivante :


1. <%@page contentType="text/html"%> 2. <%@page pageEncoding="UTF-8"%> 3. 4. <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> 5. <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> 6. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 7. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 9. "http://www.w3.org/TR/html4/loose.dtd"> 10. 11. <html> 12. <head> 13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 14. <title>dmo JSF</title> 15. <link href="styles.css" rel="stylesheet" type="text/css"/> 16. </head> 17. <body background="<c:url value="/ressources/standard.jpg"/>"> 18. <f:view> 19. <h2><h:outputText value="#{msg['form.titre']}"/></h2> 20. <h:form id="formulaire"> 21. <h:messages globalOnly="true"/> 22. <h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1"> 23. <!-- headers --> 24. <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/> 25. <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/> 26. <h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/> 27. <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/> 28. <!-- ligne 1 --> 29. <h:outputText value="#{msg['saisie1.prompt']}"/> 30. <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/> 31. <h:message for="saisie1" styleClass="error"/> 32. <h:outputText value="#{form.saisie1}"/> 33. <!-- ligne 2 --> 34. <h:outputText value="#{msg['saisie2.prompt']}" /> 35. <h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/> 36. <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/> 37. <h:outputText value="#{form.saisie2}"/> 38. <!-- ligne 3 --> 39. <h:outputText value="#{msg['saisie3.prompt']}" /> 40. <h:inputText id="saisie3" value="#{form.saisie3}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/> 41. <h:message for="saisie3" styleClass="error"/> 42. <h:outputText value="#{form.saisie3}"/> 43. <!-- ligne 4 -->

http://tahe.developpez.com/java/javaee

307/341

44. 45.

<h:outputText value="#{msg['saisie4.prompt']}" /> <h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}"> 46. <f:validateLongRange minimum="1" maximum="10" /> 47. </h:inputText> 48. <h:message for="saisie4" styleClass="error"/> 49. <h:outputText value="#{form.saisie4}"/> 50. <!-- ligne 5 --> 51. ... 52. <!-- ligne 6 --> 53. ... 54. <!-- ligne 7 --> 55. ... 56. <!-- ligne 8 --> 57. ... 58. <!-- ligne 9 --> 59. ... 60. <!-- ligne 10 --> 61. ... 62. <!-- ligne 11 --> 63. ... 64. <!-- ligne 12 --> 65. ... 66. </h:panelGrid> 67. <!-- boutons de commande --> 68. <h:panelGrid columns="2"> 69. <h:commandButton value="#{msg.submit}" action="#{form.submit}"/> 70. <h:commandButton value="#{msg.cancel}" immediate="true" action="#{form.cancel}"/> 71. </h:panelGrid> 72. </h:form> 73. </f:view> 74. </body> 75. </html>

La principale nouveaut vient de la prsence de balises : pour afficher des messages d'erreurs <h:messages>(ligne 21), <h:message> (lignes 31, 36, ...) qui posent des contraintes de validit sur les saisies <f:validateLongRange> (ligne 46), <f:validateDoubleRange>, <f:validateLength> qui dfinissent un convertisseur entre la saisie et son modle comme <f:convertDateTime>. Le modle de cette page est la classe [Form.java] suivante :
1. package forms; 2. 3. ... 4. public class Form { 5. 6. 7. public Form() { 8. } 9. 10. // saisies 11. private Integer saisie1 = 0; 12. private Integer saisie2 = 0; 13. private Integer saisie3 = 0; 14. private Integer saisie4 = 0; 15. private Double saisie5 = 0.0; 16. private Double saisie6 = 0.0; 17. private Boolean saisie7 = true; 18. private Date saisie8 = new Date(); 19. private String saisie9 = ""; 20. private Integer saisie10 = 0; 21. private Integer saisie11 = 0; 22. private Integer saisie12 = 0; 23. private String errorSaisie11 = ""; 24. private String errorSaisie12 = ""; 25. 26. // actions 27. public String submit() { 28. ... 29. } 30. 31. public String cancel() { 32. ... 33. } 34. 35. // validateurs 36. public void validateSaisie10(FacesContext context, UIComponent component, Object value) {

http://tahe.developpez.com/java/javaee

308/341

37. .... 38. } 39. 40. 41. // getters et setters 42. ... 43. }

La nouveaut ici est que les champs du modle ne sont plus uniquement de type String mais de types divers.

21.6.4

L'environnement de l'application

Nous donnons ici la teneur des fichiers qui configurent l'application sans donner d'explications particulires. Ces fichiers permettent de mieux comprendre ce qui suit. [faces-config.xml]
1. <?xml version="1.0" encoding="UTF-8"?> 2. <!-- =========== FULL CONFIGURATION FILE ================================== --> 3. <faces-config version="1.2" 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/webfacesconfig_1_2.xsd"> 4. <application> 5. <resource-bundle> 6. <base-name> 7. messages 8. </base-name> 9. <var>msg</var> 10. </resource-bundle> 11. </application> 12. <managed-bean> 13. <managed-bean-name>form</managed-bean-name> 14. <managed-bean-class>forms.Form</managed-bean-class> 15. <managed-bean-scope>request</managed-bean-scope> 16. </managed-bean> 17. </faces-config>

[messages_fr_FR.properties]
1. form.titre=Jsf - validations et conversions 2. saisie1.prompt=1-Nombre entier de type int 3. saisie2.prompt=2-Nombre entier de type int 4. saisie3.prompt=3-Nombre entier de type int 5. data.required=Vous devez entrer une donne 6. integer.required=Vous devez entrer un nombre entier 7. saisie4.prompt=4-Nombre entier de type int dans l'intervalle [1,10] 8. saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10] 9. saisie5.prompt=5-Nombre rel de type double 10. double.required=Vous devez entrer un nombre 11. saisie6.prompt=6-Nombre rel>=0 de type double 12. saisie6.error=6-Vous devez entrer un nombre >=0 13. saisie7.prompt=7-Boolen 14. saisie7.error=7-Vous devez entrer un boolen 15. saisie8.prompt=8-Date au format jj/mm/aaaa 16. saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa 17. date.required=Vous devez entrer une date 18. saisie9.prompt=9-Chane de 4 caractres 19. saisie9.error=9-Vous devez entrer une chane de 4 caractres exactement 20. submit=Valider 21. cancel=Annuler 22. saisie.type=Type de la saisie 23. saisie.champ=Champ de saisie 24. saisie.erreur=Erreur de saisie 25. bean.valeur=Valeurs du modle du formulaire 26. saisie10.prompt=10-Nombre entier de type int <1 ou >7 27. saisie10.incorrecte=10-Saisie n 10 incorrecte 28. saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7 29. saisies11et12.incorrectes=La proprit saisie11+saisie12=10 n'est pas vrifie 30. saisies11et12.incorrectes_detail=La proprit saisie11+saisie12=10 n'est pas vrifie 31. saisie11.prompt=11-Nombre entier de type int 32. saisie12.prompt=12-Nombre entier de type int 33. error.sign="!" 34. error.sign_detail="!"

http://tahe.developpez.com/java/javaee

309/341

21.6.5

Les diffrentes saisies du formulaire

Nous tudions maintenant successivement les diffrentes saisies du formulaire.

21.6.5.1
1. 2. 3. 4. 5.

Saisies 1 4 : saisie d'un nombre entier


<!-- ligne 1 --> <h:outputText value="#{msg['saisie1.prompt']}"/> <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/> <h:message for="saisie1" styleClass="error"/> <h:outputText value="#{form.saisie1}"/>

La page [form.jsp] prsente la saisie 1 sous la forme suivante :

Le modle form.saisie1 est dfini comme suit dans [Form.java] :


1.private Integer saisie1 = 0;

Sur un GET du navigateur , la page [form.jsp] associe son modle [Form.java] produit visuellement ce qui suit : 1 2 3 4

la ligne 2 produit [1] la ligne 3 produit [2] la ligne 4 produit [3] la ligne 5 produit [4]

Supposons que la saisie suivante soit faite puis valide : 1 2

On obtient alors le rsultat suivant dans le formulaire renvoy par l'application :

en [1], la saisie errone en [2], le message d'erreur le signalant en [3], on voit que la valeur du champ Integer saisie1 du modle n'a pas chang.

Expliquons ce qui s'est pass. Pour cela revenons au cycle de traitement d'une page Jsf :

http://tahe.developpez.com/java/javaee

310/341

C D2 E D

Nous examinons ce cycle pour le composant :


<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>

et son modle :
private Integer saisie1 = 0;

en [A], la page [form.jsp] envoye lors du GET du navigateur est restaure. En [A], la page est telle que l'utilisateur l'a reue. Le composant id="saisie1" retrouve sa valeur initiale "0". en [B], les composants de la page reoivent pour valeurs, les valeurs postes par le navigateur. En [B], la page est telle que l'utilisateur l'a saisie et valide. Le composant id="saisie1" reoit pour valeur, la valeur poste "x". en [C], si la page contient des validateurs et des convertisseurs explicites, ceux-ci sont excuts. Des convertisseurs implicites sont galement excuts si le type du champ associ au composant n'est pas de type String. C'est le cas ici, o le champ form.saisie1 est de type Integer. Jsf va essayer de transformer la valeur "x" du composant id="saisie1" en un type Integer. Ceci va provoquer une erreur qui va arrter le cycle de traitement [A-F]. Cette erreur sera associe au composant id="saisie1". Via [D2], on passe ensuite directement la phase de rendu de la rponse. La mme page [form.jsp] est renvoye. la phase [D] n'a lieu que si tous les composants d'une page ont pass la phase de conversion / validation. C'est dans cette phase, que la valeur du composant id="saisie1" sera affecte son modle form.saisie1.

Si la phase [C] choue, le code suivant est alors excut de nouveau :


1. <!-- ligne 1 --> 2. <h:outputText value="#{msg['saisie1.prompt']}"/> 3. <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/> 4. <h:message for="saisie1" styleClass="error"/> 5. <h:outputText value="#{form.saisie1}"/>

et affiche :

Le message affich en [2] vient de la ligne 4 de [form.jsp]. La balise <h:message for="idComposant"/> affiche le message d'erreur associ au composant dsign par l'attribut for, si erreur il y a. Le message affich en [2] est standard et se trouve dans le fichier [Messages.properties] de l'archive [jsf-impl.jar] :

http://tahe.developpez.com/java/javaee

311/341

En [2], on voit que le fichier des messages existe en plusieurs variantes. Leur examen montre cependant que toutes ces variantes contiennent des messages en anglais. Examinons le contenu de [Messages.properties] :
1. .. 2. # ============================================================================== 3. # SPECIFICATION DEFINED MESSAGES 4. # ============================================================================== 5. 6. 7. # ============================================================================== 8. # Component Errors 9. # ============================================================================== 10. 11. javax.faces.component.UIInput.CONVERSION={0}: Conversion error occurred. 12. javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required. 13. ... 14. 15. # ============================================================================== 16. # Converter Errors 17. # ============================================================================== 18. javax.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed decimal number. 19. javax.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a signed decimal number consisting of zero or more digits, that may be followed by a decimal point and fraction. Example: {1} 20. ... 21. javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number consisting of one or more digits. 22. javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between -2147483648 and 2147483647 Example: {1} 23. ... 24. 25. # ============================================================================== 26. # Validator Errors 27. # ============================================================================== 28. 29. javax.faces.validator.NOT_IN_RANGE=Validation Error: Specified attribute is not between the expected values of {0} and {1}. 30. javax.faces.validator.DoubleRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}" 31. ... 32. 33. # ============================================================================== 34. # IMPLEMENTATION DEFINED MESSAGES 35. # ============================================================================== 36. 37. com.sun.faces.APPLICATION_ASSOCIATE_CTOR_WRONG_CALLSTACK=ApplicationAssociate ctor not called in same callstack as ConfigureListener.contextInitialized(). 38. com.sun.faces.APPLICATION_ASSOCIATE_EXISTS=ApplicationAssociate already exists for this webapp. 39. ...

Le fichier contient environ 180 messages diviss en catgories :

erreurs sur un composant, ligne 8

http://tahe.developpez.com/java/javaee

312/341

erreurs de conversion entre un composant et son modle, ligne 16 erreurs de validation lorsque des validateurs sont prsents dans la page, ligne 26 erreurs gnrales, ligne 34

L'erreur qui s'est produite sur le composant id="saisie1" est de type erreur de conversion d'un type String vers un type Integer. Le message d'erreur associ est celui de la ligne 22 du fichier des messages.
javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between -2147483648 and 2147483647 Example: {1}

Le message d'erreur qui a t affich est reproduit ci-dessous :

On voit que dans le message :


le paramtre {2} a t remplac par l'identifiant du composant pour lequel l'erreur de conversion s'est produite la paramtre {0} a t remplac par la saisie faite en [1] pour le composant le paramtre {1} a t remplac par le nombre 9346.

La plupart des messages lis aux composants ont deux versions : une version rsume (summary) et une version dtaille (detail). C'est le cas des lignes 21-22 :
1. javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number consisting of one or more digits. 2. javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between -2147483648 and 2147483647 Example: {1}

Le message ayant la cl _detail (ligne 2) est le message dit dtaill. L'autre est le message dit rsum. La balise <h:message> affiche par dfaut le message dtaill. Ce comportement peut tre chang par les attributs showSummary et showDetail. C'est ce qui est fait pour le composant d'id saisie2 :
1. <!-- ligne 2 --> 2. <h:outputText value="#{msg['saisie2.prompt']}" /> 3. <h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/> 4. <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/> 5. <h:outputText value="#{form.saisie2}"/>

Ligne 2, le composant saisie2 est li au champ form.saisie2 suivant :


private Integer saisie2 = 0;

Le rsultat obenu est le suivant :

1 2

en [1], le message dtaill, en [2], le message rsum

La balise <h:messages> affiche sous la forme d'une liste tous les messages d'erreurs rsums de tous les composants ainsi que les messages d'erreurs non lis un composant. L encore des attributs peuvent changer ce comportement par dfaut :

showDetail : true / false pour demander ou non les messages dtaills showSummary : true / false pour demander ou non les messages rsums globalOnly : true / false pour demander ne voir ou non que les messages d'erreurs non lies des composants. Un tel message pourrait, par exemple, tre cr par le dveloppeur.

http://tahe.developpez.com/java/javaee

313/341

Le message d'erreur associ une conversion peut tre chang de diverses faons. Tout d'abord, on peut indiquer l'application d'utiliser un autre fichier de messages. Cette modification se fait dans [faces-config.xml] :
1. <faces-config ..."> 2. <application> 3. <resource-bundle> 4. <base-name> 5. messages 6. </base-name> 7. <var>msg</var> 8. </resource-bundle> 9. <message-bundle>messages</message-bundle> 10. </application> 11. ... 12. </faces-config>

Les lignes 3-8 dfinissent un fichier des messages mais ce n'est pas celui-ci qui est utilis par les balises <h:message> et <h:messages>. Il faut utiliser la balise <message-bundle> de la ligne 9 pour le dfinir. La ligne 9 indique aux balises <h:message(s)> que le fichier [messages.properties] doit tre explor avant le fichier [javax.faces.Messages.properties]. Ainsi si on ajoute les lignes suivantes au fichier [messages_fr.properties] :
1. ... 2. # conversions 3. javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' doit tre un nombre constitu d'un ou plusieurs chiffres 4. javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' doit tre un nombre entre -2147483648 et 2147483647 Exemple: {1}

l'erreur renvoye pour les composants saisie1 et saisie2 devient :

L'autre faon de modifier le message d'erreur de conversion est d'utiliser l'attribut converterMessage du composant comme cidessous pour le composant saisie3 :
1. 2. 3. <!-- ligne 3 --> <h:outputText value="#{msg['saisie3.prompt']}" /> <h:inputText id="saisie3" value="#{form.saisie3}" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/> 4. <h:message for="saisie3" styleClass="error"/> 5. <h:outputText value="#{form.saisie3}"/>

styleClass="saisie"

Le composant saisie3 est li au champ form.saisie3 suivant :


private Integer saisie3 = 0;

ligne 3, l'attribut converterMessage fixe explicitement le message afficher lors d'une erreur de conversion. ligne 3, l'attribut required="true" indique que la saisie est obligatoire. Le champ ne peut rester vide. Un champ est considr comme vide s'il ne contient aucun caractre ou s'il contient une suite d'espaces. L encore, il existe dans [javax.faces.Messages.properties] un message par dfaut :
# ============================================================================== # Component Errors # ============================================================================== ... javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required.

1. 2. 3. 4. 5. 6.

L'attribut requiredMessage permet de remplacer ce message par dfaut. Si le fichier [messages.properties] contient les messages suivants :
1. ...

http://tahe.developpez.com/java/javaee

314/341

2. 3. 4. 5. 6.

data.required=Vous devez entrer une donne integer.required=Vous devez entrer un nombre entier ... # conversions javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' doit tre un nombre constitu d'un ou plusieurs chiffres 7. javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' doit tre un nombre entre -2147483648 et 2147483647 Exemple: {1}

On pourra obtenir le rsultat suivant :

ou encore celui-ci :

Vrifier qu'une saisie correspond bien un nombre entier n'est pas toujours suffisant. Il faut parfois vrifier que le nombre saisi appartient un intervalle donn. On utilise alors un validateur. La saisie n 4 en donne un exemple. Son code dans [form.jsp] est le suivant :
1. 2. 3. 4. 5. 6. 7. <!-- ligne 4 --> <h:outputText value="#{msg['saisie4.prompt']}" /> <h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}"> <f:validateLongRange minimum="1" maximum="10" /> </h:inputText> <h:message for="saisie4" styleClass="error"/> <h:outputText value="#{form.saisie4}"/>

Ligne 3, le composant saisie4 est li au modle form.saisie4 suivant :


private Integer saisie4 = 0;

Lignes 3-5, la balise <h:inputText> a une balise enfant <f:validateLongRange> qui admet deux attributs facultatifs minimum et maximum. Cette balise, appele galement validateur, permet d'ajouter une contrainte la valeur de la saisie : ce doit tre non seulement un entier, mais un entier dans l'intervalle [minimum, maximum] si les deux attributs minimum et maximum sont prsents, suprieure ou gale minimum si seul l'attribut minimum est prsent, infrieure ou gale maximum si seul l'attribut maximum est prsent. Le validateur <f:validateLongRange> a des messages d'erreur par dfaut dans [javax.faces.Messages.properties] :
1. 2. 3. 4. 5. 6. # ============================================================================== # Validator Errors # ==============================================================================

... javax.faces.validator.LongRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of ''{0}'' 7. javax.faces.validator.LongRangeValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of ''{0}'' 8. javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2}: Validation Error: Specified attribute is not between the expected values of {0} and {1}.

http://tahe.developpez.com/java/javaee

315/341

9. javax.faces.validator.LongRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type.

De nouveau, il est possible de remplacer ces messages par d'autres. Il existe un attribut validatorMessage qui permet de dfinir un message spcifique pour le composant. Ainsi avec le code Jsf suivant :
1. <h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}"> 2. <f:validateLongRange minimum="1" maximum="10" /> 3. </h:inputText>

et le message suivant dans [messages.properties] :


saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]

on obtient le rsultat suivant :

21.6.5.2

Saisies 5 et 6 : saisie d'un nombre rel

La saisie des nombres rels obit des rgles similaires celles de la saisie des nombres entiers. Le code Jsp des saisies 5 et 6 est le suivant :
1. 2. 3. <!-- ligne 5 --> <h:outputText value="#{msg['saisie5.prompt']}" /> <h:inputText id="saisie5" value="#{form.saisie5}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/> 4. <h:message for="saisie5" styleClass="error"/> 5. <h:outputText value="#{form.saisie5}"/> 6. <!-- ligne 6 --> 7. <h:outputText value="#{msg['saisie6.prompt']}"/> 8. <h:inputText id="saisie6" value="#{form.saisie6}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}" validatorMessage="#{msg['saisie6.error']}"> 9. <f:validateDoubleRange minimum="0.0"/> 10. </h:inputText> 11. <h:message for="saisie6" styleClass="error"/> 12. <h:outputText value="#{form.saisie6}"/>

Les lments du modle [Form.java] lis aux composants saisie5 et saisie6 :


private Double saisie5 = 0.0; private Double saisie6 = 0.0;

Les messages d'erreurs associs aux convertisseurs et validateurs des composants saisie5 et saisie6, dans [messages.properties] :
1. double.required=Vous devez entrer un nombre 2. saisie6.error=6-Vous devez entrer un nombre >=0

Voici un exemple d'excution :

http://tahe.developpez.com/java/javaee

316/341

21.6.5.3

Saisie 7 : saisie d'un boolen

La saisie d'un boolen devrait tre normalement faite avec une case cocher. Si elle est faite avec un champ de saisie, la chane "true" est convertie en boolen true et tout autre chane en boolen false. Le code Jsp de l'exemple :
1. 2. 3. <!-- ligne 7 --> <h:outputText value="#{msg['saisie7.prompt']}"/> <h:inputText id="saisie7" value="#{form.saisie7}" required="true" requiredMessage="#{msg['data.required']}"/> 4. <h:message for="saisie7" styleClass="error"/> 5. <h:outputText value="#{form.saisie7}"/>

styleClass="saisie"

Le modle du composant saisie7 :


private Boolean saisie7 = true;

Voici un exemple de saisie et sa rponse : 1

En [1], la valeur saisie. Par conversion, cette chane "x" devient le boolen false. C'est ce que montre [2]. La valeur [3] du modle n'a pas chang. Elle ne change que lorsque toutes les conversions et validations de la page ont russi. Ce n'tait pas le cas dans cet exemple.

21.6.5.4

Saisie 8 : saisie d'une date

La saisie d'une date est faite dans l'exemple avec le code Jsp suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. <!-- ligne 8 --> <h:outputText value="#{msg['saisie8.prompt']}"/> <h:inputText id="saisie8" value="#{form.saisie8}" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}"> <f:convertDateTime pattern="dd/MM/yyyy"/> </h:inputText> <h:message for="saisie8" styleClass="error"/> <h:outputText value="#{form.saisie8}"> <f:convertDateTime pattern="dd/MM/yyyy"/> </h:outputText>

styleClass="saisie"

Le composant saisie8 de la ligne 3 utilise un convertisseur java.lang.String <--> java.util.Date. Le modle form.saisie8 associ au composant saisie8 est le suivant :
private Date saisie8 = new Date();

Le composant dfini par les lignes 7-9 utilise galement un convertisseur mais uniquement dans le sens java.util.Date --> java.lang.String. Le convertisseur <f:convertDateTime> admet divers attributs dont l'attribut pattern qui fixe la forme de la chane de caractres qui doit tre transforme en date o avec laquelle une date doit tre affiche. Lors de la demande initiale de la page [form.jsp], la ligne 8 prcdente s'affiche comme suit :

http://tahe.developpez.com/java/javaee

317/341

1 Les champs [1] et [2] affichent tous deux la valeur du modle form.saisie8 :
private Date saisie8 = new Date();

o saisie8 prend pour valeur la date du jour. Le convertisseur utilis dans les deux cas pour l'affichage de la date est le suivant :
<f:convertDateTime pattern="dd/MM/yyyy"/>

o dd (day) dsigne le n du jour, MM (Month) le n du mois et yyyy (year) l'anne. En [1], le convertisseur est utilis pour la conversion inverse java.lang.String --> java.util.Date. La date saisie devra donc suivre le modle "dd/MM/yyyy" pour tre valide. Il existe des messages par dfaut pour les dates non valides dans [javax.faces.Messages.properties] :
1. 2. 3. 4. 5. 6. # ============================================================================== # Converter Errors # ============================================================================== ... javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date. javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1} 7. javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' could not be understood as a time. 8. javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' could not be understood as a time. Example: {1} 9. javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' could not be understood as a date and time. 10. javax.faces.converter.DateTimeConverter.DATETIME_detail={2}: ''{0}'' could not be understood as a date and time. Example: {1} 11. javax.faces.converter.DateTimeConverter.PATTERN_TYPE={1}: A 'pattern' or 'type' attribute must be specified to convert the value ''{0}''.

qu'on peut remplacer par ses propres messages. Ainsi dans l'exemple
1. <h:inputText id="saisie8" value="#{form.saisie8}" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}"> 2. <f:convertDateTime pattern="dd/MM/yyyy"/> 3. </h:inputText> styleClass="saisie"

le message affich en cas d'erreur de conversion sera le message de cl saisie8.error suivant :


saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa

Voici un exemple :

21.6.5.5

Saisie 9 : saisie d'une chane de longueur contrainte

La saisie 9 montre comment imposer une chane saisie d'avoir un nombre de caractres compris dans un intervalle :
1. 2. 3. 4. 5. 6. 7. <!-- ligne 9 --> <h:outputText value="#{msg['saisie9.prompt']}"/> <h:inputText id="saisie9" value="#{form.saisie9}" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9.error']}"> <f:validateLength minimum="4" maximum="4"/> </h:inputText> <h:message for="saisie9" styleClass="error"/> <h:outputText value="#{form.saisie9}"/>

styleClass="saisie"

Ligne 4, la validateur <f:validateLength minimum="4" maximum="4"/> impose la chane saisie d'avoir exactement 4 caractres. On peut n'utiliser que l'un des attributs : minimum pour un nombre minimal de caractres, maximum pour un nombre maximal.

http://tahe.developpez.com/java/javaee

318/341

Le modle form.saisie9 du composant saisie9 de la ligne 3 est le suivant :


private String saisie9 = "";

Il existe des messages d'erreur par dfaut pour ce type de validation :


1. 2. 3. 4. 5. 6. # ============================================================================== # Validator Errors # ==============================================================================

... javax.faces.validator.LengthValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of ''{0}'' 7. javax.faces.validator.LengthValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of ''{0}''

qu'on peut remplacer en utilisant l'attribut validatorMessage comme dans la ligne 3 ci-dessus. Le message de cl saisie9.error est le suivant :
saisie9.error=9-Vous devez entrer une chane de 4 caractres exactement

Voici un exemple d'excution :

21.6.5.6

Saisie 10 : crire une mthode de validation spcifique

Rsumons : Jsf permet de vrifier parmi les valeurs saisies, la validit des nombres (entiers, rels), des dates et la longueur des chanes. C'est trs peu. On peut tre tonn de ne pas trouver par exemple un validateur de type "expression rgulire" qui vrifierait qu'une valeur saisie suit un modle donn. Ce serait utile pour vrifier qu'une adresse lectronique vrifie un format de base. Jsf permet d'ajouter aux validateurs et convertisseurs existants ses propres validateurs et convertisseurs. Aussi est-il possible, par exemple, de trouver le validateur de type "expression rgulire" dans des bibliothques tierces ainsi que d'autres validateurs et convertisseurs. Il faut esprer que les versions futures de Jsf intgreront certains d'entre-eux afin d'avoir une base plus riche qu'actuellement. On pourra ici suivre le tutoriel Netbeans "Validating and Converting User Input With the JSF Framework" [http://www.netbeans.org/kb/articles/jAstrologer-validate.html] pour dcouvrir comment construire ses propres validateurs et convertisseurs. Nous prsentons ici une autre mthode : celle qui consiste valider une donne saisie par une mthode du modle du formulaire. C'est l'exemple suivant :
1. 2. 3. <!-- ligne 10 --> <h:outputText value="#{msg['saisie10.prompt']}"/> <h:inputText id="saisie10" value="#{form.saisie10}" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/> 4. <h:message for="saisie10" styleClass="error"/> 5. <h:outputText value="#{form.saisie10}"/>

styleClass="saisie"

Le modle form.saisie10 associ au composant saisie10 de la ligne 3 est le suivant :


private Integer saisie10 = 0;

On veut que le nombre saisi soit <1 ou >7. On ne peut le vrifier avec les validateurs de base de Jsf. On crit alors sa propre mthode de validation du composant saisie10. On l'indique avec l'attribut validator du composant valider :
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>

Le composant saisie10 est valid par la mthode form.validateSaisie10. Celle-ci est la suivante :
1. 2. public void validateSaisie10(FacesContext context, UIComponent component, Object value) { int saisie = (Integer) value;

http://tahe.developpez.com/java/javaee

319/341

3. 4. 5. 6. 7. 8.}

if (!(saisie < 1 || saisie > 7)) { FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); }

La signature d'une mthode de validation est obligatoirement celle de la ligne 1 :


FacesContext context : contexte d'excution de la page - donne accs diverses informations, notamment aux objets HttpServletRequest request et HttpServletResponse response. UIComponent component : le composant qu'il faut valider. La balise <h:inputText> est reprsent par un composant de type UIInput driv de UIComponent. Ici c'est ce composant UIInput qui est reu en second paramtre. Object value : la valeur saisie vrifier transforme dans le type de son modle. Il est important de comprendre ici que si la conversion String -> type du modle a chou, alors la mthode de validation n'est pas excute. Lorsqu'on arrive dans la mthode validateSaisie10, c'est que la conversion String --> Integer a russi. Le 3ime paramtre est alors de type Integer. ligne 2 : la valeur saisie est transforme en type int. ligne 3 : on vrifie que la valeur saisie est <1 ou >7. Si c'est le cas, la validation est termine. Si ce n'est pas le cas, le validateur doit signaler l'erreur en lanant une exception de type ValidatorException.

La classe ValidatorException a deux constructeurs :

1 2

le constructeur [1] a pour paramtre un message d'erreur de type FacesMessage. Ce type de message est celui affich par les balises <h:messages> et <h:message>. le constructeur [2] permet de plus d'encapsuler la cause de type Throwable ou driv de l'erreur.

Il nous faut construire un message de type FacesMessage. Cette classe a divers constructeurs :

Le constructeur [1] dfinit les proprits d'un objet FacesMessage :


FacesMessage.Severity severity : un niveau de gravit pris dans l'numration suivante : SEVERITY_ERROR, SEVERITY_FATAL, SEVERITY_INFO, SEVERITY_WARN. String summary : la version rsume du message d'erreur - est affiche par les balises <h:message showSummary="true"> et <h:messages> String detail : la version dtaille du message d'erreur - est affiche par les balises <h:message> et <h:messages showDetail="true">

N'importe lequel des constructeurs peut-tre utilis, les paramtres manquants pouvant tre fixs ultrieurement par des mthodes set.

http://tahe.developpez.com/java/javaee

320/341

Le constructeur [1] ne permet pas de dsigner un message qui serait dans un fichier de messages utilis pour l'internationalisation des pages. C'est videmment dommage. David Geary et Cay Horstmann comblent cette lacune dans leur livre "Core JavaServer Faces" avec la classe utilitaire com.corejsf.util.Messages. C'est cette classe qui est utilise ligne 4 pour crer le message d'erreur. Elle ne contient que des mthodes statiques dont la mthode getMessage utilise ligne 4 :
public static FacesMessage getMessage(String bundleName, String resourceId, Object[] params)

La mthode getMessage admet trois paramtres : String bundleName : le nom d'un fichier de messages sans son suffixe .properties mais avec son nom de paquetage. Ici, notre premier paramtre pourrait tre messsages pour dsigner le fichier [messages.properties]. Avant d'utiliser le fichier dsign par le premier paramtre, getMessage essaie d'utiliser le fichier des messages de l'application, s'il y en a un. Ainsi si dans [faces-config.xml] on a dclar un fichier de messages avec la balise :
1. <application> 2.... 3. <message-bundle>messages</message-bundle> 4.</application>

on peut passer null comme premier paramtre la mthode getMessage. String resourceId : la cl du message exploiter dans le fichier des messages. Nous avons vu qu'un message pouvait avoir la fois une version rsume et une version dtaille. resourceId est l'identifiant de la version rsume. La version dtaille sera recherche automatiquement avec la cl resourceId_detail. Ainsi, aurons-nous deux messages dans [messages.properties] pour l'erreur sur la saisie n 10 :
saisie10.incorrecte=10-Saisie n 10 incorrecte saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7

Le message de type FacesMessage produit par la mthode Messages.getMessage inclut la fois les versions rsume et dtaille si elles ont t trouves. Les deux versions doivent tre prsentes sinon on rcupre une exception de type [NullPointerException].

Object[] params : les paramtres effectifs du message si celui-ci a des paramtres formels {0}, {1}, ... Ces paramtres formels seront remplacs par les lments du tableau params.

Revenons au code de la mthode de validation du composant saisie10 :


1. public void validateSaisie10(FacesContext context, UIComponent component, Object value) { 2. int saisie = (Integer) value; 3. if (!(saisie < 1 || saisie > 7)) { 4. FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null); 5. message.setSeverity(FacesMessage.SEVERITY_ERROR); 6. throw new ValidatorException(message); 7. } 8. }

en [4], le message de type FacesMessage est cr l'aide de la mthode statique Messages.getMessage en [5], on fixe le niveau de gravit du message en [6], on lance une exception de type ValidatorException avec le message construit prcdemment. La mthode de validation a t appele par le code Jsp suivant :
1. 2. 3. <!-- ligne 10 --> <h:outputText value="#{msg['saisie10.prompt']}"/> <h:inputText id="saisie10" value="#{form.saisie10}" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/> 4. <h:message for="saisie10" styleClass="error"/> 5. <h:outputText value="#{form.saisie10}"/>

styleClass="saisie"

Ligne 3, la mthode de validation est excute pour le composant d'id saisie10. Aussi le message d'erreur produit par la mthode validateSaisie10 est-il associ ce composant et donc affich par la ligne 4 (attribut for="saisie10"). C'est la version dtaille qui est affiche par dfaut par la balise <h:message>. Voici un exemple d'excution :

http://tahe.developpez.com/java/javaee

321/341

21.6.5.7

Saisies 11 et 12 : validation d'un groupe de composants

Jusqu' maintenant, les mthodes de validation rencontres ne validaient qu'un unique composant. Comment faire si la validation souhaite concerne plusieurs composants ? C'est ce que nous voyons maintenant. Dans le formulaire :

nous voulons que les saisies 11 et 12 soient deux nombres entiers dont la somme soit gale 10. Le code Jsf sera le suivant :
1. 2. 3. <!-- ligne 11 --> <h:outputText value="#{msg['saisie11.prompt']}"/> <h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/> 4. <h:panelGroup> 5. <h:message for="saisie11" styleClass="error"/> 6. <h:outputText value="#{form.errorSaisie11}" styleClass="error"/> 7. </h:panelGroup> 8. <h:outputText value="#{form.saisie11}"/> 9. <!-- ligne 12 --> 10. <h:outputText value="#{msg['saisie12.prompt']}"/> 11. <h:inputText id="saisie12" value="#{form.saisie12}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/> 12. <h:panelGroup> 13. <h:message for="saisie12" styleClass="error"/> 14. <h:outputText value="#{form.errorSaisie12}" styleClass="error"/> 15. </h:panelGroup> 16. <h:outputText value="#{form.saisie12}"/> 17. </h:panelGrid>

et le modle associ :
a) private Integer saisie11 = 0; b) private Integer saisie12 = 0; c) private String errorSaisie11 = ""; d)private String errorSaisie12 = "";

Ligne 3 du code Jsf, on utilise les techniques dj prsentes pour vrifier que la valeur saisie pour le composant saisie11 est bien un entier. Il en est de mme, ligne 11, pour le composant saisie12. Pour vrifier que saisie11 + saisie12 =10, on pourrait construire un validateur spcifique. Pour ce faire, on pourra s'inspirer du tutoriel Netbeans "Validating and Converting User Input With the JSF Framework" cit page 319. Nous suivons ici une autre dmarche. La page [form.jsp] est valide par un bouton [Valider] dont le code Jsf est le suivant :
1. <!-- boutons de commande --> 2. <h:panelGrid columns="2"> 3. <h:commandButton value="#{msg['submit']" action="#{form.submit}"/> 4. ... 5. </h:panelGrid>

o le message msg['submit'] est le suivant :


submit=Valider

On voit ligne 3, que la mthode form.submit va tre excute pour traiter le clic sur le bouton [Valider]. Celle-ci est la suivante :
1. 2. 3. 4. 5. 6. 7. // actions public String submit() { // dernires validations validateForm(); // on renvoie le mme formulaire return null; }

http://tahe.developpez.com/java/javaee

322/341

8. 9. // validations globales 10. private void validateForm() { 11. if ((saisie11 + saisie12) != 10) { 12.... 13.}

Il est important de comprendre que lorsque la mthode submit s'excute, tous les validateurs et convertisseurs du formulaire ont t excuts et russis les champs du modle [Form.java] ont reu les valeurs postes par le client. En effet, revenons au cycle de traitement d'un POST Jsf : A B C

La mthode submit est un gestionnaire d'vnement. Elle gre l'vnement clic sur le bouton [Valider]. Comme tous les gestionnaires d'vnement, elle s'excute dans la phase [E], une fois que tous les validateurs et convertisseurs ont t excuts et russis [C] et que le modle a t mis jour avec les valeurs postes [D]. Il n'est donc plus question ici de lancer des exceptions de type [ValidatorException] comme nous l'avons fait prcdemment. Nous nous contenterons de renvoyer le formulaire avec des messages d'erreur :

2 3

En [1], nous alerterons l'utilisateur et en [2] et [3], nous mettrons un signe d'erreur. Dans le code Jsf, le message [1] sera obtenu de la faon suivante :
1. <f:view> 2. <h2><h:outputText value="#{msg['form.titre']}"/></h2> 3. <h:form id="formulaire"> 4. <h:messages globalOnly="true" /> 5. ...

En ligne 4, la balise <h:messages> affiche par dfaut la version rsume des messages d'erreurs de toutes les saisies errones de composants du formulaire ainsi que tous les messages d'erreur non lis des composants. L'attribut globalOnly="true" limite l'affichage ces derniers. Les messages [2] et [3] sont affichs avec de simples balises <h:outputText> :
1. 2. <!-- ligne 11 --> <h:outputText value="#{msg['saisie11.prompt']}"/>

http://tahe.developpez.com/java/javaee

323/341

3.

<h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/> 4. <h:panelGroup> 5. <h:message for="saisie11" styleClass="error"/> 6. <h:outputText value="#{form.errorSaisie11}" styleClass="error"/> 7. </h:panelGroup> 8. <h:outputText value="#{form.saisie11}"/> 9. <!-- ligne 12 --> 10. ... 11. <h:outputText value="#{form.errorSaisie12}" styleClass="error"/> 12. ... 13. </h:panelGrid>

Lignes 4-7, le composant saisie11 a deux messages d'erreur possibles : celui indiquant une conversion errone ou une absence de donne. Ce message gnr par Jsf lui-mme sera contenu dans un type FacesMessage et affich par la balise <h:message> de la ligne 5. celui que nous allons gnrer si saisie11 + saisie12 n'est pas gal 10. Il sera affich par la ligne 6. Le message d'erreur sera contenu dans le modle form.errorSaisie11. Les deux messages correspondent des erreurs qui ne peuvent se produire en mme temps. La vrification saisie11 + saisie12 = 10 est faite dans la mthode submit qui ne s'excute que s'il ne reste aucune erreur dans le formulaire. Lorsqu'elle s'excutera, le composant saisie11 aura t vrifi et son modle form.saise11 aura reu sa valeur. Le message de la ligne 5 ne pourra plus tre affich. Inversement, si le message de la ligne 5 est affich, il reste alors au moins une erreur dans le formulaire et la mthode submit ne s'excutera pas. Le message de la ligne 6 ne sera pas affich. Afin que les deux messages d'erreur possibles soient dans la mme colonne du tableau, ils ont t rassembls dans une balise <h:panelGroup> (lignes 4 et 7). La mthode submit est la suivante :
1. // actions 2. public String submit() { 3. // dernires validations 4. validateForm(); 5. // on renvoie le mme formulaire 6. return null; 7. } 8. 9. // validations globales 10. private void validateForm() { 11. if ((saisie11 + saisie12) != 10) { 12. // msg global 13. FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null); 14. message.setSeverity(FacesMessage.SEVERITY_ERROR); 15. FacesContext context = FacesContext.getCurrentInstance(); 16. context.addMessage(null, message); 17. // msg lis aux champs 18. message = Messages.getMessage(null, "error.sign", null); 19. setErrorSaisie11(message.getSummary()); 20. setErrorSaisie12(message.getSummary()); 21. } else { 22. setErrorSaisie11(""); 23. setErrorSaisie12(""); 24. } 25.}

ligne 4 : la mthode submit appelle la mthode validateForm pour faire les dernires validations. ligne 11 : on vrifie si saisie11+saisie12=10 si ce n'est pas le cas, lignes 13-14, on cre un message de type FacesMessage avec le message d'id saisies11et12.incorrectes. Celuici est le suivant :
saisies11et12.incorrectes=La proprit saisie11+saisie12=10 n'est pas vrifie

le message ainsi construit est ajout (lignes 15-16) la liste des messages d'erreur de l'application. Ce message n'est pas li un composant particulier. C'est un message global de l'application. Il sera affich par la balise <h:messages globalOnly="true"/> prsente plus haut. ligne 18 : on cre un nouveau message de type FacesMessage avec le message d'id error.sign. Celui-ci est le suivant :
error.sign="!"

Nous avons dit que la mthode statique [Messages.getMessage] construisait un message de type FacesMessage avec une version rsume et une version dtaille si elles existaient. Ici, seule la version rsume du message error.sign existe. On

http://tahe.developpez.com/java/javaee

324/341

obtient la version rsume d'un message m, par m.getSummary(). Lignes 19 et 20, la version rsume du message error.sign est mise dans les champs errorSaisie11 et errorSaisie12 du modle. Ils seront affichs par les balises Jsf suivantes :
1. 2. 3. <h:outputText value="#{form.saisie11}"/> ... <h:outputText value="#{form.saisie12}"/>

lignes 22-23 : si la proprit saisie11+saisie12=10 est vrifie, alors les deux champs errorSaisie11 et errorSaisie12 du modle sont vids afin qu'un ventuel message d'erreur prcdent soit effac. Il faut se rappeler ici que le modle est conserv entre les requtes, dans la session du client.

Voici un exemple d'excution :

2 4

On remarquera dans la colonne [1] que le modle a reu les valeurs postes, ce qui montre que toutes les oprations de validation et de conversion entre les valeurs postes et le modle ont russi. Le gestionnaire d'vnement form.submit qui gre le clic sur le bouton [Valider] a pu ainsi s'excuter. C'est lui qui a produit les messages affichs en [2] et [3]. On voit que le modle a t mis jour alors mme que le formulaire a t refus et renvoy au client. On pourrait vouloir que le modle ne soit pas mis jour dans un tel cas. En effet, en imaginant que l'utilisateur annule sa mise jour avec le bouton [Annuler] [4], on ne pourra pas revenir au modle initial sauf l'avoir mmoris. La plupart du temps, ceci n'est pas rellement un problme.

21.6.5.8

POST d'un formulaire sans vrification des saisies

Considrons le formulaire ci-dessus et supposons que l'utilisateur ne comprenant pas ses erreurs veuille abandonner la saisie du formulaire. Il va alors utiliser le bouton [Annuler] gnr par le code Jsf suivant :
1. <!-- boutons de commande --> 2. <h:panelGrid columns="2"> 3. <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/> 4. <h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/> 5. </h:panelGrid>

http://tahe.developpez.com/java/javaee

325/341

Ligne 4, le message msg['cancel'] est le suivant :


cancel=Annuler

La mthode form.cancel associe au bouton [Annuler] ne sera excute que si le formulaire est valide. C'est ce que nous avons montr pour la mthode form.submit associe au bouton [Valider]. Si l'utilisateur veut annuler la saisie du formulaire, il est bien sr inutile de vrifier la validit de ses saisies. Ce rsultat est obtenu avec l'attribut immediate="true" qui indique Jsf d'excuter la mthode form.cancel sans passer par la phase de validation et de conversion. Revenons au cycle de traitement du POST Jsf : A B

C D E

Les vnements des composants d'action <h:commandButton> et <h:commandLink> ayant l'attribut immediate="true" sont traits dans la phase [C] puis le cycle Jsf passe directement la phase [E] de rendu de la rponse. La mthode form.cancel est la suivante :
1. public String cancel() { 2. saisie1 = 0; 3. saisie2 = 0; 4. saisie3 = 0; 5. saisie4 = 0; 6. saisie5 = 0.0; 7. saisie6 = 0.0; 8. saisie7 = true; 9. saisie8 = new Date(); 10. saisie9 = ""; 11. saisie10 = 0; 12. return null; 13.}

Si on utilise le bouton [Annuler] dans le formulaire prcdent, on obtient en retour la page suivante :

http://tahe.developpez.com/java/javaee

326/341

3 4

on obtient de nouveau le formulaire car le gestionnaire d'vnement form.cancel rend la cl de navigation null. La page [form.jsp] est donc renvoye. le modle [Form.java] a t modifi par la mthode form.cancel. Ceci est reflt par la colonne [2] qui affiche ce modle. la colonne [3], elle, reflte la valeur poste pour les composants.

Revenons sur le code Jsf du composant saisie1 [4] ;


1. <!-- ligne 1 --> 2. <h:outputText value="#{msg['saisie1.prompt']}"/> 3. <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/> 4. <h:message for="saisie1" styleClass="error"/> 5. <h:outputText value="#{form.saisie1}"/>

Ligne 4, la valeur du composant saisie1 est lie au modle form.saisie1. Cela entrane plusieurs choses :

lors d'un GET de [form.jsp], le composant saisie1 affichera la valeur du modle form.saisie1. lors d'un POST de [form.jsp], la valeur poste pour le composant saisie1 n'est affecte au modle form.saisie1 que si l'ensemble des validations et conversions du formulaire russissent. Que le modle ait t mis jour ou non par les valeurs postes, si le formulaire est renvoy l'issue du POST, les composants affichent la valeur qui a t poste et non pas la valeur du modle qui leur est associ. C'est ce que montre la copie d'cran ci-dessus, o les colonnes [1] et [2] n'ont pas les mmes valeurs.

21.7

Exemple n 7

Thme : Gestion des vnements lis des changements de valeur de composants de saisie.

21.7.1

L'application

http://tahe.developpez.com/java/javaee

327/341

L'application montre un exemple de POST ralis sans l'aide d'un bouton ou d'un lien. Le formulaire est le suivant :

1 2

Le contenu de la liste combo2 [1] est lie l'lment slectionn dans le combo1 [1]. Lorsqu'on change la slection dans [1], un POST du formulaire est ralis au cours duquel le contenu de combo2 est modifi pour reflter l'lment slectionn dans [1], puis le formulaire renvoy. Au cours de ce POST, aucune validation n'est faite.

21.7.2

Le projet Netbeans

Le projet Netbeans de l'application est le suivant :

On a un unique formulaire [form.jsp] avec son modle [Form.java].

21.7.3

Le formulaire [form.jsp]

Le formulaire [form.jsp] est le suivant :


1. ... 2. <html> 3. <f:view> 4. <head> 5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6. <title><h:outputText value="#{msg['app.titre']}"/></title> 7. <link href="styles.css" rel="stylesheet" type="text/css"/> 8. </head> 9. <body background="<c:url value="/ressources/standard.jpg"/>"> 10. <h2><h:outputText value="#{msg['app.titre2']}"/></h2> 11. <h:form id="formulaire"> 12. <h:panelGrid columns="4" border="1" columnClasses="col1,col2,col3,col4"> 13. <!-- headers --> 14. <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/> 15. <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/> 16. <h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/> 17. <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/> 18. <!-- ligne 1 --> 19. <h:outputText value="#{msg['combo1.promp