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