Vous êtes sur la page 1sur 78

Construire un service web Java EE avec l'IDE Netbeans 6.

5 et le serveur Java EE Glassfish

Serge Tah, fvrier 2009 http://tahe.developpez.com

Serge Tah, http://tahe.developpez.com, fvrier 2009

1/78

Le texte qui suit fait rfrence aux documents suivants : [ref1] : [http://tahe.ftp-developpez.com/fichiers-archive/javaee.pdf] - un cours Java EE [ref2] : [http://tahe.ftp-developpez.com/fichiers-archive/jpa.pdf] - un cours JPA Par facilit, ces rfrences sont notes par la suite [ref1] et [ref2].

Objectifs et Outils

Le tutoriel vise montrer les dmarches qui mnent la cration et au dploiement d'un service web J2EE avec l'IDE Netbeans 6.5 et le serveur d'application Glassfish qui l'accompagne. Par ailleurs, nous prsentons divers clients pour ce service web : clients Java, C#, Asp.Net, Flex. Les outils utiliss sont :

le SGBD MySQL 5 [http://www.mysql.com/] l'IDE Netbeans 6.5 [http://www.netbeans.org/] l'IDE Visual Studio Express 2008 SP1 (C# et Web) [http://www.microsoft.com/Express/] l'IDE Adobe Flex Builder 3 [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex] le serveur Apache de l'outil Wamp [http://www.wampserver.com/] Adobe Flash Player : [http://www.adobe.com/fr/products/flashplayer/]

Le code n'est pas expliqu dans ses moindres dtails. Aussi ce tutoriel est-il destin des personnes ayant une premire exprience avec Netbeans, Java EE, Ejb3 et JPA. Tous les lments utiliss dans ce tutoriel sont expliqus dans [ref1] et [ref2] mais pas ncessairement dans le tutoriel lui-mme. Le tutoriel est accompagn d'un fichier zip contenant les lments suivants : 3 1 2 4

5 6

[1] : le contenu du zip [2] : le service web JEE sous forme d'archive ear [3] : un ensemble de projets Netbeans 6.5 visant construire progressivement le service web JEE [4] : des clients .NET du service JEE - un client C# et deux clients ASP.NET / C# [5] : les clients Flex 3 du service JEE [6] : le script de cration de la base de donnes utilise par l'application

La nature du service web ralis

Une socit de services en informatique [ISTIA-IAIE] dsire proposer un service de prise de rendez-vous. Le premier march vis est celui des mdecins travaillant seuls. Ceux-ci n'ont en gnral pas de secrtariat. Les clients dsirant prendre rendez-vous tlphonent alors directement au mdecin. Celui-ci est ainsi drang frquemment au cours d'une journe ce qui diminue sa disponibilit ses patients. La socit [ISTIA-IAIE] souhaite leur proposer un service de prise de rendez-vous fonctionnant sur le principe suivant :

un secrtariat assure les prises de RV pour un grand nombre de mdecins. Ce secrtariat peut tre rduit une unique personne. Le salaire de celle-ci est mutualis entre tous les mdecins utilisant le service de RV. le secrtariat et tous les mdecins sont relis Internet les RV sont enregistrs dans une base de donnes centralise, accessible par Internet, par le secrtariat et les mdecins

Serge Tah, http://tahe.developpez.com, fvrier 2009

2/78

la prise de RV est normalement faite par le secrtariat. Elle peut tre faite galement par les mdecins eux-mmes. C'est le cas notamment lorsqu' la fin d'une consultation, le mdecin fixe lui-mme un nouveau RV son patient.

L'architecture du service de prise de RV est le suivant :

Secrtariat Internet Mdecin 1 Mdecin 2 ...


Les mdecins gagnent en efficacit s'ils n'ont plus grer les RV. S'ils sont suffisamment nombreux, leur contribution aux frais de fonctionnement du secrtariat sera faible. La socit [ISTIA-IAIE] dcide de raliser la partie serveur sous la forme d'un service web. L'application aura l'architecture suivante :

Base de donnes des RV

Utilisateur
3

Client

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Client

HTTP / SOAP

Java EE - serveur Sun

[1] : les couches [dao, jpa] permettant l'accs aux donnes sont ralises l'aide d'un service web J2EE [2] : nous prsenterons divers types de clients : Java, C#, Asp.Net

Fonctionnement de l'application

Nous appellerons [RdvMedecins] l'application. Nous prsentons ci-dessous des copies d'cran de ce que pourrait tre son fonctionnement avec un client web. Ce client web n'est pas prsent dans ce tutoriel. On le trouvera dans le document [http://tahe.developpez.com/dotnet/exercices/], exercice n 4. La page d'accueil de l'application est la suivante :

A partir de cette premire page, l'utilisateur (Secrtariat, Mdecin) va engager un certain nombre d'actions. Nous les prsentons cidessous. La vue de gauche prsente la vue partir de laquelle l'utilisateur fait une demande, la vue de droite la rponse envoye par le serveur.

Serge Tah, http://tahe.developpez.com, fvrier 2009

3/78

L'utilisateur a slectionn un mdecin et a saisi un jour de RV L'utilisateur a slectionn un mdecin et a saisi un jour de RV

On obtient la liste (vue partielle ici) des RV du mdecin On obtientpour liste (vue partielle ici) des RV du mdecin slectionn la le jour indiqu. slectionn pour le jour indiqu.

On obtient une page renseigner On rserve

Serge Tah, http://tahe.developpez.com, fvrier 2009

4/78

On la renseigne Le nouveau RV apparat dans la liste

L'utilisateur peut supprimer un RV

Le RV supprim a disparu de la liste des RV

Il la retrouve dans son dernier tat Une fois l'opration de prise de RV ou d'annulation de RV faite, l'utilisateur peut revenir la page d'accueil

Serge Tah, http://tahe.developpez.com, fvrier 2009

5/78

On efface pour repartir sur une nouvelle opration

Le service web Java EE des rendez-vous

Revenons l'architecture de l'application construire :

Utilisateur

Client

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Client

HTTP / SOAP

Java EE - serveur Sun

Nous nous intressons dans cette partie la construction du service web J2EE [1] excut sur un serveur Sun / Glassfish.

4.1

La base de donnes

La base de donnes qu'on appellera [dbrdvmedecins] est une base de donnes MySQL5 avec quatre tables :

4.1.1

La table [MEDECINS]

Elle contient des informations sur les mdecins grs par l'application [RdvMedecins].

Serge Tah, http://tahe.developpez.com, fvrier 2009

6/78

ID : n identifiant le mdecin - cl primaire de la table VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une modification est apporte la ligne. NOM : le nom du mdecin PRENOM : son prnom TITRE : son titre (Melle, Mme, Mr)

4.1.2

La table [CLIENTS]

Les clients des diffrents mdecins sont enregistrs dans la table [CLIENTS] :

ID : n identifiant le client - cl primaire de la table VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une modification est apporte la ligne. NOM : le nom du client PRENOM : son prnom TITRE : son titre (Melle, Mme, Mr)

4.1.3

La table [CRENEAUX]

Elle liste les crneaux horaires o les RV sont possibles :

ID : n identifiant le crneau horaire - cl primaire de la table (ligne 8) VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une modification est apporte la ligne. ID_MEDECIN : n identifiant le mdecin auquel appartient ce crneau cl trangre sur la colonne MEDECINS(ID). HDEBUT : heure dbut crneau MDEBUT : minutes dbut crneau

Serge Tah, http://tahe.developpez.com, fvrier 2009

7/78

HFIN : heure fin crneau MFIN : minutes fin crneau

La seconde ligne de la table [CRENEAUX] (cf [1] ci-dessus) indique, par exemple, que le crneau n 2 commence 8 h 20 et se termine 8 h 40 et appartient au mdecin n 1 (Mme Marie PELISSIER).

4.1.4

La table [RV]

Elle liste les RV pris pour chaque mdecin :

ID : n identifiant le RV de faon unique cl primaire JOUR : jour du RV ID_CRENEAU : crneau horaire du RV - cl trangre sur le champ [ID] de la table [CRENEAUX] fixe la fois le crneau horaire et le mdecin concern. ID_CLIENT : n du client pour qui est faite la rservation cl trangre sur le champ [ID] de la table [CLIENTS]

Cette table a une contrainte d'unicit sur les valeurs des colonnes jointes (JOUR, ID_CRENEAU) :
ALTER TABLE RV ADD CONSTRAINT UNQ1_RV UNIQUE (JOUR, ID_CRENEAU);

Si une ligne de la table[RV] a la valeur (JOUR1, ID_CRENEAU1) pour les colonnes (JOUR, ID_CRENEAU), cette valeur ne peut se retrouver nulle part ailleurs. Sinon, cela signifierait que deux RV ont t pris au mme moment pour le mme mdecin. D'un point de vue programmation Java, le pilote JDBC de la base lance une SQLException lorsque ce cas se produit. La ligne d'id gal 3 (cf [1] ci-dessus) signifie qu'un RV a t pris pour le crneau n 20 et le client n 4 le 23/08/2006. La table [CRENEAUX] nous apprend que le crneau n 20 correspond au crneau horaire 16 h 20 - 16 h 40 et appartient au mdecin n 1 (Mme Marie PELISSIER). La table [CLIENTS] nous apprend que le client n 4 est Melle Brigitte BISTROU.

4.2

Gnration de la base de donnes

Crez la base de donnes MySql [dbrdvmedecins] avec l'outil de votre choix. Pour crer les tables et les remplir on pourra utiliser le script [createbd.sql] qui vous sera fourni. Son contenu est le suivant :
1. create table CLIENTS ( 2. ID bigint not null auto_increment, 3. VERSION integer not null, 4. TITRE varchar(5) not null, 5. NOM varchar(30) not null, 6. PRENOM varchar(30) not null, 7. primary key (ID) 8. ) ENGINE=InnoDB; 9. 10. create table CRENEAUX ( 11. ID bigint not null auto_increment, 12. VERSION integer not null, 13. HDEBUT integer not null, 14. MDEBUT integer not null, 15. HFIN integer not null, 16. MFIN integer not null, 17. ID_MEDECIN bigint not null, 18. primary key (ID) 19. ) ENGINE=InnoDB; 20. 21. create table MEDECINS ( 22. ID bigint not null auto_increment,

Serge Tah, http://tahe.developpez.com, fvrier 2009

8/78

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. 62.

VERSION integer not null, TITRE varchar(5) not null, NOM varchar(30) not null, PRENOM varchar(30) not null, primary key (ID) ) ENGINE=InnoDB; create table RV ( ID bigint not null auto_increment, JOUR date not null, ID_CLIENT bigint not null, ID_CRENEAU bigint not null, primary key (ID) ) ENGINE=InnoDB; alter table CRENEAUX add index FK9BD7A197FE16862 (ID_MEDECIN), add constraint FK9BD7A197FE16862 foreign key (ID_MEDECIN) references MEDECINS (ID); alter table RV add index FKA4494D97AD2 (ID_CLIENT), add constraint FKA4494D97AD2 foreign key (ID_CLIENT) references CLIENTS (ID); alter table RV add index FKA441A673246 (ID_CRENEAU), add constraint FKA441A673246 foreign key (ID_CRENEAU) references CRENEAUX (ID); INSERT INTO CLIENTS ( VERSION, NOM, PRENOM, TITRE) VALUES (1, 'MARTIN', 'Jules', 'Mr'); ... INSERT INTO MEDECINS ( VERSION, NOM, PRENOM, TITRE) VALUES (1, 'PELISSIER', 'Marie', 'Mme'); ...

INSERT INTO CRENEAUX ( VERSION, ID_MEDECIN, HDEBUT, MDEBUT, HFIN, MFIN) VALUES (1, 1, 8, 0, 8, 20); 63. ... 64. 65. INSERT INTO RV ( JOUR, ID_CRENEAU, ID_CLIENT) VALUES ('2006-08-22', 1, 2); 66. ... 67. 68. ALTER TABLE RV ADD CONSTRAINT UNQ1_RV UNIQUE (JOUR, ID_CRENEAU); 69. 70. COMMIT WORK;

4.3

Les lments de l'architecture ct serveur

Revenons l'architecture de l'application construire : 2 1

Utilisateur
3

Client

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Client

HTTP / SOAP

Java EE - serveur Sun

Ct serveur, l'application sera forme : (a) d'une couche Jpa permettant de travailler avec la BD au moyen d'objets (b) d'un Ejb charg de grer les oprations avec la couche Jpa (c) d'un service web charg d'exposer des clients distants, l'interface de l'Ejb sous la forme d'un service web.

Serge Tah, http://tahe.developpez.com, fvrier 2009

9/78

Les lments (b) et (c) impmentent la couche [dao] reprsente sur le schma prcdent. On sait qu'une application peut accder un Ejb distant via les protocoles RMI et JNDI. Dans la pratique, cela limite les clients des clients Java. Un service web utilise un protocole de communication standardis que divers langages implmentent : .NET, Php, C++, ... C'est ce que nous voulons montrer ici en utilisant un client .NET. Pour une courte introduction aux services web, on pourra lire le cours [ref1], paragraphe 14, page 109. Un service web peut tre implment de deux faons :

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

Client du service web

Conteneur web tcp-ip

Conteneur Ejb3

Jpa

Donnes

serveur Java EE

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

Client du service web


Nous allons utiliser ici la premire solution : Client 1

Conteneur Ejb3 serveur Java EE

Jpa

Donnes

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

Dans le cours [ref1], paragraphe 14, page 109, on trouvera un exemple utilisant la seconde solution.

4.4

Configuration Hibernate du serveur Glassfish

Selon sa version, le serveur Glassfish V2 livr avec Netbeans peut ne pas avoir les bibliothques Hibernate dont la couche Jpa / Hibernate a besoin. Si dans la suite du tutoriel, vous dcouvrez que Glassfish ne vous propose pas d'implmentation Jpa / Hibernate ou qu'au dploiement des services, une exception indique que les bibliothques d'Hibernate ne sont pas trouves, vous devez rajouter les bibliothques dans le dossier [<glassfish>/domains/domain1/lib] puis redmarrer le serveur Glassfish :

Serge Tah, http://tahe.developpez.com, fvrier 2009

10/78

en [1], le dossier <glassfish>/.../lib en [2], les bibliothques Hibernate en [3], le pilote Jdbc de MySQL

Les bibliothques d'Hibernate sont dans le zip qui accompagne le tutoriel.

4.5

Les outils de gnration automatique de Netbeans

Revenons l'architecture que nous devons construire : Client 1

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

Avec Netbeans, il est possible de gnrer automatiquement la couche [JPA] et la couche [Ejb] qui contrle l'accs aux entits JPA gnres. Il est intressant de connatre ces mthodes de gnration automatique car le code gnr donne de prcieuses indications sur la faon d'crire des entits JPA ou le code Ejb qui les utilise. Nous dcrivons maintenant certains de ces outils de gnration automatique. Pour comprendre le code gnr, il faut avoir de bonnes notions sur les entits JPA [ref1] et les EJB [ref2]. Cration d'une connexion Netbeans la base de donnes

lancer le SGBD MySQL 5 afin que la BD soit disponible crer une connexion Netbeans sur la base [dbrdvmedecins]

Serge Tah, http://tahe.developpez.com, fvrier 2009

11/78

4 2 3

dans l'onglet [Files], dans la branche [Databases] [1], slectionner le pilote Jdbc MySQL [2] puis slectionner l'option [3] "Connect Using" permettant de crer une connexion avec une base MySQL en [4], donner les informations qui vous sont demandes puis valider en [5]

en [6], la connexion est cre. On y voit les quatre tables de la base de donnes connecte.

Cration d'un projet Ejb

1 2

en [1], crer une nouvelle application, un module Ejb en [2], choisir la catgorie [Java EE] et en [3] le type [EJB Module]

Serge Tah, http://tahe.developpez.com, fvrier 2009

12/78

5 4

en [4] choisir un dossier pour le projet et en [5] lui donner un nom - puis terminer l'assistant en [6] le projet gnr

Ajout d'une ressource JDBC au serveur Glassfish Nous allons ajouter une ressource JDBC au serveur Glassfish.

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Ejb3

dans l'onglet [Services], lancer le serveur Glassfish [2, 3] dans l'onglet [Projects], cliquer droit sur le projet Ejb et en [5] slectionner l'option [New / Other] permettant d'ajouter un lment au projet.

Serge Tah, http://tahe.developpez.com, fvrier 2009

13/78

9 6

10

en [6], slectionner la catgorie [Glassfish] et en [7] indiquer qu'on veut crer une ressource JDBC en slectionnant le type [JDBC Resource] en [8], indiquer que cette ressource JDBC va utiliser son propre pool de connexions en [9], donner un nom la ressource JDBC en [10], passer l'tape suivante

11

12 13 15

16 14

en [11], on dfinit les caractristiques du pool de connexions de la ressource JDBC en [12], donner un nom au pool de connexions en [13], choisir la connexion Netbeans [dbrdvmedecins] cre prcdemment en [14], passer l'tape suivante en [15], il n'y a normalement rien changer dans cette page. Les proprits de la connexion la base MySQL [dbrdvmedecins] ont t tires de celles de la connexion Netbeans [dbrdvmedecins] cre prcdemment

Serge Tah, http://tahe.developpez.com, fvrier 2009

14/78

en [16], passer l'tape suivante

17

19

18

en [17], garder les valeurs par dfaut proposes en [18], terminer l'assistant. Celui cre le fichier [sun-resources.xml] [19] dont le contenu 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/dbrdvmedecins" object-type="user" poolname="dbrdvmedecinsPool"> 5. <description/> 6. </jdbc-resource> 7. <jdbc-connection-pool ..."> 8. <property name="URL" value="jdbc:mysql://localhost:3306/dbrdvmedecins"/> 9. <property name="User" value="root"/> 10. <property name="Password" value="()"/> 11. </jdbc-connection-pool> 12. </resources>

Le fichier ci-dessus reprend toutes les informations saisies dans l'assistant sous un format XML. Il sera utilis par l'IDE Netbeans pour demander au serveur Glassfish de crer la ressource "jdbc/dbrdvmedecins" dfinie ligne 4. Cration d'une unit de persistance L'unit de persistance [persistence.xml] configure la couche JPA : elle indique l'implmentation JPA utilise (Toplink, Hibernate, ...) et configure celle-ci.

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Ejb3

Serge Tah, http://tahe.developpez.com, fvrier 2009

15/78

4 3

en [1], cliquer droit sur le projet Ejb et slectionner [New / Other] en [2] en [3], slectionner la catgorie [Persistence] puis en [4], indiquer que vous voulez crer une unit de persistance JPA

5 6 7 8 9

en [5], donner un nom l'unit de persistance cre en [6], choisir [Hibernate] comme implmentation JPA en [7], slectionner la ressource Glassfish "jdbc/dbrdvmedecins" qui vient d'tre cre en [8], indiquer qu'aucune action ne doit tre faite sur la base, lors de l'instanciation de la couche JPA terminer l'assistant en [9], le fichier [persistence.xml] cr par l'assistant

Son contenu est le suivant :


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="serveur-ejb-dao-jpa-hibernate-generePU" transaction-type="JTA"> 4. <provider>org.hibernate.ejb.HibernatePersistence</provider> 5. <jta-data-source>jdbc/dbrdvmedecins</jta-data-source> 6. <exclude-unlisted-classes>false</exclude-unlisted-classes> 7. <properties/> 8. </persistence-unit> 9. </persistence>

De nouveau, il reprend dans un format XML les informations donnes dans l'assistant. Ce fichier est insuffisant pour travailler avec la base MySQL5 "dbrdvmedecins". Il nous faudrait indiquer Hibernate le type de SGBD grer. Ce sera fait ultrieurement. Cration des entits JPA

Serge Tah, http://tahe.developpez.com, fvrier 2009

16/78

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Ejb3

1 4

en [1], cliquer droit sur le projet et en [2] choisir l'option [New / Other] en [3], slectionner la catgorie [Persistence] puis en [4], indiquer que vous voulez crer des entits JPA partir d'une base de donnes existante.

5 8 6

en [5], slectionner la source JDBC "jdbc/dbrdvmedecins" que nous avons cre en [6], les quatre tables de la base de donnes associe en [7,8], les inclure toutes dans la gnration des entits JPA en [9], poursuivre l'assistant

Serge Tah, http://tahe.developpez.com, fvrier 2009

17/78

10

12

11

13

en [10], les entits JPA qui vont tre gnres en [11], donner un nom au package des entits JPA en [12], choisir le type Java qui va encapsuler les listes d'objets rendus par la couche JPA terminer l'assistant en [13], les quatre entits JPA gnres, une pour chaque table de la base de donnes.

Voici par exemple le code de l'entit [Rv] qui reprsente une ligne de la table [rv] de la base [dbrdvmedecins].
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. package jpa; ... @Entity @Table(name = "rv") public class Rv implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "ID") private Long id; @Basic(optional = false) @Column(name = "JOUR") @Temporal(TemporalType.DATE) private Date jour; @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID") @ManyToOne(optional = false) private Creneaux idCreneau; @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID") @ManyToOne(optional = false) private Clients idClient; public Rv() { } ... }

