version 2.0
Transcription
version 2.0
Intergiciel et Construction d’Applications Réparties c 2007, Cédric JOFFROY, Sébastien MOSSER, Michel RIVEILL (version du 14 novembre 2008 - 11:13) Chapitre9 Premiers pas J2EE L’objectif de cette annexe est de permettre à chacun de se familiariser un peu avec une plate-forme respectant les spécifications J2EE (Java 2 Enterprise Edition). Pour cela nous allons développer une petite application permettant à chacun de géré ses cartes de visite (ajout, suppression et consultation) et de les rendre accessibles aux autres utilisateurs (uniquement consultation). Pour atteindre ce but, l’application se compose de deux parties : le service d’annuaire qui doit pouvoir être utilisée à distance et différentes applications clientes de type client léger ou client lourd graphique ou en ligne de commande. Pour des raisons de simplicité nous ne nous intéresserons pas aux problèmes liés à l’authentification des différentes classes d’utilisateurs. Pour mettre en œuvrecette application nous allons utiliser : – un container managed entity bean pour gérer la persistance des cartes de visites, – un statefull session bean pour l’ajout et la suppression des cartes de visites, – un stateless session bean pour la consultation des cartes stockées, – un client lourd pour administrer l’application et permettre l’ajout et la suppression de cartes de visites – un client léger de consultation en utilisant une (servlet). Une version html de ce texte est disponible à l’adresse http://rangiroa.polytech. unice.fr/riveill/enseignement/tp/j2ee.html. Elle contiendra les évolutions futures du sujet. 9.1 Les outils à utiliser Parmis toutes les plates-formes J2EE disponible, nous avons fait le choix d’utiliser J2EE Jonas développé par des équipes françaises dans le cadre du consortium ObjectWeb qui peut être récupéré sur le site web du consortium1 . 1 www.objectweb.com 2 9.1.1 CHAPITRE 9. PREMIERS PAS J2EE Installation de Jonas L’installation proposée ici concerne la version 4.7.7 et s’adresse aux utilisation d’une machine de type Linux ou Mac Os X. On laisse au lecteur attentif la modification de cette procédure pour installer Jonas sur une plateforme Windows ou pour installer une autre version de Jonas. Nous allons installer Jonas dans le répertoire /opt/. LINUX :~ > tar xvfz jonas4 .7.7 - tomcat5 .5.15. tgz LINUX :~ > sudo mv JONAS_4_7_7 / opt /. Pour fonctionner correctement, Jonas utiliser trois variables d’environnement qui doivent être correctement positionnée : – JONAS ROOT : chemin d’accès au repertoire de Jonas, – PATH : chemin d’accès aux binaires de Jonas, – CLASSPATH : doit contenir les deux archives nécéssaire au bon déroulement de ces ateliers : ejb-2 1-api.jar et servlet-2 4.jar. – selon les versions Jonas, il peut y avoir d’autres variables d’environneent : SPS=’ :’ et JONAS BASE=JONAS ROOT 9.1.2 Installation d’ANT L’installation Ant est recommandée si l’on veut tester les exemples fournis avec Jonas et qui se trouve dans le répertoire /opt/JONAS 4 7 7/examples/src/. Ant est par exemple disponible à l’url http://ant.apache.org/bindownload.cgi et s’installe très aisément. LINUX :~ > unzip apache - ant -1.6.5 - bin . zip LINUX :~ > sudo mv apache - ant -1.6.5 / opt /. Comme pour Jonas, il est nécessaire que la variable d’environnement ANT HOME contienne le répertoire d’installation de Ant. 9.1.3 Mise à jour des variables d’environnement Pour être certain de ne pas oublier une mise à jour lors d’une utilisation ultérieure, nous vous invitons à regrouper celles-ci dans le fichier environnement.sh. # !/ bin / sh # Mise à jour des variables d ’ environnemnts pour Jonas JONAS_ROOT =/ opt / JONAS_4_7_7 echo " JONAS_ROOT set to $JONAS_ROOT " PATH = $JONAS_ROOT / bin / unix : $PATH echo " PATH set to $PATH " CLASSPATH = $CLASSPATH : $JONAS_ROOT / lib / commons / j2ee / ejb -2 _1 - api . jar CLASSPATH = $CLASSPATH : $JONAS_ROOT / lib / commons / j2ee / servlet -2 _4 . jar echo " CLASSPATH set to $CLASSPATH " # Mise à jour des variables d ’ environnement pour Ant 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 3 ANT_HOME =/ opt / apache - ant -1.6.5 echo " ANT_HOME set to $ANT_HOME " export JONAS_ROOT ANT_HOME PATH CLASSPATH Listing 9.1 – Fichier environment.sh Il ne reste plus qu’à exécuter ce shell à l’aide de la commande source pour s’affranchir du dialecte utilisé (zsh, tcsh ou bash) : LINUX :~ > source environnement . sh 9.1.4 Pour tester l’installation Avant toutes autres manipulation, un test de l’installation s’impose. Suivez attentivement les différents messages d’erreurs. LINUX :~ > jonas check LINUX :~ > jonas start 1. Le check vérifie l’état de l’installation, et peut renvoyer des warnings, rien de bien grave. 2. Le start lance le serveur Jonas. Lorsque le lancement est terminé, il rend la main en indiquant qu’il à démarré. 9.2 Gestion de la persistance - entity bean Les objets persistants sont les cartes de visites. Nous allons donc les implémenter sous la forme d’entity bean dont la persistance est directement gérée par le container. Avant d’écrire l’entity bean, construisons la base de données. 9.2.1 La base de données Pour conserver les cartes de visite nous allons utiliser une base de donnée qui contiendra une seule table CARTEDEVISITE structurée de la manière suivante : – un id permettant d’identifier de manière unique chacune des cartes de visite, – un nom, – un prénom, – une adresse mail, – un numéro de téléphone. Tous ces champs sont de type varchar et sont non nuls, à l’exception du champ id qui est de type identity (shortcut pour un integer primary key dans le SGBD utilisé). Nous utiliserons le micro–moteur de base de données HSQLDB, livré avec Jonas. Les informations de connexion sont les suivantes urlid localhost - dbjonas url jdbc : hsqldb : hsql :// localhost :9001/ db_jonas username jonas 4 CHAPITRE 9. PREMIERS PAS J2EE password jonas Listing 9.2 – Fichier sql-rc.conf Description du schéma relationnel 1 6 drop table if exists CARTEDEVISITE ; create table CARTEDEVISITE ( ID IDENTITY , NOM varchar (255) PRENOM varchar (255) EMAIL varchar (255) PHONE varchar (255) ); not not not not null , null , null , null Listing 9.3 – Fichier database.sql Chargement de la relation dans le moteur 2 LINUX :~ > java - jar $JONAS_ROOT / lib / commons / jonas / hsqldb . jar \ -- autoCommit -- continueOnErr \ -- rcfile ./ sql - rc . conf localhost - dbjonas database . sql Script de chargement automatique Pour simplifier l’initialisation de la base de données, on met en place un script qui permet d’exécuter cette ligne de commande : 2 # !/ bin / sh echo " Database initialization " java - jar $JONAS_ROOT / lib / commons / jonas / hsqldb . jar \ -- autoCommit -- continueOnErr \ -- rcfile ./ sql - rc . conf localhost - dbjonas database . sql Listing 9.4 – Fichier initDatabase.sh Pour exécuter ce script (après l’avoir rendu exécutable . . .) : LINUX :~ > ./ initDatabase . sh On dispose maintenant d’une relation vide dans le moteur de base de données HSQL. Si vous n’êtes pas convaincu, lancez le client graphique Hsql Manager sans toucher au mot de passe : LINUX :~ > java - cp $JONAS_ROOT / lib / commons / jonas / hsqldb . jar org . hsqldb . util . DatabaseManager 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 9.2.2 5 Ecriture du container managed entity bean Le code d’un entity bean est réparti dans plusieurs classes Java et descripteurs : – Remote Interface : l’interface qui définit les différentes méthodes du Bean qui seront accessibles pour les clients, – Home Interface : l’interface qui est visible par le client pour créer, retrouver puis manipuler des instances du Bean, – la classe d’implémentation du Bean, – deux descripteurs de déploiement indiquant entre autre le mapping objet ⇔ relationnel à utiliser pour garantir la persistance des objets, et les requetes permettant de les retrouver. Interface remote : on décrit dans cette interface les fonctions permettant de manipuler les cartes de visite. package carte_cmp ; 4 import javax . ejb . EJBObject ; import java . rmi . RemoteException ; public interface CarteDeVisiteCMP extends EJBObject { 9 14 19 public public public public public Integer getId () throws RemoteException ; String getNom () throws RemoteException ; String getPrenom () throws RemoteException ; String getEmail () throws RemoteException ; String getPhone () throws RemoteException ; // Pas public public public public de setId , le SGBD la genere . void setPrenom ( String prenom ) throws RemoteException ; void setNom ( String nom ) throws RemoteException ; void setEmail ( String email ) throws RemoteException ; void setPhone ( String phone ) throws RemoteException ; } Listing 9.5 – Fichier CarteDeVisiteCMP.java Interface Home : cette interface, accessible par les clients de l’application, permet de retouver les instances de cartes de visites, d’en créer, . . .. Les instances qu’elle renvoie sont du type de l’interface précédente, ce qui masque aux clients l’implémentation réelle du Bean. 1 package carte_cmp ; import java . rmi . RemoteException ; import javax . ejb .*; import java . util .*; 6 public interface CarteDeVisiteCMPHome extends EJBHome { public CarteDeVisiteCMP create ( String nom , String prenom , String email , String phone ) 6 CHAPITRE 9. PREMIERS PAS J2EE throws RemoteException , CreateException ; 11 public CarteDeVisiteCMP findByPrimaryKey ( Integer id ) throws RemoteException , FinderException ; public Collection findByNom ( String nom ) throws RemoteException , FinderException ; 16 public Collection findByPrefix ( String prefix ) throws RemoteException , FinderException ; 21 } Listing 9.6 – Fichier CarteDeVisiteCMPHome.java Implémentation du bean Le choix d’utiliser un CMP entity bean simplifie grandement la mise en oeuvre puis que la plupart des méthodes n’ont pas besoin d’être implémentées et seront générées à partir des fichiers de déploiement. package carte_cmp ; 4 9 14 19 24 29 34 import import import import import java . sql .*; javax . sql .*; java . util .*; javax . ejb .*; javax . naming .*; public abstract class CarteDeVisiteCMPBean implements EntityBean { /* * Abstract == > gere par le conteneur * */ public abstract Integer getId (); public abstract void setId ( Integer id ); public abstract String getNom (); public abstract void setNom ( String nom ); public abstract String getPrenom (); public abstract void setPrenom ( String prenom ); public abstract String getEmail (); public abstract void setEmail ( String email ); public abstract String getPhone (); public abstract void setPhone ( String phone ); // Cree le composant et le sauvegarde dans la base de donnees avec // un insert public Integer ejbCreate ( String nom , String prenom , String email , String phone ) throws CreateException { // renseigne les propriete du composant setNom ( nom ); setPrenom ( prenom ); setEmail ( email ); setPhone ( phone ); return null ; } public void ejbPostCreate ( String nom , String prenom , 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 7 String email , String phone ) { // Dans le cas de tables de liaisons , on instancierait les // liaisons ici . 39 } 49 // les methodes suivantes n ’ ont pas besoin d etre implemante dans // le cadre d un CMP entity bean public void ejbRemove () throws RemoveException { } public void setEntityContext ( EntityContext context ) { this . context = context ; } public void unsetEntityContext () { this . context = null ; } 54 public public public public 44 void void void void ejbActivate () { } ejbPassivate () { } ejbLoad () { } ejbStore () { } } Listing 9.7 – Fichier CarteDeVisiteCMPBean.java La clé primaire utilisée sera de type auto incrémentée (par la base de donnée). Nous n’utiliserons donc jamais la méthode setId(). TODO pourquoi alors mettre cette méthode ? on a besoin de l’opération ’setId’ car le générateur de code rale si elle n’y est pas. Reste à le revérifier. Descripteur de déploiement J2EE : ce fichier est commun à tous les serveur d’applications J2ee et permet de préciser le mapping à utiliser entre l’entity bean et la base de donnée. 5 10 15 20 25 <? xml version ="1.0" encoding =" ISO -8859 -1"? > <ejb - jar > < description > Descripteur de deploiement pour l EJB carte de visite </ description > < display - name > EJB CarteDeVisite </ display - name > < enterprise - beans > < entity > < description > EJB CarteDeVisite ( CMP ) </ description > <ejb - name > CarteDeVisiteCMP </ ejb - name > < home > carte_cmp . CarteDeVisiteCMPHome </ home > < remote > carte_cmp . CarteDeVisiteCMP </ remote > <ejb - class > carte_cmp . CarteDeVisiteCMPBean </ ejb - class > < persistence - type > Container </ persistence - type > < prim - key - class > java . lang . Integer </ prim - key - class > < reentrant > False </ reentrant > < abstract - schema - name > cartedevisite </ abstract - schema - name > <cmp - field > < field - name > id </ field - name > </ cmp - field > <cmp - field > < field - name > nom </ field - name > </ cmp - field > <cmp - field > < field - name > prenom </ field - name > </ cmp - field > 8 30 35 40 45 50 55 60 65 70 75 CHAPITRE 9. PREMIERS PAS J2EE <cmp - field > < field - name > email </ field - name > </ cmp - field > <cmp - field > < field - name > phone </ field - name > </ cmp - field > < primkey - field > id </ primkey - field > < query > < query - method > < method - name > findByNom </ method - name > < method - params > < method - param > java . lang . String </ method - param > </ method - params > </ query - method > <ejb - ql > SELECT OBJECT ( o ) FROM cartedevisite o WHERE o . nom = ?1 </ ejb - ql > </ query > < query > < query - method > < method - name > findByPrefix </ method - name > < method - params > < method - param > java . lang . String </ method - param > </ method - params > </ query - method > <ejb - ql > SELECT OBJECT ( o ) FROM cartedevisite o WHERE LOCATE (?1 , o . nom ) > 0 </ ejb - ql > </ query > < query > < query - method > < method - name > findByPrimaryKey </ method - name > < method - params > < method - param > java . lang . Integer </ method - param > </ method - params > </ query - method > <ejb - ql > SELECT OBJECT ( o ) FROM cartedevisite o WHERE o . id = ?1 </ ejb - ql > </ query > </ entity > </ enterprise - beans > < assembly - descriptor > < container - transaction > < method > <ejb - name > CarteDeVisiteCMP </ ejb - name > < method - name >* </ method - name > </ method > < trans - attribute > Required </ trans - attribute > </ container - transaction > </ assembly - descriptor > </ ejb - jar > Listing 9.8 – Fichier ejb-jar.xml 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 9 Descripteur Jonas : En complément du descripteur J2EE, chaque fournisseur de plateforme propose son propre descripteur afin de compléter la description initiale. Dans Jonas, il permet de décrire l’utilisation des ressources tierces comme par exemple l’accès à la base de données. 1 6 11 16 21 26 31 <? xml version ="1.0" encoding =" ISO -8859 -1"? > < jonas - ejb - jar > < jonas - entity > <ejb - name > CarteDeVisiteCMP </ ejb - name > < jndi - name > MyCarteDeVisiteCMP </ jndi - name > < jdbc - mapping > < jndi - name > jdbc_1 </ jndi - name > < jdbc - table - name > CARTEDEVISITE </ jdbc - table - name > < automatic - pk > true </ automatic - pk > <cmp - field - jdbc - mapping > < field - name > id </ field - name > < jdbc - field - name > ID </ jdbc - field - name > </ cmp - field - jdbc - mapping > <cmp - field - jdbc - mapping > < field - name > nom </ field - name > < jdbc - field - name > NOM </ jdbc - field - name > </ cmp - field - jdbc - mapping > <cmp - field - jdbc - mapping > < field - name > prenom </ field - name > < jdbc - field - name > PRENOM </ jdbc - field - name > </ cmp - field - jdbc - mapping > <cmp - field - jdbc - mapping > < field - name > phone </ field - name > < jdbc - field - name > PHONE </ jdbc - field - name > </ cmp - field - jdbc - mapping > <cmp - field - jdbc - mapping > < field - name > email </ field - name > < jdbc - field - name > EMAIL </ jdbc - field - name > </ cmp - field - jdbc - mapping > </ jdbc - mapping > </ jonas - entity > </ jonas - ejb - jar > Listing 9.9 – Fichier jonas-ejb-jar.xml 9.2.3 Compilation et déploiement de l’entity bean Nous avons maintenant tout ce qui est nécessaire pour compiler et déployer l’entity bean. A l’issue de cette première étape nous avons l’arborescence suivante : 3 8 + - - > bin / + - - > META - INF / + - - > ejb - jar . xml + - - > jonas - ejb - jar . xml + - - > src / + - - > CarteDeVisiteCMPBean . java + - - > CarteDeVisiteCMPHome . java + - - > CarteDeVisiteCMP . java + - - > Test . java 10 CHAPITRE 9. PREMIERS PAS J2EE Nous conserverons cette arborescence pour les autres beans que nous créerons. Le répertoire bean contiendra toujours le code compilé des fichiers java et le répertoire bin/META-INF, les descripteurs. Pour pouvoir être exécuté par Jonas, un bean a besoin d’être contenu dans une archive qui facilite son déploiement. Pour cela, on génère le bytecode, puis on écrit les descripteur de déploiement et on archive l’ensemble dans un fichier jar. 1 6 LINUX :~/ CarteDeVisite > javac -d bin src /*. java LINUX :~/ CarteDeVisite > cp *. xml bin / META - INF /. LINUX :~/ CarteDeVisite > cd bin LINUX :~/ CarteDeVisite / bin > jar cf ../ CarteDeVisiteCMP . jar LINUX :~/ CarteDeVisite / bin > cd .. LINUX :~/ CarteDeVisite > Déployer un bean dans Jonas se fait en deux étapes : – déposer l’archive dans le repertoire ejbjars de Jonas, – utiliser l’outils d’administration de Jonas pour publier le bean. 4 LINUX :~/ CarteDeVisite > cp CarteDeVisiteCMP . jar $JONAS_ROOT / ejbjars /. LINUX :~/ CarteDeVisite > cd $JONAS_ROOT / ejbjars LINUX :/ opt / JONAS_4_7_7 / ejbjars > jonas admin -a CarteDeVisiteCMP . jar LINUX :/ opt / JONAS_4_7_7 / ejbjars > cd LINUX :~/ CarteDeVisite > 9.2.4 Client d’un entity bean Ce client, non essentiel à l’application, permet de valider le code de l’entity bean. Nous faisons le choix d’implémenter un client simple qui crée deux entity bean, effectue une recherche par nom et une recherche à l’aide d’un préfixe. package carte_cmp ; 5 import import import import java . util . Properties ; javax . naming . InitialContext ; javax . naming . Context ; javax . rmi . PortableRemoteObject ; import java . util . Collection ; import java . util . Iterator ; 10 public class Test { public static void main ( String args []) { 15 try { // Recherche de l ’ interface home de l ’ EJB Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyCarteDeVisiteCMP " ); 20 // Reference a l ’ interface locale de l ’ EJB C arteDeVisiteCMPHome home = ( CarteDeVisiteCMPHome ) PortableRemoteObject . narrow ( objref , CarteDeVisiteCMPHome . class ); 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 11 // Creation deux carte de visite dans la base de donnees CarteDeVisiteCMP toto = home . create ( " TOTO " , " toto " , " toto@toto " , " 0123456 " ); CarteDeVisiteCMP titi = home . create ( " TITI " , " titi " , " titi@titi " , " 9876443 " ); 25 30 // Objet de type client pour faire nos tests CarteDeVisiteCMP test ; // Recherche par le nom de la personne System . out . println ( " Liste des clients qui ont pour nom TOTO : " ); Collection cl = home . findByNom ( " TOTO " ); Iterator iLast = cl . iterator (); while ( iLast . hasNext ()) { test = iLast . next (); System . out . println ( " ->" + test . getPrenom ()); } 35 40 // Recherche par le prefixe de la personne System . out . println ( " Liste des clients qui ont pour prefixe T : " ); Collection c2 = home . findByPrefix ( " T " ); Iterator iLast1 = c2 . iterator (); while ( iLast1 . hasNext ()) { test = iLast1 . next (); System . out . println ( " ->" + test . getPrenom ()); } 45 50 } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); System . exit (2); } 55 } } Listing 9.10 – Fichier Test.java 9.2.5 Compilation et exécution du client Après avoir compilé le client, nous utilisons pour l’exécuter, l’utilitaire jclient qui permet l’exécution du client dans le contexte de Jonas et d’avoir ainsi l’accès à l’annuaire de référencement JNDI dans lequel sont enregistrés tous les beans publiés dans Jonas. Nous considèrerons que le client possède dans son CLASSPATH le jar du bean, pour pouvoir utiliser le proxy (classe RemoteStub générée par genic) permettant d’accèder au bean comme un objet distant. Pour ’travailler proprement’, il serait beaucoup plus judicieux de donner au client soit le proxy, soit une manière de le générer. 1 6 LINUX :~/ CarteDeVisite > jclient \ - cp $JONAS_ROOT / ejbjars / CarteDeVisiteCMP . jar \ - argclient carte_cmp . Test ClientContainer . info : Starting client ... Liste des clients qui ont pour nom TOTO : -> toto 12 CHAPITRE 9. PREMIERS PAS J2EE Liste des clients qui ont pour prefixe T : -> toto -> titi 9.2.6 Automatisation des tâches répétitives La compilation, le déployement et la publication d’un bean, ainsi que la compilation et l’exécution d’un client sont des tâches répétitives qu’il est aisé d’automatiser. Voici un exemple de script bash qui rendra la suite de l’ennoncé beaucoup plus lisible. Ce script permet : – de compiler et générer l’archive d’un bean (compile), – de le déployer et de le publier (load), – de compiler et d’exécuter un client (run), – de retirer le bean publié (unload), – et de nettoyer les sources (clean). TODO valider le script pour tcsh 1 # !/ bin / sh # ATTENTION : fonctionne en bash , zsh 6 JAR_NAME = CarteDeVisiteCMP . jar CLIE NT_CLASS_NAME = carte_cmp . Test # ## DO NOT EDIT AFTER THIS LINE !! ### export JAR_NAME CLIENT_CLASS_NAME 11 16 function compile () { echo " ## src --> bin compilation " javac -d bin src /*. java echo " ## Deploying xml descriptors " cp *. xml bin / META - INF /. echo " ## Building $JAR_NAME " cd bin jar cf ../ $JAR_NAME * cd .. } 21 26 31 36 function load () { echo " ## Moving $JAR_NAME --> $JONAS_ROOT / ejbjars " mv $JAR_NAME $JONAS_ROOT / ejbjars /. echo " ## Loading $JAR_NAME in JONAS " cd $JONAS_ROOT / ejbjars jonas admin -a $JAR_NAME cd } function unload () { echo " ## Unloading $JAR_NAME from JONAS " cd $JONAS_ROOT / ejbjars jonas admin -r $JAR_NAME cd } 9.2. GESTION DE LA PERSISTANCE - ENTITY BEAN 41 46 51 56 61 13 function run () { echo " ## Runnning client ... " jclient - cp $JONAS_ROOT / ejbjars / $JAR_NAME - argclient $CLIENT_CLASS_NAME } function clean () { echo " ## Cleaning directories " rm - rf *~ *. jar src /*~ bin /* mkdir bin / META - INF } function main () { case $1 in " compile " ) compile ;; " load " ) load ;; " unload " ) unload ;; " run " ) run ;; " clean " ) clean ;; " all " ) unloand ; clean ; compile ; load ; run ;; *) " Unknown Command : [ $1 ] " ;; esac } main $@ Listing 9.11 – Fichier manage.sh La gestion d’un bean devient simple. Il suffit de modifier l’entête pour indique le nom du bean et le nom du client et d’exécuter ce script en précisant l’action a effectuer. 4 9 14 19 LINUX :~/ CarteDeVisite > ./ manage . sh clean # # Cleaning directories LINUX :~/ CarteDeVisite > ./ manage . sh compile # # src --> bin compilation # # Deploying xml descriptors # # Building CarteDeVisiteCMP . jar LINUX :~/ CarteDeVisite > ./ manage . sh load # # Moving CarteDeVisiteCMP . jar --> / opt / JONAS_4_7_7 / ejbjars # # Loading CarteDeVisiteCMP . jar in JONAS LINUX :~/ CarteDeVisite > ./ manage . sh run # # Runnning client ... ClientContainer . info : Starting client ... Liste des clients qui ont pour nom TOTO : -> toto -> toto Liste des clients qui ont pour prefixe T : -> toto -> titi -> toto -> titi LINUX :~/ CarteDeVisite > ./ manage . sh unload # # Unloading CarteDeVisiteCMP . jar from JONAS Note : Nous utiliserons systématiquement ce script par la suite. 14 CHAPITRE 9. PREMIERS PAS J2EE 9.3 La partie métier - statefull session bean 9.3.1 Principe Dans le cadre d’une architecture n − tiers, il n’est pas pensable d’offrir aux différents clients la possibilité de manipuler directement les entity beans qui représentent des données dans la base de données. En effet, les accès sont trop élémentaires. Nous allons encapsuler la logique métier de l’application dans un session bean. Les opérations d’ajout et de supression de cartes étant critiques nous allons uniquement les autoriser pour l’administrateur de l’application à l’aide d’un bean de session à état (statefull session bean), dans lequel on positionnera un état estIdentifié. L’éxécution des actions d’ajout et de suppression sera autorisée uniquement pour les utilisateurs authentifiés. 9.3.2 Ecriture du statefull session bean Comme pour un entity bean, l’écriture d’un session bean demande l’écriture de deux interfaces (l’interface remote et l’interface home), d’une classe java implémentant le bean et de deux descripteurs de configuration (celui présent dans toutes plates-formes J2EE et celui de Jonas). Interface remote package administrators ; 3 8 import javax . ejb . EJBObject ; import java . rmi . RemoteException ; public interface Administrator extends EJBObject { // Log into the bean public boolean login ( String username , String password ) throws RemoteException ; // Log out from the bean public boolean logout () throws RemoteException ; 13 // Add a ’ CarteDeVisite ’ inside the system public boolean add ( String nom , String prenom , String mail , String phone ) throws RemoteException ; 18 // Delete a card , using the id to retrieve the good one public boolean delete ( int id ) throws RemoteException ; } Listing 9.12 – Fichier Administrator.java Interface home 9.3. LA PARTIE MÉTIER - STATEFULL SESSION BEAN 15 package administrators ; 3 8 import java . rmi . RemoteException ; import javax . ejb . CreateException ; import javax . ejb . EJBHome ; public interface AdministratorHome extends EJBHome { // Constructeur par defaut Administrator create () throws RemoteException , CreateException ; // Permet de se logger directement a la creation Administrator create ( String username , String password ) throws RemoteException , CreateException ; 13 } Listing 9.13 – Fichier AdministratorHome.java Implémentation du session bean package administrators ; 5 import java . rmi . RemoteException ; import javax . ejb . SessionBean ; import javax . ejb . SessionContext ; import javax . rmi .*; import javax . naming . InitialContext ; import javax . naming . Context ; 10 import java . util .*; import carte_cmp .*; 15 public class AdministratorBean implements SessionBean { private boolean isLogged ; 25 public boolean login ( String username , String password ) { if ( username . equals ( " admin " ) && password . equals ( " admin " )) { this . isLogged = true ; return true ; } return false ; } 30 public boolean logout () { this . isLogged = false ; return true ; } 20 public boolean add ( String nom , String prenom , String mail , String phone ) { try { if ( isLogged ) { 16 CHAPITRE 9. PREMIERS PAS J2EE Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyCarteDeVisiteCMP " ); 35 // Reference a l ’ interface locale de l ’ EJB CarteDeVisiteCMPHome home = ( CarteDeVisiteCMPHome ) PortableRemoteObject . narrow ( objref , CarteDeVisiteCMPHome . class ); 40 // Objet de type CarteDeVisite , avec creation de cet objet CarteDeVisiteCMP test = home . create ( nom , prenom , mail , phone ); 45 return true ; } else return false ; } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); } return false ; 50 } 55 public boolean delete ( int id ) { try { if ( isLogged ) { Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyCarteDeVisiteCMP " ); 60 // Reference a l ’ interface locale de l ’ EJB CarteDeVisiteCMPHome home = ( CarteDeVisiteCMPHome ) PortableRemoteObject . narrow ( objref , CarteDeVisiteCMPHome . class ); 65 // Objet de type CarteDeVisite , en vue de le supprimer // de la table CarteDeVisiteCMP test = home . findByPrimaryKey ( id ); if ( test != null ){ test . remove (); return true ; } else return false ; } else return false ; 70 } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); } return false ; 75 } 80 public void ejbCreate () 85 90 { this . isLogin = false ; } public void ejbCreate ( String username , String password ) { if ( username . equals ( " admin " ) && password . equals ( " admin " )) this . isLogin = true ; else this . isLogin = false ; } public void ejbRemove () {} 9.3. LA PARTIE MÉTIER - STATEFULL SESSION BEAN public void ejbActivate () {} public void ejbPassivate () {} public void setSessionContext ( SessionContext sc ) {} 95 } Listing 9.14 – Fichier AdministratorBean.java Descripteur J2EE 5 10 15 20 25 <? xml version ="1.0" encoding =" ISO -8859 -1"? > <ejb - jar > < description > Descripteur de deploiement du bean administratif </ description > < display - name > Administrator </ display - name > < enterprise - beans > < session > < description > Administrator </ description > < display - name > Administrator </ display - name > <ejb - name > Administrator </ ejb - name > < home > administrators . AdministratorHome </ home > < remote > administrators . Administrator </ remote > <ejb - class > administrators . AdministratorBean </ ejb - class > < session - type > Stateful </ session - type > < transaction - type > Container </ transaction - type > </ session > </ enterprise - beans > < assembly - descriptor > < container - transaction > < method > <ejb - name > Administrator </ ejb - name > < method - name >* </ method - name > </ method > < trans - attribute > Required </ trans - attribute > </ container - transaction > </ assembly - descriptor > </ ejb - jar > Listing 9.15 – Fichier ejb-jar.xml Descripteur Jonas 3 <? xml version ="1.0" encoding =" ISO -8859 -1"? > < jonas - ejb - jar > < jonas - session > <ejb - name > Administrator </ ejb - name > < jndi - name > MyAdministrator </ jndi - name > </ jonas - session > </ jonas - ejb - jar > Listing 9.16 – Fichier jonas-ejb-jar.xml 17 18 CHAPITRE 9. PREMIERS PAS J2EE 9.3.3 Ecriture d’un client du session bean Comme pour l’entity bean, nous allons construire un client capable de se connecter au session bean précédemment décrit. package administrators ; 3 import import import import java . util . Properties ; javax . naming . InitialContext ; javax . naming . Context ; javax . rmi . PortableRemoteObject ; 8 import import import import import java . io . BufferedReader ; java . io . InputStreamReader ; java . io . IOException ; java . io . OutputStreamWriter ; java . io . PrintWriter ; 13 import java . util .*; import carte_cmp .*; 18 public class AdministratorClient { public static void main ( String args []) { try { 23 28 33 38 43 48 InputStreamReader isr = new InputStreamReader ( System . in ); BufferedReader br = new BufferedReader ( isr ); boolean isQuit = false ; // Recherche de l ’ interface home de l ’ EJB Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyAdministrator " ); // Reference a l ’ interface locale de l ’ EJB AdministratorHome home = ( AdministratorHome ) PortableRemoteObject . narrow ( objref , AdministratorHome . class ); // Creation d ’ un administrateur Administrator myAdministrator = home . create (); while ( isQuit == false ) { System . out . println ( " 1. Connexion " ); System . out . println ( " 2. Quitter " ); System . out . println ( " >" ); String principalChoice = null ; principalChoice = br . readLine (); if ( principalChoice . equals ( " 2 " )){ isQuit = true ; System . out . println ( " Bye bye ... " ); } else if ( principalChoice . equals ( " 1 " )){ System . out . println ( " Username : " ); String username = null ; 9.3. LA PARTIE MÉTIER - STATEFULL SESSION BEAN 19 username = br . readLine (); 53 58 63 68 73 78 83 88 93 98 103 System . out . println ( " Password : " ); String password = null ; password = br . readLine (); if ( myAdministrator . login ( username , password )){ boolean isLog = true ; while ( isLog == true ){ System . out . println ( " 1. Ajouter une carte " + " de visite " ); System . out . println ( " 2. Retirer une carte " + " de visite " ); System . out . println ( " 3. Se deconnecter " ); System . out . println ( " >" ); String secondaryChoice = null ; secondaryChoice = br . readLine (); if ( secondaryChoice . equals ( " 1 " )){ System . out . println ( " Ajout d ’ une " + " carte de visite ... " ); System . out . println ( " Entrer le nom : " ); String nom = null ; nom = br . readLine (); System . out . println ( " Entrer le prenom : " ); String prenom = null ; prenom = br . readLine (); System . out . println ( " Entrer le mail : " ); String mail = null ; mail = br . readLine (); System . out . println ( " Entrer le numero de " + " tel : " ); String phone = null ; phone = br . readLine (); boolean isAdd = myAdministrator . add ( nom , prenom , mail , phone ); if ( isAdd ){ System . out . println ( " Ajout reussi ... " ); } else { System . out . println ( " Ajout echoue ... " ); } } else if ( secondaryChoice . equals ( " 2 " )){ System . out . println ( " Suppression d ’ une " + " carte de visite ... " ); System . out . println ( " Entrer l ’ ID : " ); String id = null ; id = br . readLine (); int idToDelete = Integer . parseInt ( id ); boolean isDelete = myAdministrator . delete ( idToDelete ); if ( isDelete ){ System . out . println ( " Suppression " + " reussie ... " ); } 20 CHAPITRE 9. PREMIERS PAS J2EE else { System . out . println ( " Supression " + " echouee ... " ); } 108 } else if ( secondaryChoice . equals ( " 3 " )){ myAdministrator . logout (); isLog = false ; System . out . println ( " Logout ... " ); } else { System . out . println ( " Choix incorrect ... " ); } 113 118 } } else { System . out . println ( " Username ou Password " + " incorrect ... " ); } 123 } else { System . out . println ( " Choix incorrect ... " ); } 128 } } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); System . exit (2); } 133 } } Listing 9.17 – Fichier AdministratorClient.java 9.3.4 Compilation du bean et du client, déploiement du bean et exécution du client Il est essentiel que le répertoire du session bean connaisse les interfaces de l’entity bean qu’il va accéder. Il est donc nécessaire de copier les interfaces home et remote de l’entity bean (CarteDeVisiteCMPHome.java et CarteDeVisiteCMP.java) dans le répertoire src du session bean avec l’ensemble du package2 . Il reste aussi à copier le script associé à l’entity bean (manage.sh) dans le répertoire du session bean et de le mettre à jour avec les modifications suivantes : JAR_NAME = Administrator . jar CLIE NT_CLASS_NAME = administrators . AdministratorClient L’exécution du client doit être paramétrée de la manière suivante : 3 jclient - cp / opt / JONAS_4_7_7 / ejbjars / $JAR_NAME :\ $JONAS_ROOT / ejbjars / CarteDeVisiteCMP . jar \ - argclient $CLIENT_CLASS_NAME 2 Une solution plus éléguante, laisxssée au lecteur en exercice, serait d’implémenter un système de chargement de classe dynamique qui téléchargerait ces classes depuis le serveur Jonas à l’exécution. 9.3. LA PARTIE MÉTIER - STATEFULL SESSION BEAN 21 Il ne reste plus qu’à utiliser le script pour compiler le bean et le client, deployer le bean puis exécution le client. 2 7 12 17 22 27 32 37 42 47 52 LINUX :~/ Administrator > ./ manage . sh compile # # src --> bin compilation # # Deploying xml descriptors # # Building Administrator . jar LINUX :~/ Administrator > LINUX :~/ Administrator > ./ manage . sh load # # Moving Administrator . jar --> / opt / JONAS_4_7_7 / ejbjars # # Loading Administrator . jar in JONAS LINUX :~/ Administrator > ./ manage . sh run # # Runnning client ... ClientContainer . info : Starting client ... 1. Connexion 2. Quitter > toto Choix incorrect ... 1. Connexion 2. Quitter > 1 Username : toto Password : titi Username ou Password incorrect ... 1. Connexion 2. Quitter > 1 Username : admin Password : admin 1. Ajouter une carte de visite 2. Retirer une carte de visite 3. Se deconnecter > 1 Ajout d ’ une carte de visite ... Entrer le nom : MOSSER Entrer le prenom : sebastien Entrer le mail : mosser@polytech . unice . fr Entrer le numero de tel : 01234566 Ajout reussi ... 1. Ajouter une carte de visite 2. Retirer une carte de visite 3. Se deconnecter > 1 22 57 62 67 72 CHAPITRE 9. PREMIERS PAS J2EE Ajout d ’ une carte de visite ... Entrer le nom : JOFFROY Entrer le prenom : Cedric Entrer le mail : joffroy@polytech . unice . fr Entrer le numero de tel : 987654321 Ajout reussi ... 1. Ajouter une carte de visite 2. Retirer une carte de visite 3. Se deconnecter > 3 Logout ... 1. Connexion 2. Quitter > 2 Bye bye ... 9.4 La partie métier (suite) - stateless session bean Après avoir écrit la partie métier qui sera utilisé par l’administrateur de l’application, nous allons construire celle utilisée par tous les utilisateurs souhaitant consulter la base de cartes de visite. La consultation d’une carte ne nécessite pas de session, nous utiliserons donc un stateless session bean. Comme tout bean, un stateless session bean a une remote interface, une home interface, une implémentation et deux descripteurs. 9.4.1 Ecriture du stateless session bean Remote interface 1 package consultators ; import javax . ejb . EJBObject ; import java . rmi . RemoteException ; 6 import java . util .*; public interface Consultator extends EJBObject { // Find a card looking on name public Collection rechercherNom ( String nom ) throws RemoteException ; 11 // Find a card with a prefix public Collection rechercherPrefix ( String prefix ) throws RemoteException ; 16 } 9.4. LA PARTIE MÉTIER (SUITE) - STATELESS SESSION BEAN Listing 9.18 – Fichier Consultator.java Home interface package consultators ; 3 import java . rmi . RemoteException ; import javax . ejb . CreateException ; import javax . ejb . EJBHome ; public interface ConsultatorHome extends EJBHome { 8 Consultator create () throws RemoteException , CreateException ; } Listing 9.19 – Fichier ConsultatorHome.java Implémentation du bean package consultators ; 4 9 import java . rmi . RemoteException ; import javax . ejb . SessionBean ; import javax . ejb . SessionContext ; import javax . rmi .*; import javax . naming . InitialContext ; import javax . naming . Context ; import java . util .*; import carte_cmp .*; 14 public class ConsultatorBean implements SessionBean { 19 24 public Collection rechercherNom ( String nom ) { try { Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyCarteDeVisiteCMP " ); // Reference a l ’ interface locale de l ’ EJB CarteDeVisiteCMPHome home = ( CarteDeVisiteCMPHome ) PortableRemoteObject . narrow ( objref , CarteDeVisiteCMPHome . class ); // Objet de type client pour faire nos tests CarteDeVisiteCMP test ; 29 // Recherche par le nom de la personne Collection c1 = home . findByNom ( nom ); 23 24 CHAPITRE 9. PREMIERS PAS J2EE return c1 ; } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); } return null ; 34 39 } 44 public Collection rechercherPrefix ( String prefix ) { try { Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyCarteDeVisiteCMP " ); 49 // Reference a l ’ interface locale de l ’ EJB CarteDeVisiteCMPHome home = ( CarteDeVisiteCMPHome ) PortableRemoteObject . narrow ( objref , CarteDeVisiteCMPHome . class ); // Objet de type client pour faire nos tests CarteDeVisiteCMP test ; // Recherche par le prefix de la personne Collection c1 = home . findByPrefix ( prefix ); 54 return c1 ; } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); } return null ; 59 } 64 public public public public public 69 void void void void void ejbCreate () {} ejbRemove () {} ejbActivate () {} ejbPassivate () {} setSessionContext ( SessionContext sc ) {} } Listing 9.20 – Fichier ConsultatorBean.java Descripteur J2EE 5 10 <? xml version ="1.0" encoding =" ISO -8859 -1"? > <ejb - jar > < description > Descripteur de deploiement du consultator </ description > < display - name > Consultator </ display - name > < enterprise - beans > < session > < description > Consultator </ description > < display - name > Consultator </ display - name > <ejb - name > Consultator </ ejb - name > < home > consultators . ConsultatorHome </ home > < remote > consultators . Consultator </ remote > 9.4. LA PARTIE MÉTIER (SUITE) - STATELESS SESSION BEAN 15 20 25 <ejb - class > consultators . ConsultatorBean </ ejb - class > < session - type > Stateless </ session - type > < transaction - type > Container </ transaction - type > </ session > </ enterprise - beans > < assembly - descriptor > < container - transaction > < method > <ejb - name > Consultator </ ejb - name > < method - name >* </ method - name > </ method > < trans - attribute > Required </ trans - attribute > </ container - transaction > </ assembly - descriptor > </ ejb - jar > Listing 9.21 – Fichier ejb-jar.xml Descripteur Jonas 4 <? xml version ="1.0" encoding =" ISO -8859 -1"? > < jonas - ejb - jar > < jonas - session > <ejb - name > Consultator </ ejb - name > < jndi - name > MyConsultator </ jndi - name > </ jonas - session > </ jonas - ejb - jar > Listing 9.22 – Fichier jonas-ejb-jar.xml 9.4.2 Client d’un session bean Nous commençons à être rodé... aucune difficulté particulière. package consultators ; 3 import import import import java . util . Properties ; javax . naming . InitialContext ; javax . naming . Context ; javax . rmi . PortableRemoteObject ; 8 import import import import import java . io . BufferedReader ; java . io . InputStreamReader ; java . io . IOException ; java . io . OutputStreamWriter ; java . io . PrintWriter ; 13 import java . util .*; import carte_cmp .*; public class ConsultatorClient { 18 public static void main ( String args []) { 25 26 CHAPITRE 9. PREMIERS PAS J2EE try { 23 28 33 // Recherche de l ’ interface home de l ’ EJB Context initialContext = new InitialContext (); Object objref = initialContext . lookup ( " MyConsultator " ); // Reference a l ’ EJB ConsultatorHome home = ( ConsultatorHome ) PortableRemoteObject . narrow ( objref , ConsultatorHome . class ); Consultator myConsultator = home . create (); // On va chercher des gens InputStreamReader isr = new InputStreamReader ( System . in ); BufferedReader br = new BufferedReader ( isr ); boolean isQuit = false ; 38 while ( isQuit == false ){ System . out . println ( " 1. Rechercher par nom " ); System . out . println ( " 2. Rechercher par prefixe ( sur le nom ) " ); System . out . println ( " 3. Quitter " ); System . out . println ( " >" ); 43 48 53 58 63 68 73 String choice = null ; choice = br . readLine (); if ( choice . equals ( " 1 " )){ System . out . println ( " Recherche par nom ... " ); String nom = null ; System . out . println ( " Entrez le nom : " ); nom = br . readLine (); CarteDeVisiteCMP test = null ; Collection cl = myConsultator . rechercherNom ( nom ); System . out . println ( " recherche sur le nom : " + nom ); if ( cl != null ){ Iterator iLast = cl . iterator (); while ( iLast . hasNext ()) { test = iLast . next (); System . out . println ( " ->" + test . getPrenom ()); } } else { System . out . println ( " Echec de recherche ... " ); } } else if ( choice . equals ( " 2 " )){ System . out . println ( " Recherche par prefixe ... " ); String prefix = null ; System . out . println ( " Entrez le prefixe : " ); prefix = br . readLine (); CarteDeVisiteCMP test = null ; Collection c2 = myConsultator . rechercherPrefix ( prefix ); System . out . println ( " recherche sur le prefix : " + prefix ); if ( c2 != null ){ Iterator iLast2 = c2 . iterator (); while ( iLast2 . hasNext ()) { test = iLast2 . next (); 9.4. LA PARTIE MÉTIER (SUITE) - STATELESS SESSION BEAN System . out . println ( " ->" + test . getPrenom ()); } 78 } else { System . out . println ( " Echec de recherche ... " ); } } else if ( choice . equals ( " 3 " )){ System . out . println ( " Bye bye ... " ); isQuit = true ; } else { System . out . println ( " Choix incorrect ... " ); } 83 88 } } catch ( Exception e ) { System . err . println ( " Erreur : " + e ); System . exit (2); } 93 } } Listing 9.23 – Fichier ConsultatorClient.java 9.4.3 Compilation, déploiement et exécution A vous de jouer. Voici un exemple d’exécution : 3 8 13 18 23 LINUX :~/ Consultator > ./ manage . sh run # # Runnning client ... ClientContainer . info : Starting client ... 1. Rechercher par nom 2. Rechercher par prefixe ( sur le nom ) 3. Quitter > 1 Recherche par nom ... Entrez le nom : mosser recherche sur le nom : mosser -> sebastien 1. Rechercher par nom 2. Rechercher par prefixe ( sur le nom ) 3. Quitter > 2 Recherche par prefixe ... Entrez le prefixe : m recherche sur le prefix : m -> sebastien 1. Rechercher par nom 2. Rechercher par prefixe ( sur le nom ) 3. Quitter > 27 28 28 CHAPITRE 9. PREMIERS PAS J2EE 3 Bye bye ... 9.5 La partie présentation - servlet 9.5.1 Principe Après avoir construit plusieurs clients lourds, il nous semble essentiel de pouvoir offrir aux utilisateurs éventuels la possibilité de consulter les cartes depuis un client léger. Dans l’approche J2EE, les clients légers sont offerts par exemple par l’utilisation de servlet qui génèrent des pages html consultables depuis n’importe quel butineur web. 9.5.2 Ecriture de la servlet Les différents répertoires utilisés sont : – src : contient les sources de la servlet – bin/WEB-INF/ : contient les descripteurs de déploiements – bin/WEB-INF/classes : le code compilé de la servlet. La servlet implémentée réagira à une requête GET3 et son code est le suivant : 1 6 11 import import import import import import import import import import import import java . io . PrintWriter ; java . io . IOException ; java . util . Collection ; java . util . Iterator ; javax . servlet . ServletException ; javax . servlet . http . HttpServlet ; javax . servlet . http . HttpServletRequest ; javax . servlet . http . HttpServletResponse ; javax . servlet . http . HttpSession ; javax . naming . Context ; javax . naming . InitialContext ; javax . rmi . PortableRemoteObject ; import consultators .*; import carte_cmp .*; 16 public class Consult extends HttpServlet { protected void doGet ( HttpServletRequest req , HttpServletResponse res ) throws ServletException , IOException { res . setContentType ( " text / html " ); PrintWriter out = res . getWriter (); head ( out ); Object prefix = req . getParameter ( " prefix " ); look ( prefix , out ); foot ( out ); } 21 26 private void look ( Object p , PrintWriter out ) { 3 Il est possible de construire une servlet réagissant à une requête POST méthode doPost. en implémentant une 9.5. LA PARTIE PRÉSENTATION - SERVLET 31 36 41 46 51 56 61 66 71 if ( p == null ) { out . println ( " You must enter a name ! " ); return ; } String prefix = ( String ) p ; if ( prefix . equals ( " " )) { out . println ( " You must enter a name ! " ); return ; } // Initializing context Context initialContext = null ; try { initialContext = new InitialContext (); } catch ( Exception e ) { out . println ( " <h3 > Unable to find InitialContext ! </ h3 > " ); out . println ( e + " < br / > " ); return ; } // Connecting to the bean ConsultatorHome home = null ; try { home = ( ConsultatorHome ) PortableRemoteObject . narrow ( initialContext . lookup ( " MyConsultator " ) , ConsultatorHome . class ); } catch ( Exception e ) { out . println ( " <h3 > Unable to retrieve MyConsultator ! </ h3 > " ); out . println ( e + " < br / > " ); return ; } // Instanciation Consultator consult = null ; try { consult = home . create (); } catch ( Exception e ) { out . println ( " <h3 > Unable to create a Consultator " + " instance ! </ h3 > " ); out . println ( e + " < br / > " ); return ; } 76 81 // Doing the search ... Collection result = null ;; try { result = consult . rechercherPrefix ( prefix ); out . println ( " <h2 > Search Result for " + prefix + " </ h2 > " ); printResult ( result , out ); } catch ( Exception e ) { out . println ( " <h3 > Unable to invoke the Consultator ! </ h3 > " ); 29 30 CHAPITRE 9. PREMIERS PAS J2EE out . println ( e + " < br / > " ); return ; 86 } } 91 private void printResult ( Collection c , PrintWriter out ) throws Exception { if ( c . size () == 0) { out . println ( " < strong > No match found ! </ strong > " ); } Iterator i = c . iterator (); while ( i . hasNext ()) { CarteDeVisiteCMP card = ( CarteDeVisiteCMP ) i . next (); out . println ( " <h4 > " + card . getNom () + " " + card . getPrenom () + " </ h4 > " ); out . println ( " <ul > " ); out . println ( " <li > Telephone : " + card . getPhone () + " </ li > " ); out . println ( " <li > Email : " + card . getEmail () + " </ li > " ); out . println ( " </ ul > " ); } 96 101 106 } 111 private void head ( PrintWriter out ) { out . println ( " < html > < head > < title > Card Consultation Form </ title > " + " </ head > " ); out . println ( " < body > " ); out . println ( " <h1 > Looking for Someone ? </ h1 > < br > " ); out . println ( " < form method =\" get \" > " ); out . println ( " <p > " ); out . println ( " Name : & nbsp ; " + " < input type =\" text \" name =\" prefix \" / > " ); out . println ( " < br / > < input type =\" submit \" value =\" Search !\"/ > " ); out . println ( " </p > " ); out . println ( " </ form > " ); } 116 121 private void foot ( PrintWriter out ) { out . println ( " </ body > " ); out . println ( " </ html > " ); out . close (); } 126 131 } Listing 9.24 – Fichier Consult.java Descripteur J2EE Le descripteur permet de décrire l’URL associée à la servlet ainsi que les liens avec les beans utilisés. <? xml version ="1.0" encoding =" ISO -8859 -1"? > 9.5. LA PARTIE PRÉSENTATION - SERVLET 4 9 14 19 31 <web - app xmlns =" http :// java . sun . com / xml / ns / j2ee " xmlns : xsi =" http :// www . w3 . org /2001/ XMLSchema - instance " xsi : schemaLocation =" http :// java . sun . com / xml / ns / j2ee http :// java . sun . com / xml / ns / j2ee / web - app_2_4 . xsd " version ="2.4" > < servlet > < servlet - name > CardManager </ servlet - name > < servlet - class > Consult </ servlet - class > </ servlet > < servlet - mapping > < servlet - name > CardManager </ servlet - name > <url - pattern >/ card / consult </ url - pattern > </ servlet - mapping > <ejb - ref > <ejb - ref - name > Consultator </ ejb - ref - name > <ejb - ref - type > Session </ ejb - ref - type > < home > consultators . ConsultatorHome </ home > < remote > consultators . Consultator </ remote > <ejb - link > Consultator . jar </ ejb - link > </ ejb - ref > </ web - app > Listing 9.25 – Fichier web.xml Descripteur Jonas Ce descripteur décrit les liens avec le session bean utilisé. 3 8 <? xml version ="1.0" encoding =" ISO -8859 -1"? > < jonas - web - app xmlns =" http :// www . objectweb . org / jonas / ns " xmlns : xsi =" http :// www . w3 . org /2001/ XMLSchema - instance " xsi : schemaLocation =" http :// www . objectweb . org / jonas / ns http :// www . objectweb . org / jonas / ns / jonas - web - app_4_0 . xsd " > < jonas - ejb - ref > <ejb - ref - name > Consultator </ ejb - ref - name > < jndi - name > MyConsultator </ jndi - name > </ jonas - ejb - ref > < host > localhost </ host > < context - root > web - application </ context - root > </ jonas - web - app > Listing 9.26 – Fichier jonas-web.xml 9.5.3 Compilation et déploiement Les servlets sont chargées comme des applications web, et doivent être packagées comme telles. Ainsi, on archivera le contenu du répertoire bin dans un fichier war (pour Web Archive) et non dans un jar (Java Archive)4 . 4 Un fichier .war se génère comme un fichier .jar en utilisant la commande jar 32 CHAPITRE 9. PREMIERS PAS J2EE Compilation La servlet accède au session bean et entity bean via son interface remote qu’il faut donc copier dans le répertoire src. TODO pourquoi aussi l’entity bean ? TODO pourquoi pas via un proxy ? 3 LINUX :~ > javac - cp $JONAS \ _ROOT / lib / commons / j2ee . servlet . jar -d bin / WEB - INF / classes src /* ja LINUX :~ > cp web . xml jonas - web . xml bin / WEB - INF /. LINUX :~ > cd bin LINUX :~/ bin > jar cf ../ Consult . war * LINUX :~/ bin > cd .. Chargement LINUX :~ > mv Consult . war > JONAS_ROOT / webapps /. LINUX :~ > cd $JONAS_ROOT / webapps LINUX :~ > jonas admin -a Consult . war Exécution Il suffit d’ouvrir son butineur préféré, et de se rendre à l’adresse décrite dans le fichier de mapping : http://localhost:9000/web-application/card/consult. 9.6 Pour aller plus loin – Ecrire une servlet pour l’administration de l’annuaire. – Utiliser la sécurité J2EE – Utiliser les transactions