Cration de la couche Ejb d'accs aux entits JPA

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Ejb3

Serge Tah, http://tahe.developpez.com, fvrier 2009

18/78

en [1], cliquer droit sur le projet et en [2], slectionner l'option [New / Other] en [3], slectionner la catgorie [Persistence] puis en [4] le type [Session Beans for Entity Classes]

7 5 6

en [5], les entits JPA cres prcdemment sont prsentes en [6], les slectionner toutes en [7], elles ont t slectionnes en [8], poursuivre l'assistant

Serge Tah, http://tahe.developpez.com, fvrier 2009

19/78

11

10

en [9], donner un nom au package des ejb qui vont tre gnrs en [10], indiquer que les Ejb doivent implmenter la fois une interface locale et distante terminer l'assistant en [11], les Ejb gnrs

Voici par exemple, le code de l'Ejb qui gre l'accs l'entit [Rv], donc la table [rv] de la base de donnes [dbrdvmedecins] :
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. package ejb; ... @Stateless public class RvFacade implements RvFacadeLocal, RvFacadeRemote { @PersistenceContext private EntityManager em; public void create(Rv rv) { em.persist(rv); } public void edit(Rv rv) { em.merge(rv); } public void remove(Rv rv) { em.remove(em.merge(rv)); } public Rv find(Object id) { return em.find(Rv.class, id); } public List<Rv> findAll() { return em.createQuery("select object(o) from Rv as o").getResultList(); } }

Comme il a t dit, la gnration automatique de code peut tre trs utile pour dmarrer un projet et se former sur les entits JPA et les EJB. Dans la suite, nous rcrivons les couches JPA et EJB avec notre propre code mais le lecteur y retrouvera des informations que nous venons de voir dans la gnration automatique des couches.

4.6

Le projet Netbeans du module Ejb

Nous crons un nouveau module Ejb vierge ( cf paragraphe 4.5, page 12) :

Serge Tah, http://tahe.developpez.com, fvrier 2009

20/78

le package [rdvmedecins.entites] regroupe les entits de la couche Jpa le package [rdvmedecins.dao] implmente l'Ejb de la couche [dao] le package [rdvmedecins.exceptions] implmente une classe d'exception spcifique l'application

Dans la suite, nous supposons que le lecteur a suivi toutes les tapes du paragraphe 4.5, page 11. Il devra en rpter certaines.

4.6.1

Configuration de la couche JPA

Rappelons l'architecture de notre application client / serveur : Client 1

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

Le projet Netbeans :

La couche [JPA] est configure par les fichiers [persistence.xml] et [sun-resources.xml] ci-dessus. Ces deux fichiers sont gnrs par des assistants dj rencontrs :

la gnration du fichier [sun-resources.xml] a t dcrite au pararaphe 4.5, page 13. la gnration du fichier [persistence.xml] a t dcrite au pararaphe 4.5, page 15.

Le fichier [persistence.xml] gnr doit tre modifi de la faon suivante :


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="dbrdvmedecins" transaction-type="JTA"> 4. <provider>org.hibernate.ejb.HibernatePersistence</provider> 5. <jta-data-source>jdbc/dbrdvmedecins</jta-data-source> 6. <properties> 7. <!-- Dialecte -->

Serge Tah, http://tahe.developpez.com, fvrier 2009

21/78

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> 8. </properties> 9. </persistence-unit> 10. </persistence>

ligne 3 : le type de transactions est JTA : les transactions seront gres par le conteneur Ejb3 de Glassfish ligne 4 : une implmentation Jpa / Hibernate est utilise. Pour cela, la bibliothque Hibernate a t ajoute au serveur Glassfish (cf paragraphe 4.4, page 10). ligne 5 : la source de donnes JTA utilise par la couche Jpa a le nom JNDI jdbc/dbrdvmedecins . ligne 8 : cette ligne n'est pas gnre automatiquement. Elle doit tre ajoute la main. Elle indique Hibernate, que le SGBD utilis est MySQL5.

La source de donnes "jdbc/dbrdvmedecins" est configure dans le fichier [sun-resources.xml] 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/dbrdvmedecins" object-type="user" poolname="dbrdvmedecinsPool"> 5. <description/> 6. </jdbc-resource> 7. <jdbc-connection-pool ...> 8. <property name="URL" value="jdbc:mysql://localhost/dbrdvmedecins"/> 9. <property name="User" value="root"/> 10. <property name="Password" value="()"/> 11. </jdbc-connection-pool> 12. </resources>

lignes 8-10 : les caractristiques Jdbc de la source de donnes (Url de la base, nom et mot de passe de l'utilisateur). La base de donnes MySQL dbrdvmedecins est celle dcrite au paragraphe 4.1, page 6. ligne 7 : les caractristiques du pool de connexions associ cette source de donnes

4.6.2

Les entits de la couche JPA

Rappelons l'architecture de notre application client / serveur : Client 1

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

Le projet Netbeans :

Serge Tah, http://tahe.developpez.com, fvrier 2009

22/78

Le package [rdvmedecins.entites] implmente la couche [Jpa]. Nous avons vu au paragraphe 4.5, page 16 comment gnrer automatiquement les entits Jpa d'une application. Nous n'utiliserons pas ici cette technique mais dfinirons nous-mmes les entits. Celles-ci reprendront cependant une bonne partie du code gnr au paragraphe 4.5, page 16. Ici, nous souhaitons que les entits [Medecin] et [Client] soient des classes filles d'une classe [Personne]. La classe Personne est utilise pour reprsenter les mdecins et les clients :
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. package rdvmedecins.entites; ... @MappedSuperclass public class Personne implements Serializable { // caractristiques d'une personne @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private Long id; @Version @Column(name = "VERSION", nullable = false) private Integer version; @Column(name = private String @Column(name = private String @Column(name = private String "TITRE", length = 5, nullable = false) titre; "NOM", length = 30, nullable = false) nom; "PRENOM", length = 30, nullable = false) prenom;

// constructeur par dfaut public Personne() { } // constructeur avec paramtres public Personne(String titre, String nom, String prenom) { // on passe par les setters ... } // constructeur par recopie public Personne(Personne personne) { // on passe par les setters ... } // toString @Override public String toString() { return "[" + titre + "," + prenom + "," + nom + "]"; } // getters et setters .... }

ligne 3 : on notera que la classe [Personne] n'est pas elle-mme une entit (@Entity). Elle va tre la classe parent d'entits. L'annotation @MappedSuperClass dsigne cette situation.

L'entit [Client] encapsule les lignes de la table [clients]. Elle drive de la classe [Personne] prcdente :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. package rdvmedecins.entites; .... @Entity @Table(name = "CLIENTS") public class Client extends Personne implements Serializable { // constructeur par dfaut public Client() { } // constructeur avec paramtres public Client(String titre, String nom, String prenom) { // parent super(titre, nom, prenom); }

Serge Tah, http://tahe.developpez.com, fvrier 2009

23/78

16. 17. // constructeur par recopie 18. public Client(Client client) { 19. // parent 20. super(client); 21. } 22. }

ligne 3 : la classe [Client] est une entit Jpa ligne 4 : elle est associe la table [clients] ligne 5 : elle drive de la classe [Personne]

L'entit [Medecin] qui encapsule les lignes de la table [medecins] suit le mme modle :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. package rdvmedecins.entites; ... @Entity @Table(name = "MEDECINS") public class Medecin extends Personne implements Serializable { // constructeur par dfaut public Medecin() { } // constructeur avec paramtres public Medecin(String titre, String nom, String prenom) { // parent super(titre, nom, prenom); } // constructeur par recopie public Medecin(Medecin medecin) { // parent super(medecin); } }

L'entit [Creneau] encapsule les lignes de la table [creneaux] :


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. package rdvmedecins.entites; .... @Entity @Table(name = "CRENEAUX") public class Creneau implements Serializable { // caractristiques d'un crneau de RV @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private Long id; @Version @Column(name = "VERSION", nullable = false) private Integer version; @ManyToOne @JoinColumn(name = "ID_MEDECIN", nullable = false) private Medecin medecin; @Column(name = "HDEBUT", nullable = false) private Integer hdebut; @Column(name = "MDEBUT", nullable = false) private Integer mdebut; @Column(name = "HFIN", nullable = false) private Integer hfin; @Column(name = "MFIN", nullable = false) private Integer mfin; // constructeur par dfaut public Creneau() { } // constructeur avec paramtres public Creneau(Medecin medecin, Integer hDebut,Integer mDebut, Integer hFin, Integer mFin) { // on passe par les setters ... }

Serge Tah, http://tahe.developpez.com, fvrier 2009

24/78

38. // constructeur par recopie 39. public Creneau(Creneau creneau) { 40. // on passe par les setters 41. ... 42. } 43. 44. // toString 45. @Override 46. public String toString() { 47. return "[" + getId() + "," + getVersion() + "," + getMedecin() + "," + getHdebut() + ":" + getMdebut() + "," + getHfin() + ":" + getMfin() + "]"; 48. } 49. 50. // setters - getters 51. ... 52. }

les lignes 15-17 modlisent la relation "plusieurs un" qui existe entre la table [creneaux] et la table [medecins] de la base de donnes : un mdecin a plusieurs crneaux, un crneau appartient un seul mdecin.

L'entit [Rv] encapsule les lignes de la table [rv] :


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. package rdvmedecins.entites; ... @Entity @Table(name = "RV") public class Rv implements Serializable { // caractristiques @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private Long id; @Column(name = "JOUR", nullable = false) @Temporal(TemporalType.DATE) private Date jour; @ManyToOne @JoinColumn(name = "ID_CLIENT", nullable = false) private Client client; @ManyToOne @JoinColumn(name = "ID_CRENEAU", nullable = false) private Creneau creneau; // constructeur par dfaut public Rv() { } // constructeur avec paramtres public Rv(Date jour, Client client, Creneau creneau) { // on passe par les setters ... } // constructeur par recopie public Rv(Rv rv) { // on passe par les setters ... }

// toString @Override public String toString() { return "[" + getId() + "," + new SimpleDateFormat("dd/MM/yyyy").format(getJour()) + "," + getClient() + "," + getCreneau() + "]"; 42. } 43. 44. // getters et setters 45. ... 46. }

les lignes 15-17 modlisent la relation "plusieurs un" qui existe entre la table [rv] et la table [clients] (un client peut apparatre dans plusieurs Rv) de la base de donnes et les lignes 18-20 la relation "plusieurs un" qui existe entre la table [rv] et la table [creneaux] (un crneau peut apparatre dans plusieurs Rv).

Serge Tah, http://tahe.developpez.com, fvrier 2009

25/78

4.6.3

La classe d'exception

La classe d'exception [RdvMedecinsException] de l'application 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. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. package rdvmedecins.exceptions; import javax.ejb.ApplicationException; @ApplicationException(rollback=true) public class RdvMedecinsException extends RuntimeException { private static final long serialVersionUID = 1L; // champs privs private int code = 0; // constructeurs public RdvMedecinsException() { super(); } public RdvMedecinsException(String message) { super(message); } public RdvMedecinsException(String message, Throwable cause) { super(message, cause); } public RdvMedecinsException(Throwable cause) { super(cause); } public RdvMedecinsException(String message, int code) { super(message); setCode(code); } public RdvMedecinsException(Throwable cause, int code) { super(cause); setCode(code); } public RdvMedecinsException(String message, Throwable cause, int code) { super(message, cause); setCode(code); } // getters - setters ... }

ligne 6 : la classe drive de la classe [RuntimeException]. Le compilateur ne force donc pas la grer avec des try / catch. ligne 5 : l'annotation @ApplicationException fait que l'exception ne sera pas "avale" par une exception de type [EjbException].

Pour comprendre l'annotation @ApplicationException revenons l'architecture utilise ct serveur :

Serge Tah, http://tahe.developpez.com, fvrier 2009

26/78

Client 1

S
Client 2

Exception de quel type ?

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

L'exception de type [RdvMedecinsException] sera lance par les mthodes de l'Ejb de la couche [dao] l'intrieur du conteneur Ejb3 et intercepte par celui-ci. Sans l'annotation @ApplicationException le conteneur Ejb3 encapsule l'exception survenue, dans une exception de type [EjbException] et relance celle-ci. On peut ne pas vouloir de cette encapsulation et laisser sortir du conteneur Ejb3 une exception de type [RdvMedecinsException]. C'est ce que permet l'annotation @ApplicationException. Par ailleurs, l'attribut (rollback=true) de cette annotation indique au conteneur Ejb3 que si l'exception de type [RdvMedecinsException] se produit l'intrieur d'une mthode excute au sein d'une transaction avec un SGBD, celle-ci doit tre annule. En termes techniques, cela s'appelle faire un rollback de la transaction.

4.6.4

L'Ejb de la couche [dao]


Client 1

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

L'interface java [IDao] de la couche [dao] est la suivante :


1. package rdvmedecins.dao; 2. ... 3. public interface IDao { 4. 5. // liste des clients 6. public List<Client> getAllClients(); 7. // liste des Mdecins 8. public List<Medecin> getAllMedecins(); 9. // liste des crneaux horaires d'un mdecin 10. public List<Creneau> getAllCreneaux(Medecin medecin); 11. // liste des Rv d'un mdecin, un jour donn 12. public List<Rv> getRvMedecinJour(Medecin medecin, String jour); 13. // trouver un client identifi par son id 14. public Client getClientById(Long id); 15. // trouver un client idenbtifi par son id 16. public Medecin getMedecinById(Long id); 17. // trouver un Rv identifi par son id 18. public Rv getRvById(Long id); 19. // trouver un crneau horaire identifi par son id

Serge Tah, http://tahe.developpez.com, fvrier 2009

27/78

20. 21. 22. 23. 24. 25. }

public Creneau getCreneauById(Long id); // ajouter un RV public Rv ajouterRv(String jour, Creneau creneau, Client client); // supprimer un RV public void supprimerRv(Rv rv);

L'interface locale [IDaoLocal] de l'Ejb se contente de driver l'interface [IDao] prcdente :


1. 2. 3. 4. 5. 6. 7. package rdvmedecins.dao; import javax.ejb.Local; @Local public interface IDaoLocal extends IDao{ }

Il en est de mme pour l'interface distante [IDaoRemote] :


1. 2. 3. 4. 5. 6. 7. package rdvmedecins.dao; import javax.ejb.Remote; @Remote public interface IDaoRemote extends IDao { }

L'Ejb [DaoJpa] implmente les deux interfaces, locale et distante :


1. 2. 3. 4. 5. 6. 7. package rdvmedecins.dao; ... @Stateless(mappedName="rdvmedecins.dao") @TransactionAttribute(TransactionAttributeType.REQUIRED) public class DaoJpa implements IDaoLocal,IDaoRemote { ... }

la ligne 3 indique que l'Ejb distant porte le nom "rdvmedecins.dao" la ligne 4 indique que toutes les mthodes de l'Ejb se droulent au sein d'une transaction gre par le conteneur Ejb3. la ligne 5 montre que l'Ejb implmente les interfaces locale et distante.

Le code complet de l'Ejb est 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. package rdvmedecins.dao; ... @Stateless(mappedName="rdvmedecins.dao") @TransactionAttribute(TransactionAttributeType.REQUIRED) public class DaoJpa implements IDaoLocal,IDaoRemote { @PersistenceContext private EntityManager em; // liste des clients public List<Client> getAllClients() { try { return em.createQuery("select c from Client c").getResultList(); } catch (Throwable th) { throw new RdvMedecinsException(th, 1); } } // liste des mdecins public List<Medecin> getAllMedecins() { try { return em.createQuery("select m from Medecin m").getResultList(); } catch (Throwable th) { throw new RdvMedecinsException(th, 2); } } // liste des crneaux horaires d'un mdecin donn // medecin : le mdecin public List<Creneau> getAllCreneaux(Medecin medecin) { try {

Serge Tah, http://tahe.developpez.com, fvrier 2009

28/78

32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43.

return em.createQuery("select c from Creneau c join c.medecin m where m.id=:idMedecin").setParameter("idMedecin", medecin.getId()).getResultList(); } catch (Throwable th) { throw new RdvMedecinsException(th, 3); } } // liste des Rv d'un mdecin donn, un jour donn // medecin : le mdecin // jour : le jour public List<Rv> getRvMedecinJour(Medecin medecin, String jour) { try { return em.createQuery("select rv from Rv rv join rv.creneau c join c.medecin m where m.id=:idMedecin and rv.jour=:jour").setParameter("idMedecin", medecin.getId()).setParameter("jour", new SimpleDateFormat("yyyy:MM:dd").parse(jour)).getResultList(); } catch (Throwable th) { throw new RdvMedecinsException(th, 4); } } // ajout d'un Rv // jour : jour du Rv // creneau : crneau horaire du Rv // client : client pour lequel est pris le Rv public Rv ajouterRv(String jour, Creneau creneau, Client client) { try { Rv rv = new Rv(new SimpleDateFormat("yyyy:MM:dd").parse(jour), client, creneau); em.persist(rv); return rv; } catch (Throwable th) { throw new RdvMedecinsException(th, 5); } } // suppression d'un Rv // rv : le Rv supprim public void supprimerRv(Rv rv) { try { em.remove(em.merge(rv)); } catch (Throwable th) { throw new RdvMedecinsException(th, 6); } } // rcuprer un client donn public Client getClientById(Long id) { try { return (Client) em.find(Client.class, id); } catch (Throwable th) { throw new RdvMedecinsException(th, 7); } } // rcuprer un mdecin donn public Medecin getMedecinById(Long id) { try { return (Medecin) em.find(Medecin.class, id); } catch (Throwable th) { throw new RdvMedecinsException(th, 8); } } // rcuprer un Rv donn public Rv getRvById(Long id) { try { return (Rv) em.find(Rv.class, id); } catch (Throwable th) { throw new RdvMedecinsException(th, 9); } } // rcuprer un crneau donn public Creneau getCreneauById(Long id) { try { return (Creneau) em.find(Creneau.class, id); } catch (Throwable th) { throw new RdvMedecinsException(th, 10);

44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 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. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105.

Serge Tah, http://tahe.developpez.com, fvrier 2009

29/78

106. 107. 108.

} } }

ligne 8 : l'objet EntityManager qui gre l'accs au contexte de persistance. A l'instanciation de la classe, ce champ sera initialis par le conteneur Ejb grce l'annotation @PersistenceContext de la ligne 7. ligne 15 : requte JPQL qui retourne toutes les lignes de la table [clients] sous la forme d'une liste d'objets [Client]. ligne 22 : requte analogue pour les mdecins ligne 32 : une requte JPQL ralisant une jointure entre les tables [creneaux] et [medecins]. Elle est paramtre par l'id du mdecin. ligne 43 : une requte JPQL ralisant une jointure entre les tables [rv], [creneaux] et [medecins] et ayant deux paramtres : l'id du mdecin et le jour du Rv. lignes 55-57 : cration d'un Rv puis persistance de celui-ci en base de donnes. ligne 67 : suppression d'un Rv en base de donnes. ligne 76 : ralise un select sur la base de donnes pour trouver un client donn ligne 85 : idem pour un mdecin ligne 94 : idem pour un Rv ligne 103 : idem pour un crneau horaire toutes les oprations avec le contexte de persistance em de la ligne 9 sont susceptibles de rencontrer un problme avec la base de donnes. Aussi sont-elles toutes entoures par un try / catch. L'ventuelle exception est encapsule dans l'exception "maison" RdvMedecinsException.

Le module Ejb une fois compil donne naissance un fichier .jar :

4.7

Dploiement de l'Ejb de la couche [dao] avec Netbeans

Netbeans permet de dployer de faon simple sur le serveur Glassfish l'Ejb cr prcdemment.

Serge Tah, http://tahe.developpez.com, fvrier 2009

30/78

dans les proprits du projet Ejb, vrifier les options d'excution [1]. en [2], le nom du serveur sur lequel va tre dploy l'Ejb dans l'onglet [Services] [3], on le lance [4]. 6

5 7

en [5], le serveur Glassfish une fois lanc. Il n'a pas encore de module Ejb. lancez le serveur MySQL et assurez-vous que la base [dbrdvmedecins] est en ligne. Pour cela, vous pouvez utiliser la connexion Netbeans cre au paragraphe 4.5, page 11. dans l'onglet [Projects] [6], on dploie le module Ejb [7] : il faut que le SGBD MySQL5 soit lanc pour que la ressource JDBC "jdbc/dbrdvmedecins" utilise par l'Ejb soit accessible. en [8], l'Ejb dploy apparat dans l'arborescence du serveur Glassfish

9 10

en [9], on enlve l'Ejb dploy en [10], l'Ejb n'apparat plus dans l'arborescence du serveur Glassfish.

4.8

Dploiement de l'Ejb de la couche [dao] avec Glassfish

Nous montrons ici comment dployer sur le serveur Glassfish un Ejb partir de son archive .jar.

Serge Tah, http://tahe.developpez.com, fvrier 2009

31/78

lancez le serveur MySQL et assurez-vous que la base [dbrdvmedecins] est en ligne. Pour cela, vous pouvez utiliser la connexion Netbeans cre au paragraphe 4.5, page 11.

Rappelons la configuration Jpa du module Ejb qui va tre dploy. Cette configuration est faite dans le fichier [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="dbrdvmedecins" transaction-type="JTA"> 4. <provider>org.hibernate.ejb.HibernatePersistence</provider> 5. <jta-data-source>jdbc/dbrdvmedecins</jta-data-source> 6. <properties> 7. <!-- Dialecte --> 8. <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> 9. </properties> 10. </persistence-unit> 11. </persistence>

La ligne 5 indique que la couche Jpa utilise une source de donnes JTA, c.a.d. gre par le conteneur Ejb3, nomme "jdbc/dbrdvmedecins". Nous avons vu au paragraphe 4.5, page 13, comment crer cette ressource JDBC partir de Netbeans. Nous montrons ici comment faire la mme chose directement avec Glassfish. Nous suivons ici une procdure dcrite au paragraphe 13.1.2, page 79 de [ref1]. Nous commenons par supprimer la ressource afin de pouvoir la recrer. Nous le faisons partir de Netbeans :

2 1 3

en [1], les ressources JDBC du serveur Glassfish en [2], la ressource "jdbc/dbrdvmedecins" de notre Ejb en [3], le pool de connexions de cette ressource JDBC

5 4

en [4], on supprime le pool de connexions. Cela va avoir pour effet de supprimer toutes les ressources JDBC qui l'utilisent, donc la ressource "jdbc/dbrdvmedecins". en [5] et [6], la ressource JDBC et le pool de connexions ont t dtruits.

Maintenant, nous utilisons la console d'administration du serveur Glassfish pour crer la ressource JDBC et dployer l'Ejb.

Serge Tah, http://tahe.developpez.com, fvrier 2009

32/78

4 2

dans l'onglet [services] [1] de Netbeans, lancez le serveur Glassfish [2] puis accdez [3] sa console d'administration en [4], connectez-vous comme administrateur (mot de passe : adminadmin si vous n'avez pas chang celui-ci lors de l'installation ou aprs).

10 5 7 6 9 8

en [5], slectionnez la branche [Connection Pools] des ressources de Glassfish en [6], crez un nouveau pool de connexions. On rappelle qu'un pool de connexions est une technique pour limiter le nombre d'ouvertures / fermetures de connexions avec un SGBD. Au dmarrage du serveur, N, un nombre dfini par configuration, connexions sont ouvertes avec le SGBD. Ces connexions ouvertes sont ensuites mises disposition des Ejb qui les sollicitent pour faire une opration avec le SGBD. Ds que celle-ci est termine, l'Ejb rend la connexion au pool. La connexion n'est jamais ferme. Elle est partage entre les diffrents threads qui accdent au SGBD en [7], donnez un nom au pool en [8], la classe modlisant la source de donnes est la classe [javax.sql.DataSource] en [9], le SGBD qui dtient la source de donnes est ici MySQl. en [10], passez l'tape suivante

11

12 13

Serge Tah, http://tahe.developpez.com, fvrier 2009

33/78

en [11], l'attribut "Connection Validation Required" fait qu'avant de donner une connexion, le pool vrifie qu'elle est oprationnelle. Si ce n'est pas le cas, il en cre une nouvelle. Ceci permet une application de continuer fonctionner aprs une coupure momentane avec le SGBD. Pendant la coupure, aucune connexion n'est utilisable et des exceptions sont remontes au client. Lorsque la coupure est termine, les clients qui continuent demander des connexions les obtiennent de nouveau : grce l'attribut "Connection Validation Required" toutes les connexions du pool vont tre recres. Sans cet attribut, le pool constaterait que les connexions initiales ont t coupes mais ne chercherait pas en recrer de nouvelles. en [12], on demande le niveau d'isolement "Read Committed" pour les transactions. Ce niveau assure que une transaction T2 ne peut lire des donnes modifies par une transaction T1 tant que cette dernire n'est pas totalement termine. en [13], on demande ce que toutes les transactions utilisent le niveau d'isolement prcis en [12]

14 15 16 20 17

18

19

en [14] et [15], prcisez l'Url de la BD dont le pool gre les connexions en [16], l'utilisateur sera root en [17], ajoutez une proprit en [18], ajoutez la proprit "Password" avec la valeur () en [19]. Bien que la copie d'cran [19] ne le montre pas, il ne faut pas mettre la chane vide mais bien () (parenthse ouvrante, parenthse fermante) pour dsigner un mot de passe vide. Si l'utilisateur root de votre SGBD MySQL a un mot de passe non vide, mettez ce mot de passe. en [20], terminez l'assistant de cration du pool de connexions pour la base MySQL [dbrdvmedecins].

22

21

en [21], le pool a t cr. On clique sur son lien. en [22], le boton [Ping] permet de crer une connexion avec la bd [dbrdvmedecins] en [23], si tout va bien, un message indique que la connexion a russi

23

Une fois le pool de connexions cr, on peut crer une ressource Jdbc :

Serge Tah, http://tahe.developpez.com, fvrier 2009

34/78

2 1 3 4

en [1], on slectionne la branche [JDBC Resources] de l'arbre des objets du serveur en [2], on cre une nouvelle ressource JDBC en [3], on donne un nom la ressource JDBC. Celui-ci doit correspondre au nom utilis dans le fichier [persistence.xml] :

<jta-data-source>jdbc/dbrdvmedecins</jta-data-source>

en [4], on prcise le pool de connexions que doit utiliser la nouvelle ressource JDBC : celui qu'on vient de crer en [5], on termine l'assistant de cration

en [6] la nouvelle ressource JDBC

Maintenant que la ressource JDBC est cre, on peut dployer l'archive jar de l'Ejb :

2 3 1 4

en [1], slectionnez la branche [Enterprise Applications] en [2], avec le bouton [Deploy], indiquez que vous voulez dployer une nouvelle application en [3], indiquez que l'application est un module Ejb en [4], slectionnez le jar de l'Ejb [serveur-ejb-dao-jpa-hibernate.jar] qui vous aura t donn pour le TP. en [5], vous pouvez changer le nom du module Ejb si vous le souhaitez en [6], terminez l'assistant de dploiement du module ejb

Serge Tah, http://tahe.developpez.com, fvrier 2009

35/78

en [7], le module Ejb a t dploy. Il peut dsormais tre utilis.

4.9

Tests de l'Ejb de la couche [dao]

Maintenant que l'Ejb de la couche [dao] de notre application a t dploy, nous pouvons le tester. Nous le ferons au moyen du client Java suivant :

1 2 3 4

La classe [MainTestsDaoRemote] [1] est une classe de test JUnit 4. Les bibliothques en [2] sont constitues d'une part :

du jar de l'Ejb de la couche [dao] [3] (cf page 30). des bibliothques Glassfish [4] ncessaires aux clients distants des Ejb.

La classe de test est la suivante :


1. package dao; 2. ... 3. public class MainTestsDaoRemote { 4. 5. // couche [dao] teste 6. private static IDaoRemote dao; 7. 8. @BeforeClass 9. public static void init() throws NamingException { 10. // initialisation environnement JNDI 11. InitialContext initialContext = new InitialContext(); 12. // instanciation couche dao 13. dao = (IDaoRemote) initialContext.lookup("rdvmedecins.dao"); 14. } 15. 16. @Test 17. public void test1() { 18. // donnes du test 19. String jour = "2006:08:23"; 20. // affichage clients 21. List<Client> clients = null;

Serge Tah, http://tahe.developpez.com, fvrier 2009

36/78

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. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78.

79. 80. 81. 82. 83. 84. // mthode utilitaire - affiche les lments d'une collection 85. private static void display(String message, List elements) { 86. System.out.println(message); 87. for (Object element : elements) { 88. System.out.println(element); 89. } 90. } 91. }

try { clients = dao.getAllClients(); display("Liste des clients :", clients); } catch (Exception ex) { System.out.println(ex); } // affichage mdecins List<Medecin> medecins = null; try { medecins = dao.getAllMedecins(); display("Liste des mdecins :", medecins); } catch (Exception ex) { System.out.println(ex); } // affichage crneaux d'un mdecin Medecin medecin = medecins.get(0); List<Creneau> creneaux = null; try { creneaux = dao.getAllCreneaux(medecin); display(String.format("Liste des crneaux du mdecin %s", medecin), creneaux); } catch (Exception ex) { System.out.println(ex); } // liste des Rv d'un mdecin, un jour donn try { display(String.format("Liste des crneaux du mdecin %s, le [%s]", medecin, jour), dao.getRvMedecinJour(medecin, jour)); } catch (Exception ex) { System.out.println(ex); } // ajouter un RV Rv rv = null; Creneau creneau = creneaux.get(2); Client client = clients.get(0); System.out.println(String.format("Ajout d'un Rv le [%s] dans le crneau %s pour le client %s", jour, creneau, client)); try { rv = dao.ajouterRv(jour, creneau, client); System.out.println("Rv ajout"); display(String.format("Liste des Rv du mdecin %s, le [%s]", medecin, jour), dao.getRvMedecinJour(medecin, "2006:08:23")); } catch (Exception ex) { System.out.println(ex); } // ajouter un RV dans le mme crneau du mme jour // doit provoquer une exception System.out.println(String.format("Ajout d'un Rv le [%s] dans le crneau %s pour le client %s", jour, creneau, client)); try { rv = dao.ajouterRv(jour, creneau, client); System.out.println("Rv ajout"); display(String.format("Liste des Rv du mdecin %s, le [%s]", medecin, jour), dao.getRvMedecinJour(medecin, "2006:08:23")); } catch (Exception ex) { System.out.println(ex); } // supprimer un RV System.out.println("Suppression du Rv ajout"); try { dao.supprimerRv(rv); System.out.println("Rv supprim"); display(String.format("Liste des Rv du mdecin %s, le [%s]", medecin, jour), dao.getRvMedecinJour(medecin, "2006:08:23")); } catch (Exception ex) { System.out.println(ex); } }

ligne 13 : on notera l'instanciation du proxy de l'Ejb distant. On utilise son nom JNDI "rdvmedecins.dao".

Serge Tah, http://tahe.developpez.com, fvrier 2009

37/78

les mthodes de test utilisent les mthodes exposes par l'Ejb (cf page 27).

Si tout va bien, les tests doivent passer :

Maintenant que l'Ejb de la couche [dao] est oprationnel, on peut passer son exposition publique via un service web.

4.10

Le service web de la couche [dao]

Pour une courte introduction la notion de service web, on lira le paragraphe 14, page 111 de [ref1]. Revenons l'architecture du serveur de notre application client / serveur : Client 1

S
Client 2

HTTP / SOAP

Service web [dao]

Couche Ejb [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

conteneur Web

conteneur Ejb3

Nous nous intressons ci-dessus au service web de la couche [dao]. Ce service a pour seul rle de rendre disponible l'interface de l'Ejb de la couche [dao] des clients multi-plateformes capables de dialoguer avec un service web. Rappelons qu'il y a deux faons d'implmenter un service web :

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

Client du service web

Conteneur web tcp-ip

Conteneur Ejb3

Jpa

Donnes

serveur Java EE

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

Client du service web

Conteneur Ejb3 serveur Java EE

Jpa

Donnes

Nous utilisons ici la premire solution. Dans l'IDE Netbeans, il nous faut construire un projet d'entreprise avec deux modules :

le module Ejb qui s'excutera dans le conteneur Ejb : l'Ejb de la couche [dao]. le module web qui s'excutera dans le conteneur web : le service web que nous sommes en train de construire.

Nous allons construire ce projet d'entreprise de deux faons.

Serge Tah, http://tahe.developpez.com, fvrier 2009

38/78

4.10.1

Projet Netbeans - Version 1

Nous construisons tout d'abord un projet Netbeans de type "Web Application" :

3 1 2

en [1], on cre un nouveau projet dans la catgorie "Java Web" [2] de type "Web Application" [3].

4 5 6

en [4], on donne un nom au projet et en [5] on prcise le dossier dans lequel il doit tre gnr en [6], on fixe le serveur d'application qui va excuter l'application web en [7], on fixe le contexte de l'application en [8], on valide la configuration du projet.

10 9

12

11

12

en [9], le projet gnr.

Le service web que nous construisons va utiliser l'EJB du projet prcdent [10]. Aussi a-t-il besoin de rfrencer le .jar du module Ejb [10].

en [11], on ajoute un projet Netbeans aux bibliothques du projet web [12]

Serge Tah, http://tahe.developpez.com, fvrier 2009

39/78

13

en [13], on slectionne le dossier du module Ejb dans le systme de fichiers et on valide. 15 14

en [14], le module Ejb a t ajout aux bibliothques du projet web.

En [15], nous implmentons le service web avec la classe [WsDaoJpa] suivante :


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. package rdvmedecins.ws; ... @WebService() public class WsDaoJpa implements IDao { @EJB private IDaoLocal dao; // liste des clients @WebMethod public List<Client> getAllClients() { return dao.getAllClients(); } // liste des mdecins @WebMethod public List<Medecin> getAllMedecins() { return dao.getAllMedecins(); } // liste des crneaux horaires d'un mdecin donn // medecin : le mdecin @WebMethod public List<Creneau> getAllCreneaux(Medecin medecin) { return dao.getAllCreneaux(medecin); } // liste des Rv d'un mdecin donn, un jour donn // medecin : le mdecin // jour : le jour @WebMethod public List<Rv> getRvMedecinJour(Medecin medecin, String jour) { return dao.getRvMedecinJour(medecin, jour); } // ajout d'un Rv // jour : jour du Rv // creneau : crneau horaire du Rv // client : client pour lequel est pris le Rv @WebMethod public Rv ajouterRv(String jour, Creneau creneau, Client client) { return dao.ajouterRv(jour, creneau, client); }

Serge Tah, http://tahe.developpez.com, fvrier 2009

40/78

44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. }

// suppression d'un Rv // rv : le Rv supprim @WebMethod public void supprimerRv(Rv rv) { dao.supprimerRv(rv); } // rcuprer un client donn @WebMethod public Client getClientById(Long id) { return dao.getClientById(id); } // rcuprer un mdecin donn @WebMethod public Medecin getMedecinById(Long id) { return dao.getMedecinById(id); } // rcuprer un Rv donn @WebMethod public Rv getRvById(Long id) { return dao.getRvById(id); } // rcuprer un crneau donn @WebMethod public Creneau getCreneauById(Long id) { return dao.getCreneauById(id); }

ligne 4, la classe [WsdaoJpa] implmente l'interface [IDao]. Rappelons que cette interface est dfinie dans l'archive de l'Ejb de la couche [dao] sous la forme suivante :
package rdvmedecins.dao; ... public interface IDao { // liste des clients public List<Client> getAllClients(); // liste des Mdecins public List<Medecin> getAllMedecins(); // liste des crneaux horaires d'un mdecin public List<Creneau> getAllCreneaux(Medecin medecin); // liste des Rv d'un mdecin, un jour donn public List<Rv> getRvMedecinJour(Medecin medecin, String jour); // trouver un client identifi par son id public Client getClientById(Long id); // trouver un client idenbtifi par son id public Medecin getMedecinById(Long id); // trouver un Rv identifi par son id public Rv getRvById(Long id); // trouver un crneau horaire identifi par son id public Creneau getCreneauById(Long id); // ajouter un RV public Rv ajouterRv(String jour, Creneau creneau, Client client); // supprimer un RV public void supprimerRv(Rv rv);

a) b) c) d) e) f) g) h) i) j) k) l) m) n) o) p) q) r) s) t) u) v) w) x) y)

ligne 3 : l'annotation @WebService fait de la classe [WsDaoJpa] un service web. lignes 6-7 : la rfrence de l'Ejb de la couche [dao] sera injecte par le serveur d'applications dans le champ de la ligne 7. C'est l'implmentation locale IDaoLocal qui est ici injecte parce que le service web s'excute dans la mme Jvm que l'Ejb. toutes les mthodes du service web sont tagues avec l'annotation @WebMethod pour en faire des mthodes visibles aux clients distants. Une mthode non tague avec l'annotation @WebMethod serait interne au service web et non visible aux clients distants. Chaque mthode M du service web se contente d'appeler la mthode M correspondante de l'Ejb inject en ligne 7.

La cration de ce service web est reflte par une nouvelle branche dans le projet Netbeans :

Serge Tah, http://tahe.developpez.com, fvrier 2009

41/78

On voit en [1] le service web WsDaoJpa et en [2] les mthodes qu'il expose aux clients distants. Rappelons l'architecture du service web en construction :

Client du service web

Conteneur web

Conteneur 2 Ejb3

Jpa

Donnes

tcp-ip

serveur Java EE

Les composantes du service web que nous allons dployer sont :


[1] : le module web que nous venons de construire [2] : le module Ejb que nous avons construit lors d'une tape prcdente et dont dpend le service web

Pour les dployer ensemble, il faut rassembler les deux modules dans un projet Netbeans dit "d'entreprise" :

3 1 2

En [1] on cre un nouveau projet d'entreprise [2, 3].

4 5

en [4,5], on donne un nom au projet et on fixe son dossier de cration

Serge Tah, http://tahe.developpez.com, fvrier 2009

42/78

en [6], on choisit le serveur d'application sur lequel sera dploye l'application d'entreprise en [7], un projet d'entreprise peut avoir trois composantes : application web, module Ejb, application cliente. Ici, le projet est cr sans aucune composante. Celles-ci seront rajoutes ultrieurement. 8

en [8], l'application d'entreprise nouvellement cre.

9 10 11

en [9], cliquer droit sur [Java EE Modules] et ajouter un nouveau module en [10], seuls les modules Netbeans actuellement ouverts dans l'IDE sont prsents. Ici nous slectionnons le module web [serveur-webservice-1-ejb-dao-jpa-hibernate] et le module Ejb [serveur-ejb-dao-jpa-hibernate] que nous avons construits. en [11], les deux modules ajouts au projet d'entreprise.

Il nous reste dployer cette application d'entreprise sur le serveur Glassfish. Pour la suite, le SGBD MySQL doit tre lanc afin que la source de donnes JDBC "jdbc/dbrdvmedecins" utilise par le module Ejb soit accessible.

2 3

en [1], on lance le serveur Glassfish si le module Ejb [serveur-ejb-dao-jpa-hibernate] est dploy, on le dcharge [2] en [3], on dploie l'application d'entreprise

Serge Tah, http://tahe.developpez.com, fvrier 2009

43/78

en [4], elle est dploye. On voit qu'elle contient les deux modules : Web et Ejb.

4.10.2

Projet Netbeans - version 2

Nous montrons maintenant comment dployer le service web lorsqu'on ne dispose pas du code source du module Ejb mais seulement son archive .jar. Le nouveau projet Netbeans du service web sera le suivant : 1

2 3

Les lments notables du projet sont les suivants :


[1] : le service web est implment par un projet Netbeans de type [Web Application]. [2] : le service web est implment par la classe [WsDaoJpa] dj tudie [3] : l'archive de l'Ejb de la couche [dao] qui permet la classe [WsDaoJpa] d'avoir accs aux dfinitions des diffrentes classes, interfaces, entits des couches [dao] et [jpa].

Nous construisons ensuite le projet d'entreprise ncessaire au dploiement du service web : 2 3

[1], on cre une application d'entreprise [ea-rdvmedecins], au dpart sans aucun module. en [2], on ajoute le module web [serveur-webservice-ejb-dao-jpa-hibernate] prcdent en [3], le rsultat.

Telle quelle, l'application d'entreprise [ea-rdvmedecins] ne peut pas tre dploye sur le serveur Glassfish partir de Netbeans. On obtient une erreur. Il faut alors dployer la main l'archive ear de l'application [ea-rdvmedecins] :

Serge Tah, http://tahe.developpez.com, fvrier 2009

44/78

1 2 3 4

l'archive [ea-rdvmedecins.ear] est trouve dans le dossier [dist] [2] de l'onglet [Files] de Netbeans. dans cette archive [3], on trouve les deux lments de l'application d'entreprise : l'archive de l'Ejb [serveur-ejb-dao-jpa-hibernate]. Cette archive est prsente parce qu'elle faisait partie des bibliothques rfrences par le service web. l'archive du service web [serveur-webservice- ejb-dao-jpa-hibernate]. l'archive [ea-rdvmedecins.ear] est construite par un simple Build [4] de l'application d'entreprise. en [5], l'opration de dploiement qui choue.

Pour dployer l'archive [ea-rdvmedecins.ear] de l'application d'entreprise, nous procdons comme il a t montr lors du dploiement de l'archive de l'Ejb [serveur-ejb-dao-jpa-hibernate.jar] au paragraphe 4.2, page 8. Nous utilisons de nouveau le client web d'administration du serveur Glassfish. Nous ne rptons pas des tapes dj dcrites. Tout d'abord, on commencera par "dcharger" l'application d'entreprise dploye au paragraphe 4.10.1, page 39 :

3 4 2

[1] : slectionnez la branche [Enterprise Applications] du serveur Glassfish en [2] slectionner l'application d'entreprise dcharger puis en [3] la dcharger en [4] l'application d'entreprise a t dcharge

1 2 3

en [1], choisissez la branche [Enterprise Applications] du serveur Glassfish en [2], dployez une nouvelle application d'entreprise en [3], slectionnez le type [Enterprise Application]

Serge Tah, http://tahe.developpez.com, fvrier 2009

45/78

en [4], dsignez le fichier .ear du projet Netbeans [ea-rdvmedecins] en [5], dployez cette archive

7 10 6 9 8

en [6], l'application a t dploye en [7], le service web [WsDaoJpa] apparat dans la branche [Web Services] du serveur Glassfish. On le slectionne. en [8], on a diverses informations sur le service web. La plus intressante pour un client est l'information [9] : l'uri du service web. en [10], on peut tester le service web

11

12 13

en [11], l'uri du service web laquelle on a ajout le paramtre ?tester. Cette uri prsente une page de test. Toutes les mthodes (@WebMethod) exposes par le service web sont affiches et peuvent tre testes. Ici, on teste la mthode [13] qui demande la liste des clients.

Serge Tah, http://tahe.developpez.com, fvrier 2009

46/78

14

en [14], nous ne prsentons qu'une vue partielle de la page de rponse. Mais on peut voir que la mthode getAllClients a bien renvoy la liste des clients. La copie d'cran nous montre qu'elle envoie sa rponse dans un format XML.

Un service web est entirement dcrit par un fichier XML appel fichier WSDL :

en [1] dans l'outil web d'administration du serveur Glassfish, slectionnez le service web [WsDaoJpa] en [2], suivez le lien [View WSDL]

Serge Tah, http://tahe.developpez.com, fvrier 2009

47/78

en [3] : l'uri du fichier WSDL. C'est une information importante connatre. Elle est ncessaire pour configurer les clients de ce service web. en [4], la description XML du service web. Nous ne commenterons pas ce contenu complexe.

4.10.3

Tests JUnit du service web

Nous crons un projet Netbeans pour "jouer" les tests dj jous avec un client Ejb avec, cette fois-ci, un client pour le service web dernirement dploy. Nous suivons ici une dmarche analogue celle dcrite au paragraphe 14.2.1, page 115 de [ref1].

1 2

en [1], un projet Java classique en [2], la classe de test en [3], le client utilise l'archive de l'Ejb pour avoir accs aux dfinitions de l'interface de la couche [dao] et des entits Jpa. On rappelle que cette archive est dans le sous-dossier [dist] du dossier du module Ejb.

Pour accder au service web distant, il est ncessaire de gnrer des classes proxy :

Client

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

HTTP / SOAP

Java EE - serveur Sun

Serge Tah, http://tahe.developpez.com, fvrier 2009

48/78

Dans le schma ci-dessus, la couche [2] [C=Client] communique avec la couche [1] [S=Serveur]. Pour dialoguer avec la couche [S], le client [C] est amen crer une connexion rseau avec la couche [S] et dialoguer avec elle selon un protocole prcis. Les connexions rseau sont des connexions TCP et le protocole de transport est HTTP. La couche [S] qui reprsente le service web est implmente par une servlet Java excute par le serveur Glassfish. Nous n'avons pas crit cette servlet. Sa gnration est automatise par Glassfish partir des annotations @Webservice et @WebMethod de la classe [WsDaoJpa] que nous avons crite. De mme, nous allons automatiser la gnration de la couche [C] du client. On appelle parfois la couche [C], une couche proxy du service web distant, le terme proxy dsignant un lment intermdiaire dans une chane logicielle. Ici, le proxy C est l'intermdiaire entre le client que nous allons crire et le service web que nous avons dploy. Avec Netbeans 6.5, le proxy C peut tre gnr de la faon suivante (pour la suite, il faut que le service web soit actif sur le serveur Glassfish) :

en [1], ajouter un nouvel lment au projet Java en [2], slectionner la branche [Web services] en [3], slectionner [Web Service Client]

6 5

en [4], fournir l'uri du fichier WSDL du service web. Cette uri a t prsente au paragraphe 4.10.2, page 46. en [5], laisser la valeur par dfaut [JAX-WS]. L'autre valeur possible est [JAX-RPC] aprs avoir valid l'assistant de cration du proxy du service web, le projet Netbeans a t enrichi d'une branche [Web Service References] [6]. Cette branche montre les mthodes exposes par le service web distant.

Serge Tah, http://tahe.developpez.com, fvrier 2009

49/78

9 10 8

dans l'onglet [Files] [7], des codes sources Java ont t rajouts [8]. Ils correspondent au proxy C gnr. en [9] le code de l'une des classes. On y voit [10] qu'elles ont t places dans un paquetage [rdvmedecins.ws]. Nous ne commenterons pas le code de ces classes qui est de nouveau assez complexe.

Pour le client Java que nous sommes en train de construire, le proxy C gnr sert d'intermdiaire. Pour accder la mthode M du service web distant, le client Java appelle la mthode M du proxy C. Le client Java appelle ainsi des mthodes locales (excutes dans la mme Jvm) et de faon transparente pour lui, ces appels locaux sont traduits en appels distants. Il nous reste savoir appeler les mthodes M du proxy C. Revenons notre classe de test JUnit :

En [1], la classe de test [MainTestsDaoRemote] est celle dj utilise lors du test de l'Ejb de la couche [dao] :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. package dao; ... public class MainTestsDaoRemote { // couche [dao] teste private static IDaoRemote dao; @BeforeClass public static void init() throws NamingException { } @Test public void test1() { ... } }

ligne [13], le test test1 est conserv l'identique. ligne [9], le contenu de la mthode [init] a t supprim.

A ce stade, le projet prsente des erreurs car la mthode de test [test1] utilise les entits [Client], [Medecin], [Creneau], [Rv] qui ne sont plus dans les mmes packages qu'auparavant. Elles sont dans le package du proxy C gnr. On supprime les instructions import concernes et on les rgnre par l'opration Fix Imports.

Serge Tah, http://tahe.developpez.com, fvrier 2009

50/78

Revenons au code de la classe de test [MainTestsDaoRemote] :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. package dao; ... public class MainTestsDaoRemote { // couche [dao] teste private static IDaoRemote dao; @BeforeClass public static void init() throws NamingException { }

La mthode [init] de la ligne 10 doit initialiser la rfrence de la couche [dao] de la ligne 7. Il nous faut savoir comment utiliser le proxy C gnr, dans notre code. Netbeans nous aide dans cette dmarche.

2 1

slectionner en [1] la mthode [getAllClients] du service web puis, avec la souris, tirer cette mthode pour aller la dposer au sein de la mthode [init] de la classe de test.

On obtient le rsultat [2]. Ce squelette de code nous montre comment utiliser le proxy C gnr :
1. 2. 3. 4. 5. try { // Call Web Service Operation rdvmedecins.ws.WsDaoJpaService service = new rdvmedecins.ws.WsDaoJpaService(); rdvmedecins.ws.WsDaoJpa port = service.getWsDaoJpaPort(); // TODO process result here java.util.List<rdvmedecins.ws.Client> result = port.getAllClients();

Serge Tah, http://tahe.developpez.com, fvrier 2009

51/78

6. 7. 8. 9. }

System.out.println("Result = "+result); } catch (Exception ex) { // TODO handle custom exceptions here

la ligne [5] nous montre que la mthode [getAllClients] est une mthode de l'objet de type [WsDaoJpa] dfini ligne 3. Le type [WsDaoJpa] est une interface prsentant les mmes mthodes que celles exposes par le service web distant. ligne [3], l'objet [WsDaoJpa port] est obtenu partir d'un autre objet de type [WsDaoJpaService] dfini ligne 2. Le type [WsDaoJpaService] reprsente le proxy C gnr localement. l'accs au service web distant peut chouer, aussi l'ensemble du code est-il entour d'un try / catch. les objets du proxy C sont dans le package [rdvmedecins.ws]

Une fois ce code compris, on voit que la rfrence locale du service web distant peut tre obtenue par le code :
WsDaoJpa dao=new WsDaoJpaService().getWsDaoJpaPort();

Le code de la classe de test JUnit devient alors 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. package dao; import import import import import import ... rdvmedecins.ws.Client; rdvmedecins.ws.Creneau; rdvmedecins.ws.Medecin; rdvmedecins.ws.Rv; rdvmedecins.ws.WsDaoJpa; rdvmedecins.ws.WsDaoJpaService;

public class MainTestsDaoRemote { // couche [dao] teste private static WsDaoJpa dao; @BeforeClass public static void init(){ dao=new WsDaoJpaService().getWsDaoJpaPort(); } @Test public void test1() { ... } // mthode utilitaire - affiche les lments d'une collection private static void display(String message, List elements) { ... } }

Nous sommes dsormais prts pour les tests : 2

En [1], le test JUnit est excut. En [2], il est russi. Si on regarde les affichages sur la console Netbeans, on trouve des lignes comme les suivantes :
Liste des clients : rdvmedecins.ws.Client@1982fc1

Serge Tah, http://tahe.developpez.com, fvrier 2009

52/78

rdvmedecins.ws.Client@676437 rdvmedecins.ws.Client@1e4853f rdvmedecins.ws.Client@1e808ca

Ct serveur, l'entit [Client] a une mthode toString qui affiche les diffrents champs d'un objet de type [Client]. Lors de la gnration automatique du proxy C, les entits sont cres dans le proxy C mais avec seulement les champs privs accompagns de leurs mthodes get / set. Ainsi la mthode toString n'a pas t gnre dans l'entit [Client] du proxy C. Ce qui explique l'affichage prcdent. Ceci n'enlve rien au test JUnit : il a t russi. On considrera dsormais qu'on a un service web oprationnel.

5
5.1

Clients .NET du service Java EE des rendez-vous


Un client C# 2008

Nous supposons dsormais que le service web prcdent est disponible et actif. Un service web est utilisable par des clients qui peuvent tre crits en diffrents langages. Nous nous proposons ici d'crire un client console C# pour afficher la liste des mdecins.

Utilisateur
3

Client C#

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

.NET
Commenons par crer le projet C# :

HTTP / SOAP

Java EE - serveur Sun

On cre une application console [1] appele [ListeDesMedecins] [2]. En [3], on cre une rfrence sur un service web. Cet outil est similaire celui utilis dans Netbeans : il cre un proxy local du service web distant.

Serge Tah, http://tahe.developpez.com, fvrier 2009

53/78

en [1], on indique l'uri du fichier WSDL du service web (cf paragraphe 4.10.2, page 46). en [2], on demande l'assistant de dcouvrir les services web exposs cette uri en [3], le service web trouv et en [4] les mthodes qu'il expose. en [5], on prcise dans quel espace de noms, les classes du proxy C doivent tre gnres en [6], on gnre le proxy C. 2

3 1 4

en [1], la rfrence web que nous venons de crer. Double-cliquons dessus. en [2], l'explorateur d'objets ouvert par le double-clic. en [3], on slectionne l'espace de noms [ListeDesMedecins.Ws.RdvMedecins] qui est l'espace de noms du proxy C gnr. Le 1er terme [ListeDesMedecins] est l'espace de noms par dfaut de l'application C# cre (nous avons appele celle-ci ListeDesMedecins). Le second terme [Ws.Rdvmedecins] est l'espace de noms que nous avons donn au proxy C dans l'assistant. Au final, les objets du proxy C sont l'espace de noms [ListeDesMedecins.Ws.RdvMedecins]. en [4], les objets du proxy C gnr

7 5

Serge Tah, http://tahe.developpez.com, fvrier 2009

54/78

en [5], [WsDaoJpaClient] est la classe implmentant localement les mthodes du service web distant. en [6], les mthodes de la classe [WsDaoJpaClient]. On y retrouve les mthodes du service web distant commepar exemple la mthode getAllClients en [7].

Examinons le code des entits gnres :

En [1] ci-dessus, on demande voir le code de la classe [client] :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. ... public partial class client : personne { } .... public partial class personne : object, System.ComponentModel.INotifyPropertyChanged { ... public long id { ... } ... }

Ci-dessus, nous n'avons gard que le code ncessaire notre tude.


ligne 2 : la classe [client] drive de la classe [personne] comme dans le service web. ligne 8 : une proprit publique de la classe [personne]

Ce qu'on remarque, c'est que le code gnr ne respecte pas les normes de codage habituelles de C#. Les noms des classes et des proprits publiques devraient commencer par une majuscule. Revenons notre application C# : 2

[Program.cs] [1] est la classe de test. Son code est le suivant :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. using System; using ListeDesMedecins.Ws.RdvMedecins; using Client = ListeDesMedecins.Ws.RdvMedecins.client; namespace ListeDesMedecins { class Program { static void Main(string[] args) { try { // instanciation couche [dao] du client WsDaoJpaClient dao = new WsDaoJpaClient(); // liste des mdecins foreach (Client client in dao.getAllClients()) { Console.WriteLine(String.Format("{0} {1} {2}", client.titre, client.prenom, client.nom)); }

Serge Tah, http://tahe.developpez.com, fvrier 2009

55/78

15. 16. 17. 18. 19. 20. }

} catch (Exception e) { Console.WriteLine(e); } } }

Ligne 12, la classe [Client] du proxy C est utilise alors que nous venons de voir qu'elle s'appelait en ralit [client]. C'est la dclaration de la ligne 3 qui nous permet d'utiliser [Client] au lieu de [client]. Cela nous permet de respecter la norme de codage des noms de classe. Toujours ligne 12, la mthode [getAllClients] est utilise parce c'est ainsi qu'elle s'appelle dans le proxy C. La norme de codage C# voudrait qu'elle s'appelle [GetAllClients]. L'excution du programme [1] prcdent donne les rsultats montrs en [2].

5.2

Un premier client ASP.NET 2008

Nous crivons maintenant un premier client ASP.NET / C# pour afficher la liste des clients.

Navigateur
3

Couche [web]

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

ASP.NET

HTTP / SOAP

Java EE - serveur Sun

Dans cette architecture, il y a deux serveurs web :


celui qui excute le service web distant celui qui excute le client ASP.NET du service web distant

Nous allons crer le projet web du client ASP.NET avec la version Visual Web Express 2008 SP1. Le paragraphe suivant montre comment crer le mme projet si on n'a pas la version SP1 de Visual Web Express 2008.

1 2 4 7 6

On cre une application web [1, 2, 3] appele [ListeDesClients1] [4,5,6]. Le projet ainsi cr est visible en [7].

Serge Tah, http://tahe.developpez.com, fvrier 2009

56/78

en [5], par un clic droit sur le nom du projet, on ajoute une rfrence de service web. en [6], on indique l'uri du fichier WSDL du service web (cf paragraphe 4.10.2, page 46). en [7], on demande l'assistant de dcouvrir les services web exposs cette uri

9 10

11

12

en [8], le service web trouv et en [9] les mthodes qu'il expose sont affiches. en [10], on prcise dans quel espace de noms, les classes du proxy C doivent tre gnres en [11], la rfrence du service web gnre

Un double-clic sur la rfrence [Ws.Rdvmedecins] [12] affiche les classes et interfaces gnres pour le proxy C :

Serge Tah, http://tahe.developpez.com, fvrier 2009

57/78

en [1], l'explorateur d'objets ouvert par le double-clic. en [2], on slectionne l'espace de noms [ListeDesClients1.Ws.RdvMedecins] qui est l'espace de noms du proxy C gnr. Le 1er terme [ListeDesClients1] est l'espace de noms par dfaut de l'application ASP.NET cre (nous avons appele celle-ci ListeDesClients1). Le second terme [Ws.Rdvmedecins] est l'espace de noms que nous avons donn au proxy C dans l'assistant. Au final, les objets du proxy C sont l'espace de noms [ListeDesClients1.Ws.RdvMedecins]. en [3], les objets du proxy C gnr

6 4

en [4], [WsDaoJpaService] est la classe implmentant localement les mthodes du service web distant. en [5], les mthodes de la classe [WsDaoJpaService]. On y retrouve les mthodes du service web distant comme par exemple la mthode getAllClients en [6].

Examinons le code des entits gnres :

Serge Tah, http://tahe.developpez.com, fvrier 2009

58/78

En [1] ci-dessus, on demande voir le code de la classe [client] :


1. public partial class client : personne { 2. } 3. 4. ... 5. public partial class personne { 6. ... 7. public long id { 8. ... 9. }

Ci-dessus, nous n'avons gard que le code ncessaire notre tude.


ligne 1 : la classe [client] drive de la classe [personne] comme dans le service web. ligne 5 : la classe [personne] ligne 7 : une proprit publique de la classe [personne]

On retrouve un code analogue celui dj comment pour le client C# la page 55.


l'espace de noms du proxy C gnr est celui dfini dans son assistant de cration prfix par l'espace de noms du projet web lui-mme : ListeDesClients1.Ws.RdvMedecins la classe proxy qui implmente localement les mthodes du service web distant s'appelle WsDaoJpaService. les noms des classes et des proprits commencent par une minuscule

Nous pouvons crire une page [Default.aspx] affichant la liste des clients. La page est la suivante :

1 2 3

N
1

Type
MultiView Vues

Nom
Conteneur de vues

Rle

Serge Tah, http://tahe.developpez.com, fvrier 2009

59/78

N
2 3 4 5

Type
View Repeater View Label

Nom
VueClients RepeaterClients VueErreurs LabelErreur

Rle
la vue qui contient la liste des clients affiche la liste des clients sous la forme d'une liste puces la vue qui affiche une ventuelle erreur le texte de l'erreur

Le code de prsentation de cette page est comme suit :


1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ListeDesClients1._Default" %> 2. 3. <%@ Import Namespace="ListeDesClients1.Ws.RdvMedecins" %> 4. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 5. <html xmlns="http://www.w3.org/1999/xhtml"> 6. <head id="Head1" runat="server"> 7. <title>Liste des clients</title> 8. </head> 9. <body> 10. <form id="form1" runat="server"> 11. <div> 12. <asp:MultiView ID="Vues" runat="server"> 13. <asp:View ID="VueClients" runat="server"> 14. Liste des clients :<br /> 15. <br /> 16. <ul> 17. <asp:Repeater ID="RepeaterClients" runat="server"> 18. <ItemTemplate> 19. <li> 20. <%#(Container.DataItem as client).nom%>, 21. <%#(Container.DataItem as client).prenom%> 22. </li> 23. </ItemTemplate> 24. </asp:Repeater> 25. </ul> 26. </asp:View> 27. <asp:View ID="VuesErreurs" runat="server"> 28. L'erreur suivante s'est produite :<br /> 29. <br /> 30. <asp:Label ID="LabelErreur" runat="server"></asp:Label></asp:View> 31. </asp:MultiView><br /> 32. </div> 33. </form> 34. </body> 35. </html>

On notera, ligne 3, la ncessit d'importer l'espace de noms [ListeDesClients1.Ws.RdvMedecins] du proxy C du service web afin que la classe [client] des lignes 20 et 21 soit accessible. Le code de contrle [Default.aspx.cs] associ la page de prsentation [Default.aspx] est 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. using System; using ListeDesClients1.Ws.RdvMedecins; namespace ListeDesClients1 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { try { // instanciation proxy local de la couche [dao] distante WsDaoJpaService dao = new WsDaoJpaService(); // vue client Vues.ActiveViewIndex = 0; // initialisation vue client RepeaterClients.DataSource = dao.getAllClients(); RepeaterClients.DataBind(); } catch (Exception ex) { // vue erreurs Vues.ActiveViewIndex = 1;

Serge Tah, http://tahe.developpez.com, fvrier 2009

60/78

24. //initialisation vue erreurs 25. LabelErreur.Text = ex.ToString(); 26. } 27. } 28. } 29. }

ligne 8 : la mthode Page_Load est excute au chargement initial de la page. Ce chargement a lieu chaque requte d'un client demandant la page. ligne 13 : on instancie le proxy local C. Ce proxy C implmente l'interface du service web distant. L'application va dialoguer avec ce proxy C. ligne 15 : si pas d'exception lors de l'instanciation du proxy local C, c'est la vue nomme "VueClients" qui doit tre affiche, c.a.d. la vue n 0 dans le MultiView "Vues" de la page. ligne 17 : la source de donnes du rpteur est la liste des clients fournie par la mthode getAllClients du proxy C. ligne 23 : en cas d'exception lors de l'instanciation du proxy local C, c'est la vue nomme "VueErreurs" qui doit tre affiche, c.a.d. la vue n 1 dans le MultiView "Vues" de la page. ligne 25 : l'exception est affiche dans le label "LabelErreur".

L'excution du projet web donne le rsultat suivant :

5.3

Un second client ASP.NET 2008

Nous crivons maintenant un second client ASP.NET / C# avec cette fois-ci la version Visual Web Developer Express 2008 qui a prcd la version SP1.

Navigateur
3

Couche [web]

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

ASP.NET

HTTP / SOAP

Java EE - serveur Sun

Crons le projet web du client .NET :

Serge Tah, http://tahe.developpez.com, fvrier 2009

61/78

On cre un site web [1, 2] appele [ListeDesClients] [3].

4 5 6

en [4], le projet web en [5], par un clic droit sur le nom du projet, on ajoute une rfrence de service comme il a t fait prcdemment. en [6], le projet une fois la rfrence au service web distant ajoute

Contrairement au projet web SP1 tudi prcdemment, on n'a pas accs aux objets du client du service web via l'explorateur d'objets. Ce qu'il faut savoir : l'espace de noms du proxy C gnr est celui dfini dans son assistant de cration : Ws.RdvMedecins la classe proxy qui implmente localement les mthodes du service web distant s'appelle WsDaoJpaClient. les noms des classes et des proprits commencent par une minuscule Nous pouvons crire une page [Default.aspx] affichant la liste des clients. La page est la suivante :

Serge Tah, http://tahe.developpez.com, fvrier 2009

62/78

1 2 3

N
1 2 3 4 5

Type
MultiView Vues View Repeater View Label

Nom
Conteneur de vues VueClients RepeaterClients VueErreurs LabelErreur

Rle
la vue qui contient la liste des clients affiche la liste des clients sous la forme d'une liste puces la vue qui affiche une ventuelle erreur le texte de l'erreur

Le code de prsentation de cette page est 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. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Import Namespace="Ws.RdvMedecins" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Liste des clients</title> </head> <body> <form id="form1" runat="server"> <div> <asp:MultiView ID="Vues" runat="server"> <asp:View ID="VueClients" runat="server"> Liste des clients :<br /> <br /> <ul> <asp:Repeater ID="RepeaterClients" runat="server"> <ItemTemplate> <li> <%#(Container.DataItem as client).nom%>, <%#(Container.DataItem as client).prenom%> </li> </ItemTemplate> </asp:Repeater> </ul> </asp:View> <asp:View ID="VuesErreurs" runat="server"> L'erreur suivante s'est produite :<br /> <br /> <asp:Label ID="LabelErreur" runat="server"></asp:Label></asp:View> </asp:MultiView><br /> </div> </form> </body> </html>

On notera, ligne 2, la ncessit d'importer l'espace de noms [Ws.RdvMedecins] du proxy C du service web afin que la classe [client] de la ligne 19 soit accessible.

Serge Tah, http://tahe.developpez.com, fvrier 2009

63/78

Le code de contrle [Default.aspx.cs] associ la page de prsentation [Default.aspx] est 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. using System; using Ws.RdvMedecins; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { try { // instanciation proxy local de la couche [dao] distante WsDaoJpaClient dao = new WsDaoJpaClient(); // vue client Vues.ActiveViewIndex = 0; // initialisation vue client RepeaterClients.DataSource = dao.getAllClients(); RepeaterClients.DataBind(); } catch (Exception ex) { // vue erreurs Vues.ActiveViewIndex = 1; //initialisation vue erreurs LabelErreur.Text = ex.ToString(); } } }

Ce code est analogue celui de la version prcdente. L'excution du projet web donne le rsultat suivant :

Clients Flex du service Java EE des rendez-vous

Nous prsentons maintenant deux clients Flex du service web JEE des rendez-vous. L'IDE utilis est Flex Builder 3. Une version de dmonstration de ce produit est tlchargeable l'Url [https://www.adobe.com/cfusion/tdrc/index.cfm? loc=fr_fr&product=flex]. Flex Builder 3 est un IDE Eclipse. Par ailleurs, pour excuter le client Flex, nous utilisons un serveur web Apache de l'outil Wamp [http://www.wampserver.com/]. N'importe quel serveur Apache fait l'affaire. Le navigateur qui affiche le client Flex doit disposer du plugin Flash Player version 9 minimale. Les applications Flex ont la particularit de s'excuter au sein du plugin Flash Player du navigateur. En cela elles se rapprochent des applications Ajax qui embarquent dans les pages envoyes au navigateur des scripts JavaScript qui sont ensuite excuts au sein du navigateur. Une application Flex n'est pas une application web au sens o on l'entend habituellement : c'est une application cliente de services dlivrs par des serveurs web. En cela, elle est analogue une application de bureau qui serait cliente de ces mmes services. Elle diffre cependant en un point : elle est tlcharge initialement depuis un serveur web au sein d'un navigateur disposant du plugin Flash Player capable de l'excuter. Comme une application de bureau, une application Flex est compose principalement de deux lments :

une partie prsentation : les vues affiches dans le navigateur. Ces vues ont la richesse des fentres des applications de bureau. Une vue est dcrite l'aide d'un langage balises appel MXML.

Serge Tah, http://tahe.developpez.com, fvrier 2009

64/78

une partie code qui gre principalement les vnements provoqus par les actions de l'utilisateur sur la vue. Ce code peut tre crit galement en MXML ou avec un langage orient objet appel ActionScript. Il faut distinguer deux types d'vnements : l'vnement qui ncessite d'avoir un change avec le serveur web : remplissage d'une liste par des donnes fournies par une application web, envoi des donnes d'un formulaire au serveur, ... Flex fournit un certain nombre de mthodes pour communiquer avec le serveur de faon transparente pour le dveloppeur. Ces mthodes sont par dfaut asynchrones : l'utilisateur peut continuer interagir avec la vue pendant la requte au serveur. l'vnement qui modifie la vue affiche sans change de donnes avec le serveur, par exemple tirer un lment d'un arbre pour le dposer dans une liste. Ce type d'vnement est entirement trait localement au sein du navigateur.

Une application Flex est souvent excute de la faon suivante :

Navigateur + Flash Player


1

Serveur web

Html + SWF
2

Html + SWF

en [1], une page Html est demande en [2], elle est envoye. Elle embarque avec elle un fichier binaire SWF (ShockWave Flash)contenant l'intgralit de l'application Flex : toutes les vues et le code de gestion des vnements de celles-ci. Ce fichier sera excut par le plugin FlashPlayer du navigateur.

Navigateur + Flash Player


3

Serveur web application


4

Html + SWF

l'excution du client Flex se fait en local sur le navigateur sauf lorsqu'il a besoin de donnes externes. Dans ce cas, il les demande au serveur [3]. Il les reoit en [4] selon des formats divers : XML ou binaire. L'application interroge sur le serveur web peut tre crite dans un langage quelconque. Seul compte le format de la rponse.

Nous avons dcrit l'architecture d'excution d'une application Flex afin que le lecteur peroive bien la diffrence entre celle-ci et celle d'une application Web classique, sans Ajax, telle que l'application Asp.Net dcrite prcdemment. Dans cette dernire, le navigateur est passif : il affiche simplement des pages Html construites sur le serveur web qui les lui envoie. Dans la suite, nous donnons deux exemples de clients Flex dans le seul but de montrer la diversit des clients d'un service web. L'auteur tant lui-mme un dbutant Flex, certains points ne seront peut-tre pas dtaills comme ils le devraient.

6.1

Un premier client Flex

Nous crivons maintenant un premier client Flex pour afficher la liste des clients. L'architecture client / serveur mise en place sera la suivante :

Serge Tah, http://tahe.developpez.com, fvrier 2009

65/78

Navigateur

Couche [Flex]

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Apache

HTTP / SOAP

Java EE - serveur Glassfish

Dans cette architecture, il y a deux serveurs web :


le serveur Glassfish qui excute le service web distant le serveur Apache qui excute le client Flex du service web distant

Nous construisons le client Flex avec l'IDE Flex Builder 3 :

dans Flex Builder 3, on cre un nouveau projet en [1] on lui donne un nom en [2] et on prcise en [3] dans quel dossier le gnrer 7 4 5 9 8

en [4], on donne un nom l'application principale (celle qui va tre excute) en [5], le projet une fois gnr en [6], le fichier MXML principal de l'application un fichier MXML comporte une vue et le code de gestion des vnements de cette vue. L'onglet [Source] [7] donne accs au fichier MXML. On y trouvera des balises <mx> dcrivant la vue ainsi que du code ActionScript. la vue peut tre construite graphiquement en utilisant l'onglet [Design] [8]. Les balises MXML dcrivant la vue sont alors gnres automatiquement dans l'onglet [Source]. L'inverse est vrai : les balises MXML ajoutes directement dans l'onglet [Source] sont refltes graphiquement dans l'onglet [Design].

Comme il a t fait avec les clients C# et Asp.Net prcdents, nous allons gnrer le proxy local C [B] du service web distant S [A] :

Serge Tah, http://tahe.developpez.com, fvrier 2009

66/78

Navigateur

Couche [Flex]

C
B

S
A

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Apache

HTTP / SOAP

Java EE - serveur Glassfish

Pour que le proxy C puisse tre gnr, il faut que le service web JEE soit actif.

1 2

en [1], choisir l'option Data / Import Web Service en [2], slectionner le dossier de gnration des classes et interfaces du proxy C.

4 5

6 10 7 8

en [3], mettre l'Uri du fichier WSDL du service web distant S (cf paragraphe 4.10.2, page 46) puis passer l'tape suivante en [4] et [5], le service web dcrit par le fichier WSDL indiqu en [3] en [6] : la liste des mthodes qui vont tre gnres pour le proxy C. On notera que ce ne sont pas les mthodes relles du service S. Elles n'ont pas la bonne signature. Ici, chaque mthode prsente a un unique paramtre quelque soit le nombre de paramtres de la mthode relle du service web. Cet unique paramtre est une instance de classe encapsulant dans ses champs les paramtres attendus par la mthode distante. en [7] : le package dans lequel les classes et interfaces du proxy C vont tre gnres en [8] : le nom de la classe locale qui fera office de proxy vers le service web distant en [9] : terminer l'assistant. en [10] : la liste des classes et interfaces du proxy C gnr.

Serge Tah, http://tahe.developpez.com, fvrier 2009

67/78

11

en [11] : la classe [WsDaoJpaService] implmentant les mthodes du proxy C.

La classe [WsDaoJpaService] gnre implmente l'interface [IWsDaoJpaService] suivante :


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. /** * Service.as * This file was auto-generated from WSDL by the Apache Axis2 generator modified by Adobe * Any change made to this file will be overwritten when the code is re-generated. */ package generated.webservices{ import mx.rpc.AsyncToken; import flash.utils.ByteArray; import mx.rpc.soap.types.*; public interface IWsDaoJpaService { //Stub functions for the getAllClients operation /** * Call the operation on the server passing in the arguments defined in the WSDL file * @param getAllClients * @return An AsyncToken */ function getAllClients(getAllClients:GetAllClients):AsyncToken; .... ... ... ... ... ... ... } } function getAllClients_send():AsyncToken; function get getAllClients_lastResult():GetAllClientsResponse; function set getAllClients_lastResult(lastResult:GetAllClientsResponse):void; function addgetAllClientsEventListener(listener:Function):void; function get getAllClients_request_var():GetAllClients_request; function set getAllClients_request_var(request:GetAllClients_request):void;

ligne 11 : l'interface [IWsDaoJpaService] implmente par la classe [WsDaoJpaService] lignes 19-31 : les diffrentes mthodes gnres pour la mthode getAllClients() du service web distant. La seule qui se rapproche de celle rellement expose par le service web est celle de la ligne 19. Elle porte le bon nom mais n'a pas la bonne signature : la mthode getAllClients() du service web distant n'a pas de paramtres.

Le paramtre unique de la mthode getAllClients du proxy C gnr est de type GetAllClients suivant :
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. /** * GetAllClients.as * This file was auto-generated from WSDL by the Apache Axis2 generator modified by Adobe * Any change made to this file will be overwritten when the code is re-generated. */ package generated.webservices { import mx.utils.ObjectProxy; import flash.utils.ByteArray; import mx.rpc.soap.types.*; /** * Wrapper class for a operation required type */ public class GetAllClients { /** * Constructor, initializes the type class */

Serge Tah, http://tahe.developpez.com, fvrier 2009

68/78

21. 22. 23. } 24. }

public function GetAllClients() {}

C'est une classe vide. Cela pourrait correspondre au fait que la mthode cible getAllClients n'admet pas de paramtre. Maintenant examinons les classes gnres pour les entits Medecin, Client, Rv et Creneau. Examinons par exemple la classe Client :
1. package generated.webservices 2. { 3. import mx.utils.ObjectProxy; 4. import flash.utils.ByteArray; 5. import mx.rpc.soap.types.*; 6. 7. public class Client extends generated.webservices.Personne 8. { 9. public function Client() {} 10. 11. } 12. }

La classe Client est vide galement. Elle drive (ligne 7) de la classe Personne suivante :
1. package generated.webservices 2. { 3. import mx.utils.ObjectProxy; 4. import flash.utils.ByteArray; 5. import mx.rpc.soap.types.*; 6. 7. public class Personne 8. { 9. public function Personne() {} 10. 11. public var id:Number; 12. public var nom:String; 13. public var prenom:String; 14. public var titre:String; 15. public var version:Number; 16. } 17. }

lignes 11-15 : on retrouve les attributs de la classe Personne dfinie au sein du service web JEE.

Nous avons les principaux lments du proxy C. Nous pouvons dsormais l'utiliser. Le fichier principal [rdvmedecins01.xml] du client est le suivant :
1. <?xml version="1.0" encoding="utf-8"?> 2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();"> 3. <mx:Script> 4. <![CDATA[ 5. import generated.webservices.Client; 6. ... 7. 8. // donnes 9. private var ws:WsDaoJpaService; 10. [Bindable] 11. private var clients:ArrayCollection; 12. 13. private function init():void{ 14. ... 15. } 16. 17. private function loadClients():void{ 18. ... 19. } 20. 21. ... 22. private function displayClient(client:Client):String{ 23. ... 24. } 25. ]]> 26. </mx:Script>

Serge Tah, http://tahe.developpez.com, fvrier 2009

69/78

27. <mx:Label text="Liste des clients" fontSize="14"/> 28. <mx:List dataProvider="{clients}" labelFunction="displayClient"></mx:List> 29. <mx:Button label="Afficher les clients" click="loadClients()"/> 30. <mx:Text id="txtMsgErreur" width="454" height="75"/> 31. 32. </mx:Application>

Dans ce code, il faut distinguer diverses choses :


la dfinition de l'application (ligne 2) la description de la vue de celle-ci (lignes 27-30) les gestionnaires d'vnements en langage ActionScript au sein de la balise <mx:Script> (lignes 3-26).

Commentons pour commencer, la dfinition de l'application elle-mme et la description de sa vue :

ligne 2 : dfinit le mode de disposition des composants dans le conteneur de la vue. L'attribut layout="vertical" indique que les composants seront les uns sous les autres. la mthode excuter lorsque la vue aura t instancie, c.a.d. le moment o tous ses composants auront t instancis. L'attribut creationComplete="init();" indique que c'est la mthode init de la ligne 13 qui doit tre excute. creationComplete est l'un des vnements que peut mettre la classe Application. les ligne 27-30 dfinissent les composants de la vue ligne 27 : dfinit un texte ligne 28 : une liste dans laquelle on mettra la liste des clients. La balise dataProvider="{clients}" indique la source des donnes qui doivent remplir la liste. Ici, la liste sera remplie avec l'objet clients dfini ligne 11. Pour pouvoir crire dataProvider="{clients}", il faut que le champ clients ait l'attribut [Bindable] (ligne 10). Cet attribut permet une variable ActionScript d'tre rfrence l'extrieur de la balise <mx:Script>. Le champ clients est de type ArrayCollection, un type ActionScript qui permet de stocker des listes d'objets, ici une liste d'objet de type Client. ligne 29 : un bouton. Son vnement click est gr. L'attribut click="loadClients()" indique que la mthode loadClients de la ligne 17 doit tre excute lors d'un clic sur le bouton. Ce sera ce bouton qui dclenchera la demande au service web de la liste des clients. ligne 30 : une zone de texte destine afficher un ventuel message d'erreur qui serait renvoy par le serveur sur la demande prcdente.

Les lignes 27-30 gnrent la vue suivante dans l'onglet [Design] :

1 5 2

3 4

[1] : a t gnr par le composant Label de la ligne 27 [2] : a t gnr par le composant List de la ligne 28 [3] : a t gnr par le composant Button de la ligne 29 [4] : a t gnr par le composant Text de la ligne 30 [5] : un exemple d'excution

Examinons maintenant le code ActionScript de la page. Ce code gre les vnements de la vue.

Serge Tah, http://tahe.developpez.com, fvrier 2009

70/78

1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();"> 2. 3. <mx:Script> 4. <![CDATA[ 5. import generated.webservices.Client; 6. ... 7. 8. // donnes 9. private var ws:WsDaoJpaService; 10. [Bindable] 11. private var clients:ArrayCollection; 12. 13. private function init():void{ 14. // on instancie le proxy du service web 15. ws=new WsDaoJpaService(); 16. // on configure les gestionnaires d'vts 17. ws.addgetAllClientsEventListener(loadClientsCompleted); 18. ws.addEventListener(FaultEvent.FAULT,loadClientsFault); 19. } 20. 21. private function loadClients():void{ 22. // on demande la liste des clients 23. ws.getAllClients(new GetAllClients()); 24. } 25. 26. private function loadClientsCompleted(event:GetAllClientsResultEvent):void{ 27. // on rcupre les clients dans le rsultat envoy 28. clients=event.result as ArrayCollection; 29. } 30. 31. private function loadClientsFault(event:FaultEvent):void{ 32. // on affiche le msg d'erreur 33. txtMsgErreur.text=event.fault.message; 34. } 35. 36. private function displayClient(client:Client):String{ 37. // on affiche un client 38. return client.nom + " " + client.prenom; 39. } 40. ]]> 41. </mx:Script> 42. <mx:Label text="Liste des clients" fontSize="14"/> 43. <mx:List dataProvider="{clients}" labelFunction="displayClient"></mx:List> 44. <mx:Button label="Afficher les clients" click="loadClients()"/> 45. <mx:Text id="txtMsgErreur" width="454" height="75"/> 46.

ligne 9 : ws va dsigner le proxy C de type WsDaoJpaService, la classe gnre prcdemment qui implmente les mthodes d'accs au service web distant. ligne 13 : la mthode init excute lorsque la vue a t instancie (ligne 1) ligne 15 : une instance du proxy C est cre ligne 17 : un gestionnaire d'vnement est associ l'vnement "la mthode asynchrone GetAllClients s'est termine avec succs". Pour toute mthode m du service web distant, le proxy C implmente une mthode addmEventListener qui permet d'associer un gestionnaire l'vnement "la mthode asynchrone m s'est termine avec succs". Ici, la ligne 17 indique que la mthode loadClientsCompleted de la ligne 26 doit tre excute lorsque le client Flex aura reu la liste des clients. ligne 18 : un gestionnaire d'vnement est associ l'vnement "une mthode asynchrone du proxy C s'est termine sur un chec". Ici, la ligne 18 indique que la mthode loadClientsFault de la ligne 31 doit tre excute chaque fois qu'une requte asynchrone du proxy C vers le service web S choue. Ici la seule requte qui sera faite est celle qui demande la liste des clients. au final, la mthode init de la ligne 13 a instanci le proxy C et dfini des gestionnaires d'vnements pour la requte asynchrone qui sera faite ultrieurement. ligne 21 : la mthode excute lors d'un clic sur le bouton [Afficher les clients] (ligne 44) ligne 23 : la mthode asynchrone getAllClients du proxy C est excute. On lui passe une instance GetAllClients charge d'encapsuler les paramtres de la mthode distante appele. Ici, il n'y a aucun paramtre. On cre une instance vide. La mthode getAllClients est asynchrone. L'excution se poursuit sans attendre les donnes renvoyes par le serveur. L'utilisateur peut notamment continuer interagir avec la vue. Les vnements qu'il provoque continueront tre grs. Grce la mthode init, nous savons que : la mthode loadClientsCompleted (ligne 26) sera excute lorsque le client Flex aura reu la liste des clients la mthode loadClientsFault (ligne 31) sera excute si la requte se termine sur une erreur.

Serge Tah, http://tahe.developpez.com, fvrier 2009

71/78

ligne 26 : la mthode loadClientsCompleted excute en cas de succs de la requte reoit en paramtre un type GetAllClientsResultEvent. Pour chaque mthode m du service web distant, le proxy C dfinit une classe MResultEvent qui contient le rsultat de la requte dans son champ result. ligne 28 : la liste des clients est rcupre dans l'vnement. On sait que la mthode getAllClients du service web distant renvoie une liste. On met celle-ci dans le champ clients de la ligne 11. Un transtypage est ncessaire. La liste de la ligne 43 tant lie (Bindable) au champ clients, elle est avertie que ses donnes ont chang. Elle affiche alors la liste des clients. Elle va afficher chaque lment de la liste clients avec la mthode displayClient (ligne 43). ligne 36 : la mthode displayClient reoit un type Client. Elle doit retourner la chane de caractres que doit afficher la liste pour ce client. Ici le nom et le prnom (ligne 38). ligne 31 : la mthode excute lorsqu'une requte au service web choue. Elle reoit un paramtre de type FaultEvent. Cette classe a un champ fault qui encapsule l'erreur renvoye par le serveur. fault.message est le message accompagnant l'erreur. ligne 33 : le message d'erreur est affich dans la zone de texte prvue cet effet.

Lorsque l'application a t construite, son code excutable se trouve dans le dossier [bin-debug] du projet Flex :

Ci-dessus, le fichier [rdvmedecins01.html] reprsente le fichier Html qui sera demand par le navigateur au serveur web pour avoir le client Flex le fichier [rdvmedecins01.swf] est le binaire du client Flex qui sera encapsul dans la page Html envoye au navigateur puis excut par le plugin Flash Player de celui-ci. Nous sommes prts excuter le client Flex. Il nous faut auparavant mettre en place l'environnement d'excution qui lui est ncessaire. Revenons l'architecture client / serveur teste :

Navigateur
3

Couche [Flex]

C
2

S
1

Couche [dao]

Couche [JPA / Hibernate]

Couche [JDBC]

SGBD

BD

Apache

HTTP / SOAP

Java EE - serveur Glassfish

Ct serveur : lancer le SGBD MySQL lancer le serveur Glassfish dployer le service web JEE des rendez-vous s'il n'est pas dploy ventuellement tester l'un des clients prcdents pour vrifier que tout est en place ct serveur. Ct client : Lancer le serveur Apache qui sera demande l'application Flex. Ici nous utilisons l'outil Wamp. Avec cet outil, nous pouvons associer un alias au dossier [bin-debug] du projet Flex.

Serge Tah, http://tahe.developpez.com, fvrier 2009

72/78

2 1 3

l'icne de Wamp est en bas de l'cran [1] par un clic gauche sur l'icne Wamp, slectionner l'option Apache [2] / Alias Directories [3, 4] slectionner l'option [5] : Add un alias

7 6

en [6] donner un alias (un nom quelconque) l'application web qui va tre excute en [7] indiquer la racine de l'application web qui portera cet alias : c'est le dossier [bin-debug] du projet Flex que nous venons de construire.

Rappelons la structure du dossier [bin-debug] du projet Flex :

Alias rdvmedecins

Le fichier [rdvmedecins01.html] est le fichier Html de l'application Flex. Grce l'alias que nous venons de crer sur le dossier [bindebug], ce fichier sera obtenu par l'url [http://localhost/rdvmedecins/rdvmedecins01.html]. Nous demandons celle-ci dans un navigateur disposant du plugin Flash Player version 9 ou suprieure :

Serge Tah, http://tahe.developpez.com, fvrier 2009

73/78

3 2 4

en [1], l'Url de l'application Flex en [2], nous demandons la liste des clients en [3], le rsultat obtenu lorsque tout va bien en [4], le rsultat obtenu lorsque nous demandons l'affichage des clients alors que le service web a t arrt.

Vous pourriez avoir la curiosit d'afficher le code source de la page Html reue :
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. <!-- saved from url=(0014)about:internet --> <html lang="en"> <!-Smart developers always View Source. This application was built using Adobe Flex, an open source framework for building rich Internet applications that get delivered via the Flash Player or to desktops via Adobe AIR. Learn more about Flex at http://flex.org // --> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!-- BEGIN Browser History required section --> <link rel="stylesheet" type="text/css" href="history/history.css" /> <!-- END Browser History required section --> <title></title> <script src="AC_OETags.js" language="javascript"></script> ... <script language="JavaScript" type="text/javascript"> <!-// ----------------------------------------------------------------------------// Globals // Major version of Flash required var requiredMajorVersion = 9; // Minor version of Flash required var requiredMinorVersion = 0; // Minor version of Flash required var requiredRevision = 124; // ----------------------------------------------------------------------------// --> </script> </head>

<body scroll="no"> <script language="JavaScript" type="text/javascript"> <!-// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65) 43. .... 44. // --> 45. </script>

Serge Tah, http://tahe.developpez.com, fvrier 2009

74/78

46. <noscript> 47. <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 48. id="rdvmedecins01" width="100%" height="100%" 49. codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash .cab"> 50. <param name="movie" value="rdvmedecins01.swf" /> 51. <param name="quality" value="high" /> 52. <param name="bgcolor" value="#869ca7" /> 53. <param name="allowScriptAccess" value="sameDomain" /> 54. <embed src="rdvmedecins01.swf" quality="high" bgcolor="#869ca7" 55. width="100%" height="100%" name="rdvmedecins01" align="middle" 56. play="true" 57. loop="false" 58. quality="high" 59. allowScriptAccess="sameDomain" 60. type="application/x-shockwave-flash" 61. pluginspage="http://www.adobe.com/go/getflashplayer"> 62. </embed> 63. </object> 64. </noscript> 65. </body> 66. </html>

Le corps de la page commence ligne 39. Il ne contient pas du Html classique mais un objet (ligne 47) de type "application/xshockwave-flash" (ligne 60). C'est le fichier [rdvmedecins01.swf] (ligne 54) que l'on peut voir dans le dossier [bin-debug] du projet Flex. C'est un fichier de taille importante : 600 K environ pour ce simple exemple.

6.2

Un deuxime client Flex

Le deuxime client Flex ne va pas utiliser le proxy C gnr pour le premier. On veut montrer que cette tape n'est pas indispensable mme si elle prsente des avantages vis vis de celle qui va tre prsente ici. Le projet volue de la faon suivante :

3 2

en [1] la nouvelle application Flex en [2] les excutables qui lui sont associs en [3] la nouvelle vue : on va afficher la liste des mdecins.

Le code MXML de l'application [rdvmedecins02.mxml] est le suivant :


1. <?xml version="1.0" encoding="utf-8"?> 2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> 3. <mx:Script> 4. <![CDATA[ 5. import mx.rpc.events.ResultEvent; 6. import mx.rpc.events.FaultEvent;

Serge Tah, http://tahe.developpez.com, fvrier 2009

75/78

7. import mx.collections.ArrayCollection; 8. 9. // donnes 10. [Bindable] 11. private var medecins:ArrayCollection; 12. 13. private function loadMedecins():void{ 14. // on demande la liste des mdecins 15. wsrdvmedecins.getAllMedecins.send(); 16. } 17. 18. private function loadMedecinsCompleted(event:ResultEvent):void{ 19. // on rcupre les mdecins 20. medecins=event.result as ArrayCollection; 21. } 22. 23. private function loadMedecinsFault(event:FaultEvent):void{ 24. // on affiche le msg d'erreur 25. txtMsgErreur.text=event.fault.message; 26. } 27. 28. // affichage d'un mdecin 29. private function displayMedecin(medecin:Object):String{ 30. return medecin.nom + " " + medecin.prenom; 31. } 32. 33. ]]> 34. </mx:Script> 35. <mx:WebService id="wsrdvmedecins" 36. wsdl="http://localhost:8080/serveur-webservice-ejb-dao-jpa-hibernate/WsDaoJpaService?wsdl"> 37. <mx:operation name="getAllMedecins" 38. result="loadMedecinsCompleted(event)" fault="loadMedecinsFault(event);"> 39. <mx:request/> 40. </mx:operation> 41. </mx:WebService> 42. <mx:Label text="Liste des mdecins" fontSize="14"/> 43. <mx:List dataProvider="{medecins}" labelFunction="displayMedecin"></mx:List> 44. <mx:Button label="Afficher les mdecins" click="loadMedecins()"/> 45. <mx:Text id="txtMsgErreur" width="300" height="113"/> 46. 47. </mx:Application>

Nous ne commenterons que les nouveauts :


lignes 42 - 45 : la nouvelle vue. Elle est identique la prcdente si ce n'est qu'elle a t adapte pour afficher les mdecins plutt que les clients. lignes 35-41 : le service web est ici dcrit par une balise <mx:WebService> (ligne 35). Le proxy C utilis dans la version prcdente n'est plus utilis ici. ligne 35 : l'attribut id donne un nom au service web. ligne 36 : l'attribut wsdl donne l'uri du fichier WSDL du service web. C'est la mme Uri qu'utilise par le prcdent client et dfinie page 46. lignes 37-40 : dfinissent une mthode du service web distant au moyen de la balise <mx:operation> ligne 37 : la mthode rfrence est dfinie par l'attribut name. Ici nous rfrenons la mthode distante getAllMedecins. ligne 38 : on dfinit les mthodes excuter en cas de succs de l'opration (attribut result) et en cas d'chec (attribut fault). ligne 39 : la balise <mx:request> sert dfinir les paramtres de l'opration. Ici la mthode distante getAllMedecins n'a pas de paramtre, donc nous ne mettons rien. Pour une mthode admettant des paramtres param1 et param2, on crirait :
<mx:Request> <param1>{param1}</param1> <param1>{param1}</param1> </mx:Request>

o param1 et param2 seraient des variables dclares et initialises dans la balise <mx:Script>
[Bindable] private var param1:Type1; [Bindable] private var param2:Type2;

Dans la balise <mx:Script> on trouve du code ActionScript analogue celui tudi dans le client prcdent. Seule diffre la mthode loadMedecins des lignes 13-16. C'est le mode d'appel de la mthode distante [getAllMedecins] qui diffre :

Serge Tah, http://tahe.developpez.com, fvrier 2009

76/78

ligne 15 : on utilise le service web [wsrdvmedecins] dfini ligne 35 et son opration [getAllMedecins] dfinie ligne 37. Pour excuter cette opration, la mthode send est utilise. C'est elle qui dmarre l'appel asynchrone de la mthode getAllMedecins du service web dfini ligne 35. La mthode send fera l'appel avec les paramtres dfinis par la balise <mx:request> de la ligne 39. Ici il n'y a aucun paramtre. Si la mthode avait eu les paramtres param1 et param2, le script loadMedecins aurait affect des valeurs ces paramtres avant d'appeler la mthode send.

Il ne nous reste plus qu' tester cette nouvelle application :

Conclusion

Nous avons montr les dmarches qui mnent la cration et au dploiement d'un service web JEE avec l'IDE Netbeans 6.5 et le serveur d'application Glassfish qui l'accompagne. Par ailleurs, nous avons prsent divers clients pour ce service web : clients Java, C#, Asp.Net, Flex. C'est cette diversit des clients qui fait l'intrt des services web. Le lecteur intress approfondir le sujet des services web JEE pourra lire les divers tutoriels disponibles sur le site de Netbeans : [http://www.netbeans.org/kb/trails/java-ee.html]. Au paragraphe 3, page 3, nous avons prsent des copies d'cran d'un client web Asp.net. Celui-ci fait l'objet d'un exercice d'universit qu'on trouvera l'Url [http://tahe.ftp-developpez.com/fichiers-archive/dotnet/exercices/rdvmedecins.zip].

Serge Tah, http://tahe.developpez.com, fvrier 2009

77/78

Table des matires


1OBJECTIFS ET OUTILS...........................................................................................................................................................2 2LA NATURE DU SERVICE WEB RALIS..........................................................................................................................2 3FONCTIONNEMENT DE L'APPLICATION.........................................................................................................................3 4LE SERVICE WEB JAVA EE DES RENDEZ-VOUS............................................................................................................6 4.1LA BASE DE DONNES......................................................................................................................................................................6 4.1.1LA TABLE [MEDECINS]............................................................................................................................................................6 4.1.2LA TABLE [CLIENTS]................................................................................................................................................................7 4.1.3LA TABLE [CRENEAUX]..........................................................................................................................................................7 4.1.4LA TABLE [RV]...........................................................................................................................................................................8 4.2GNRATION DE LA BASE DE DONNES..............................................................................................................................................8 4.3LES LMENTS DE L'ARCHITECTURE CT SERVEUR...........................................................................................................................9 4.4CONFIGURATION HIBERNATE DU SERVEUR GLASSFISH.....................................................................................................................10 4.5LES OUTILS DE GNRATION AUTOMATIQUE DE NETBEANS................................................................................................................11 4.6LE PROJET NETBEANS DU MODULE EJB..........................................................................................................................................20 4.6.1CONFIGURATION DE LA COUCHE JPA............................................................................................................................................21 4.6.2LES ENTITS DE LA COUCHE JPA..................................................................................................................................................22 4.6.3LA CLASSE D'EXCEPTION..............................................................................................................................................................26 4.6.4L'EJB DE LA COUCHE [DAO].........................................................................................................................................................27 4.7DPLOIEMENT DE L'EJB DE LA COUCHE [DAO] AVEC NETBEANS.......................................................................................................30 4.8DPLOIEMENT DE L'EJB DE LA COUCHE [DAO] AVEC GLASSFISH.......................................................................................................31 4.9TESTS DE L'EJB DE LA COUCHE [DAO]............................................................................................................................................36 4.10LE SERVICE WEB DE LA COUCHE [DAO].........................................................................................................................................38 4.10.1PROJET NETBEANS - VERSION 1.................................................................................................................................................39 4.10.2PROJET NETBEANS - VERSION 2..................................................................................................................................................44 4.10.3TESTS JUNIT DU SERVICE WEB...................................................................................................................................................48 5CLIENTS .NET DU SERVICE JAVA EE DES RENDEZ-VOUS.......................................................................................53 5.1UN CLIENT C# 2008....................................................................................................................................................................53 5.2UN PREMIER CLIENT ASP.NET 2008..........................................................................................................................................56 5.3UN SECOND CLIENT ASP.NET 2008............................................................................................................................................61 6CLIENTS FLEX DU SERVICE JAVA EE DES RENDEZ-VOUS......................................................................................64 6.1UN PREMIER CLIENT FLEX............................................................................................................................................................65 6.2UN DEUXIME CLIENT FLEX..........................................................................................................................................................75 7CONCLUSION..........................................................................................................................................................................77

Serge Tah, http://tahe.developpez.com, fvrier 2009

78/78

Vous aimerez peut-être aussi