Java EE Guide De Développement D'applications Web En
User Manual:
Open the PDF directly: View PDF .
Page Count: 507 [warning: Documents this large are best viewed by clicking the View PDF Link!]
- Java EE.pdf
- 01-Java EE.pdf
- 02-Avant propos
- 03-Organisation du guide
- 04-À qui s’adresse ce guide
- 05-Les conventions
- 06-Définitions de J2EE Java EE
- 07-Encodage des applications Java
- 08-Les architectures Web
- 09-Mise en place de l’environnement
- 10-Installation du serveur d’applications Java EE (Tomcat)
- 11-Installation de l’environnement de développement (IDE) Eclipse
- 12-Qu’est ce que Tomcat
- 13-Installation de Tomcat
- 14-Coupler Tomcat et le serveur Web Apache
- 15-Architecture et configuration de Tomcat
- 16-Rappels XML
- 17-Les fichiers de configuration Tomcat
- 18-Le fichier de configuration des applications
- 19-Le fichier de configuration des utilisateurs
- 20-Le fichier de configuration de la sécurité
- 21-Arborescence d’un projet Tomcat
- 22-Analyse, monitoring et supervision
- 23-ApacheTomcat et SSL HTTPS
- 24-Qu’est ce qu’une JavaServer Page
- 25-Déclarations, commentaires et scriptlets
- 26-Les objets implicites
- 27-Premières JSP simples
- 28-Gérer les exceptions et erreurs en JSP
- 29-Bibliothèque de tags JSTL
- 30-Bibliothèque de balises personnalisées
- 31-Les JavaBeans ou Beans
- 32-Transfert de contrôle
- 33-Travailler avec des fichiers et répertoires
- 34-Qu’est ce qu’une Servlet
- 35-Le projet BetaBoutique
- 36-Première Servlet
- 37-Servlet authentification
- 38-Interface ServletConfig
- 39-Interface ServletContext
- 40-Traitement des requêtes
- 41-Traitement des réponses
- 42-Synchronisation des traitements
- 43-Etat des clients
- 44-Les filtres
- 45-Interface RequestDispatcher
- 46-Introduction au modèle MVC
- 47-Gestion des exceptions, erreurs et page d’accueil
- 48-En résumé
- 49-Travailler avec une base de données
- 50-Partage de connexions
- 51-Ecouteurs listeners et cycle de vie
- 52-Sources de données et pools de connexion
- 53-Bases de données et MVC
- 54-Classe modèle
- 55-Modèle et JavaBean
- 56-Les transactions
- 57-Multilingue et JDBC
- 58-Authentification et Realms
- 59-Framework
- 60-ApacheStruts
- 61-Projet Web
- 62-Formulaires Struts
- 63-Vues et Struts
- 64-Les validations et vérifications de données
- 65-Le contrôleur Struts
- 66-Développement du module d’administration
- 67-Web 2.0
- 68-En résumé
- 69-Gestion des traces et des logs
- 70-Ant Another Neat Tool
- 71-Déployer un projet Java EE
- 72-Optimisation de la mémoire
- 73-En résumé
Ce livre sur le développement d’applications web en Java s’adresse à tout développeur qui souhaite disposer de tous les détails des différentes
étapes de réalisation d’une application web :
l’
analyse, la modélisation, le codage, la mise en
production
, les tests et la maintenance
.
Le livre suit une démarche progressive et s’appuie sur une étude de cas d’un développement d’une
boutique de vente en ligne
. Il est découpé en
sept chapitres progressifs qui peuvent également être étudiés de manière autonome.
Le premier chapitre présente le langage Java, explique les règles de nommage et les bonnes pratiques à adopter lors des développements de
projets Java EE. Le chapitre 2 est consacré à la mise en place du serveur Java EE de référence, Tomcat, sous Windows et Linux. Les chapitres 3
et 4 explorent en détail les servlets et les JavaServer Page (JSP), en application avec l’étude de cas et le modèle
MVC
. Le chapitre 5 présente les
bases de données en Java EE et détaille la mise en place de
JDBC
et des technologies associées. Le chapitre 6 concerne le développement
Java EE à l’aide d’un framework. En accord avec les standards actuels en entreprise, Struts a été utilisé pour ce livre. Cependant les explications
sont valables pour d’autres frameworks Java (description des outils proposés par un framework Java tant en terme de validation de données que
d’approche
MVC II
).
Enfin, le dernier chapitre est consacré aux techniques avancées Java EE et permet de
déployer
un véritable projet sur un serveur en production à
partir d’un nom de domaine.
Le code lié à l’étude de cas traitée dans le livre est en téléchargement sur cette page.
L’auteur propose à ses lecteurs un lieu d’échanges via le site www.gdawj.com qui apporte également un certain nombre d’éléments
complémentaires (FAQ, outils, application déployée...).
Ce livre numérique a été conçu et est diffusé dans le respect des droits d’auteur. Toutes les marques citées ont été déposées par leur éditeur respectif. La loi du 11 Mars
1957 n’autorisant aux termes des alinéas 2 et 3 de l’article 41, d’une part, que les “copies ou reproductions strictement réservées à l’usage privé du copiste et non destinées
à une utilisation collective
”
, et, d’autre part, que les analyses et les courtes citations dans un but d’exemple et d’illustration, “toute représentation ou reproduction intégrale,
ou partielle, faite sans le consentement de l’auteur ou de ses ayants droit ou ayant cause, est illicite
”
(alinéa 1er de l’article 40). Cette représentation ou reproduction, par
quelque procédé que ce soit, constituerait donc une contrefaçon sanctionnée par les articles 425 et suivants du Code Pénal. Copyright Editions ENI
JavaEE
Guide de développement d'applications web en Java
JérômeLAFOSSE
Résumé
L'auteur
Ingénieur en informatique et diplômé du CNAM, Jérôme Lafosse intervient comme consultant, concepteur et formateur sur les technologies
Java. Spécialiste des technologies web, il travaille à promouvoir les outils et solutions Open Source pour le développement de projets
Internet. Il enseigne également la plate-forme Java Entreprise Edition et la conception de projets Web en Licence et Master. Son expérience
pédagogique s'allie à ses compétences techniques et offrent au lecteur un guide réellement opérationnel sur le développement
d'applications web en Java.
- 1 -© ENI Editions - All rigths reserved
Avantpropos
LaréalisationdesitesWebpassepardifférentesétapes :l
’
analyse,lamodélisation,lecodage,lamiseenproduction,
lestestsetlamaintenance.Toutescesphasesdeconceptionsontlongues,complexesetdoiventêtremaîtriséesen
détail pour mener à bien les projets Internet. Pour ce type de projet, différents langages de programmation sont
utiliséscommePHP,Ruby,Perl,.NETouJava.Javaestreconnuactuellementcommel
’
undesmeilleurslangagesde
programmationobjetpourlaréalisationdeprojetsInternetcomplexesavecsonAPIspécifique,JavaEE.
Ce guide détaillé suit une démarche progressive et vous aidera à créer des applications Web complexes et
fonctionnelles.Touslesconceptsdelacréationd
’
unprojetprofessionnelsontabordésdanscelivre,delapriseen
maindulangage,àl
’
installationdel
’
environnement,àlaconfigurationd
’
unserveurWebJavajusqu
’
àlacréationetla
miseenproductiond
’
unprojet.Cetouvragepermetdepercevoirledéveloppementd
’
applicationsWebenJavadans
saglobalité.
Monobjectifestdefournirunguidecompletdedéveloppementd
’
applicationsWebenJavasurlesdeuxprincipaux
environnements de développement que sont Windows et Linux, sans faire l
’
impasse sur une partie du cycle de
développement.Ils
’
agitd
’
explicationsetdeconseilsconcrets,illustrésparuneétudedecasréalistedeboutiquede
venteenligne.
Les lecteurs sont d
’
ailleurs invités à échanger via le site www.gdawj.com, véritable complément du livre puisqu
’
il
propose un exemple de l
’
application déployée, un forum de questions, des outils complémentaires pour le
développementd
’
applicationsweb,etc.
- 1 -© ENI Editions - All rigths reserved
Organisationduguide
Leguideestdiviséenseptchapitresspécifiquesetautonomes :
●Lechapitre1(ObjectifsetspécificationsdeJavaEE)présentelelangageJava,lesrèglesdenommageainsi
quel
’
installationdel
’
environnementdedéveloppement.
●Lechapitre2(Leserveurd
’
applicationsApache
Tomcat)estconsacréàlamiseenplaceduserveurJavade
référence,Tomcat.
●Le chapitre 3 (Les JavaServer Page) aborde la programmation de Servlets avec les classes, objets et
méthodes.
●Lechapitre4(LesServlets)exploreendétailledéveloppementdepagesWebautraversdespagesJSP.
●Lechapitre5(Javaetlesbasesdedonnées)présentelesbasesdedonnéesenJavaainsiquelessolutions
techniquesetlesoutilsadaptésàlapersistancedesdonnées.
●Le chapitre 6 (Framework Java EE) est consacré à l
’
étuded
’
un framework de développement Java nommé
Struts.
●Lechapitre7(Techniquesavancées)estdédiéauxtechniquesavancéesendéveloppementJavaEE.
- 1 -© ENI Editions - All rigths reserved
Àquis
’
adresseceguide?
QuevousayezpeudeconnaissancesendéveloppementWebouquevoussoyezunexpertJavaEE,ceguideapour
objectifdevousprésenterendétailetdefaçonexhaustive,touteslesétapesderéalisationd
’
applicationsInternetà
partird
’
unprojet concretmais facilementportable. Aucours demes étudeset demon parcoursprofessionnel, j
’
ai
étudiéleslangagesduWeb,l
’
installationd
’
unserveurJava,lamiseenœuvred
’
unframework,lesbasesdedonnées
pourInternet,lesServlets/JSPetledéveloppementdeprojetsmaisaucunouvrageneregroupaittouscesaspectsde
conceptionetcertainespartiesimportantesn
’
étaientpasdétaillées,commes
’
ilmanquaitquelquesclésessentiellesqui
nes
’
acquièrentqu
’
avec l
’
expérience.Ladémarche pédagogique deceguide est denepas faire l
’
impassesurdes
pointsessentielsd
’
unprojetWebcommeledéploiementsurunserveurenproductionavecunnomdedomaine,la
miseenplaced
’
unpooldeconnexionsurunebasededonnéesquelconqueouencorelaconfigurationcomplèted
’
un
serveurJava.L
’
ouvrageutilisepourcelaunemiseenpageadaptéeafindemettreenvaleurlesconceptsessentielsà
partirdeschémas,graphiques,etc.
- 1 -© ENI Editions - All rigths reserved
Lesconventions
1.Lesconventionsduguide,deJava,decodageetlesrèglesdenommage
Danslecycledevied
’
unproduitlogiciel,laphasedemaintenancereprésentelamajeurepartiedutemps(environ80
%).Demême,unlogicielestrarementdéveloppéparuneseuleetmêmepersonne.C
’
estuneéquipeentièrequi
réaliseleprojetdedéveloppementavectouslesavantagesmaisaussitouteslescontraintesquecelaimplique.
Laréussited
’
unprojetdépendbeaucoupdel
’
homogénéitédanslecodage.Cetteétapeessentiellepasseparlamise
enœuvredeconventionsstrictesrespectéespartoutel
’
équipeimpliquéedansleprojet.Laplupartdesoutilsde
développement(IDE)proposentdesfonctionnalitéspourpermettrecettehomogénéitémaisilexistedesrèglesque
seuls les développeurs doivent appliquer. Le fait de vouloir à tout prix avoir un code esthétique et de faible
complexitépeutêtreunfreinauxperformances.Àl
’
inverse,lacourseàlaperformancepeutdécoulersuruncodepeu
lisible.Ilrevientdoncauxdéveloppeursdetrouverlebonéquilibreentrelesconventionsetlescontraintesduprojet
(projetaxésurlaperformance,projetOpenSource...).
2.Lesconventionsduguide
Toutaulongdeceguideserontutiliséesdesconventionspourlarédactiondesexplications,lespartiesdecode,les
rappels et les résumés. Les explications seront rédigées sous format textuel et graphique avec un maximum de
clarté.Lespartiesdecodeserontbiendifférenciéesparlapolicedecaractèresetserontencadrées.Lesschémas
viendrontappuyeruneexplicationetpermettrontd
’
expliquersousformegraphiqueuneconvention,unchoix,une
spécificationouunexemple.
3.Lesconventionsdecodage
Desconventionsdecodagesontutiliséestoutaulongdecedocument.Pardéfaut,lesconventionsdecodagepourla
plupartdesprojetsOpenSourcesuiventcesinstructions.Parexemple,silaparenthèse{estaprèslacondition
if
,le
coden
’
estpascorrect.
Touslesblocsdecodedoiventcommencerparunenouvelleligne.
public class MaClasse
{
public void maMethode()
{
if (xxx)
{
}
}
}
Chaqueconditionnellecontientlesparenthèsesouvrantesetfermantes.
//Correct
if (expression)
{
//le code
}
//Incorrect
if (expression)
//le code
Uneindentationsetrouveaprèschaqueinstruction.Lesnomsdesfonctionsetdesparamètres,nedoiventpasavoir
depréfixe,commencentparuneminusculeetchaquepartiedemotestenmajuscule.
public class MaClasse
{
private String maChaine;
public void maMethode(String monParametre)
{
}
- 1 -© ENI Editions - All rigths reserved
}
Laversiondel
’
application(ducode)estpréciséedanschaquefichierd
’
extension
.java
.
@version 2.8
Lenomdel
’
auteurestprécisé.
@author jeromelafosse
Chaqueimportationdepaquetage(paquet)Javaestpleinementqualifiée.
//Correct
import java.util.Date;
import java.net.HttpURLConnection;
//Incorrect
import java.util.*;
import java.net.*;
4.LesconventionsJava
Pour utiliser efficacement le langage de programmation Java, il existe plusieurs conventions à connaître et à
appliquer.LesinstructionsJavaseterminentparunpoint
virgule.LesinstructionsJavautilisentdesaccolades{}
pourindiquerledébutetlafinducorps.Uncorpspeutcontenirplusieursinstructions.
Laprésentationaveclesaccoladesalignéessurlamêmecolonnequelepremiercaractèredel
’
instructionJavasera
utilisée.Cetteprésentationajoutedeslignesdecode,maiselleestplusfacileàlire.Ilestimportantdemettreen
retrait(espaceoutabulation)lesinstructionsJavaquicomportentuncorps.Ilestégalementnécessairedetoujours
utiliserlamêmemiseenretraitducode.
Javarecourtcommetoutlangageàplusieursmots
clés,c
’
est
à
diredesmotsréservésexclusivementaulangage.Il
neseradoncpaspossibled
’
utilisercesmots
cléscommenomsouvaleursdevariables.
Voiciunelistenonexhaustivedecesmots
clés :abstract
,
boolean
,
break
,
case
,
catch
,
char
,
class
,
continue
,
do
,
double
,
else
,
extends
,
false
,
final
,
finally
,
float
,
for
,
if
,
import
,
instanceof
,
int
,
interface
,
new
,
null
,
package
,
private
,
protected
,
public
,
return
,
static
,
super
,
switch
,
this
,
true
,
try
,
void
,
while
...
Lecodedoitêtresuccinct,celafacilitelamaintenanceetlalisibilitédel
’
ensemble.Ilvautmieuxdécouperparfoisdes
méthodesetajouterdescommentaires.Lesrecommandationsdoiventêtreappliquéessurl
’
intégralitéduprojetet
non avec parcimonie. Si les règles sont plus ou moins appliquées, si le code change d
’
un fichier à l
’
autre, la
compréhensionseradifficile.L
’
applicationuniformedesrèglesestungagedemaintenabilité.
Les noms des fichiers sources portent l
’
extension
.java
et le code (
bytecode
) généré porte l
’
extension .class.Les
fichiers de propriétés portent l
’
extension
.properties
(langues, traductions...) et les fichiers de configuration
l
’
extension.xml.Lefichierdeconstructionduprojetportelenom
build.xml
(Ant)etlefichierdedescriptionduprojet
estappeléREADME
.
Unprojetdedéveloppementutiliseplusieursrépertoires.Ilestgénéralementcomposédurépertoire/srcquicontient
lessources, du répertoire
/build
qui contient les classes compilées,
/docs
qui contient la documentation du projet
et
/log
quicontientlestracesd
’
exécutionsetd
’
erreursduprojet.
Souvent,pourunprojetminime,seulslesrépertoires/src
,
/build
et
/docs
sontutilisés.
Lesclassesdoiventêtreregroupéesenpackages(paquetagesoupaquets).
Ilestrecommandédenepasdépasser2000lignesdecodeparfichier.Sitelestlecas,ilestimportantd
’
optimiserle
code,devérifiers
’
iln
’
existepasderedondanceetdedécouperlesfonctionnalitésenplusieursclasses(boîteàoutils
parexemple).
Ilfautconfigurer l
’
éditeurpourquelatabulationécrivehuitcaractèresespace(configurationpardéfaut).L
’
entrée
dansunblocimposel
’
ajoutd
’
uneindentation.Desblocsdemêmeniveaudoiventdébutersurlamêmecolonne,c
’
est
à
direavoirlamêmeindentation.Leslignesblanchesdoiventêtreutiliséespourséparerdesportionsdecodeetles
méthodes.Lesespacespeuventêtreutilisésenquantitémaissouscertainesconditions.Lecaractèreespaceest
Fichiers
Sources
Formatage
- 2 - © ENI Editions - All rigths reserved
proscritavantlespoints
virgules,avantlescrochetsdestableauxetentreunevariableetlesopérateursdepré/post
incrément.Parcontre,l
’
espaceestautoriséaprèslesvirgules,avantetaprèslesaccolades,avantetaprèschaque
opérateur,aprèslesmotsréservésdulangageetentrelenomd
’
uneméthodeetlaparenthèsedesesparamètres.
//Correct
maMethode (a, b, c, d);
for (i = 0; i < 100; i++) {
++count;
(MaClasse)maVariable.get(i);
//Incorrect
MaMethode (a,b,c,d);
++ count;
(MaClasse) maVariable.get(i);
Lesnomsutilisésdoiventêtreexplicites,c
’
est
à
direquelenomdoitexpliquerlecontenudel
’
objet,lerôledela
méthode...Lesacronymesquiapparaissentdanslesnomsdoiventêtrepassésenminuscules(sauflapremièrelettre
dupremiermot).Ilestconseillédemettrelesidentifiantsenlangueanglaisepourdesprojetsinternationaux.
Lesnomsdespaquetagesdoiventêtreenminuscules.Cesnomsdoiventêtrepleinementqualifiés(commeuneURL)
et reprennent le nom du projet, l
’
URL du site... En général, la technique consiste à retourner l
’
URL du projet.
Exemple :monprojet.comdevient :com.monprojet.monpaquet
//Correct
package com.monprojet.monpaquet;
//Incorrect
package Com.MonProjet.MonPaquet;
Lesnomsdesclassesdoiventêtreenminuscules,hormislesinitialesdesmotsquilescomposent.
//Correct
class MaClasseFavorite;
//Incorrect
class maclassefavorite;
class maClassefavorite;
Lesnomsdesméthodesdoiventêtreenminusculeshormislesinitialesdesmotsquicomposentlesmots(saufla
premièrelettre).
//Correct
public void maMethodeFavorite() {
//Incorrect
public void mamethodefavorite() {
Lesaccesseursdirects(gettersetsetters)desattributsd
’
uneclassedoiventêtrepréfixésd
’
ungetpourlalecturede
l
’
attributetd
’
unsetpourl
’
écriture.Lepréfixeisdoitêtreutilisépourlesméthodesquiretournentunbooléen.
//Correct
public int getNiveau() {
public void setNiveau(int niveau) {
public boolean isVisible() {
//Incorrect
public int recupererLeNiveau() {
public void ecrireLeNiveau(int niveau) {
public boolean estIlVisible() {
Nouspouvonségalementutiliserd
’
autresmotspourlesrecherches,lessuppressions,lesajouts,lafermeturede
connexions...(
find
,
delete
,
add
,
close
...).
Nommage
Package/Paquetage
Classesetinterfaces
Méthodes
- 3 -© ENI Editions - All rigths reserved
Lesattributsdesclasses,lesvariablesainsiquelesparamètresdesméthodesdoiventêtreenminusculeshormisles
initialesdesmotsquilescomposent(sauflepremier).Lesvariablesdeboucledoiventporteruneseulelettre :
i
,
j
,
k
...
Lessignesdollars($)etsoulignement(_)sontproscrits.
//Correct
Voiture prochaineVoiture = voitures.get(this.id + 1);
float laTaille = 145.5;
//Incorrect
Voiture a = voitures.get(this.id + 1);
float la_Taille = 145.5;
Lescollectionsdoiventêtrenomméesaupluriel.
//Correct
Vector comptes;
Collection banques;
Object[] mesObjets;
//Incorrect
Vector compte;
Collection banque;
Object[] monObjet;
Lesnomsdesconstantesdoiventêtreécritsentièrementenmajuscules.Leséparateurdemotestlecaractèrede
soulignement(underscore).
//Correct
static final int LOG_CONSOLE = 1;
//Incorrect
static final int LOGCONSOLE = 1;
static final int console_Log = 1;
static final int Console_LOG = 1;
Lescommentairessontessentielsdansuncodesource.Ilspermettentdedocumenterleprojetàl
’
intérieurmêmedu
code source en vue de la génération de la documentation via l
’
outil JavaDoc. Il existe en Java deux types de
commentaires :
●lescommentairesmono
lignequipermettentdedésactivertoutcequiapparaîtsurlamêmeligne//;
●les commentaires multilignes qui permettent de désactiver tout le code qui se trouve entre les deux
délimiteurs/**/.
IlestimportantderéserverlescommentairesmultilignesauxblocsutilesàlaJavaDocetàl
’
inactivationdeportions
de code. Les commentaires mono
ligne permettent de commenter le reste, à savoir, toute information de
documentationinterneauxlignesdecode.
/*
* La classe MaClasse permet telles fonctionnalités …
*/
public class MaClasse() {
// Recuperer un objet de la collection
monFichier = (Fichier)fichiers.get((int)item.getIdFichier());
Lesvariablesdoiventêtredéclaréesligneparligne.L
’
initialisationdoitsefairelorsdeladéclarationlorsquecelaest
possible.Lesvariablesdoiventêtredéclaréesauplustôtdansunblocdecode.Lesnomsdesméthodessontaccolés
àlaparenthèseouvrantelistantleursparamètres.Aucunespacenedoityêtreinséré.
//Correct
int niveau = 10;
void maMethode() {
Attributs,variablesetparamètres
Constantes
Commentaires
Déclarations
- 4 - © ENI Editions - All rigths reserved
//Incorrect
int niveau;
niveau = 10;
void maMethode () {
L
’
ordrededéclarationdesentitésducodesourcedoitêtrelesuivant(quiestplusoumoinsnaturel) :
●Lesattributsdelaclasse(1
>statiques,2
>publiques,3
>protégés,4
>privés).
●Lesméthodesdelaclasse(1
>statiques,2
>publiques,3
>protégées,4
>privées).
Unelignedecodenepeutcontenirqu
’
uneseuleinstruction.
//Correct
count++;
i--;
println("Bonjour");
//Incorrect
count++; i --; println("Bonjour");
Ordre
Instructions
- 5 -© ENI Editions - All rigths reserved
DéfinitionsdeJ2EE/JavaEE
DenombreusespossibilitésexistentpourréaliserdesapplicationsInternetdepuisplusieursannées.Deslangagesont
étécréés,desarchitecturesetdesenvironnementsdetravailontétéconçuspourrépondreauxbesoinsetfaciliterla
tâchedesdéveloppeurs.Sun(leconcepteurdeJava)adoncmisenplaceunensembledetechnologiespourréaliser
desapplicationsWeb.CestechnologiessontregroupéessouslenomJ2EE(Java2EntrepriseEdition),désormaisJava
EE.
Depuislaversion5,lechiffre2adisparupourfaciliterlacompréhensiondelaversionetnepasmélangerle
chiffre2aveclenumérodeversion.
La plate
forme Java EE s
’
appuie entièrement sur le langage Java. Java EE est donc une norme, qui permet à des
développeurs,entreprisesetSSIIdedévelopperleurpropreapplicationquiimplémenteentotalitéoupartiellementles
spécificationsdeSUN.Ensimplifiant,ilestpossibledereprésenterJavaEEcommeunensembledespécificationsd
’
API,
unearchitecture,uneméthodedepackagingetdedéploiementd
’
applicationsetlagestiond
’
applicationsdéployéessur
unserveurcompatibleJava.
Une API est un ensemble de librairies ou bibliothèques de fonctions destinées à être utilisées par les
programmeursdansleursapplications.
Java Entreprise Edition est destiné aux gros (très gros) systèmes d
’
entreprises. Les librairies utilisées fonctionnent
difficilement sur un simple PC et requièrent une puissance beaucoup plus importante (notamment au niveau de la
mémoire).
JavaEntrepriseEditionestapparueàlafindesannées90.CetteévolutionapporteaulangageJavauneplate
forme
logicielle robuste et complète pour le développement. La plate
forme Java EE a souvent été remise en cause, mal
utiliséeetmalcomprise.DesoutilsOpenSourcesontvenuslaconcurrencer.Cesremarquesetlaconcurrenceontpermis
àSund
’
améliorersonproduitetd
’
éditerdesversionsdeplusenplusabouties.JavaEEneremplaceenaucuncasJ2SE.
Aucontraire,J2SEestlabasedeJavaEEquiestpluscompletetquiestaxésurleWeb.Laplate
formeJ2SEoffredes
outilsdedéveloppementd
’
applicationsclient/serveur,applicationsgraphiquesfenêtréesetApplets.
Laplate
formeJ2SEestcomposéedesélémentssuivants :
●Lamachine virtuelleJava (JVM):permetd
’
exécuterdesapplicationsJava.Elleconstitueunepasserelleet
permetuneportabilitéentrelesarchitectures(Windows,Linux,Mac...).
●LabibliothèquedeclassesJava:unensembledecomposantslogicielsprêtàl
’
emploi.
●
Les outils de développement
: le compilateur javac , un interpréteur Java nommé java, le générateur de
documentationjavadoc,laconsoledesupervisationJconsole...Laplate
formeJavaEEestuneextensiondela
plate
formeJ2SE.Ellepermetundéveloppementd
’
applicationsquivonts
’
exécutersurunserveurd
’
applications.
Lesapplicationsserontutiliséespardesclientslégers(commedesnavigateursWeb)oubiendesapplications
lourdes(IHM).LadernièreversionstabledeJavaEEestlaversionJavaEE5.0etfonctionneavecleJDK5.0et
6.0.
1.PourquoichoisirJavaEE
Ilexisteactuellementbeaucoupd
’
autresplates
formesdedéveloppementquisontbaséessurd
’
autreslangages(C#,
PHP5,.NET...).Lesprincipauxavantagesd
’
utiliserJavaEE(etdoncJava)sontlaportabilité,l
’
indépendance,lasécurité
etlamultitudedelibrairiesproposées.
Ledéveloppementd
’
applicationsd
’
entreprisenécessitelamiseenœuvred
’
uneinfrastructureimportante.Beaucoup
defonctionnalitéssontutiliséesetdéveloppées,lebutétantdeproduiredesapplicationssûres,robustesetfacilesà
maintenir.Certains servicessont d
’
ailleurs récurrents comme : l
’
accèsaux bases dedonnées, l
’
envoi de mails, les
transactions,lagestiondefichiers,lagestiond
’
images,letéléchargement,lechargementouupload,lasupervisiondu
système...
C
’
estpourcelaquel
’
architectureJavaEEestintéressantecartouslesélémentsfondamentauxsontdéjàenplace.
Pasbesoindeconcevoir unearchitecture,deslibrairies etdesoutilsspécialementadaptés. Cela nécessiteraitun
tempsetuninvestissementconsidérables.
Enfin, la plate
forme Java EE est basée sur des spécifications, ce qui signifie que les projets sont portables sur
n
’
importe quel serveur d
’
applications conforme (Tomcat, JBoss, WebSphere...) à ces spécifications. Cette
implémentationestgratuiteetpermetdebénéficierdelatotalitédel
’
APIsansinvestissement.Laplate
formeJavaEE
est la plus riche des plates
formes Java et offre un environnement standard de développement et d
’
exécution
d
’
applicationsd
’
entreprisemulti
tiers.
- 1 -© ENI Editions - All rigths reserved
Lefait que JavaEE soit standardiséa contribué àson adoption parde très nombreuxéditeurs de logiciels/outils
informatique. Ces éditeurs associés à Sun Microsystems font partie du JCP
(
Java Community Process). Le Java
CommunityProcessregroupelesentreprisessuivantes :Sun,IBM,Oracle,Borland,Nokia,Sony,lafondationApache,
ObjectWeb...L
’
objectifduJCPestdedéfinirlesspécificationsdestechnologiesbaséessurJava.
ChaquedemandedemodificationestappeléeuneJSR
(
JavaSpecificationRequest
).
2.L
’
APIJavaEE(JDBC,Servlets,JSP)
Laplate
formeJavaEEestcomposéedeplusieursAPI(ensembledelibrairesetservices).JavaEEfaitintervenirtrois
typesdecomposantslogiciels(Servlets,JSP,EJB).
a.LesServlets
L
’
APIServletfournitlesélémentsnécessairesàlaconceptiondecomposantsWebdynamiquesaveclelangageJava.
LesServletssontdescomposantslogicielsentièrementécritsenJava.LesServletseffectuentdestraitementscôté
serveuren réponseauxrequêtes desclients distants.Une Servlet estchargée enmémoire lors deson premier
appel.Demême,iln
’
existequ
’
uneseuleinstanced
’
uneServletenmémoire,leserveurutilisealorsunthreadglobal
pourtraiterlesdemandesémisesparlesclients.
UneServletestuneclasseJava.CetteclassedoitêtrechargéepuisinterprétéeparunemachinevirtuelleJava(celle
duserveurd
’
applications).LaServletestalorsprêteàrecevoirdesrequêtesetàrenvoyerdesréponses.Lorsque
l
’
applicationouleserveurs
’
arrête,laServletestdétruite,puissoninstanceestnettoyéeparleramasse
miettesde
lamachinevirtuelle.
LesServletspermettentdedévelopperdespagesdynamiques,dontlecontenuestcrééàlavoléesurdemande.
C
’
estlecasparexemple,lorsqu
’
unclientsouhaiteobtenirlalistedesarticlesd
’
uneboutiquepouruneplagedeprix.
LespagesHTMLsontalorsgénéréesdynamiquementenfonctiondecritèresspécifiques(prix,dates,recherches...).
UneServletestuncomposantJavaquiimplémentel
’
interfacejavax.servlet.Servlet.Cetteinterfacepermetdegérer
lesrequêtesduclient,dirigéesverslaServletenquestion.LeserveurreçoitunedemandeadresséeàuneServlet
souslaformed
’
unerequêteHTTP.IltransmetalorslarequêteàlaServletconcernéeparletraitementpuisrenvoie
laréponsefournieparcelle
ciauclient.LaServletestgéréeparleconteneurdeServletsJavaEE.Lorsqueleserveur
Web reçoit la requête du client, il charge la Servlet (si elle n
’
est pas encore chargée) et invoque l
’
interface
javax.servlet.Servletafindesatisfairelarequête.
Cycledevied’uneServlet
- 2 - © ENI Editions - All rigths reserved
Ce type de programmation est très proche du développement CGI mais offre les outils pour gérer les cookies,
sessions,accèsauxbasesdedonnéesetautresavecuneexcellenteportabilité.
ExempledeServletsimple :
public class PremiereServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, java.io.IOException
{
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, java.io.IOException
{
doGet(request, response);
}
}
b.LesJSP(JavaServerPage)
L
’
APIJSPpermetdedévelopperdespagesWebdynamiquesrapidementàpartird
’
unsqueletteHTMLetdedonnées
incluses directement dans chaque page. Les JSP permettent d
’
insérer des bouts de code Java (scriptlets)
directementdanslecodeHTML.
Dupointdevuedelastructure,uneJSPesttrèsproched
’
unepagePHPoubienASP.UnepageJSPestunfichierqui
portel
’
extension.jspou.jspf(pourlesfragmentsdecode).
Lors de la création de Servlets, le but est de construire des composants capables de produire un service
(essentiellementducodeHTML).Toutefois,ceprincipeestparfoiscomplexepourdespersonnesquinesontpas
habituéesàlaprogrammationobjetetaucode100%Java.C
’
estpourcesraisonsquelesdéveloppeursdechez
SUNontinventéJSP.
LapageJSPesttransforméeenclasseJavapuiscompiléeenServletparleserveurd
’
applications.Cetraitementest
réaliséparleserveurd
’
applicationsaupremierappeldelapageetàchaquefoisquecettepageestmodifiéeparun
programmeur.
C
’
est cette étape qui nécessite un serveur d
’
applications Java EE, un compilateur Java et qui par conséquent
nécessitepourlamajoritél
’
installationdeJavaavecunJDKplutôtqu
’
unJRE.
Cycledevied’uneJSP
- 3 -© ENI Editions - All rigths reserved
Lerésultatdelacompilation(essentiellementducodeHTML)estrenvoyéauclient.Cettetechnologieestsimple,
légère et rapide. Les développeurs de pages JSP peuvent ainsi mélanger du contenu statique et du contenu
dynamique.
<%
System.out.println("Ma première Servlet");
out.println("<h1>Ma première Servlet<h1>");
%>
Ilestdoncpossibled
’
avoirdeséquipesdedéveloppementséparéesavecunepersonnespécialistedeHTML/XHTML
etdudesignetunprogrammeurJavaquiréaliselesscriptlets.LesJSPsontexécutéessouslaformedeServlets,
ellesdisposentdoncdesmêmesfonctionnalitésquecelles
cietpeuventainsimanipulerlessessions,lesbasesde
données,lesmails...
- 4 - © ENI Editions - All rigths reserved
c.LESEJB(EntrepriseJavaBean)
LesEJBsontdescomposantsmétierdistribués,c
’
est
à
direqu
’
ilssontinvocablesparleréseau.UncomposantEJB
estuneclassequipossèdedesattributsetméthodespourmettreenapplicationlalogiquemétier.L
’
APIEJBfournit
un ensemble de services (persistance, transaction...) de gestion de composants. Il existe plusieurs (trois) types
d
’
EJB :lesbeanssessions,lesbeansentitésetlesbeanscontrôlésparmessage.
●EJBsession :ilpermetdemaintenirdesinformationssurlesclientsetlestraitementsqu
’
ilsréalisent.
●EJBentité :c
’
estuncomposantpersistant,sonétatestsauvegardédansunebasededonnées.
●EJBpilotéparmessage :lesEJBmessagesontsemblablesauxEJBsessionmaissontinvoquésdifféremment
(parlebiaisdeJavaMessageService).
La mise en place d
’
EJB nécessite l
’
utilisation d
’
un serveur d
’
applications capable de gérer ces EJB.
Actuellement,lesserveursGlassFish,JBossetJonasexistentdansledomainedulibre.LeserveurTomcatne
permetpasd
’
utiliserlesEJB.
IlexisteensuiteauseindeJavaEE,laplate
formedeServicesavecJDBC,JNI,JavaMail,JTA,RMI,JAASetXML.
L
’
APIJDBCpermet defaciliterl
’
obtentiondeconnexionsJDBCversdessourcesdedonnées(essentiellementdes
basesdedonnées,maiségalementannuaire...).L
’
APIfournitleslibrairiespourseconnecterauxbasesdedonnées
etpourlagestiondestransactions.
L
’
API JNDI permet d
’
accéder à des services de nommage ou d
’
annuaire (LDAP par exemple). Cette API est par
exempleutiliséepourseconnecteràunesourcededonnéespourdesaccèsàlabasededonnéesoulagestiondes
accès(associéeauxRealms).JNDIpermetd
’
implémenterunservicedenommage.L
’
ensembledesressourcesquele
serveurd
’
applicationsmetàdispositionviacesAPIdeservices,doitêtreenregistréavecunnomlogiqueunique,
permettantauxapplicationsderecherchercetteressourcedansleserveur.
L
’
API JavaMail fournit des fonctionnalités de gestion de courrier électronique (transfert, type de contenu, pièces
jointes...).JavaMailpermetlacréationetl
’
envoidemessagesélectroniquesviaJava.CetteAPIpermetdemanipuler
lesprotocolesdemessagerieInternetcommePOP,IMAP,SMTP.JavaMailn
’
estpasunserveurdecourriermaisplutôt
unoutilquipermetd
’
interagiraveccetypedeserveur.
LesentitésBeansontétédéveloppéespourlemodèledepersistanceenJavaEE.Cemodèledecomposantsavait
de nombreux détracteurs. Pour apporter des solutions à ce problème, de nouvelles spécifications, des outils de
mapping objet/relationnel comme TopLink et Hibernate ont été développés. Java EE apporte donc un nouveau
modèledepersistancenomméJPA.JPAs
’
appuiesurJDBCpourcommuniqueraveclabasededonnéesmaispermet
d
’
éviterdemanipulerdirectementlesfonctionnalitésdeJDBCetlelangageSQL.
3.LesautresAPI
ParmilesautresAPIJavaEE,ilfautciter :
JMS
(
JavaMessageService)permetd
’
accéderàunservicedemessagespourunegestionasynchronedescomposants.
Lecomposantappelantposteunmessage(enarrière
plan)àdestinationd
’
unefiled
’
attentedemessageshébergés
parleserveurd
’
applicationspuiscontinuesontraitementsansattendre.
RMI
(
Remote Method Invocation) permet de concevoir des applications distribuées en Java. RMI permet l
’
appel de
fonctionnalitésàdistanceparlebiaisdelacommunicationréseau.
JTA
(
JavaTransactionAPI)permetdemettreenplaceunegestiondestransactionsdansdesapplicationsdistribuées
(commit,rollback...).Leprincipedestransactionsestdeconsidérerunensembled
’
opérationscommeuneseule.Ce
typedeserviceestobligatoirepourdestraitementsbancaire.Parexemple,uneapplicationbancairequipermetde
réaliserdesvirementsentredeuxcomptesvad
’
aborddébiterlepremiercompteetensuitecréditerlesecondcompte.
Si le débit puis le crédit aboutissent sans problème, alors la transaction est validée. JDBC permet de gérer les
transactionssurunebasededonnéeslocalemaissilesdonnéessontréparties,ilfaudraalorsutiliserlestransactions
JTA.JTApermeteneffetdegérerlestransactionsdistribuéesquifontintervenirdifférentesbasesdedonnées.
JDBC(JavaDataBaseConnectivity)
JNDI(JavaNamingandDirectoryInterface)
JavaMail
JPA(JavaPersistanceAPI)
- 5 -© ENI Editions - All rigths reserved
JCA
(
J2EE Connector Architecture) : ce connecteur permet à Java EE d
’
utiliser des gros systèmes tels que les
mainframes.
JAAS
(
JavaAuthenticationandAutorisationService)estunmécanismedesécuritégéréparleserveurd
’
applications.
XML n
’
est pas véritablement une API Java mais il est très utilisé pour la mise en place et la configuration des
applications.Demême,XMLestlabased
’
unnouveaumodedecommunicationentrelesapplicationsquiestappelé
WebService.Parexemple,JAXP(JavaAPIforXMLParsing)analysedesfichiersoudonnéesXML,JAX
RPC(
JavaAPIfor
XMLbasedRPC)utilisedesWebServicesetJAXB(
JavaAPIforXMLBinding
)permetdegénérerdesclassesJavaàpartir
deschémasXMLouinversement.
Actuellement,laversiondeJavaEEest5.0associéeàl
’
APIServlet2.5,etàl
’
APIJSP2.1(etàApache
Tomcat5.X
6.X).
- 6 - © ENI Editions - All rigths reserved
EncodagedesapplicationsJava
1.Présentation
Lesordinateurstravaillentavecdesbitsousuitesd
’
octets(unoctet=8bits).Leschaînesdecaractères,entiers,réels
sont donc codées sous forme d
’
octets. Les ordinateurs utilisent donc un procédé qui consiste à transformer les
chaînesdecaractèresenoctets,associéàunetechniqueafinderelireleschaînesd
’
origine.C
’
estceprocédéquiest
appeléencodage.
Ilexisteplusieursencodagesquiutilisentplusoumoinslemêmenombred
’
octets,doncdecaractèresdisponibles
commeISO
8859
1,ASCII,UTF
8... L
’
encodageUTF
8estlepluspratiquepouréchangerdestextesconstituésde
caractèresUNICODE(standardduconsortiumUnicode).Ceconsortiumapourbutderépertoriertouslescaractères
utilisésdanslesdifférenteslanguesetd
’
associeràchacununcodenotésousformehexadécimal.L
’
encodageUTF
8
estcompatibleavecl
’
encodageASCIIcequiesttrèspratiquelorsdesdéveloppementsinformatiques.
Lorsdesdéveloppementsd
’
applicationsJavaet/ouJavaEE,iln
’
estpasraredeconstaterdenombreuxproblèmes
d
’
encodage des applications tant au niveau des messages présents dans un navigateur, que des fichiers de
propriétés d
’
une application ou encore de l
’
encodage des caractères saisis au clavier. La mise en œuvre d
’
une
applicationJavanécessitedonclagestiondeplusieursparamètres.Ladifficultéestqu
’
ilnefautpasenoublierun
seulsouspeinedeconstaterl
’
affichagede
’’
hiéroglyphes
’’
àlaplaceducaractèresouhaité.
2.Lesfichiers
Lapremièrecontrainteàvérifierestquetouslesfichiers(HTML,JSP,JSPF,XML,XSLT...)del
’
applicationdéveloppée
soient dans l
’
encodage souhaité. Pour cela, la plupart des IDE peuvent se paramétrer afin de sélectionner
l
’
encodage.AvecEclipse,nouspouvonssélectionnerl
’
encodagedanslemenuFenêtre Préférences
Editeurs
Codagedufichiertexte
et
Typesdecontenu
.
3.LespagesJSPetJSPF
IlestnécessairededéclarerdanschaquepageJSPouJSPFd
’
en
têtel
’
encodageutilisé.Pourcela,ilfaututiliserla
directiveJSPadaptée.
<jsp:directive.page contentType="text/html;charset=UTF -8" />
ou
- 1 -© ENI Editions - All rigths reserved
<%@ page contentType="text/html;charset=UTF -8" %>
Ilestpossibleégalementdecentraliserl
’
encodagedanslefichierdeconfigurationetdedéploiementdel
’
application
web.xmlduserveurTomcat.
<jsp-config>
<jsp-property-group>
<description>Config. de l ’encodage des pages JSP</description>
<url-pattern>*.jsp</url -pattern>
<page-encoding>UTF -8</page -encoding>
</jsp-property-group>
</jsp-config>
4.LespagesHTML/XHTML
Il est également important de prévenir le navigateur client de l
’
encodage qu
’
il doit utiliser pour afficher la page
HTML/XHTML.Cettedirectiveestpréciséeaveclabalisemetaetleparamètrecontent
.
<head>
<meta equiv="Content -Type" content="text/html;charset=UTF -8">
...
</head>
Ilestégalementpossibledepréciserl
’
encodaged
’
unefeuilledestyleexterneàl
’
aidedeladirectiveplacéeentout
débutdefichier.
@charset "UTF-8";
5.LesfeuillesdestyleXSL
Si des transformations XSLT sont utilisées dans notre application, il est nécessaire de déclarer également
explicitementl
’
encodagedanscespages.
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8"
indent="yes" />
6.CodeJava
DupointdevueducodeJava,ilestpossibled
’
utiliserunfiltrequivaforcerleserveurd
’
applicationsJavaàlireles
paramètresdelarequêtedansl
’
encodagesouhaitéetquivarenvoyerlesréponsesaveclemêmeencodage.
package application.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class EncodingFilter implements Filter {
public static final String ENCODING = "encoding";
private String encoding;
public void init(FilterConfig filterConfig) throws ServletException
{
this.encoding = filterConfig.getInitParameter(ENCODING);
}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain filterChain)throws IOException, ServletException
{
req.setCharacterEncoding(encoding);
resp.setContentType("text/html;charset="+encoding);
- 2 - © ENI Editions - All rigths reserved
filterChain.doFilter(req, resp);
}
public void destroy() {}
}
Ilestalorspossiblededéclarercefiltreauseindufichierdeconfigurationdel
’
applicationweb.xml.Lesfiltresétant
exécutésdansl
’
ordrededéclaration,cemappingdoitêtrelepremierdéclarédanslefichierdeconfiguration.
<filter>
<filter -name>encodingfilter</filter -name>
<filter -class>application.filters.EncodingFilter</filter -class>
<init-param>
<param -name>encoding</param -name>
<param -value>UTF -8</param -value>
</init-param>
</filter>
<filter-mapping>
<filter -name>encodingfilter</filter -name>
<url-pattern>/*</url -pattern>
</filter -mapping>
L
’
encodage peut aussi être géré au sein d
’
une Servlet générique. Chaque Servlet du projet devra alors ensuite
hériterdecetteServlet.
package application.servlets;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class EncodingServlet extends HttpServlet {
public static final String ENCODING = "encoding";
private String encoding;
public void init(ServletConfig servletConfig)throws ServletException
{
this.encoding = servletConfig.getInitParameter(ENCODING);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException
{
req.setCharacterEncoding(encoding);
resp.setContentType("text/html;charset="+encoding);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
}
}
7.EncodagedelaJVM
Ilestimportantd
’
exécuterlaJVMdansl
’
encodagevoulu.Letraitementdeschaînesdecaractèresdoitêtrelemême
quelerestedel
’
application.C
’
estaulancementdelaJVM,doncaulancementduserveurJavaEE,quel
’
encodageest
spécifiéàl
’
aidedel
’
argument :
-Dfile.encoding=UTF -8
AvecTomcat,cetargumentestspécifiédanslefichierdelancementduserveur,catalina.sh
.
JAVA_OPTS="$JAVA_OPTS "-Dfile.encoding=utf-8"
Ilestparfoiségalementnécessairedevérifierl
’
encodagedesURLdel
’
application.AvecTomcat,cetencodageest
déclaréexplicitementvial
’
attributURIEncodingsurleconnecteurCoyote.Voicilalignedufichierserver.xmlconcerné :
- 3 -© ENI Editions - All rigths reserved
<Connector port="8080" maxHttpHeaderSize="8192"... URIEncoding="UTF-8" />
Lecodesuivantesttrèsutilecarilpermetlatransformationd
’
unechaînedecaractèresdansunencodageprécis.
public static String transformStringEncoding(String
init,String encodingBefore, String encodingAfter)
{
try
{
return new String(init.getBytes(encodingBefore),
encodingAfter);
}
catch (UnsupportedEncodingException uee)
{
return null;
}
}
8.Gestiondel
’
encodage
IlesttoutàfaitpossibleenJavadegérerl
’
encodageàutiliser.Pourlesflotsd
’
entréeparexemple,laconnexionest
réaliséeàl
’
aidedelaclasseInputStreamReader.Lejeudecaractèresàutiliserpeutêtrealorsprécisé.Pardéfaut,le
jeudecaractèresestfonctiondusystèmed
’
exploitationutiliséetdelalocalisation(ex :fr_FRUTF
8).
Avecunflot,ilestpossibledepréciserl
’
encodageutilisé :
InputStreamReader i=new InputStreamReader(is,"UTF-8");
LenomdujeudecaractèresutilisépardéfautestobtenuenprogrammationaveclaméthodeSystem.getProperty()et
leparamètre
file.encoding
.
package com.betaboutique.classes;
public class Programmation {
public static void main(String[] args) {
System.out.println("Encodage : "+System.getProperty("file.encoding"));
}
}
LorsdesdéveloppementsWeb,ilestassezcourantquelesparamètresreçusparlesméthodesHTTPGETetPOSTne
soientpasdansunformatcorrect.Lesproblèmesportentalorssurlesaccents,lescaractèresspéciaux...Pourcela,la
transformationd
’
unencodagepeutêtreforcéenutilisantlesoctets.Lecodeci
dessouspermetdetransformerun
paramètrereçuencaractèresUTF
8.
String parametre=(String)request.getParameter("parametre");
String parametreUTF8=new String(parametre.getBytes(),"UTF -8");
Demêmepourlesenvoisd
’
informationsenprogrammationJavaàtraversdesflux,l
’
encodageetlestransformations
dejeuxdecaractèressontutilisés.
package com.betaboutique.classes;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
public class Programmation {
public static void main(String[] args) {
- 4 - © ENI Editions - All rigths reserved
try
{
//paramètre à envoyer en direction du flux
String parametreaenvoyer="mon paramètre";
String unautreparametreaenvoyer="un autre paramètre";
String unautreparametreeniso="un paramètre en iso";
URL url=new URL("http://www.essai.com"+"?parametre1="
+URLEncoder.encode (parametreaenvoyer,"UTF -8")
+"¶metre2="+URLEncoder.encode(new String
(unautreparametreaenvoyer.getBytes(), "UTF -8"),"UTF -8")
+"¶metre3="+URLEncoder.encode(unautreparametreeniso,"ISO -8859-1"));
//poster les informations dans le flux
InputStream fluxLecture=url.openStream();
//fermer le flux
fluxLecture.close();
}
catch(Exception e)
{
System.out.println("Erreur de connexion");
}
}
}
- 5 -© ENI Editions - All rigths reserved
LesarchitecturesWeb
1.Lestypesd
’
architectures
DanslesapplicationsWeb,lacommunicationentreleclientetleserveurestréaliséeselonleprotocoleTCP/IPquiest
chargédu routage des données. Le transit desinformations s
’
effectue selon le protocole HTTP pour le Web, les
donnéessontalorstransmisesentreleclientetleserveurviaTCP/IP.Ondistinguealorsdeuxtypesdeclients :
●Le client léger : il est aussi appelé client Web car le module d
’
exécution est alors un navigateur. Les
applicationsclientessontcomposéesdepagesHTML/XHTMLvoireDHTMLavecl
’
utilisationdulangageclient
JavaScript.
●
Leclientlourd
:ils
’
agitd
’
uneapplicationcomposéed
’
uneinterfacegraphiqueévoluéeouenmodeconsole.
Dans l
’
idéal, les clients lourds communiquants ne contiennent que la logique présentation (affichage des
données).Touslestraitementssontdéléguésàdescomposantsmétierdistants.
Ilexisteactuellementungrandnombred
’
architecturesutiliséespourleWeb.
L’architecture2tiersestcomposéededeuxéléments,unclientetunserveur.Cettearchitecturephysiquesimple
peutêtrereprésentéedecettefaçon :
Cettearchitecturepeutaussiêtrereprésentéeavecunserveurdebasededonnées(SGBD),leschémaestalorsle
suivant :
Danscetyped
’
architecture,leclientassumelestâchesdeprésentationetcommuniqueuniquementavecleserveur
d
’
applications.Leclientestdit
’’
lourd
’’
.
Cetyped
’
architecturepeutêtredéveloppétrèsrapidementenfonctiondela
complexitéduprojet.Ilexisteuntrèsgrandnombred
’
outilsdedéveloppementetdelangagespourlesarchitectures
2
tiers.
Dupointdevuedesinconvénients, leproblèmed
’
évolutivité,demaintenanceetlamise enplacelorsdeprojets
complexespeuventêtrecités.
Dansl’architecture 3tiers,leclientestconstituéd
’
unsimplenavigateurInternetetcommuniqueavecleserveur.
Cettearchitectureestcomposéedetroisélémentsoutroiscouches.Lacoucheprésentationouaffichageestleclient
’’
léger
’’
danslamesureoùilnefaitaucuntraitement.Lacouchefonctionnelleoumétierestengénéralunserveur
Web.Etenfin,lacouchededonnéesestliéeauserveurdebasesdedonnées(SGBD).
●
Lacoucheprésentation
(depremierniveau)souventappeléeIHM(InterfaceHommeMachine)correspondàla
partievisibleetinteractive.CettepartieestréaliséepourleWebenHTMLengénéralavecJavaScript,Flash...
- 1 -© ENI Editions - All rigths reserved
●
Lacouchemétier
(desecondniveau)correspondàlapartiefonctionnelledel
’
application.Lesopérationsà
réaliser, les fonctions d
’
accèsauxdonnéesetlestraitementssont mis àladispositiondesutilisateurset
invoquésparleursrequêtes.Pourfournircesservices,elles
’
appuieparfoissurlacoucheaccèsauxdonnées
etenretourrenvoieàlacoucheprésentationlesrésultatsqu
’
elleacalculés.
●Ladernièrecouche(detroisièmeniveau)gèrel
’
accèsauxdonnéesdusystème.Cesdonnéespeuventêtre
stockéessurlemêmesystème(fichiers,fichiersXML,basededonnées,images...)ousurd
’
autressystèmes.
L
’
accèsauxdonnéesesttransparentpourlacouchemétieretcorresponduniquementàlapréoccupationde
lacoucheaccèsauxdonnées.
D
’
une manière générale cette abstraction améliore la maintenance du système. Parmi les avantages de cette
architecture,laflexibilitédel
’
ensemblepeutêtrecitée.Lapartieclientestcomposéeuniquementd
’
affichage(pasde
programmation,derequêtesSQL...).Defait,desmodificationspeuventêtreréaliséesauniveauduSGBDsansque
celaapporteunimpactsurlacoucheclient.Demême,parlasuitetoutenouvelletechnologiepeutêtreintroduite
sanstoutremettreenquestion.Dupointdevuedéveloppement,laséparationentreleclient,leserveuretleSGBD
permetunespécialisation desdéveloppeursetune meilleurerépartitiondes tâches etfonctions(développeur de
modèle/designer,programmeur,administrateurdebasesdedonnées...).
Legrosinconvénientdecemodèle(etleprincipal),estl
’
expertisequ
’
ilestnécessaired
’
avoiretquiestassezlongue
àobtenirpourbienmaîtriserchaquetiersetinterconnexions.Lescoûtsdedéveloppementd
’
unearchitecture3
tiers
sontplusélevésquepourdu2
tiers.
L’
architecturen
tiersaétépenséepourpallierleslimitationsdesarchitectures3
tiersetconcevoirdesapplications
puissantes et simples à maintenir. D
’
un point de vue théorique, cette architecture permet de solutionner les
problèmessuivants :
●Ellepermetl
’
utilisationdeclientsriches.
●Elleséparenettementtouslesniveauxdel
’
application.
●Ellefacilitelagestiondessessions.
●Elleoffredegrandescapacitésd
’
extension.
Une définition possible de ce type d
’
architecture est : une architecture 3
tiers dans laquelle le traitement des
données(coucheaccèsauxdonnéesoumiddleware)contientlui
mêmeplusieurscouchesmultipliantainsilestiers.
Danscecas,l
’
applicationclientepeutêtredéveloppéeavecdescomposantsgraphiques(Swingparexemple)etfaire
appelàdesrèglesmétierEJBquiaccèdentàunebasededonnées.
Il est aussi possible de trouver des applications clientes avec JSP, EJB et base de données. Le client est un
navigateurWeb.LespagesJSPaccèdentauxrèglesmétieretconstruisentlecontenuHTMLfourniaunavigateur.
Lestypesd’architecturesenJavaEE
- 2 - © ENI Editions - All rigths reserved
Undérivédel
’
architectureprécédenteestuneAppletclienteavecJSPetbasededonnées.Danscecas,leclientest
encore un navigateur mais une Applet permet d
’
obtenir une interface utilisateur plus complexe (mais aussi plus
lourde) et plus interactive. Cette Applet peut accéder au contenu produit par des JSP. Celles
ci obtiennent des
donnéesnécessairesgrâceàJDBC.
Une autre version de l
’
architecture précédente est JWS
(
Java Web Start) avec JSP et base de données. C
’
est
pratiquementlemêmecasquel
’
architectureavecAppletsicen
’
estqueleclientestuneapplicationtéléchargeableet
utilisabledemanièreautonome(sansnavigateur)maisuniquementavecuneconnexionréseau.
Lederniertyped
’
architecturereposesurl
’
intégrationdeservicesWeb.Uneapplicationclienteaccèdeauxdonnées
grâceàunserviceWebprogramméenJava.
- 3 -© ENI Editions - All rigths reserved
Les architectures proposées par Java EE reposent sur le découpage des applications en plusieurs tiers aux
responsabilitésclairementséparées.Lesprogrammeursvontdévelopperdescomposantsquiseronthébergésparun
serveurd
’
applicationJavaEE(Tomcat,JBoss...).Lesapplicationsdistribuées(réaliséessousformedecomposants
distincts)permettentdediviserlelogicielenplusieurscouchesappeléestiers(chaquecouchereprésenteuntiers).Le
modèlelepluscourantétantl
’
architecture3
tiers/n
tiers.Cettedivisionfacilitelamaintenanceetl
’
adaptabilitédu
produit.
2.L
’
architectureMVC(ModelViewController)
L
’
architectureMVCproposéeparSunestlasolutiondedéveloppementWebcôtéserveurquipermetdeséparerla
partielogique/métierdelapartieprésentationdansuneapplicationWeb.C
’
estunpointessentieldudéveloppement
deprojetscarcelapermetàtoutel
’
équipedetravaillerséparément(chacunpossèdesesfichiers,seslogicielsde
développementetsescomposants).
CettearchitecturetrouvesonoriginedanslelangageSmallTalkaudébutdesannées1980,cen
’
estdoncpasun
modèle(designpattern)nouveauuniquementliéàJavaEE.L
’
objectifprincipalestdediviserl
’
applicationentrois
partiesdistinctes :le
modèle
,lavueetle
contrôleur
.
Dansl
’
architectureMVC,nousretrouvons :
●Le
modèle
quiestreprésentéparlesEJBet/ouJavaBeanset/ousystèmesdepersistances(Hibernate,objets
sérialisésenXML,stockagededonnéesparlebiaisdeJDBC...).
●LavuequiestreprésentéeparlesJSP.
●Le
contrôleur
quiestreprésentéparlesServlets.
Principedefonctionnementdel
’
architectureMVC
- 4 - © ENI Editions - All rigths reserved
●1.LeclientenvoieunerequêteHTTPauserveur.C
’
estengénéraluneServlet(ouunprogrammeexécutable
côtéserveur)quitraitelademande.
●2. La Servlet récupère les informations transmises par le client et délègue le traitement à un composant
métieradapté.
●3.Lescomposantsdumodèlemanipulentounondesdonnéesdusystèmed
’
information(lecture,écriture,
miseàjour,suppression).
●4. Une fois les traitements terminés, les composants rendent la main à la Servlet en lui retournant un
résultat.LaServletstockealorslerésultatdansuncontexteadapté(session,requête,réponse...).
●5.LaServletappellelaJSPadéquatequipeutaccéderaurésultat.
●6.LaJSPs
’
exécute,utiliselesdonnéestransmisesparlaServletetgénèrelaréponseauclient.
Lescomposantssontbiensûrplusnombreuxmaiségalementplussimples
.Leursspécificitésfontqu
’
ilspourront
êtredéveloppéspardesspécialistes :lesServletsetEJBpardesdéveloppeursJava,lesJSPpardesdéveloppeurset
Webdesigner,lesaccèsauxdonnéespardesspécialistesSQL...Cedécoupagepermetégalementunemaintenance
plusaiséedusystème.Ainsi,lechangementdelachartegraphiqueseraopéréfacilementenutilisantlesvuessans
toucheraumodèleetaucontrôleur.
Afin de faciliter l
’
utilisation du modèle MVC dans les architectures Java EE, des frameworks (outils composés de
spécifications,librairies,outils...)dedéveloppemententièrementbaséssurcemodèleontétédéveloppés(Apache
Struts/Spring).Leschémacomplexeci
dessousreprendunexempledemiseenplaced
’
unetellearchitecturedansun
projetd
’
entreprise.
3.LesdifférentsmodulesJavaEE
Comme indiqué précédemment, les architectures Java EE offrent de multiples possibilités. Il est donc nécessaire
d
’
organiserlesdifférentsélémentsd
’
uneapplicationenfonctiondeleurrôle.
LemoduleWebcontientlesélémentsd
’
uneapplicationJavaEEquivontpermettrel
’
utilisationdecetteapplicationau
traversd
’
unnavigateur Web etde touteapplication utilisant leprotocole HTTP.Leséléments regroupésdans ce
module Web sont les Servlets, les JSP et les ressources statiques de l
’
application Internet (images, JavaScripts,
fichiersstatiques...).IlyaégalementdesbibliothèquesdynamiquesdéveloppéesenJavafourniessousformede
fichiers
.jar
etquisontutiliséesparlesServletset/ouJSP(manipulationd
’
images,defichiers...).LesmodulesWeb
possèdentundescripteurdedéploiement,lefichier web.xml. L
’
ensembledesfichiersestregroupédansunfichier
d
’
extension.warsignifiantWebARchive.
Les composants EJB sont constitués de fichiers de code Java. Il y a aussi dans ce module des bibliothèques au
ModuleWeb
ModuleEJB/composantsmétier
- 5 -© ENI Editions - All rigths reserved
format
.jar
.Lesmodulessontensuiteassemblésenarchived
’
extension
.jar
.
Ilestpossibled
’
utiliserunclientricheavecuneinterfacegraphiquedéveloppéeenutilisantlesAPIdeprogrammation
Java comme Swing et/ou AWT. Un module client permet un assemblage en classes et fournit un descripteur de
déploiement.Lemoduleclientestunfichierd
’
archiveportantl
’
extension
.jar
.
ModuleClient
- 6 - © ENI Editions - All rigths reserved
Miseenplacedel
’
environnement
L
’
interface Java EE permet de créer des sites Web dynamiques avec une technologie Java. La mise en place d
’
un
environnementJavaEEnécessitel
’
utilisationd
’
unserveurd
’
applicationscapabled
’
exécuterlecodeetderépondreaux
requêtesdesclients.GlassFish,Jonas,JBoss,WebSphereetApache
Tomcatfontpartiedecesserveursd
’
applications
Java.
Ilestégalementnécessaired
’
utiliserunenvironnementdedéveloppementévolué.Iln
’
estpaspossiblededévelopper
demanièreconfortabledescentainesdefichierssources,ladocumentation,lesfichiersdeconfigurationavecunsimple
éditeurdetexteetlecompilateurenlignedecommandes.
IlexisteplusieursgrandsIDEJava :Eclipseetsesdifférentesversions,JBuilder,NetBean...
Eclipseesttrèspuissant,ildisposed
’
unegrandepanopliedeplug
inspourl
’
interfaceravecTomcat,pourmanipuler
Mysql,pourgérerlesfichiersXML,lesJSP,lecodeJavaScript...Enfin,pointimportant,EclipseestunprojetOpenSource
gratuit.
Pourlaréalisationdespagesetdesexemples,laversionLombozd
’
Eclipseserautilisée(Eclipse+ensembledeplug
inspourJavaEE).Cetteversioncomplèted
’
EclipseaétédéveloppéeparleconsortiumObjectWeb.
L
’
installationseraexpliquéepourunsystèmeWindowsetpourunsystèmeLinux.Vousremarquezalorsl
’
intérêtde
développer en Java, tout est portable et indépendant de la plate
forme de développement. Nous vérifierons
l
’
installation complète de l
’
environnement en déployant une application très simple. Il est essentiel de tester
l
’
installationavec un petitprojet plutôt quede rencontrer desproblèmes ensuite lorsde la réalisationd
’
exemples
complexes.
1.InstallationduJDK(JavaDevelopmentKit)
LeJavaDevelopmentKit(courammentabrégéenJDK)estl
’
environnementdanslequellecodeJavaestcompilépour
êtretransforméenbytecodeafinquelaJVM(machinevirtuelleJava)puissel
’
interpréter/l
’
exécuter.
La première étape nécessaire à l
’
installation de l
’
environnement est la mise en place des bibliothèques de
développementJava.Eneffet,avantl
’
installationduserveurJavaEE,ilestimpératifqueleJDKsoitinstallésurla
machineoùcelui
ciserainstallé.Leserveurd
’
applicationsfonctionneenJavaetadoncbesoinluiaussiduJDKpour
travailler.LeJDKfournirauncompilateurJavanécessairepourletraitementdesJSP.Avantd
’
installeruneversionde
développementJava,ilestnécessairedevérifiersilesystèmeactuelnepossèdepasdéjàuneversiondeJava.
PourvérifiersilesystèmeactuelpossèdeuneversiondeJava,ilfautouvriruneinvitedecommandesMs
DOSet
lancerlacommande :
java -version
NouspouvonsvoirquelaversionindiquéedanscetexempleestJava1.6(ouégalementappelée6.0).Lecompilateur
utiliséestJ2SE(ilauraitétéaussipossibled
’
utiliserJavaEE).Sicettecommanden
’
indiqueaucunrésultat,c
’
estquele
JDKn
’
estpasinstalléouquelavariabled
’
environnementPATHn
’
estpascorrectementconfiguréesurlesystème.
Les variables d
’
environnement sont des raccourcis utilisés par Windows et Linux pour désigner certains
dossiers(exécutables)dudisquedur(ex :pasbesoindesaisir/usr/bin/java/javac,leraccourcijavacsuffit).
Unefoislesvariablesd
’
environnementcorrectementconfigurées,l
’
exécutiondevosapplicationsserabeaucoup
plussimple.
Nous pouvons également obtenir des informations sur l
’
API Java en utilisant sous Windows le panneau de
configuration.L
’
icôneJavaouvreunefenêtreaveclesongletssuivants :Général
Miseàjour
Java
Sécuritéet
Avancé
.
a.InstallationsousWindows
Unefoiscesvérificationseffectuées,l
’
installationdeJavaEEs
’
effectuesimplementenexécutantlefichiertéléchargé
surlesitedeSunMicrosystems(http://java.sun.com/).Leprogrammed
’
installationdémarrepuisindiquelesétapes
àsuivre.
- 1 -© ENI Editions - All rigths reserved
LapagesuivantepermetdetéléchargerleJDK/JavaEE/JavaSE :http://java.sun.com/javase/downloads/index.jsp
La version Windows Platform d
’
environ 160 Mo permet d
’
installer l
’
environnement de développement complet.
L
’
installation ne pose pas de problème particulier, il suffit de double cliquer sur l
’
exécutable téléchargé et
l
’
installations
’
effectuetouteseuleaprèsavoirindiquélerépertoired
’
installationdesbibliothèques.
1
Lancementdel
’
installation
2
Acceptationdesconditions
- 2 - © ENI Editions - All rigths reserved
3
Choixdurépertoired
’
installation
4
Findel
’
installation
b.InstallationsousLinux
L
’
installationsousLinuxnécessiteletéléchargementdelalibrairieauformat
.bin
.
Toujourssurlamêmepage,ilestpossibledetéléchargerlaversionduJDKpourLinux.
LapagesuivantepermetdetéléchargerleJDK/J2EE/JSE :
https://sdlc5b.sun.com/ECom/EComActionServlet;jsessionid=BB61072B1EEB17E1232E99D84013FACB
LaversionLinuxPlatform
JavaEE SDKd
’
environ150MopermetuneinstallationsousLinux.L
’
installationsous
- 3 -© ENI Editions - All rigths reserved
LinuxestunpeudifférentedecellesousWindows.Aprèsletéléchargementdelabibliothèque,ilestnécessairede
copiercelle
cidansunrépertoiredelibrairiesLinux(engénéral/usr/local/src
).
Exempleaveclejdk
1.6.0 :
#mv /home/jdk-1_6_0-linux-i586.bin /usr/local/src
Puis,ilfautrendrecepaquetexécutable.
#chmod +x jdk-1_6_0-linux-i586.bin
L
’
installationpeutêtreensuitelancée.
#./jdk-1_6_0-linux-i586.bin
Toutlerépertoireestalorsdécompactédans/usr/local/src
.
IlfautmaintenantdéplacerlerépertoireduJDKdansunrépertoirefinald
’
installation(engénéral/usr/local
).
#mv /jdk.6.0_00 /usr/local
Ilfautensuitesedéplacerdanslerépertoire/usr/local
.
#cd /usr/local
Unlien(raccourci)appelé
jdk
peutêtrecréé,ilseraplussimpled
’
accèsquelenomcompletjdk.6.0_00.
#ln -s jdk.6.0_00 jdk
IlresteàplacerlesdroitssurlesfichiersdelalibrairieJava.
#chmod 755 jdk.6.0_00
Javaestdésormaisinstallémaisilfautencoreparamétrer lesvariablesd
’
environnementpourpouvoirlancerles
commandes directement (sans indiquer le chemin complet, ex :
/usr/local/jdk/java
). Dans l
’
état actuel nous ne
pouvonspaslancerJavadirectement :
#java -version (not found...)
Ilexisteplusieurssolutionspourcela :
1.Éditerlefichier
/etc/profile
etajouterlalignesuivantedanslefichier.
PATH=$PATH:/usr/local/jdk/bin
2.Éditerlefichier/root/.bashrcetajouterlalignesuivantedanslefichier.
export PATH=$PATH:/usr/local/jdk/bin
3.Éditerlefichierdel
’
utilisateurconnectéetajouterlalignesuivantedanslefichier.
export PATH=$PATH:/usr/local/jdk/bin
4.Exporterlavariabled
’
environnementPATHdirectementenlignedecommandedansunshell.
export PATH=’’$PATH:/usr/local/jdk/bin’’
echo $PATH (pour vérifier)
Pourquelesmodificationssoienteffectives,ilfautfermertouteslesfenêtresetouvrirunnouveauterminal.
Attention, par la suite nous réaliserons un script de démarrage de Tomcat (serveur d
’
applications) qui
exporteralui
mêmelesvariablesd
’
environnement.
Vouspouvezdésormaisvérifierquel
’
installationduJDKsousLinuxestopérationnelle.
- 4 - © ENI Editions - All rigths reserved
#java -version
- 5 -© ENI Editions - All rigths reserved
Installationduserveurd
’
applicationsJavaEE(Tomcat)
UnserveurJavaEEestaussiappeléserveurd
’
applications(applicationssignifiantapplicationsWeb).L
’
utilisationd
’
un
serveurJavaEEestobligatoirepourledéveloppementdepagesWebdynamiquesenJavaEE.
UnserveurHTTPclassiquereçoitdesrequêtesHTTPetrenvoiedesréponsesmaisilneconnaîtpaslesServlets,les
JSP...Ilestdoncessentield
’
utiliserunprogrammeappelémoteurdeServletsquiestcontenudansleserveurJavaEE
etquipermetdepalliercemanque.Danslaplupartdescas,leserveurJavaEEcontientégalementunserveurHTTP
mais il n
’
est pas aussi puissant que les serveurs spécialisés du monde informatique pour les contenus statiques
(Apache).
Ilexisteungrandnombredeserveursquirépondentàcettenorme(Tomcat,WebSphere,JRun,JBoss,GlassFish...).
Nous utiliserons le serveur Apache
Tomcat de la fondation Apache. Il est très important de comprendre que ces
serveursservent uniquement à fournir uneplate
forme d
’
exploitationdel
’
API Java EE fournie par SUN. Donc, si le
serveur répond à la norme et au standard Java EE, le choix n
’
a alors que peu d
’
importance. La différence sera
observéed
’
unpointdevuerapidité,sécurité,facilitéd
’
utilisation,montéeencharge,gestionoupasdesEJB...
LesversionsmajeuresdeTomcatcorrespondenttoutesàuneimplémentationderéférencedestechnologiesServletet
JSP.VoiciunbrefrappeldesrelationsentrelesversionsdestechnologiesJavaEEetlesversionsdeTomcat.
Commeindiquéprécédemment,ilestimpératifqueleJDKsoitdéjàinstalléavantl
’
installationduserveurd
’
applications.
LeserveurTomcat6estdisponibleenlibretéléchargement.LesversionsbinairesdeTomcatsontenfaitconstituées
declassesJavaetsontdoncportablesentrelessystèmesd
’
exploitationetlesplates
formesmatérielles.
Ilexistetroisformatsd
’
archivesbinaires :
●Lesarchivesau
formatZIP
:unefoislerépertoiredécompressé,leserveurestdirectementopérationnelaprès
configuration.Ceformatestintéressantpourlesadministrateurscarilpermetunemiseàjourrapideencasde
changementdeversionduserveur.Deplus,laconfigurationdusystèmen
’
estpasmodifiée,l
’
installationest
transparente.
●Lesarchivesau
formatTAR.GZ
:c
’
estleformatlepluscommunsouslessystèmesLinux.
●Les installeurs Windows : au
format EXE
permettent une installation à partir d
’
un assistant qui réalise
égalementlaconfiguration.C
’
estlaméthodelaplussimplepourinstallerTomcatsurlesystèmedeMicrosoft.
1.Quelleversionchoisir?
Tomcat 6.X est une adaptation de Tomcat 5.5 pour Java 5.0 (JDK 1.5) avec des améliorations concernant les
performances. La version 1.5 de Java a vu apparaître de nombreuses modifications dans le langage. Tomcat 6.X
requiertdoncauminimumunemachinevirtuelleJava1.5pourfonctionnercorrectement.
Pourleguideetleprojet,laversiondeTomcatutiliséeestlaversion6.0.16.
L
’
installationdeTomcatversion6supposeletéléchargementdelabibliothèquedepuislesitedelafondationApache
àl
’
adressesuivante :http://tomcat.apache.org/
Lasectiondownloadpermetdechoisirlaversionadaptéeànosbesoins.LesliensCoresontalorsutilisésauformat
souhaité(WindowsServiceInstallerparexemple)afindetéléchargerTomcatpourWindows(http://apache.miroir
francais.fr/tomcat/tomcat
6/v6.0.16/bin/apache
tomcat
6.0.16.exe).
Une fois la bibliothèque téléchargée, il est demandé d
’
indiquer le chemin de la JVM (Java Virtual Machine) sur le
système(d
’
oùl
’
importanced
’
installerleJDKavantleserveur).
SpécificationsJ2EE
APIServlet
APIJSP
ApacheTomcat
J2EE1.2
2.2
1.1
3.X
J2EE1.3
2.3
1.2
4.X
J2EE1.4
2.4
2.0
5.X
JavaEE5.0
2.5
2.1
5.X
6.X
InstallationsousWindows
- 1 -© ENI Editions - All rigths reserved
Tomcat6utiliseuncertainnombredeportsTCP/IPpourfonctionner.Ilfautdoncs
’
assurerquecesportsne
sontpasdéjàutilisés:Port 8080 :portduconnecteurHTTPTomcat,Port8005 :portd
’
arrêtduserveur,
Port8809 :portduconnecteurJK.
L
’
installationavecl
’
installeurWindowspermetdecréerlesentréesdanslemenuDémarrerdeWindowsainsiqu
’
un
service pour Tomcat permettant, si nécessaire, le démarrage de celui
ci au lancement du système. L
’
installeur
propose également de créer un utilisateur Tomcat pour administrer et gérer les applications (un super
administrateur).
1
Lancementdel
’
installation
2
Acceptationdesconditions
- 2 - © ENI Editions - All rigths reserved
3
Créationdel
’
utilisateur(UserName :admin,Password :admin)
4
Suitedel
’
installation
- 3 -© ENI Editions - All rigths reserved
5
Findel
’
installation
Aprèsl
’
installation,lefonctionnementduserveurpeutêtretestéenlançantlemoniteurTomcatsituédanslemenu
démarrer.Lemoniteurselancedanslesystray(barredestâchesàcôtédel
’
horlogeWindows)avecuneicônerouge.
Leservicedemonitoringestlancémaisleserveurpasencore.PourréellementlancerleserveurTomcat,ilfautsoit
doublecliquersurl
’
icônesoitfaireunclicdroitetstartservice.
Danscettefenêtredemonitoring,TomcatpeutêtredémarréencliquantsurStart,stoppéavecleboutonStop...Les
autresongletsdelafenêtrepermettentdegérerleslogs,leJDKassociéauserveur,lelancementaudémarragede
lamachine,lesopérationsd
’
arrêtduserveur...
Afindevérifierqueleserveurestcorrectementinstalléetopérationnel(capabled
’
exécuterdesServletsetpages
VérifierqueleserveurJavaEEestactif
- 4 - © ENI Editions - All rigths reserved
JSP),nouspouvonslancerunnavigateuretsaisirl
’
adressesuivante :http://localhost:8080/
Lenomlocalhostcorrespondàl
’
adresselocaledelamachine(leserveurdelamachinelocale)etleport8080estle
portHTTPpourTomcat.
Suivantleserveurutilisé(Tomcat,JRun...)lefonctionnementestidentique,seulleportd
’
accèsHTTPchange(8080,
8200...).LelienTomcatManagerdumenupermetdegérerlesdifférentesapplicationsdéployéessurleserveur.Une
authentificationestnécessaire.Lescoordonnées(identifiantetmotdepasse)correspondentàcellessaisieslorsde
l
’
installationduserveur(UserName :admin,Password :admin).
Il peut être intéressant d
’
installer la partie administration de Tomcat. Pour cela, il faut télécharger le ZIP
AdministrationWebApplication(http://tomcat.apache.org)etdécompressercefichierdanslerépertoired
’
installation
de Tomcat. Il sera alors possible d
’
accéder à la partie administration du serveur à l
’
adresse suivante :
http://localhost:8080/admin.
Pourvérifierlefonctionnementduserveur,nouspouvonslancerlesexemplesprésents,pardéfaut,surleserveur
avecdesServletsetdesJSP.
Pourl
’
installationsousLinux,voirlechapitreLeserveurd
’
applicationsApache
TomcatconsacréàTomcat.
- 5 -© ENI Editions - All rigths reserved
Installationdel
’
environnementdedéveloppement(IDE)Eclipse
1.Présentation
Eclipseestl
’
environnementdedéveloppement(spécialisépourlelangageJava)quiserautilisédanscetouvrage.Le
choix d
’
Eclipse repose essentiellement sur sa gratuité, sa facilité d
’
utilisation, sa puissance de développement et
surtout ses nombreux plug
ins (bibliothèques additives). Il existe actuellement beaucoup de versions d
’
Eclipse de
base(versions2.1,3.X).LaversionutiliséedanscetouvragecorrespondàEclipse3.3Europa.
2.Installation
Eclipse a été décliné en plusieurs versions spécifiques pour des développements orientés. Par exemple, pour le
développement Java EE, la version Lomboz d
’
Eclipse développée par le consortium ObjectWeb
(http://www.objectweb.org/)estactuellementl
’
unedespluspoussées etstables(parseurXML, syntaxe JSP,CSS,
HTML,XHTML...).
LaversionutiliséedeLombozregroupeEclipseetlesplug
inspourledéveloppementJavaEE.
Laversiontéléchargéeestlasuivante :http://lomboz.objectweb.org/downloads/drops/R
3.3
200710290621/
IlexisteuneversionpourWindowsetuneautrepourLinux.Ilestimportantdetéléchargerleprojetcomplet(Lomboz
CompleteInstallation)afindedisposerd
’
unsystèmerobusteetstable.
L
’
installationneposepasdeproblèmeparticulier.Lefichiertéléchargéauformat
.zip
doitêtredécompressédansle
répertoireoùnoussouhaitonsinstallerEclipse/Lomboz.Commepourleserveurd
’
applications,ilestimportantdebien
installer avant l
’
IDE un JDK Java. Lors de l
’
installation d
’
Eclipse, il sera alors demandé de préciser le répertoire
d
’
installationduJDK.
SiaulancementdeWindowsnousobtenonsunefenêtred
’
erreurdecetype :
JVMTerminated.Exitcode=1...
,ilfaut
alorssupprimerlerépertoire.metadataquisetrouvedanslerépertoiredesprojetsd
’
Eclipse(Workspace)etrelancer
l
’
IDE.
Lefichiertéléchargéauformat
.tar.gz
doitêtrecopiédanslerépertoiredessources.
#cp lomboz.tar.gz /usr/local/src
Ilfautensuitedézipperetdétarrercettearchive.
#gunzip lomboz.tar.gz
Unrépertoirenommélombozestalorscréé.Ilfautdéplacercerépertoiredans/usr/local
.
#mv -r lomboz /usr/local/lomboz
Ilfautenfinpositionnerdesdroitscorrectssurlesfichiers.
InstallationsousWindows
InstallationsousLinux
- 1 -© ENI Editions - All rigths reserved
#chown -R tomcat:tomcat /usr/local/lomboz
Nouspouvonsensuitelancerl
’
IDEEclipse.
#/usr/local/lomboz/eclipse&
Le lancement d
’
Eclipseest donc effectuéen tapant la commande shell adaptée (depuis lerépertoire d
’
installation
d
’
Eclipseouaveclechemincomplet).
#eclipse -vm /usr/local/jdk
CettecommandepermetdelancerEclipseenprécisantlerépertoired
’
installationduJDK.Pardéfaut,lesprojetssont
stockés dans le répertoire workspace situé dans le répertoire d
’
Eclipse. Il est également possible de modifier ce
paramètreavecl
’
option
data
.
#eclipse -data ’/home/lafosse/mesprojets’
3.Lesplug
insEclipse
Un plug
in est un composant logiciel additionnel qui permet à un logiciel principal d
’
apporter de nouvelles
fonctionnalités.Ilexisteunemultitudedeplug
insplusoumoinsintéressantsetfiablespourEclipsequisontclassés
parcatégorie(J2EE,graphiques...)àcetteadresseURL:http://www.eclipseplugincentral.com
4.Lancementd
’
EclipseetparamétrageduserveurJavaEE(Tomcat)
Aulancementd
’
Eclipse,unsplashscreen(écrand
’
attente)estaffichéetpermetdechargerenarrière
planlestrès
nombreuseslibrairiesdel
’
environnement.
Ilfautensuitevaliderleworkspace(répertoiredetravail)etl
’
environnements
’
ouvre.
- 2 - © ENI Editions - All rigths reserved
Eclipsepossèdeunplug
indéveloppéparlasociétéSYSDEOquipermetdegérerleserveurd
’
applicationsTomcat.Ce
plug
in permet de disposer d
’
une barre d
’
outils et de démarrer, arrêter et redémarrer Tomcat directement depuis
Eclipse. L
’
installation est très simple, il suffit de télécharger le plug
in Eclipse
Tomcat à cette adresse :
http://www.eclipsetotale.com/tomcatPlugin.html
Ilestimportantdechoisirleplug
inquicorrespondàlaversiond
’
Eclipseinstallée.Pourl
’
utilisationdeceguideetle
développementduprojet,leplug
inutiliséesttomcatPluginV321.zip
.
Unefoisletéléchargementterminé,ilfautdécompressercefichierdanslerépertoiredesplug
insd
’
Eclipse(
/lomboz
eclipe3.3/plugins/
).
Avantl
’
installationd
’
unnouveauplug
inEclipse,ilestimportantdefermerl
’
environnementdedéveloppement,
d
’
installerleplug
inetderelancerl
’
environnement.
Lorsdupremierlancement,ilestimportantdeparamétrercorrectementl
’
IDEavecleserveurJavaTomcatetdevérifier
leJDKutilisé.PourcontrôlerTomcatdepuisLombozetéviterdetoujoursrechargerlespages,démarrer/arrêterTomcat
aveclemenudémarrer,ilfautréaliserlesopérationssuivantes :
MenuWindows
Preferences
Tomcat
.
Danslafenêtreprincipale, ilfautindiquerla version deTomcatutilisée(Version 6.x),lerépertoired
’
installationde
Tomcatetlesdéclarationsdecontextes.Lesdéclarationsdecontextespermettentdeprécisersiunseulfichierest
utilisépourdéclarerlesapplicationsousiaucontraire,ilyaunfichierparapplication.Ilestpréférabled
’
utiliserun
fichierparapplicationoucontextepourdesraisonspratiquesdemaintenabilité.
Installationduplug
indegestionduserveurTomcat
- 3 -© ENI Editions - All rigths reserved
Dans la fenêtre
Application Tomcat Manager
, il faut vérifier que l
’
URL est bien http://localhost:8080/manager et
indiquerl
’
identifiantetlemotdepasseTomcat(ceuxutiliséslorsdel
’
installationduserveur,pardéfaut :UserName :
admin
Password :admin).
Enfin,l
’
installationdel
’
environnementestterminéeenvérifiantlacompatibilitéetlaversionduJDKutiliséparEclipse :
WindowsPreferencesJavaCompiler
.
- 4 - © ENI Editions - All rigths reserved
Danslafenêtre,ilestvisiblequelecompilateurJavaJDKutiliséestlaversion6.0(JDK1.6).Nouspouvonsdésormais
relancerEclipseetobserverlanouvellebarred
’
outilsproposée.
5.Enrésumé
Ce chapitre nous a présenté les conventions utilisées dans ce guide et au sein de Java ainsi que la gestion de
l
’
encodageenJava.
Laplate
formeJavaEEaétéexpliquéeendétailainsiquesesservicesassociéstelsquelesServlets,JSP,EJB,JDBC...
Dansuntroisièmetemps,lesarchitecturesWebontétéprésentéesafindemontrerlapertinenced
’
unchoixphysique
avantledéveloppementd
’
unprojet.
Enfin, l
’
environnement Java EE a été mis en place avec l
’
installation du JDK Java, du serveur d
’
applications, de
l
’
environnementdedéveloppementetduparamétragedel
’
ensemble.
- 5 -© ENI Editions - All rigths reserved
Qu
’
estcequeTomcat?
1.Présentationetdéfinition
Apache
Tomcatestleserveurd
’
applicationsJavaduprojetJakartadelafondationApache.Ceserveurlibre,sous
licenceApachepermetd
’
exécuterdesapplicationsWebdéveloppéesaveclestechnologiesJava(Servlets,JSP...).
Apache
Tomcattrouvesesoriginesautoutdébutdel
’
apparitiondestechnologiesServletsetJSPJavalorsqueSun
MicrosystemsdécidededonnerlecodedesonserveurJavaWebServeràlafondationApache(1999).Aujourd
’
hui,
TomcatestpourSunMicrosystems,leserveurderéférencepourlestechnologiesJavaEEServletetJSP.Tomcatest
unmoteurdeServletsfiable,évolutifetadaptéàl
’
utilisationprofessionnelle.Ilestactuellementutilisédanslemonde
entieretmisenapplicationauseindedomainestrèsvariés.
2.LafondationApache
LeserveurWebApacheaétédéveloppéparRobMcCoolen1994.LapremièreversiondeceserveurWebestrendue
disponibleenAvril1995souslenomd
’
Apache(APatchyServer).Aujourd
’
hui,leserveurWebApacheestleserveurle
plusutilisédelaplanète.En1999,lesdéveloppeursàl
’
origined
’
Apachefondentl
’
ApacheSoftwareFoundation.Cette
organisationàbutnonlucratifdéveloppedenombreuxprojetsetlogicielslibres(leserveurTomcat,deslibrairies
pourledéveloppementInternet,leserveurWebApache,desbibliothèquesdebalises...).
3.LeprojetJakarta
Jakarta est un des très nombreux projets de la fondation Apache. Jakarta divise ses projets en trois grandes
catégories :
●lesserveursd
’
applications;
●lesbibliothèques,outilsetAPI;
●lesframeworks.
Leserveurd
’
applicationsTomcatappartientàlapremièrecatégoriedesprojetsApache.Parmilesautresprojets,ily
a :
●JMeter :outildemesuredeperformancesdesapplicationsWeb;
●Log4J :bibliothèquedegestiondesfichiersjournaux(logs)ettracesdeprogrammation;
●Struts :leframeworkdedéveloppementWebenJavalepluscélèbre;
●
ANT
:l
’
outild
’
automatisationdesapplicationsWeb;
●Commons :unensembledebibliothèquesdeprogrammationJava.
Actuellement,leprojetTomcataprisunetelleampleurqu
’
iln
’
estplusconsidérécommeunsous
projetJakarta(dela
catégorieserveursd
’
applications)maiscommeunprojetcompletdénomméApache
Tomcat.
4.ÉvolutionsdeTomcat
LapremièreversiondeTomcatestlaversion3.Xquiestl
’
implémentationdestechnologiesServlets2.2etJSP1.1.
CetteversionaétéconçueàpartirducodesourcedonnéparSunMicrosystemsàlafondationApache.Àpartirde
2000,leserveuraétécomplètementmodifiéetdonnealorsnaissanceàlaversion4.X.Leserveurpossèdealorsun
nouveaumoteurdeServletsbaptiséCatalina(Servlets2.3etJSP1.2).
Tomcat5.XestapparurécemmentetimplémentelesServlets2.4etJSP2.0.Cetteversionapportedesnouveautés
auniveaudumonitoring(intégrationdeJMX
JavaManagementExtension)ainsiqueplusieursoptimisations(mémoire,
- 1 -© ENI Editions - All rigths reserved
configurationduserveur...).Tomcat5.XintègrelesupportdelaversionJava5.0.LadernièreversiondeTomcat6.X
permetl
’
utilisationdeJava6.0.CetteversionreposesurlesServlets2.5etJSP2.1.
LeserveurJakartaTomcatestdéveloppédepuissespremièresversionsenJava.Lesapplicationshébergéespar
Tomcat sont elles
mêmes écrites en Java, l
’
intégration est alors totale et robuste. Aujourd
’
hui, la version 6.X de
Tomcatsaittirerprofitdesaméliorationsapportéesàlaplate
formeJavaSE,notammententermedeperformance.
- 2 - © ENI Editions - All rigths reserved
InstallationdeTomcat
1.Quelleversionchoisir?
Actuellement, Tomcat propose une version 6.X stable qui est supportée par la majorité des environnements de
développement.Laversion6.XdeTomcatutiliselaspécificationJavaEE5ainsiquel
’
APIServlet2.5etl
’
APIJSP2.1.
C
’
estcetteversiondeTomcatquiserautiliséetoutaulongdeceguideetquiestparfaitementgéréeparEclipsepour
lesopérationsdedémarrage,d
’
arrêtetderedémarrageduserveur.LeserveurTomcat6.Xestdisponibleenlibre
téléchargementsurlesiteInternetd
’
Apacheàcetteadresse :http://tomcat.apache.org
2.InstallationsousWindows
Pour l
’
installation sous Windows, vous pouvez vous référer au premier chapitre de ce guide de développement
d
’
applicationsWebenJava.Ladémarcheàsuivreestexpliquéeendétailavecuneprogressionétapeparétape.
3.InstallationsousLinux
Commeindiquédanslepremierchapitredeceguide,leserveurd
’
applicationsabesoind
’
unJDKJavapourfonctionner
correctement.Ilestdoncnécessaired
’
avoirinstalléunJDK1.5ouJDK1.6fonctionnelavantdeprocéderàl
’
installation
duserveurTomcat.LaversiondeTomcatutiliséedansceguideestapache
tomcat
6.0.16etpeutêtretéléchargéeà
cetteadresse :http://tomcat.apache.org/download
60.cgi.
Lorsdel
’
installation,nousallonspréparerleserveurdefaçonàcequ
’
ilpuissedialoguerparlasuiteavecleserveur
Web.LesystèmeLinuxutilisétoutaulongdeceguideestuneversionDebian(Etch)stable.
Avanttoutechose,ilestnécessairedemettreàjourlespaquetsinstallés.
#apt-get update
#apt-get upgrade
Apacheserautiliséparlasuitelorsdudéploiementd
’
unexemplecompletsurunserveurenproduction.
Les serveurs déjà installés sur le système sont désinstallés afin de paramétrer l
’
ensemble de manière stable et
uniforme.
#apt-get remove --purge apache
#apt-get remove --purge apache2
#apt-get remove --purge apache -perl
#apt-get remove --purge apache -ssl
Nousallonsensuite,détruirelesrépertoiresdesprécédentesinstallations.
#rm -rf /etc/apache
#rm -rf /etc/apache2
#rm -rf /etc/apache -ssl
#rm -rf /etc/apache -perl
Nousprocédonsàl
’
installationduserveurWebApache(1.3ou2.0auchoix).
#apt-get install apache
L
’
étapesuivanteconsisteàinstallerleJDK1.5ouJDK1.6Java.Ilexistedetrèsfortesdépendancesdelaplate
forme
JavaEE5etTomcat6.XavecJava5.Ilestdoncimpératifd
’
installerunserveurTomcat6surunJDK5.0auminimum.
Pourl
’
installationdujdksousLinux,vouspouvezvousréférerauchapitreObjectifsetspécificationsdeJavaEEdece
guidededéveloppementd
’
applicationsWebenJava.
Vérifierlespaquetsinstallés
MiseenplaceduserveurWebApache
InstallationduJDK1.5/1.6
- 1 -© ENI Editions - All rigths reserved
L
’
installationdeTomcatàpartird
’
unearchiveestassezsimple.Ilestnécessairedetéléchargersurlesited
’
Apache
Tomcat l
’
archive du serveur au format
.bin
. La version utilisée pour ce guide est la suivante : apache
tomcat
6.0.16.tar.gz
.
Aveclaversion6.0.XdeTomcat,lapartieadministrationn
’
estpasinclusemaisfaitpartied
’
uneoption.Ilest
donc important de télécharger également la partie administration du serveur : apache
tomcat
6.0.16
admin.tar.gz
.
Aprèsletéléchargement,ilfautcopierl
’
archivedanslerépertoiredessourcesLinux(/usr/local/src
).
#cp apache-tomcat-6.0.16.tar.gz /usr/local/src
Ilfautensuitedétarrerl
’
archivedanslerépertoire/usr/local
.
#tar -xzf apache-tomcat-6.0.16.tar.gz -C /usr/local
IlfautmaintenantcréerunlienpourréférencerTomcatdirectement.
#cd /usr/local
#ln -s apache-tomcat-6.0.16 ./tomcat
IlseradoncpossiblederéférencerTomcatdecettemanièresanssesoucierdelaversionutilisée.
#cd /usr/local/tomcat
Désormais,ilestnécessairedecréerunutilisateursystème,dédiéàTomcatetd
’
indiquerquecetutilisateurestle
propriétairedeTomcat.
#groupadd tomcat
#useradd -g tomcat -d /usr/local/tomcat tomcat
#chown -R tomcat:tomcat apache -tomcat-6.0.16
#chmod 770 apache -tomcat-6.0.16
Leserveurdebaseestdésormaisopérationnel.Nouspouvonsalorsvérifiersonfonctionnementenlançantleserveur
etenouvrantunnavigateuràl
’
adressesuivante :http://localhost:8080/.
#/usr/local/tomcat/bin/startup.sh
Pourunfonctionnementcorrectetafind
’
éviterdepositionnerlesvariablesd
’
environnementàchaquedémarrageou
danslesfichiersspécifiques,ilestnécessairedecréerunscriptdedémarrageetd
’
arrêtdeTomcat.
#! /bin/bash
# LAFOSSE JEROME
NAME="apache -tomcat6.0.16"
#variables d ’environnement
TOMCAT_HOME=/usr/local/tomcat
CATALINA_HOME=/usr/local/tomcat
JAVA_HOME=/usr/local/jdk
CATALINA_OPTS=" -Dfile.encoding=iso8859 -1"
TOMCAT_USER=tomcat
LC_ALL=fr_FR
#exporter les variables
export TOMCAT_HOME CATALINA_HOME JAVA_HOME CATALINA_OPTS TOMCAT_USER LC_ALL
cd $TOMCAT_HOME/logs
case "$1" in
start)
echo -ne "Demarrer $NAME.\n"
/bin/su $TOMCAT_USER $TOMCAT_HOME/bin/startup.sh
;;
stop)
echo -ne "Arreter $NAME.\n"
/bin/su $TOMCAT_USER $TOMCAT_HOME/bin/shutdown.sh
InstallationdeTomcat6.0.X
- 2 - © ENI Editions - All rigths reserved
;;
*)
echo "Usage : /etc/init.d/tomcat {start|stop}"
exit 1
;;
esac
exit 0
Ilestaussipossibledemodifierlégèrementlescriptafindegérerlapartieredémarrageduserveuretainsid
’
éviterde
réaliserdesarrêts/démarragespourunsimpleredémarrage.
...
restart)
echo -ne "Redemarrer $NAME.\n"
/bin/su $TOMCAT_USER $TOMCAT_HOME/bin/shutdown.sh
sleep 7
/bin/su $TOMCAT_USER $TOMCAT_HOME/bin/startup.sh
;;
...
Il est nécessaire de copier ce script dans le répertoire dédié aux services de la machine Linux et de le rendre
exécutable.
#cp tomcat /etc/init.d/
#chmod 755 /etc/init.d/tomcat
Lepropriétairedecescriptdoitêtrelesuperutilisateurrootcarc
’
estleseulutilisateurquialedroitdedémarrerdes
services.
#chown root:root /etc/init.d/tomcat
Nouspouvonsdésormaistesterlefonctionnementduserveurenutilisantlescommandessuivantes :
#/etc/init.d/tomcat start
#/etc/init.d/tomcat stop
Pour activer le script au démarrage du système afin de lancer Tomcat dès l
’
amorçage du système, il est possible
d
’
utiliserlacommande :
#chkconfig -
-add /etc/init.d/tomcat
Àcestadedel
’
installationleserveurTomcatestopérationnel.Ilestdésormaispossibledelancerunnavigateuretde
seconnectersurlapaged
’
accueilduserveuràl
’
adressesuivante:http://localhost:8080/
Ilestsouventtrèsutiledesuivrelestraceslorsd
’
undémarrageouarrêtduserveur.Demême,lesfichiersjournaux
permettentderenseignerlesadministrateurssurlesproblèmesdeconnexionauSGBD,lesexceptions,lestracesde
programmation...LesfichiersjournauxdeTomcatsontstockésdanslerépertoire :/usr/local/tomcat/logs
.
Unebonnehabitudeestd
’
ouvrirenpermanenceuneconsoleaveclacommandesuivantequitraceentempsréelle
fichierdelogsduserveur.
#tail -f /usr/local/tomcat/logs/catalina.out&
Suivrelesfichiersjournaux
- 3 -© ENI Editions - All rigths reserved
LenomdonnéauservicedeTomcat6.0.X(moteurdeServlets)estCatalina.
4.MiseenplacedelapartieadministrationdeTomcat
Pardéfaut,lapartieadministrationduserveurn
’
estpasinstallée.Sinouscliquonssurlelien
TomcatAdministration
ouquenousaccédonsàl
’
adressesuivante :http://localhost:8080/admin/lemessagesuivantestaffiché :Tomcat
’
s
administrationwebapplicationisnolongerinstalledbydefault.Downloadandinstallthe"admin"packagetouseit.Nous
allons donc procéder à l
’
installation de la partie administration en utilisant la librairie apache
tomcat
6.0.16
admin.tar.gz
.
Ilestnécessairedecommencerl
’
installationendétarrantl
’
archivedanslerépertoiredessourcesLinux.
#tar -xzf apache-tomcat-6.0.16-admin.tar.gz /usr/local/src
Lerépertoireapache
tomcat
6.0.16vaêtrecréé.Ilfautalorscopiertoutlecontenudecerépertoire(quipossèdeplus
defichiersquelaversiond
’
origine)danslerépertoired
’
installationdeTomcat.
#cd /usr/local/src/apache-tomcat-6.0.16
#cp -r * /usr/local/tomcat/
Leserveurestpresqueopérationnel,ilfautjusteremettrelesbonsdroitssurlesfichiersetrépertoiresetredémarrer
leserveur(arrêtpuisdémarrage).
#chown -R tomcat:tomcat /usr/local/apache -tomcat-6.0.16
#chmod 770 apache -tomcat-6.0.16
#/etc/init.d/tomcat stop
#/etc/inid.t/tomcat start
Nouspouvonsdésormaisnousconnecteràl
’
adressesuivantehttp://localhost:8080/admin/maisilestnécessairede
créerunutilisateurmanagerTomcat.
Pourcela,ilestnécessaired
’
éditerlefichier :/usr/local/tomcat/conf/tomcat
users.xml
.
#vi /usr/local/tomcat/conf/tomcat-users.xml
Lalignesuivanteestajoutée,ellepermetdecréerunutilisateuravecl
’
identifiantadminetlemotdepasseadminpuis
onredémarreleserveur.
<user username= ’admin’ password= ’admin’ roles=’admin,manager ’/>
#/etc/init.d/tomcat stop
#/etc/init.d/tomcat start
Enfait,lapartieadministrationdeTomcatestuneapplication/webappfourniesousformedefichiers.tar
ou
.gz
.Laseuledifférenceaveclesautreswebappsestqueleprojetn
’
estpaslivrésouslaformed
’
une
archive.wardirectementdéployable.Ilfautdonccopierlesfichiersdeconfigurationaubonendroit.
Pour éviter certains problèmes au démarrage du serveur, il est nécessaire d
’
éditer le
fichier/usr/local/tomcat/conf/server.xmletdemettreencommentairelalignesuivantequiestappeléeAPR
pour Apache Portable Runtime et qui empêche parfois Tomcat de fonctionner correctement :
<Listener
className= ’org.apache.catalina.core.AprLigecycleListener ’/>
5.AugmenterlamémoireallouéeàTomcat
TomcatestdéveloppéenlangageJavaetcommetoutprogrammequiutilisecelangage,ilestassezgourmanden
termedemémoire.Ilestdoncparfoisnécessaired
’
augmenterlamémoireallouéeàTomcatafinqu
’
ilpuissecompiler
plus rapidement les pages et répondre au plus vite aux requêtes clients (même en phase de développement un
serveurrapideestplussouple).
SousWindows
- 4 - © ENI Editions - All rigths reserved
SousWindows,lamémoireallouéeàTomcatpeutêtreparamétréeaveclaconsoledegestionsituéedanslesystray.
Ilfautouvrirl
’
ongletJavaetparamétrerleschamps
Initialmemorypool
et
Maximummemorypool
.Pourl
’
utilisation
deceguideetdesesexemples,200MoserontallouésàTomcat.
Une autre solution pour la gestion de la mémoire allouée à Tomcat est d
’
utiliser Eclipse et l
’
onglet Windows
Preferences.DanslapartieréservéeàTomcat,ilestpossibledepréciserlamémoireinitiale(
Xms)etlamémoire
maxi(
Xmx).Danscetexemple,200MosontallouésàTomcat.Ilestimportantdenepasallouertropdemémoirepar
rapportauxpossibilitésdelamachineafind
’
éviterdesblocageslorsdedéploiements/codages.
Pour augmenter la mémoire allouée à Tomcat sous Linux, il est nécessaire d
’
éditer le fichier de
configuration/usr/local/tomcat/bin/catalina.shetd
’
ajouterlalignesuivanteendébutdefichier.Danscetexemple,2Go
deRAMsontallouésàTomcatpourunserveurenproduction.
JAVA_OPTS="-Xms2048m -Xmx2048m"
SousLinux
- 5 -© ENI Editions - All rigths reserved
CouplerTomcatetleserveurWebApache
1.Présentation
Sinousnesouhaitonspasinstallerunserveurenproduction,cettepartieestalorsfacultative.Eneffet,ellenepermet
pasd
’
améliorerl
’
environnementdedéveloppementmaiselleestrecommandéepourundéploiementsurunserveuren
production.
Dans une architecture en production, il est recommandé d
’
utiliser un serveur Web en frontal d
’
un serveur
d
’
applications. Ces recommandations sont également appliquées dans le cas de l
’
utilisation d
’
un conteneur Web
commeTomcat.L
’
utilisationd
’
unserveurWebenfrontalestnécessairepourdesraisonsdeperformance,desécurité
etdeconfigurabilité.
●Performance :lemoteurHTTPdeTomcatestbeaucouppluslentquelemoteurHTTPd
’
unserveurWebdédié
àcette tâche. Le serveur Webpermet dedélivrer les contenus statiques commeles pages HTML, le code
JavaScript, les images du site, les feuilles de style... Tomcat sera utilisé alors pour servir uniquement les
contenusdynamiquesenJava.
●Sécurité :leserveurWebestutiliséenfrontaletisoleleconteneurWebd
’
Internet.Leconteneurestalorsau
plusprèsdesdonnéesetilestmoinssollicitépourdesservicessimples.
●
Configurabilité
:unserveurWebcommeApachedisposed
’
uneplusgrandepalettedeservices(pointdevue
HTP)queTomcat(.htaccess,gestiondesdroits,urlrewriting,alias,annuaire...).
2.Unconnecteurpourl
’
intégrationduserveurWeb
L
’
intégrationd
’
unserveurTomcatavecunserveurWebsefaitautraversd
’
unconnecteurconfiguréauseindeTomcat
etd
’
uneextensionajoutéeauserveurWeb.UnconnecteurTomcatestuneclasseJavaquisupporteunprotocole
réseauspécifiqueetpropriétaire.Lalibrairied
’
extensionduserveurWebestchargéedynamiquementparleserveur
Weblorsdesondémarrageetpermetundialogueentrelesdeuxserveurs.Plusieursconnecteursexistentcommele
modulemod_jserv pour le serveur JServ et le module
mod_webapp
pour Tomcat 4.X. Ces modules sont désormais
abandonnésauprofitduconnecteurJK.
Le
connecteurJK
utiliseleprotocoleAJP(
ApacheJServProtocol
)danssaversion1.3(AJP13).Ceconnecteurestplus
performant mais il offre également le support de plusieurs systèmes d
’
exploitation, de plusieurs serveurs Web
(Apache,IIS,LotusDomini)etduprotocoleHTTPS.Ceconnecteurestaujourd
’
huilaréférencepourlecouplaged
’
un
serveurd
’
applicationsavecunserveurWeb.
a.Fonctionnement
LeconnecteurJKutilisedoncleprotocoleAJP13etnécessitel
’
installationdumodulemod_jkpourfonctionneravec
Apache.LeconnecteurJKpermetainsid
’
utiliserleserveurWebenfrontaletdedéléguercertainestâchesauserveur
Tomcat.
Les requêtes des clients sont envoyées au serveur Web Apache qui retourne alors directement les contenus
statiquescommelesimages,JavaScript,pagesHTML,...Pourlesrequêtesavecducontenudynamique,lemodule
mod_jkduserveurWebestalorssollicitéetdélèguecertainestâchesauserveurd
’
applicationsTomcat.
LaconfigurationdeTomcatavecunserveurWebutiliselanotiondeworkeroutravailleur.Untravailleurestliéàune
instancedeserveurTomcat.Untravailleurestcaractériséparunnomd
’
hôteouuneadresseIPetunnumérode
port(commeunesocket/prise).LetravailleurAJP13représenteuneinstancedeTomcatenfonctionnementetilest
utilisécommeplug
inpourleserveurApache.Lemodulemod_jkagitalorscommeunrouteurderequêtesversle
serveurTomcat.
- 1 -© ENI Editions - All rigths reserved
b.Installationdumodulemod_jk
L
’
extensiond
’
ApachequisupporteleconnecteurJKestlemodulemod_jk.Cemoduleestlivrésousformedebinaires
(oucodesource).
Lemodulemod_jkfonctionnesurleport8009deTomcat.Pardéfaut,lefichierdeconfigurationdeTomcat
server.xml propose un connecteur AJP13 qui fonctionne sur le port 8009 :
<Connector port= ’8009’
enableLookups= ’false’ redirectPort= ’8443’ protocol= ’AJP/1.3 ’ />
Avantdecommencerl
’
installation,ilfautmettreenplacesousLinuxDebian,lepaquet apache
dev
quipermetde
compilerlesmodulesd
’
Apache.
#apt-get install apache-dev
L
’
installationdeTomcat6.XavecuneintégrationpourunserveurWebnerequiertaucuneconfigurationparticulière.
Pourutilisermod_jk,ilfautunconnecteurcompatibleconfigurédans le fichierserver.xmldel
’
instancedeserveur
Tomcat 6.X. Cette configuration existe désormais par défaut avec Tomcat 6.X et propose un connecteur qui
fonctionnesurleport8009.Ensuite,ilestnécessairedetéléchargerleconnecteur(ladernièreversion)surlesite
d
’
Apache
Tomcat(http://tomcat.apache.org).Leliensuivantestutilisé :TheApacheJakartaTomcatConnector.Pource
guide, c
’
est la version jakarta
tomcat
connectors
1.2.15
src.tar.gz du connecteur qui a été utilisée. L
’
installation
commenceencopiantlabibliothèquesourcedanslerépertoiredessourcesLinux.
#cp jakarta-tomcat-connectors-1.2.15-src.tar.gz /usr/local/src
Ensuite,l
’
archiveestdétarré(e):
#tar -xzf jakarta-tomcat-connectors-1.2.15-src.tar.gz
Il faut ensuite compiler le fichier source présent dans le répertoire/usr/local/src/jakarta
tomcat
connectors
1.2.15
src/jk/native
.
#./configure -with-apxs=/usr/bin/apxs
#make
L
’
option
with
apxs permet de préciser l
’
emplacement de la commande apxs qui est utilisée pour construire les
modulesd
’
Apache.Unfichiersourcecompilévaalorsêtregénéré.Cefichiersourceestalorscopiédanslerépertoire
deslibrairiesApache.
#cp apache-1.3/mod_jk.so.0.0.0 /usr/lib/apache/1.3/mod_jk.so
Ilfautalorspositionnerlesdroitscorrectssurlemodule.
#chown root:root /usr/lib/apache/1.3/mod_jk.so
#chmod 644 /usr/lib/apache/1.3/mod_jk.so
c.Configurerlemodulemod_jk
Ilnousresteàconfigurerlefichierdegestiond
’
Apachepourqu
’
ilchargedynamiquementlorsdesondémarragele
modulemod_jk.Pourcela,ilfautéditerselonlaversiond
’
Apachelefichierhttpd.confoumodules.confetajouterles
lignessuivantes :
LoadModule jk_module /usr/lib/apache/1.3/mod_jk.so
JkWorkersFile /etc/apache/workers.properties
JkLogFile /usr/local/tomcat/logs/mod_jk.log
JkLogLevel warn
Lapremièreopérationpermetderéaliserlechargementdumodulemod_jkdansleserveurApache(
LoadModule
).La
directiveJkWorkersFilepermetdespécifierl
’
emplacementdufichierdeconfigurationdumodule.Ladirective
JkLogFile
précisel
’
emplacementd
’
unfichierjournalréservéaumoduleetladirectiveJkLogLevelindiqueletypedemessages
enregistrésdanscefichier.
Ilnereste maintenant plusqu
’
àconfigurer le serveurWeb Apache età créerlefichier workers.properties. Ilfaut
commencerparéditerlefichierdeconfigurationd
’
Apache
/etc/apache/httpd.conf
puisajouterleslignessuivantespour
notrehôtevirtuel.
- 2 - © ENI Editions - All rigths reserved
<VirtualHost *>
ServerName monserveur.com
#les parties statiques de mon application sont gérées par Apache
Alias /images /usr/local/tomcat/webapps/monapplication/images
Alias /css /usr/local/tomcat/webapps/monapplication/css
DocumentRoot /usr/local/tomcat/webapps/monapplication
#les requêtes ne sont transmises à Tomcat que pour les servlets et JSP
<Location "/*.jsp">
JkMount worker1
</Location>
<Location "/*.do">
JkMount worker1
</Location>
#pages d ’accueil autorisées
DirectoryIndex index.html index.htm index.jsp
</VirtualHost>
Vousremarquezqueleserveurseracapablededéfinirlapageindex.jspcommepaged
’
accueildel
’
application.De
même,touslescontenusautresquelesServlets(extension
.do
)etlesJSP(extension.jsp)seronttraités parle
serveurWebquiestplusapproprié.
La directiveJkMount est très importante car c
’
est elle qui permet au serveur Apache d
’
accéder aux applications
Tomcat.Ellepermeteneffet,despécifieruntravailleurpourl
’
accèsaucontexte.Ilyauradoncuneredirectionde
requêtesutilisateuràdestinationdutravailleur.LadirectiveJkUnMountpermetderéaliserl
’
inverseetdoncdene
pasredirigerlesrequêtesutilisateursàdestinationderessourcesparticulières.
Exemple :
JkUnMount /usr/local/tomcat/webapps/monapplication/mesimages/*.gif
D
’
autresdirectivesd
’
Apachesontutilisablesaveclemod_jk
.
JkAutoAliaspermetderéaliserunaliasdurépertoirede
TomcatsurlerépertoirededonnéesApache,JkLogStampFormatpermetdegérerleformatdeladatedanslefichier
journal du module,JkExtractSSLpermet de transmettreles informations SSL vers leserveur Tomcat (état onpar
défaut)...
d.Créerlefichierdeconfigurationdutravailleur
Une fois le fichier de configuration d
’
Apache modifié, il faut ensuite créer le fichier de configuration du travailleur
indiquéparladirectiveJkWorkersFiledanslefichierd
’
Apache.Cefichierpermetdegérerlacommunicationentrele
serveurWebetleserveurTomcat.Lefichierutilisénomméworkers.propertiesportel
’
extension
.properties
quiestun
formattrèsutiliséaveclestechnologiesJava.Cesfichierssontcomposésd
’
unensembledepairesclé/valeur.
Lasyntaxepourlefichierworkers.propertiesestlasuivante :
worker.<nom_du_travailleur>.<directive>=<valeur>
Lasyntaxedenotrefichierestlasuivante :
workers.tomcat_home=/usr/local/tomcat
workers.java_home=$(JAVA_HOME)
ps=/
#liste des travailleurs (il serait possible de placer plusieurs travailleurs
séparés par des virgules)
workers.list=worker1
#protocole de worker1
workers.worker1.type=ajp13
#nom d’hote pour worker1
workers.worker1.host=localhost
#port du connecteur JK pour worker1
workers.worker1.port=8009
#le facteur de charge
workers.worker1.lbfactor=50
#nombre de connexions AJP maintenues dans le cache
workers.worker1.cachesize=10
#temps pendant lequel la connexion est maintenue dans le cache
workers.worker1.cache_timeout=600
#ne pas couper les connexions inactives
workers.worker1.socket_keepalive=1
- 3 -© ENI Editions - All rigths reserved
#temps d ’expiration lors de la communication entre Apache et Tomcat
workers.worker1.socket_timeout=300
Ce fichier est créé dans le répertoire local d
’
Apache à savoir : /etc/apache/workers.properties conformément à la
directiveJkWorkersFile.Lesparamètresdeconfigurationdufichierworkers.propertiessont,entreautres :
●worker.list :permetdespécifierunelistedetravailleursséparéspardesvirgules.
●type :permetdespécifierletypedetravailleur.
●host :permetdespécifierlenomd
’
hôteouadresseIPduserveurTomcatàcontacterpourletravail.
●
port
:indiquelenumérodeportduconnecteurJK.
●socket_timeout :indique letemps d
’
expirationdela communicationentreApacheet Tomcat.Sile serveur
Tomcatnerépondpasdansledélaiindiqué,mod_jkgénèreuneerreuretrecommence.
●retries :positionnelenombredetentativesdeconnexionsversTomcatencasdenonréponsedecedernier
(3pardéfaut).
●socket_keepalive :permetd
’
éviterquelefirewallcoupelesconnexionsinactives(défaut0).
●recycle_timeout :indiquelenombredesecondesau
delàduquelleserveurcoupeuneconnexionAJPencas
d
’
inactivité(défaut0,bonnemoyenne300).
●cachesize :préciselenombredeconnexionsAJPmaintenuesencache.
●cache_timeout: permetde spécifiercombien de temps une connexion doit êtremaintenue dansle cache
avantd
’
êtreferméeparmod_jkafinderéduirelenombredeprocesseursderequêtesactifssurleserveur
Tomcat(défaut0).
●
lbfactor
:permetdegérerlefacteurdecharged
’
untravailleurdanslecasoùlarépartitiondelachargeest
miseenœuvreparmod_jk.Cettevaleurpermetdepréciserquelpourcentagederequêtesl
’
instanceTomcat
seraamenéeàtraiter(défaut1).
Unefois laconfiguration duconnecteur JKterminée, il faut redémarrer les deux serveurset testerl
’
accèsàune
ServletouuneJSPpournotrehôteprécédemmentdéfini(http://monserveur.com/index.jsp).
Pourcela,sousWindows,ilestpossibled
’
éditerlefichier C:\WINDOWS\system32\drivers\etc\hostsetd
’
ajouterle
nomdel
’
hôtevirtuel.
127.0.0.1 localhost
127.0.0.1 monserveur.com
SousLinux,nouspouvonsréaliserlamêmeopérationenéditantlefichier/etc/hosts
.
127.0.0.1 localhost
127.0.0.1 monserveur.com
Désormais,lorsquel
’
urlsuivante :http://monserveur.comseraappeléedansunnavigateur,c
’
estlamachinelocale
(127.0.0.1)quirépondraauxrequêtesduclient.
#/etc/init.d/apache restart
#/etc/init.d/tomcat stop
#/etc/init.d/tomcat start
Silapage.jspestaffichée,l
’
installationestcorrecte.Eneffet,c
’
estl
’
hôtevirtuelduserveurWebApachequiest
précisé par le nom de domaine (http://monserveur.com) et le fichier.jsp est exécuté par Tomcat par le biais du
connecteur.
Encasdeproblèmelorsdutestduconnecteur,lesfichiersjournauxd
’
Apacheetdemod_jkpeuventnous
aiderenindiquantlesraisonsdesdysfonctionnements.
- 4 - © ENI Editions - All rigths reserved
- 5 -© ENI Editions - All rigths reserved
ArchitectureetconfigurationdeTomcat
Leserveurd
’
applicationsTomcatutiliseunearchitecturespécifique(spécifiqueauxprojetsJavaEE)qu
’
ilestnécessaire
debienmaîtriser.Tomcatestlivrépré
configuré,ilestpossibledel
’
utilisercommecelasansavoiràmodifierlesfichiers
deconfiguration,maislorsdesdéveloppementsetdelamiseenproductiondesapplications,ilseranécessairedebien
contrôlerl
’
administrationduserveur.
1.LescomposantsdeTomcat
Tomcat est constitué d
’
un ensemble de composants dédiés à l
’
exécution d
’
un service particulier et précis. Les
composantsdeTomcatsontappelésdesconteneurs(parcequ
’
ilscontiennenteuxaussidescomposants).Ilexiste
actuellementcinqtypesdeconteneurs :Server
,
Service
,
Engine
,
HostetContext
.
Touscesconteneurssontreprésentésdanslefichierdeconfigurationduserveurserver.xmlquiestleprincipalfichier
de configuration de Tomcat. Chaque conteneur est représenté par une balise XML en suivant une structure
arborescenteadaptéeenconséquence.
2.Arborescenceduserveur
Lastructuredesfichiersauseindurépertoired
’
installationestprésentéedanscetteimage.
- 1 -© ENI Editions - All rigths reserved
Parmi les répertoires présents, certains sont dédiés à la configuration du serveur et sont donc difficilement
modifiables,d
’
autrespeuventêtremodifiéssuivantlesdéveloppements.
Le répertoire
/bin
contient tous les scripts et fichiers indispensables au bon fonctionnement de Tomcat. Ces
exécutablescontiennentdesscriptsshelletdesfichiersbatchquipermettentdedémarreretd
’
arrêterleserveursur
les différentes plates
formes prises en charge. Les fichiers d
’
extension
.bat
sont utilisés sous Windows et les
fichiers.shsontutiliséssousLinux.
Le répertoire
/lib
est le répertoire partagé de Tomcat. La totalité de son contenu est accessible à toutes les
applications Web déployées sur le serveur. Les ressources placées dans ce répertoire peuvent être livrées au
format.classetdonccopiéesdanslerépertoire/lib/classesousousformedefichiers
.jar
.Cerépertoire
/lib
seratrès
souventutilisé pourplacer lepilote JDBCde labase dedonnées utilisée.Par exemple,lors dudéploiement d
’
un
projet, la librairiemysql
connector
java
3.1.11
bin.jar
seracopiéedanslerépertoire
/lib
etsera doncutilisablepar
toutes nos classes du projet (ce qui est valable pour les librairies
mail.jar
,
xalan.jar
...). Dans le cas où plusieurs
applicationsWebonttoutesbesoind
’
unemêmebibliothèque,ilpeutêtreplusjudicieuxdecopiercettebibliothèque
danscerépertoireplutôtquedanschacunedesapplications.
Lerépertoire
/conf
contienttouslesfichiersdeconfigurationdeTomcataveclesquatrefichiersimportantsquesont
server.xml
,
tomcat
users.xml
,
web.xmlet
catalina.policy
.
Lerépertoire
/logs
contienttouslesfichiersjournauxduserveurTomcat.Ilestimportantdepréciserquelesfichiers
journauxsontautomatiquementremplacéstouslesjoursàminuit,etcontiennentdansleurintituléladateauformat
anglais.Ilexistetroisprincipauxtypesdefichiersjournaux :lesfichiersrelatifsauserveur,lesfichiersrelatifsaux
applicationsetauxnomsd
’
hôtes.
Lerépertoire/tempestunrépertoiretemporairepourlesapplicationsnondéployées.
Lerépertoire
/webapps
estlerépertoirepardéfautd
’
installationdesapplicationsJavaEE.Ilcontientpardéfautdes
applicationsd
’
exemplesainsiquel
’
applicationtomcat
docsquifournitladocumentationduserveur.Ilesttoutàfait
possiblederéférencerdesapplicationsWebquinesetrouventpasdanslerépertoire
/webapps
.Danscecas,ilfaut
préciserlerépertoiredel
’
applicationdefaçonprécisedanslefichierdeconfigurationserver.xml
.
Lerépertoire/workestutilisépourletraitementdespagesJSPetleurtransformationenclassesJava.Toutesles
ServletsgénéréesàpartirdepagesJSPserontstockéesdanscerépertoire.Chaqueapplicationpossèdealorsson
propre sous
répertoire (ex : work/Catalina/localhost/monapplication) en suivant l
’
arborescence suivante :
work/<engine>/<host>/<context>
où
<engine>
,
<host>et
<context>
représententlenomdesconteneursEngine
,
Host
etContextdanslesquelscetteapplicationestinstallée.Cerépertoireserad
’
unegrandeutilitélorsdudébuggagede
pagesJSP.Lecodetransforméseraeneffetaccessible.Demême,ilseraparfoisnécessairedesupprimerlecontenu
decerépertoirepournotrehôtelorsquel
’
onvoudraregénérertouteslespagesJSP.
- 2 - © ENI Editions - All rigths reserved
RappelsXML
LesfichiersdeconfigurationdeTomcatsontécritsaveclelangageXML.Ilestdoncimportantdeprésenterlasyntaxeet
lesbalisesdecelangageafindeconfigurercorrectementleserveurd
’
applications.XML(eXtended Markup Language
)
dérivedulangageSGML(StandardGeneralizedMarkupLanguage)développédanslesannées80.SGMLestunlangage
trèscomplexeàapprendreetàutiliser.Uneversionplussimpledecelangageaétéproposéepourlaprésentationde
documentWeb :leHTML(HyperTextMarkupLanguage
).
HTMLestaujourd
’
huilestandardpourledéveloppementWeb.Ilcommenceàêtreremplacéprogressivementparle
XHTML (eXtend Hypertext Markup Language) qui est assez similaire mais qui respecte les normes XML. HTML est
’’
un
langage
’’
dedescription,ilestliéàuneDTD(DocumentTypeDefinition)quipermetdevérifierlasyntaxedulangage.
XMLutiliselasimplicitéduHTMLaveclasouplessedeSGML.LepointcommunleplusimportantentreSGMLetXMLest
l
’
utilisationd
’
uneDTDoud
’
unschéma.Cetteassociationn
’
estpasobligatoireetunfichierXMLpeuttrèsbiensesuffire
àlui
même.
Dans un document XML, la mise en forme des données est complètement séparée des données. Les données (le
contenu)sontséparéesdel
’
apparence(lecontenant).Ilseradoncpossibledefournirplusieurstypesdesortiespour
unmêmefichierdedonnées(image,fichierHTML,fichierXML,fichierPDF...).
LeslangagesSGML,HTML/XHTMLetXMLsontenfaitcomposésdebalisesquipeuventêtrecomparéesàdesmotsdu
langagefrançais.Parcontre,ilyadesrèglesàrespecterpourl
’
utilisationdecesmotsdansundocument,cesrègles
sontappelées :grammaires.
XMLpermetdeséparerlefonddelaforme.Celasignifiequ
’
undocumentXMLnecomportequedesdonnées.Ainsi,pour
produireundocumentHTMLàpartird
’
unfichierXML,ilestnécessairedecréeraumoinsdeuxfichiers,lepremierpour
lesdonnéesetlesecondpourlamiseenformedecesdonnées.Untroisièmefichierpeutparfoisêtreutilisé,c
’
estune
DTDouunschémapermettantdedéfinirlesbalisesetlagrammaireutilisées.
UndocumentXMLbienforméestundocumentXMLquirespectecertainesrègles.
●LedocumentdoitcommencerparunedéclarationXML(prologue).
<?version="1.0" standalone="yes" encoding="iso-8859-1"?>
●Ilnedoitexisterqu
’
uneseulebaliseracine.
<maracine>
<balise>donnée1</balise>
<balise>donnée2</balise>
...
</maracine>
●Lesvaleursdesattributsdoiventêtreimpérativementencadréespardesguillemetssimplesoudoubles.
<balise attribut1="valeur" attribut2=’valeur’>
●Toutebaliseouvertedoitêtrefermée.
<balise>donnée</balise>
●Unebalisevidedoitêtreobligatoirementfermée.
<balise/>
<balise></balise>
●Lesbalisesdoiventêtrecorrectementimbriquées.
<baliseparent>
<baliseenfant>donnée</baliseenfant>
HTML
XML
Bienformé
- 1 -© ENI Editions - All rigths reserved
</baliseparent>
●Lesnomsdesbalisesdoiventcommencerparunelettreou
’’
_
’’
,
lesautrescaractèrespeuventêtredeschiffres,
deslettres,
’’
_
’’
,
’’
.
’’
ou
’’
’’
.
<_balise attribut28=’valeur’/>
●Lesnomsdesbalisesetdesattributsdoiventconserverunecasseidentique.
<mABaLise attriBuT=’12’></mABaLise>
<mABaLise attriBuT=’14’></mABaLise>
●Lesnomsdesbalisesnedoiventpascommencerparxml
.
●Ledocumentdoitcontenirunouplusieurséléments.Siledocumentcontientunseulélément,alorscedocument
seracomposéduseulélémentracine.
●Lecaractèreinférieur<estréservéàl
’
ouverturedesbalises.
<mabalise/>
●Lescaractèresinférieur<,esperluette&,supérieur>,quote
’
,
etdoublequotes
’’
doiventêtreremplacéspar
leursentitésHTMLounumériques(<;&;>;...).
UndocumentXMLestditvalidelorsquecelui
ciestbienforméetqu
’
ilestconformeàunegrammaire.
UnfichierXMLestcomposéd
’
unprologue,d
’
unélémentracineetd
’
unarbre.L
’
arbreestcomposéd
’
élémentsimbriqués
lesunsdanslesautres.Leprologueestconstituédelapremièrelignedudocumentpermettantd
’
indiquerquec
’
estun
documentXMLetéventuellementdel
’
associationàuneDTD.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!-- Élément racine -->
<dvds>
<!-- Premier enfant -->
<dvd>
<!-- Élément enfant titre -->
<titre>Les infiltrés</titre>
<prix>17.99</prix>
<duree>125</duree>
</dvd>
<dvd>
<titre>Le cercle rouge</titre>
<prix>14</prix>
<duree>120</duree>
</dvd>
<dvd langue="en">
<titre>Psychose</titre>
<prix>22</prix>
<duree>155</duree>
</dvd>
</dvds>
Danscetexemple,leprologueestindiquéparlapremièrelignedudocument<?xmlversion="1.0"encoding="ISO
8859
1"?>,ilprécisequeledocumentestunfichierXMLutilisantl
’
encodage
ISO
8859
1(caractèresstandardsduclavier).
Dansleprologue,l
’
attributversionpréciselaversionXMLutiliséedansledocument.L
’
attributencodingpermetdedéfinir
lejeu decaractères utilisé.Pour laFrance, lejeu decaractères standardest
ISO
8859
1.Pour fairedes pagesen
international (Japonais, Anglais, Français...), il faut utiliser l
’
encodage Unicode UTF
8. Un troisième attribut optionnel
nomméstandalonepermetdeprécisersiouiounonlefichierXMLestliéàuneDTDexternepourvérifiersasyntaxe.
●sansutilisationd
’
uneDTD:
<?xml version="1.0" encoding="ISO -8859-1" standalone="yes"?>
Valide
Structured
’
undocumentXML
- 2 - © ENI Editions - All rigths reserved
●avecutilisationd
’
uneDTD:
<?xml version="1.0" encoding="ISO -8859-1" standalone="no"?> <!DOCTYPE dvds
SYSTEM "dvds.dtd">
L
’
élément
<dvds>
estl
’
élémentracinedudocument.Ilestlui
mêmeconstituédetroiséléments
<dvd>
.Danschacundes
élémentsdecetexemplevousretrouvezunélément
<titre>
,
<prix>
et
<duree>
.L
’
élément
<dvd>
possèdeunattribut
nommélangue.
CettevuereprésentelecodeXMLdelapageprécédenteouvertavecunnavigateur
UneDTDpeutêtreassociéeàcedocumentetpermetalorsdedéfinirlastructuredudocument.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE dvds SYSTEM "dvd.dtd"/>
<!-- Élément racine -->
<dvds>
<!-- Premier enfant -->
<dvd>
<!-- Élément enfant titre -->
<titre>Les infiltrés</titre>
<prix>17.99</prix>
<duree>125</duree>
</dvd>
<dvd>
<titre>Le cercle rouge</titre>
<prix>14</prix>
<duree>120</duree>
</dvd>
<dvd langue="en">
<titre>Psychose</titre>
<prix>22</prix>
<duree>155</duree>
</dvd>
</dvds>
- 3 -© ENI Editions - All rigths reserved
Lors de la déclaration d
’
une DTD, il faut préciser l
’
élément racine (
<dvds>
dans l
’
exemple) et le nom du fichier qui
contient la grammaire du document (
dvd.dtd
dans l
’
exemple). Bien que facultative, une DTD permet de simplement
vérifierlavaliditéd
’
undocumentXML.DansundocumentXML,lescommentairessontutiliséscommedansundocument
HTMLaveclasyntaxesuivante
<!
moncommentaire
>
.
L
’
élément racine
<dvds>
est la base du document XML. Il doit être unique et englobe tous les autres éléments. Il
s
’
ouvrejusteaprèsleprologueetsefermeàlafindudocument.Lesautresélémentsformentlastructuredudocument.
Cesontdonclesbranchesetlesfeuillesdel
’
arborescence.Lesélémentscontenantssontappelésélémentparentetles
autresélémentsimbriquésélémentenfant.Lesélémentspeuventcontenirunouplusieursattributs.Chaqueélémentne
peutcontenirqu
’
unefoislemêmeattribut.Unattributestcomposéd
’
unnometd
’
unevaleur.Unattributnepeutêtre
présentquedansunebaliseouvranted
’
unélément(etpasdanslafermante).
CertainscaractèresontunsensparticulierenXML,ilestnécessairedetrouverunremplaçantquandilfautinsérerces
caractèresdansledocument.Ilfautalorsavoirrecoursauxentités.LescaractèresréservésenXMLsontlessuivants :
Pourleslettresaccentuées,ilfaudraparfoisutiliserlesentitésnumériquesdutype&#numero;(oùnumeroestune
valeurdécimale).Parexemple,lecaractèrecodéépeutêtreremplacéparé;
.
Dansl
’
exempleprécédent,lefichierestcorrectementaffichédansunnavigateur.Ledocumentestvalideetbienformé,
ilpeutdésormaisêtreutilisé.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE dvds SYSTEM "dvd.dtd"/>
<!-- Élément racine -->
<dvds>
<!-- Premier enfant -->
<dvd>
<!-- Élément enfant titre -->
<titre>Les infiltrés</titre>
<prix>17.99</prix>
<duree>125</duree>
</dvd>
<dvd>
<titre>Le cercle rouge</titre>
<prix>14</prix>
<duree>120</duree>
</dvd>
<dvd langue="en">
<titre>Psychose</titre>
<prix>22</prix>
<duree>155</duree>
</dvd>
</dvds>
Riennenousempêchedeneplusavoirdeprixetdeduréepourchaquedvd.Lastructuredudocumentestcorrectedu
pointdevuedesbalisesmaislagrammairenel
’
estpas.C
’
estlàqu
’
intervientlaDTD(fichierdvd.dtdci
dessous)qui
permetdedéfinirlasyntaxequeledocumentXMLdevrarespecter.
Exemple1:
<?xml version="1.0" encoding="ISO -8859-1"?>
<!ELEMENT dvds (dvd*)>
<!ELEMENT dvd (titre, prix, duree)>
Explications
Caractère
Entité
&
& ;
<
< ;
>
> ;
’’
" ;
’
&aquot ;
Syntaxed’
uneDTD
- 4 - © ENI Editions - All rigths reserved
<!ATTLIST dvd type (thriller | policier | horreur | théâtre) #IMPLIED
langue CDATA "fr"
>
<!ELEMENT titre (#PCDATA)>
<!ELEMENT prix (#PCDATA)>
<!ELEMENT duree (#PCDATA)>
Exemple2:
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE dvds SYSTEM "dvd.dtd"/>
<!-- Élément racine -->
<dvds>
<!-- Premier enfant -->
<dvd>
<!-- Élément enfant titre -->
<titre>Les infiltrés</titre>
</dvd>
<dvd>
<titre>Le cercle rouge</titre>
</dvd>
<dvd langue="en">
<titre>Psychose</titre>
</dvd>
</dvds>
LefichierXMLestassociéàcetteDTD.Ildevradoncrespecterlagrammairesuivante :labaliseracinedoitêtre
<dvds>
,
labaliseracineestcomposéedebalises
<dvd>
etchaquebalise
<dvd>
contientunebalise
<titre>
,
<prix>
et
<duree>
.
La balise
<dvd>
contient deux attributs optionnels type et langue. L
’
attributtype ne peut contenir que les valeurs
’
thriller,policier,horreuretthéâtre
’
.
Labaliselanguecontientpardéfautlavaleur
’
fr
’
.
LemotcléSYSTEMdanslefichierXMLindiquequelefichierDTDsetrouvesurl
’
ordinateurlocaletqu
’
ilestdisponibleen
accèsprivé uniquement.Le motclé PUBLICindiquequ
’
uneressource est disponible pour tous(accès public)sur un
serveurWebdistant.
<!DOCTYPE dvds SYSTEM "dvd.dtd">
<!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
Unedéclarationd
’
élémentestdelaforme :
<!ELEMENT nom type_element>
nomestlenomdel
’
élémentettype_elementestletypeauquell
’
élémentestassocié.Unélémentpeutêtredetype
texte, vide (EMPTY), séquence ou choix d
’
éléments. Dans ces deux derniers cas, la liste des éléments enfants est
indiquée.
Unélémenttexteestprécisépar
#PCDATA
.
<!ELEMENT titre (#PCDATA)>
UnélémentvideutiliselemotcléEMPTY
.
<!ELEMENT titre EMPTY>
DansledocumentXML,l
’
élémentvideserareprésentépar :<monelement/>.Unélémentvidepeutparcontreposséder
desattributs.
Lorsdeladéclarationdeséquenceoudechoixd
’
éléments,uneindicationd
’
occurence(?,+ou*)peutêtreattribuéeà
chaqueélémentenfant.
<!ELEMENT elt0 (elt1, elt2?, elt3+, elt*)>
●elt1necontientaucuneindicationd
’
occurrence,ildoitdoncapparaîtreuneseuleetuniquefoisdansl
’
élément
elt0(1et1seul).
●elt2apourindicationd
’
occurrence?,l
’
élémentdoitapparaîtreaumaximumunefoisetilpeutnepasapparaître
Leséléments
- 5 -© ENI Editions - All rigths reserved
dutout(0ou1).
●elt3apourindicationd
’
occurrence+,l
’
élémentdoitapparaîtreaumoinsunefoisetautantdefoisquel
’
auteurle
désire(1ouplusieurs).
●elt4apourindicationd
’
occurrence*,l
’
élémentdoitapparaîtreautantdefoisquel
’
auteurledésire(ilpeutne
pasapparaîtredutout)(0ouplusieurs).
Uneséquenced
’
élémentsestunelisteordonnéed
’
élémentsdevantapparaîtrecommeélémentsenfantsdel
’
élément
quiestentraind
’
êtredéfini.Cederniernepourraconteniraucunautreélémentqueceuxfigurantdanslaséquence.
Cettelisteestcomposéed
’
élémentsséparéspardesvirgulesetestplacéeentreparenthèses.Chaqueélémentdoit
êtredéclaréparailleursdanslaDTD.DanslefichierXML,lesélémentsdoiventapparaîtredansl
’
ordredelaséquence.
<!ELEMENT elt0 (elt1, elt2, elt3)>
Ilestpossiblebiensûrd
’
utiliserlesindicateursd
’
occurrence.
<!ELEMENT elt0 (elt1, elt2?, elt3+, elt*)>
DesattributspeuventêtreprésentsdansundocumentXML, la DTDpermetdoncdedéfinirdescontraintessurces
attributs.Lemotclédedéclarationd
’
unattributest
ATTLIST
.Chaqueattributpeutêtrerequis,optionneloufixeetavoir
unevaleurpardéfaut.
Unattributpeutavoirunevaleurpardéfaut.
<!ELEMENT elt(...)>
<!ATTLIST elt attr CDATA "valeur">
Unattributpeutêtreobligatoire.
<!ELEMENT elt (...)>
<!ATTLIST elt attr CDATA #REQUIRED>
Untelattributestobligatoire.SonabsencedéclencheuneerreurduvérificateursyntaxiquesurlefichierXML.
Unattributpeutêtreoptionnel.
<!ELEMENT elt (...)>
<!ATTLIST elt attr CDATA #IMPLIED>
Unattributpeutêtrefixe.
<!ELEMENT elt (...)>
<!ATTLIST elt attr CDATA #FIXED "valeur">
L
’
attributnepeutdoncprendrequ
’
uneseulevaleurfixée.
LasyntaxeXMLaétéprésentéemaisilestimportantdemontrersonutilisationavecunsimplefichierXHTMLetavecun
fichierdeconfigurationd
’
uneapplicationTomcat.
Ci
dessous, un exemple d
’
un fichier simple en XHTML. Nous remarquons l
’
utilisation d
’
une DTD (grammaire) PUBLIC
proposéeparleserveurWebduW3C.Nouspouvonségalementnoterquechaquebaliseestfermée,correctement
imbriquéeetquecertainsélémentspossèdentdesattributs(
<img/>
).
<!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1 -strict.dtd">
<html>
<head><title>Mon Titre</title></head>
<body>
<h1>Ma page</h1>
<img src="monimage.png" width="300"/>
Lesséquencesd’
éléments
Ladéclarationd’
attributs
Exemplesconcrets
- 6 - © ENI Editions - All rigths reserved
</body>
</html>
Une application déployée avec Tomcat est configurée par l
’
intermédiaire d
’
un fichier nommé web.xml. Une syntaxe
possibledecefichierestlasuivante :
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app
PUBLIC " -//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web -app_2_2.dtd"
>
<web-app>
<description>Une application déployée avec Tomcat</description>
<servlet>
<servlet -name>DisplaySource</servlet -name>
<display -name>DisplaySource</display -name>
<description>display source of sample jsp pages
</description>
<servlet -class>org.displaytag.sample.DisplaySourceServlet
</servlet -class>
<servlet -mapping>
<servlet -name>DisplaySource</servlet -name>
<url-pattern>*.source</url -pattern>
</servlet -mapping>
<mime -mapping>
<extension>css</extension>
<mime -type>text/css</mime -type>
</mime -mapping>
<welcome -file-list>
<welcome -file>index.jsp</welcome -file>
<welcome -file>index.html</welcome -file>
</welcome -file-list>
<error -page>
<error -code>404</error -code>
<location>/404.jsp</location>
</error -page>
</web-app>
Nousremarquonsquecefichierdeconfigurationd
’
uneapplicationTomcatdoitêtreconformeàlaDTDPUBLICdeSun
Microsystems (
<!DOCTYPE web
app PUBLIC "
//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web
app_2_2.dtd">
).
DansunéditeurtelqueEclipse,lasyntaxedufichierdeconfigurationseradoncvérifiéeentempsréelgrâceàune
connexionInternet.Parexemple,iln
’
estpaspossibledeplacerlesbalises
<welcome
file
list>aprèslesbalises
<error
page>
cequiesttrèsutileetpermetd
’
augmenterlafiabilitéduprojet.
- 7 -© ENI Editions - All rigths reserved
LesfichiersdeconfigurationTomcat
Leprincipal fichierdeconfiguration duserveur Tomcats
’
appelleserver.xmletsetrouvedanslerépertoire
/conf
du
serveur.IlfournitàTomcattouslesparamètresnécessairespourlefonctionnement,lesportsàécouter,leshôtes
virtuels,lesinstancesdeprocesseurHTTPetlesclassesàutiliserpourgérerlesconnexionsentrantes.Cefichierest
auformatXMLetpossèdeunebalisededéclarationXML(prologue)maisiln
’
estliéàaucunfichierdevalidationDTDou
schéma.Cefichierestdoncbienformémaisnonvalide.CommetoutfichierXML,lelangageestsensibleàlacasseetle
fichier server.xml doit utiliser des balises commençant par une majuscule suivie de lettres en minuscules. Lors du
démarrage,Tomcatvérifielasyntaxedesélémentsdéclarésdanscefichier.Tomcatcomprendunprocessusprincipal,le
serveurquicontientlui
mêmeplusieurssous
composantsquisontutiliséspourtraiterlesrequêtes.
<?xml version="1.0" encoding="UTF -8"?>
<Server>
<Listener className="org.apache.catalina.mbeans.Global
ResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.storeconfig.Store
ConfigLifecycleListener"/>
<Listener className="org.apache.catalina.mbeans.ServerLife
cycleListener"/>
<GlobalNamingResources>
<Environment
name="simpleValue"
type="java.lang.Integer"
value="30"/>
<Resource
auth="Container"
description="User database that can be updated and saved"
name="UserDatabase"
type="org.apache.catalina.UserDatabase"
pathname="conf/tomcat -users.xml"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"/>
</GlobalNamingResources>
<Service
name="Catalina">
<Connector
port="8080"
redirectPort="8443"
minSpareThreads="25"
connectionTimeout="20000"
maxSpareThreads="75"
maxThreads="150"
maxHttpHeaderSize="8192">
</Connector>
<Connector
port="8009"
redirectPort="8443"
protocol="AJP/1.3">
</Connector>
<Engine
defaultHost="localhost"
name="Catalina">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
<Host
appBase="webapps"
name="localhost">
</Host>
</Engine>
</Service>
</Server>
L’
élément
<Server>
estlaracinedufichierserver.xml.Ilreprésentel
’
instancedeserveurTomcat.Cetélémentutilise
trois attributs et contient tous les autres éléments qui contrôlent le serveur. Le premier attribut facultatif est
className. Cet attribut correspond à la classe à utiliser en tant que serveur Tomcat. Tomcat utilise généralement
l
’
implémentationpardéfaut.Lesdeuxautresattributssont
port
etshutdown.Ilscontrôlentleservicepourécouterles
commandesd
’
arrêtduserveur.
Lefichierserver.xml
- 1 -© ENI Editions - All rigths reserved
L’
élément
<Service>
regroupelesélémentsquipermettentlaconnectivitéauserveurainsiquelemoteurd
’
exécution
deTomcat.Leseulattributobligatoiredecettebaliseestname.Cetattributpermetd
’
affecterunnomauservicequi
s
’
affichedanslesfichiersjournaux.LenompardéfautdecetattributestCatalina
.
L’
élément
<Executor>permetdegérerlesthreads(sous
processus)àl
’
intérieurdelamachinevirtuelleJava.Chaque
thread est dédié au traitement d
’
une requête client et à l
’
envoi de sa réponse. Pour éviter les créations et
suppressionsinutilesdethreads,Tomcat6.Xutiliseunmécanismenommépooldethreads.Plutôtquededétruireun
threadaprèstraitementd
’
unerequêteclient,celui
ciestrecyclé.Lepooldethreadsestdimensionnéavecunevaleur
initialequipermetdedéfinircombiendethreadsdoiventêtrecréésdedansetlenombremaximumdethreads.Ces
deuxparamètressontrespectivementconfigurésàpartirdesattributsminSpareThreadsetmaxThreadsdel
’
élément
<Executor>
.
L’
élément
<Connector>
:cetélémentfilsdelabalise
<Service>
permetd
’
implémenterlacouchetransportdesrequêtes
clients vers le moteur de Servlets Tomcat (Catalina). Depuis la version 5.0 de Tomcat, il existe deux types de
connecteursselonleprotocole,àsavoirHTTPetAJP.Lechoixdetransiterparl
’
unoul
’
autredesprotocolessefaitavec
l
’
attribut
protocol
.L
’
attribut
port
permetd
’
indiquerleportàl
’
écouteduconnecteur.Pardéfaut,Tomcatutiliseleport
8080 pour le HTTP et le port 8009 pour le connecteur AJP. L
’
attribut address permet de spécifier une adresse IP
particulièrepourécouterlesconnexionsentrantes.Enfin,l
’
attributsecurepermetdedéfinirunconnecteurHTTPS.
L’
élément
<Engine>
estchargéderépartirtouteslesrequêtes.C
’
estlemoteurdeServletsCatalinadeTomcat.Une
applicationestobligatoirementassociéeàunélément
<Engine>
.Lesdeuxattributsobligatoiresdecetélémentsont
nameetdefaultHost.L
’
attributnamepermetd
’
identifierlemoteurdeServletsetl
’
attributdefaultHostpermetdedéfinir
lequeldeséléments<Host>delaconfigurationvarecevoirlesrequêtesencasdenoncorrespondancedenomd
’
hôte
(hôtepardéfaut).
L’
élément
<Host>:leconteneur<Host>permetdeconfigurerlesattributsàassocieràunhôteunique.Lapossibilité
deconfigureruneseulemachinepourservirplusieurshôtesoffreunesouplesseconsidérable.L
’
hébergementvirtuel
estunetechniqueutiliséeparlesserveursHTTP,permettantd
’
hébergerplusieurssitesdistinctsàunemêmeadresse
IP. Par exemple, lorsqu
’
un client saisit l
’
adresse : http://www.monsite.com/accueil.html, la requête suivante est
envoyée
GET /accueil.html HTTP/1.1
Host : www.monsite.com
LesystèmeDNSvapermettreaunavigateurdetrouverl
’
adresseIPduserveurWebàcontacteretlarequêtesera
envoyéeauserveurindiqué.Unautresitehttp://www.monentreprise.compeutêtrehébergésurlamêmemachineet
doncposséderlamêmeadresseIP.C
’
estdanscecasprécisquel
’
élément<Host>estutilisépouridentifierdefaçon
uniqueetpréciselesite.Doncautantd
’
hôtesquenécessairepeuventêtreconfigurésdansunserveurTomcatetc
’
est
l
’
attributnamequipermetdepréciserlenomd
’
hôte.
Siunclientaccèdeauserveurdirectementparl
’
adresseIP,leserveurseraalorsdansl
’
incapacitéderésoudrelenom
d
’
hôte.L
’
attributdeconfigurationdefaultHostdel
’
élément
<Engine>
permetdoncdedéfinirl
’
hôtequiseracontacté
danscecasprécis.
Il peut être intéressant de mapper plusieurs noms de domaine sur un contenu unique. Par exemple, les sites
www.monentreprise.cometmonentreprise.compeuventêtreutilisésaveclemêmehôte.Danscecas,lesnomsd
’
hôtes
doiventretourneruncontenuidentique,ilfautalorsutiliserl
’
élément
<Alias>
àl
’
intérieurduconteneur<Host>
.
<Engine defaultHost="www.monsite.com">
<Host name="www.monsite.com">
...
</Host>
<Host name="www.monentreprise.com">
<Alias>monentreprise.com</Alias>
...
</Host>
</Engine>
L
’
autre attribut obligatoire est appBase. Il permet de spécifier le répertoire racine dans lequel sont stockées les
applications accessibles via cet hôte. La valeur par défaut est webapps et correspond au répertoire
/webapps
de
Tomcat.
Les autres attributs de configuration permettent de gérer le déploiement des applications. Entre autres, l
’
attribut
autoDeploypermetd
’
indiqueràTomcatsilesapplicationsdéposéesdanslerépertoiredeswebapps(indiquédoncpar
appBase)doiventêtreautomatiquementdéployéessansredémarrageduserveurpourunhôte.Pardéfautlavaleurest
autoDeploy=
’’
true
’’
cequiesttrèsintéressantpourunserveurendéveloppement,maisilobligeTomcatàsurveilleren
permanence le contenu de ce répertoire. Ce procédé est très coûteux en terme de ressources sur un serveur en
production.
L
’
attribut
liveDeploy
indique qu
’
un nouveau projet déposé dans le répertoire /webapps doit être rechargé
automatiquementauniveauduprojetetpasd
’
unhôte.
L
’
attributdeployOnStartuprenddisponibletouteslesapplicationsaudémarragedeTomcat,cequiestévidemmentla
valeurpardéfaut.
- 2 - © ENI Editions - All rigths reserved
L
’
attributunpackWARspermetdedécompresserlesfichiersd
’
archivesWARcequiestlecaspardéfaut.
L
’
attribut
deployXML
permetd
’
autoriserledéploiementdesapplicationsvialesfichiersdecontexteXML.
Enfin,lerépertoireworkDirpermetdespécifierunrépertoiredetravailpourlesapplications.LesclassesdesServletset
desJSPserontgénéréesdanscerépertoire.Chaqueapplicationpossèdesonpropresous
répertoire.Pardéfaut,ce
répertoireestplacédecettefaçon :/work/Catalina/nom_hôte
.
L’
élément
<Context>
quiestunebalisefilledel
’
élément<Host>permetdedéployeruneapplicationWebdansTomcat.
UncontextepermetderelieruneURLàuneapplicationWeb.
Cetélémentestutilisépourdéclarerexplicitementuneapplication,ilpeutêtreutilisédanslefichierserver.xmloubien
dans les fichiers de contexte XML, c
’
est cette dernière méthode qui est préconisée avec Tomcat 6.X. La balise
<Context>
possède deux attributs obligatoires afin de préciser le répertoire qui contient l
’
application et l
’
URLpour
accéderàcetteapplication.
L
’
attributdocBasepermetdefaireréférenceaurépertoiredesdonnéesdel
’
applicationoubiendirectementaufichier
WARdel
’
application.
L
’
attributpathpermetd
’
indiquerlechemindecontextedecetteapplicationWeb.Cechemincommencetoujoursparle
caractère/,chaqueapplicationdoitposséderunevaleuruniquedecetattribut.L
’
attributfacultatif
reloadable
permet
d
’
activerunesurveillancedesrépertoires/WEB
INF/lib
et/WEB
INF/classesdel
’
application.
ChaquemodificationapportéeaucontenudecesrépertoiresseraautomatiquementpriseencompteparTomcatqui
rechargeraalorsautomatiquementl
’
application.Ilestpréférabled
’
utiliserl
’
outilmanagerduserveurd
’
applicationspour
rechargerlesclassesetainsiéviterungaspillageinutiledesressources.
L
’
attributfacultatifworkDirpermetdespécifierlerépertoiredetravailpourl
’
application.Sicetattributestspécifiédans
l
’
hôte,ilsurchargealorsceluidéfinidansl
’
élément<Host>
.
L
’
attribut facultatif
cookie
permet d
’
indiquer si le serveur utilise les cookies pour gérer les sessions utilisateurs. La
valeurpardéfautesttrue.
<Host name="monsite.com" appBase="/usr/local/tomcat/webapps/monsite"
debug="0" unpackWARs="true" autoDeploy="false" liveDeploy="false">
<Context path="" docBase="." debug="1" reloadable="false">
</Context>
</Host>
Tomcatutiliseuncontextepardéfautquiestmisenœuvredanslefichier/conf/context.xml
.
L’
élément
<Realm>
:laplate
formeJavaEEdéfinitunmécanismestandardbasésurlanotionderôlespourlagestion
desauthentificationsdanslesapplicationsWeb.Un
<Realm>
peutêtredéfinientantqu
’
élémentenfantdesbalises
<Engine>
,
<Host>ou
<Context>
.Suivantleplacement,l
’
authentificationseraappliquéeàtoutlemoteurdeServlets,à
unhôteparticulierouàuneapplication.
<Host name="www.monsite.com" appBase="/usr/local/tomcat/webapps/monsite"
debug="0" unpackWARs="true" autoDeploy="false" liveDeploy="false">
<Alias>monsite.com</Alias>
<Alias>mesites.com</Alias>
<Context path="" docBase="." debug="0" reloadable="false">
<Resource name="jdbc/monsitemysql" auth="Container"
type="javax.sql.DataSource"
username="admin"
password="43tZAE3"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/monsite"
maxActive="20"
maxIdle="10"
maxWait="10000"
logAbandoned="true"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/monsitemysql" localDataSource="true"
userTable="administrateur"
userNameCol="nomadministrateur"
userCredCol="motdepasseadministrateur"
userRoleTable="roles"
roleNameCol="role"
/>
</Context>
</Host>
- 3 -© ENI Editions - All rigths reserved
L’
élément
<Loader>
définitunchargeurdeclasseJava.LerôledecetélémentestdechargerlesclassesJavad
’
une
application Web. Le serveur charge les classes contenues dans le répertoire /WEB
INF/classes de l
’
application, les
classescontenuesdanslesfichiersJARprésentsdanslerépertoire/WEB
INF/lib
etlesclassesrenduesaccessiblesaux
applicationsparlemoteurdeServlets.
L’
élément
<Manager>
permetdeconfigurerlegestionnairedesessionpouruneapplicationWebspécifique.Pardéfaut,
unconteneurWebJavaEEstockelesinformationsdesessionutilisateurdanslaMachineVirtuelleJava.Enplusd
’
être
stockéesenmémoire,lessessionsutilisateurssontsauvegardéessoitdansunebasededonnéessoitdansunfichier
parlaclassed
’
implémentationorg.apache.catalina.session.PersistentManager
.
L’
élément
<Valve>
représenteuncomposantsousformed
’
uneclasseJava.Cetélémentpeutêtreconsidérécommeun
filtrederequêtes.Voicilesdifférentesvaleurspossiblespourl
’
attributclassNameetletypedefiltreassocié :
●org.apache.catalina.valves.AccessLogValve :génèreunfichierjournaldesaccèsauserveur.
●org.apache.catalina.valves.JDBCAccessLogValve :génèreunfichierjournaldesaccèsàunebasededonnées.
●
org.apache.catalina.valves.RemoteAddrValve
:appliqueunerestrictiond
’
accèsenfonctiondesadressesIPdes
clients.
●org.apache.catalina.valves.RemoteHostValve :appliqueunerestrictiond
’
accèsenfonctiondesnomsdemachines
desclients.
●org.apache.catalina.valves.RequestDumperValve :génèreunfichierjournaldesrequêtesdesclients.
●org.apache.catalina.authenticator.SingleSignOn :permetuneauthentificationuniqueentreplusieursapplications.
IlestimportantdenoterquenouspouvonsimplémentervotreproprefiltreenécrivantuneclasseJavasurlemodèle
decellesdéjàfournies.
Exemple :
AvecJDBCAccessLogValve,lecodesuivantpeutêtreinsérédanslefichierdeconfigurationserver.xml
.
<Engine...>
...
<Valve className="org.apache.catalina.valves.JDBCAccessLogValve"
connectionURL="jdbc:mysql://localhost:3306/stattomcat?
user=monutilisateurmysql&password=monmotdepasse"
driverName="com.mysql.jdbc.Driver"
tableName="log"
resolveHosts="false"
pattern="common"/>
....
</Engine>
L
’
attribut connectionURL permet de spécifier la base de données et les coordonnées de connexion à la base de
données.
L
’
attributdriverName permet de spécifier le pilote JDBC d
’
accèsàla basededonnées. Celui
cidevra biensûr être
installédanslerépertoire
/lib
deTomcat.
L
’
attributtableNamepermetdepréciserlenomdelatablequivarecevoirlesdonnéesdesjournaux.
L
’
attributresolveHostspermetderemplacerl
’
adresseIPduclientparlenomdemachine.
Enfin,l
’
attributpatternpermetdedéfinirleformatd
’
entréedesfichiersjournaux.
Labasededonnéesdoitavoirlasyntaxesuivante :
CREATE DATABASE "stattomcat";
USE "stattomcat";
CREATE TABLE "log" (
"id" INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
"remoteHost" CHAR(15) NOT NULL DEFAULT "",
"user" CHAR(15),
"timestamp" TIMESTAMP NOT NULL DEFAULT 0,
"virtualHost" VARCHAR(64) NOT NULL DEFAULT "",
"method" VARCHAR(8) NOT NULL DEFAULT "",
"query" VARCHAR(255) NOT NULL DEFAULT "",
- 4 - © ENI Editions - All rigths reserved
"status" INTEGER UNSIGNED NOT NULL DEFAULT 0,
"bytes" INTEGER UNSIGNED NOT NULL DEFAULT 0,
"referer" VARCHAR(128),
"userAgent" VARCHAR(128),
PRIMARY KEY("id")
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
L’
élément
<Listener>permetdedéfinirunécouteurd
’
événementssurleséléments
<Server>
,
<Host>ou
<Engine>
.
Cetélémentnedisposequed
’
unseul attributobligatoire nomméclassName quiest la classe Java quiimplémente
l
’
écouteur.Tomcatpossèdepardéfautdeuxécouteursdéfinissurl
’
élément
<Server>
.Cesécouteurs permettentla
supervisionglobaleduserveurainsiquedesressourcesJNDI.LesobjetsMBeansdesupervisionsontdéfinisetutilisés
parl
’
APIJavaJMX(JavaManagementExtension)quenousverronsdanscechapitre.
ParmilesautresfichiersdeconfigurationdeTomcat,ilexistelesfichierstomcat
users.xml
,
catalina.policy
etweb.xml
.
Ces fichiers sont présents dans le répertoire
/conf
de Tomcat. Les fichiers tomcat
users.xml et
catalina.policy
sont
utiliséspourlasécuritéduserveuretlefichierweb.xmldéfinitlesapplicationsWebdéployéessurleserveur.
- 5 -© ENI Editions - All rigths reserved
Lefichierdeconfigurationdesapplications
Le fichier web.xml est un autre fichier de configuration. Un fichier web.xml est un descripteur de déploiement
d
’
applications Web Java EE. Le fichier
/conf/web.xml
de Tomcat définit les paramètres de configuration utilisés par
touteslesapplicationsWebinstalléessauf,silesapplicationsfournissentleurproprefichierdeconfigurationweb.xml
.
Parlasuite,touteslesapplicationsdéployéesutiliserontleurproprefichierweb.xml
.
CefichierdeconfigurationcommenceparladéclarationdeServletsquisontspécifiquesàTomcat.LaServletpardéfaut
deTomcatestdéfinieaveclaclasseDefaultServletetsonattributlistingsquipermetd
’
autoriserounonl
’
indexation.
La Servlet InvokerServlet permet de déclencher des Servlets directement avec des URL
http://serveur/application/servlet/nomdelaclasseservlet.
LatroisièmeServletestJspServletetpermetdetransformerdespagesJSPenServlet.
Les sections suivantes de ce fichier de configuration concernent les paramètres Java EE : par exemple, le temps
d
’
expirationdessessions,lestypesMIMEdesen
têtesHTTP,lespagesd
’
accueil(ex:index.html,index.jsp...).
Lefichierweb.xml
- 1 -© ENI Editions - All rigths reserved
Lefichierdeconfigurationdesutilisateurs
Cefichierestutilisé pourlesauthentificationsde Tomcat. Tomcatutiliselesystème d
’
authentificationbasésurune
connexionJNDI.Cegestionnaired
’
authentificationestassociéaufichiertomcat
users.xmlcontenantlesassociations
identifiant,motdepasseetrôle.LapartiemanageretadministrationdeTomcatutilisecefichierd
’
authentification.
Ladéclarationdecesystèmed
’
authentificationseretrouvedanslefichierdeconfigurationduserveur/conf/server.xml
.
<!-- Global JNDI resources -->
<GlobalNamingResources>
<!-- Test entry for demonstration purposes -->
<Environment name="simpleValue" type="java.lang.Integer" value="30"/>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users -->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat -users.xml" />
</GlobalNamingResources>
Voicilecontenudecefichierquisetrouvedanslerépertoire :/conf/tomcat
users.xml
.
<?xml version= ’1.0’ encoding= ’utf-8’?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="role1" password="tomcat" roles="role1"/>
<user username="admin" password="admin" roles="admin,manager"/>
</tomcat -users>
Nousremarquonsladéfinitiondesrôlesetdesutilisateursavecleurmotdepasse.
L
’
applicationmanagerdeTomcatn
’
estaccessiblequeparlesutilisateursquipossèdentlerôlemanager,déclarédans
cefichier.Ilserapossibledemodifiercefichierpourréaliseruneauthentificationquireposesurunebasededonnées
ouunannuaire.
Lefichiertomcat
users.xml
- 1 -© ENI Editions - All rigths reserved
Lefichierdeconfigurationdelasécurité
LegestionnairedesécuritédelamachinevirtuelleJavaestappeléSecurityManageretpermetd
’
assurerlasécuritédes
applications exécutées sur le système. Par l
’
intermédiaire de ce gestionnaire, il est possible de régler l
’
accès aux
ressources que le programme Java peut utiliser. Par défaut, Tomcat est exécuté sans le gestionnaire de sécurité
SecurityManager.Lesapplicationsdéployéessurleserveurpeuventdonclire,écrireetsupprimerdesfichiers,lancer
des programmes et des commandes système, ouvrir des flux à travers le réseau, gérer la machine virtuelle, voire
l
’
arrêter...
Pour lancer Tomcat en mode sécurisé, il faut utiliser l
’
option
security sur le script de démarrage (startup.bat ou
startup.sh
).
Danscecasprécis,toutcequin
’
estpasexplicitementprécisédanslefichier
catalina.policy
estinterdit.
Lefichiercatalina.policy
- 1 -© ENI Editions - All rigths reserved
Arborescenced
’
unprojetTomcat
UneapplicationWebestunensemblederessourcesquiparticipentaufonctionnementdel
’
application.Uneapplication
estconstituéedeplusieurséléments :
●Lecomposantserveurdynamique :ServletsetJSP.
●LeslibrairiesdeclassesJavautilitaires.
●LesélémentsWebstatiques :pagesHTML,images,feuillesdestyle,JavaScript...
●Lescomposantsclientsdynamiques:Applets,JWS,JavaBean.
●Undescripteurdedéploiementetdeconfigurationdel
’
applicationWebsouslaformed
’
un(ouplusieurs)fichier
(s)deconfigurationauformatXML :web.xml
.
Les spécifications de l
’
API Servlet indiquent l
’
organisation des dossiers et fichiers à respecter pour déployer une
applicationWeb.L
’
applicationestconfiguréeparlefichierXML :/monapplicationweb/WEB
INF/web.xml
.
L
’
arborescencedel
’
applicationWebestlasuivante :
Ledossiernommédansl
’
exemplemonapplicationwebreprésentelaracinedel
’
applicationWeb.N
’
importequelnompeut
luiêtredonné,engénéralc
’
estlenomdel
’
application(ex :facturation,boutique...).Unefoisenproductioncedossier
estpublic,nousretrouvonsdonclesfichiersHTML,JavaScript...
LedossierMETA
INFcontientlefichiermanifest.mfquiestgénéréparl
’
archiveur
jar.exe
lorsqu
’
unearchiveduprojetest
réalisée pour le déployer. Ce fichier permet de stocker des informations concernant l
’
archive comme sa version, le
contenu,laversionducompilateur...
Exemple :
Manifest-Version: 1.0
Created-By: 1.5.0_04 (Sun Microsystems Inc.)
Ledossier WEB
INFreprésente lapartie privéede l
’
application Web. Il contient le fichier web.xmlquireprésente le
descripteurdedéploiement.
Lesous
dossier/classesdudossierWEB
INFcontientlesfichiers.class(compilés)del
’
application(Servlets,JavaBean,
classesutilitaires...).
Lesous
dossier/srcdudossierWEB
INFcontientlesfichiers
.java
(sources)del
’
application(Servlets,JavaBean,classes
utilitaires...).
Lesous
dossier
/lib
dudossierWEB
INFpeutcontenirdeslibrairies
.jar
utilesaufonctionnementdel
’
applicationWeb.
Parexemple,lalibrairiedegestiond
’
e
mails,leslibrairiesdeStruts,leslibrairiesdemanipulationdecodeXML,des
fichiers
.dtd
et
.tld
...
Lesous
dossier/ressourcesdudossierWEB
INFcontientlesfichiersdeconfigurationpourlesapplications:parexemple
lestraductions,lesfichiersdegestiondeLog4J...
Une application Web utilise fréquemment des fichiers ressources qui doivent être placés dans la même
arborescence que les classes, c
’
est
à
dire dans le répertoire /WEB
INF/classes. Cependant, ces fichiers
ressourcesserontplacésplutôtdansledossier/WEB
INF/srcsachantqu
’
Eclipserecopieautomatiquementlesfichiers
decerépertoireverslerépertoire/WEB
INF/classes.Eneffet,lorsdurafraîchissementetdelacompilationcomplète
despages,Eclipsevidelerépertoire/WEB
INF/classescequiestcontraignant.
UneapplicationWebestaccessiblepardéfautàl
’
adresse :http://nomhote:port/webapp/
- 1 -© ENI Editions - All rigths reserved
CesecondexempleprésenteuneapplicationJavaEEpluscomplexeavec:
●lerépertoire/adminpourl
’
administration,
●lerépertoire/csspourlesfeuillesdestyle,
●lerépertoirepourles/imagesetpourles/javascript
,
●lerépertoire/jspquicontientlesJSPnoncompilées,
●lerépertoire/workquicontientlesJSPcompilées,
●lerépertoire
/logs
quipermetdestockerlesfichiersjournauxLog4J.
Bien sûr, il n
’
est pas nécessaire de connaître par cœur les noms des fichiers, répertoires et l
’
organisation de
l
’
arborescencepourdévelopperuneapplicationJavaEE.DesoutilsdedéveloppementévoluéscommeEclipsegénèrent
automatiquementl
’
arborescenceàlasuitedelacréationd
’
unprojetTomcat/JavaEE.
1.Ledescripteurdedéploiementweb.xml
Le fichier web.xml contient des informations qui permettent de définir l
’
environnementd
’
exécution de l
’
application
Internetetdelierlescomposantsentreeux.LefichierpermetdedéfinirlesrelationsentrelesURLetlesServlets/JSP,
lapaged
’
accueil,lapaged
’
erreur,lescontraintesdesécurité,lesauthentifications,lesressourcespouraccéderaux
données... Chaque application Web possède son propre fichier de configuration web.xml placé dans le
répertoire/WEB
INFdel
’
application.Cefichierestappeléledescripteurdedéploiementdel
’
applicationouwebapp.
Undesavantagesdel
’
utilisationd
’
unfichierauformatXMLestlecaractèreautodescriptifdecelangage.Lefichier
web.xml commence par déclarer l
’
encodageetle typedegrammaireutilisée pourvérifiersa syntaxe (DOCTYPEou
schémaXML).LefichierreposesuruneDTD,ilestdoncimportantderespecterlasyntaxeetl
’
ordredesbalises.En
réalité,uneapplicationsimplen
’
apasbesoindecedescripteurcarlefichierweb.xmlduserveurpeutsuffiremaisc
’
est
unebonnehabitudequedetoujoursdéfinirunfichierdedéploiementavecuneapplication.
Tomcatpossèdeundescripteurdedéploiementpardéfautquiestutilisépourlesapplicationsetquiestplacédansle
répertoire
/conf
.
Ledescripteurdedéploiementpermetdedécrirelesélémentssuivants :
●lesparamètresd
’
initialisationdesServlets;
●lesvariablesglobales;
●lasessiondel
’
application;
●lesliensentrelesURI(UniformResourceIdentifier)etlesServlets;
●lesclassesdegestiondesévénements;
- 2 - © ENI Editions - All rigths reserved
●lesdéclarationsdesfiltres;
●lesliensentrelestypesMIMEetlesapplicationscorrespondantes;
●lespagesd
’
accueilpardéfaut;
●lesliensentrelescodesd
’
erreurHTTPetlespagesd
’
erreur;
●lesressourcestellesqueJDBC,JNDI...
●lesressourcesEJB.
L
’
élémentracinedudescripteurdedéploiementestlabalise
<web
app>
.Touslesautresélémentsserontdoncdes
enfantsdecettebalise.Voicici
dessouslalisteexhaustiveordonnéedesbalisesdufichierdedéploiementweb.xml
.
L’
élément
<icon>
permetdespécifierlecheminversuneicônequipermetdereprésenterl
’
applicationWebpourdes
outilsdegestiond
’
applications.
L’
élément
<display
name>:cenœudpermetd
’
assignerunnomàl
’
applicationWeb.Cenompeutêtreutiliséparun
outildegestiond
’
applications.
L’
élément
<description>
:cenœudpermetdedécrirel
’
applicationdemanièretextuelle.
L’
élément
<distributable>
estunebalisevidequipermetdedistribuerl
’
applicationsurplusieursserveurs(exemple :
clusterdeserveursTomcat).
L’
élément
<context
param>
: ce nœudpermetde définir desparamètresqui seront valablespourl
’
application.Les
informationsserontpasséessousformed
’
unepairenom
valeurdansla
ServletConfig
etdoncaccessiblesdepuisdes
ServletsetpagesJSP.Ceprincipeesttrèssouventutilisépourdéfinirdeschemins,desfichiersdeconfiguration,des
identifiants... Un paramètre peut aussi être passé à une Servlet précise (et non à toutes comme avec <context
param>
)enutilisantlenœud
<init
param>
desServlets.
L’
élément
<filter>
permetdedéfiniruneclassedefiltrequiseraapplicableavantl
’
exécutiondelaServlet.Leprincipe
defonctionnementestsimilaireauxclassesTomcatValve.Unfiltrepeutpermettreparexempled
’
écrireunfichierXSL
pourrépondreàuncontenuXMLsilarequêteseterminepar.xmlouenHTMLsielleseterminepar.html
.
L’
élément
<filter
mapping>
:cenœudpermetd
’
associerunfiltreàuneServletspécifiqueouàuneURL.Lesfiltressont
mappésenrespectantl
’
ordredanslequelilsapparaissentdansledescripteurdedéploiement.
L’
élément
<listener>permetdedéfinirunécouteurquiseraappelésuiteàunévénementparticulier.
L’
élément
<servlet>
permetdedéfiniruneServletetlesattributs.LaServletseradéfinieparsonnometsaclasse.
L’
élément
<servlet
mapping>
:ce nœudpermetdemapperunmodèled
’
URIversunnomdeServletdéfinidansun
nœud
<servlet>
.
L’
élément
<session
config>
permetdedéfinirlasessiondel
’
applicationWebavecentreautresledélaid
’
expirationen
minutesdessessionsdel
’
application.
L’
élément
<mime
mapping>
:ce nœudmappeuneextensionautypeMIME.C
’
estuneassociationentrelesfichiers
publicsetdestypesMIME.
L’
élément
<welcome
file
list>:cenœudpermetdespécifierleoulesfichiers(parordredepriorité)quidoiventêtre
servisàpartird
’
unrépertoirelorsqueseullenomdurépertoireestspécifié(ex :index.html
,
index.jsp
).
L’
élément
<error
page>
permetdedéfinirunepaged
’
erreurquiserviraencasderenvoid
’
uncodeerronésuiteàune
exception.
L’
élément
<taglib>
permetdedéfinirl
’
emplacementdesbibliothèquesdebalises.Lenœudmappeunmodèled
’
URI
versunfichierdedescripteurdebibliothèquesdebalises.
L’
élément
<ressource
env
ref>
:cenœudconfigureuneressourceexternequipeutêtreutiliséeparlaServlet.
L’
élément
<ressource
ref>
:cenœudconfigureégalementuneressourceexternequipeutêtreutiliséeparlaServlet.
L’
élément
<login
config>
permet de configurer la méthode d
’
authentification et le nom du Realm à utiliser pour
l
’
application.
L’
élément
<env
entry>:cenœuddéfinitlenomd
’
uneressourceaccessiblegrâceàl
’
interfaceJNDI.
L’
élément
<ejb
ref>
permetdedéclareruneréférencedistanteàunEntrepriseJavaBean.
L’
élément
<ejb
local
ref>
permetdedéclareruneréférencelocaleàunEntrepriseJavaBean.
- 3 -© ENI Editions - All rigths reserved
2.Déployerunpremierprojet
Afindemettreenapplicationlesexplicationsdeceguide,nousallonsdéployerunepremièreapplicationsimple.La
configurationdesapplicationsWebdéployéesauseinduserveurTomcatsefaitàl
’
aidedufichier/conf/server.xmlou
d
’
unfichierspécifiqueparapplicationprésentdans/conf/Catalina/localhost/monapplication.xml
.
a.Projetsimplemanuellement
Cesfichierspeuventêtrecréésàlamaincarlastructureestassezsimplemaisnousallonsdansunpremiertemps
utiliserl
’
outilmanagerdeTomcatquipermetdedéployeruneapplicationavecuneinterfaceWeb.
Le lien
Tomcat Manager
ouvre une fenêtre d
’
authentification. Les informations de connexion correspondent aux
donnéesinséréeslorsdel
’
installationduserveur.
Nousobtenonsunepagequilistetouteslesapplicationsactuellementdéployéessurleserveur.
Pourdéployerunenouvelleapplication,ilfaututiliserl
’
interfaceenprécisantlesinformationspourledéploiement.La
première partie permet de préciser le contexte (chemin d
’
accès à l
’
application par URL). Le second paramètre
optionnelpermetd
’
indiquerlefichierXMLpourlaconfigurationdel
’
application.Ledernierparamètrecorrespondau
répertoire(ouaufichier.war)quicontientl
’
application.Sinousutilisonsunrépertoire,celui
cidoitd
’
abordêtrecréé
avantledéploiementdel
’
applicationetildoitrespecterl
’
arborescenceJavaEE(répertoireWEB
INF...).Sinousne
précisonspasdefichierdeconfigurationXML,leprojetseradéployédanslerépertoire
/webapps
deTomcatetsera
doncdirectementaccessiblesansfichierdeconfiguration.
Nousutilisonsl
’
applicationmonapplication1quiestunsimpleprojetJavaEEavecl
’
arborescencehabituelle(/WEB
INF
,
/WEB
INF/classes
,
/WEB
INF/lib
,
/WEB
INF/src) et un simple fichier JSP, index.jsp qui affiche juste le nom de
l
’
applicationenHTMLetenJava.
<h1>monapplication1</h1>
<% out.println("mon application1 en Java"); %>
- 4 - © ENI Editions - All rigths reserved
NouscliquonssurDeploy
.
Le déploiement a eu pour effet d
’
ajouter une copie complète du projet dans le répertoire des webapps de
Tomcat
/webapps
.Donc,iln
’
yapasd
’
insertiondelaconfigurationdanslefichierserver.xmlnidecréationd
’
unfichier
particulierpourlecontexte(/conf/Catalina/localhost)cartouslesfichiersdéployésdanslerépertoiredeswebappsdu
serveursontautomatiquementexploitables.
Leprojetaétécopiédanslerépertoiredes
/webapps
cequisignifiequedésormais,lesfichiersdevrontêtremodifiés
danscerépertoire/webapps/monapplication1
.
Notreapplicationestalorsaccessibleàl
’
adressesuivante :http://localhost:8080/monapplication1/
b.ProjetsimpleavecEclipse
PourcréerunprojetTomcatavecEclipse,nousallonsutiliserlacommandeFileNewProject
.
AvantdepouvoirutiliserTomcatavecEclipse,ilestnécessaired
’
installerleplug
inSysdeocommepréciséau
chapitreObjectifsetspécificationsdeJavaEEdeceguide.
- 5 -© ENI Editions - All rigths reserved
L
’
item
Projet Tomcat
permet de créer un projet Tomcat sans avoir à écrire le déploiement dans le fichier de
configurationdeTomcat(server.xml)ouunfichierparticulier(
/conf/Catalina/localhost/
)etilpermetégalementdecréer
automatiquementl
’
arborescenceduprojet.
LasecondepagenousdemandededéfinirunnompourleprojetEclipse.Nousprécisonslerépertoiresurledisque
(cerépertoirevidedoitêtrecrééavantledéploiement)quirecevraleprojetEclipseetl
’
intégralitédesesfichiers.
- 6 - © ENI Editions - All rigths reserved
Lors de cette dernière étape, nous indiquons le nom du contexte qui sera accessible à l
’
adresse :
http://localhost:8080/monapplication2.
Eclipse a alors automatiquement créé l
’
arborescence nécessaire au projet dans le répertoire sélectionné avec
location
.
Demême,unfichiernommémonapplication2.xmlaétécrééparEclipsedanslerépertoire/conf/Catalina/localhost
.
<Context path="/monapplication2" reloadable="true"
docBase="E:\monapplication2" workDir="E:\monapplication2\work" />
Nousretrouvonsleparamétragecourantd
’
uneapplicationWebaveclechemind
’
accèsàlawebapp(lecontexte
avecleparamètrepath
),l
’
attributquiindiquequel
’
applicationserarechargéeautomatiquement(
reloadable=
’’
true
’’
),
le répertoire d
’
accès aux fichiers de l
’
application (attribut docBase) et le répertoire des JSP compilées (attribut
workDir
).L
’
applicationmonapplication2estidentiqueentermedecontenuàmonapplication1
.
Nous pouvons vérifier le fonctionnement de l
’
application en demandant le contexte /monapplication2 via l
’
URL :
http://localhost:8080/monapplication2/
Dans une dernière étape, pour bien voir les différentes possibilités de déclaration d
’
applications, nous allons
déployeruneapplicationnomméemonapplication3avecEclipsemaisendéclarantl
’
applicationdirectementdansle
fichier de configuration du serveur (server.xml) et non dans un fichier spécifique au projet
(
/conf/Catalina/localhost/monapplication3.xml
).
Pourcela,danslemenud
’
Eclipse,nousutilisonslemenuWindows Preferences
Tomcat
etnousmodifionsla
partie
Déclarationdescontextes
enprécisantdésormaislefichierdeconfigurationduserveur :server.xml
.
- 7 -© ENI Editions - All rigths reserved
Danslemenud
’
Eclipsenousréalisonsalorsunnouveauprojet :FileNewProject
.
Ensuite,nousindiquonslerépertoire(quiestvidemaisquidoitdéjàexistersurledisquedur)del
’
application.
- 8 - © ENI Editions - All rigths reserved
Nousallonscréerunesimplepageindex.jspdanslerépertoiredemonapplication3
.
<h1>monapplication3</h1>
<% out.println("mon application3 en Java"); %>
Si nous testons maintenant l
’
application monapplication3, nous remarquons que celle
ci n
’
est pas accessible à
l
’
adressesuivante :http://localhost:8080/monapplication3/.
Eneffet,leprojetEclipsemonapplication3existeetilaétéinsérédanslefichier/conf/server.xmlenfindefichier.
...
<Context path="/monapplication3" reloadable="true"
docBase="E:\monapplication3" workDir="E:\monapplication3\work" />
</Host>
</Engine>
- 9 -© ENI Editions - All rigths reserved
</Service>
</Server>
Leproblème,c
’
estquelesapplicationsdéclaréesdanscefichiernécessitentunredémarrageduserveur.Aprèscette
action,laconnexionàl
’
adressesuivante :http://localhost:8080/monapplication3/estopérationnelle.
NousavonsvuplusieursmanièresdedéployerunprojetJavaEEavecTomcat.Nousremarquonsrapidementque
l
’
utilisationd
’
unfichierspécifiqueparcontexte(/conf/Catalina/localhost)estbeaucoupplussouplequeladéclaration
danslefichierdeconfigurationduserveur :server.xml
.
Eneffet, nousn
’
avons pas besoin de relancer le serveur, de lire tout le fichierserver.xml pour la gestion d
’
une
applicationetnousobtenonsunmeilleurdécoupage...
Pourlasuite,nousutiliseronsladéclarationdesapplicationsavecunfichierparapplication(/conf/Catalina/localhost
).
L
’
opérationdesuppressiond
’
unprojetavecEclipseesttrèssimple,ilsuffitdefaireunclicdroitsurlenomduprojet
etdevaliderl
’
action
delete
.Eclipsedemandealorssiladéclarationduprojetdoitêtresuppriméeetégalementle
répertoiredesfichierssourcesassociés.
3.DéployerunprojetJavaEEavecunfichierwar
Lorsdelamiseenproductiond
’
unprojet,unfichierd
’
empaquetage.warestutilisépourledéploiement.Danslesgros
projets,cefichierestcrééavecl
’
outilANTquenousverronsdansunprochainchapitre.Latotalitédel
’
applicationWeb
estalorscontenuedansunfichierportantl
’
extension.warpourWebARchive.Cefichierd
’
empaquetageestdestinéau
déploiement de l
’
application Web au sein du serveur d
’
applications. Par la suite, c
’
est le conteneur du serveur
d
’
applicationsquis
’
occupedelamiseenœuvredel
’
application.
L
’
outil
jar.exe
du JDK Java permet de créer l
’
application Web au format WAR. Pour l
’
utilisation de cet outil de
déploiement, nous allons réaliser une copie du répertoiremonapplication1 sous le nom monapplicationwar. Ensuite,
nousallonsjustechangerlecontenudelapageindex.jspenindiquantlenomdunouveauprojet.
<h1>monapplicationwar</h1>
<% out.println("monapplicationwar en Java"); %>
Désormais, nous possédons une application complète et utilisable. Pour réaliser la création du fichier .war, nous
ouvrons une console de commande et nous nous plaçons dans le répertoire de l
’
application afin de respecter
l
’
organisationdesfichiersauseindel
’
archive.Ensuite,nouslançonslacommande
jar
(àpartirduchemincompletsila
variabled
’
environnementn
’
apasétéprécisée).
Supprimerunprojet
- 10 - © ENI Editions - All rigths reserved
Leparamètre
cvf
permetdecréerlefichierMANIFEST.MFquidonneradesinformationssurlaversiondel
’
application,la
versiondeJava...Unfichiernommémonapplicationwar.warestalorscréé.
Pourdéployerceprojet,ilneresteplusqu
’
àdéposercefichierdanslerépertoire
/webapps
den
’
importequelserveur
compatibleJavaEE.Commeindiquéprécédemment,touteslesapplicationsdurépertoire
/webapps
d
’
uneapplication
sont automatiquement déployées. Il faut remarquer l
’
intérêt de développer avec un langage comme Java qui est
portable,plusbesoindesesoucierdel
’
environnementd
’
exécutionfinalsilestandardWARestrespecté.
Nousréalisonsjusteunecopiedenotrearchivedanslerépertoire
/webapps
deTomcat.Nouspatientonsquelques
instants et le serveur déploie automatiquement la nouvelle application en décompressant l
’
archive dans son
répertoire
/webapps
.Latraceduserveurdanscecasprécisestd
’
ailleursobservable :
Lanouvelleapplicationestdéployée,lerésultatestvisibleavecl
’
interfacemanagerdeTomcat.Ilestalorspossible
d
’
accéderdirectementàl
’
applicationsansdéclarationdansleserveurd
’
applications.
- 11 -© ENI Editions - All rigths reserved
Analyse,monitoringetsupervision
1.Présentation
LesystèmedejournalisationdeTomcatetdeJavaesttrèscomplexeetévolué.TomcatutiliselabibliothèqueJakarta
commons
logging
. Cette bibliothèque fait partie du projet Jakarta de la fondation Apache. Le projet contient un
ensemble de bibliothèques de développement Java très variées. La librairie commons
logging
est semblable à la
bibliothèqueLog4Jetpermetlajournalisationtoutcommel
’
API
java.util.logging
deJava.
LefichierdejournalisationdeTomcatutiliselefichierdeconfiguration
/conf/logging.properties
.C
’
estleprincipalfichier
deconfigurationpourleserveurmaischaqueapplicationdéployéepeutfournirsonproprefichier
loggin.properties
dans
sonrépertoire/WEB
INF/classes
.
Lamachine virtuelle de Java estresponsable de laréservation de l
’
espace mémoire nécessaire à l
’
application.Au
démarragedel
’
application,celle
ciréservedelamémoireauprèsdusystèmed
’
exploitation.Sil
’
applicationnécessite
plus de mémoire que cette valeur limite, alors la machine virtuelle s
’
arrête en déclenchant l
’
erreur
java.lang.OutOfMemory
Error.CetteerreuresttrèsfréquenteenJavaetdoitdoncêtreévitéeaumaximum.
Ilestdoncextrêmementimportantdemesureretsurveillerlaconsommationmémoireduserveurdanslamachine
virtuelle pour anticiper ce problème. La configuration de la machine virtuelle peut être visualisée par le biais du
manageràcetteadresse :http://localhost:8080/manager/status.
Onretrouvelaquantitédemémoiredisponible(Free Memory)danslamachinevirtuelle,lamémoireutilisable(
Total
Memory)etlamémoiremaximumallouableauprèsdusystème(MaxMemory
).
2.Testerlamontéeencharge
Nousréaliseronsparlasuitedestestsdemontéeenchargeavecl
’
outilJMeter.Ilestégalementpossibledevisualiser
lenombredeconnexionsJDBCdisponiblesàuninstantprécis,lenombredethreadsoccupés,lamémoireconsommée,
lesclasseschargées...
JavaManagementExtensionsJMXestuneAPIJavaconçuepourlasupervisiondesapplications.JMXestintégréeà
partirdelaplate
formeJava1.5.Actuellement,JMXestlestandardpourledéveloppementdesoutilsdesupervisionet
d
’
administrationdanslestechnologiesJava.
DansunemachinevirtuelleJava,ilestpossibled
’
associerauxdifférentsobjetsJavadescomposantsquipermettent
d
’
obtenirdesinformationssurl
’
exécutionetletraitementdesobjets.CescomposantssontappelésdesMBeans.Ces
MBeanssontaccessiblesvial
’
élémentcentral,leserveurMBeansquiestcapabled
’
utiliserdesprotocolesvariéspour
laconnexiond
’
outilsdesupervision.
DonclesdonnéesdesMBeanspeuventêtreaccédéesgrâceàunclientJMXenlocaloubienàdistance.Tomcatcrée
desMBeansdynamiquementpendantsonexécution,lorsquedesapplicationsdéployéesfonctionnentsurleserveur.
LesMBeansdynamiquesdeTomcatsontcréésgrâceauxélémentsdeconfigurationappelés<Listener>présentsdans
lefichierserver.xml
.
Les informations JMX sont disponibles à partir du manager à l
’
URL http://localhost:8080/manager/jmxproxy/. Il est
ainsipossibled
’
obtenirdesinformationssurlesconnecteurs,lesthreads,lesclasses...
- 1 -© ENI Editions - All rigths reserved
Ilestpossibled
’
utiliserdesfiltresetparexempled
’
afficheruniquementlesinformationssurlesobjetsMBeandetype
ThreadPoolhttp://localhost:8080/manager/jmxproxy/?qry=Catalina:type=ThreadPool,*.
3.JConsoleetMC4J,desconsolesJMX
L
’
ergonomieofferteparlemanagerdeTomcatestlimitéeetilestassezdifficiledecomprendrelesparamètresen
tempsréel.Ilexisteainsiplusieursoutilsévoluésquipermettentd
’
afficherdesrapportsdétaillés.
a.JConsole
Pourpouvoirobtenirdesstatistiquesentempsréel,ilfaututiliserunclientlourdJMX.LelogicielJConsoledéveloppé
parSun et livré en standardavec le JDK1.6 permet d
’
utiliser JMX. Son utilisation est assez simple et permet la
créationdegraphiquesentempsréel.SiJConsoleestlancépardéfautsansrienindiquer(
/jdk/bin/jconsole
),nous
obtenons des informations sur la machine virtuelle Java installée sur le poste local. Les données ne sont pas
’’
correctes
’’
carellesneconcernentpasuniquementlamachinevirtuelledeTomcat.Pourcela,ilestnécessairede
configurerTomcatavecunconnecteurenluipassantdesoptionsaudémarrage(portpourseconnecterauserveur
Mbean...).Demême,ilestimportantdesécurisercetteconnexionparidentifiantetmotdepasseafind
’
éviteràdes
- 2 - © ENI Editions - All rigths reserved
hôtesdistantsdeseconnecterànotremachine.
AvantdepouvoirconnecterTomcatàJConsole,ilfautconfigurerlamachinevirtuelleJavaduserveurpourautoriser
l
’
accèsdistantdesonconnecteur.Pourcela,ilestnécessaired
’
ajouterdesoptionsàlamachinevirtuellevialescript
dedémarragedeTomcat.Pourcela,ilestnécessairedeconfigurerlasécuritéd
’
accèsauconnecteur.
Laconfigurationdelasécuritédémarreparlacréationdesfichiersjmxremote.accessetjmxremote.password.Ilexiste
plusieurspossibilitéspourlamiseenplacedecesfichiers.Cesfichierspeuventêtreutilisésdanslerépertoire
/conf
duserveurTomcatoudanslerépertoire
/jdk/jre/lib/management
del
’
installationduJDKJava(c
’
estcetteseconde
optionquiestutiliséedansceguide).
Lefichierjmxremote.accesspermetd
’
indiquerlesnomsd
’
utilisateursautorisésàutiliserleconnecteurainsiqueles
permissionssurleconnecteur.
Lefichierjmxremote.passwordcontientlesmotsdepasseassociésauxutilisateurs.
Lerépertoire
/jdk/jre/lib/management
contientdesfichiersd
’
exemplesquipeuventêtrerenomméspourl
’
utilisateur.
Nous allons copier et renommer le fichier jmxremote.password.template en jmxremote.password. Nous ajoutons
ensuitenotreutilisateurenfindefichier.
# Following are two commented -out entries. The "measureRole" role has
# password "QED". The "controlRole" role has password "R&D".
#
# monitorRole QED
# controlRole R&D
moniteurjava monmotdepasse
NouspourronsdoncutilisercetutilisateuravecsonmotdepassepourseconnecterauserveurMBeanmaispourle
moment,nousnepourronsrienfairecarl
’
utilisateurnepossèdeaucundroit.Lefichierjmxremote.accesspermetde
définirlesdroitsenfonctiondesutilisateursdufichierjmxremote.password
.
Nouséditonsdonccefichieretajoutonslalignesuivante :
# Default access control entries:
# o The "monitorRole" role has readonly access.
# o The "controlRole" role has readwrite access.
monitorRole readonly
controlRole readwrite
moniteurjava readwrite
L
’
optionreadwritepermetd
’
indiquerqu
’
ilserapossiblederéaliserdesopérationsdelecturesurlesobjetsMBean
mais aussi d
’
écriture ce qui est important par exemple pour vider à distance le garbage collector de la machine
virtuelle.IlfautdésormaisdémarrerlamachinevirtuelledeTomcatavecunconnecteuractivéenpassantdesoptions
au démarrage. Pour cela si le serveur Tomcat est installé avec une archive, le fichier
/bin/catalina.bat
peut être
modifié.
Parcontre,sileserveurTomcataétéinstalléenmodeservice,c
’
est
à
direavecl
’
installationpourWindows,cefichier
n
’
est pas présent. Il faut dans ce cas ajouter des paramètres dans la console Tomcat ou dans Eclipse pour le
démarragedeTomcat.
-Dcom.sun.management.jmxremote.port=8364
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=
E:\jdk1.6\jre\lib\management\jmxremote.password
-Dcom.sun.management.jmxremote.access.file=
E:\jdk1.6\jre\lib\management\jmxremote.access
-Dcom.sun.management.jmxremote.ssl=false
SousWindows
Sécurité
- 3 -© ENI Editions - All rigths reserved
Sileserveurestlancé,alorsl
’
erreursuivanteestobtenuedanslaconsoled
’
Eclipse.
Cetteerreurindiquequelepropriétairedufichierdoitêtrel
’
utilisateurquidémarreleserveur.
SousWindowsXP,danslemenuOutils,ilfautchoisirOptionsdesdossiers,puisdanslafenêtrecliquersurl
’
onglet
Affichage
et décocher Utiliser le partage de fichiers simple. Cette action permet de gérer la sécurité sur les
fichiers.
Ilfautensuiteréaliserunclicdroitsurlefichierjmxremote.passwordpuissélectionnerl
’
ongletSécuritéetcliquersur
leboutonParamètresavancés.Ilfautalorssélectionnerl
’
onglet
Autorisations
etdécocherlacaseHéritedel’
objet
parentlesentrées...
Uneboîtededialogueapparaîtalors,ilfautrépondreCopieràlaquestionposée.
Ensuite, dans la partie
Autorisations
, il faut supprimer tous les utilisateurs autorisés et sélectionner l
’
utilisateur
Administrateur
dusystèmequialesdroitssurlamachinevirtuelleJava.
- 4 - © ENI Editions - All rigths reserved
Nousnedevonsnousretrouverqu
’
avecnotreutilisateurautorisé.
LeserveurTomcatpeutêtrelancé,lemessaged
’
erreurdisparaît.Désormaissinouslançonsl
’
outilJConsole,nous
pourronstoujoursnousconnecterenlocalmaiségalementàlapartiedesobjetsMBeanquicontrôlentTomcat.
Pourcela,ilfaututilisernosinformationsdeconnexion.
- 5 -© ENI Editions - All rigths reserved
LechampRemoteProcesscorrespondàl
’
adresselocaledelamachineetauportMBeans.LeschampsUsernameet
Passwordcorrespondentauxinformationsdeconnexiondufichierjmxremote.password
.
Danscecasdefigure,lesindicationssontcorrectesetconformesàcellesindiquéesparlemanagerdeTomcat.Des
informationsprécisessurlesapplicationsdéployéesparleserveursontobtenues.Danslepremieronglet,ilyaun
résumépratiqueaveclesThreadsexécutés,laconsommationdelamémoireetlesclasseschargées.Ledeuxième
onglet permet d
’
afficher des informations sur la consommation mémoire ce qui est indispensable lors de
développement de gros projets afin de vérifier si les connexions JDBC ne sont pas fermées, si des classes ou
Servletsconsommenttropdemémoire...Lesderniersongletspermettentd
’
avoirdesinformationssurlesThreads,
classes,objetsMBeanetlamachinevirtuelle.
- 6 - © ENI Editions - All rigths reserved
Nousvoyonsiciunfonctionnementconformedelamémoireaveclespartieschargementetdéchargement.
Sous Linux l
’
installation est pratiquement identique. Nous éditons le fichier /usr/local/tomcat/bin/catalina.sh qui
permetdedémarrerTomcat.Nousajoutonsalorsuniquementlesdeuxlignessuivantesenmilieudefichierversla
ligne260quiestrelativeàlavariableJAVA_OPTSetquicorrespondaudémarrage.
-Dcom.sun.management.jmxremote.port=8364
-Dcom.sun.management.jmxremote.ssl=false
NousprocédonsensuitecommesousWindowsetnouscopionslefichierd
’
exemplejmxremote.password.templateen
jmxremote.password
.
#cp /usr/local/jdk/jre/lib/management/jmxremote.password.template
/usr/local/jdk/jre/lib/management/jmxremote.password
Nousmodifionslesdroitssurcefichier.
#chmod 744 jmxremote.password
CommesousWindows,nousajoutonsnotreutilisateurJMXaufichierjmxremote.password
.
# Following are two commented -out entries. The "measureRole" role has
# password "QED". The "controlRole" role has password "R&D".
#
# monitorRole QED
# controlRole R&D
moniteurjava monmotdepasse
Pourlagestiondesdroitssurlefichier,c
’
estplussimplequesousWindows,ilsuffitdepositionnerlesdroitssuivants
surlefichier.
#chmod 600 jmxremote.password
SousLinux
- 7 -© ENI Editions - All rigths reserved
Ensuite,cequiestimportantc
’
estquel
’
utilisateurquilancelaJVMsoitlepropriétairedecefichier.
#chown tomcat:tomcat jmxremote.password
Désormais,nousallonsdéclarernosdroitsaveclefichierjmxremote.access
.
# o The "monitorRole" role has readonly access.
# o The "controlRole" role has readwrite access.
monitorRole readonly
controlRole readwrite
moniteurjava readwrite
L
’
application de supervision et le connecteur sont désormais opérationnels. Il est donc possible d
’
utiliser une
interfacedistantepourseconnecterauserveuretobtenirdesinformationsdesupervision.
b.MC4J
MC4J est une application JMX encore plus puissante que JConsole. Ce logiciel libre simple d
’
utilisation est
téléchargeableàl
’
adressesuivante :http://mc4j.org.
Celogicielestorientéverslasupervisiondesserveursd
’
applicationsJavaEE.MC4Jestparexemplecompatibleavec
Tomcat,JBoss,Weblogic,WebSphere...Ilestégalementdisponiblepourlamajoritédessystèmesd
’
exploitationavec
uneversionpourWindows,LinuxetMacOSX.Aprèsletéléchargementetl
’
installationdulogiciel,ilpeutêtrelancé
depuislamachinelocaleoun
’
importequellemachinedistantecapabled
’
accéderàlamachinelocale.
Après le démarrage de MC4J, il faut configurer une connexion au serveur Tomcat en choisissantCreate Server
Connection
danslemenu
Management
del
’
interfacegraphique.Enpremier,letypedeserveurauquelseconnecter
doitêtreindiqué.Ilfautensuitedonnerunnomànotreconnexionetlenumérodeportpourlaconnexion.Ensuite,si
lasécuritéestactivéesurleserveurd
’
applications,ilfautsaisirlesinformationsd
’
authentification.
- 8 - © ENI Editions - All rigths reserved
Lorsdeladeuxièmeétape,l
’
assistantdemandedepréciserlerépertoired
’
installationdeTomcat.Danslecasoù
MC4J et le serveur Tomcat à superviser sont sur des machines différentes, il faudra tout de même installer une
version de Tomcat en local (avec une version identique du serveur à superviser) car MC4J utilise les classes de
Tomcatpourlasupervision.
Unefoislaconnexionétablie,l
’
écranprincipaldelaconsoleMC4JapparaîtaveclalistedesMBeansduserveur.Le
tempsdechargementpeutparfoisêtreassezlongenfonctiondelaconnexionréseauetdelatailleduprojetà
superviser. Beaucoup d
’
informations utiles pour la supervision du projet sont alors affichées comme les classes
chargéesenmémoire,lesconnexionsJDBC,lesrequêtesdesclients.
AvecunoutilcommeMC4J,ilestdésormaisassezfaciledesuivrel
’
évolutionentempsréeldesressourcesinternes
duserveurpendantuntest demontéeenchargeoule développementdecelui
ci.Cetoutil estégalementtrès
pratiquesurunserveurenproduction,ilfaitpartiedeslogicielsindispensablesd
’
unadministrateurdeserveurJava
EE.
- 9 -© ENI Editions - All rigths reserved
4.JMeteretlestestsdemontéeencharge
Aprèsavoircorrectementconfiguréleserveuretlesoutilsdesupervision,ilestessentield
’
installerunoutilpourtester
lamontéeenchargeduserveur.Ilestimportantd
’
utilisercesoutilslorsdedéveloppementJavaEEafindesimulerla
- 10 - © ENI Editions - All rigths reserved
chargeetd
’
enmesurerl
’
impactetlesconséquencesafindevaliderleschoixdedéveloppement,lespagesdecode...
Parexemple,lorsdudéveloppementd
’
uneapplicationJavaEEdegestionetdetraitementd
’
images,ilseranécessaire
de bien vérifier que lors de l
’
appel par 100 utilisateurs en simultané, le serveur peut répondre dans un délai
raisonnable, que l
’
application ne va pas saturer la mémoire allouée au serveur... De même, lors d
’
un choix de
conceptionsuruncomposantdeprogrammation,l
’
outildetestpourranouspermettredevaliderleslibrairies,logiciels
ouarchitectures(ex :JDBCouHibernate).Ilexisteplusieursoutilssurlemarchépourlestestsdemontéeencharge,
telsquel
’
outilLoadRunnerdeMercuryetlelogiciellibreApacheJMeter.
JMeterestunoutilécritenJavaetdéveloppéparlafondationApache.JMeterpeutgénérerdestestssurlesserveurs
WebHTTPetHTTPS,desbasesdedonnéesavecJDBC,desannuairesetdesserveursFTP.JMeterfournitenfinun
ensembled
’
outilspourcollecteretmesurerlesrésultatsdestests.Dupointdevuedelaprogrammation,ilpourra
analyserplusieurstypesderessourcescommedesfichiersJava,desServletsetpagesJSP.ApacheJMeterest100%
Java,ilrequiertdoncjusteunemachinevirtuelleJavapourfonctionneretlancerlestests.
JMeterpeutêtreinstallésurlamêmemachinequeleserveurd
’
applications,maisafindenepasfausserles
tests,ilvautmieuxéviterd
’
exécuterJMeteretTomcatsurlesmêmesmachines.
L
’
installation de JMeter est simple, il faut télécharger les fichiers binaires de JMeter sur le site
http://jakarta.apache.org/jmeter. Il y a une archive au format
.zip
pour Windows et
.gz
pour Linux. L
’
installation
nécessite simplement la décompression de l
’
archive dans un répertoire du système. Il est bien sûr indispensable
d
’
avoirinstalléaupréalableunJREouJDKcorrectementaveclavariabled
’
environnementJAVA_HOMEparamétrée.
Pour lancer JMeter, il faut utiliser le script
/bin/jmeter.bat
ou l
’
exécutable /bin/jmeter.exe sous Windows et la
commande/bin/jmetersousLinux.
Ilestpossibled
’
ajouterdeslibrairiesàJMeterpourtesterdesconnexionsJDBCparexemple.Pourcela,il
suffitdecopierlalibrairiedanslerépertoire
/lib
et
/lib/ext
deJMeter.
SiJMeterdétecteuneerreur,celle
ciseraécritedanslefichierdelogappelé
/log/jmeter.log
.Cefichierestcrééau
lancement de l
’
application. Le fichier de configuration de JMeter pour SSL, proxy et utilisateurs
est/bin/jmeter.properties
.
Audémarragedel
’
interface,leplandetests(TestPlan)etleplandetravail(WorkBench)sontaffichés.Unplandetest
permetdedécrirelasériedetestsàexécuterparJMeter.Pourajouterousupprimerunplandetests,ilfautfaireun
clicdroitdansl
’
explorateur.Ilestégalementpossibledechargerdesélémentsdetestsprésentsdansunfichier.Un
plandetestsestsauvegardéauformat
JMX
.Unplandetravailcontientlesélémentsquinesontpasutilisésparle
plandetests,ils
’
agitd
’
unespacedestockagetemporaire.
Avantdecommencerl
’
écritured
’
unplandetests,ilestnécessairedeconfigurerleserveuretlesressourcesdansdes
conditionsquisoientleplusprochepossibled
’
unenvironnementenproduction.Ilfautdoncparamétrercorrectement
lamémoireallouée,leserveurTomcat,lesconnecteursauxbasesdedonnées...
Unplandetestsconsisteàtesterlamontéeenchargeduserveurensimulantdesaccèsdistants.
Ilexisteplusieursétapesessentiellespourlaconstructiond
’
unplandetest :
●Définirlegroupedethreadsquisimulentlenombrederequêtesendirectionduserveur.Unthreadestenfait
unutilisateurpotentielquinaviguesurlesiteenproduction.
●Écrire la configuration du serveur en précisant le nom de la machine en production, le port à utiliser, le
protocole...
●ÉcrirelesrequêtesHTTPàinvoquerparlesutilisateurs.
●AjouterunouplusieurscomposantsdemesureJMeterquiva(ouvont)analyserettraiterlesréponsessurla
charge.
●Enregistrerleplandetestsetlelancer.
Lapremièreétapeconsisteàcréerlesutilisateurs(Threads)quivontenvoyerdesrequêtesauserveur.Nousallons
créerparexemple5utilisateursquivontenvoyerdesrequêtesà2pagesdusite,2foisdesuite.Ilyauradoncau
ApacheJMeter
Utilisation
Créerunplandetests
- 11 -© ENI Editions - All rigths reserved
total :5utilisateurs*2pages*2appels=20requêtes.
Ilfautajouterungrouped
’
utilisateurs(
ThreadGroup
)ànotreplandetests.
Nousfaisonsunclicdroitsurleplandetestsetl
’
action
Ajouter
GroupedeThread
.
L
’
écranaffichépermetdeconfigurerlenombredeThreadsainsiquel
’
intervalledetempspendantlequelilsseront
démarrés. La propriété
Nombre de Threads
(
Number of Thread) correspond au nombre d
’
utilisateurs, dans notre
exemple: 5.La propriétéPériode de chauffe
(
Ram
Up)définitlenombredesecondesentreledéclenchementde
chaqueutilisateur.Enpositionnantà0,JMetervadémarrertouslesutilisateursenmêmetempsetdoncsimulerdes
accèssimultanésauserveurd
’
applications.Enfin,lapropriété
CompteurdeBoucle
(
Loop
Count)permetd
’
indiquerle
nombredebouclesàeffectuersurleserveur.Nouspositionnonscettepropriétéà2poureffectuer2répétitions.Ilest
aussipossiblederejouercetteséquenceàl
’
infiniencochantlacaseappropriée.
Lasecondeétapeconsisteàpréciserleserveurd
’
applicationsquiserautilisépourlestests.
Ilfautd
’
abordsélectionnerlegroupedeThreaddansleplandetests.Puisaprèsunclicdroitsurcetélément,les
choixAjouter(Add)
ElémentdeConfiguration
RequêteHTTPRequestpardéfaut
sontsélectionnés.
Utilisateur
Configurationduserveur
- 12 - © ENI Editions - All rigths reserved
Lesvaleursàprécisersontleprotocoleutilisé,lenomdedomaineduserveurousonadresseIPetlenumérodeport
de celui
ci. Des paramètres aux requêtes peuvent être également ajoutés. Dans ce cas, ils seront transmis avec
touteslesrequêtesendirectionduserveur.
Lorsdecettetroisièmeétape,ilesttempsd
’
écrirelesrequêtesHTTP,l
’
objectifétantdesimulerlanavigationd
’
un
utilisateursurlesiteenproduction.
D
’
après notre plan de tests, il faut utiliser deux requêtes HTTP. La première pour la page d
’
accueil du site
http://127.0.0.1:8080/index.jspetlasecondeversunepaged
’
aidehttp://127.0.0.1:8080/aide.jsp.
JMetervadoncenvoyerlesrequêtesdansl
’
ordreoùellesapparaissentdansl
’
arbre.
Pourajouterunepage,ilfautfaireunclicdroitsurnotregroupedeThreadsdansnotreplandetravailetajouterune
requêteHTTP.
Écrirelesrequêtesutilisateurs
- 13 -© ENI Editions - All rigths reserved
Laconfigurationduserveuraétéréaliséeprécédemment,iln
’
yadoncquetrèspeud
’
élémentsàrenseigner.Les
élémentsindispensablessontlaméthodeHTTPàutiliser(GETouPOST)ainsiquelenometlechemindelaressource.
Nousdonnonsunnomexpliciteànotrerequête(paged
’
accueil)etnouspositionnonslechemind
’
accèsàlapage(/
étantdonnéquec
’
estlaracinedusite).
Nousréalisonsensuitelamêmeopérationpournotresecondepage/requête.
Lorsdecetteétape,ilfautajouterunouplusieursécouteurspouranalyserlestests.Pourajouterunmoniteurde
résultats,nousréalisonsunclicdroitsurlegroupedeThreaddansnotreplandetestsetnousajoutonsparexemple
un écouteur graphique. Nous précisons alors un fichier de sauvegarde pour les résultats en utilisant le bouton
Parcourir.Lefichierdesortieestauformat
.jtl
.
Ajouterunécouteur/composantdemesure
- 14 - © ENI Editions - All rigths reserved
L
’
enregistrementdu plande tests n
’
est pas obligatoire mais conseillé. Pour sauvegarder un plan de tests, il faut
sélectionnerl
’
item
Enregistrer
dumenuetpréciserunnomdefichier.jmx
.
Pourlancerl
’
exécutionduplandetests,ilfaututiliserlemenuDémarrer(Run).JMeterindiquealorsavecunpetitcarré
vertlumineuxenhautàdroitedelafenêtreprincipalel
’
exécutiondestests.Lerectanglerepasseengrisquandles
tests sont arrêtés. Après avoir sélectionné
Stop
, le rectangle vert reste allumé jusqu
’
à la fermeture de tous les
Threads.
Il est possible d
’
ouvrir le même plan de tests avec plusieurs fenêtres d
’
analyse sans problème. Il est également
possibled
’
utiliserdesbouclesinfiniespoursimulerdesaccèsentempsréelsuruneplusgrandepériode.
Lesrésultatsd
’
analysepeuventêtredetoutesortecommedesgraphiques,destableauxdedonnées,desarbres...
JMeterestunoutiltrèspuissantquipermetd
’
analyserentempsréellamontéeencharged
’
unserveur.Lesrésultats
graphiques,textuelsetsousformed
’
arbressonttrèsprécieuxenphasesdedéveloppementetdeproduction.
Enregistreretlancerleplandetests
- 15 -© ENI Editions - All rigths reserved
ApacheTomcatetSSL/HTTPS
1.Présentation
LeserveurTomcatproposedanssaconfigurationpardéfautunconnecteurHTTPSpréconfiguré.Laconfigurationest
encommentairedanslefichierprincipalserver.xml.Ilestdonctrèssimpled
’
activerceconnecteurenréactivantcette
ligneetenutilisantdesclésdecryptageconformes.
LeconnecteurHTTPSdeTomcatutilisel
’
élémentdeconfiguration
<Connector>
avecleportSSLindiqué(8443).Cet
élémentpossèdelesmêmesattributsquelesautresconnecteursmaiségalementlesattributskeystoreFile(chemin
destockagedesclés),keystorePass(motdepassedufichierdestockagedesclés),sslProtocol(leprotocoleutilisépar
HTTPS),
clientAuth (certificat client obligatoire ou non), truststoreFile (fichier de validation des certificats clients),
truststorePass(lemotdepassedufichierdescertificatsclients).
Voicilaconfigurationquel
’
onpeututiliseravecuncertificatauto
signé.
<Connector port="8443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
PourlamiseenplacedeTomcatenmodeHTTPS,ilestnécessairededisposerdecléscryptéesconformémentaux
spécificationsenvigueur.
Commepourl
’
utilisationd
’
unserveurTomcatHTTPenproduction,laconfigurationidéaleentermedeperformanceset
defiabilitéestl
’
utilisationdumoteurHTTPd
’
unserveurWebàlaplacedeceluideTomcat.Pourcela,nousutiliserons
surunserveurdeproductionsousLinux,leserveurApache
SSL.Laconfigurationd
’
Apache
SSLestsemblableàcelle
d
’
Apache.Ilestcependantnécessaired
’
utiliserdescléscorrectesetuneconfigurationadaptée.Voicici
dessousun
exempledeconfigurationsurunserveurLinuxDebian.
Fichier
/etc/apache
ssl/httpd.conf
NameVirtualHost adresseip:443
<VirtualHost adresseip:443>
DocumentRoot /usr/local/tomcat/webapps/monprojet
ServerName monprojet.com
ServerAlias *.monprojet.com
CustomLog /var/log/apache/monprojet -access_log combined
Port 443
SSLRequireSSL
SSLEnable
Alias /javascript /usr/local/tomcat/webapps/monprojet/javascript
JkUnMount /javascript/* worker1
JkAutoAlias /usr/local/tomcat/webapps/monprojet/
<Location "/WEB -INF">
AllowOverride None
deny from all
</Location>
<Location "/META -INF">
AllowOverride None
deny from all
</Location>
<Location "/*.jsp">
JkMount worker1
</Location>
<Location "/*.do">
JkMount worker1
</Location>
DirectoryIndex index.html index.htm index.php index.jsp
</VirtualHost>
...
SSLEnable
- 1 -© ENI Editions - All rigths reserved
...
SSLCertificateFile /etc/apache -ssl/cles/monprojet.cert
SSLCertificateKeyFile /etc/apache -ssl/cles/monprojet.key
2.Enrésumé
Ce chapitre a présenté le serveur d
’
applications Apache
Tomcat. La première partie a concerné l
’
installation de
Tomcat,laréalisationd
’
unscriptdegestionduserveuretlaprésentationdesfichiersjournaux.
Dansundeuxièmetemps,leguideaintroduitlamiseenplacedelapartieadministrationetlagestiondelamémoire
allouéeauserveur.
Latroisièmepartieplustechnique,aprécisécommentcouplerTomcatavecleserveurWebApache.
L
’
architecture de Tomcat avec l
’
arborescence et les fichiers de configuration au format XML ont été également
détaillés.
PuisunbrefrappelXMLaétéévoquéafindepouvoirmanipulerlesfichiersdeconfigurationduserveurJavaEE.Cette
étapeaétésuivied
’
uneprésentationdel
’
arborescenced
’
unprojet/webappTomcatavecl
’
introductiondudescripteur
dedéploiementweb.xmletledéploiementd
’
unpremierprojetsimple.
L
’
avant
dernièrepartieaconcernélasupervision,lemonitoringduserveuretdesesapplicationsdéployées.L
’
outil
JMXintégréàTomcatetlesoutilsdemanipulationJConsoleetMC4Jontétédétaillés,suivisdelamiseenœuvrede
JMeterpourlestestsdemontéeencharge.
EnfinladernièrepartieaétéaxéesurlamiseenœuvredeTomcatavecl
’
utilisationduprotocoleHTTPS.
- 2 - © ENI Editions - All rigths reserved
Qu
’
estcequ
’
uneJavaServerPage?
1.Présentation
UnepageJSPestunepageHTMLquipeutcontenirducodeJava.D
’
unpointdevuetechnique,ilestpossibledefaire
lamêmechoseavecunepageJSPqu
’
avecuneServlet.Ladifférenceestessentiellementauniveaudelastructure.
UnepageJSPestunsquelettedepageHTMLquicontientdesmorceauxdecodeJavapermettantderéaliserdes
traitementsdynamiquesoud
’
intégrerdesdonnées.UneServletestàl
’
inverse,plutôtcomposéedecodeJavaet
parfoisdeportionsdecodeHTMLpourlagestiondel
’
affichage.
La technologie JSP permet l
’
utilisation et la création de balises JSP (taglib) qui ajoutent des fonctionnalités pour
étendrelespossibilitésdedéveloppement.LemécanismeJSPpermetdeséparerlalogiquecontrôlantlaprésentation
des données de la logique métier, contrôlant la valeur des données. D
’
un point de vue technique, les JSP sont
compiléesparlemoteurJASPERpourdevenirdesServletsJava.
IlexisteégalementdesactionsJSP(balisesXML)quiappellentdesfonctionsafinderendreunservicespécifique :
inclusiondepage,redirection...
LeprincipedefonctionnementesttrèsprochedeslangagesPHPetASPmaisàl
’
inversedecesmécanismes,JSP
compilelecodeaulieudel
’
interpréteràchaquefois,cequisoulagelachargedetraitementduserveur.
2.Introduction
LespagesJSPsontexécutées parleserveurd
’
applicationspourrépondre auxrequêtesdesclients. LesJSPont
plusieursfonctionnalités :
●lacréationdesitesWebdynamiques(pagesWebdynamiques);
●letravailavecdesbasesdedonnées;
●
l
’
améliorationdelasécurité(lecodeestexécutéparleserveuretdoncnonaccessibleparlesclients).
●
l
’
utilisationdesJavaBeans(simplicitéducode,manipulationd
’
objetssimples...);
●
l
’
utilisationdebalisespersonnalisées(taglib).
Aveccemodèledeprogrammation,ilestplusaisédescinderledéveloppementd
’
uneapplicationWeb :legraphiste
s
’
occupedelapartieprésentationHTMLetledéveloppeurs
’
occupedelapartielogiqueaveclestraitementsetl
’
accès
auxdonnées.
Côtéserveur, unepageJSP estinterprétée uneseule fois, soitlors dupremier appel,soitlors dulancement du
conteneurWeb.Enfait,leconteneurWebcréeuneServletàpartirdelapageJSP.LaServletestensuitecompilée,
chargéeenmémoire(s
’
iln
’
yapasd
’
erreur)etmiseenservicepourrépondreauxrequêtesclients.
IlexisteuneServletsystèmedansleconteneurJavaEEquiestconfiguréepourtraiterlesressourcesconcernantles
fichiersportantl
’
extension.jspet.jspf
(
javax.servlet.jspetjavax.servlet.jsp.tagext
).
3.JASPER
Les JSP sont compilées par le compilateur JASPER (il en existe d
’
autres) pour devenir des Servlets JAVA. Tomcat
intègreunmoteurdeServletsappeléCATALINA.JASPERgénèreuneServletsourceJava(fichier
.java
)quiestàson
tour compilée par CATALINA pour devenir une Servlet exécutable (fichier .class). La syntaxe propre à Java est
encadréepardesbalises
<%
et
%>
,cequiexpliqueaucompilateurJASPERqueleresteducodedoitêtretraité
commedulangageHTML.
4.Cycledevied
’
uneServlet
UneJSPestcomposéedetextebrutcontenantducodeHTMLetducodeJava.Lorsdupremierappeldelapagepar
unutilisateurdistant,lapageJSPesttraduiteenuneServletJavaparl
’
intermédiaireduparseurJSPcontenudansle
serveurJavaEE.
- 1 -© ENI Editions - All rigths reserved
LecompilateurvaensuitetraduirecefichierJavagénéré,enunfichier .class,etcenouveaufichier .classestalors
exécutéparlamachinevirtuelle.
Latransformationdelapage.jspenServlet.classn
’
esteffectuéequ
’
uneseulefois,lorsdupremierappeldelapage.
C
’
estpourcela,quelepremierchargementdelapageestbeaucouppluslongquelessuivants,quinenécessitent
pascette étape.Les utilisateurssuivants n
’
auront pas ce temps d
’
attentepuisquela pageestdéjàcompilée et
chargée en mémoire. En cas de modification de la page, celle
ci est à nouveau compilée et mise à jour
automatiquement.
Lecycled
’
unepageJSPcomportequatrephases :
●
Chargement et instanciation
:leserveurlocaliselaclasseJavacorrespondantàlapageJSPetlacharge
danslamachinevirtuelle.
●
Initialisation
:lapageJSPestinitialiséeselonleprinciped
’
uneServlet.
●
Traitementdesrequêtesclients
:lapagerépondauxrequêtesdesclients.AucuncodeJavan
’
estenvoyé
auxclients.
●Finducycle :touteslesrequêtessonttraitéesetterminées,lesinstancesdelaclassesontdétruites.
Lorsqu
’
un client envoie une requête à une page JSP, le serveur transmet celle
ci au conteneur de JSP et celui
ci
déterminelaclassed
’
implémentationquidevralatraiter.
- 2 - © ENI Editions - All rigths reserved
UnepageJSPestenfaituneServletcequisignifiequetoutcequiestutilisablepouruneServlet(cookies,sessions,
context...)peutêtreégalementmisenœuvredanslespagesJSP.
Pourrésumer,lesdéveloppeursetconcepteursgraphiqueutilisentlatechnologieJSPpourdévelopperrapidement
despagesWebdynamiquesaucontenuriche.LespagesJSPpeuventêtrecrééesfacilementetentretenuescarelles
sontbaséessurducodeHTMLetXML.ToutelapuissancedeJavaestalorscachéederrièrelespagesJSP.
- 3 -© ENI Editions - All rigths reserved
Déclarations,commentairesetscriptlets
1.Présentation
UnepageJSPportel
’
extension.jspetdoitêtreaccessibleavecunnavigateurdansl
’
arborescenceduserveurJavaEE.
Les pages sont placées dans le répertoire de l
’
application (
webapp
) à partir de la racine ou dans un répertoire
spécifique(/jsp,/vues,/vues/articles/...).LespagesJSPsontconçuespouravoiruncomportementdynamiqueetelles
sontchargéesderépondreauxrequêtesenvoyéesparlesclients.C
’
estlecodeJavainsérédanslecodeHTMLqui
permetcecomportementdynamique.
2.LesélémentsJSP
Nous avons besoin, comme pour d
’
autres langages de scripts (ex : PHP), d
’
indiquer au serveur où commence et
s
’
arrêtelecodeHTMLetlecodeJava.Pourcela,laspécificationJSPdéfinitdesbalisesquipeuventêtreemployées
pourdélimiterlecode.Cesbalisespermettentdedéfinirtroiscatégoriesd
’
éléments :
●Lesdirectives :informationssurlapage.
●Lesscripts :placementducodedanslapage.
●Lesactions :inclusiondecodebasésurdesbalisesdanslapagecourante.
3.Lesdirectives
Lesdirectivessontdesélémentsquipermettentdepréciserdesinformationsrelativesàlapage.Ilexistelestrois
directivessuivantes :
page
,
includeet
taglib
.LesdirectivesJSPcommencentpar
<%@
etseterminentpar
%>
.Les
directivessontparfoisappeléesdirectives de moteurJSP.Ellesfournissentdesinstructionset des paramètresqui
déterminentlafaçondontunepageJSPesttraitée.Parcontre,unedirectiven
’
affectequelapageJSPquilacontient.
Chaquedirectiveadesattributsauxquelspeuventêtreaffectéesdesvaleursspécifiques.L
’
instructiondedirective
inclutlenomdeladirective,suividel
’
attributetdespairesdevaleursdontl
’
utilisationestsouhaitée.
L
’
élémentdirective de pagepermet de définir les attributs de la pageJSP, comme lesimportations de classes ou
paquetages,lapageinvoquéeencasd
’
erreur,letypedecontenu...Ladirective
page
peutêtredéfinieplusieursfois
dansunmêmedocument.Ilestrecommandédetoujoursplacercettedirectiveentoutdébutdedocumentpourune
questiondelogiqueetdelisibilité.
Nouspouvonsdévelopperunepaged
’
erreurgénériquepourleprojetbetaboutique.Cettepageestplacéedansun
répertoirenommé/vuesafindebienséparerlesélémentsdetraitements(Servlets)desélémentsd
’
affichage(JSP).
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>ERREURS</title>
</head>
<body>
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+(String)erreurs.get(i)+"</li>");
}
Ladirectivepage
- 1 -© ENI Editions - All rigths reserved
%>
</ul>
</body>
</html>
Ilfautremarquerladéfinitiondeladirective
page
quipermetàlapremièreligne,parl
’
intermédiairedesesattributs,
d
’
indiquerlelangageutilisé,letypemimeetl
’
encodage.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
L
’
attributlanguagepermetdepréciserlelangageutilisé,Javadanstouslescas.L
’
attributcontentTypeindiqueletype
MIMEutiliséparlapageJSP.L
’
attribut
pageEncoding
indiqueletyped
’
encodagedecaractèreutilisédanslaJSPpourla
réponseHTTP.Ilexisteplusieursautresattributscomme
errorPage
quipermetdespécifierlapageJSPàafficherencas
d
’
erreur.L
’
attributinfopermetdefaireunedescriptiondelaJSPetpeutêtrerécupéréparl
’
instruction
getServletInfo()
.
L
’
attributsessionpermetparexempled
’
indiquersilecontextedesessionHTTPestaccessibleounondanslaJSP.
Ilestpossibledemodifierlapremièrelignedecettepageetl
’
attributcontentTypepourafficherdutextebrut.
<%@ page language="java" contentType="text/plain;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
Ilyaégalementsurlasecondelignedufichierunautreexempled
’
utilisationdeladirective
page
avecl
’
importationde
laclasseJavaArrayListpourlamanipulationd
’
unecollectiond
’
objets.
<%@ page import="java.util.ArrayList" %>
De cette manière, les objetsArrayList, ainsi que toutes les fonctions de cette classe, pourront être manipulés et
utilisés.
Nouspouvonségalementmodifierlégèrementnotrepaged
’
erreurpourutiliserlesecondtypededirective,àsavoirla
directiveinclude.Dansnotreexemple,unepageHTMLstatiqueestutiliséepourinclureunmessagedescriptifendébut
delaJSP.UneautrepageJSPpourrabienentenduêtreinclusesansproblème.
Leprojetbetaboutiqueutiliseune Servletd
’
authentification nomméeServletAuthentificationqui permetde vérifier la
syntaxedel
’
identifiantetdumotdepasse.Encasd
’
erreur,lesdonnéessonttransféréesàlapageJSPerreurs.jsp
.
Cettepageestchargéed
’
afficherlesraisonsdel
’
échecdel
’
authentification.Voicilecodedecettepageavecl
’
inclusion
d
’
unepaged
’
en
têteauformatHTML.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<%@ include file="enteteerreurs.html" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>ERREURS</title>
</head>
<body>
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+(String)erreurs.get(i)+"</li>");
}
%>
</ul>
</body>
</html>
Si aucune directive n
’
est utilisée dans une page JSP, le moteur JSP du serveur Web utilisera ses propres
paramètrespardéfaut.Parexemple,siladirective
page
avecl
’
attributcontentTypeestomise,lemoteurJSP
Ladirectiveinclude
- 2 - © ENI Editions - All rigths reserved
afficheraautomatiquementlesinformationscommeunepageWEB(contentType=
’’
text/html
’’
).
ChaquepageJSPcompiléeesttransforméeenuneServletplacéedanslerépertoire
/work/org/apache/jsp
.Pourcette
page nomméeerreurs.jsp, la Servlet générée est présente sous le nom :/work/org/apache/jsp/erreurs_jsp.jsp. Son
codecompiléestprésentdanslefichier :/work/org/apache/jsp/erreurs_jsp.class
.
VoicilecodedelaJSPtransforméeenServletparlemoteurJSP :
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.ArrayList;
public final class erreurs_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static java.util.List _jspx_dependants;
static {
_jspx_dependants = new java.util.ArrayList(1);
_jspx_dependants.add("/enteteerreurs.html");
}
public Object getDependants() {
return _jspx_dependants;
}
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html; charset=ISO -8859-1");
pageContext = _jspxFactory.getPageContext(this, request,
response, null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write( ’\r’);
out.write( ’\n’);
out.write("<h2><font color=\"red\">ERREURS SUR LA PAGE</font></h2>");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<!DOCTYPE html PUBLIC \" -//W3C//DTD HTML 4.01
Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
out.write("<html>\n");
out.write("<head>\n");
out.write("<meta http -equiv=\"Content -Type\"
content=\"text/html; charset=ISO -8859-1\">\n");
out.write("<title>ERREURS</title>\n");
out.write("</head>\n");
out.write("<body>\r\n");
out.write("<! -- un commentaire HTML renvoyé au
client -->\r\n");
out.write( ’\n’);
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
out.write("\r\n");
out.write("<h2>Les erreurs suivantes se sont
- 3 -© ENI Editions - All rigths reserved
produites</h2>\r\n");
out.write("<ul>\r\n");
out.write("\t");
//commentaire monoligne
/* commentaire multilignes */
for(int i=0;i<erreurs.size();i++)
{
out.write("\r\n");
out.write("\t\t<li>");
out.print( this.miseEnFormeMessage((String)erreurs.get(i)) );
out.write("</li>\r\n");
out.write("\t");
}
out.write("\r\n");
out.write("</ul>\n");
out.write("</body>\n");
out.write("</html>\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null)
_jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null)
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
LederniertypededirectiveJSPestl
’
utilisationdelibrairiesexternespersonnalisées,parlebiaisdeladirectivetaglib
(<%
@taglib...
%>),quenousutiliseronsdanslechapitredédiéàcettetechnologie.
4.Lesscripts
Lesscripts sont deséléments qui permettentde placer lecode Java dansles pages JSP.Il existe troistypes de
scripts :
●Lesdéclarations :pourdéclareret/ouinitialiserunevariable.
●Lesscriptlets :pourcontenirlesinstructionsJava.
●Lesexpressions :pourrenvoyerdesinformationsauxutilisateurs.
Ilpeutyavoirunepartiedescript,ducodeHTML,puisdenouveauduscriptdansunemêmepageàconditionque
chaquemorceaudescriptsoitbienentouréparlesbalisesJava.
a.Lesdéclarations
UnedéclarationestemployéepourintroduireetéventuellementinitialiserunevariableouuneméthodeJava,comme
dans un programme. Une déclaration permet de définir un bloc pour les variables globales ainsi que pour des
méthodesqui pourront ensuite être utiliséesdans lereste du document JSP. Ceprincipe est très pratique pour
l
’
utilisationdeméthodes,carenJavatoutestobjet,etdoncnécessiteladéclarationdeclasses.Cependant,cette
techniqueautoriselasimpleutilisationdeméthodessansclasseetobjet.
Pourcréerunedéclaration,ilfautplacerlecodeentreledélimiteurd
’
ouverture<%!etledélimiteurdefermeture%
>
.Lesdéclarationspeuventêtreplacéesàn
’
importequelendroitdansunepageJSP,maisilestpréférablecomme
pourlesdirectives,delesplacerendébutdefichier.
Nous allons modifier notre page d
’
erreur JSP afin d
’
ajouterune méthode quipermet d
’
insérer la balise HTML de
couleurrougepourlesmessagesd
’
erreurs.
Ladirectivetaglib
- 4 - © ENI Editions - All rigths reserved
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ include file="enteteerreurs.html" %>
<%@ page import="java.util.ArrayList" %>
<%!
//retourner le message en couleur rouge
private String miseEnFormeMessage(String message)
{
return "<font color=\"red\">"+message+"</font>";
}
%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html;
charset=ISO -8859-1">
<title>ERREURS</title>
</head>
<body>
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+this.miseEnFormeMessage((String)
erreurs.get(i))+"</li>");
}
%>
</ul>
</body>
</html>
b.Lesscriptlets
LesscriptletscontiennentdesinstructionsJava.Unescriptletestunblocdecodeincorporédansunepage.Lecode
écritdansunescriptletestenJava.Demême,lesscriptletspeuventcontenirn
’
importequelcodeJavavalide.Une
pagecontenantunescriptletestsouventappeléeunmodèle.Pourajouterunescriptletàunepage,ilfautplacerle
codeentreledélimiteurd
’
ouverture
<%
etledélimiteurdefermeture
%>
.
À l
’
intérieurd
’
unescriptletl
’
objetoutpeutêtreutiliséaveclaméthodeprint(ou println)pourgénérerducontenu
texteàdestinationduclient.LesvisiteursquiinvoquentlapageJSPnepourrontpasvoirlecodeJavacontenudans
cettepagemêmes
’
ilsaffichentlecodesourcedelapageavecleurnavigateurWeb,cederniernecontenantquela
sortiegénéréeaprèstraitement.
Lapartiedecodeprésentedanslapaged
’
erreurprécédentecorrespondàunescriptlet.
<%
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+this.miseEnFormeMessage((String)
erreurs.get(i))+"</li>");
}
%>
Le code d
’
une scriptlet ressemble beaucoup à l
’
élément déclaration des scripts, cependant il existe plusieurs
différencesentrelesdeuxsyntaxes :
●Les scriptlets ne peuvent être employées pour définir des méthodes. Seules les déclarations permettent
cela.
●Lesvariablesdéclaréesdansunedéclarationsontdesvariablesd
’
instance(doncaccessiblesdanstoutesles
scriptletsdelapage).
- 5 -© ENI Editions - All rigths reserved
●Lesvariablesdéclaréesdansunescriptletsontlocales(doncvisiblesuniquementàl
’
intérieurdublocdans
lequelellessontdéfinies).
c.Lesexpressions
Lesexpressionssontutiliséespourrenvoyerauclientlesvaleursd
’
expressionsJava.Ellespermettenteneffetde
générerune sortie surune page JSP.Souvent, lesexpressionssont utiliséescommeraccourcis poursimplifier le
code.Parexemple,lecode
out.println(...)
peutêtreremplacéparl
’
expression
<%=%>
.LeserveurJavaEEtraitele
codecontenudansl
’
expressionetconvertitlerésultatenunechaîne.
Uneexpressionnepeutpass
’
acheverparunpointvirgule.Siunpointvirguleestutilisédansuneexpression,une
erreurseproduira.Eneffet,l
’
expressionsuivante
<%=exp>
estconvertieparlecompilateurenlascriptletsuivante
<%out.println(exp);%>
.
Une expression peut contenir un appel de méthode, une instruction ou autre. Nous allons modifier notre page
d
’
erreurduprojetbetaboutiqueafind
’
utiliseruneexpressionàlaplacedel
’
objetout
.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ include file="enteteerreurs.html" %>
<%@ page import="java.util.ArrayList" %>
<%!
//retourner le message en couleur rouge
private String miseEnFormeMessage(String message)
{
return "<font color=\"red\">"+message+"</font>";
}
%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>ERREURS</title>
</head>
<body>
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++)
{
%>
<li><%= this.miseEnFormeMessage((String)erreurs.get(i))%></li>
}
%>
</ul>
</body>
</html>
d.Lescommentaires
L
’
ajoutdecommentairespermetdeclarifierlecodeHTMLetJSP.Ilestpossibled
’
utiliserdescommentairesHTML
danslespagesJSP.Cescommentairesapparaissentdanslapagerenvoyéeaunavigateurclient.Ilssontalorsdela
formesuivante :
<!
uncommentaireHTMLrenvoyéauclient
>
.
Cecommentairen
’
apparaîtpasdanslapageduclientmaispeutêtrevisualisédanslesourceHTMLavecl
’
outildu
navigateur.Ilexisteégalementlapossibilitéd
’
ajouterdescommentairesdanslecodeJSPenutilisantlesbalises
suivantes :
<%
uncommentaireJSPquineserapasrenvoyéauclient
%>
.
L
’
ensemble des informations et du code placé entre les balises de commentaire JSP sera supprimé avant le
traitementdelapageJSPsurleserveurWebetneserapasrenvoyéauclient.
Parcontre,uncommentaireJSPestdelamêmeformequ
’
uncommentaireHTML,ilnedoitpasseplacerdansducode
Java(unescriptlet). Lesscriptletssontcodées enlangageJava, les commentairesJavasont doncappliqués.On
retrouvelestypesmonoligne//etmultilignes/**/.
- 6 - © ENI Editions - All rigths reserved
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>COMMENTAIRES</title>
</head>
<body>
<!-- un commentaire HTML renvoyé au client -->
<%-- un commentaire JSP qui ne sera pas renvoyé au client --%>
<%
//commentaire monoligne
/* commentaire multilignes */
%>
</body>
</html>
e.Lesactions
LesactionsdansunepageJSPsontécritessouslaformed
’
élémentsXMLetpermettentdecondenserenuneseule
ligneuntraitementquiseraitpluslongàécrireàl
’
aided
’
unscriptJava.Ilexistedeuxtypesd
’
actions :
●
Lesactionsstandards
.
●Lesactionspersonnalisées
.
Les actions standards sont définies par la spécification JSP (d
’
oùle nom standard)et sont lesactions de base,
accessiblesàtouteslespagesJSP,cardéfiniesdansl
’
APIJavaEE.LesélémentsXMLcorrespondant,commencent
tous par le préfixe jsp. Les actions personnalisées viennent enrichir les actions standards en offrant d
’
autres
possibilités.Ceslibrairiessontdéclaréesparladirective
taglib
vueprécédemment(
<%@taglibprefix=
’’
...
’’
uri=
’’
...
’’
%
>
).
Pour le moment nous allons étudier les librairies standards avant d
’
aborder par la suite les librairies
personnalisées.
LaspécificationJSPdéfinitlesactionsstandardssuivantes :
●<jsp:useBean>
●<jsp:setProperty>
●<jsp:getProperty>
●<jsp:param>
●<jsp:include>
●<jsp:forward>
●<jsp:plugin>,<jsp:params>,<jsp:fallback>
●<jsp:attribute>
●<jsp:body>
●<jsp:invoke>
●<jsp:doBody>
Cesactionsstandardssontcertainementlesplusimportantes.Ellespermettentdecréeroud
’
importerunJavaBean
<jsp:useBean>,<jsp:setProperty>,<jsp:getProperty>
- 7 -© ENI Editions - All rigths reserved
danslapageetdelemanipuler.L
’
action
<jsp:useBean../>
possèdeplusieursattributsmaistroissontessentiels(id,
classetscope).
<jsp:useBean id="monObjet" class="maClasse" scope="session" />
Lasignificationestlasuivante :sil
’
objetmonObjetexistedanslaportéeindiquéeparlapropriétéscopealorsilest
importé dans la page et peut être utilisé, sinon cet objet est créé avec la portée indiquée et peut être utilisé.
L
’
élément
id
estenfaitlenomutilisépouraccéderauJavaBeandanslerestedelapage.Cenomdoitêtreunique,il
s
’
agitd
’
unnomdevariableréférençantl
’
instanceduJavaBean.
Lesvaleurspossiblessont :
●request :pourlesattributsdeportéerequête.
●session :pourlesattributsdeportéesession.
●
application
:pourlesattributsdeportéeapplication.
●
page
:pourlesattributsdeportéepage.
L
’
élémentclassestlenompleinementqualifiédelaclasseduJavaBean.Ilestindispensabledecréeruneréférenceà
unJavaBean à l
’
aide de
<jsp:useBean.../>
avant de pouvoir utiliser les actions standards
<jsp:setProperty.../>
et
<jsp:getProperty.../>
quisontlesaccesseursduJavaBean.
Dansnotreprojetbetaboutique,nousavonsutiliséunobjetappeléclient1delaclasseJavaBeanClientquipermetde
stocker les informations du client en cas de succès. Nous allons modifier notre Servlet d
’
authentification afin de
stockercetobjetdanslaportéerequêteetrécupérersesinformationspourl
’
affichage.
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.javabean.Client;
public class ServletAuthentification extends HttpServlet {
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident)) &&
(motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre [urlBienvenue]
n’a pas été initialisé");
}
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//on déclenche la Servlet qui permet de gérer la partie
Modèle de l ’application
request.setAttribute("client",client);
getServletContext().getRequestDispatcher(urlBienvenue).forward(request,
response);
}
}
//authentification incorrecte
else
- 8 - © ENI Editions - All rigths reserved
{
erreursParametres.add("Les coordonnées de l ’utilisateur sont
incorrectes");
}
...
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<%
Client client=(Client)request.getAttribute("client");
if(client==null)
{
client.setIdentifiant("");
client.setMotdepasse("");
}
%>
<h2>Bienvenue client : <%= client.getIdentifiant() %>
<%= client.getMotdepasse() %></h2>
</body>
</html>
Nousremarquonsqueladernièrepartieducode,quicorrespondàlapagebienvenue.jsp,permetderécupérerl
’
objet
clientdanslaportéerequest,devérifiersaprésence(
client==null
)etd
’
affichersesinformations.Cecodeestunpeu
lourd à utiliser et nécessite beaucoup de tests. Comme l
’
objetclient est un JavaBean, nous pouvons utiliser les
actionsstandardsJSPpourlemanipuler.Nousallonsmodifiernotrecodeafind
’
utiliserceprincipe.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
</body>
</html>
Nousremarquonsquelecodeestbeaucoupplussimpleet
’’
propre
’’
.
LeJavaBeanestutilisédanssaportéeetses
accesseurs
<jsp:getProperty.../>
pouraccéderauxattributs.
Pourrésumer,lecodesuivant :
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
estéquivalentaucodesuivant :
<%
Client client=(Client)request.getAttribute("client");
if(client==null)
{
- 9 -© ENI Editions - All rigths reserved
client.setIdentifiant("");
client.setMotdepasse("");
}
%>
Voicid
’
ailleurslaportiondecodedelaServletgénérée(/work/org/apache/jsp/bienvenue_jsp.java
) :
...
out.write("<body>\n");
betaboutique.javabean.Client client = null;
synchronized (session) {
client = (betaboutique.javabean.Client) _jspx_page_context.
getAttribute("client", PageContext.SESSION_SCOPE);
if (client == null){
client = new betaboutique.javabean.Client();
_jspx_page_context.setAttribute("client", client,
PageContext.SESSION_SCOPE);
}
}
out.write("\r\n");
out.write("<h2>Bienvenue client : ");
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString
((((betaboutique.javabean.Client)_jspx_page_context.findAttribute
("client")).getIdentifiant())));
out.write( ’ ’);
out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString
((((betaboutique.javabean.Client)_jspx_page_context.findAttribute
("client")).getMotdepasse())));
out.write("</h2>\n");
out.write("</body>\n");
...
Commentcelafonctionne
t
il?
La classeClient, qui est située dans le paquetagebetaboutique.javabean, est instanciée. Nous utilisons un objet
JavaBeannomméclientquieststockédanslaportéerequest.Letestquipermetdesavoirsil
’
objetn
’
estpasnull
,
n
’
estplusobligatoireavecl
’
action<jsp:useBean...>.LaServletréaliseelle
mêmeletestdefaçontransparente.Nous
pouvons,dans notreexemple, accéder auJavaBean cardans notreServlet l
’
objetaétépasséavec une portée
request.Lecodeestlesuivant :
...
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//déclencher la Servlet qui permet de gérer la partie Modèle de l ’application
request.setAttribute("client",client);
getServletContext().getRequestDispatcher(urlBienvenue).forward
(request, response);
...
MaintenantmodifionslaportéedenotreJavaBeandecettefaçon:
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
</body>
- 10 - © ENI Editions - All rigths reserved
</html>
NotrecodeJavaBeanclientseraalorsaccessibledanslesautrespagesJSPdelasessiondecetutilisateur.Sila
portéeutiliséeestdetype
application
,l
’
objetseraalorsvisiblepartouteslespagesdusitequelquesoitl
’
utilisateur
connecté.Enfin,silaportéeest
page
,l
’
objetpourraêtreutiliséuniquementdanslapage.Dèsquel
’
utilisateurquitte
lapagecourante,l
’
objetJavaBeanestalorsdétruit.
NouspouvonsmodifiernotreServletafindemanipulerunobjetclientdanslasessiondel
’
utilisateur.
...
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//déclencher la Servlet qui permet de gérer la partie Modèle de l ’application
HttpSession session=request.getSession();
session.setAttribute("client",client);
getServletContext().getRequestDispatcher(urlBienvenue).forward
(request, response);
...
L
’
objetclientestdésormaisstockédansuneportéesession.Sinousutilisonslecodedudernierexempleexécuté,
voicilerésultat :
LeJavaBeanauneportéerequestetnepeutdoncpaslireunJavaBeandeportéesession.Ilestpossibledemodifier
laportéedenotreJavaBeanetd
’
actualisernotrepage.
...
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client" property=
"identifiant"/> <jsp:getProperty name="client" property="motdepasse"/></h2>
...
LeJavaBeanestdésormaisaccessible.Ilpossèdeuneportéesession;ilpeutdoncêtreaccédédansd
’
autrespages
dusite(ex :sessionjavabean.jsp
).
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client avec la session : <jsp:getProperty
name="client" property="identifiant"/> <jsp:getProperty
name="client" property="motdepasse"/></h2>
</body>
</html>
- 11 -© ENI Editions - All rigths reserved
Pourrésumer,lecodesuivant :
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
estéquivalentaucodesuivant :
<%
Client client=(Client)session.getAttribute("client");
if(client==null)
{
client.setIdentifiant("");
client.setMotdepasse("");
}
%>
L
’
action
<jsp:setProperty.../>
permetdemodifierlavaleurd
’
unepropriétéduJavaBean.L
’
attributnamecorrespondà
l
’
identifiant de l
’
objetJavaBeanetl
’
attribut
property
est lenom dela propriétéà modifier.La valeurde l
’
attribut
property
peutêtre*etpermetdanscecasdeliertouslesparamètresdelarequêteauJavaBean.
Voici un exemple de cette utilisation. Le formulaire HTML de la première page (authentification.html) appelle
directementunepageJSPquirenseignelesattributscorrespondantsetaffichelerésultatàl
’
écran.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Authentification - Client</h1>
<form action="bienvenueformulaire.jsp" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Identifiant/Login : </td>
<td><input type="text" name="identifiant" id="identifiant"
value="" size="20"/></td>
</tr>
<tr>
<td>Mot de passe : </td>
<td><input type="text" name="motdepasse" id="motdepasse"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
<jsp:setProperty name="client" property="*"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
</body>
</html>
- 12 - © ENI Editions - All rigths reserved
Siundesattributsn
’
estpasrenseignédanslarequête,l
’
action
<jsp:setProperty..../>
tented
’
utiliserleparamètrede
larequêteportantlemêmenomquelapropriété.Sil
’
attributn
’
estpasprésent,ilprendsimplementlavaleurnull.
L
’
action
<jsp:getProperty.../>
permet de lire la valeur d
’
une propriété du JavaBean. L
’
attribut name correspond à
l
’
identifiantdel
’
objetJavaBeanetleparamètre
property
correspondàundesattributsdel
’
objet.
Lecodesuivant :
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/></h2>
Estéquivalentaucodesuivant :
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
<h2>Bienvenue client :<%= client.getIdentifiant() %></h2>
NousremarquonsquecettetechniquetrèspuissantepermetàunintégrateurWebderéaliserducodeetd
’
utiliser
unlangageobjetdynamiquesimplement,sansêtreundéveloppeurJavaconfirmé.Cetteaction
<jsp:useBean.../>
est
donctrèsintéressanteàutilisersaufdanslecasd
’
uneportée
page
.Pourutiliserlesactions
<jsp:setProperty.../>
et
<jsp:getProperty.../>
,l
’
attributàmanipulerdoitêtreunJavaBeancequiobligeadéclarerchaqueaccesseur(cequi
parcontre,n
’
estpasobligatoirelorsquel
’
onutiliserequest.getParameter(...)
).
Son utilisation est facilement remplaçable par request.getParameter(...), elle est alors intéressante pour des
questionsde visibilitéet pourrenseigner uneliste complètede paramètresen uneseule étape<jsp:setProperty
name=
’’
client
’’
property=
’’
*
’’
/>
.
Cetteactionpermetderéaliseruneredirectionetdoncdetransférerlecontrôleàuneautrepage.
Lasyntaxeestlasuivante :
<jsp:forward page="mapage.jsp"/>
Elleestéquivalenteaucodesuivant :
<%
RequestDispatcher disp=request.getRequestDispatcher("mapage.jsp");
disp.forward(request,response);
%>
Desparamètrespeuventêtreégalementpassésàlapageappeléeenutilisantlabalise
<jsp:param>
.
<jsp:forward page="mapage.jsp">
<jsp:param name="identifiant" value="jerome"/>
<jsp:param name="motdepasse" value="lafosse"/>
</jsp:forward>
CetteactionpermetdedonnerlecontrôleàlapageJSPcourante,puislorsquecettepageestterminée,àuneautre
pageindiquéeparcettebalise.Cetélémentpermetd
’
inclurelecontenud
’
uneressourceWebstatiqueoudynamique
(danscecasaprèssontraitement)danslecontenudelaréponseHTTPdelapageJSPretournée.
Lasyntaxeestlasuivante :
<jsp:include page="mapage.jsp"/>
Elleestéquivalenteaucodesuivant :
<%
RequestDispatcher disp=request.getRequestDispatcher("mapage.jsp");
disp.include(request,response);
%>
Desparamètrespeuventêtreégalementpassésàlapageincluseenutilisantlabalise
<jsp:param>
.
<jsp:include page="mapage.jsp">
<jsp:param name="identifiant" value="jerome"/>
<jsp:param name="motdepasse" value="lafosse"/>
<
jsp:forward>
<jsp:include>
- 13 -© ENI Editions - All rigths reserved
</jsp:include>
Ilnefautpasconfondreladirectiveinclude(
<%@includefile=
’’
mapage.jsp
’’
%>
)
etl
’
action
<jsp:include/>
.La
directiveestexécutéeaumomentdelacompilationdelapageJSP,tandisquel
’
actionestexécutéeautant
de fois que la page est appelée et peut donc produire un résultat différent à chaque appel (principe d
’
un
paramètrederequêteetd
’
uneinclusiondecontenudynamique).
- 14 - © ENI Editions - All rigths reserved
Lesobjetsimplicites
1.Présentation
LesobjetsimplicitessontcréésautomatiquementlorsdutraitementdelapageJSPparleserveurJavaEE.Ilexiste
neufobjetsimplicites.L
’
avantagedesobjetsimplicitesestqu
’
ilspermettentd
’
invoquerdirectementleursméthodes
sans avoir à les déclarer et initialiser. Par exemple, une page JSP peut accéder directement à la requête par
l
’
intermédiaired
’
unobjetimplicitenommérequest.LesobjetsimplicitesutilisablesenJSPsontlessuivants :
●request :classejavax.servlet.ServletRequest
.
●response :classejavax.servlet.ServletResponse
.
●session :classejavax.servlet.http.HttpSession
.
●pageContext :classejavax.servlet.jsp.PageContext
.
●
application
:classejavax.servlet.ServletContext
.
●
config
:classejavax.servlet.ServletConfig
.
●exception :classe
java.lang.Throwable
.
●out :classejavax.servlet.jsp.JspWriter
.
●
page
:classe
java.lang.Object
.
2.Utilisation
L
’
objetimpliciterequestpermetderéférencerlecontextedelarequêteHTTP.Cetobjetpermetd
’
utiliserlesméthodes
relativesàlarequêtecourante.Leplussouvent,cetobjetestutilisépourconnaîtrelesparamètresdelarequête.Les
paramètresenvoyésaveclesméthodesPOSTetGETsontdoncmanipulables.
L
’
objetimpliciteresponsepermetdegérerlaréponserenvoyéeauclientdel
’
application.Lesinformationsgénérées
parleserveurWebavantqu
’
ellesnesoientenvoyéesauclientsontstockéesdanscetobjet.
L
’
objetimplicitesessionpermetderéférencerlecontextedesessionHTTPassociéauclient.Cetobjetpermetainside
récupéreretmanipulerlesobjetsdessessionsutilisateurs.
L
’
objetimplicitepageContextpermetderéférencerlecontextedelapageJSP.Cetobjetimplicitepermetdecentraliser
lesdifférentsattributsdelapageJSPdansunseulobjetetfournitlesfonctionnalitésassociéesàcesattributs.
Ilestpossibleparexemplederécupérerunattributdanslarequêteoulasession,modifierlavaleurd
’
unattribut,
récupérerunattributdufichierdeconfigurationdel
’
application(web.xml
)...
L
’
objetimplicite
application
permet de référencer le contexte de l
’
application Web. Le principal intérêt de cet objet
impliciteestdemanipulerlefichierdeconfigurationdel
’
application(web.xml)afindelireetd
’
écriredesparamètres.
Voiciun exemple, qui permet d
’
afficher l
’
adresseemailduWebmestre présente danslefichierde configuration de
l
’
application(web.xml
).
L’
objetrequest
L’
objetresponse
L’
objetsession
L’
objetpageContext
L’
objetapplication
- 1 -© ENI Editions - All rigths reserved
<%
out.println("Contactez le Webmestre :
"+application.getInitParameter("emailAdministrateur"));
%>
L
’
objet implicite
config
permet de référencer le contexte de l
’
application Web. Cet objet permet de manipuler les
informationsconcernantlaconfigurationdel
’
environnementdanslequelunepageJSPesttraitéesurunserveurWeb.
Il ne faut pas confondre une application et l
’
environnement de l
’
application. L
’
objetimplicite
application
permet de
manipulerlesobjetsdel
’
application,doncdéclarésdanslefichierdeconfigurationdecettefaçon :
<context -param>
<param -name>emailAdministrateur</param -name>
<param -value>admin@betaboutique.fr</param -value>
</context -param>
Parcontre,sil
’
objetimplicite
config
estutilisépouraccéderàcesattributscelanefonctionnepas.L
’
objetimplicite
config
permetdemanipulerlesinformationsdel
’
environnement,soitladéclarationsuivante :
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init-param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init-param>
Voici un exemple de code de la page bienvenueformulaire.jsp du projet betaboutique afin de bien comprendre la
distinctionentrel
’
objet
application
etl
’
objet
config
.
<%
//afficher les informations avec les objets implicites
out.println("Contactez le Webmestre :
"+application.getInitParameter("emailAdministrateur")+"<br/>");
out.println("Contactez le Webmestre :
"+config.getInitParameter("emailAdministrateur")+"<br/>");
out.println("Adresse postale du Webmestre :
"+application.getInitParameter("adressewebmestre")+"<br/>");
out.println("Adresse postale du Webmestre :
"+config.getInitParameter("adressewebmestre")+"<br/>");
%>
...
<servlet>
<servlet -name>configjsp</servlet -name>
<jsp -file>/bienvenueformulaire.jsp</jsp -file>
<init -param>
<param -name>adressewebmestre</param -name>
<param -value>2 rue du haut Dole</param -value>
</init -param>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>configjsp</servlet -name>
<url -pattern>/configjsp</url -pattern>
</servlet -mapping>
...
L’
objetconfig
- 2 - © ENI Editions - All rigths reserved
Cetexemplemontreclairementquel
’
objetimplicite
application
correspondàl
’
applicationdéployéeetquel
’
objet
config
correspondàunepartiespécifiquedel
’
environnementdel
’
application.
L
’
objetimpliciteexceptionestutilisépourgérerleserreursquipourraientseproduirelorsdutraitementdelapage
JSP.Cetobjetestaccessibledansunepaged
’
erreurdéclaréedanslefichierdeconfigurationparlesbalises :
<error
page.../>
.Voiciunexempled
’
appeld
’
unepageJSPetsoncode.
...
<error-page>
<exception -type>java.lang.Throwable</exception -type>
<location>/erreur.jsp</location>
</error-page>
...
...
<%@page isErrorPage="true" %>
<%= exception.printStackTrace(); %>
...
L
’
objetimpliciteoutpermetderéférencerlefluxdesortiedesdonnées.Ilpermetd
’
envoyerdutexteendirectionde
l
’
utilisateurWeb.ContrairementauxServlets,ilestaccessibledirectementetéviteleblocdecodesuivant :
//flux de sortie
PrintWriter out=response.getWriter();
L
’
objetimplicite
page
permetderéférencerl
’
instancecourantedelaServletobtenueaprèscompilationdelaJSP.Cet
objetestsynonymedumot
cléthisetn
’
estpastrèsutiliséenprogrammationJSP.
L’
objetexception
L’
objetout
L’
objetpage
- 3 -© ENI Editions - All rigths reserved
PremièresJSPsimples
1.Présentation
NousallonsintégrerdespremièresJSPsimplespourlamiseenpagedelaboutiqueBetaBoutique.LespagesJSP
utiliséespourdelamiseenpage,sontappeléesdesfragments,enréférenceàdesmorceauxdecarrelagedansle
mondedubâtiment.
LesfragmentsdepagesenJSPpossèdentl
’
extension.jspf.Pourledéveloppementdeceprojet,ilestutiledecréer
unesecondeapplicationavecEclipse(ex :betaboutiquefin)afindepouvoirfairedesessais(testerlesexemplesdu
guide)avecl
’
applicationbetaboutiqueetd
’
utiliseruneautreversionfinaledelaboutique.
L
’
applicationbetaboutiquefinpossèdeàcetteétapeduguidel
’
arborescencesuivante :
Lerépertoire/vuescontientlespages.jspet.jspfquiserontutiliséespourl
’
affichage.
Lerépertoire
/img
contienttouteslesimagesduprojet.
Lerépertoire/csscontientlesfeuillesdestyleduprojet.
Lerépertoire/javascriptcontientleslibrairiesJavaScriptutiliséespourlescontrôles,effets,auto
complétion...
Lerépertoire/admincontientlespagesetrépertoirespourlapartieadministration.
Cetteapplicationcontientlecodedéfinitifmaispaslesexemplesettestsquenousréalisons.
Lefichierdeconfigurationdel
’
applicationestlesuivant :
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- fichier de point de départ de l ’application -->
<!--
il interdit en plus l ’exploration de l ’arborescence -->
<!--
attention, chemin toujours relatif -->
<welcome-file-list>
<welcome -file>index.jsp</welcome -file>
</welcome -file-list>
</web-app>
Pourlemomentlefichierdeconfigurationdel
’
application(web.xml)estsimpleetindiquelapaged
’
accueilàutiliser
lorsdel
’
appeldu site(projet). Decette façon,en utilisant l
’
URLsuivante :http://localhost:8080/betaboutiquefin/,
c
’
estlapageindex.jspquiseraappelée.
Lapageindex.jspesttrèssimple,ellepossèdedeuxdirectivesd
’
inclusionafind
’
utiliserunepagefragmentpourl
’
en
têtedusiteetuneautrepourlepieddepage.
- 1 -© ENI Editions - All rigths reserved
<%@ include file="vues/outils/entete.jspf" %>
<%@ include file="vues/outils/piedpage.jspf" %>
Le code utilisé pour la mise en page est donc présent dans les deux pages suivantes /vues/outils/entete.jspf
et/vues/outils/piedpage.jspf
.
Lors de l
’
utilisation d
’
inclusions, il faut faire très attention aux chemins des images, librairies, feuilles de style et
scriptsJavaScript.Eneffet,dansnotreexemplec
’
estlapageindex.jspquiestappelée,notrecheminseradoncrelatif
àcettepage.Danscecas,auseindelapage/vues/outils/entete.jspf,ilfaudrautiliserlerépertoire
/img
directement
sansremonterdansl
’
arborescence(ex :
../../img
).
Voiciunexempled
’
utilisationdelafeuilledestyleetducheminprécisé:
<!-- feuilles de styles -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
2.Utilisation
NousutilisonsdonctroispagesJSPpourréaliserlamiseenpageduprojet.Lapage/vues/outils/entete.jspfutilise
elle
mêmelapage/vues/outils/navigation.jspf.Lapage/vues/outils/piedpage.jspfestutiliséepourlebasdepage.
Lecodedelapage/vues/outils/entete.jspfestprésentéci
dessous.Cecodesimple,estessentiellementcomposéde
balisesHTML.Laseulepartieutilisée(etquinécessitel
’
utilisationd
’
unepageJSP)enJavaestladirectived
’
inclusion
pourlemenudenavigation :
<html>
<head>
<title>BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
<!-- feuilles de styles -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
<link rel="stylesheet" type="text/css" href="css/styles_panier.css"
title="defaut" />
<!-- javascript -->
<link type="text/css" rel="stylesheet" media="all"
href="javascript/jtip/jtip.css"/>
<script type="text/javascript" src=" javascript/jquery/jquery.js"></script>
- 2 - © ENI Editions - All rigths reserved
<script type="text/javascript" src="javascript/jtip/jtip.js"></script>
</head>
<body>
<div id="global">
<div id="entete">
<table border="0" cellspacing="0" cellpadding="0"
width="100%" height="100%">
<tr>
<td align="left" valign="top"><a href="/"><img
src="img/bandeau_haut_gauche.jpg" border="0"/></a></td>
<td align="left" valign="top"><img src=
"img/bandeau_haut_centre.jpg"/></td>
<td align="left" valign="top" background=
"img/bandeau_haut_droite.jpg" width="222">
<table border="0" cellspacing="0"
cellpadding="0" width="100%" height="100%">
<tr><td height="50">
<form id="formulairerecherche"
name="formulairerecherche" action="" method="post">
<div class="titregris">RECHERCHE</div>
<input type="text" id="recherche"
name="recherche" value=""/>
<input type="image" src="img/ok.gif"
align="absmiddle"/>
</form>
</td></tr>
<tr><td align="center"><span class="liengris">
<a href="">MON COMPTE</a></span>
<span class="liengris"><a href="">MON PANIER</a></span></td></tr>
<tr><td align="right" height="20"><span
class="liengris"><a href="">DECONNEXION</a> </span><br/><br/>
</td></tr>
</table>
</td>
</tr>
</table>
</div>
<div id="contenu">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td valign="top" width="200">
<! -- COLONNE GAUCHE -->
<%@ include file="navigation.jspf" %>
</td>
<! -- COLONNE CENTRE -->
<td valign="top" width="100%" style="padding -left:5px">
<ul id="menuhaut">
<li><a href="/" title="Accueil">Accueil</a></li>
<li><a href="" title="Promotions">Promotions</a></li>
<li><a href="" title="Nouveautés">Nouveautés
</a></li>
<li><a href="" title="CGV">Conditions de vente</a></li>
<li><a href="" title="Panier">Commander</a></li>
</ul><br/>
La page de navigation /vues/outils/navigation.jspf est également très simple. Elle permet d
’
afficher la liste des
catégoriesainsiquelerésumédupanierréaliséenJavaScript.Pourlemoment,lescatégoriessontaffichéesendur,
maisilestévidentqueparlasuite,cemenuseragénérédemanièredynamique(lecturedescatégoriesdansune
sourcededonnées).
<table border="0" cellspacing="0" cellpadding="0" name="menugauche"
- 3 -© ENI Editions - All rigths reserved
id="menugauche" style="background: url( ’img/fondmenu.gif ’)
repeat-x;" height="500">
<tr>
<td width="100%" valign="top">
<ul class="menucategorie">
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Policier et Thriller</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Comédie</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Documentaires</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Jeunesse et Famille</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Séries TV</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Spectacles et Humour</a></li>
<li><a href=""><img src="img/fleche.gif"
border="0" align="absmiddle"/> Fantastique</a></li>
</ul>
</td>
</tr>
<tr>
<td valign="top" align="center" bgcolor="#ffce00"
height="50" class="textepanier">
<a href="panierresumer.php?width=400"
class="jTip" id="aidedynamique" name="Panier Dynamique"
onclick="javascript:window.location= ’’;">
< img src="img/panier.gif" border="0"
style="padding -top:10px;"/> <span>Panier</span>
</a>
<br/>
</td>
</tr>
<tr>
<td align="center"><img src="img/cartes.gif"
border="0" style="margin -bottom:5px;"/><br/></td>
</tr>
</table>
LeprojetBetaBoutiqueestcodéenXHTMLavecl
’
utilisationdetableaux,balises
<div/>
,
<span/>
...Ilestévidentque
pourundéveloppementprofessionnel,lestechniquesconformesauW3Cseraientutiliséesdepréférence(pasde
tableaupourlamiseenpage,utilisationmassivedefeuillesdestyle).
Enfin,lecodedelapage/vues/outils/piedpage.jspfestprésentéci
dessous.Ilpermetdefermerlamiseenpageet
d
’
afficherdesinformationssurlaboutique.
</td>
</tr>
</table>
<div id="betaboutique">
BetaBoutique est une boutique de et par la
société BetaBoutique SARL au Capital
10 000 Euros n° siret 111 222 333 444 555
</div>
</div>
</body>
</html>
NousavonsvucommentutiliserdespagesJSPsimplesainsiquelesfragments.jspf.Lapageobtenueàcetteétape
duprojetestprésentéeci
dessous.Lecodedelapageindex.jspestlesuivant :
<%@ include file="vues/outils/entete.jspf" %>
<%@ include file="vues/outils/piedpage.jspf" %>
- 4 - © ENI Editions - All rigths reserved
Nousremarquonsquechaquepagedusitedevrautiliserl
’
inclusiondelapaged
’
en
têteentete.jspfetdelapagede
pieddepage
piedpage.jspf
.
CettetechniqueestutiliséepartouslesdéveloppeursWebavectousleslangagesdeprogrammation.Parcontre,
Java EE offre une possibilité très souple qui permet de configurer un en
tête et un pied de page directement
applicablesàtouteslespagesdusiteparl
’
intermédiairedufichierdeconfigurationdel
’
application(web.xml
).
Pourappliquernotreen
têteetnotrepieddepageàtouteslespagesdusite,nousallonsajouterlesdirectives
<include
prelude/>
et
<include
coda/>
.CettetechniquenécessiteleremplacementdelagrammaireXMLparunschéma
pourlefichierweb.xml
.
<?xml version="1.0" encoding="UTF -8"?>
<web-app version="2.4"
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">
<!-- fichier de point de départ de l ’application -->
<!--
il interdit en plus l ’exploration de l ’arborescence -->
<welcome-file-list>
<welcome -file>index.jsp</welcome -file>
</welcome -file-list>
<jsp -config>
<jsp -property -group>
<url -pattern>*.jsp</url -pattern>
include -prelude>/vues/outils/entete.jspf</include -prelude>
<include -coda>/vues/outils/piedpage.jspf</include -coda>
</jsp -property -group>
</jsp -config>
</web-app>
NousallonsdésormaistraiterlorsdelaprochainepartielesexceptionseterreursenJSP.
- 5 -© ENI Editions - All rigths reserved
GérerlesexceptionseterreursenJSP
1.Présentation
L
’
écriture du code dans un langage informatique doit également prévoir la gestion de diverses erreurs qui ne
manquerontpasdesurvenir.Nousavonsdéjàabordélagestiondeserreursetdesexceptionslorsdespremiers
exemplesdecechapitre.Nousavonsdéjàrencontrédesbogueslorsdenosdéveloppements.Parfois,lesréponsesà
cesboguessontaffichéesdemanièrepeusympathiquesouslaformedetraceoustacktrace.
LesapplicationsWebJavapeuventtraiterlesexceptionsdedifférentesfaçons.Lamanièrelapluscourantedetraiter
lesexceptionsenJavaestd
’
utiliserlesblocstry...catchouladirectivethrows
.
EnJava,unprogrammedéclenche(throws)uneexceptionouuneerreurlorsqu
’
undysfonctionnementdétournele
programmedesonexécutionnormale.Lalistedesexceptionsestrecenséedansladocumentationjavadocdela
classeException.http://java.sun.com/j2se/1.3/docs/api/java/lang/Exception.html
Cependant,nousavonsbesoind
’
unmoyenpermettantdetraiterdesexceptionsimprévues.Nousdisposonspour
celaenJSPdedeuxsolutions :
●Ladirective
page
.
●Ledescripteurdedéploiementdel
’
application(lefichierweb.xml
).
2.Ladirectivepage
Ladirective
page
possèdeunattributnommé
errorPage
.Siuneexceptionseproduitetqu
’
ellen
’
estpasinterceptée
parnotreprogramme,c
’
est
à
direnontraitéedansnotrecode,lapageindiquéeestalorsretournéeparleserveur.Si
l
’
attribut
errorPage
commence par la barre oblique (slash), c
’
est que l
’
adresse de la page cible est exprimée
relativement à la racine du répertoire de l
’
application Web. Lors de la définition de la page d
’
erreur, l
’
attribut
isErrorPagedoitavoirlavaleurtrue
.
Nousallonsutilisernotreapplicationbetaboutiqueetmodifierlapagebienvenue.jspafindedéclenchervolontairement
desexceptions.Nousallonsappelerlapageerreursexception.jspquipermetd
’
afficherdesinformationssurl
’
exception
quivientdesedéclencher.
<%@ page isErrorPage="true" %>
<html>
<head><title>Erreurs</title></head>
<body>
<h1>Exception</h1>
<hr/>
<h2>Description de l ’exception : <%= exception.toString() %></h2>
<hr/>
<h2>Message de l ’exception : <%= exception.getMessage() %></h2>
<hr/>
</body>
</html>
Pourdéclencherunepremièreexceptionnousallonstenterdeconvertirunechaînedecaractèresenentier.Lecode
JSP est correct mais son exécution aura pour conséquence de déclencher une exception de type
java.lang.NumberFormatException
.
<%@ page errorPage="erreursexception.jsp" %>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>BIENVENUE</title>
</head>
<body>
<%
//declaration d ’une chaine de caracteres
- 1 -© ENI Editions - All rigths reserved
String chaine="machaine";
Integer chaineentier=new Integer(chaine);
%>
</body>
</html>
Lemessagenousindiquequ
’
ils
’
agitd
’
uneerreurinterneàlaServlet.Lacauseestaffichéeaveclatracedepile
(
stacktrace).Cetaffichagedetracedepileestdéclenchéenréponseàlapremièreexceptiontrouvéeetrenvoyée
parlaméthode
exception.printStackTrace()
.
IlfautbienfaireladifférenceentreuneerreurdecompilationquiempêchelatransformationparlemoteurdeJSPde
lapageenServletetuneerreurd
’
exécution.Leserreursd
’
exécutionsurviennentpendantl
’
accèsàlapageparle
visiteur.CeserreurssontcauséesparunproblèmedanslapageJSP(biencompilée)oubiendanslecodequiest
appeléparlapage,commeunJavaBeanparexempleouuneinclusion.
Lorsdudéveloppementd
’
applications,ilestimportantdesuivrelesconseilssuivantspourcorrigerlesproblèmes :
●Ilfautcommencerparlirelemessageaffichésurlapaged
’
erreur.
●Silemessageaffichénenouspermetpasdecorrigerleproblème,ilfautanalyserlefichier
.java
.
●Sivraiment dansles cas extrêmes,l
’
erreurn
’
estpas trouvée,il fautcompiler laServlet pourvoir latrace
affichée.
Ladirective
page
estdonctrèsutilepour désignerunepaged
’
erreurà afficheren casde problème.Cependant,
l
’
inconvénientdecetteméthodeestquelamêmepaged
’
erreurestrenvoyéequellequesoitl
’
exceptionrencontrée.
3.Ledescripteurdedéploiement(web.xml)
Ledescripteurdedéploiementpermetdedésignerdesgestionnairesd
’
erreurspourtoutel
’
application.Ilestainsi
possibled
’
avoirdespagesd
’
erreurdifférentes,enfonctiondutyped
’
exception,maisaussideserreursrelativesau
serveur (ex : page non trouvée, problème du serveur...). Il est possible de définir des pages d
’
erreur pour les
exceptionsJavaetpourleserreursHTTP.
La définition des pages d
’
erreur vient immédiatement après l
’
élément
<welcome
file
list/>
du fichier web.xml
conformémentàlaDTD.
Nousallonsdéfinirunepageencasd
’
erreur404(pagenontrouvéesurleserveur)danslefichierweb.xml
.
<error-page>
<error -code>404</error -code>
<location>/404.html</location>
</error-page>
Ladéfinitiond
’
unepaged
’
erreurpouruneexceptionJavaestidentiqueàcelledeserreursHTTP.Lenomdelaclasse
del
’
exceptionJavaetlapaged
’
erreurassociéesontindiquées.Dansnotrecas,lapageaffichéeencasdeproblème
deconversionsera/erreurconversion.html
.
Pourtestercettetechnique,nousallonsmodifiernotrepagebienvenue.jspetenleverladirectivedelapaged
’
erreur.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
- 2 - © ENI Editions - All rigths reserved
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<%
//declaration d ’une chaine de caracteres
String chaine="machaine";
Integer chaineentier=new Integer(chaine);
%>
</body>
</html>
Nousallonsdéclenchercettepageavantdeparamétrerlefichierweb.xmletdoncdegénéreruneerreurd
’
exécution.
Latraceaffichéesanstraitementestprésentéeci
dessous :
Une astuce de programmation consiste à déclencher volontairement l
’
erreur et à observer la trace. Dans notre
exemple,ilestvisiblequel
’
exceptionestdetype :java.lang.NumberFormatException
.
Nousallonsdoncdéfinirnotreexceptionpourl
’
erreurdeconversionenindiquantlaclasseprécisée.
<error-page>
<exception -type>java.lang.NumberFormatException</exception -type>
<location>/erreurconversion.html</location>
</error-page>
Nous avons modifié le fichier de configuration de l
’
application, il faut donc recharger l
’
application pour que nos
modificationssoientprisesencompte.Nousdéclenchonsànouveaulapagebienvenue.jspetnousobservonsquela
paged
’
erreuradaptéeestalorsaffichée.Cettetechniqueestdoncbeaucouppluspuissantequel
’
utilisationdela
directive
page
propreauxJSP.
Une page d
’
erreur indiquée dans une page JSP a la priorité sur celles indiquées par le descripteur de
déploiement.
Nouspouvonsdonc,aveccettetechniquegérerplusieursexceptionsJavaeterreursHTTP.Laprioritécorrespondà
l
’
importancedelaportéedel
’
exception,dansledescripteurdedéploiement.Nousallonsmodifierledescripteurde
déploiementdelafaçonsuivante :
<error-page>
<exception -type>java.lang.Throwable</exception -type>
<location>/erreurglobale.html</location>
</error-page>
<error-page>
<exception -type>java.lang.NumberFormatException</exception -type>
- 3 -© ENI Editions - All rigths reserved
<location>/erreurconversion.html</location>
</error-page>
<error-page>
<error -code>404</error -code>
<location>/404.html</location>
</error-page>
Nousavonsajoutéletraitementdel
’
exception
java.lang.Throwable
quicorrespondàtouteslesexceptionsgénérées
enJava.Autrementdit,dèsqu
’
uneexceptionseradéclenchée(erreurdeconversion,calcul,problèmedefermeture
d
’
unesourcededonnéesouautre)lapageerreurglobale.htmlseraappelée.Nouspouvonsrechargerl
’
applicationet
déclencherlapagebienvenue.jsppourvoirlerésultat.
Nousremarquonsquelapagepossèdeuneerreurquidéclencheuneexceptiondeconversionmaisquec
’
estlapage
d
’
erreurglobalequiestaffichée.Eneffet,leserreurssontattrapéesparordredeprioritédanslefichierdedéfinition
de l
’
application (web.xml). Comme l
’
exception
java.lang.Throwable
est plus générale que l
’
exception
java.lang.NumberFormatException
c
’
estbienlapageerreurglobale.htmlquiestaffichée.
Pourunserveurenproduction,ilfaudrajustegérerl
’
exceptiongénérale(
java.lang.Throwable
)avecunepaged
’
erreur.
L
’
idéalseraitbienentenduunepaged
’
erreuradaptéeàlaplupartdeserreursrencontrées.
Parcontre,lorsdel
’
étapededéveloppementilestimportantdenepasmettreunepagestatiqueHTMLassociéeà
l
’
exception générale
java.lang.Throwable
car nous n
’
aurons aucune trace à l
’
écran en cas d
’
erreur ce qui est très
contraignantpourlesdébogages.
- 4 - © ENI Editions - All rigths reserved
BibliothèquedetagsJSTL
1.Présentation
Ilexistedenombreuseslibrairiesdetags(utilisablesàpartirdebalisesXML)proposéespourlatechnologieJSPJava.
Une bibliothèque de balises est composée de grammaires XML (fichier
.tld
) et de classes d
’
implémentation des
fonctionnalités.
Lesresponsables Java EEse sontaperçus que denombreux développeursdépensaientbeaucoup d
’
énergiepour
créerdenouvellesbalisesrépondantsouventauxmêmesbesoins.Cesactionsavaientdessyntaxesetdesnoms
différentsmaisaccomplissaientpratiquementlamêmechose.LebutdeJSTL(
JavaserverpageStandardTagLibrary
)est
destandardiseruncertainnombred
’
actions.JSTLestdoncunensembledetagspersonnalisésdéveloppéssousla
JSR052permettantderéaliserdesopérationsdestructure(conditions,itérations...),gérerleslangues,exécuterdes
requêtesSQLetutiliserlelangageXML.
JSTLestactuellementlestandardpourl
’
utilisationdetags,maisilexistedenombreusesautreslibrairies :
●LalibrairiedetagsStruts :manipulationdeJavaBean,HTML,conditions...
(http://struts.apache.org/1.x/struts
taglib/index.html)
●LalibrairieDisplaytag :gestiondel
’
affichagedetableauxHTML,XML,Excel...
(http://displaytag.sourceforge.net/11/)
●LalibrairieImagetaglib :gestiondesopérationssurdesimages.
(http://jakarta.apache.org/taglibs/sandbox/doc/image
doc/index.html)
●LalibrairieUploadtaglib :gestionduchargementascendantdefichiers.
(http://www.servletsuite.com/servlets/uptag.htm)
●LalibrairieAjaxUploadtaglib :gestionduchargementascendantdefichiersaveclatechnologieAjax.
(http://www.servletsuite.com/servlets/ajaxuploadtag.htm)
2.Utilisation
Lamiseenplaced
’
unelibrairiedetagsJSPnécessitelechargementdel
’
archived
’
implémentationauformat
.jar
dans
leprojetetl
’
associationURI/TLDdansledescripteurdedéploiementweb.xml
.
Pourutiliserunelibrairiedetag,ilestnécessairedeprocéderdelafaçonsuivante :
●Lapremièreétapeconsisteàtéléchargerl
’
archiveauformat
.jar
quicontientl
’
implémentationdesbalises.Puis
ilfautcopiercettearchivedanslerépertoiredeslibrairies,àsavoir/WEB
INF/lib
.Ilfautaussicopiertousles
fichiers
.tld
(qui sont les grammaires XML des balises) dans le répertoire/WEB
INF/tld
ou /WEB
INF/tlds(à
créer).
●Ladeuxièmeétapeconsisteàcréerl
’
associationentrelalibrairiedetagsetnotreprojetdansledescripteur
dedéploiement(web.xml),parlebiaisd
’
unedéclarationetd
’
uneURI.
●La dernière étape consiste à ajouter la directive
<%@ taglib.../>
dans chaque page JSP devant utiliser la
librairie.
Lesfichiers
.jar
contiennentlecodeJavapourl
’
utilisationdesbalises.Parcontre,lesfichiers
.tld
contiennentla
grammairedesbalisesutilisablesaveclesarchives
.jar
.Ainsi,ladéclarationdecesgrammairesnouspermet
de bien utiliser les balises et de bénéficier de messages d
’
erreurs efficaces lors des utilisations (nombre de
paramètrespourlabalise,syntaxedesparamètres...).
- 1 -© ENI Editions - All rigths reserved
DansunpremiertempsnousallonsutiliserlabibliothèquedetagsstandardJavaEEJSTL.
JSTLpossèdequatrebibliothèquesdetags :
●Fonctionsdebaseavec
c.tld
etl
’
URIhttp://java.sun.com/jstl/core
●FonctionsdetraitementXMLavec
x.tld
etl
’
URIhttp://java.sun/com/jstl/xml
●Fonctionsd
’
internationalisation(langues)avecfmt.tldetl
’
URIhttp://java.sun.com/jstl/fmt
●FonctionsdetraitementSQLavec
sql.tld
etl
’
URIhttp://java.sun.com/jstl/sql
3.Implémentation
PourutiliserJSTL,ilfautcopierleslibrairiesjstl.jaretstandard.jardansnotrerépertoiredelibrairies/WEB
INF/lib
.Ces
archives sont disponibles sur Internet sur le site Java. Dans un second temps, nous allons copier les fichiers
.tld
(grammairesdesbalises)dansunrépertoirenommé/WEB
INF/tld
quenousallonscréer.
L
’
arborescencedenotreprojetdoitavoirlastructuresuivante :
Ilrestealorsunedernièreétapeaprèslechargementdeslibrairies
.jar
etlamiseenplacedesgrammaires
.tld
,c
’
estla
déclarationdesbibliothèquesàutiliserdansledescripteurdedéploiementduprojet(web.xml
).
Nousplaçonslecodesuivantenfindefichierweb.xmlaprèslesbalises
<error
page/>
.
...
<taglib>
<taglib -uri>/WEB -INF/tld/c.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/c.tld</taglib -location>
</taglib>
<taglib>
<taglib -uri>/WEB -INF/tld/x.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/x.tld</taglib -location>
</taglib>
<taglib>
<taglib -uri>/WEB -INF/tld/sql.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/sql.tld</taglib -location>
</taglib>
...
Labalise
<taglib
uri/>
nousindiqueuneURIpleinementqualifiéequiseraparlasuiteutiliséedansnotredirective
<%
@taglib/>
despagesJSP.Dansnotrecas,lavaleurutiliséeestlecheminverslalibrairie
c.tld
,maisnouspourrions
utilisern
’
importequelleURIpleinementqualifiée.Voiciunautreexempled
’
URI :
...
<taglib>
<taglib -uri>http://java.sun.com/jstl/core</taglib -uri>
<taglib -location>/WEB -INF/tld/c.tld</taglib -location>
</taglib>
<taglib>
<taglib -uri>http://java.sun.com/jstl/xml</taglib -uri>
<taglib -location>/WEB -INF/tld/x.tld</taglib -location>
</taglib>
<taglib>
<taglib -uri>http://java.sun.com/jstl/sql</taglib -uri>
- 2 - © ENI Editions - All rigths reserved
<taglib -location>/WEB -INF/tld/sql.tld</taglib -location>
</taglib>
...
Lasecondebalise
<taglib
location/>
permetdepréciserlecheminverslagrammairedeslibrairiesquenousvenons
d
’
installer.Danslaconfigurationprécédente,nousavonsdoncparamétréleslibrairies
c.tld
,
x.tld
et
sql.tld
.
Désormaisilneresteplusqu
’
àdéclarernosbalisesdetagsdanschaquepageJSPquisouhaitelesutiliser.Cette
opérationsimpleestréaliséeparl
’
intermédiairedeladirective
<%@taglib.../>
.Ilfautpréciserlepréfixedesbaliseset
lecheminpleinementqualifiéidentiqueàceluirenseignédansledescripteurdedéploiement :<%@tagliburi="/WEB
INF/tld/c.tld"prefix="c"%>
.
4.Utilisationdebibliothèques
a.Labibliothèquecore
Nousallons utiliserlabibliothèque core :
c.tld
.Cettebibliothèqueregroupelesactionsfondamentales.Pourcela,
nousutilisonsnotrepagebienvenue.jspetnousallonsdéfinirlesdirectivespourl
’
utilisationdenosbibliothèques.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>BIENVENUE</title>
</head>
<body>
</body>
</html>
Nosbibliothèquesdebalisessontdésormaisdéfinies,nouspouvonsutiliserdenouvellesbalisesXMLtrèspuissantes
pournosdéveloppements.
●Letagsetpermetdestockerunevariabledansuneportéeparticulière(
page
,
request
,
sessionou
application
).
●Letagoutpermetd
’
afficherlavaleurd
’
unevariable,cetagestéquivalentà
<%=...%>
.
●Letagremovepermetdesupprimerunevariable.
●Letagcatchpermetdegérerlesexceptions.
●Letag
if
estutilisépourréaliserunecondition.
●Letagchooseestutilisépourdescasmutuellementexclusifs(équivalentduswitch
).
●Letagforeachestutilisépourréaliserdesitérations.
●LetagforTokensestutilisépourdécouperunechaîneselonunouplusieursséparateurs.
●Letagimportpermetd
’
accéderàuneressourceviasonURLpourl
’
inclureoul
’
utiliserdanslapageJSP.
●Letag
redirect
permetderéaliseruneredirectionversunenouvelleURL.
Grâceàl
’
utilisationdegrammairesXML(fichiers
.tld
pourlestaglibs),Eclipsesaitgérerl
’
auto
complétiondes
balisesetleserreursdedéclaration.
- 3 -© ENI Editions - All rigths reserved
Voiciunexempled
’
utilisationdelabibliothèque
core
.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>BIENVENUE</title>
</head>
<body>
<c:set var="maVariable" value="ma valeur avec JSTL" scope="page" />
<c:out value="${maVariable}" />
<br/>
<c:if test="${1==1}">Une conditionnelle opérationnelle</c:if>
<c:if test="${1==2}">Une conditionnelle non affichée</c:if>
<br/>
<c:forEach begin="1" end="4" var="i">
<c:out value="${i}"/><br/>
</c:forEach>
<br/>
<c:import url="/message.txt" var="message"/>
<c:out value="${message}"/>
</body>
</html>
b.LabibliothèqueXML
Dansunsecondtemps,nousallonsutiliserlabibliothèquexml :
x.tld
.
Pourcela,nousallonscréerunenouvellepage
xmltaglib.jsp
etdéfinirladirectivepourutilisernosnouvellesbalises.
Cettebibliothèquetrèspuissantepermetdemanipulerdesdonnéesenprovenanced
’
uncontenuXML(documentou
contenu généré à la volée par programmation). Nous allons créer une structure de fichier XML dans notre page
xmltaglib.jsp
.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>XML</title>
</head>
<body>
<c:set var="xml">
<?xml version="1.0" encoding="ISO -8859-1"?>
<dvds>
<dvd id="1">
<titre>Taxi 4</titre>
<image>taxi4.png</image>
</dvd>
<dvd id="2">
<titre>Le choc</titre>
<image>choc.png</image>
</dvd>
<dvd id="3">
<titre>Mort un dimanche de pluie</titre>
<image>muDd32D3.png</image>
</dvd>
</dvds>
- 4 - © ENI Editions - All rigths reserved
</c:set>
<c:out value="${xml}" escapeXml="false"/>
</body>
</html>
CettepageaffichelecontenucompletXMLsouslaformed
’
unfluxtextuel.Eneffet,lavariablenomméexmlstockele
contenuducodeXMLetlabalise
<c:out.../>
permetdel
’
afficher.Letagparsepermetd
’
analyserledocumentetde
stockerlerésultatdansunevariablequipourraêtreexploitéeparlaJSP.Letagsetestéquivalentautagsetdela
bibliothèque
core
.Ilpermet d
’
évaluerl
’
expressionfourniedansl
’
attributselectetdestockerlerésultatdansune
variable.Letagoutestéquivalentautagoutdelabibliothèque
core
.Ilpermetd
’
envoyerlerésultatdanslefluxde
sortie.L
’
attributselectpermetdepréciserlecheminXPathdel
’
arbreXML.Letag
if
estéquivalentautag
if
dela
bibliothèque
core
àladifférencequ
’
ilévalueuneexpressionXPath.Letagchooseestéquivalentautagchoosedela
bibliothèque
core
àladifférencequ
’
ilutiliseuneexpressionXPath.LetagforEachestéquivalentautagforEachdela
bibliothèque
core
.Ilpermetderéaliserdesbouclessurdesnœuds.
Letagtransformpermetd
’
appliquerunetransformationXSLT(eXtensibleStylesheetLanguageTransformations)àun
documentXML.L
’
attributxslpermetdespécifierlafeuilledestylesXSLàutiliser.
LetagtransformestintroduitdanslecadreduprojetbetaboutiqueafinderéaliserunetransformationXSLTsurle
documentXMLquicontientlalistedesDVD.Lecodeprécédentpermetd
’
affichersousformetextuellelecontenudu
documentXML.
Nousallonsdoncséparerlesdonnéesdelamiseenpageenutilisantlatechniquesuivante :
UnpremierdocumentauformatXML(oucontenuXMLgénéréàlavolée)contientuniquementdesmétadonnéeset
nes
’
occupeenaucuncasdelaprésentation.Soncontenupeutavoirlastructuresuivante :
<?xml version="1.0" encoding="ISO -8859-1"?>
<dvds>
<dvd id="1">
<titre>Taxi 4</titre>
<image>taxi4.png</image>
</dvd>
<dvd id="2">
<titre>Le choc</titre>
<image>choc.png</image>
</dvd>
<dvd id="3">
<titre>Mort un dimanche de pluie</titre>
<image>muDd32D3.png</image>
</dvd>
</dvds>
Unseconddocument(présentendursurleserveur)estcomposédebalisesXML/XPathetHTML,ilpermetdelireles
donnéesdufichierXMLetdegérerlamiseenformedecesdonnées.Ils
’
occupeuniquementd
’
opérationssimples
(conditions,boucles)etdelamiseenpage.CedocumentestunefeuilledestylesXSLTauformat.xsl
.
Voiciunexempledesoncontenupourl
’
affichagedesDVD :
<?xml version="1.0" encoding="ISO -8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/
1999/XSL/Transform">
<xsl:output method="html" indent="yes" encoding="ISO -8859-1"/>
<xsl:template match="/">
<html>
- 5 -© ENI Editions - All rigths reserved
<head><title>BETABOUTIQUE</title></head>
<body>
<h1>Liste des DVD</h1>
<table border="0" cellspacing="0" cellpadding="5"
style="border -style:solid;border -width:1px">
<tr><td>TITRE</td><td>IMAGE</td><td>ID</td></tr>
<xsl:for -each select="/dvds/dvd">
<tr>
<xsl:if test="position() mod 2=1">
<xsl:attribute name="bgcolor">#eaeaea
</xsl:attribute>
</xsl:if>
<td><xsl:value -of select="titre"/></td>
<td><xsl:value -of select="image"/></td>
<td><xsl:value -of select="@id"/></td>
</tr>
</xsl:for -each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
NousavonsdoncdésormaisnosdonnéesXML,notrefichierdemiseenforme.xsl,ilnenousresteplusqu
’
àutiliser
notre page JSP qui permet par l
’
intermédiaire de la balise
<x:transform.../>
de transformer le tout en document
HTML.
<%@ page language="java" contentType="text/html; charset=ISO -8859-1"
pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>XML</title>
</head>
<body>
<c:set var="xml">
<?xml version="1.0" encoding="ISO -8859-1"?>
<dvds>
<dvd id="1">
<titre>Taxi 4</titre>
<image>taxi4.png</image>
</dvd>
<dvd id="2">
<titre>Le choc</titre>
<image>choc.png</image>
</dvd>
<dvd id="3">
<titre>Mort un dimanche de pluie</titre>
<image>muDd32D3.png</image>
</dvd>
</dvds>
</c:set>
<!-- utilisation du fichier de mise en forme -->
<c:import url="/feuillexsl.xsl" var="xsl"/>
<x:transform xml="${xml}" xslt="${xsl}"/>
</body>
</html>
- 6 - © ENI Editions - All rigths reserved
La composition des deux fichiers une fois la transformation réalisée donne un document au format HTML. Cette
techniquetrèspuissantepermetdecomplètementséparerlapartiedonnéesdelapartieprésentation.Ledocument
résultant dans notre cas est un fichier au format HTML, mais nous aurions pu générer des documents PDF, des
imagesSVG,despages3DVRML...Bienentendu,parlasuite,lecontenuXMLdesdonnéesseragénérédefaçon
dynamiqueavecunepageJSPouuneServlet.Lesdonnéesserontluesdansunesourcededonnéesdetypefichier
oubasededonnées,lafeuilledestyleseraadaptéeenconséquencepourl
’
affichageetlabalise
<x:tranform.../>
permettradegénéreràlavoléelapageHTML(oun
’
importequelautreformatcompatible).
c.LabibliothèqueI18n
NousallonsmettreenplacelabibliothèqueI18n :fmt.tld.Cettebibliothèqueregroupelesactionspourlagestionde
l
’
internationalisation.L
’
internationalisationenprogrammationetdéveloppementconsisteàgérerleslanguesetdonc
àproposerdespagesmultilingues.LetermeI18neststandardisépourl
’
internationalisationetcorrespondaux18
caractèresquicomposentlemotinternationalisationentreleietlen.
En programmation la langue est présentée avec une locale. Une locale est composée de deux paramètres : la
premièrepartie
lang
etlasecondepartiecountryséparéesparlecaractèreunderscore.Parexemple:
●lefrançaisFranceestreprésentépar :fr_FR
.
●lefrançaiscanadienestreprésentépar :fr_CA
●
l
’
anglaisaméricainestreprésentépar :en_US
Pourmanipulerlabibliothèquedelangue,nousajoutonssadéfinitiondanslefichierdeconfigurationdel
’
application
(
web.xml
).
...
<taglib>
<taglib -uri>/WEB -INF/tld/fmt.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/fmt.tld</taglib -location>
</taglib>
...
NousajoutonségalementladéclarationavecladirectiveadaptéedansnotrepageJSP,quiutilisecettebibliothèque
detags.
<%@ taglib uri="/WEB-INF/tld/fmt.tld" prefix="fmt" %>
PourlamiseenœuvredelalocalisationdesmessagesenJava,ilfaututiliserunensembledefichiersappelésbundle
enanglais.Ilfautdéfinirunfichierparlangue/locale.Chaquefichierpossèdeunpréfixecommunappelébasenameet
doitavoirl
’
extension
.properties
.Lesfichierspourdeslanguesparticulièresdoiventavoirlemêmepréfixesuivid
’
un
caractèreunderscoreetducodelangue.Pourêtreaccessibles,cesfichiersdoiventêtreinclusdansleclasspath,ils
- 7 -© ENI Editions - All rigths reserved
doiventdoncêtreplacésdanslerépertoire/WEB
INF/classes
.
CerépertoireestfréquemmenteffacéparEclipselorsdudéveloppement,lorsdesmisesàjourduprojet(compilation
desfichiers...).Nosfichiersdelanguesserontdonceffacésàchaquefois.Leplussimpleestalorsdedéposerles
fichiersdelanguesdansunrépertoirenommé/WEB
INF/src/ressourcescarcelui
ciseracopiéautomatiquement(età
chaquemodification)danslerépertoire/WEB
INF/classes
.
Pourlamiseenœuvredel
’
internationalisation,nousallonscréerunrépertoireressourcesdansl
’
arborescence/WEB
INF/srcetdeuxfichiers.Lepremierfichiernommébetaboutique.propertiescorrespondauxdonnéesdelalanguepar
défaut(françaisdansnotrecas)etlefichierbetaboutique_en.propertiescorrespondauxdonnéesenanglais.Nous
utilisonségalementunfichierpourlefrançaisnommébetaboutique_fr.properties
.Voicil
’
arborescenceaufinal :
Unfichierdelanguecontientdespairesdevaleurscomposéesd
’
unecléuniqueetdesavaleurassociée.
Exemple :
dvd.webmestre=Merci de contacter webmestre@betaboutique.com
Voicilesdeuxfichiersquenousallonsdéfinir.Cetexempleregroupeseulementdeuxtraductions,maisnouspouvons
indiquerautantdetraductionsquesouhaitées.
Fichier :betaboutique.properties
#gestion des dvd
dvd.accueil=Bienvenue sur la page des DVD
dvd.webmestre=Merci de contacter webmestre@betaboutique.com
Fichier :betaboutique_en.properties
#gestion des dvd en anglais
dvd.accueil=Welcome in the DVD page
dvd.webmestre=Please contact webmestre@betaboutique.com
Latechniqued
’
internationalisationabordéedanscechapitreestvalablepourtoutelaprogrammationJava
etserautiliséedelamêmefaçonavecdesServlets,Struts,desapplicationsJWSoudesApplets.
●LetagsetBundlepermetdelocaliser/paramétrerlebundleutilisépardéfaut.
●Letagmessagepermetdelocaliserunmessageenfonctiondesalangueetdel
’
afficher.
●LetagsetLocalepermetdepositionnerunelocale(changerdelangue).
●LetagformatNumberpermetdeformaterunnombreenfonctiondelalocale.
●LetagparseNumberpermetdesconversions.
●LetagformatDatepermetdeformaterladateselonlalocale.
●LetagsetTimeZonepermetdestockerlefuseauhorairedansunevariable.
- 8 - © ENI Editions - All rigths reserved
●LetagtimeZonepermetdepréciserlefuseauhoraireàutiliserdanslecorpsdelapage.
Avantdetestercesfonctionnalités,nousallonsmettrenotrenavigateurenfrançais.Pourcela,avecFirefoxnous
utilisonslemenuOutilsOptionsAvancé
Langues
ChoisiretnouslaissonsuniquementleslanguesFrançais[fr]
etFrançais/France[fr
fr]
.Pourlapriseencomptedecesopérations,ilfautfermerlenavigateuretouvrirunenouvelle
fenêtre.
Pourmettreenapplicationl
’
internationalisation,nousallonscréerunepageJSPnomméei18n.jspetutiliserlecode
ci
dessous :
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<%@ taglib uri="/WEB -INF/tld/fmt.tld" prefix="fmt" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>I18n</title>
</head>
<body>
<!-- préciser le bundle/fichier de langue à utiliser -->
<fmt:setBundle basename="ressources.betaboutique" />
<!-- afficher le message appelé dvd.accueil -->
<fmt:message key="dvd.accueil"/>
<br/>
<!-- afficher le message appelé dvd.webmestre -->
<fmt:message key="dvd.webmestre"/>
<br/>
<!-- afficher un message inexistant -->
<fmt:message key="dvd.retour"/>
</body>
</html>
Siaucuncoupledevaleurn
’
esttrouvépourlacléfournie,letagrenvoiealors???xxx???oùxxxreprésentelenomde
laclé,commepourlecas :
dvd.retour
.
Notrepageestcorrectementaffichéeenfrançais(languepardéfautdanslenavigateur).Nousallonsparamétrer
notrenavigateurenanglaisselonlemêmeprincipequeprécédemmentetrelancerlenavigateur.
- 9 -© ENI Editions - All rigths reserved
Lapageestautomatiquementtraduitedanslalanguesélectionnée.
Cettetechniquedeprogrammationtrèsutilepermetdeproposerdessitesmultilingues.Parlasuite,nouspourrons
proposerunservice basésuruneServlet quipermetde changer lalocaleà lavoléeavec des liensHTML.Nous
pouvonségalementmodifierunelocaleaveclestaglibsJSPdecettefaçon(aprèsavoirpositionnélenavigateuren
français) :
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<%@ taglib uri="/WEB -INF/tld/fmt.tld" prefix="fmt" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>I18n</title>
</head>
<body>
<!-- locale en anglais -->
<fmt:setLocale value="en"/>
<!-- préciser le bundle/fichier de langue à utiliser -->
<fmt:setBundle basename="ressources.betaboutique" />
- 10 - © ENI Editions - All rigths reserved
<!-- afficher le message appelé dvd.accueil -->
<fmt:message key="dvd.accueil"/>
<br/>
<!-- afficher le message appelé dvd.webmestre -->
<fmt:message key="dvd.webmestre"/>
<br/>
<!-- afficher un message inexistant -->
<fmt:message key="dvd.retour"/>
</body>
</html>
d.LabibliothèqueDataBase
CettebibliothèqueSQLfacilitel
’
accèsetlamanipulationdebasesdedonnées.Ellepermetentreautresdecréerune
connexionversunebasededonnées,deréaliserdesrequêtesdesélection,d
’
encapsulerplusieursrequêtesdans
unetransactionouderéaliserdesmisesàjour.Cesbalisessonttrèspratiquespourdessitesquinécessitentun
développementrapide.Parcontre,ellessontpeuutiliséesdanslesprojetsquiséparentlapartieVuedelapartie
Modèle/Accèsauxdonnées.Enutilisantdetellesbalises,eneffetlecodeHTML,lecodedetraitementetl
’
accèsaux
donnéessontmélangésdanslamêmepageJSP.
L
’
exemplesuivantnommésql.jsppermetdeseconnecteràlabasededonnéesbetaboutiqueetdelisterlatotalité
desenregistrementsdelatable
categorie
afindegénérerlemenuprincipaldenavigation.Letagpermetdecréerun
lienverslabasededonnéesàpartird
’
uneconnexionsimpleoud
’
unpooldeconnexionJDBC.
CecodeutiliseuneconnexionJDBCversunebasededonnéesMySQL.Ilestdoncnécessairedecopierlepilote
MySQLappropriédanslerépertoire/WEB
INF/lib
del
’
applicationou
/common/lib
duserveurTomcatou
/lib
duserveur
Tomcat6.X.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/sql.tld" prefix="sql" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>SQL</title>
</head>
<body>
<sql:setDataSource driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
user="root" password=""/>
<!-- sélectionner toutes les catégories -->
<sql:query sql="SELECT * FROM categorie ORDER BY nomcategorie
ASC" var="categories"/>
<!-- afficher les catégories -->
<ul>
<c:forEach var="categorie" begin="0" items="${categories.rows}">
<li><c:out value="${categorie.nomcategorie}"/></li>
</c:forEach>
</ul>
</body>
</html>
Danslecasd
’
unpooldeconnexionJDBC,c
’
estletag
<sql:setDataSource.../>
quichange.Lenomdenotreconnexion
JNDIestpréciséenparamètre.
<sql:setDataSource dataSource="jdbc_betaboutique_MySQL"/>
Cette bibliothèque de tag permet également de gérer des paramètres dans les requêtes, les transactions, les
rollbacks...
- 11 -© ENI Editions - All rigths reserved
Bibliothèquedebalisespersonnalisées
1.Présentation
Nousavonsétudiélesbalises/taglibsproposéesparJava,descommunautésdiversesetdesdéveloppeurs.Parfois,
pourlesbesoinsd
’
unprojetouparhabitudededéveloppementnousavonsbesoind
’
unebibliothèquedebalisesqui
n
’
existepasencoreouquinefournitparlesservicessouhaités(balisespourlapagination,lecryptagededonnées,la
transformation de chaînes de caractères). Nous pouvons alors développer notre propre bibliothèque de balises
personnaliséesensuivantleprincipedustandardJSTLetdesestaglibs.
Decettefaçon,nospagesJSPnecontiendrontaucuncodeJava.Enfait,ilnes
’
agitpasvraimentdesupprimerlecode
Javamaisplutôtdelemasquerdefaçonqu
’
ilsoitinvisiblepourlesgraphistes/concepteursdespages.
NousavonsutiliséplusieursbalisesJSPstandardscomme
<jsp:useBean.../>
,
<jsp:forward.../>
...Cesbalisesrépondent
à des besoins courants et sont utilisées par tous les programmeurs JSP. Les bibliothèques de balises servent à
répondreàdesbesoinsparticulierspournosapplications.Demême,lorsqu
’
unprogrammeuraconçuunebibliothèque
de balises, les développeurs peuvent ensuite s
’
en servir dans leurs pages JSP sans connaître le détail de leur
implémentation.Cesbalisesdynamiquess
’
utilisentdelamêmemanièrequelesbalisesHTML,lepartagedestâches
entreprogrammeursetconcepteursdel
’
interfacedevientbeaucoupplusclair.
2.Actionspersonnalisées
Le terme action personnalisée fait référence à une balise dans une page JSP. Les actions personnalisées sont
employées dans des pages JSP comme des balises ordinaires. Elles sont identifiées par un préfixe et un nom :
<préfixe:nom/>
.Le préfixe permet d
’
éviterdesconflitsdenomsentrelesbalisesdesdifférentesbibliothèques.Ce
préfixeestchoisiparledéveloppeur.Lepréfixeestsuividunomdel
’
action,égalementchoisiparledéveloppeurdela
bibliothèque.Lesactionspeuventêtrevides(justelaprésencedelabalise)maispeuventégalementposséderun
corps.Enfin,lesactionspeuventavoirdesattributsquispécifientlesdétailsdeleurcomportement.
3.Miseenplace
Lapremièreétapedanslacréationd
’
unebalisepersonnaliséeconsisteàcréerlefichierdeclassegestionnairede
balises. Ce gestionnaire de balises stocke les méthodes qui lancent des actions spécifiques lorsque les balises
personnaliséessonttraitées.CetteclasseJavachargéed
’
implémenterlecomportementdel
’
actiondoitrespecterles
spécificationsd
’
unJavaBeanetimplémenterunedesinterfacesd
’
extensiondebalises.
Ilexisteplusieursinterfacespourlagestiondesactions :
●
Tag
et
BodyTag
:pourlesactionssimplesavecousanscorps.
●IterationTag :pourgérerlesitérationsplusfacilement.
●
SimpleTag
etJspFragmentTag :pourencapsulerlecontenuducorpsdel
’
actiondansunobjet.
L
’
interface
SimpleTag
etlaclasse
SimpleTagSupport
permettentd
’
implémentertouslesgestionnairesdebalisesJSP2.0,
avecousansitérationetévaluationducorps.Pourutiliseruneactionpersonnalisée,ilsuffitdecréeruneclassequi
étend la classe de base
SimpleTagSupport
et redéfinir les méthodes nécessaires pour produire le comportement
souhaité.
Danslamajoritédescas,laméthode
doTag()
estsuffisante.Cetteméthodegèrel
’
intégralitéducomportementde
l
’
action(sanssesoucierdudébutdelabalise,ducorpsetdelafin).Ungestionnairedebalisesdoitimporterles
paquetagesjavax.servlet.jspetjavax.servlet.jsp.tagext
.
Ungestionnairedebalisessimpledoitavoiruneméthode
doStartTag()
quicontientlecodeexécuté.Cetteméthode
doitêtredéclaréeenaccèspublicafind
’
êtreaccessibleendehorsdugestionnairedebalises.Laméthode
doStartTag()
doitretournerunevaleurpourindiquerquelabaliserenvoiequelquechoseoulavaleur
SKIP_BODY
poursautercette
étape.
Afin de mettre en place notre bibliothèque de balises, nous allons commencer par créer un nouveau paquetage
nommé
taglib
etplacerlaclassesuivantedanscepaquet.
package taglib;
Lesgestionnairesdebalisessimples
- 1 -© ENI Editions - All rigths reserved
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.*;
public class SimpleTag extends TagSupport {
public int doStartTag()throws JspException
{
return SKIP_BODY;
}
}
Laclassedugestionnairedebalisesestcréée,nouspouvonsutiliserl
’
objetPageContextpourgérerlefluxdesortieet
envoyerdesdonnéesàlapageutilisatrice.LaméthodegetOut()decetobjetpermetd
’
envoyerdesinformationsau
client.Cetteméthodepeutgénéreruneexceptionqu
’
ilestdoncnécessairedetraiter.
package taglib;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.*;
public class SimpleTag extends TagSupport {
public int doStartTag()throws JspException
{
try
{
pageContext.getOut().print("Webmestre : info@betaboutique.com");
}
catch(Exception e)
{
throw new JspTagException(e.getMessage());
}
return SKIP_BODY;
}
}
4.Miseenplaced
’
unfichierdedescription
Lesfichiersdedéfinitiondesbibliothèquesportentl
’
extension
.tld
commeparexemple
c.tld
,
x.tld
oufmt.tld.Cesfichiers
quisontdesgrammairesXML,sontplacésdanslerépertoire/WEB
INF/tld
del
’
applicationcourante.
UnefoislegestionnairecrééavecuneclasseJavaadaptée,nousdevonscréerlefichierdedescriptionauformatXML.
Lefichiercommenceparunen
têteXMLquicontientdesinformationssurlefichieravecleprologueetlagrammaire
utilisée.Nouscommençonsparcréerlefichiersuivant :/WEB
INF/tld/betaboutique.tld
.
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE taglib
PUBLIC " -//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web -jsptaglibrary_1_2.dtd">
●Lesbalises
<taglib>
et
<taglib/>
sontutiliséespourdélimiterlecorpsprincipaldufichierdedescription.
●Labalise
<tlib
version/>correspondaunumérodeversiondelabibliothèqueduconcepteur.
●Labalise
<jsp
version/>correspondàlaversionJSPsupportéeparlabibliothèquedudéveloppeur.
●Labalise<short
name/>estunnomabrégédelabibliothèque.
●Labalise
<uri/>
estunidentificateuroptionnelderessources.
●Labalise
<info/>
estunebrèvedescriptiondelabibliothèque.
- 2 - © ENI Editions - All rigths reserved
Ensuite,vientladéfinitiondechacunedesbalises.Chaquedéfinitioncommenceaveclabalise
<tag/>
quiaccepteles
sous
élémentsci
dessous :
●Labalise
<name/>
estlenomofficieldelabalise.CenomestlemêmequeceluiutilisédanslespagesJSP.
●Labalise
<tag
class/>estladésignationdelaclasseJavasupportantlabalise.
●Labalise
<info/>
estunedescriptiondelabalise.
●Labalise
<body
content/>stipulelecontenudelabalise.
Voicinotrefichierdedescriptionbetaboutique.tlddanssaversionminimale :
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE taglib
PUBLIC " -//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web -jsptaglibrary_1_2.dtd">
<taglib>
<tlib -version>1.0</tlib -version>
<jsp -version>2.0</jsp -version>
<short -name>Une premiere balise pour le Webmestre</short -name>
<tag>
<name>webmestre</name>
<tag -class>taglib.SimpleTag</tag -class>
</tag>
</taglib>
5.Configurationdelalibrairiedansledescripteurweb.xml
Nousavonscréélecodedugestionnairedebalisesainsiquesagrammaireassociée,nousdevonsmaintenantréaliser
ladernièreétapequiconsisteàparamétrerledescripteurdedéploiement(web.xml)aveclabalise
<taglib.../>
.Cette
opérationestidentiqueàl
’
utilisationdesactionsstandardsJSTL,elleestsouventnomméeaiguillage.
Il faut d
’
abord définir la balise
<taglib
uri.../>
pour préciser une adresse pleinement qualifiée. Puis il faut indiquer
simplementlalocalisationdenotrefichierdedescriptiondesbalisessurleserveuretrechargerlecontextepourque
cesmodificationssoientprisesencompte.
...
<taglib>
<taglib -uri>/WEB -INF/tld/betaboutique.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/betaboutique.tld</taglib -location>
</taglib>
...
6.Utilisationd
’
unelibrairiepersonnalisée
Nous avons suivi les trois étapes nécessaires à la mise en place d
’
unebibliothèque debalises personnalisées,à
savoir :
●lacréationdelaclasseJavapourlegestionnairedebalises;
●lecodagedufichierdedescriptiondesbalisesauformatXML;
●laconfigurationdufichierweb.xml
.
Nouspouvonsmaintenantutilisernotrebibliothèqueavecladirective
<%@ taglib .../>
etl
’
URIcorrespondantedans
une page JSP. Nous allons reprendre notre page bienvenue.jsp du projet betaboutique pour mettre en œuvre la
librairie.
Ladirective
taglib
emploie l
’
attributuripour spécifierun identifiantrelatif au fichier de description des balises. Cet
identifiant doit être le même que celui spécifié dans le fichier web.xml. Il faut également préciser un préfixe pour
référencerlabibliothèquede balises quicontientl
’
informationsurlabalisepersonnalisée.Chaquebibliothèque de
- 3 -© ENI Editions - All rigths reserved
balisesabesoind
’
unpréfixedifférentpouréviterdesconflitsd
’
actions.
PourutiliserunebalisepersonnaliséedansunepageJSP,nousdevonssaisirlepréfixeetlenomdel
’
actionquenous
avons affectés dans le fichier de description, séparés par un symbole deux points. Les balises simples qui ne
contiennentpasdecorpsoud
’
informationentrelabalisededébutetdefinpeuventêtreréduitesàuneseulebalise.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<bb:webmestre/>
OU
<bb:webmestre></bb:webmestre>
</body>
</html>
7.Gestionnairedebalisesetgestiondesattributs
Unebalisepersonnaliséepeutcontenirdesattributsquiserontspécifiésauseindela pageJSP.Dansunebalise
personnalisée,unattributestreprésentéparunevariableauseindufichierdeclassedugestionnairedebalises.
Cettevariablepeutavoirunevaleurpardéfaut,quiserautiliséeparlabalisesiaucunevaleurn
’
estpréciséepour
l
’
attributlorsdesonutilisation.
Lorsque,danslapageJSP,labaliseestutiliséeavecunattribut,lavaleurdonnéepourcetattributesttransmiseau
gestionnairedebalises.Ilfautalorscréeruneméthoded
’
affectation(accesseur)pourcetattributdanslegestionnaire
debalises.Laméthodedoitêtreenaccèspublic,iln
’
yapasdetypederetouretlenomdelaméthodeestlemême
queceluidel
’
attribut.
Nousallonscréerunesecondebalisequiaffichel
’
adresseemailduWebmestreavecunlienmailtooupassuivantle
choixducodeur.
package taglib;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.*;
public class SimpleTag extends TagSupport {
//lien en mailto ou pas
private boolean mailto=false;
//action
public int doStartTag()throws JspException
{
try
{
if(mailto)
{
pageContext.getOut().print("<a
href=\"mailto:info@betaboutique.com\">Webmestre :
info@betaboutique.com</a>");
}
else
{
pageContext.getOut().print("Webmestre :
info@betaboutique.com");
}
}
catch(Exception e)
- 4 - © ENI Editions - All rigths reserved
{
throw new JspTagException(e.getMessage());
}
return SKIP_BODY;
}
//action a la fermeture du tag
public int doEndTag()
{
try
{
JspWriter out=pageContext.getOut();
out.print("<hr/>");
}
catch(Exception e)
{
}
return SKIP_BODY;
}
public boolean isMailto() {
return mailto;
}
public void setMailto(boolean mailto) {
this.mailto = mailto;
}
}
Ensuite,nousdevonsmodifiernotrefichierdedescriptionpourgérerlesattributs.Labalise
<attribute.../>
permetde
préciserdesdétailssurunattributetdoitsesitueràlasuitedelabalise<tagclass>danslefichierdedescription.Le
nomdel
’
attributestpréciséàl
’
aidedelabalise
<name>
.Lenomdonnéàcettebaliseestsensibleàlacasseetdoit
êtrelemêmequeceluiutilisédanslespagesJSP.Labalisesuivante
<required>
indiquesil
’
attributestindispensable
lorsdesonutilisationdansunepageJSP.Silavaleurutiliséeestfalse,cetattributseraoptionnel.Sisavaleurest
true
,l
’
attributdevraêtreobligatoirementutilisé,sinonlapageJSPprovoquerauneerreur.
Voicilenouveaucontenudufichierbetaboutique.tld
.
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE taglib
PUBLIC " -//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web -jsptaglibrary_1_2.dtd">
<taglib>
<tlib -version>1.0</tlib -version>
<jsp -version>2.0</jsp -version>
<short -name>Une premiere balise pour le Webmestre</short -name>
<tag>
<name>webmestre</name>
<tag -class>taglib.SimpleTag</tag -class>
<attribute>
<name>mailto</name>
<required>true</required>
</attribute>
</tag>
</taglib>
Si désormais nous utilisons notre page bienvenue.jsp avec le code suivant, la page nous indique que d
’
après la
grammairedelabibliothèquel
’
attributmailtoestobligatoire.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
- 5 -© ENI Editions - All rigths reserved
</head>
<body>
<bb:webmestre/>
</body>
</html>
Nousdevonsdoncpréciserl
’
attributmailtopourgénérerunepagesanserreur.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<bb:webmestre mailto="false"/>
OU
<bb:webmestre mailto="true"/>
</body>
</html>
Lapageestexécutéesanserreuretl
’
attributmailtoestbienprisencompte.Vousremarquezégalementl
’
utilitédela
fonction
doEndTag()
quipermetd
’
afficherunebarredeséparationHTML(
<hr/>
)aprèschaqueutilisationdelabalise.
L
’
ajout de la balise
<rtexprvalue>true</rtexprvalue>
dans le fichier de description autorise la valeur de
l
’
attributàêtreaffectéedurantl
’
exécutionducodeJSP.
8.Gestionnairedebalisesetgestionducorpsdesbalises
Lecorpsd
’
unebaliseestl
’
informationcompriseentrelesbalisesdedébutetdefin.Lecorpsdelabalisepeutêtre
constituédetexteoudecodeJSP.Ungestionnairedebalisespeutdoncêtrecréépourutiliserl
’
informationcontenue
danslecorpsdelabalisepersonnalisée.
Laméthode
doStartTag()
esttoujoursutilisée,maispourunebaliseaveccorps,cetteméthodedoitretournerlavaleur
EVAL_BODY_INCLUDE, pour que le serveur Web traite l
’
information contenue dans le corps de la balise. Le
gestionnairedebalisesdoitégalementinclureuneméthode
doEndTag()
lorsdutraitementducorps.Cetteméthode
contientpourrappel,lecodeàexécuteraprèsletraitementducorps.
Laméthode
doEndTag()
doitretournerunevaleurpourindiquersilerestedelapageJSPdoitêtretraitéounon.Dans
laplupartdescas,lavaleurretournéeestEVAL_PAGEpourindiquerquelerestedelapagedoitêtretraitéparle
serveur.Pourquelerestedelapagenesoitpastraité,ilfaututiliserlavaleurSKIP_PAGE(pourlessessionspar
exemple).
package taglib;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.*;
public class SimpleTag extends TagSupport {
//lien en mailto ou pas
- 6 - © ENI Editions - All rigths reserved
private boolean mailto=false;
//action
public int doStartTag()throws JspException
{
try
{
if(mailto)
{
pageContext.getOut().print("<a
href=\"mailto:info@betaboutique.com\">");
}
else
{
pageContext.getOut().print("Webmestre : ");
}
}
catch(Exception e)
{
throw new JspTagException(e.getMessage());
}
return EVAL_BODY_INCLUDE;
}
//action a la fermeture du tag
public int doEndTag()
{
try
{
JspWriter out=pageContext.getOut();
if(mailto)
{
pageContext.getOut().print("</a>");
}
out.print("<hr/>");
}
catch(Exception e)
{
}
return EVAL_PAGE;
}
public boolean isMailto() {
return mailto;
}
public void setMailto(boolean mailto) {
this.mailto = mailto;
}
}
Aprèsavoir créé un fichier gestionnairede balisespour une balise personnalisée, ilest conseillé d
’
inclure dans le
fichierdedescriptionunenoteindiquantsilabalisecomprenduncorps.Labalise
<body
content/>permetdesignaler
laprésenceéventuelled
’
uncorps.Letypedecontenudelabalisepersonnaliséeestalorspréciséeninsérantune
valeurexempleentrelesbalises
<body
content>et
</body
content>.Siaucuntypedecontenun
’
estspécifié,ilprendra
lavaleurpréciséepardéfaut.Unebonnehabitudeestd
’
utiliserletermeJSPpourpréciserletypedecontenu.
Ilestconseillédetoujoursinclurelabalise
<body
content>auseindufichierdedescription.Danslecasd
’
une
balisevideilfaudrautiliser
<body
content>empty</body
content>.Siparcontre,desinstructionsdynamiques
autre que du code JSP sont utilisées, il faut insérer la définition suivante :
<body
content>tagdependent</body
content>
.
Nouspouvonsdésormaisutilisernotrebaliseavecuncorps,aveclefichierdedéfinitionsuivant :
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE taglib
PUBLIC " -//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web -jsptaglibrary_1_2.dtd">
- 7 -© ENI Editions - All rigths reserved
<taglib>
<tlib -version>1.0</tlib -version>
<jsp -version>2.0</jsp -version>
<short -name>Une premiere balise pour le Webmestre</short -name>
<tag>
<name>webmestre</name>
<tag -class>taglib.SimpleTag</tag -class>
<body -content>JSP</body -content>
<attribute>
<name>mailto</name>
<required>true</required>
</attribute>
</tag>
</taglib>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<bb:webmestre mailto="false">contact@betaboutique.com</bb:webmestre>
<bb:webmestre mailto="true">info@betaboutique.com</bb:webmestre>
</body>
</html>
9.Gestionnairedebalisesetgestionducontenuducorps
Leprincipedel
’
exempleprécédentesttrèsintéressantmaisilnousempêchedemanipulerlecontenuducorpsdela
balise(adresseemaildansnotrecas).Pourmanipulerlecorpsd
’
unebalise,legestionnairedebalisesdoitdériverde
laclasse
BodyTagSupport
.Laclasse
BodyTagSupport
dériveelle
mêmedelaclasse
TagSupport
etcontientdesméthodes
permettantcetypedetraitement.
Ilfautcréeruneméthode
doAfterBody()
pourtraiterlecorpsd
’
unebalise.Danscetteméthode,l
’
objetBodyContent
permetdestockerdesinformationssurlecorpsreçu.Laméthode
getString()
permetderécupérerlecorpsdelabalise
sousla forme d
’
unechaîne decaractères quipeut alors être manipulée. Enfin, la méthode getEnclosingWriter()de
l
’
objetBodyContentpermetderetournerlerésultatàlapageJSP.
Lecodesuivantpermetdetraiterlecorpspasséaveclabaliseetdelemettreenmajuscule.
package taglib;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.*;
public class SimpleTag extends BodyTagSupport {
//lien en mailto ou pas
private boolean mailto=false;
//action
public int doAfterBody()throws JspException
{
try
{
BodyContent bodycontent=this.getBodyContent();
String bodytext=bodycontent.getString();
if(mailto)
{
bodycontent.getEnclosingWriter().print("<a
href=\"mailto:info@betaboutique.com\">"+bodytext.toUpperCase()+"
- 8 - © ENI Editions - All rigths reserved
</a><br/><hr/>");
}
else
{
bodycontent.getEnclosingWriter().print("Webmestre :
"+bodytext.toUpperCase()+"<br/><hr/>");
}
}
catch(Exception e)
{
throw new JspTagException(e.getMessage());
}
return EVAL_BODY_INCLUDE;
}
public boolean isMailto() {
return mailto;
}
public void setMailto(boolean mailto) {
this.mailto = mailto;
}
}
L
’
utilisationd
’
unebalisepersonnaliséequimanipulelecontenud
’
uncorpsestidentiqueàl
’
utilisationdetouteautre
balisecontenantdel
’
informationentrelesindicateursdedébutetdefin.Laseuledifférencerésidedanslefaitquele
gestionnairedebalisespeutmodifiercetteinformationavantdelaretourneràlapageJSP.Siunproblèmesurgit
pendantletraitementdugestionnairedebalises,uneerreurdeserveurseragénéréeetlerestedelapageJSPne
serapastraité.Lecorpsdelabalisepeutcontenirdirectementdutexteouêtregénérédynamiquementpard
’
autres
méthodes,commeducodeJavacontenudansunescriptletouuneexpression.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<bb:webmestre mailto="false">contact@betaboutique.com</bb:webmestre>
<bb:webmestre mailto="true">info@betaboutique.com
<%= new java.util.Date() %></bb:webmestre>
</body>
</html>
Cettetechniquetrèspuissanteestcourammentutiliséepouraccéderàunebasededonnées,manipulerdesimages,
réaliserduformatage(résumés,remplacementdecaractères...)ouenvoyerunemailautomatique.
Unefoisquelalibrairieestterminée,c
’
est
à
direquel
’
implémentationducodeestréaliséeetquelagrammaire
.tld
estdéclarée,nouspouvonsempaqueterlatotalitédelalibrairiedansunearchiveauformat
.jar
.Nousallonsillustrer
par un nouvel exemple la lecture de paramètre dans le fichier de configuration de l
’
application web.xml. Cette
technique est utilisée pour réaliser des liens vers des ressources comme les feuilles de style CSS ou les fichiers
JavaScript.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<context -param>
<param -name>urlApplication</param -name>
<param -value>http://192.168.0.1:8080/betaboutique/</param -value>
</context -param>
</web-app>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
- 9 -© ENI Editions - All rigths reserved
<head>
<title>TAGLIB</title>
</head>
<body>
<%
String parametre=pageContext.getServletContext().getInitParameter
("urlApplication");
if(parametre!=null)
{
out.println(parametre);
}
%>
</body>
</html>
Cette technique est très lourde à mettre en place et peu adaptée. Nous allons développer une taglib simple
permettant de réaliser ce type de code avec une seule balise. Nous commençons par créer une nouvelle classe
nomméeConfigurationdanslepaquetage
betaboutique.taglib
.
package betaboutique.taglib;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.*;
@SuppressWarnings("serial")
public class Configuration extends TagSupport
{
private String attribut;
public int doStartTag() throws JspException
{
try
{
/* afficher le paramètre contenu dans le contexte
de l’application */
pageContext.getOut().print(pageContext.getServlet
Context().getInitParameter(attribut));
}
catch (Exception e)
{
throw new JspTagException(e.getMessage());
}
return SKIP_BODY;
}
public int doEndTag()
{
return SKIP_BODY;
}
public void setAttribut(String attribut)
{
this.attribut = attribut;
}
}
Ensuite,nousprocédonsàladéfinitiondelagrammaireaveclefichierconfiguration.tlddanslerépertoire/WEB
INF/tld
.
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE taglib PUBLIC " -//Sun Microsystems, Inc.//DTD JSP Tag
Library 1.2//EN" "http://java.sun.com/dtd/web -jsptaglibrary_1_2.dtd">
<taglib>
<tlib -version>1.0</tlib -version>
<jsp -version>2.0</jsp -version>
<short -name>Opérations sur les parametres</short -name>
<tag>
<name>config</name>
<tag -class>betaboutique.taglib.Configuration</tag -class>
<body -content>JSP</body -content>
- 10 - © ENI Editions - All rigths reserved
<attribute>
<name>attribut</name>
<required>true</required>
</attribute>
</tag>
</taglib>
Ilnerestemaintenantplusqueladernièreétapedelaconfiguration,àsavoirladéclarationdelalibrairiedansle
fichierdegestiondel
’
applicationweb.xml
.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<context -param>
<param -name>urlApplication</param -name>
<param -value>http://192.168.0.1:8080/betaboutique/
</param-value>
</context -param>
<taglib>
<taglib -uri>/WEB -INF/tld/configuration.tld</taglib -uri>
<taglib -location>/WEB -INF/tld/configuration.tld</taglib -location>
</taglib>
</web-app>
L
’
exempleprécédentpeutmaintenantêtreremplacéparlecodesimplesuivant :
<%@ taglib uri="/WEB -INF/tld/configuration.tld" prefix="conf" %>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>TAGLIB</title></head>
<body>
<conf:config attribut="urlApplication"/>
</body>
</html>
- 11 -© ENI Editions - All rigths reserved
LesJavaBeansouBeans
1.Présentation
UncomposantJavaBean,égalementappelécomposantlogiciel,estuneclasseJavaconçuepourêtreréutilisablelorsdes
développementsenJava.UncomposantJavaBeandéfinit :
●Despropriétéscorrespondantauxdonnéesd
’
unobjet.
●DesévénementspermettantauJavaBeandecommuniqueravecd
’
autresclasses.
UnesimpleclasseestuncomposantJavaBeansi :
●Elleestenaccèspublic.
●Ellepossèdeunconstructeurpublicsansparamètre(constructeurpardéfaut).
●Elledéfinitdesméthodespréfixéespargetetsetappeléesaccesseurs,permettantd
’
interrogeretdemodifierdes
donnéesdelaclasse.Lespropriétéspeuventêtreéventuellementhéritéesd
’
unesuper
classe.
Les composants JavaBeans permettent une séparation entre le code Java et la gestion de l
’
affichage des données. En
renvoyantlecodedanslesBeansaulieudelelaisseraccessibledanslesscriptlets,nousallégeonslecodesourceJSP.En
informatique, les standards favorisent la portabilité des programmes d
’
un système à un autre. Nombreux sont les
programmeurs qui ont été forcés de récrire tout ou partie de leurs programmes pour les rendre utilisables sur d
’
autres
plates
formesquecelled
’
origine.
UncomposantJavaBeanestuncomposantlogiciel.Uncomposantlogicielestunblocdeprogrammeélémentairequioffre
desaccèsviauneinterface,cequipermetdemasquerlacomplexitédesdétailsducomposant.Lebutestdeconcevoirdes
applicationscomplexesetvolumineusesparcombinaisondepetitsblocs.
LemodèleJavaBean(JCA)proposeunstandardpourledéveloppementdecomposantsréutilisablesetportablesenlangage
Java. Les JavaBeans sont des composants fonctionnels capables de communiquer entre eux de façon standardisée.
L
’
architectureJCAestenthéorieapteàêtredéployéesousn
’
importequelsystèmed
’
exploitationetdansn
’
importequel
environnementapplicatif.LesJavaBeanspermettentdemasquerleursdétailsfonctionnels,cetteapprocheestappeléesous
leterme :encapsulation.Chaqueobjetpossèdeunepartieprivée,inaccessibleauxobjetsquiutilisentsesservicesetune
partiepublique,l
’
interface.Chaqueobjetest donc capable de travailleravecd
’
autres,chaquecomposantsimple(article,
utilisateur,produit...)estresponsabled
’
unetâchebienprécisequiparticipeàl
’
ensembleduprojet.
2.Utilisation
Techniquement,lesJavaBeanspermettentd
’
éviterquelecodedespagesJSPdeviennetroplongetdifficileàmanipuler.Les
méthodespubliquesquiconserventl
’
intégritéduprinciped
’
encapsulationserventàlireetmodifierlesvaleursdecertaines
variablesduJavaBean.Lesméthodesquiserventàliresontdeslecteurs(accesseurs)etcellesquipermettentdecréerou
modifierdesvaleurssontappeléesdesmodificateurs(mutateurs).Enpratiqueellessontsouventappeléesgetteretsetter.
Lesaccesseursetmutateursontdesnomsstandardisésquicommencentparuneminusculeetquisontsuivisparlenomde
lapropriétécommençantparunemajuscule.
Exemple :unattribut
prix
possèdedeuxméthodes,
getPrix()
etsetPrix(param)
.
Pournotreprojetbetaboutique,nousavonscrééunpremierJavaBeanclientaveclesattributsassociés.Parlasuite,nous
auronsuneclasseJavaBeanpourlesarticlesdelaboutique,lescommandesetlescatégories.
LeJavaBeandelaclasseClientpossèdelastructuresuivante :
package betaboutique.javabean;
public class Client implements java.io.Serializable {
private String identifiant=null;
private String motdepasse=null;
//Constructeur par défaut (sans paramètre)
public Client()
{
}
public String getIdentifiant() {
- 1 -© ENI Editions - All rigths reserved
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
}
CetteclasseestbienunJavaBeancarelleestsérialisable,ellepossèdeunconstructeurpardéfautsansparamètreettous
sesattributsconserventl
’
encapsulationenproposantuniquementunaccèsparl
’
intermédiairedesméthodes.
Sinousreprenonsnotrepaged
’
authentification(authentification.html),laServlet(ServletAuthentification)devérificationetla
pagedebienvenue(bienvenue.jsp),nouspouvonsinstancierunobjetclientencasdesuccèsetleliredanslapageJSP.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Authentification - Client</h1>
<form action="authentificationclient" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Identifiant/Login : </td>
<td><input type="text" name="identifiant" id="identifiant"
value="" size="20"/></td>
</tr>
<tr>
<td>Mot de passe : </td>
<td><input type="text" name="motdepasse" id="motdepasse"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
request.setAttribute("client",client);
getServletContext().getRequestDispatcher
(urlBienvenue).forward(request, response);
}
}
- 2 - © ENI Editions - All rigths reserved
...
LapagedebienvenueaccèdeauJavaBeanclientdefaçonsimpleetintuitive.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
</body>
</html>
Pourrappel,labalise
<jsp:useBean.../>
estobligatoireetpermetparlebiaisdesesattributsdemanipulerl
’
objetdeportée
indiquéeparl
’
attributscope.L
’
attribut
id
correspondaunomduJavaBeanàcréerouàrécupérerdanslaportée(variablequi
serviraàmanipulerl
’
objet),l
’
attributclasscorrespondàlaclasseduJavaBeanetl
’
attributscopecorrespondàlaportéeoùle
JavaBeanvaêtrecrééoulu.Sil
’
objetestprésentdanslaportée,ilestlu,sinonunnouvelobjetdenomindiquéparl
’
attribut
id
estcréé.
Laportéescopepeutavoirlesvaleurssuivantes :
●request :JavaBeanvalableuniquementdanslarequête;
●session :JavaBeanvalabletoutaulongdelasessiondel
’
utilisateur;
●
page
:JavaBeanvalableuniquementpourlapageencours;
●
application
:JavaBeanpartagéparl
’
ensembledespagesdel
’
application.
Suiteàcettedéclaration,lemoteurJSPsaitquel
’
objetdésignéestuncomposantJavaBean,cequipermetd
’
exploiterles
caractéristiquesparticulièresàcegenred
’
objet.
Les JavaBeans et les Entreprises JavaBeans (EJB) sont deux choses différentes, mais en raison de quelques
similarités, ils partagent le même nom. Les JavaBeans sont des composants écrits en Java manipulés par des
ServletsetpagesJSP.LesEJBsontdescomposantsspéciaux,fonctionnantsurserveurJavaEEetutiliséspourconstruire
lalogiqueapplicativeetd
’
accèsauxdonnées.
Lesbalises JavaBean lesplus utilisées sont :
<jsp:setProperty.../>
qui permet d
’
assignerune valeur àunepropriété (ou
touteslesvaleursautomatiquementaveclesigne*)et
<jsp:getProperty.../>
quipermetdelirelavaleurd
’
unattribut.La
balise
<jsp:setProperty.../>
possèdeunattributparamquipermetdelireunattributdanslarequêteaveclenomindiquépar
lechampparametdel
’
affecterdirectementauJavaBean.
Lapseudo
valeur*forcetouteslespropriétésd
’
uncomposantJavaBeanàprendrelesvaleursquiontététransmisesau
serveurdanslefluxdelarequête.Sicettetechniqueestutiliséeauretourd
’
uneServletousuiteàunesaisiedansun
formulaire HTML, les noms des composants de saisie du formulaire doivent être les mêmes que les propriétés
correspondantesdanslecomposantJavaBean.
LesJavaBeanspeuventêtrecréésdansunepageJSPetêtreutilisésdansd
’
autrespagesJSPdelamêmeapplicationpar
exemple,enfonctiondelaportéedéclarée.Nousallonsmontrerceprincipeenutilisantunliensurlapagedebienvenue
bienvenue.jsppourallersurlapagesessionjavabean.jspetafficherlesvaleursduJavaBean.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
- 3 -© ENI Editions - All rigths reserved
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
HttpSession session=request.getSession();
session.setAttribute("client",client);
session.setAttribute("identifiantutilisateur",
"un autre identifiant envoyé avec la requête");
getServletContext().getRequestDispatcher
(urlBienvenue).forward(request, response);
}
}
...
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" <%@ page language="java"
contentType="text/html; charset=ISO -8859-1"
pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
<jsp:setProperty name="client" property="identifiant"
value="changement de l ’identifiant après affichage"/>
<a href="sessionjavabean.jsp">Lire le JavaBean dans la session</a>
</body>
</html>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><meta http -equiv="Content -Type" content="text/html;
charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client avec la session : <jsp:getProperty
name="client" property="identifiant"/> - <jsp:getProperty
name="client" property="motdepasse"/></h2>
</body>
</html>
- 4 - © ENI Editions - All rigths reserved
L
’
instruction
<jsp:useBean.../>
permetdecréerunJavaBeand
’
untypespécifiépuisdelelieraunomfourniparl
’
attribut
id
.
Enfait,l
’
instruction
<jsp:useBean.../>
secomporteainsiseulementsiaucuncomposantJavaBeandecetypeetdecenomn
’
a
été détecté dans la portée déclarée. S
’
il en existe un (créé par une autre page JSP ou une Servlet), c
’
est le JavaBean
existantquiseraemployé.Deplus,silenomprécisépar
id
esttrouvé,l
’
actionestréaliséeavecsuccès,danslecascontraire
ilenrésulteuneexceptionClassCastException
.
Le code placé entre la balise d
’
ouverture et de fermeture
<jsp:useBean...>... </jsp:useBean>
n
’
est exécuté que si le
JavaBeann
’
existepas.SiunJavaBeanconvient,lecodeestignoré.
Dansl
’
exemplesuivant,sileJavaBeann
’
existepas,ilutilised
’
autresvaleurspourl
’
identifiantetlemotdepasse.Danslecas
contraire,cecoden
’
estpasexécuté.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
<jsp:setProperty name="client" property="identifiant"
value="changement de l ’identifiant après affichage"/>
<a href="sessionjavabean.jsp">Lire le JavaBean dans la
session</a><br/><br/>
<jsp:useBean id="client2" class="betaboutique.javabean.Client"
scope="request">
<jsp:setProperty name="client2" property="identifiant"
value="identifiant client 2"/>
<jsp:setProperty name="client2" property="motdepasse"
value="motdepasse client 2"/>
<%= "Le JavaBean client2 n ’est pas présent dans la
requête, il a été créé et initialisé" %>
</jsp:useBean>
</body>
</html>
3.LibrairiesdegestiondesJavaBeans
- 5 -© ENI Editions - All rigths reserved
LesJavaBeansétanttrèsutilisésdanslemondedelaprogrammationJava,plusieurssociétésetorganismesontdéveloppé
deslibrairiesspécifiquesdeleurgestionetmanipulation.
Nous allons utiliser la librairie commons
beansutils développée par la communauté Apache
(http://commons.apache.org/beanutils/). Cette librairie très pratique, est désormais intégrée en standard dans de très
nombreuxoutilscompatiblesJavaEEcommeleframeworkStruts.
Lemodèle(oupatternenanglais)JavaBeanestutiliséenstandard,maisilmanqueparfoisdesméthodesauxlibrairiesde
baseJavapourmanipulercesobjets. Cetteadressefournittouslescomposantsnécessairesàl
’
utilisationdelalibrairie
proposéeparlacommunautéApache:
(http://commons.apache.org/downloads/download_beanutils.cgi).
Ladocumentationdel
’
APIestprésenteàcetteadresse :
(http://commons.apache.org/beanutils/apidocs/org/apache/commons/beanutils/packagesummary.html#package_description)
Aprèsavoirtéléchargélalibrairieauformatadapté,nouspouvonscopierlesarchives
.jar
danslerépertoire/WEB
INF/lib
de
notreprojetetrenseignerleclasspathJava.L
’
arborescencedenotreprojetestalorslasuivante :
Pourinsérerlesnouvelleslibrairiesdansleprojet/classpath,ilfaututiliserlemenuProjectPropertiesJavaBuildPath
AddExternalJARs
etajouterleslibrairies.
Nous allons aborder la manipulation des JavaBeans avec la librairie common
beans et une nouvelle Servlet nommée
ServletCommonBean. Cette Servlet sera utilisée pour manipuler le JavaBean client inséré dans la session par la Servlet
d
’
authentification. Le JavaBean client est plus compliqué et possède désormais un attribut adresse qui est lui
mêmeun
objet.
package betaboutique.javabean;
public class Client implements java.io.Serializable {
private String identifiant=null;
- 6 - © ENI Editions - All rigths reserved
private String motdepasse=null;
private Adresse adresse=new Adresse();
//Constructeur par défaut (sans paramètre)
public Client()
{
}
public String getIdentifiant() {
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
public Adresse getAdresse() {
return adresse;
}
public void setAdresse(Adresse adresse) {
this.adresse = adresse;
}
}
package betaboutique.javabean;
public class Adresse implements java.io.Serializable {
private String adresse=null;
private String rue=null;
//Constructeur par défaut (sans paramètre)
public Adresse()
{
}
public String getAdresse() {
return adresse;
}
public void setAdresse(String adresse) {
this.adresse = adresse;
}
public String getRue() {
return rue;
}
public void setRue(String rue) {
this.rue = rue;
}
}
NousallonsréaliseruneauthentificationparlebiaisdelaServletadaptéequivastockerunobjetJavaBeanclientdansla
session et nous rediriger sur la page de bienvenue. Sur cette page de retour, nous allons utiliser un lien HTML pour
déclenchernotrenouvelleServletdemanipulationduJavaBeanprésentdanslasession.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
- 7 -© ENI Editions - All rigths reserved
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
Adresse adresseclient=new Adresse();
adresseclient.setAdresse("39800 POLIGNY");
adresseclient.setRue("2 rue du haut");
client.setAdresse(adresseclient);
HttpSession session=request.getSession();
session.setAttribute("client",client);
session.setAttribute("identifiantutilisateur", "un
autre identifiant envoyé avec la requête");
getServletContext().getRequestDispatcher
(urlBienvenue).forward(request, response);
}
}
...
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
<br/>
<a href="librairiecommonbean">Utiliser la librairie commonbean</a>
</body>
</html>
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.beanutils.PropertyUtils;
import betaboutique.javabean.Client;
@SuppressWarnings("serial")
public class ServletCommonBean extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la session
HttpSession session=request.getSession();
//créer un JavaBean client
Client client=new Client();
//récupérer notre JavaBean dans la session
client=(Client)session.getAttribute("client");
//afficher l ’identifiant du client
System.out.println("identifiant : "+client.getIdentifiant());
- 8 - © ENI Editions - All rigths reserved
//afficher les infos du client avec commonbean
try
{
System.out.println("identifiant commonbean :
"+PropertyUtils.getSimpleProperty(client,"identifiant"));
//changer une propriété du JavaBean à la volée
PropertyUtils.setSimpleProperty(client,
"identifiant","ma nouvelle valeur");
System.out.println("identifiant commonbean :
"+PropertyUtils.getSimpleProperty(client,"identifiant"));
//lire une propriété objet du JavaBean
System.out.println("adresse commonbean :
"+PropertyUtils.getNestedProperty(client,"adresse.rue"));
}
catch(Exception e)
{
System.out.println("Erreur de lecture");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
LaclassePropertyUtilsaétéutiliséecarellepermetdemanipulerdesJavaBeansenprogrammationJava(doncdansune
ServletoupageJSP).Ilestpossibled
’
accéderàl
’
objetainsiqu
’
àl
’
ensembledesespropriétés.
La classe PropertyUtils permet de manipuler un JavaBean existant (instancié à partir d
’
une classe JavaBean). La classe
DynaBeanpermetdemanipulerdesJavaBeanscréésàpartird
’
uneclasseoudesJavaBeanscréésàlavolée,sansavoirune
classe correspondante. Par exemple, toutes les données d
’
un formulaire HTML pour l
’
inscription d
’
une personne seront
stockées dans un DynaBean et manipulées comme un JavaBean. La classe WrapDynaBean permet de réaliser la
transformationd
’
unJavaBeanversunDynaBean
.
NousallonsmodifiernotreclassepourcréerunobjetDynaBeanàpartird
’
unJavaBeanetaffichersesvaleurs.
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.WrapDynaBean;
import betaboutique.javabean.Adresse;
import betaboutique.javabean.Client;
@SuppressWarnings("serial")
public class ServletCommonBean extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la session
HttpSession session=request.getSession();
//créer un JavaBean client
Client client=new Client();
//récupérer notre JavaBean dans la session
client=(Client)session.getAttribute("client");
try
{
//transformer l ’objet client JavaBean en DynaBean
DynaBean dynaBeanclientcopie = new WrapDynaBean(client);
//lecture de ses valeurs
System.out.println("dynaclient recopié identifiant :
"+dynaBeanclientcopie.get("identifiant"));
Adresse adresse=(Adresse)(dynaBeanclientcopie.get("adresse"));
- 9 -© ENI Editions - All rigths reserved
System.out.println("dynaclient recopié adresse :
"+adresse.getRue());
}
catch(Exception e)
{
System.out.println("Erreur de lecture");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
LaclasseLazyDynaBeanpermetdecréerdirectementdesDynaBeans,afindelesmanipulerparlasuitedansnospagesetde
bénéficierdelapuissancedecesobjets.
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.LazyDynaBean;
@SuppressWarnings("serial")
public class ServletCommonBean extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
//créer un dynabean dynamiquement
DynaBean dynaBeanclient = new LazyDynaBean();
dynaBeanclient.set("age","12 ans");
dynaBeanclient.set("profession","vendeur");
System.out.println("dynaclient créé :
"+dynaBeanclient.get("age"));
System.out.println("dynaclient créé :
"+dynaBeanclient.get("profession"));
}
catch(Exception e)
{
System.out.println("Erreur de lecture");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
UneautreclassetrèsutiledemanipulationdesJavaBeansestlaclasseBeanUtils.Nousallonsutilisercetteclasseavecun
formulaireHTMLsimple.Leformulairenommé :article.htmlpermetdesaisirlenomdel
’
article,saréférenceetsonprix.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
- 10 - © ENI Editions - All rigths reserved
<body>
<h1>Article</h1>
<form action="article" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Nom :</td>
<td><input type="text" name="nomarticle" id="nomarticle"
value="" size="20"/></td>
</tr>
<tr>
<td>Référence : </td>
<td><input type="text" name="refarticle" id="refarticle"
value="" size="20"/></td>
</tr>
<tr>
<td>Prix : </td>
<td><input type="text" name="prixarticle" id="prixarticle"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
CettepagevadéclencherlaServlet
ServletArticle
quipermetderenseignerdirectementunJavaBean
article
etd
’
allersurune
paged
’
affichagedel
’
article(
article.jsp
).
...
<servlet>
<servlet -name>servletarticle</servlet -name>
<servlet -class>betaboutique.servlets.client.ServletArticle
</servlet -class>
</servlet>
...
<servlet -mapping>
<servlet -name>servletarticle</servlet -name>
<url -pattern>/article</url -pattern>
</servlet -mapping>
...
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import betaboutique.javabean.Article;
@SuppressWarnings("serial")
public class ServletArticle extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//attention, pas de contrôle sur les saisies !
//création d ’un JavaBean article
Article article=new Article();
//création d ’une collection
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
// chaque paramètre de la requête est mis dans une collection
String name = (String) names.nextElement();
map.put(name,request.getParameterValues(name));
- 11 -© ENI Editions - All rigths reserved
}
try
{
//insérer toutes nos données dans le Bean
article en une seule étape
BeanUtils.populate(article, map);
//appeler la page d ’affichage de l ’article,
insérer l ’article dans une base ou autre...
System.out.println("Article : "+article.getNomarticle());
//l ’objet JavaBean article est mis dans la requête
request.setAttribute("article", article);
//redirection sur la page d ’affichage
getServletContext().getRequestDispatcher
("/article.jsp").forward(request, response);
}
catch(Exception e)
{
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>FICHE ARTICLE</title>
</head>
<body>
<jsp:useBean id="article" class="betaboutique.javabean.Article"
scope="request"/>
<h2>ARTICLE</h2><br/>
Nom : <jsp:getProperty name="article"
property="nomarticle"/><br/>
Référence : <jsp:getProperty name="article"
property="refarticle"/><br/>
Prix : <jsp:getProperty name="article"
property="prixarticle"/><br/>
<br/>
</body>
</html>
Cettetechniqueesttrèspuissante.Ellepermetderenseignerdirectementunobjetàpartirdedonnéessaisies.Cetobjet
pourra être ensuite manipulé avec toutes les librairies qui fonctionnent avec les JavaBeans et DynaBeans, comme la
sérialisationd
’
objet,latransformationd
’
objetsendonnéesXML,PDF...
Enuneseuleligne,grâceaucodesuivant :
BeanUtils.populate(article,map);
toutnotreobjetaétérenseigné.
Enplus,siunepropriétéestenvoyéeetcopiéedansnotreJavaBeanmaisqu
’
ellen
’
appartientpasànotreJavaBean,celane
gênepassonfonctionnement.Parexemple,sinousinséronsdansnotreformulaireHTMLunnouveauchamppourl
’
imagede
l
’
articlemaisqueleJavaBeannelepossèdepas,lorsdelacopie,celaneprovoquerapasd
’
erreur.L
’
attributserasimplement
ignoré.
Nous allons prendre un dernier exemple plus complexe avec la classe
Article
qui possède un attribut de type collection
(ensembledevaleurs)pourstockerlesacteursassociésàunarticle/DVD.
Leprincipeseraitlemêmeavecunepropriétéquiseraitunobjetd
’
uneautreclasse(ex :personneetl
’
instancedelaclasse
Adresse
).
package betaboutique.javabean;
import java.util.ArrayList;
- 12 - © ENI Editions - All rigths reserved
public class Article implements java.io.Serializable {
private String nomarticle=null;
private String refarticle=null;
private float prixarticle=0;
private ArrayList<String> acteurs=new ArrayList<String>();
//Constructeur par défaut (sans paramètre)
public Article()
{
}
public String getNomarticle() {
return nomarticle;
}
public void setNomarticle(String nomarticle) {
this.nomarticle = nomarticle;
}
public float getPrixarticle() {
return prixarticle;
}
public void setPrixarticle(float prixarticle) {
this.prixarticle = prixarticle;
}
public String getRefarticle() {
return refarticle;
}
public void setRefarticle(String refarticle) {
this.refarticle = refarticle;
}
public ArrayList<String> getActeurs() {
return acteurs;
}
public void setActeurs(ArrayList<String> acteurs) {
this.acteurs = acteurs;
}
}
LeformulaireHTMLpermetdesaisirlesarticlesavecdeschamps
<input.../>
.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Article</h1>
<form action="article" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Nom :</td>
<td><input type="text" name="nomarticle"
id="nomarticle" value="" size="20"/></td>
</tr>
<tr>
<td>Référence : </td>
<td><input type="text" name="refarticle"
id="refarticle" value="" size="20"/></td>
</tr>
<tr>
<td>Prix : </td>
<td><input type="text" name="prixarticle"
id="prixarticle" value="" size="20"/></td>
- 13 -© ENI Editions - All rigths reserved
</tr>
<tr>
<td>Acteur 1: </td>
<td><input type="text" name="acteur1"
id="acteur1" value="" size="20"/></td>
</tr>
<tr>
<td>Acteur 2: </td>
<td><input type="text" name="acteur2"
id="acteur2" value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
LaServletestunpeupluscomplexeetpermetderecomposerlacollectiondesacteursetdelastockerdansleJavaBean.La
paged
’
affichagepermetdeparcourirlacollectioncontenuedansleBean.
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import betaboutique.javabean.Article;
@SuppressWarnings("serial")
public class ServletArticle extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//création d ’un JavaBean article
Article article=new Article();
//liste des acteurs
ArrayList<String> acteurs=new ArrayList<String>();
//création d ’une collection
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
//chaque paramètre de la requête est mis dans une collection
String name = (String) names.nextElement();
//premier acteur
if(name.equals("acteur1"))
{
acteurs.add((String)request.getParameter(name));
}
//second acteur
else if(name.equals("acteur2"))
{
acteurs.add((String)request.getParameter(name));
}
//paramètre habituel
else
{
map.put(name,request.getParameterValues(name));
}
}
- 14 - © ENI Editions - All rigths reserved
//fin de la boucle, mettre la liste des acteurs dans le JavaBean
map.put("acteurs",acteurs);
try
{
//insérer toutes nos données dans le Bean
article en une seule étape
BeanUtils.populate(article, map);
//l ’objet JavaBean article est mis dans la requête
request.setAttribute("article", article);
//redirection sur la page d ’affichage
getServletContext().getRequestDispatcher
("/article.jsp").forward(request, response);
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld"
prefix="bb" %>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>FICHE ARTICLE</title>
</head>
<body>
<jsp:useBean id="article" class="betaboutique.javabean.Article"
scope="request"/>
<h2>ARTICLE</h2><br/>
Nom : <jsp:getProperty name="article" property="nomarticle"/><br/>
Référence : <jsp:getProperty name="article" property="refarticle"/><br/>
Prix : <jsp:getProperty name="article" property="prixarticle"/><br/>
Acteur : <jsp:getProperty name="article" property="acteurs"/><br/>
Acteur :
<ul>
<c:forEach var="acteur" items="${article.acteurs}">
<li><c:out value="${acteur}"/></li>
</c:forEach>
</ul>
<br/>
</body>
</html>
4.SérialiserdesJavaBeans
Les JavaBeans sont des standards et peuvent donc être sérialisés. La sérialisation (sérialization en anglais) permet
d
’
encoderunobjet(etsonétat)souslaformed
’
une suite d
’
octets.Cettesuite d
’
octetspeutensuiteêtreutiliséepour
sauvegarderl
’
objet(persistance)surledisque,dansunebasededonnéesoupermettresontransportsurleréseau.
Ilexisteensuiteunprocessussymétriquequipermetdedécoderlasuited
’
octetspourcréerunecopieconformedel
’
objet
d
’
origine.Cetteactivitéestappeléedésérialisation.
Dansdenombreuxcas,ilestnécessairedesauvegarderl
’
étatcompletd
’
unobjet.Lasolutionquivientimmédiatementà
l
’
espritestdesauvegardersousformedechaînesdecaractèreschacundesattributs.Cettesolution,s
’
avèrerapidement
trèscomplexepourlesattributsdetypetableau,collectionoumêmeobjet.Lasauvegarded
’
élémentsdetypeintouString
parexempleneposepasdeproblème,maisparcontre,lasauvegardesousformedechaînesdecaractèresd
’
éléments
issusd
’
autresclassesestcomplexe.Deplus,lalectureparlasuitedel
’
objetsérialiséenvuedelasauvegardedel
’
objetest
- 15 -© ENI Editions - All rigths reserved
trèscomplexeàgérer.LasolutionofferteparJavaestlasérialisationd
’
objets.
Ensuite,ilserapossibledelirel
’
étatcompletdecetobjetdepuisunfichierbinaireetderé
instancierunobjetdontl
’
état
seralemêmequeceluidel
’
objetprécédemmentsauvegardé.
Pourrendreuneclassesérialisable,c
’
est
à
direpourquelesobjetsdecetteclassesoienttransformablesenfluxd
’
octets,il
suffitderéaliseruneimplémentationdel
’
interface
Serializable
lorsdeladéfinitiondecetteclasse.L
’
interface
Serializable
ne
comprendaucuneméthode,ellenesertqu
’
àdéclarerlacapacitédesérialisationdesobjetsissusdelaclasse.
Unobjetestsérialisableàconditionquetoussescomposantssoienteux
mêmessérialisables.Lesméta
éléments
ainsiqueleschaînesdecaractèressontdesélémentssérialisables.Parcontre,lesélémentsquisontdéjàdesflux
d
’
octetsnesontpassérialisables(images,threads,descripteursdeflux...).Siuneclassesérialisablepossèdeunattribut
non sérialisable (une image par exemple), il faut alors déclarer cet attribut comme transient. La classe sera alors
opérationnellemaiscetattributneserapasprisencomptelorsdelasérialisation.
a.Sérialiserunobjet
Nous avons des classes sérialisables (JavaBean) dans notre projet, nous pouvons donc exploiter des flux d
’
objets
sérialisés.NousallonsdoncmodifiernotreServletdegestiond
’
articlesafind
’
enregistrernotreobjet
article
dansunfichier
binaire.LaclasseObjectOutputStreampermetlasérialisationd
’
objetsversunflux.L
’
écritured
’
unobjetestréaliséeavecla
méthode
writeObject(...)
etlapurgedubufferaveclaméthodeflush().LaServlet
ServletArticle
permetdestockersousla
formed
’
unfluxbinairel
’
objet
article
dansunfichiernommé
article.dat
présentàlaracinedudisque
C:\
.Lenomdufichier
binaireainsiquesonextensionn
’
ontpasd
’
importance.AprèsexécutiondelaServlet,nouspouvonsobserverlacréation
d
’
unfichier
C:\article.dat
avecunfluxd
’
octets.
package betaboutique.servlets.client;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import betaboutique.javabean.Article;
@SuppressWarnings("serial")
public class ServletArticle extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//création d ’un JavaBean article
Article article=new Article();
//liste des acteurs
ArrayList<String> acteurs=new ArrayList<String>();
//création d ’une collection
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
//chaque paramètre de la requête est mis dans une collection
String name = (String) names.nextElement();
//premier acteur
if(name.equals("acteur1"))
{
acteurs.add((String)request.getParameter(name));
}
//second acteur
else if(name.equals("acteur2"))
{
acteurs.add((String)request.getParameter(name));
}
//paramètre habituel
else
{
- 16 - © ENI Editions - All rigths reserved
map.put(name,request.getParameterValues(name));
}
}
//fin de la boucle, mettre la liste des acteurs
dans le JavaBean map.put("acteurs",acteurs);
try
{
//insérer toutes nos données dans le Bean
article en une seule étape
BeanUtils.populate(article, map);
try
{
//sérialiser notre objet article dans un
fichier binaire nommé article.dat
ObjectOutputStream f=new ObjectOutputStream(new
FileOutputStream("C:\\article.dat"));
//écriture
f.writeObject(article);
f.flush();
f.close();
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
b.Désérialiserunobjet
LaclasseObjectInputStreampermetdelireunfluxsérialiséenentréeetdoncdedésérialiserunobjetdepuisunflux.La
désérialisationpermetdelireunobjet(ouplusieurs)etdecréerdenouvellesinstancesdecetobjetàpartirdel
’
étatde
celui sauvegardé. La méthode
readObject()
retourne une référence de type
Object
. Il y aura donc juste à réaliser un
transtypage(cast)del
’
objetpourl
’
utiliser.
NousallonscréerunepageJSPnomméedeserialisationarticle.jspquipermetdelireleJavaBeanetdel
’
afficherendirect.
Cettepageseraappeléedirectementetserafonctionnelletantquel
’
objetsérialisé
article.dat
seraprésentsurledisquede
lamachine.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ page import="java.io.ObjectInputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="betaboutique.javabean.Article" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>FICHE ARTICLE</title>
</head>
<body>
<%
//lecture de l ’objet sérialisé
ObjectInputStream f = new ObjectInputStream(new
FileInputStream("C:\\article.dat"));
- 17 -© ENI Editions - All rigths reserved
Article articleserialise = (Article) f.readObject();
%>
<!-- renseigner le JavaBean -->
<jsp:useBean id="article" class="betaboutique.javabean.Article">
<jsp:setProperty name="article" property="nomarticle"
value="<%= articleserialise.getNomarticle() %>"/>
<jsp:setProperty name="article" property="refarticle"
value="<%= articleserialise.getRefarticle() %>"/>
<jsp:setProperty name="article" property="prixarticle"
value="<%= articleserialise.getPrixarticle() %>"/>
<jsp:setProperty name="article" property="acteurs"
value="<%= articleserialise.getActeurs() %>"/>
</jsp:useBean>
<h2>ARTICLE</h2><br/>
Nom : <jsp:getProperty name="article" property="nomarticle"/><br/>
Référence : <jsp:getProperty name="article" property="refarticle"/><br/>
Prix : <jsp:getProperty name="article" property="prixarticle"/><br/>
Acteur : <jsp:getProperty name="article" property="acteurs"/><br/>
Acteur :
<ul>
<c:forEach var="acteur" items="${article.acteurs}">
<li><c:out value="${acteur}"/></li>
</c:forEach>
</ul>
<br/>
</body>
</html>
Cettetechniquetrèsintéressantepermetdesmanipulationsd
’
objets.Nousretrouvonsbiennotreobjetd
’
origineavecses
attributssimples(chaînesdecaractères)etcomplexes(collection).Cetteméthodeestunmoyenefficacederéaliserdela
persistanced
’
objet,maisdufaitdesalenteurlorsdenombreuxaccès,nepermetpasdegérerlapersistancecomplète
d
’
uneapplication.
c.SérialisationetdésérialisationenXML
Lasérialisationprécédentesousformedefluxd
’
octetsestintéressantemaisempêchelalecturedufichieravecd
’
autres
langages que Java. Il sera donc obligatoire de désérialiser chaque objet pour pouvoir le relire. Pour répondre à ce
problème,Javaproposedesclassesquipermettentderéaliserlasérialisationetladésérialisationàpartirdedonnées
XML.
Nousallonsreprendrenotreexempleprécédent(ServletetpageJSP)pourréaliserlamêmeétapeavecunfluxXML.
...
try
{
//on insère toutes nos données dans le Bean article en une seule
étape
BeanUtils.populate(article, map);
try
{
//encoder l ’objet en XML
XMLEncoder e = new XMLEncoder(new BufferedOutputStream
(new FileOutputStream("C:\\article.xml")));
e.writeObject(article);
e.close();
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
...
L
’
objet
article
estdésormaissérialisésouslaformed
’
unfichierXMLnommé
article.xml
quipossèdelastructuresuivante :
- 18 - © ENI Editions - All rigths reserved
Dufaitdecettesérialisationavecle standard XML, ilserapossibled
’
exploiter l
’
objetavecn
’
importequelletechnologie
compatiblecommeparexemplePHP,ASP,FlashouJavaScript.
LapageJSPsuivantepermetalorsd
’
afficherl
’
objetsérialiséàpartirdufichierXML.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ page import="java.beans.XMLDecoder" %>
<%@ page import="java.io.BufferedInputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="betaboutique.javabean.Article" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>FICHE ARTICLE</title>
</head>
<body>
<%
//lecture de l ’objet sérialisé
XMLDecoder e = new XMLDecoder(new BufferedInputStream(new
FileInputStream("C:\\article.xml")));
Article articleserialise = (Article) e.readObject();
%>
<!-- renseigner le JavaBean -->
<jsp:useBean id="article" class="betaboutique.javabean.Article">
<jsp:setProperty name="article" property="nomarticle"
value="<%= articleserialise.getNomarticle() %>"/>
<jsp:setProperty name="article" property="refarticle"
value="<%= articleserialise.getRefarticle() %>"/>
<jsp:setProperty name="article" property="prixarticle"
value="<%= articleserialise.getPrixarticle() %>"/>
<jsp:setProperty name="article" property="acteurs"
value="<%= articleserialise.getActeurs() %>"/>
</jsp:useBean>
<h2>ARTICLE</h2><br/>
Nom : <jsp:getProperty name="article" property="nomarticle"/><br/>
Référence : <jsp:getProperty name="article" property="refarticle"/><br/>
Prix : <jsp:getProperty name="article" property="prixarticle"/><br/>
- 19 -© ENI Editions - All rigths reserved
Acteur : <jsp:getProperty name="article" property="acteurs"/><br/>
Acteur :
<ul>
<c:forEach var="acteur" items="${article.acteurs}">
<li><c:out value="${acteur}"/></li>
</c:forEach>
</ul>
<br/>
</body>
</html>
Latechniqueprécédentebienquetrèsfaciled
’
utilisationnécessitelepassageparunfichier
’’
temporaire
’’
.
Deplus,cette
APIesttrèslenteavecdenombreusesconnexionsetn
’
estdoncpasenvisageablepourlapersistancemassivededonnées
dansunenvironnementenproduction.
L
’
APIXStream(http://xstream.codehaus.org/)simpled
’
utilisation,permetdesérialiserdesobjetsJava,maissurtout,elle
esttrès rapideet neconsomme pas beaucoup de mémoire. L
’
API XStream est disponible sous la forme d
’
unelibrairie
xstream
1.2.2.jarquenousdevonsinclureànotreprojet(/WEB
INF/lib
etclasspath).
Un des avantages de cette librairie est qu
’
elle permet de travailler avec des objets sérialisables, c
’
est
à
dire qui
implémententl
’
interface
Serializable
maiségalementavecdesobjetsnonsérialisables.NousallonsmodifiernotreServlet
ServletArticle
afind
’
afficherl
’
articledanslaconsoleJavasouslaformed
’
unfluxXML.
...
try
{
//on insère toutes nos données dans le Bean article en une seule étape
BeanUtils.populate(article, map);
try
{
//instanciation de la classe XStream
XStream xstream = new XStream(new DomDriver());
//conversion du contenu de l ’objet article en XML
String xml=xstream.toXML(article);
//affichage de la conversion
System.out.println("XML : "+xml);
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
}
...
Lerésultat decette exécution produit le contenu XML ci
dessous. Nous remarquons deux éléments très important : la
librairieatraitéelle
mêmelescaractèresspéciauxenentitéXML(lesapostrophesdutitre)etlastructureXMLestlamême
quecelledelaclasse.Lesbalisesontlesmêmesnomsetsontfacilementmanipulables.
- 20 - © ENI Editions - All rigths reserved
Nouspouvonsreprendrenotreexempleprécédentetsérialiserl
’
objet
article
dansunfichieretlelireenledésérialisant
aveclapagedeserialiserarticle.jsp
.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ page import="com.thoughtworks.xstream.XStream" %>
<%@ page import="com.thoughtworks.xstream.io.xml.DomDriver" %>
<%@ page import="java.io.File" %>
<%@ page import="java.beans.XMLDecoder" %>
<%@ page import="java.io.BufferedInputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="betaboutique.javabean.Article" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><meta http -equiv="Content -Type" content="text/html;
charset=ISO -8859-1">
<title>FICHE ARTICLE</title>
</head>
<body>
<%
//lecture de l ’objet sérialisé
XStream xstream = new XStream(new DomDriver());
FileInputStream fis = new FileInputStream(new File
("C:\\article.xml"));
Article articleserialise =null;
try
{
articleserialise = (Article) xstream.fromXML(fis);
}
catch(Exception e)
{
}
%>
<!-- renseigner le JavaBean -->
<jsp:useBean id="article" class="betaboutique.javabean.Article">
<jsp:setProperty name="article" property="nomarticle"
- 21 -© ENI Editions - All rigths reserved
value="<%= articleserialise.getNomarticle() %>"/>
<jsp:setProperty name="article" property="refarticle"
value="<%= articleserialise.getRefarticle() %>"/>
<jsp:setProperty name="article" property="prixarticle"
value="<%= articleserialise.getPrixarticle() %>"/>
<jsp:setProperty name="article" property="acteurs"
value="<%= articleserialise.getActeurs() %>"/>
</jsp:useBean>
<h2>ARTICLE</h2><br/>
Nom : <jsp:getProperty name="article" property="nomarticle"/><br/>
Référence : <jsp:getProperty name="article" property="refarticle"/><br/>
Prix : <jsp:getProperty name="article" property="prixarticle"/><br/>
Acteur : <jsp:getProperty name="article" property="acteurs"/><br/>
Acteur :
<ul>
<c:forEach var="acteur" items="${article.acteurs}">
<li><c:out value="${acteur}"/></li>
</c:forEach>
</ul>
<br/>
</body>
</html>
Nousallonsmodifiernotreapplicationpourmettreenœuvreunmodèleàplusieursétapesetfacilementmaintenable.Un
formulairedesaisiepermetd
’
insérerunnouvelarticle.CettesaisieesttransforméeenJavaBean.L
’
objetJavaBeanest
retournéàlapageJSPquipermetdetransformersimplementleJavaBeanenfluxXML.Ceprincipepourraitêtreégalement
envisagépourunelectured
’
informationsdansunebasededonnées.LapageJSPaffichealorslesdonnéesauformatXML.
...
try
{
//on insère toutes nos données dans le Bean article en une seule étape
BeanUtils.populate(article, map);
//stocker le JavaBean dans la requête
request.setAttribute("article", article);
//redirection sur la page d ’affichage
getServletContext().getRequestDispatcher("/article.jsp").forward
(request, response);
}
catch(Exception e)
{
System.out.println("Erreur dans la Servlet ServletArticle");
}
...
<?xml version="1.0" encoding="ISO -8859-1"?>
<%@ page language="java" contentType="text/xml;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="com.thoughtworks.xstream.XStream" %>
<%@ page import="com.thoughtworks.xstream.io.xml.DomDriver" %>
<%@ page import="betaboutique.javabean.Article" %>
<%
Article article=(Article)request.getAttribute("article");
if(article!=null)
{
//instanciation d ’un objet xstream
XStream xstream = new XStream(new DomDriver());
//afficher le flux XML
out.println(xstream.toXML(article));
}
%>
LasaisieduformulairepourunarticleentraîneautomatiquementuneréponseauformatXML.Cettetechniquepeutêtre
particulièrementutiliséepourafficherdeslistingsd
’
articles,lepanierencours...
IlfautremarquerquelapageJSPcommenceparleprologueXMLetquelecontenudelapage(attributcontentType)esten
XML.DésormaiscettepagepeutêtreparséeaveclelangageFlashouJavaScript.Nousallonsaffichernotrepageavecune
feuilleXSLTpourprésenternosrésultatsdansuntableau.
VoicilecontenudelanouvellepageJSPquipermetdestockerlefluxXMLdansunevariableetdeletransformerenHTML
pourl
’
affichageparl
’
intermédiairedelafeuilleXSLTarticlexsl.xsl
.
- 22 - © ENI Editions - All rigths reserved
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="com.thoughtworks.xstream.XStream" %>
<%@ page import="com.thoughtworks.xstream.io.xml.DomDriver" %>
<%@ page import="betaboutique.javabean.Article" %>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%
String buffer=null;
Article article=(Article)request.getAttribute("article");
if(article!=null)
{
//instanciation d ’un objet xstream
XStream xstream = new XStream(new DomDriver());
//stocker le flux XML dans un buffer
buffer=xstream.toXML(article);
}
%>
<!-- stocker le flux XML dans une variable pour
le traiter par la suite -->
<c:set var="xml">
<?xml version="1.0" encoding="ISO -8859-1"?>
<%= buffer %>
</c:set>
<c:out value="${xml}"/>
Cette première partie de code permet de stocker le flux XML dans une variable et de l
’
afficher. Dans une application
professionnelle,nouspourrionsutiliserunparamètrepoursavoirsinousvoulonsenretourlatransformationoulecontenu
XMLd
’
origine.CetteétapeseraitégalementnécessairepourledéveloppementdelafeuilleXSLTafindebienanalyserla
structuregénéréeparXStreampourlesdonnées(nomdelaracine,nomdesnœuds,nomdesattributs...).
Lerésultatprésentédanslenavigateurestlesuivant :
Maintenant,nousallonsdéclencherlatransformationdecefluxXMLenHTMLgrâceànotrefeuilledestyleXSLT.
<?xml version="1.0" encoding="ISO -8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- sortie -->
<xsl:output method="html" indent="yes"
encoding="ISO -8859-1"/>
<xsl:template match="/">
<html><head><title>BETABOUTIQUE</title></head>
<body>
<h1>FICHE ARTICLE</h1>
<table border="0" cellspacing="0" cellpadding="5"
style="border -style:solid;border -width:1px;background -
color:#eaeaea">
<tr><td>Titre : </td><td><xsl:value -of select=
"/betaboutique.javabean.Article/nomarticle"/></td></tr>
<tr><td>Référence : </td><td><xsl:value -of select="
/betaboutique.javabean.Article/refarticle"/></td></tr>
<tr><td>Prix : </td><td><xsl:value -of select=
"/betaboutique.javabean.Article/prixarticle"/></td></tr>
<tr><td>Acteur(s) : </td>
<td>
<table border="0" cellspacing="0" cellpadding="5"
style="border -style:solid;border -width:1px">
tr><td>Nom</td></tr>
xsl:for -each select="/betaboutique.javabean.Article/
acteurs/string">
<tr>
<xsl:if test="position() mod 2=1">
<xsl:attribute name="bgcolor">#eaeaea</xsl:attribute>
</xsl:if>
<td><xsl:value -of select="."/></td>
</tr>
</xsl:for -each>
</table>
- 23 -© ENI Editions - All rigths reserved
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
LapageJSPdetransformationducontenuXMLencontenuHTMLestlasuivante :
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="com.thoughtworks.xstream.XStream" %>
<%@ page import="com.thoughtworks.xstream.io.xml.DomDriver" %>
<%@ page import="betaboutique.javabean.Article" %>
<%@ taglib uri="/WEB -INF/tld/c.tld" prefix="c" %>
<%@ taglib uri="/WEB -INF/tld/x.tld" prefix="x" %>
<%
String buffer=null;
Article article=(Article)request.getAttribute("article");
if(article!=null)
{
//instanciation d ’un objet xstream
XStream xstream = new XStream(new DomDriver());
//stocker le flux XML dans un buffer
buffer=xstream.toXML(article);
}
%>
<!-- stocker le flux XML dans une variable pour le traiter par la suite -->
<c:set var="xml">
<?xml version="1.0" encoding="ISO -8859-1"?>
<%= buffer %>
</c:set>
<!-- transformer le contenu XML en HTML par le biais de la feuille XSLT -->
<c:import url="/articlexsl.xsl" var="xsl"/>
<x:transform xml="${xml}" xslt="${xsl}"/>
LesdonnéessontcorrectementaffichéesaveclesbonnesentitésHTML,lastructureconservéeetlesélémentscomposés
(comme les acteurs) sont également bien traités. Grâce à cette technique, nous avons totalement séparé la partie
DonnéesdelapartieMiseenpage.Cetypedeprogrammationpermetunemeilleuremaintenanceetsurtoutdemanipuler
lesdonnéesavecdifférentslangagesselonlesbesoinsducommanditaire(Java,PHP,FlashouJavaScript).
Pourrésumer,latechnologieJavaBeanestunstandarddeprogrammation,ellepermetdoncd
’
utiliserdeslibrairiestrès
puissantes de manipulation comme la sérialisation, la transformation d
’
objets en XML, les transformations en PDF, en
VRML...Cettetechnologiepermetdegénérerdespages100%compatiblesXMLafindelesparseravecune(ouplusieurs)
feuille(s)XSLT,ducodeJavaScriptouuneapplicationgraphiqueJavaévoluée.
- 24 - © ENI Editions - All rigths reserved
Transfertdecontrôle
1.Présentation
Ilesttrèsfréquentlorsdedéveloppementsd
’
applications,d
’
échangerdesinformationsentreuneServletetunepage
JSPpuisdetransférerlecontrôledelaJSPversuneServlet.Nousallonsintroduireunexempledechaquecontrôleafin
deréaliserdesredirections,desappelsdepages...Cettetechniqueaétéprésentéetoutaulongdesexemples,mais
ilestimportantdebiencomprendresonfonctionnement.
Nousallonsutiliserl
’
exempledel
’
authentificationaveclaServletServletAuthentificationpourprésenterlesdifférents
typesderedirections.
2.Utilisation
a.Transfertducontrôled
’
uneServletàunepageJSP
Dans un premier temps, nous réalisons une simple redirection entre une page HTML ou JSP et une Servlet.
L
’
utilisateurréaliseuneauthentification(authentification.html),laServlet(ServletAuthentification)vérifiel
’
intégritédes
donnéesetappelleunepagedebienvenue(bienvenue.jsp
).
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Authentification - Client</h1>
<form action="authentificationclient" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Identifiant/Login : </td>
<td><input type="text" name="identifiant" id="identifiant"
value="" size="20"/></td>
</tr>
<tr>
<td>Mot de passe : </td>
<td><input type="text" name="motdepasse" id="motdepasse"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
- 1 -© ENI Editions - All rigths reserved
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//insérer le JavaBean dans la requête
request.setAttribute("client", client);
//redirection simple
response.sendRedirect("bienvenue.jsp");
}
}
...
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
</body>
</html>
Dans cet exemple, la page bienvenue.jsp ne peut pas afficher le JavaBean client. En effet, l
’
action
response.sendRedirect(
’’
bienvenue.jsp
’’
)
réaliseuneredirectionforcéeetperdlesdonnéesprésentesdanslarequête.
Cette technique permet de vider, la totalité de la requête et donc d
’
éviter par exemple de réaliser plusieurs
insertionsd
’
unmêmearticledansunpanierencasderafraîchissementdelapageparl
’
utilisateur.
Aveccetteméthode,ilestquandmêmepossibledeforcerlepassagedeparamètreuniquementenGETavecles
appelsHTTP.Voiciunsecondexempleaveclepassaged
’
unmessagedanslaredirectionforcée.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//créer un JavaBean client
client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//insé rer le JavaBean dans la requête
request.setAttribute("client", client);
//redirection simple
response.sendRedirect("bienvenue.jsp?message=bienvenue");
}
}
...
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ taglib uri="/WEB -INF/tld/betaboutique.tld" prefix="bb" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
- 2 - © ENI Editions - All rigths reserved
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="session"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
<% out.println("Message : "+request.getParameter("message"))
; %>
</body>
</html>
PourréaliserdestransfertsdecontrôleentreuneServletetunepageJSPsansperdrelesdonnées,nousdevons
utiliserleRequestDispatcher.Cetteclassepermetdepositionnerdesattributsdanslarequête(session
,
context...)et
delesliredansunepageJSPappelée.
LaServletprécédenteestmodifiéeavecl
’
utilisationdelaclasseRequestDispatcher
.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//insérer le JavaBean dans la requête
request.setAttribute("client", client);
//redirection simple
getServletContext().getRequestDispatcher("/bienvenue.jsp?
message=bienvenue").forward(request, response);
}
}
...
Ilestpossibled
’
améliorerl
’
exempleprécédentenpassantleparamètremessagedanslarequêteetnondanslenom
delapage.Parcontre,danscecas,ilfautmodifierlapagebienvenue.jspafind
’
utiliserlaméthode
getAttribute(...)
et
non
getParameter(...)
.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//créer un JavaBean client
Client client=new Client();
client.setIdentifiant(identifiant);
client.setMotdepasse(motdepasse);
//insérer le JavaBean dans la requête
request.setAttribute("client", client);
//insérer le message dans la requête
request.setAttribute("message", "bienvenue sur le site");
- 3 -© ENI Editions - All rigths reserved
//redirection simple
getServletContext().getRequestDispatcher
("/bienvenue.jsp").forward(request, response);
}
}
...
b.Transfertducontrôled
’
unepageJSPàuneServlet
Lorsdudéveloppementd
’
uneapplication,ilesttrèsfréquentqu
’
unepageJSPtransfèrelecontrôleàuneServlet
avecdesinformationsassociées.
Pour réaliser cette opération, il faut utiliser la directive
<jsp:forward page=
’’
nompage
’’
/>
.
Cette directive permet
égalementdepasserdesparamètresàlaServletavecladirective
<jsp:param.../>
.Parexemple,dansunepageJSP
quipermetdefaireuncalculoudechargerunfichier,aprèsleblocdetraitement,l
’
utilisateurestautomatiquement
redirigéversuneServletd
’
insertiondansunebasededonnéesaveclecodesuivant :
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<jsp:forward page="ServletInsertionImage">
<jsp:param name="insertion" value="OK"/>
</jsp:forward>
Bienquecettetechniquesoittrèssimpled
’
utilisation,ellen
’
estpastrèssouventutiliséeétantdonnéquedansla
plupartdescasc
’
est l
’
utilisateurlui
mêmequidéclenche,parl
’
intermédiaired
’
unlien ou d
’
unformulaire HTML,la
ServletappeléeparlapageJSP.
c.Transfertducontrôled
’
unepageJSPàuneautrepageJSP
UnepageJSPpeutdonnerlecontrôleàuneautrepageJSPenutilisantdeuxméthodes :
●Enutilisantladirective
<jsp:forward.../>
delapremièrepageverslaseconde.
●Enutilisantleboutond
’
unformulaireaveclesdonnéesassociées,ouunlienavecparamètres.
Pour illustrer ces techniques, nous allons de nouveau utiliser le formulaire d
’
authentification qui cette fois
ci va
appeler directement la page JSP bienvenueformulaire.jsp. Nous utilisons donc un formulaire avec passage de
paramètreenPOSTetunlienavecpassagedeparamètreenGET.Nousdonnonsenmêmetemps,l
’
utilisationdes
différentsaccèsenfonctiondelaméthodeHTTPutilisée(request.getAttribute(...)enPOSTetrequest.getParameter(...)
enGET).
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Authentification - Client</h1>
<form action="bienvenueformulaire.jsp" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Identifiant/Login : </td>
<td><input type="text" name="identifiant" id="identifiant"
value="" size="20"/></td>
</tr>
<tr>
<td>Mot de passe : </td>
<td><input type="text" name="motdepasse" id="motdepasse"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
- 4 - © ENI Editions - All rigths reserved
</table>
<br/>
<a href="bienvenueformulaire.jsp?identifiant=monidentifiant&
motdepasse=monmotdepasse">Passage du controle à une
autre page JSP avec paramètres</a>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="betaboutique.javabean.Client" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<!-- affichage des paramètres en POST -->
<jsp:useBean id="client" class="betaboutique.javabean.Client"
scope="request"/>
<jsp:setProperty name="client" property="*"/>
<h2>Bienvenue client : <jsp:getProperty name="client"
property="identifiant"/> <jsp:getProperty name="client"
property="motdepasse"/></h2>
<!-- affichage des paramètres en GET -->
<%
out.println("Identifiant : "+request.getParameter("identifiant")+"<br/>");
out.println("Mot de passe : "+request.getParameter("motdepasse")+"<br/>");
%>
</body>
</html>
- 5 -© ENI Editions - All rigths reserved
Travailleravecdesfichiersetrépertoires
1.Présentation
L
’
APIJavaestextrêmementcomplèteencequiconcernelamanipulationderépertoiresetfichiers.LespagesJSP
sont ainsi très souvent utilisées pour créer un fichier, lire le contenu d
’
un fichier, réaliser une copie d
’
un fichier
image...
Nousallonsétudierlesdifférentestechniques,classesetméthodesproposéesparJavapourgérerlesfichierset
répertoires.
2.Travailleraveclesrépertoires
NousallonscréerunepageJSPnommée
repertoire.jsp
avecunensembledecommandespourgérerlesrépertoiresen
Java. Il est nécessaire de vérifier l
’
existence d
’
un répertoire avant toute opération le concernant. Pour vérifier
l
’
existenced
’
unrépertoire,nousutilisonsunobjetissudelaclasse
File
situéedanslepaquetage
java.io
.
Cette bibliothèque est très complète pour manipuler des fichiers et répertoires (existence, gestion des droits ou
renommage).
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.io.File" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>REPERTOIRE</title>
</head>
<body>
<%
//nom du fichier présent sur le disque dur
String nomFichier="C:"+java.io.File.separatorChar+"article.xml";
//instancier un objet file
File file=new File(nomFichier);
//présence du fichier sur le disque
if(file.exists() && file.isFile())
{
out.println("Le fichier "+nomFichier+" existe sur le système<br/>");
}
else
{
out.println("Le fichier "+nomFichier+" n ’existe pas
sur le système<br/>");}
//gestion des droits
if(file.canRead())
{
out.println("Le fichier "+nomFichier+" est accessible en lecture<br/>");
}
if(file.canWrite())
{
out.println("Le fichier "+nomFichier+" est accessible en écriture<br/>");
}
//propriétés
out.println("Le chemin absolu est : "+file.getAbsoluteFile()+"<br/>");
out.println("Le chemin absolu est : "+file.getAbsolutePath()+"<br/>");
out.println("Le nom du fichier est : "+file.getName()+"<br/>");
out.println("Sa date de dernière modification est :
"+file.lastModified()+"<br/>");
out.println("Sa taille est : "+file.length()+" octets<br/>");
%>
- 1 -© ENI Editions - All rigths reserved
</body>
</html>
Cet exemple nous confirme que Java est bien multi plate
forme. En effet, pour la lecture du fichier
article.xml
,
l
’
instruction
java.io.File.separatorChar
permet de mettre le chemin dans le bon sens :
C:\article.xml
sous Windows
et
/article.xml
sousLinux.
Voiciundeuxièmeexemplequipermetd
’
afficherl
’
arborescencedesracinesdefichiersetleparcoursd
’
unrépertoire.
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.io.File" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>REPERTOIRE</title>
</head>
<body>
<%
//nom du fichier présent sur le disque dur
String nomFichier="C:"+java.io.File.separatorChar+"unzipped";
//instancier un fichier
File fichier=new File(nomFichier);
//listing du répertoire
File[] listing=fichier.listFiles();
//afficher l ’arborescence
out.println("<ul>");
for(int i=0;i<listing.length;i++)
{
out.println("<li>"+listing[i]+"</li>");
}
out.println("</ul>");
//créer un répertoire nommé essai dans nomFichier
File nouveauRepertoire=new File(nomFichier+java.io.File.
separatorChar+"essai");
nouveauRepertoire.mkdir();
//afficher la liste des racines
File[] listeRacine=File.listRoots();
out.println("<ul>");
for(int i=0;i<listeRacine.length;i++)
{
out.println("<li>"+listeRacine[i]+"</li>");
}
out.println("</ul>");
%>
</body>
</html>
3.Travailleraveclesfichiers
NousallonscréerunepageJSPnomméefichier.jspavecunensembledecommandespourgérerlesfichiersenJava.
Lesopérationsdebaseserontutilisées,avecl
’
écriturededonnéesetlalectured
’
informations.
Lapremièreétapeconsisteàcréerunobjetdetype
File
,quiserviraàspécifierlecheminetlenomdufichierquenous
souhaitonslire.Ensuite,unobjetdetype
FileReader
utilisantl
’
objetdetype
File
,doitêtrecréé.
Lalecturedesinformationsenprovenanced
’
unfichierserameilleuresilesinformationssontstockéesaufuretà
mesuredansuntampon.Laméthode
readLine()
del
’
objet
BufferedReader
permetdelireunelignedansunfichieravec
lecaractèrederuptureindiquantunenouvelleligne.
<%@ page language="java" contentType="text/html;
- 2 - © ENI Editions - All rigths reserved
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileReader" %>
<%@ page import="java.io.BufferedReader" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>FICHIER</title>
</head>
<body>
<%
//nom du fichier présent sur le disque dur
String nomFichier="C:"+java.io.File.separatorChar+"article.xml";
//instancier un objet file
File file=new File(nomFichier);
//instancier un file reader
FileReader filereader=new FileReader(file);
//instancier un buffer
BufferedReader buffreader=new BufferedReader(filereader);
//lecture du contenu du fichier
String ligne=null;
while((ligne=buffreader.readLine())!=null)
{
out.println("Ligne : "+ligne+"<br/>");
}
%>
</body>
</html>
L
’
APIJavapossèdedetrèsnombreusesclassesetméthodespourlamanipulationdesfichiers,avectoujourslesouci
derespecterlaportabilité.Ilserafacileparexempledeconnaîtrelalistedesracinesdel
’
arborescence,parcourirdes
répertoires,supprimerunfichierourépertoire,renommeroudéplacerunfichier,créerunfichiertemporaire...
4.Enrésumé
CechapitreaprésentélemécanismedeJSPenJava.LapremièrepartieaintroduitleprincipedesJSPetlecyclede
vied
’
unepaged
’
exemple.Dansunsecondtemps,lesdéclarations,commentairesetscriptletsontétéexpliquésà
partirdespagesduprojetBetaBoutique.LechapitresuivantaprésentélesobjetsimplicitesJSPquisontutilisables
directementsans instanciationni importation.La partieassociée a permis de mettreenœuvre une première JSP
simpleetdeprésenterlanotiondefragmentJSPF.Ensuite,lagestiondesexceptionsetdeserreursenJSPaété
largementdétailléedansunchapitredédié.
LechapitresuivantaprésentélanotiondebibliothèquesdetagsoutaglibsJava.Cesbibliothèquespermettentde
réaliserdesopérationsdeprogrammation,demanipulerdesobjetssimples,degérerdesdonnéesXML,demanipuler
leslangues...Cesbibliothèquestrèsutiles,sontparfoislimitéesetnécessitentdoncledéveloppementdebalises
personnalisées,comme cela a été expliquéau chapitresuivant. Ces balisespersonnalisées sontbasées sur des
classesJavaetdesfichiersdedescriptionsauformatXML.
LesJavaBeansontétéensuiteprésentésavecdesexemplesduprojetBetaBoutique.Desmanipulationssimplesont
étéd
’
abordréaliséesetontétéagrémentéesdemanipulationspluscomplexesàpartirdefluxd
’
octetsetXML.Les
techniquesdesérialisationetdésérialisationsousformesdedonnéesbrutesouXMLontaussiétédétailléesàpartir
d
’
exemplesduprojet.
L
’
avant
dernier chapitre a permis de lister et détailler le transfert de contrôle en Java EE, qui est un élément
importantdelaprogrammationMVC.Enfin,ledernierchapitreaétéuneintroductionàlamanipulationdefichierset
répertoiresenJSP/Java.
- 3 -© ENI Editions - All rigths reserved
Qu
’
estcequ
’
uneServlet?
1.Présentation
LesServletssontlabasedelaprogrammationJavaEE.Laconceptiond
’
unsiteWebdynamiqueenJavareposesur
ceséléments.UneServletestuncomposantWebconçuàpartird
’
uneclasseJavaquiestdéployéeauseind
’
une
application.LesServletssontchronologiquementledeuxièmeélémentdeJavaEEaprèsJDBC.
UneServletinteragitavecunclientWebparl
’
intermédiaireduprotocoleHTTPvialemécanismederequête/réponse.
Bien que la totalité du traitement puisse être effectué dans la Servlet, celle
ci fait souvent appel à des classes
utilitairespourlalogiquemétier.Ellesapportentuncontenudynamiqueenréponseàdesrequêtesclient.
Engénéral,uneServletn
’
estpasappeléedirectement,ellenel
’
estqu
’
àtraversuneURL.Parexemple,laServlet
ServletMonApplication.java
(compilée en ServletMonApplication.class) est appelée lorsque la pagemonapplication.htm
estinvoquée.Lapagemonapplication.htmn
’
existepassurleserveurellenesertqu
’
àfairelelienaveclaServlet.
LesServletsontdenombreuxavantages :
●Ellessontportablesetévolutives.
●Ellessontperformantesetrapidescarchargéesenmémoiredèslepremierappel.
●Ellessontdisponiblescarelless
’
exécutentdansuncontextemultitâche(uneseuleinstancecréée).
BienquelesServletsaientétéconçuespourtravailleravectouslestypesdeserveurs(HTTP,FTP,SMTP...)ellesne
sontemployéesenpratiquequ
’
avecdesserveursWebHTTP.L
’
APIServletcontientpourcelauneclassenommée
HttpServlet
afindegérerleprotocoleHTTPetlesméthodesGETetPOST.
2.RequêtesHTTP
Pourcomprendrelefonctionnementdel
’
exécutiondeServlets,ilestnécessaired
’
expliqueraupréalableleprotocole
HTTP.SousWindowsouLinux,unclientTelnetpeutêtrelancéaveclacommandesuivante :
Ensuite,unecommandeHTTPpeutêtrelancéepourrécupérerlecontenudelapaged
’
accueil.
GET /index.html HTTP/1.0
Laréponseestalorslasuivante :
- 1 -© ENI Editions - All rigths reserved
LalignedecommandeTelnetcomportelenomduprogrammeTelnetsuividunomdel
’
ordinateurhôteetleport.La
requêteestcomposéedesontype(GET)suividel
’
URIrelativeàlaressourceconcernéepuisdel
’
identificateurHTTP
etdunumérodeversionsupportéparleprogrammeTelnet.
Unepremièrepressionsurlatouche[Entrée]terminelarequêteetunesecondeindiqueauserveurquel
’
en
têteest
terminé.Lapremièrelignedelaréponsecorrespondàlaligned
’
état.ElleindiquelaversionHTTPutilisée,lecodede
laréponseetlemessage.Ensuiteviennentlesen
têtesderéponse :chemin,utilisationducache,lecookieutilisé
pourlasession,letypedecontenu,lalongueurdelaréponse,ladate...
Lorsqu
’
un logiciel/programme Web est utilisé comme un navigateur, c
’
est le navigateur lui
même qui génère ces
requêtesquanddespagesInternetsontdemandées.L
’
utilisationduprotocoleHTTPestalorstransparentepourle
client.Dansnotreexemple,nousremarquonsquelapage :http://www.google.fr/index.htmln
’
existepasetquele
serveurrenvoieunepaged
’
erreuradaptéeenconséquence.Lorsqu
’
unerequêteestenvoyéeenutilisantlaméthode
POST, elle peut comporter un corps que le client va transmettre à la ressource demandée. Le plus souvent les
requêtesPOSTsontenvoyéespardesformulairesaffichésdanslesnavigateursWeb.LaméthodeGETpeutenvoyer
desdonnéesdansl
’
URLalorsquelaméthodePOSTutiliselecorpsdelarequêtepourplacerlesdonnées.
VoiciunexempledeméthodePOSTavecdeuxparamètres(identifiantetmotdepasse) :
POST /servlet/personnes HTTP/1.0
Content-type: application/x -www-form-urlencoding
Content-length : 39
identifiant=monidentifiant&motdepasse=monmotdepasse
a.Commentleserveurvarépondreauxrequêtesdesclients?
AvecunerequêteetlaméthodeGET,leserveurlocaliselaressourcecorrespondanteàl
’
URIindiquéeetretourne
cetteressourcedanslecorpsdumessageHTTPenvoyéaunavigateurclient.Lenavigateurduclientanalysecelui
ci
etaffichelapage.Laressourcedemandéepeutêtreunprogramme.Danscecas,leserveurdoitdécoderl
’
URIet
appelerleprogrammespécifique.LeprogrammepeutêtreécritenC,Perl...
CetypedeprogrammeappeléCGIestexécutédansunprocessusdifférentdeceluiduserveuretnécessitela
création d
’
un nouveau programme en cours d
’
exécution (thread/processus) à chaque appel. Les Servlets Java
offrentdenombreuxavantagessurcesprogrammesCGI.Ellespeuventêtreexécutéesdanslemêmeprocessus
que le serveur ce qui économise énormément de ressources, elles sont donc beaucoup plus rapides et sont
portables.Eneffet,lesprogrammesécritsenCdoiventêtrerecompiléspourchaquesystèmed
’
exploitationutilisé.
- 2 - © ENI Editions - All rigths reserved
LeprojetBetaBoutique
1.Présentation
Tout au long du guide, nous allons développer un projet de boutique en ligne afin de tester et de mettre en
applicationlespartiesthéoriquesetlesexemples.
Ledéveloppementd
’
uneboutiquepermet :
●degérerdesutilisateurs/personnespourlesclients,lesadministrateurs(création,modification,suppression,
authentification);
●degérerdesarticles/produits(création,modification,suppression,tri);
●degérerunpanierdynamique(création,modification,suppression,session,listes...);
●degérerdescommandesetréservations(transactions,mails,tri...);
●degérerdeslangues(articlesenplusieurslangues);
●demanipulerlestockd
’
articles/produitsavecunclientlourd(applicationJWS).
Dansunpremiertemps,lediagrammedescasd
’
utilisationdelaboutiqueseraprésentéetnousutiliseronsensuitele
servicePersonne/ClientspourdévelopperlespremièresServlets.
L
’
entreprisefictiveBetaBoutiquevenddesarticlesenrapportaveclecinéma,principalementdesDVD.Elleexerceson
métieravecdesfichespapier,descommandesparfaxetparchèquebancaireenvoyéparcourrierpostalpuisenvoie
lacommandeauclient.Dèsquelechèqueestencaissé,elleutiliselesservicesdeLaPostepourexpédierlescolis.La
sociétéBetaBoutiquen
’
arriveplusàgérermanuellementsonexpansionetsouhaiteutiliserunsystèmed
’
Information
pourluipermettrederépondreàcettecroissance.ElleattendplusieursservicesduSystèmed
’
Informationcommela
venteenligne,lagestionducatalogued
’
articles(essentiellementdesDVD)etlabasededonnéesdesclients.
2.Expressiondesbesoins
Pour modéliser l
’
expression des besoins de la société BetaBoutique, nous allons utiliser UML (
Unified Modeling
Language).Dansunpremiertemps,lediagrammedescasd
’
utilisationquipermetdereprésenterlesfonctionnalitésdu
systèmedupointdevueutilisateurseraprésenté.
Lediagrammedescasd
’
utilisationsecompose :
●d
’
acteurs(entitésexterneshumainesourobot/matérielquiutilisentlesystème);
●decasd
’
utilisation(fonctionnalitésproposéesparlesystème).
Lesacteursutilisantlesystèmesont :
●
Employé
:lesemployésmettentàjourlecatalogued
’
articlesetvérifientlescommandesetlalistedesclients.
●
Internaute
:lespersonnesvisitantlesitepeuventconsulterlecatalogue.
●
Client
:lesclientspeuventvisualiserlecatalogue,gérerleurscoordonnéesetacheterdesarticlesenligne.
IlestégalementpossibledementionnercommesystèmesexternesLaPostequipermetdegérerleslivraisonsetle
systèmebancairepourl
’
encaissementdesachatsparcarte.
Dansundiagrammedescasd
’
utilisation,lerectanglequienglobelescasreprésentelesystèmeétudié.Les
acteurssontreprésentésparuneicôneappeléestickmanetlescasd
’
utilisationsontreprésentésparune
formeovale.Lescasd
’
utilisationconcernentlesbesoinsdesutilisateurs.Lescasd
’
utilisationsontdonctrèssouvent
réaliséssurlabased
’
interviewsetd
’
entretiensaveclepersonnel.
- 1 -© ENI Editions - All rigths reserved
Lesacteurs"LaPoste"et"Banque"représententrespectivementleservicedelivraisonparlaposteetlepaiementen
ligne,associésaubondecommande(ex:n°decolissimoinséré).
Le diagramme des cas d
’
utilisation peut être lu comme ceci : Un employé peut gérer les articles du catalogue,
administrerlesclients,visualiserlescommandes.
Uninternautepeutcréeruncompteetconsulterlecatalogue.Unclientpeuts
’
authentifierpouraccéderàsoncompte.
Ilpeutgérersoncompteetacheterdesarticlesaprèsavoircomplétésonpanierpourcréerunbondecommande.Les
casprésentésdanslediagrammepeuventêtreaccompagnésd
’
untexteexplicatifaveclenomducasd
’
utilisation,un
résuméduservice,lesacteursimpliqués...
3.Maquettesdelaplateforme
Lesmaquettesd
’
écranfacilitentlacompréhensiondescasd
’
utilisation.Lesutilisateursserepèrentfacilementgrâceà
cesschémasvisuelsetpeuventainsivaliderleschoixd
’
analyse.
LesInternautesetlesclientsvisualisentlecontenuducatalogueàpartird
’
unnavigateur.Surlacolonnedegauche
quireprésentelemenu,sontaffichéeslescatégoriesd
’
articlesvendusparlasociétéBetaBoutique.Encliquantsurune
catégorie,levisiteurestredirigéversunepagequiaffichetouslesproduitsdelacatégoriesélectionnée.
a.Découpageutilisé
LaboutiqueBetaBoutiqueseradécoupéeselonlemodèlesuivantavecunen
tête,unmenudenavigationetunpied
depage.Lesfichiers.jspfsontdespagesJSPquisontutiliséespourdéfinirdesfragmentsdepagesetquisont
inclusesdansd
’
autrespages(commepourlesmenus,en
têtes...).
Lesfichiers.jspsontutiliséspourdesfichierssources
’’
complets
’’
etlesfichiers.jspfsontutiliséspourdes
fragmentsousegmentsdefichierssources(menu,formulairederecherche...).
- 2 - © ENI Editions - All rigths reserved
b.Cataloguedesarticles
Lapagedecataloguedesarticles,permetd
’
afficherlalistepaginéedesproduitsparcatégorie.Unrésultatpaginé
estundécoupagedesréponsesparpage(exemple5résultatsparpageet3pagespourafficher15articles).
- 3 -© ENI Editions - All rigths reserved
c.Fichearticle
Lafichearticlepermetd
’
afficherledétailduproduitaveclenom,lanote,ladescription,leprixetunboutonpour
ajoutercetarticleaupanier.
d.Rechercherunarticle
Lazonedesaisieprésenteenhautàdroitedusitepermetderechercherunarticleàpartirdesonnomoudesa
description.Lesvisiteurspeuventainsirechercherdesarticlesàpartird
’
unechaînedecaractères.Larecherchene
tientpascomptedelacasse(Majuscules/Minuscules).Siaucunarticlenecorrespondàlarecherche,unmessage
informatifestaffichéenconséquence.
- 4 - © ENI Editions - All rigths reserved
e.Authentification
Cettepagepermetauclientdeseconnecteretsedéconnecterdusystème.Leclientdoitavoircrééuncompte
auparavant. Il saisit alors son identifiant et son mot de passe. Si l
’
authentification est correcte, l
’
utilisateur est
redirigésurlapaged
’
accueiletunmessaged
’
invitationestalorsaffichésurlatotalitédespagesdusitejusqu
’
àla
prochainedéconnexion.Cemessagepermetd
’
afficherl
’
identifiantdel
’
utilisateuractuellementconnecté.Lorsquele
clientsedéconnecte,ilredevientInternaute/visiteursimple.
- 5 -© ENI Editions - All rigths reserved
f.Créeruncompte
Cettepagepermetauvisiteurdesecréeruncomptedanslesystèmeetdedevenirainsiunclient.Pourcréerun
compte,l
’
Internautedoitsaisirunidentifiantetunmotdepasseavecuneconfirmation.L
’
identifiantdoitêtreunique
danslesystème.Sicen
’
estpaslecas,l
’
Internautedoitêtreavertietdoitrecommencersasaisieavecunautre
identifiant.
Lesdeuxmotsdepasse(motdepasseetsaconfirmation)doiventêtreidentiques.
Si les informations sont correctes, le visiteur est alors invité à compléter ses informations personnelles (nom,
prénom,email,adresse...).
g.Gérerlecompteclient
Chaqueclientpeutconsulteretmettreàjoursesinformationspersonnellesauseindusystème.Pourcela,leclient
doit se connecter au système et cliquer sur le lien MON COMPTE (après authentification). La page affiche en
consultationsesdonnées.
- 6 - © ENI Editions - All rigths reserved
Leclientpeutpasserenmodeéditionencliquantsur
Modifier
.
h.Acheterdesarticles
Lesclients(visiteursauthentifiés)peuventacheterdesarticlesdanslesystème.Leclientvisualiselecataloguepar
catégoried
’
articlesouréaliseunerecherche.Lorsqu
’
ilestintéresséparunarticle,ilcliquesurlelienadaptépour
l
’
ajouter à son panier. Le client a ensuite la possibilité de modifier la quantité désirée pour chaque article ou
supprimerunouplusieursarticles.Leclientpeutvisualiseràtoutmomentlecontenudesonpanierpendanttoutela
- 7 -© ENI Editions - All rigths reserved
duréedesasession.Lorsquelecaddieestvide,unmessageinformatifestaffiché.
Lepanieraffichelalistedesarticlesaveclenom,laphoto,laquantité,leprixunitaireetlesous
total(prix*quantité).
Lemontanttotaldupanierestégalementaffichéenbasdupanier.Lorsqueleclientestsatisfaitetqu
’
ilsouhaite
validersacommande,ilpeutsaisirlesinformationsdesacartebancaireainsiquesonadressedelivraison.Unefois
touteslesdonnéesvalidées,unbondecommandeestcrééetlepanierélectroniqueestautomatiquementvidé.
i.Gérerlescommandes
Les employés de la société BetaBoutique peuvent visualiser et supprimer les commandes dans le système. Pour
chaquecommande,lesemployéspeuventvisualiserlecontenuetlesarticlesassociés.
j.Gérerlesarticles
LesemployésdelasociétéBetaBoutiquepeuventvisualiseretsupprimerlesarticlesdanslesystème.
- 8 - © ENI Editions - All rigths reserved
- 9 -© ENI Editions - All rigths reserved
PremièreServlet
1.Cycledevied
’
uneServlet
ChaqueServletdéployéesurunserveurd
’
applicationspossèdeuncycledeviebienprécis.
●LaclasseJavareprésentantlaServletestdéployéeauseinduconteneurWeb.
●LeconteneurWebvaensuitecréeruneinstancedelaclassedelaServletetlachargerenmémoire.
●Le conteneur Web passe ensuite à la phase d
’
initialisation de la Servlet en invoquant la méthode init
(ServletConfig)
. Cet objet permet par l
’
intermédiaire de l
’
objet
ServletConfig
de récupérer les paramètres
d
’
initialisationdéfinisdansledescripteurdedéploiementweb.xmldel
’
applicationWeb.
●LaServletpasseensuitedans unétatenserviceoude manièreplusprécise,enattentede réceptionde
requêtesclients.Leconteneurgèreensuiteunefiled
’
attentedeThreads.ChaqueThreadinvoquelaméthode
service(...)
delaServletquiexécuteralaméthodeHTTPadaptée(
doPost(...),doGet(...)
...).
●L
’
instancedelaServletrestechargéeenmémoiretantqueleconteneurWebs
’
exécute.Lorsdel
’
arrêtdu
conteneurWeb,laméthodedestroy()estinvoquéesurl
’
instancedelaServletpourindiquerqu
’
ellen
’
estplus
dansl
’
étatenservice.
●UneServletestunobjetJava.UneinstancenonutiliséeestdoncsuppriméedelamémoireparleGarbage
CollectorJava(déclenchementautomatiquedelaméthode
finalize()
).
2.Fonctionnementd
’
uneServlet(laclasseHttpServlet)
Lorsqu
’
unclient(généralementparl
’
intermédiaired
’
unnavigateurmaispasnécessairement)envoieunerequêteHTTP
au serveur, la méthode
service(...)
est invoquée sur la Servlet par le conteneur Web. La méthode reçoit deux
paramètres de type javax.servlet.ServletRequest et javax.servlet.ServletResponse qui permettent d
’
effectuer des
traitementssurlarequêteémiseparleclientetsurlaréponsequiserarenvoyée.
Unobjetdetypejavax.servlet.ServletRequestfournitdesméthodespermettantderécupéreroud
’
ajouterdesdonnées
danslarequêteainsiquedesméthodespourlesmétadonnéesdelarequête(infosclients,taille,typedecontenu,
protocole...).
Un objet de type javax.servlet.ServletResponse fournit des méthodes pour insérer des données dans la réponse
renvoyéeauclient.
L
’
interface javax.servlet.http.HttpServletRequest hérite de l
’
interface javax.servlet.ServletRequest et l
’
interface
- 1 -© ENI Editions - All rigths reserved
javax.servlet.http.HttpServletResponsehéritedel
’
interfacejavax.servlet.ServletResponse
.
Par défaut, la méthode
service(...)
invoque la méthode correspondant à la méthode HTTP utilisée par le client
(principalement
doGet(...)
et
doPost(...)
) en lui transmettant les paramètres de type
javax.servlet.http.HttpServletRequest et javax.servlet.http.HttpServletResponse. Le développeur n
’
a plus qu
’
à
implémenterlestraitementsauseindelaméthode
doXXX(...)
.
LorsqueleconteneurWebreçoitunerequête,ilanalysel
’
URI,lesen
têtes,lecorpsetstocketouteslesdonnéesdans
un objet implémentant l
’
interfacejavax.servlet.ServletRequest.Ilcréeensuite une instanced
’
unobjetimplémentant
l
’
interface javax.servlet.ServletResponse. Cet objet encapsule la réponse qui sera envoyée au client. Le conteneur
appelleensuiteuneméthodedelaclassedelaServlet,enluipassantlesobjetspourlarequêteetpourlaréponse.
LaServlettraitealorslarequêteetrenvoielaréponseauclientparl
’
intermédiaireduserveur.
a.Laméthodeservice()
L
’
interfaceServletdéfinitunnombrelimitédeméthodes.Lesméthodes
init()
etdestroy()negèrentpaslesrequêtes.
Laseuleméthodeconcernéeparcettetâcheest
service()
.LesHttpServletsontconçuespourrépondreauxrequêtes
HTTP.EllesdoiventdonctraiterdesrequêtesGET,POST,HEAD...Laméthode
doGet(...)
traitelesrequêtesdetype
GETetlaméthode
doPost(...)
lesrequêtesdetypePOST.Ilexisteautantdeméthodes
doXXX(...)
qu
’
ilexistedetype
derequêtesHTTP.
Lerôleduprogrammeurestdoncd
’
écrireuneimplémentationdelaclasseHttpServletenredéfinissantlesméthodes
dontilabesoin.Danslamajoritédescasils
’
agiradesméthodes
doGet(...)
et
doPost(...)
.
Pourrésumer,uneServletétendlaclasseHttpServletetredéfinitlaméthode
service()
pourtraiterlesrequêtesHTTP.
Toutefois,laclasseHttpServletimplémentedéjàlaméthode
service()
.Ilestdoncpréférabledenepaslefaireetde
redéfinir(définir)uniquement
doPost(...)
et
doGet(...)
.
Pourrappel,lorsqueleconteneurdeServletsreçoitdesrequêtesHTTP,ilassociechaquerequêteàuneServlet.Il
appelleensuiteautomatiquementlaméthode
service()
decelle
ci.Laméthode
service()
détermineletypederequête
HTTPetappellelaméthode
doXXX(...)
adaptée.Siparcontre,laServletredéfinitlaméthode
service(),
c
’
estcette
méthodequiseraexécutée.
Ilpeutparfoisêtreutilederedéfinirlaméthode
service()
delaclasseHttpServletdansnosServletssinousvoulons
effectuerunmêmetraitementpourdesrequêtesémisespardifférentesméthodesHTTP(GET,POST...).
b.Laméthodeinit()
L
’
interfacejavax.servlet.ServletfournitdesméthodesquicorrespondentaucycledeviedelaServlet :initialisation,en
service,destruction.
LorsquelaServletestchargéeenmémoiresuiteàuneinstanciationparleconteneurWeb,laméthode
init(...)
dela
Servletestexécutée.Pardéfaut,cetteméthodenefaitriendutout.Ilestcependantconseilléderedéfinircette
méthodedanslecodedesServletspourchargerdesressourcesutiliséesdanslerestedelaServlet(connexion
JDBC,ouvertured
’
unfichier...),oubienpourrécupérerdesparamètresd
’
initialisationrenseignésdansledescripteur
dedéploiement(fichierweb.xmldel
’
application)parl
’
intermédiairedel
’
objet
ServletConfig
passéenparamètredela
méthode.
c.Laméthodedestroy()
Lorsdel
’
arrêtduconteneurWeb,lesinstancesdeServletschargéesenmémoiresontdétruites(parleGarbage
Collector)maisavantlaréalisationdecetteétape,leconteneurexécutelaméthodedestroy()dechaqueServlet
chargée.
Pardéfaut,cetteméthodenefaitriendutout.IlestcependantconseilléderedéfinirdanslecodedeServletscette
méthodepourfermerproprementlesressourcesprécédemmentouvertes(connexionsJDBC,fichiers...).
3.Invocationd
’
uneServlet
LamanièrelaplusnaturelledecommuniqueravecuneServletestd
’
utiliserunclientWebetleprotocoleHTTP.La
méthode
doGet(...)
d
’
uneServletestinvoquéeprincipalementlorsquesonURLestsaisiedirectementdanslabarre
d
’
adressedunavigateuroulorsqu
’
ilyaunclicsurunlienhypertextequipointesurl
’
URLdelaServlet.
- 2 - © ENI Editions - All rigths reserved
Chaque méthode
doGet(...)
et
doPost(...)
prend deux paramètres. L
’
objet HttpServletRequest encapsule la requête
envoyéeauserveuretcontienttouteslesdonnéesdelarequête.L
’
objetHttpServletResponseencapsulelaréponseà
retournerauclient.
NousallonscréernospremièresServletsafindemontrerlefonctionnementdesméthodesévoquéesdansceguide.
Pourcela,sousEclipse,nousallonscréerunnouveauprojetTomcatsouslenom :betaboutique
.
Attention,avantdecréerleprojetbetaboutique,unrépertoiredecenomdoitêtrecréésurledisquedurdu
système.
Nousallonscréerauseindeceprojetunpaquetagenommé :betaboutique.servlets.exemples
.
- 3 -© ENI Editions - All rigths reserved
Danscepaquetage,nousajoutonsunenouvelleclasseJavanommée :MaServlet1.java
.
- 4 - © ENI Editions - All rigths reserved
Toutes ces opérations peuvent bien sûr être réalisées à la main en utilisant un éditeur de texte simple et en
enregistrantlefichierMaServlet1.javadanslerépertoireWEB
INF/src/duprojet.
LecontenudufichierMaServlet1.javaestlesuivant :
package betaboutique.servlets.exemples;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse res)throws
ServletException, IOException
{
res.setContentType("text/html");
PrintWriter out=res.getWriter();
out.println("<html><body>");
out.println("<h1>MaServlet1 en GET</h1>");
out.println("</body></html>");
out.flush();
out.close();
}
}
CetteServletgénèreunwarningTheserializableclassMaServlet1doesnotdeclareastaticfinalserialVersionUIDfieldof
- 5 -© ENI Editions - All rigths reserved
type long. En effet, une classe sérialisable (comme les JavaBeans) utilise un attribut serialVersionUID qui permet
d
’
affecterunnumérodeversionàlaclasse.Cenumérodoitêtrechangéetmisàjourlorsqueunchampsérialisable
(nontransient)estajoutéousupprimédelaclasse.C
’
estdoncaudéveloppeurdegérercenumérodeversion,maissi
cenuméroestabsent,lecompilateurgénèreraunnuméroautomatiquement.LechampserialVersionUIDestutilisélors
deladésérialisation,afindevérifierquelesversionsdesclassesJavasontcorrectes.Ilestdoncconseillédegérerle
champserialVersionUIDpourlesclassessérialisablesetdechangercettevaleurlorsd
’
unchangementsurleschamps
delaclasse.Pourdéfinirl
’
attributserialVersionUID,aprèsladéfinitiondelaclasse,lecodesuivantestutilisé :
private static final long serialVersionUID=1L;
Ce numéro (1L) n
’
a pas d
’
importance mais par convention, on utilise 1L, 2L... Sa valeur n
’
apasd
’
importance du
momentqu
’
elleestmiseàjourlorsdechangementssurleschampssérialisables.Java 5.0/6.0aintroduitdenombreux
warnings qui sont des avertissements mais qui n
’
empêchent pas l
’
exécution du code. Il est possible d
’
utiliser la
notationsuivante
@SuppressWarning
afindesupprimerunwarningspécifique.Ainsileswarningsdeversionpeuvent
êtresupprimésaveccettesyntaxe(attention,iln
’
yapasdepointvirguleenfind
’
instruction).
@SuppressWarnings("serial")
public class ServletAuthentification extends HttpServlet...}
Ilestégalementpossibledecacherunwarningsuruneméthodeavantladéclarationdecelle
ci,maisilestimportant
delimiteraumaximumlaportéedel
’
annotation
@SuppressWarnings
.Eneffet,devantuneméthode,laportéeconcerne
laméthodeelle
mêmemaisdevantuneclasse,elleconcernelatotalitédecettedernière.
CettepremièreServletestcompiléeautomatiquementparEclipse(Projet
Build Automatically
).Elleestdéployée
danslerépertoire :/WEB
INF/classes/betaboutique/servlets/exemples/MaServlet1.class
.
NouspouvonscréerunepageHTMLsimple(nomméemaservlet1.html)avecunlienhypertextepourdéclenchercette
Servlet.Lecodesuivantestplacéàlaracinedel
’
application(danslerépertoireduprojetbetaboutique
).
<html>
<head><title>MaServlet1</title></head>
<body>
<a href="maservlet1">Invoquer la Servlet MaServlet1</a>
</body>
</html>
a.CommenteffectuerlelienentrelaServletetl
’
URL?
Lefichierweb.xmlpermetdeparamétrerlesServletsdel
’
application.ChaqueServletdel
’
applicationdoitêtredécrite
danslefichierweb.xmletleliendelaServletavecl
’
URLyfigureégalement.
Ladéclarationd
’
uneServletsefaitdansl
’
élémentXML
<servlet>
:
●<servlet
name>estlenominternedelaServlet,ill
’
identifiedefaçonunique.
●<servlet
class>estlaclasseJavaassociéeàlaServlet(notreclasseJava).
Lelienentrel
’
URL/URIetlaServlets
’
effectuegrâceàl
’
élément<servlet
mapping>
:
●<servlet
name>estlenominternedonnéàlaServletquidoitêtreidentiqueàceluiindiquédansl
’
élément
<servlet>
.
●
<url
pattern>estl
’
URLpermettantdefairelelienaveclaServlet.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- servlets -->
<servlet>
<servlet -name>servletmaservlet1</servlet -name>
<servlet -class>betaboutique.servlets.exemples.MaServlet1
</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
- 6 - © ENI Editions - All rigths reserved
<servlet -name>servletmaservlet1</servlet -name>
<url -pattern>/maservlet1</url -pattern>
</servlet -mapping>
</web-app>
ÀchaqueServletdoitcorrespondreunélément
<servlet>
danslefichierdeconfigurationweb.xmlsinon,laServlet
n
’
existerapaspourleserveurd
’
applications.Lefichier/WEB
INF/web.xmln
’
estpascréépardéfautparEclipse.Nous
pouvons le créer à partir d
’
un ancien projet qui fonctionne en réalisant un simple copier/coller ou avec les
commandessuivantesd
’
Eclipse :clicdroitsurlerépertoire/WEB
INF
,
NewOtherXML
.
Désormais si nous tapons l
’
adresse suivante dans le navigateur :
http://localhost:8080/betaboutique/maservlet1.html, nous pouvons accéder à la page HTML qui va déclencher la
Servletparl
’
intermédiaired
’
unlien.NouspouvonségalementdéclencherdirectementlaServletàcetteadresse :
http://localhost:8080/betaboutique/maservlet1.
Ceci ne peut fonctionner que si le mapping de la Servlet avec l
’
URL a été correctement effectué dans le fichier
web.xml
.
b.Commentcelafonctionnet
il?
CefonctionnementesttrèspuissantetpermetainsidedéclencherdesServletssuivantdesmodèlesd
’
URL.Dans
l
’
exempleprécédantl
’
appelàuneURLdutype :http://localhost:8080/betaboutique/maservlet1déclenchelaServlet
denomservletmaservlet1quiestliéeaufichierMaServlet1.class
.
Ilestpossible,parexemple,demodifiercefonctionnementendéclenchantlaServletsuiteàl
’
appeld
’
une
’’
fausse
’’
pagenomméemaservlet1html.html
.
...
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletmaservlet1</servlet -name>
<url -pattern>/maservlet1html.html</url -pattern>
</servlet -mapping>
...
Attention,ilestimportantdebienrechargerleserveuraprèschaquemodificationdufichierdeconfiguration
web.xmlaveclemanagerdeTomcatparexemple :http://localhost:8080/manager/html/.
Ce mécanisme très puissant est utilisé par les serveurs du monde entier. Ce système peut être amélioré en
précisantquetouteslesURLquiseterminentparexemplepar.spsfdéclencherontnotreServlet.Cecipourraêtre
utilisépourunserviceparticulierdel
’
application(téléchargerlecatalogue,unefichearticleenPDF...).
...
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletmaservlet1</servlet -name>
<url -pattern>*.spsf</url -pattern>
</servlet -mapping>
...
- 7 -© ENI Editions - All rigths reserved
c.L
’
objetresponse
Dansl
’
exempledelaServletprécédente,l
’
objetresponsepermetd
’
envoyerdesdonnéesaunavigateurclient.Cet
objet permet d
’
envoyer la réponse au format TEXT, HTML, XML, sous forme de tableau d
’
octets... L
’
exemple
précédentutiliseunen
têtederéponseauformatHTMLresponse.setContentType(
’’
text/html
’’
)
.
Voicici
aprèsquatre
exemplesdifférents.
LepremierpermetderenvoyeraunavigateuruncontenuauformatHTML.
package betaboutique.servlets.exemples;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse
res)throws ServletException, IOException
{
res.setContentType("text/html");
PrintWriter out=res.getWriter();
out.println("<html><body>");
out.println("<h1>MaServlet1 en GET</h1>");
out.println("</body></html>");
out.flush();
out.close();
}
}
LesecondexemplepermetderenvoyeruncontenuauformatXML.
package betaboutique.servlets.exemples;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse
res)throws ServletException, IOException
{
res.setContentType("text/xml");
PrintWriter out=res.getWriter();
out.println("<?xml version=\"1.0\" encoding=\"ISO -8859-1\"?>");
out.println("<racine>");
out.println("<dvd1>Garde à vue</dvd1>");
out.println("</racine>");
- 8 - © ENI Editions - All rigths reserved
out.flush();
out.close();
}
}
Letroisièmepermetderenvoyeruncontenudynamiquesouslaformed
’
unfluxd
’
octets(uneimagedanscecas).
package betaboutique.servlets.exemples;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.ImageIcon;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse
res)throws ServletException, IOException
{
//acquerir l ’image si elle existe
Image image=null;
File source=new File("E:\\PROJETWEB\\betaboutique\\monimage.png");
if(source.exists())
{
//acquisition de l ’image
java.awt.Toolkit toolkit=java.awt.Toolkit.getDefaultToolkit();
image=toolkit.getImage("E:\\PROJETWEB\\
betaboutique\\monimage.png");
//transformer l ’image en BufferedImage
image=new ImageIcon(image).getImage();
BufferedImage bufferedImage=newBufferedImage(image.getWidth(null),
image.getHeight(null),BufferedImage.TYPE_INT_RGB);
Graphics2D g2=bufferedImage.createGraphics();
g2.drawImage(image,0,0,null);
g2.dispose();
//sortie de l ’image dans le navigateur
response.reset();
response.setContentType("image/png");
response.setHeader("Pragma","no -cache");
response.setHeader("Cache -Control","no -cache");
response.setDateHeader("Expires",0);
response.setHeader("Content -Disposition","filename=\"monimage\"");
ByteArrayOutputStream fluxout=new ByteArrayOutputStream();
try
{
ImageIO.write(bufferedImage,"png",fluxout);
fluxout.writeTo(response.getOutputStream());
fluxout.close();
}
catch(Exception e){}
finally
{
if(fluxout!=null) fluxout.close();
if(bufferedImage!=null)bufferedImage=null;
}
}
else{
System.out.println("Image non trouvée sur le disque dur");
}
}
}
- 9 -© ENI Editions - All rigths reserved
Enfin,ledernierexemplepermetdeforceruntéléchargementetd
’
envoyerunfichierduserveurversleclient.
package betaboutique.servlets.exemples;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
response.setContentType("application/download");
response.setHeader("Content -Disposition",
"attachment;filename=\"monimage.png\"");
ServletOutputStream out=response.getOutputStream();
File file=null;
BufferedInputStream from=null;
try
{
file=new File("E:\\PROJETWEB\\betaboutique\\monimage.png");
response.setContentLength((int) file.length());
int bufferSize=64 * 1024;
try
{
from=new BufferedInputStream(new FileInputStream(file),
bufferSize * 2);
byte[] bufferFile = new byte[bufferSize];
for (int i=0; ;i++)
{
int len=from.read(bufferFile);
if (len < 0) break;
out.write(bufferFile, 0, len);
}
out.flush();
}
finally
{
try {from.close();}catch (Exception e){}
try {out.close();}catch (Exception e){}
}
- 10 - © ENI Editions - All rigths reserved
}
catch (Exception e)
{
return;
}
}
}
d.L
’
objetrequest
L
’
objetrequestestégalementtrèspuissantetpermetdegérerlesdonnéesenvoyéesaveclarequêteàlaServlet.
Parexemplelesparamètresutilisateur(
getParameter(...)
)etlesparamètresàvaleursmultiplescommeleschamps
<select multiple...>
(
getParameterValues(...)) peuvent être récupérés. La méthode getParameterNames(...)retourne
une énumération des noms des paramètres de la requête. La méthode
getParameterMap(...)
retourne une
énumérationdetouslesparamètresstockésdansunobjetdetype
Map(collection)
.
e.LaméthodedoPost()
La méthode
doPost(...)
d
’
une Servlet est invoquée principalement lors de l
’
envoi de données saisies dans un
formulaireHTML(validationparunboutondetypesubmitparexemple).Pourrésumer,ledéveloppeurdoitcoderle
plussouventdeuxméthodesimportantesquisont
doGet(...)
et
doPost(...)
.
Ilestjudicieuxquel
’
unedecesméthodes(parexemple
doPost(...)
)appellel
’
autrepourquelaServletfonctionne
correctementaveclaplupartdescasd
’
utilisation.
package betaboutique.servlets.exemples;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MaServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
- 11 -© ENI Editions - All rigths reserved
{
PrintWriter out=response.getWriter();
out.println("Invocation de la MaServlet1 en GET ou POST");
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
- 12 - © ENI Editions - All rigths reserved
Servletauthentification
1.Gérerl
’
authentificationclient
Nousallonsmettreenapplicationl
’
utilisationdeServletspourleprojetBetaBoutique.Pourcela,nousallonscréerla
Servletetlespagesassociéespourleserviced
’
authentificationclient.Dansunpremiertemps,unseulclientpourra
s
’
authentifier par identifiant et mot de passe avec une vérification des données dans la Servlet. Pour cela, nous
commençonsparcréerunnouveaupaquetagebetaboutique.servlets.client
.
Sous Eclipse, il est facile d
’
insérer une Servlet sans écrire tout le code nécessaire pour les méthodes HTTP. Le
développeurdoitjustesurchargerlesméthodes
doGet(...)
et
doPost(...)
.
Nousfaisonsunclicdroitsurlepaquetageconcerné(betaboutique.servlets.client),noussélectionnonslesétapesNew
Other
.
Noussélectionnonsalorsl
’
ongletWebpuisServletafindecréernotrenouvelleServletdanslepaquetageadapté.
- 1 -© ENI Editions - All rigths reserved
NouspouvonségalementcréerunenouvelleServletsimplementenutilisantlesétapessuivantes :clicdroitsurle
paquetageconcerné,Newclass
.
Comme indiqué dans la maquette du projet BetaBoutique, pour l
’
authentification, l
’
utilisateur doit saisir son
identifiant/loginetsonmotdepasse.PourcelanousallonsutiliserunformulaireHTMLsimple(authentification.html)qui
vainvoquerlaServletd
’
authentification(ServletAuthentification
).
UnenouvellepageHTMLpeutêtreinséréeàlaracine,NewOtherWebHTML
.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification BetaBoutique</title>
</head>
<body>
<h1>Authentification - Client</h1>
<form action="authentificationclient" method="POST">
<table border="1" cellspacing="0" cellpadding="5">
- 2 - © ENI Editions - All rigths reserved
<tr>
<td>Identifiant/Login : </td>
<td><input type="text" name="identifiant" id="identifiant"
value="" size="20"/></td>
</tr>
<tr>
<td>Mot de passe : </td>
<td><input type="text" name="motdepasse" id="motdepasse"
value="" size="20"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit"
name="valider" id="valider" value="Valider"/></td>
</tr>
</table>
</form>
</body>
</html>
Désormais, il faut réaliser le mapping dans le fichier de configuration pour que l
’
appel
<form
action=
’’
authentificationclient
’’
...>
déclenchenotreServlet :ServletAuthentification
.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- servlets -->
<servlet>
<servlet -name>servletmaservlet1</servlet -name>
<servlet -class>betaboutique.servlets.exemples.MaServlet1
</servlet -class>
</servlet>
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletmaservlet1</servlet -name>
<url -pattern>/maservlet1</url -pattern>
</servlet -mapping>
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
</web-app>
AprèslerechargementdeTomcat,l
’
appeldel
’
URLauthentificationclient,déclencheral
’
actionservletauthentificationqui
estassociéeàlaServletbetaboutique.servlets.client.ServletAuthentification
.
Ilnenousrestedoncplusqu
’
àcréerlecodedelaServletelle
même.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//identifiant et mot de passe en dur
String ident="monidentifiant";
String mdp="monmotdepasse";
//récupération de l ’identifiant/login dans la requête
- 3 -© ENI Editions - All rigths reserved
String identifiant=request.getParameter("identifiant");
//récupération du mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//flux de sortie
PrintWriter out=response.getWriter();
//pas d ’identifiant
if(identifiant==null)
{
out.println("Authentification incorrecte !");
}
//pas de mot de passe
if(motdepasse==null)
{
out.println("Authentification incorrecte !");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
out.println("Authentification correcte,
bienvenue : "+identifiant);
}
else
{
out.println("Authentification incorrecte, mauvaise
saisie des informations !");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
a.Commentcelafonctionnet
il?
Lorsqu
’
uneServletestappeléepourlapremièrefois,c
’
estsaméthode
init()
quiestdéclenchée.C
’
estleseulcasoù
elleestappelée.SilaServletaétéappeléeparlaméthodeHTTPGET,laméthode
doGet(...)
delaServlettraitela
requêteduclient.SilaServletaétéappeléeparlaméthodeHTTPPOST,laméthode
doPost(...)
delaServlettraitela
requêteduclient.LaServletestdanscecaslaclassebetaboutique.servlets.client.ServletAuthentification.Siellen
’
est
pasdéjàchargée,ellelesera.Elleresteraalorsenmémoirepourlesfuturesrequêtes.
DansnotreServlet(etdanslaplupartdescas),laméthode
doPost(...)
renvoieàlaméthode
doGet(...)
lecontenu
entier(requêteetréponse).LeclientpourradoncenvoyerindifféremmentsesparamètresparunPOSTouunGET.
Pour s
’
en convaincre, nous pouvons déclencher la Servlet avec cette URL :
http://localhost:8080/betaboutique/authentificationclient?identifiant=monidentifiant&motdepasse=monmotdepasse
La méthode
doGet(...)
reçoit donc les deux paramètres d
’
authentification que sont identifiant et motdepasse. La
méthodegetParameter(param)del
’
objetrequestsertàrécupérerdanslarequêteduclientlavaleurduparamètrede
nomparam.Lecontenudeschampsestvérifié.S
’
ilsnesontpasàl
’
étatnull
,l
’
authentificationestvérifiéeparrapport
auxdonnéesinitialiséesdanslaServlet.
Danscetexemple,c
’
estunepagestatique(HTML)quidéclencheunepagedynamique(Java).Iln
’
existedoncaucun
moyend
’
indiqueràlapageHTMLdemanièredynamiquelerésultatdel
’
exécution.Eneffet,ilneserapossibleà
aucunmomentd
’
afficherlemessaged
’
erreur,desuccèsoumêmelessaisiesdel
’
utilisateurdanslapageHTML.Pour
évitercela,toutelapageHTMLpeutêtrecodéedanslaServlet.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
- 4 - © ENI Editions - All rigths reserved
{
//identifiant et mot de passe
String ident="monidentifiant";
String mdp="monmotdepasse";
//l ’identifiant/login est récupéré dans la requête
String identifiant=request.getParameter("identifiant");
//le mot de passe est récupéré dans la requête
String motdepasse=request.getParameter("motdepasse");
//flux de sortie
PrintWriter out=response.getWriter();
//envoi du formulaire
if(request.getParameter("valider")!=null)
{
//pas d ’identifiant
if(identifiant==null)
{
out.println("Authentification incorrecte !");
}
//pas de mot de passe
if(motdepasse==null)
{
out.println("Authentification incorrecte !");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
out.println("Authentification correcte,
bienvenue : "+identifiant);
}
else
{
out.println("Authentification incorrecte,
mauvaise saisie des informations !");
}
}
//générer le code HTML pour le formulaire
d’authentification
response.setContentType("text/html");
out.println(
"<html>"+
"<head>"+
"<meta http -equiv=\"Content -Type\" content=\"text/html;
charset=ISO -8859-1\">"+
"<title>Authentification BetaBoutique</title>"+
"</head>"+
"<body>"+
"<h1>Authentification - Client</h1>"+
"<form action=\"authentificationclient\" method=\"POST\">"+
"<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">"+
"<tr>"+
"<td>Identifiant/Login : </td>"+
"<td><input type=\"text\" name=\"identifiant\"
id=\"identifiant\" value=\""+identifiant+"\" size=\"20\"/></td>"+
"</tr>"+
"<tr>"+
"<td>Mot de passe : </td>"+
"<td><input type=\"text\" name=\"motdepasse\"
id=\"motdepasse\" value=\""+motdepasse+"\" size=\"20\"/></td>"+
"</tr>"+
"<tr>"+
"<td colspan=\"2\" align=\"center\">
<input type=\"submit\" name=\"valider\" id=\"valider\"
value=\"Valider\"/></td>"+
"</tr>"+
- 5 -© ENI Editions - All rigths reserved
"</table>"+
"</form>"+
"</body>"+
"</html>"
);
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Cette page est désormais totalement dynamique. Un message peut éventuellement être affiché en fonction de
l
’
authentification(échecousuccès)etlessaisiesdel
’
utilisateursontconservéesdansleschampsduformulaire.
Toutefois, nous remarquons immédiatement que la Servlet est mal adaptée pour générer du code HTML. Le
fonctionnementétaitbeaucoupplussimpleetclairavecunepageHTMLpourledéclenchementetuneServletpourla
logiqueapplicative.
Parlasuite,c
’
estlàqu
’
interviendrontlespagesJSP.
●Lespagesd
’
appelsetderéponsesserontdesdocumentsdynamiquesJSP.
●LalogiquedetraitementdesrequêtesseraassuréepardesServlets.
- 6 - © ENI Editions - All rigths reserved
InterfaceServletConfig
1.Présentation
L
’
objet de typejavax.servlet.ServletConfig représente les informations de configuration d
’
une Servlet au sein d
’
une
applicationWeb.
LeconteneurWebvacréerunobjetdetypejavax.servlet.ServletConfigpourchaqueélément
<servlet>
déclarédansle
fichierdeconfigurationdel
’
applicationweb.xml
.
PourdéclareruneServletdanslefichierdeconfigurationweb.xml,nousretrouvonslenomdelaServlet,laclasseJava
associée ainsi qu
’
une éventuelle/optionnelle liste de paramètres de la forme nom/valeur. Ces informations de
configurationpeuventensuiteêtrerécupéréesparlaServletdepréférencedanslaméthode
init(...)
.
Le fichier de configuration peut avoir zéro ou plusieurs éléments
<init
param>
par Servlet. Chaque élément
<init
param>
correspondàunparamètrereprésentéparunepairenom/valeuravec
<param
name>et
<param
value>
.
Nous allons modifier notre précédente Servlet pour définir l
’
identifiant et le mot de passe dans le fichier de
configurationplutôtquedanslaServletelle
même.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
</web-app>
2.Initialisationd
’
uneServlet
Aprèssonchargementenmémoire,leconteneurWebpasseenphased
’
initialisationdelaServletetlaméthodeinit
(...)
estinvoquée.Laméthode
init(...)
esttrèssouventsurchargéepourrécupérerdesinformationsdeconfiguration,
chargerdesressourcesutilesàlaServlet...L
’
interface
ServletConfig
permetensuitedemanipulerlesparamètresdu
fichier de configuration de l
’
applicationweb.xml. La méthodegetInitParameter(...)permetderécupérerlachaîne de
caractèresassociéeàunparamètredanslefichierdeconfiguration(oulavaleurnullsileparamètren
’
existepas).
La méthode getInitParameterNames() permet de récupérer sous la forme d
’
une énumération l
’
ensemble des
paramètresdéclarésdanslefichierdeconfiguration.
La méthode getServletName() permet de récupérer le nom de la Servlet déclarée au sein du descripteur de
déploiement.
L
’
exemplesuivantestuneaméliorationdelaServletd
’
authentificationafindelirel
’
identifiantetlemotdepassedans
lefichierdeconfigurationpourperfectionnerlamaintenabilitédel
’
ensemble.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
- 1 -© ENI Editions - All rigths reserved
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
public void init()
{
//récupération des paramètres d ’initialisation
de la Servlet dans le fichier web.xml
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupération de l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
//récupé ration du mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//flux de sortie
PrintWriter out=response.getWriter();
//pas d ’identifiant
if(identifiant==null)
{
out.println("Authentification incorrecte !");
}
//pas de mot de passe
if(motdepasse==null)
{
out.println("Authentification incorrecte !");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
out.println("Authentification correcte,
bienvenue : "+identifiant);
}
else
{
out.println("Authentification incorrecte, mauvaise
saisie des informations !");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
- 2 - © ENI Editions - All rigths reserved
InterfaceServletContext
1.Présentation
Laprécédenteinterface
ServletConfig
esttrèsintéressantepourgérerdesparamètresquisontpropresàuneServlet
commeunmotdepasse,uneclédecryptageouuncheminversunfichierdudisquedur.
Àl
’
inversedel
’
objetjavax.servlet.ServletConfigquireprésentelesinformationsdeconfigurationd
’
uneServletausein
d
’
uneapplicationWeb,unobjetdetypejavax.servlet.ServletContextreprésentelesinformationsdeconfigurationd
’
une
applicationWebtotale.ChaqueServletdelamêmeapplicationWebauradoncaccèsàcesparamètres.
Uneautrepropriététrèsimportantedel
’
objetjavax.servlet.ServletContextestqu
’
ilpeutégalementinteragiravecle
conteneurWebdel
’
application.Ilyauradoncunaccèsenlecturemaisaussiunautreaccèsenécrituredanslefichier
deconfiguration.Lebutétantalorsdecréer,lireetsupprimerdesattributsdefaçondynamique,cequipermetalorsle
partagederessourcesentreServletsd
’
unemêmeapplicationWeb.
2.Utilisation
Unobjetdetypejavax.servlet.ServletContextestobtenueninvoquantdirectementlaméthodegetServletContext()dans
lesméthodesdesServlets(objetservlet
).
DelamêmemanièrequeladéclarationspécifiquedeparamètreparServlet,ilestpossiblededéclarerdesparamètres
globaux pour toute l
’
application. Par exemple, il est possible de déclarer l
’
adresse email du Webmestre, de
l
’
administrateur,uneURLcomplète,uneadresseIPfixe,unrépertoire,unebasededonnées,lepiloteJDBC...Des
objetspourdespoolsdeconnexionsetdesobjetspartagéspeuventêtreaussidéclarés.
L
’
élément
<web
app>
,racinedufichierdeconfiguration web.xml,peutcontenirzéroouplusieurséléments<context
param>
qui permettent de déclarer des paramètres de l
’
application Web. Chaque élément <context
param>
correspondàunparamètrereprésentéparunepairenom/valeuravecleséléments
<param
name>et
<param
value>
.
PournotreprojetBetaboutique,nousallonsajouterl
’
emaildel
’
administrateuràcontacterlorsdel
’
authentification.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- paramètres globaux -->
<context -param>
<param -name>emailAdministrateur</param -name>
<param -value>admin@betaboutique.fr</param -value>
</context -param>
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
</servlet>
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
</web-app>
3.Récupérerdesparamètres
Pourrécupérer lesparamètres présentsdans le fichier de configuration, il existeplusieurs méthodes.La première
méthodegetInitParameter(param)permetderécupérerunechaînedecaractèrescontenantlavaleurd
’
unparamètre
- 1 -© ENI Editions - All rigths reserved
ounullsileparamètren
’
existepas.LasecondeméthodegetInitParameterNames()permetderetournerl
’
énumération
del
’
ensembledesparamètresdéclarésdanslefichierdeconfiguration.
La Servlet d
’
authentification est modifiée afin d
’
afficher l
’
email de l
’
administrateur du site en cas d
’
erreur. Il faut
remarquerl
’
utilisationdel
’
objetdetypejavax.servlet.ServletContext
.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
String emailadministrateur=null;
public void init()
{
//récupération des paramètres d ’initialisation
de la Servlet dans le fichier web.xml
config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
//récupérer l ’email de l ’administrateur
ServletContext servletContext=getServletContext();
emailadministrateur=servletContext.getInitParameter
("emailAdministrateur");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupération de l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
//récupération du mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//flux de sortie
PrintWriter out=response.getWriter();
//pas d ’identifiant
if(identifiant==null)
{
out.println("Authentification incorrecte !");
}
//pas de mot de passe
if(motdepasse==null)
{
.println("Authentification incorrecte !");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
out.println("Authentification correcte,
bienvenue : "+identifiant);
}
else
{
out.println("Authentification incorrecte, mauvaise
saisie des informations !");
out.println("Veuillez contacter : "+emailadministrateur);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
- 2 - © ENI Editions - All rigths reserved
}}
4.Ajouterdesparamètres
L
’
avantagedel
’
utilisationdel
’
interfaceServletContextestlapossibilitéd
’
ajouterdesattributsàlavoléedemanière
logicielle(enprogrammation).Lecontexteestaccessibleparl
’
ensembledesServlets/JSPdel
’
applicationWebetpeut
doncstocker,récupéreretdétruiredesattributs.Contrairementàl
’
interface
ServletConfig
,desobjetsdiverspeuvent
êtregérésetpasseulementdeschaînesdecaractères.
Ceprocédétrèsutilepermetparexemplededéclareraulancementdel
’
applicationunobjetpourlaconnexionàla
basededonnéesetdelestockeràlavoléedanslefichierdeconfigurationcommevariableglobale.Laconnexionsera
alorsdisponiblepourl
’
ensembledesclassesJavaEE(ServletsetJSP).Cesattributssontdespairesclé/valeur,laclé
étantunechaînedecaractèresetlavaleurétantunobjetden
’
importequeltype.
Lesattributsd
’
uneapplicationWebsontdoncdesvariablesglobalesappeléeségalementvariablesd
’
applicationdes
élémentsparticipantsàsonfonctionnementetquipeuventêtrepartagéessimultanémentparplusieursServletset
pagesJSP.
LaméthodesetAttribute(nom, objet)estutiliséepourpositionnerunattributquiseravisibledanstoutel
’
application
(portéeapplication).Silenomdel
’
attributexistedéjà,lavaleurexistanteestremplacéeparlanouvelle.
LaméthodegetAttribute(nom)estutiliséepourrécupérerlavaleurd
’
unattributdetypequelconquedansl
’
application
oulavaleurnullsil
’
attributn
’
existepas.
La méthode getAttributesNames() permet de récupérer le nom de tous les attributs actuellement stockés dans le
contextedel
’
applicationWeb.
Enfin,laméthoderemoveAttribute(nom)permetlasuppressiondel
’
attributindiquédanslecontextedel
’
application.
5.Miseenapplication
Afindemettreenapplicationl
’
interfaceServletContext,nousallonscréeruneclasseJavaBeanClientafindegérerun
objetclientsuiteàuneauthentificationcorrecte.L
’
objetclientseraalorsenregistrédanslecontextedel
’
applicationet
luparunesecondeServlet(ServletLectureAuthentification)
.
Pourcelanousallonscréerunnouveaupaquetageappelébetaboutique.javabean
.
Cepaquetage permet de stocker toutesles classes POJO(
Plain Old Java Object
,utilisé pour faireréférence à des
classessimplescomposéesd
’
accesseurs).
L
’
acronymePOJOestutilisépourfaireréférenceàlasimplicitéd
’
utilisationd
’
unobjetJavaencomparaison
aveclalourdeurd
’
utilisationd
’
uncomposantEJB(EntrepriseJavaBean).LesJavaBeans(ànepasconfondre
aveclesEJB)sontdescomposantslogicielssimplesréutilisablesetmanipulables.PourêtreuneclasseJavaBean,
celle
cidevrarespectercertainesconventionspoursonutilisation,saréutilisationetsaconnexionJavaBean:la
classedoitêtresérialisable(pourlessauvegardesetlectures),laclassedoitavoirunconstructeurpardéfaut(sans
argument), les propriétés des méthodes doivent être accessibles via des méthodes (accesseurs). La seule
différenceréelleentreunPOJOetunJavaBeanestlapossibilitédegérerdesévénementspourlesJavaBeans.
package betaboutique.javabean;
public class Client implements java.io.Serializable {
private String identifiant=null;
private String motdepasse=null;
//Constructeur par défaut (sans paramètre)
public Client() {
}
public String getIdentifiant() {
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
- 3 -© ENI Editions - All rigths reserved
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
}
Le nouveau code de la Servlet permet de sauvegarder l
’
objet client correctement authentifié dans le contexte de
l
’
application.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.javabean.Client;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
String emailadministrateur=null;
public void init()
{
//récupérer les paramètres d ’initialisation
de la Servlet dans le fichier web.xml
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
//récupérer l ’email de l ’administrateur
ServletContext servletContext=getServletContext();
emailadministrateur=servletContext.getInitParameter
("emailAdministrateur");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
out.println("Authentification correcte,
bienvenue : "+identifiant);
//créer l ’objet JavaBean Client
Client client1=new Client();
client1.setIdentifiant(identifiant);
client1.setMotdepasse(motdepasse);
//sauvegarder l ’objet client dans le contexte de l ’application
ServletContext servletContext=getServletContext();
servletContext.setAttribute("client1", client1);
}
...
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
- 4 - © ENI Editions - All rigths reserved
Après avoir appelé la Servlet ServletAuthentification par le biais de l
’
URL
http://localhost:8080/betaboutique/authentificationclient, nous pouvons déclencher la Servlet ci
dessous en
respectantlecodeetlaconfigurationdufichierweb.xml
.
Nousremarquonsalorsquenotreobjetclient1stockédanslavariableglobaleestaccessiblepourtoutel
’
application.
Lors du redémarrage (ou de l
’
arrêt) du serveur, le contexte sera détruit ainsi que les objets présents dans ce
contexte.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<context -param>
<param -name>emailAdministrateur</param -name>
<param -value>admin@betaboutique.fr</param -value>
</context -param>
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
</servlet>
<servlet>
<servlet -name>servletlectureauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.ServletLecture
Authentification</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
<servlet -mapping>
<servlet -name>servletlectureauthentification</servlet -name>
<url -pattern>/lectureauthentificationclient</url -pattern>
</servlet -mapping>
</web-app>
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.javabean.Client;
public class ServletLectureAuthentification extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//flux de sortie
PrintWriter out=response.getWriter();
//lecture de l ’objet dans le contexte de l ’application
ServletContext servletContext=getServletContext();
Client client1=(Client)servletContext.getAttribute("client1");
if(client1!=null)
{
- 5 -© ENI Editions - All rigths reserved
out.println("Le client dans le contexte est : ");
out.println("identifiant : "+client1.getIdentifiant());
out.println("mot de passe : "+client1.getMotdepasse());
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
- 6 - © ENI Editions - All rigths reserved
Traitementdesrequêtes
1.Présentation
Les méthodes
doGet(...)
et
doPost(...)
possèdent en premier paramètre un objet de type
javax.servlet.http.HttpServletRequest.CetobjetestcrééparleconteneurWebjusteavantledéclenchementd
’
une
desméthodesetcorrespondàlarequêteduclient.Ilexistedenombreusesméthodesdédiéesautraitementdes
requêtesutilisateurscommelarécupérationdeparamètres,lagestiondesflux...
a.Récupérerdesparamètrestransmisparleclient
LesparamètrespermettentàlaServletderéaliserlestraitementsadaptés.LaméthodegetParameter(nom)permet
derécupérersouslaformed
’
unechaînedecaractèreslavaleurd
’
unparamètrenommépasséenargument.Sile
paramètren
’
existepas,lavaleurnullestretournée.
...
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
...
LaméthodegetParameterNames()permetderécupérersouslaformed
’
uneénumérationl
’
ensembledesnomsdes
paramètrescontenusdanslarequête.
...
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
Enumeration enume=request.getParameterNames();
while(enume.hasMoreElements())
{
String nomParametre=(String)enume.nextElement();
String valeurParametre=request.getParameter(nomParametre);
out.println("parametre : nom="+nomParametre+" - valeur :
"+valeurParametre);
}
...
LaméthodegetParameterValues(nom)permetderécupérersouslaformed
’
untableaudechaînesdecaractèresun
ensemble de valeurs ou la valeur null si le paramètre n
’
existe pas. Cette méthode est utilisée pour les listes
<select>
multiplesoulescasesàcocher
<checkbox>
.Laméthode
getParameterMap()
permetderécupérersousla
formed
’
unobjetdetype
Map
l
’
ensembledesparamètrescontenusdanslarequête.
b.Gérerlesattributsducontextedelarequête
L
’
objet request permet de créer un attribut dans la requête. Si l
’
attribut existe déjà, la valeur existante est
remplacéeparlanouvelle.Cettetechniqueesttrèsutiliséepourenvoyerdesparamètresàunepagedynamique
pourparexemplel
’
affichagedeserreurs,desmessagesdesuccès,dunumérod
’
enregistrementdanslabasede
données... La méthode setAttribute(nom,objet) permet de créer un attribut dans le contexte de la requête. La
méthodegetAttribute(nom)permetderécupérerlavaleurd
’
unattributdanslecontextedelarequête.Laméthode
getAttributeNames()permetderécupérerlenomdetouslesattributsactuellementstockésdanslecontextedela
requêtesouslaformed
’
uneénumération.LaméthoderemoveAttribute(nom)permetdesupprimerunattributdu
contextedelarequête.
c.Récupérerdesinformationssurl
’
URLdelarequête
Ilexiste plusieurs méthodesqui permettent d
’
obtenirdes informationssur l
’
URLde larequête HTTPcomme : le
protocoleutilisé,lesparamètrespassés,lecheminverslaServlet...
LaméthodegetScheme()permetderetournerlenomduprotocoleutiliséHTTP,HTTPSouFTP.
Ex:http://localhost:8080/betaboutique/authentificationclientdanscecasretourne:http
- 1 -© ENI Editions - All rigths reserved
LaméthodegetContextePath()permetderetournersouslaformed
’
unechaînedecaractèreslapartiedel
’
URLqui
correspondaunomducontextedel
’
application.
Ex :http://localhost:8080/betaboutique/authentificationclientdanscecasretourne :/betaboutique
.
Laméthode
getMethod()
retournelenomdelaméthodeHTTPutilisée(GET,POST,DELETE...).
Ex:http://localhost:8080/betaboutique/authentificationclientdanscecasretourne:GETavecunlien.
LaméthodegetQueryString()retournelachaînederequêtecontenuedansl
’
URLaprèslechemindelaressource
invoquéeoulavaleurnulls
’
iln
’
enexistepas.
Ex : http://localhost:8080/betaboutique/authentificationclient?param1=1¶m2=2 dans ce cas retourne :
param1=1¶m2=2
LaméthodegetRequestUrl()retournelapartiedel
’
URLcontenueentrelenomduprotocole(httpdanscecas)etla
chaînederequête.
Ex : http://localhost:8080/betaboutique/authentificationclient?param1=1¶m2=2 dans ce cas retourne :
http://localhost:8080/betaboutique/authentificationclient.
d.Récupérerdesinformationssurleclient
IlexisteégalementplusieursméthodespourobtenirdesinformationssurleclientquiaémislarequêteHTTP.La
méthode
getRemoteAddr()
permetd
’
obtenirl
’
adresseIPduclient.LaméthodegetRemoteHost()permetd
’
obtenirle
nomcompletduclient.LaméthodegetRemoteUser()retournelenomdel
’
utilisateurquiaenvoyélarequête.
e.Récupérerdesinformationssurleserveur
LesdeuxméthodessuivantessonttrèsutiliséespourrenseignerdesURL
’’
endur
’’
;
danslecasderedirection,
d
’
invocationdeméthodesavecchemincomplet...Cesdeuxméthodespermettentdeconstruirelabasedel
’
URL
d
’
invocationdesressources.
LaméthodegetServerName()retournelenomd
’
hôteduserveur.
Ex : http://localhost:8080/betaboutique/authentificationclient?param1=1¶m2=2 dans ce cas retourne :
localhost.
Laméthode
getServerPort()
retournelenuméroduportd
’
écouteduserveur.
Ex:http://localhost:8080/betaboutique/authentificationclient?param1=1¶m2=2danscecasretourne:8080
- 2 - © ENI Editions - All rigths reserved
Traitementdesréponses
Le deuxième paramètre des méthodes
doGet(...)
et
doPost(...)
est un objet de type
javax.servlet.http.HttpServletResponse.CetobjetestcrééparleconteneurWebavantl
’
invocationdecesméthodeset
correspondàlaréponsequiseraretournéeauclient.
Ilestpossibled
’
indiquerletypeMIMEdelaréponseHTTPainsiquelatailledesdonnéescontenuesdanslecorpsdela
réponse.LaméthodesetContentType(type)permetdespécifierlecontenuducorpsdelaréponse(text/html
,
text/plain
,
text/xml
,
application/pdf
...).LaméthodesetContentLength(taille)permetdespécifierlatailleducontenudelaréponse
HTTP.
L
’
interfacejavax.servlet.ServletResponsedéfinitdeuxméthodesetobjetsquipermettentd
’
écriredanslecontenudela
réponse HTTP. La méthode getOutputStream() permet d
’
écrire des données au format binaire dans le corps de la
réponse.Ilestconseillédevaliderl
’
envoidelaréponseaveclaméthodeflush().Laméthode
getWriter()
permetd
’
écrire
desdonnéesauformattextedanslecorpsdelaréponse.CommepourlaméthodegetOutputStream(),ilestconseillé
d
’
utiliserlaméthodeflush()pourvaliderl
’
envoidelaréponse.
Ilestparfoisnécessaired
’
encoderlesURLafind
’
inclurel
’
identifiantdesessionpourlanavigationparétat(session),
pour conserver des accents sur les paramètres, pour l
’
encodage de la langue dans les paramètres... La méthode
encodeURL(url) permet d
’
encoder l
’
URL passée en paramètre en incluant l
’
identifiant de session. La méthode
encodeRedirectURL(url)
permet d
’
encoder l
’
URL passée en paramètre en incluant l
’
identifiant de session mais avec
l
’
utilisationdelaméthode
sendRedirect(...)
.
Latechniquesuivanteesttrèsutiliséeendéveloppementetpermetd
’
envoyeraunavigateurduclientunordrede
redirectionsuruneautreressourcedel
’
application(ounon).L
’
URLdelaressourceestpasséeenparamètreetpeut
être relative ou absolue. Nous allons utiliser cette méthode dans notre Servlet d
’
authentification afin de rediriger
l
’
utilisateursurlapageHTMLsuivanteencasdesuccès.
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>Authentification</title>
</head>
<body>
<h1>Utilisateur correctement authentifié</h1>
</body>
</html>
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
String emailadministrateur=null;
public void init()
{
//récupérer les paramètres d ’initialisation
de la Servlet dans le fichier web.xml
Typeettailleducontenudelaréponse
Gérerlefluxdesortie
EncoderlesURL
Redirectiond
’URLetétatsHTTP
- 1 -© ENI Editions - All rigths reserved
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
//récupérer l ’email de l ’administrateur
ServletContext servletContext=getServletContext();
emailadministrateur=servletContext.getInitParameter
("emailAdministrateur");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
//récupérer le mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//flux de sortie
PrintWriter out=response.getWriter();
//pas d ’identifiant
if(identifiant==null)
{
out.println("Authentification incorrecte !");
}
//pas de mot de passe
if(motdepasse==null)
{
out.println("Authentification incorrecte !");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//redirection vers la page de succès
response.sendRedirect("authentificationcorrecte.html");
}
else
{
out.println("Authentification incorrecte, mauvaise
saisie des informations !");
out.println("Veuillez contacter : "+emailadministrateur);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
LaméthodesendStatus(statut)permetd
’
appliqueruncoded
’
étatàlaréponseHTTPquandiln
’
yapasd
’
erreurcomme
parexempleOK(200),CONTINUE(100)...
Laméthode sendError(...)permetd
’
envoyerun coded
’
erreur HTTP au client comme par exemple NOT FOUND(404),
SERVICEUNVAILABLE(503)...
- 2 - © ENI Editions - All rigths reserved
Synchronisationdestraitements
UneServletfonctionnedansunenvironnementmultitâches.ÀchaquerequêtereçueparuneServlet,leconteneurWeb
vacréerunthreadquivaexécuterlaméthodedeservice(
doGet(...)
ou
doPost(...)
).
Ceprincipedefonctionnementpeutparfoisposerdesproblèmessilaméthodedeservicetravailleavecdesvariables
delaServlet.Chaquethreadpeutmodifierlavaleurdecesvariables.L
’
interface
javax.servlet.SingleThreadModel
permet
d
’
isolerlefonctionnementdechaquethread.LeconteneurWebprendenchargelefaitqu
’
uneinstancedelaServlet
nepeutêtreexécutéequeparunseulthreadàlafois.
Ex :déclarationd
’
uneServletquiimplémentel
’
interfaceSingleThreadModel
import javax.servlet.SingleThreadModel;
public class ServletAuthentification extends HttpServlet
implements SingleThreadModel{...}
Mêmesicettesolutionestfréquemmentrecommandée,c
’
estunmauvaisconseil.D
’
ailleurs,aveclecompilateurJDK6.0,
la méthode est
’’
barrée
’’
et donc notée comme dépréciée. L
’
interface
SingleThreadModel
ne fait que signaler au
conteneurqu
’
unseulthreaddoitêtreautorisépourlaServletàuninstantdonné.CecinegarantitpasquelaServlet
soit protégée contre les accès concurrents. Des variables statiques par exemple sont partagées par toutes les
instancesd
’
uneclasse.Uneraisonsupplémentairedenepasutiliserl
’
interface
SingleThreadModel
estquecetteoption
netientpaslacharge.LenombredeServletsquepeutcréerleconteneurestlimité.Ilestbienmoinscoûteuxdecréer
unnouveauthreadqu
’
unnouvelobjetpourlaServlet.
L
’
utilisationde lasynchronisation peutconvenir pourdes exemples simples mais dans une applicationréelle, cette
solutionestinefficace.Letempsderéponseaugmentealorsproportionnellementaveclenombrederequêtes.
La synchronisation des méthodes
doGet(...)
et
doPost(...)
est également une mauvaise idée. La méthode
service()
appelle systématiquement une de ces deux méthodes, le résultat est donc le même que dans le cas de la
synchronisationdelaméthode
service()
.
Pourrésumer,ilestparfoisnécessairedesynchroniserdesportionsdecode(commelorsd
’
utilisationdethreadsdans
desIHM)pourgérerlesaccèsconcurrents.Ilfautcependantveilleràcequelasynchronisationconcernelepluspetit
blocpossible(méthodesynchronized(object)).Laréductiondunombred
’
instructionssynchroniséesaugmentelavitesse
d
’
exécutionducode.
Il reste cependant un point délicat, l
’
accès concurrentiel aux données. Soit par exemple deux clients souhaitant
s
’
enregistrerparl
’
intermédiaired
’
unformulaireHTML.Ilsenvoientleursdonnéesenmêmetempsàdestinationd
’
une
Servletdontlerôleestletraitementdesdonnéesetl
’
enregistrementdanslatabledesclients.Quesepasse
t
ilsi
deuxinstancesdelaServleteffectuentlemêmetraitementenmêmetemps?
Danslecasd
’
uneclésansautoincrément,ilyaurauneerreurdedoublondel
’
idd
’
enregistrement.Unseuldesdeux
clientsseradoncenregistrédanslabasededonnéesdesclients.
Ceproblèmeseragéréparlasuiteparunmodetransactionneld
’
accèsauxdonnées(propreausystèmedestockage)
quiestpluspuissantquelasynchronisationdeServlet.
- 1 -© ENI Editions - All rigths reserved
Étatdesclients
LeprotocoleHTTPestsansétat.Latechnologieutiliseunmodedéconnecté(connexionpourlarequêteencourspuis
déconnexion).Chaquerequêteestconsidéréecommeprovenantd
’
unclientdifférentàchaquefois.
IlestimportantdansuneapplicationWebdepouvoirsuivrelanavigationduclientsurlesiteetdegardercertaines
informationsquipermettentdel
’
identifierdemanièreuniqueetfiable.
Pour notre projetbetaboutique,nousdevons pouvoirgarderles articlesdu client déposésdans sonpanier afin de
retrouversoncontenuàtoutmoment.Sansgestiondecookieet/ousession,toutclientquienvoieunerequêteest
considérécommeunnouveauclient.
1.Lescookies
Uncookieestunélémentdedonnéesquipeutêtreenvoyéparleserveuràdestinationdunavigateurclient.Le
cookieeststockésoitenmémoire(expirationrapide)soitsurlesystèmedefichiersduclient(expirationlongue).
L
’
avantagerésidedanslefaitqu
’
àchaqueconnexionduclient,lenavigateurtransmetégalementlecookie(oules
cookies)quenotreserveuraprécédemmentdéposé(s).Ilexistecependantuninconvénientmajeur.L
’
utilisateurpeut
configurer son navigateur pour refuser les cookies ou les supprimer directement manuellement. Un cookie est
composéd
’
unepairenom/valeuretdeplusieurspropriétéspourrenseignerledomaine,laduréedevie,lasécurité...
L
’
APIServletfournitlaclassejavax.servlet.http.CookiequipermetdemanipulerlescookiesenJava.Leconstructeur
Cookie(nom,valeur)
permetdecréeruncookie.LaméthodegetName()permetderécupérerlenomd
’
uncookie.
Laméthode
getValue()
permet de récupérerla valeur ducookie alors quela méthodesetValue(valeur) permet de
spécifierunevaleuràuncookie.
LesméthodesgetDomaine()etsetDomaine(valeur)permettentdegérerledomaineliéaucookie.
LesméthodesgetPath()etsetPath(valeur)permettentdegérerlecontextedel
’
applicationWeb.
Lesméthodes
getMaxAge()
etsetMaxAge(valeur)permettentdegérerladuréedevied
’
uncookie.
Lesméthodes
getSecure()
etsetSecure(valeur)permettentdegérerlasécuritéd
’
uncookie(httpouhttps
).
LesméthodesgetComment()etsetComment(valeur)permettentdegérerlescommentairesassociésàuncookie.
LesméthodesgetVersion()etsetVersion(valeur)permettentdegérerlesversionsdescookies.
a.Envoyerdescookiesdanslaréponse
NousallonsmodifiernotreServletd
’
authentificationafind
’
envoyeruncookieencasdesuccèsd
’
authentification.
...
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//envoyer un cookie au client
Cookie cookie1=new Cookie("cookie1","authentification correcte !");
response.addCookie(cookie1);
//redirection vers la page de succès
response.sendRedirect("authentificationcorrecte.html");
}
...
b.Récupérerdescookiesdanslarequête
Nous allons créer une nouvelle Servlet appelée ServletLectureCookies qui permet de lire les cookies. L
’
interface
javax.servlet.http.HttpServletRequestdéfinituneméthodequipermetderécupérersouslaformed
’
untableaulaliste
descookiespourundomaine.Cetteméthoderetournenullsiaucuncookien
’
aétéenvoyé.Nouspouvonsdonc
testerlaServletetlagestiondescookiesenréalisantuneauthentificationcorrecteetenappelantensuitelapage :
http://localhost:8080/betaboutique/lecturecookies.
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
- 1 -© ENI Editions - All rigths reserved
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletLectureCookies extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//lire les cookies du domaine
Cookie[] cookies=request.getCookies();
if(cookies!=null)
{
for(int i=0;i<cookies.length;i++)
{
Cookie cookie=cookies[i];
String nomCookie=cookie.getName();
String valeurCookie=cookie.getValue();
System.out.println("Cookie : nom="+nomCookie+" -
valeur="+valeurCookie);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
...
<servlet>
<servlet -name>servletlecturecookies</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
LectureCookies</servlet -class>
</servlet>
...
<servlet -mapping>
<servlet -name>servletlecturecookies</servlet -name>
<url -pattern>/lecturecookies</url -pattern>
</servlet -mapping>
...
Lecookieutilisédanscetexemplen
’
apasdeduréedevie,ilseradoncjusteaccessibleletempsdelasessionde
l
’
utilisateur(sifermetureetouvertureànouveaudunavigateurendéclenchantlaServletdelecturedescookies,le
cookieestperdu).
Pourutiliseruncookiedurable,ilfautpréciserladuréedecelui
ciensecondes.
...
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//envoyer un cookie au client
Cookie cookie1=new Cookie("cookie1","authentification correcte !");
//cookie d ’une journée
cookie1.setMaxAge(24*60*60);
response.addCookie(cookie1);
//redirection vers la page de succès
response.sendRedirect("authentificationcorrecte.html");
}
...
Sous Windows avec le navigateur InternetExplorer, les cookies sont stockés par défaut dans le répertoire
C:\Documents and Settings\UTILISATEUR\Cookies. Nous pouvons remarquer que suite à une authentification
correcte,uncookienommé :utilisateur@betaboutique[1]estalorscréé.
- 2 - © ENI Editions - All rigths reserved
c.Supprimeruncookie
Pour supprimer un cookie présent sur la machine du client, il faut lui envoyer un nouveau cookie avec les
paramètres suivants : nom identique, valeur vide, durée de vie égale à
1. Le cookie sera alors détruit
immédiatementaprèslafermeturedunavigateurcarconsidérécommeobsolète.
...
//envoyer un cookie au client
Cookie cookie1=new Cookie("cookie1","");
cookie.setMaxAge( -1);
response.addCookie(cookie1);
...
2.Lessessions
L
’
utilisationdesessionsHTTPestplussouplequelescookiesetoffredespossibilitésplusintéressantescarnous
pouvonsstockerdeschaînesdecaractèresmaisaussidesobjets.Unesessionpermetd
’
identifierunutilisateurtout
au long de sa navigation dans l
’
application Web. Une session est unique car elle est identifiée par un ID.
Contrairementaucookie,unesessionHTTPn
’
estpaspersistantepardéfaut.Chaquefoisquel
’
utilisateurvisitelesite
(ouvertureetfermeturedunavigateur),unenouvellesessionestcréée.Parcontre,ellepermetdeconserverdes
donnéescomplexesauseind
’
unfichierenregistréducôtéserveur.
Laduréedevied
’
unesessionestparamétrableauniveaudufichierdeconfigurationweb.xmlduserveurJava.En
règlegénérale,lavaleurpardéfautestde30minutes.C
’
est
à
direqu
’
auboutd
’
unedemi
heured
’
inactivitésurle
site,lasessionduclientestdétruite.
Letempsdesessionpeutêtreparamétrédanslefichierweb.xmldelafaçonsuivante :
...
<!-- définir le temps des sessions (temps en minutes) -->
<session -config>
<session -timeout>60</session -timeout>
</session -config>
...
Attention, pour rappel, il est important de bien respecter l
’
ordre des balises dans le fichier web.xml.Les
balisesrelativesauxsessionssontplacéesaprèslestags<servlet
mapping>
.
a.Obtenirunesession
L
’
interfacejavax.servlet.http.HttpServletRequestdéfinitdeuxméthodesquipermettentd
’
obtenirunesessionHTTP.
La méthode getSession() retourne la session courante et la méthode getSession(param) retourne une nouvelle
sessionsilarequêtenecontientpasdéjàdesession.
b.Travailleravecunesession
L
’
interface javax.servlet.http.HttpSession définit plusieurs méthodes pour manipuler les sessions. La méthode
setAttribute(nom,objet)permetdestockerunattributdanslecontextedelasessionHTTP.Silenomdel
’
attribut
existedéjà,lavaleurexistanteestremplacéeparlanouvelle.
LaméthodegetAttribute(nom)permetderécupérerdanslecontextedelasessionlavaleurd
’
unattributounullsi
l
’
attributn
’
existepas.LaméthoderemoveAttribute(nom)permetdesupprimerunattributdanslecontextedela
session.LaméthodeisNew()permetdesavoirsilasessionestnouvelleounon.Unesessionestnouvelletantqu
’
il
n
’
yapaseud
’
accès.Laméthode
invalidate()
permetdedétruireimmédiatementlasessioncouranteetl
’
ensemble
desesattributs.Laméthode
getId()
permetderetournerl
’
identifiantdelasessionHTTP.
NousallonsmodifiernotreServletd
’
authentificationpourajouterdanslasessionlesinformationsdel
’
utilisateuret
créerunenouvelleServlet(ServletLectureSession)pourlirelecontenudesinformationsdelasession.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//session
- 3 -© ENI Editions - All rigths reserved
HttpSession session=request.getSession();
//si pas de session, destruction et création d ’une nouvelle
if(!session.isNew())
{
session.invalidate();
session=request.getSession();
}
//stocker les paramètres de l ’utilisateur dans la session
session.setAttribute("identifiant", identifiant);
session.setAttribute("motdepasse", motdepasse);
//redirection vers la page de succès
response.sendRedirect("authentificationcorrecte.html");
}
...
...
<servlet>
<servlet -name>servletlecturesession</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
LectureSession</servlet -class>
</servlet>
<servlet -mapping>
<servlet -name>servletlecturesession</servlet -name>
<url -pattern>/lecturesession</url -pattern>
</servlet -mapping>
...
Nous pouvons désormais déclencher l
’
URL suivante après une authentification correcte afin de lire les données
enregistréesdanslasession.http://localhost:8080/betaboutique/lecturesession.
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class ServletLectureSession extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//lire les informations de la session
HttpSession session=request.getSession();
if(session==null)
{
System.out.println("Pas de session");
}
else
{
System.out.println("Identifiant :
"+session.getAttribute("identifiant"));
System.out.println("Mot de passe :
"+session.getAttribute("motdepasse"));
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
- 4 - © ENI Editions - All rigths reserved
c.Sessionsetréécritured
’
URL
Pardéfautlemécanismedesessionsutilisateursutiliseuncookiepouridentifierl
’
utilisateurcourant.Lorsqu
’
une
sessionestcrééesurleserveur,celui
cienvoiedanslaréponseHTTPuncookieavecl
’
IDdufichier(delasession)
créésurleserveur.Ensuite,àchaquerequêteenvoyéeparleclient,lecookieestinclus,permettantauserveurde
lierlasessionàunutilisateurprécis.
Sil
’
utilisateurinterditlescookies,lasessionnefonctionnerapascarleserveurnepourrapasrécupérerl
’
IDdela
sessioncourante.Ilfautdoncutiliserunealternativeaucookie :laréécritured
’
URL.
Latechniquederéécritured
’
URLn
’
estpastoujoursutilisée.Ceprocédéestlourdàgéreretimposedu
travailsupplémentaire auxdéveloppeurs. Dans lamajorité desprojets, seulela gestionde session par
cookieestutilisée.
Leprincipederéécritureconsisteàajouterunparamètredontlavaleurcorrespondàl
’
IDdesessionHTTPduclient
surchaqueURL(lesformulairesHTML,leslienshypertextes,lesredirections).Pourcelailestnécessaired
’
utiliserles
fonctions d
’
encodage des URL. L
’
interface javax.servlet.http.HttpServlet.Response définit deux méthodes qui
permettentd
’
encoderdesURLafinqueleserveurpuisselesécrirecorrectement.LaméthodeencodeURL(url)permet
d
’
encoder l
’
URL passée en paramètre en incluant l
’
identifiant de session si cela est nécessaire. Cette méthode
permetd
’
ajouterl
’
identifiantdesessionaucasoùlenavigateurnesupportepasoun
’
autorisepaslescookies.La
méthode
encodeRedirectURL(url)
est identique et permet la mise en forme lors de redirections avec la méthode
sendRedirect(...)
.
NousallonsmodifiernotreServletd
’
authentificationafinderéaliserunaffichageHTMLavecunlienverslaServletde
lectureenconservantl
’
IDdesessiondansl
’
URL.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//session
HttpSession session=request.getSession();
//si pas de session, destruction et création d ’une nouvelle
if(!session.isNew())
{
session.invalidate();
session=request.getSession();
}
//stocker les paramètres de l ’utilisateur dans la session
session.setAttribute("identifiant", identifiant);
session.setAttribute("motdepasse", motdepasse);
//lien HTML avec ID session
response.setContentType("text/html");
out.println("<h1><a href=\""+response.encodeURL("lecturesession")
+"\">Lire le contenu de la session</a></h1>");
}
...
Le serveur d
’
application détecte automatiquement si le navigateur accepte ou non les cookies. Si le navigateur
autorise les cookies, le lien ne sera pas encodé. Par contre, si les cookies sont désactivés (avec la barre de
développement Firefox par exemple), le lien est encodé avec l
’
ID de la session.
http://localhost:8080/betaboutique/lecturesession;jsessionid=0C7DD398A9367555D0D4A15F32B5
- 5 -© ENI Editions - All rigths reserved
Lesfiltres
1.Présentation
Lesfiltrespermettentdedonneràuneapplicationunestructuremodulaire.Ilspermettentd
’
encapsulerdifférentes
tâchesquipeuventêtreindispensablespourtraiterdesrequêtes.Laprincipalefonctiond
’
uneServletestderecevoir
lesrequêtesetderépondreauxclientsconcernés.Parcontre,ilesttrèssouventnécessairederéaliserunefonction
identiquepourchaqueServletenrapportaveclesrequêtesetréponsesHTTP.
Parexemple,nousvoulonsstockerdansunebasededonnéespourdesstatistiqueschaqueaccèsàdesServletsdu
serveurourouter(transporter)unparamètredanstouteslesrequêtessansêtreobligéd
’
écrirelecodepourchaque
Servlet.L
’
interfaceFilterapparueavecl
’
APIServlet2.3permetderésoudrecetypedeproblème.
Lesfiltrespermettentainsidetraiter :
●Lesrequêtesquiviennentdesclientsavantqu
’
ellesnesoienttraitéesparlesServlets.
●LesréponsesvenantdesServletsavantqu
’
ellesnesoientenvoyéesauxclients.
Ilestpossibleparexemplede :
●décrypterdesrequêtesenvoyéesauxServlets,traiterlesdonnéesaveclesServletsetcrypterlesréponses
pourlesclients;
●gérerl
’
authentificationdesclients;
●convertirdesformatsd
’
images,appliquerdestransformationsXSLTsurdesdonnéesXML...
2.Utilisation
Pour utiliser un filtre il est nécessaire de réaliser deux opérations. La première consiste à écrire une classe qui
implémente l
’
interface Filter. La seconde consiste à modifier le descripteur de déploiement (fichier web.xml) de
l
’
applicationpourindiquerauconteneurd
’
utiliserlefiltre.
Nousallonsutiliserunfiltrepourtracernosactionsauseindenotreapplicationbetaboutique
.
Lorsqu
’
unfiltreestcréé,leconteneurappellesaméthode
init(...)
.Danscetteméthode,nouspouvonsaccéderaux
paramètres d
’
initialisation avec l
’
interface
FilterConfig
. Lors du traitement de la requête, le conteneur appelle la
méthode
doFilter(...)
.Avantdedétruirelefiltre,leconteneurappellesaméthode
destroy(...)
.
Lorsquelefiltreappelle
chain.doFilter()
,lefiltresuivantdanslachaîneestexécuté.Lecodeplacéavant
chain.doFilter()
estexécutéavantletraitementdelaServlet.Toutemodificationquelefiltredoitapporteravantl
’
exécutiondela
requêtedoitêtreeffectuéeavantcetappel.Lecodeplacéaprèscetappelestexécutéaprèsle traitement de la
Servlet.Ilesttoutàfaitpossibledechaînerlesfiltresetd
’
utiliserunfiltrepartraitementspécifique.
Leschémasuivantprésentelefonctionnementd
’
unfiltreavantetaprèstraitementparlaServletinvoquée.
- 1 -© ENI Editions - All rigths reserved
a.Ladéclarationdufiltre
Ledescripteurdedéploiementestutilisépourindiquerle(oules)filtre(s)qu
’
ildoitappelerpourchaqueServletou
URLdel
’
application.Lepremierélément
<filter>
permetdedéclarerlaclasseassociéeaufiltre.Cetélémentdoitêtre
placéendébutdefichierdeconfigurationaprèsladéclarationdesvariablesglobalesaucontexte(<context
param>
).
Dansnotrecas,nousdéfinissonsunfiltrequiseraassociéàlaclasseFiltreJournalisationpermettantdegérerles
accèsauxpagesdusite.
...
<!-- definition du filtre -->
<filter>
<filter -name>filtrejournalisation</filter -name>
<filter -class>betaboutique.boiteoutils.FiltreJournalisation</filter -class>
</filter>
...
Le second élément nécessaire est
<filter
mapping>
. Il permet comme pour les Servlets de gérer le mapping
(relations)entreunnometuneServletouuneURL.Parexemple,icilefiltreestappliquéuniquementàlaServlet
servletauthentification
.
...
<filter>
<filter -name>filtrejournalisation</filter -name>
<filter -class>betaboutique.boiteoutils.FiltreJournalisation
</filter -class>
<filter-mapping>
<filter -name>filtrejournalisation</filter -name>
<servlet -name>servletauthentification</servlet -name>
</filter -mapping>
...
Pourcesecondexemple,lefiltreestappliquépourl
’
accèsàtouteslesURLdel
’
application.
...
<filter>
<filter -name>filtrejournalisation </filter -name>
<filter -class>betaboutique.boiteoutils.FiltreJournalisation
</filter -class>
<filter-mapping>
<filter -name>filtrejournalisation</filter -name>
<url -pattern>/*</url -pattern>
</filter -mapping>
...
Lecodesuivantcorrespondaufiltrebetaboutique.boiteoutils.FiltreJournalisationetpermetd
’
afficherlesinformations
de journalisation avant et après exécution des pages. Nous pouvons tester le fonctionnement avec l
’
accès à
n
’
importequelleURLdel
’
applicationétantdonnéqueleparamètre
<url
pattern>estpositionnépourtoutécouter
(/*).
- 2 - © ENI Editions - All rigths reserved
package betaboutique.boiteoutils;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class FiltreJournalisation implements Filter{
private FilterConfig filterConfig=null;
// action d ’initialisation du filtre
public void init(FilterConfig filterConfig)
{
this.filterConfig=filterConfig;
System.out.println("Initialisation du filtre :
"+this.filterConfig.getFilterName());
}
//action déclenchée lors du traitement d ’une requete
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
{
//traitement de la requête avant la Servlet
System.out.println(" ----------- REQUETE ----------- ");
System.out.println("Encodage : "+request.getCharacterEncoding());
System.out.println("Type de contenu : "+request.getContentType());
System.out.println("Taille du contenu : "+request.getContentLength());
System.out.println("Nom machine distante : "+request.getRemoteHost());
System.out.println("Adresse IP machine distante :
"+request.getRemoteAddr());
try
{
//déclencher la servlet appropriée
chain.doFilter(request, response);
}
catch(Exception e)
{
//erreur dans le filtre
System.out.println("Erreur dans le filtre FiltreJournalisation");
}
//traitement de la requête avant la Servlet
System.out.println(" ----------- REPONSE ----------- ");
System.out.println("Encodage : "+response.getCharacterEncoding());
System.out.println("Type de contenu : "+response.getContentType());
//exemple d ’écriture dans le fichier de log de l ’application
ServletContext context=this.filterConfig.getServletContext();
context.log("Encodage de la réponse :
"+response.getCharacterEncoding());
}
//action qui permet de détruire le filtre
public void destroy()
{
System.out.println("Destruction du filtre :
"+this.filterConfig.getFilterName());
}
//fin de la classe
}
- 3 -© ENI Editions - All rigths reserved
- 4 - © ENI Editions - All rigths reserved
InterfaceRequestDispatcher
1.Présentation
Laplupartdutemps,laprogrammationdeServletsconsisteàrécupérerdesdonnéesenparamètre,àtraiterces
donnéesetàrenvoyeruneréponseadaptéeauclient.
Nousavonsutiliséprécédemmentlaméthode
sendRedirect(...)
del
’
objetresponsequipermetderedirigerl
’
utilisateur
versunepageprécise.Parcontre,cetteredirectionnepermetpasd
’
envoyerdesdonnéesdirectementdanslecorps
dumessage(saufenpassantdesparamètresàl
’
URL.Ex :response.sendRedirect(
’’
mapage.jsp?param=1
’’
)
).
Un objet de type javax.servlet.RequestDispatcher est créé par le conteneur Web pour chaque application et agit
commeunrouteurderequêtesetréponsesHTTP.Unobjetdetypejavax.servlet.RequestDispatcherestdoncutilisé
pour :
●effectueruntraitementcontinuversuneautreressourceWeb;
●inclurelecontenud
’
uneautreressourceWebdanslaréponseHTTP.
Cetteinterfaceestlabasedel
’
architectureMVC(Modèle,Vue,Contrôleur)quenousverronsplustard.Lasyntaxede
laméthodegetRequestDispatcher(chemin)doitcommencerparunsigne/quipermetdefaireréférenceaucontextede
l
’
application Web courante. Ce chemin est complété ensuite vers la ressource statique (HTML, XML, PDF...) ou
dynamique (page JSP, une autre Servlet...). Une fois l
’
objetjavax.servlet.RequestDispatcher obtenu, il est possible
d
’
invoquer la méthode qui permet de déléguer le traitement de la requête HTTP à la ressource ou la méthode
permettantd
’
inclureducontenudanslaréponseHTTP.
a.Déléguerettransmettre
LadélégationconsisteàtransmettrelarequêteHTTPcouranteàuneautreressourceWebquiserachargéedeson
traitementetquipeutégalementtransmettrelarequêteàuneautreressource.Pourcela,laméthode
forward(...)
permetdetransmettrelecontenucompletdelarequête(doncaveclesparamètres)versuneautrepage.
b.Incluredesdonnées
L
’
inclusionconsisteàincorporerlecontenud
’
uneautreressourceWebdanslaréponseHTTPquiseraenvoyéeau
client.Cetteinclusionpeutêtreutiliséepourl
’
en
têteoulepieddepagedusiteenHTMLparexemple(ouenJSPs
’
il
yaducontenudynamique).Pourcela,laméthode
include(...)
permetderéalisercetteopération.
2.Utilisation
Nous allons modifier notre application d
’
authentification pour le projet betaboutique. Dans un premier temps
l
’
utilisateur saisit son identifiant et son mot de passe dans une page HTML simple (authentification.html
). Si
l
’
authentificationestincorrecteous
’
ilyaunproblèmedesaisie,l
’
utilisateurestredirigéversunepagedynamique
d
’
erreurenJSP(lelangageJSPseraprésentéauchapitresuivant).Sil
’
authentificationestcorrecte,l
’
utilisateurest
redirigéversunepagedynamiqueJSPluiindiquantlabienvenuesurlaboutique.
a.Explications
L
’
applicationproposéeestpluscomplexequeprécédemment.LapageHTMLstatiqueauthentification.htmlpermet
dedéclencherlaServletServletAuthentificationparlebiaisdel
’
URLauthentificationclient
.
CetteServletvérifielasyntaxedel
’
identifiantetdumotdepassedefaçonpluspréciseaveclavaleurnull,letest
de présence et une expression régulière pour la syntaxe. En cas d
’
erreur, une collection est renseignée puis
retournéeverslapageJSPdynamiquequiaffichesimplementlecontenudecettecollectioncomprenantsousforme
dechaînesdecaractèreslalistedeserreursrencontrées.
Ledéclenchementdelafonction
forward(...)
del
’
objetjavax.servlet.RequestDispatcherentraîneuneredirection(un
return)etdonclecodesuivantcetteinstructionn
’
estpasexécuté.
Encasdesuccèsdel
’
authentification,lesattributsidentifiantetmotdepassesontpassésàlarequêtequidéclenche
leroutageverslapagedynamiquedebienvenue(bienvenue.jsp).Celle
civaafficherlesinformationsduclient.La
Servletdéposedoncdeuxparamètresdanslarequêteàl
’
aidedelaméthodesetAttribute(...)etpasselecontrôleà
- 1 -© ENI Editions - All rigths reserved
la pagebienvenue.jsp. La page dynamique bienvenue.jsp récupère les attributs dont elle a besoin pour afficher
l
’
identifiantetlemotdepassedel
’
utilisateurcorrectementauthentifié.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- paramètres globaux -->
<context -param>
<param -name>emailAdministrateur</param -name>
<param -value>admin@betaboutique.fr</param -value>
</context -param>
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
</web-app>
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
public void init()
{
//récupérer les paramètres d ’initialisation
de la Servlet dans le fichier web.xml
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
//récupérer le mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//déclaration d ’une liste/collection d ’erreurs
ArrayList<String> erreursParametres=new ArrayList<String>();
- 2 - © ENI Editions - All rigths reserved
//pas d ’identifiant
if(identifiant==null)
{
erreursParametres.add("Le paramètre [identifiant] est null");
}
//identifiant vide
if(identifiant.equals(""))
{
erreursParametres.add("Le paramètre [identifiant] est vide");
}
//identifiant inférieur à 5 caractères
if(!identifiant.matches("^[0 -9a-zA-Z]{5,}$"))
{
erreursParametres.add("Le paramètre [identifiant]
doit avoir au moins 5 caractères");
}
//pas de mot de passe
if(motdepasse==null)
{
erreursParametres.add("Le paramètre [mot de passe] est null");
}
//mot de passe vide
if(motdepasse.equals(""))
{
erreursParametres.add("Le paramètre [mot de passe] est vide");
}
//motdepasse inférieur à 5 caractères
if(!motdepasse.matches("^[0 -9a-zA-Z]{5,}$"))
{
erreursParametres.add("Le paramètre [mot de passe]
doit avoir au moins 5 caractères");
}
//en cas d ’erreur, redirection avec le RequestDispatcher
vers la page d ’erreur dynamique
if(erreursParametres.size()>0)
{
request.setAttribute("erreurs",erreursParametres);
getServletContext().getRequestDispatcher
("/erreurs.jsp").forward(request, response);
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
//injecter dans la requête nos paramètres pour
qu’elle puisse les traiter
request.setAttribute("identifiant",identifiant);
request.setAttribute("motdepasse",motdepasse);
//authentification correcte, redirection
vers la page de bienvenue
getServletContext().getRequestDispatcher
("/bienvenue.jsp").forward(request, response);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
- 3 -© ENI Editions - All rigths reserved
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>ERREURS</title>
</head>
<body>
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+(String)erreurs.get(i)+"</li>");
}
%>
</ul>
</body>
</html>
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE</title>
</head>
<body>
<%
String identifiant=(String)request.getAttribute("identifiant");
if(identifiant==null)
{
identifiant="inconnu";
}
String motdepasse=(String)request.getAttribute("motdepasse");
if(motdepasse==null)
{
motdepasse="inconnu";
}
%>
<h2>Bienvenue client : <%= identifiant %> <%= motdepasse %></h2>
</body>
</html>
- 4 - © ENI Editions - All rigths reserved
Lefonctionnementestplusclairdésormais.UneServletcontientlalogiqueapplicativepourletraitementetdélègue
àuneouplusieurspagesdynamiques(JSP,PHP...)lamiseenformedesdonnées(police,images,styles).Ceciestla
basedelaprogrammationMVC(ModèleVueContrôleur)quireposesurleDesignPatternoumodèleMVC.
- 5 -© ENI Editions - All rigths reserved
IntroductionaumodèleMVC
1.Présentation
Danslesexemplesprécédentsduprojetbetaboutique,lesrequêtesHTTPsontgéréespardescomposantsWebqui
reçoiventlesrequêtes,créentlesréponsesetlesretournentauxclients.Ilyadoncunseulcomposantresponsable
delalogiqued
’
affichage,delalogiquemétieretdelalogiquedepersistance.Ilexisteuneautrearchitectureappelée
MVCouModèleVueContrôleurquipermetdeséparerclairementlestroisactivitésdescomposantsimpliqués.
Dansl
’
architectureprécédente,l
’
affichageetlamanipulationdesdonnéessontmélangésdansunseulcomposant
Servlet.Celapeutlargementconvenirpourunservicespécifique,nonévolutifetsimple,maisceladevientunproblème
quandlesystèmesedéveloppe.CettearchitectureconduitàplacerducodeJavaetducodeHTMLdanslesServlets
ouJSP.
Ilexisteplusieurssolutionsàceproblème.Laplussimplecorrespondàl
’
apparitiondespagesJSPetconsisteàcréer
desfichiersd
’
en
tête,depieddepage,detraitement...etd
’
inclureletoutdansunepagegénérale.
L
’
architectureMVCséparelalogiquemétierdel
’
affichage.Danscemodèle,uncomposantestchargéderecevoirles
requêtes(Servlets),unautretraitelesdonnées(Classes)etuntroisièmegèrel
’
affichage(JSP).Sil
’
interfaçageentre
cestroiscomposantsestclairementdéfini,ildevientplussimpledemodifieruncomposantsanstoucherauxdeux
autres.
●
Modèle
:lemodèleenglobelalogiquemétieretlesdonnéessurlesquellesilopère.TouteclasseJavaqui
manipulelesdonnéespeutjouerlerôledumodèleetdenombreusesapplicationsWebutilisentuniquement
desServletsordinaires(oudesclassesavecJDBCpourmanipulerlesbasesdedonnées).
●
Vue
:dèsquelarequêtecouranteesttraitée,lalogiquedeprésentationestréaliséeparunevuespécifique.
●
Contrôleur
:lecomposant(oulescomposants)contrôleurreçoi(ven)tlesrequêtesdesclients,lestraite(nt)et
lestransmet(tent)auxcomposantschargésdetraiterlesdonnées.LesServletssontlescomposantsdontla
structureestlaplusadaptée.UneServletestconçuepourrecevoirlesrequêtesdesclientsetleurretourner
uneréponse,cequiestprécisémentlerôleducontrôleur.
Dansuneapplicationcommeleprojetbetaboutique,lalogiqueenMVCestlasuivante :
Leclientémetdesrequêtesauserveur.ChaqueactionprécisecorrespondàuneServletquiredirigelesrequêtesvers
unepageJSPadéquate,ouréaliseuntraitement,ouaccèdeàdesdonnéesetdanscecas,déclencheuneautre
Servletquiserachargéederépondreàlademandedel
’
utilisateurcourant.
LeschémasuivantprésenteunestructuredetypeMVCavecl
’
utilisationdeServletsetpagesJSP.
Pourlemoment,nousn
’
utilisonspasdepartiemodèlecarnousnemanipulonspasdedonnéespersistantescomme
desenregistrementsd
’
unebasededonnéesoudesfichiers.
NotremodèleMVCseradonclimitéauxparties :clients,Contrôleur/ActionaveclesServletsetAffichage/Vueavecles
JSP.
LesServletsquijouentlerôledecontrôleurdansuneapplicationMVCdoiventdisposerd
’
unmoyenpourtransmettre
lesrequêtesauxcomposantschargésdel
’
affichage.Cemoyenestfourniparl
’
objetRequestDispatcher.Cecomposant
permetdefairesuivreunerequêted
’
uncomposantversunautre.
UnobjetRequestDispatcherpeutêtreobtenuaveclaméthodegetServletContext().Àpartirdecetobjet,ilestpossible
d
’
obtenirunRequestDispatcheràl
’
aidedesméthodessuivantes :getNamedDispatcher(nom)ougetRequestDispatcher
(chemin)
.
LaméthodegetRequestDispatcher(...)fonctionneavecuncheminquicommenceparlabarreobliqueetquiestrelatifau
contextedel
’
application. La méthodegetNamedDispatcher(...)correspondetdoitêtre identiqueàunsous
élément
- 1 -© ENI Editions - All rigths reserved
<servlet
name>d
’
unélément<servlet
mapping>
dudescripteurdedéploiementweb.xml
.
2.Utilisation
Nousallonsmodifiernotreserviced
’
authentificationafinderespecterlestandardMVC.
a.Lesspécifications
Leclientutiliseunepagestatiqueoudynamiquepours
’
authentifier(authentification.html).Cettepagedéclenchele
contrôleur qui est la Servlet : ServletAuthentification. Le contrôleur réalise un traitement simple (vérification de
l
’
authentification)etréaliseleroutagedelaréponseverslapagebienvenue.jspouverslapaged
’
erreurerreurs.jsp
quisontlesvues.
Tout le routage (chemins) est paramétré dans le fichier de configuration de l
’
application afin de faciliter la
maintenanceetl
’
évolutivitéduprojet.
...
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
<! -- url -->
<init -param>
<param -name>urlAuthentification</param -name>
<param -value>/authentification.html</param -value>
</init -param>
<init -param>
<param -name>urlErreurs</param -name>
<param -value>/erreurs.jsp</param -value>
</init -param>
<init -param>
<param -name>urlBienvenue</param -name>
<param -value>/bienvenue.jsp</param -value>
</init -param>
</servlet>
...
LesroutesoucheminssontdéfinispardesparamètresdelaServlet,celapermetdechangerleursnomssansavoir
àrecompilerl
’
application.
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
//routes
String urlAuthentification=null;
String urlErreurs=null;
- 2 - © ENI Editions - All rigths reserved
String urlBienvenue=null;
public void init()
{
//récupérer les paramètres d ’initialisation
de la Servlet dans le fichier web.xml
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
//récupérer les routes
urlAuthentification=(String)config.getInitParameter
("urlAuthentification");
urlErreurs=(String)config.getInitParameter("urlErreurs");
urlBienvenue=(String)config.getInitParameter("urlBienvenue");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer l ’identifiant/login dans la requête
String identifiant=request.getParameter("identifiant");
//récupérer le mot de passe dans la requête
String motdepasse=request.getParameter("motdepasse");
//déclaration d ’une liste/collection d ’erreurs
ArrayList<String> erreursParametres=new ArrayList<String>();
//pas d ’identifiant, retour sur la page d ’authentification
if(identifiant==null)
{
if(urlAuthentification==null)
{
throw new ServletException("Le paramètre
[urlAuthentification] n ’a pas été initialisé");
}
else
{
getServletContext().getRequestDispatcher(urlAuthentification).forward
(request, response);
}
return;
}
//identifiant vide
if(identifiant.equals(""))
{
erreursParametres.add("Le paramètre [identifiant] est vide");
}
//identifiant inférieur à 5 caractères
if(!identifiant.matches("^[0 -9a-zA-Z]{5,}$"))
{
erreursParametres.add("Le paramètre [identifiant]
doit avoir au moins 5 caractères");
}
//pas de mot de passe
if(motdepasse==null)
{
if(urlAuthentification==null)
{
throw new ServletException("Le paramètre
[urlAuthentification] n ’a pas été initialisé");
}
else
{
getServletContext().getRequestDispatcher(urlAuthentification).forward
(request, response);
}
return;
- 3 -© ENI Editions - All rigths reserved
}
//mot de passe vide
if(motdepasse.equals(""))
{
erreursParametres.add("Le paramètre [mot de passe]
est vide");
}
//motdepasse inférieur à 5 caractères
if(!motdepasse.matches("^[0 -9a-zA-Z]{5,}$"))
{
erreursParametres.add("Le paramètre [mot de passe]
doit avoir au moins 5 caractères");
}
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//injecter dans la requête nos paramètres
pour qu’elle puisse les traiter
request.setAttribute("identifiant",identifiant);
request.setAttribute("motdepasse",motdepasse);
//authentification correcte, redirection vers
la page de bienvenue
getServletContext().getRequestDispatcher(urlBienvenue).forward
(request, response);
}
}
//authentification incorrecte
else
{
erreursParametres.add("Les coordonnées de
l’utilisateur sont incorrectes");
}
//en cas d ’erreur, redirection avec le
RequestDispatcher vers la page d ’erreur dynamique
if(erreursParametres.size()>0)
{
if(urlErreurs==null)
{
throw new ServletException("Le paramètre
[urlErreurs] n ’a pas été initialisé");
}
else
{
request.setAttribute("erreurs",erreursParametres);
getServletContext().getRequestDispatcher(urlErreurs).forward
(request, response);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Désormais,nouspouvonsappelerdirectementlapage :http://localhost:8080/betaboutique/authentificationclient.
- 4 - © ENI Editions - All rigths reserved
Lemotdepasseetl
’
identifiantnesontpasprésentsdanslarequête,lecontrôleurvaredirigerl
’
utilisateurversla
page :urlAuthentification
.
...
//pas d’identifiant, retour sur la page d ’authentification
if(identifiant==null)
{
if(urlAuthentification==null)
{
throw new ServletException("Le paramètre
[urlAuthentification] n ’a pas été initialisé");
}
else
{
getServletContext().getRequestDispatcher(urlAuthentification).forward
(request, response);
}
return;
}
...
Sil
’
utilisateurréaliseunemauvaiseauthentificationsuiteàuneerreurdesaisieoudesyntaxeoudescoordonnées
incorrectes,ilestalorsredirigéverslapaged
’
erreur(vue)adaptée.
...
//en cas d ’erreur, redirection avec le RequestDispatcher vers
la page d ’erreur dynamique
if(erreursParametres.size()>0)
{
if(urlErreurs==null)
{
throw new ServletException("Le paramètre
[urlErreurs] n ’a pas été initialisé");
}
else
{
request.setAttribute("erreurs",erreursParametres);
getServletContext().getRequestDispatcher
(urlErreurs).forward(request, response);
}
}
...
Sil
’
utilisateurréaliseuneauthentificationcorrecte,ilestredirigéverslapagedebienvenue.
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//injecter dans la requête nos paramètres
pour qu’elle puisse les traiter
request.setAttribute("identifiant",identifiant);
request.setAttribute("motdepasse",motdepasse);
//authentification correcte, redirection vers
la page de bienvenue
getServletContext().getRequestDispatcher(urlBienvenue).forward
(request, response);
}
}
- 5 -© ENI Editions - All rigths reserved
...
Nous pouvons essayer de tester notre application avec tous les cas, notamment avec une mauvaise page de
routageafindedéclenchervolontairementl
’
exception.
Notre architecture est plus complexe mais respecte le modèle MVC. La logique de traitement (vérification de
l
’
authentification)estréaliséeparuneServlet.Lecontrôleurquiréaliseleroutageestégalementréaliséparune
Servletetl
’
affichagedesdonnéesestexclusivementréservéàdespagesJSP(codeHTML,CSS,JavaScript...).
Nousallonsréaliserunedernièremodificationafindemettreenœuvrel
’
architectureMVCaucompletaveclapartie
Modèle. Le modèle permet de gérer la persistance des données, dans notre cas, la Servlet contrôleur
ServletAuthentificationvadéclencheruneautreServletnomméeServletAuthentificationModelequivasauvegarderle
clientdanslasession.
Dans un système évolué, le modèle utilise une base de données avec la technologie JDBC, des fichiers
sérialisésouunframeworkdepersistancecommeHibernateouJPA.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- paramètres globaux -->
<context -param>
<param -name>emailAdministrateur</param -name>
<param -value>admin@betaboutique.fr</param -value>
</context -param>
<!-- servlets -->
<servlet>
<servlet -name>servletauthentification</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
Authentification</servlet -class>
<init -param>
<param -name>defautIdentifiant</param -name>
<param -value>monidentifiant</param -value>
</init -param>
<init -param>
<param -name>defautMotDePasse</param -name>
<param -value>monmotdepasse</param -value>
</init -param>
<! -- url -->
<init -param>
<param -name>urlAuthentification</param -name>
<param -value>/authentification.html</param -value>
</init -param>
<init -param>
<param -name>urlErreurs</param -name>
<param -value>/erreurs.jsp</param -value>
</init -param>
<init -param>
<param -name>urlBienvenue</param -name>
<param -value>/bienvenue.jsp</param -value>
</init -param>
<init -param>
<param -name>urlAuthentificationModele</param -name>
<param -value>/authentificationclientmodele</param -value>
</init -param>
</servlet>
- 6 - © ENI Editions - All rigths reserved
<servlet>
<servlet -name>servletauthentificationmodele</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
AuthentificationModele</servlet -class>
</servlet>
<servlet>
<servlet -name>bienvenuesession</servlet -name>
<jsp -file>/bienvenuesession.jsp</jsp -file>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletauthentification</servlet -name>
<url -pattern>/authentificationclient</url -pattern>
</servlet -mapping>
<servlet -mapping>
<servlet -name>servletauthentificationmodele</servlet -name>
<url -pattern>/authentificationclientmodele</url -pattern>
</servlet -mapping>
</web-app>
package betaboutique.servlets.client;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletAuthentification extends HttpServlet {
//variables de classe
String ident=null;
String mdp=null;
//routes
String urlAuthentification=null;
String urlErreurs=null;
String urlBienvenue=null;
String urlAuthentificationModele=null;
public void init()
{
//récupérer les paramètres d ’initialisation
de la Servlet dans le fichier web.xml
ServletConfig config=getServletConfig();
ident=(String)config.getInitParameter("defautIdentifiant");
mdp=(String)config.getInitParameter("defautMotDePasse");
//récupérer les routes
urlAuthentification=(String)config.getInitParameter
("urlAuthentification");
urlErreurs=(String)config.getInitParameter("urlErreurs");
urlBienvenue=(String)config.getInitParameter("urlBienvenue");
urlAuthentificationModele=(String)config.getInitParameter
("urlAuthentificationModele");
}
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
- 7 -© ENI Editions - All rigths reserved
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//déclencher la Servlet qui permet de gérer
la partie Modèle de l ’application
request.setAttribute("identifiant",identifiant);
request.setAttribute("motdepasse",motdepasse);
getServletContext().getRequestDispatcher
(urlAuthentificationModele).forward(request, response);
}
}
...
//authentification incorrecte
}
package betaboutique.servlets.client;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class ServletAuthentificationModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//écrire les informations dans la session
HttpSession session=request.getSession();
session.setAttribute("identifiant",
request.getAttribute("identifiant"));
session.setAttribute("motdepasse",
request.getAttribute("motdepasse"));
//redirection vers la page d ’affichage du contenu
de la session (avec un appel par nom par exemple)
getServletContext().getNamedDispatcher("bienvenuesession").forward
(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>BIENVENUE SESSION</title>
</head>
<body>
<%
String identifiant=(String)session.getAttribute("identifiant");
if(identifiant==null)
{
identifiant="inconnu";
}
- 8 - © ENI Editions - All rigths reserved
String motdepasse=(String)session.getAttribute("motdepasse");
if(motdepasse==null)
{
motdepasse="inconnu";
}
%>
<h2>Bienvenue client : <br/>
Identifiant : <%= identifiant %> <br/>
Mot de passe : <%= motdepasse %> <br/>
</h2>
</body>
</html>
Parlasuite,lesclassesdegestiondumodèleserontregroupéesdansunmêmepaquetageappelé
modele
.
- 9 -© ENI Editions - All rigths reserved
Gestiondesexceptions,erreursetpaged
’
accueil
1.Gestiondesexceptions
LagestiondesexceptionsenJavaesttrèsévoluéeetpermetd
’
éviterdesréponseserronéeslorsdecalculs,de
traitementsparticuliers...SiparexempledansuneServlettraitantlessaisiesd
’
unformulaire,l
’
utilisateursaisitune
chaînedecaractèresàlaplaced
’
unentier,leconstructeurdelaclasse
Integer(...)
lanceraalorsuneexception.
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
String num=request.getParameter("nombre");
Integer i=new Integer(num);
}
Parfoisl
’
utilisateurvoits
’
afficherenrésultatlatracedel
’
exceptiondanslenavigateuroun
’
obtientaucuneréponse.
Danstouslescas,l
’
applicationestdéfectueuse.Dansuneapplicationprofessionnelle,ilestnécessaired
’
effectuer
descontrôlescôtéclientetcôtéserveur.Parfoislescontrôlescôtéclient(enJavaScript)nesontpasutilisésmaisles
contrôlescôtéserveursontobligatoires.
Pourtraiterleserreursdeconversions,decalculs,d
’
accèsàdesvariablesoufichiers,leblocd
’
instructionstrycatch
estutilisé,ilpermetd
’
isolerunepartiedecodesensible.
Leproblèmeestquesiuneinstructiondéclencheuneexception,aucunmessagepourl
’
utilisateurn
’
estprévu.La
solutionpeutêtredeplacerdesinstructionsd
’
affichagedanslebloccatchpourretourneruneréponsespécifiqueen
casd
’
erreur.
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
String num=request.getParameter("nombre");
Integer i=new Integer(num);
}
catch(Exception e)
{
System.out.println("Exception");
}
}
2.Gestionsdespagesd
’
erreurs
AvecJavaEEilexisteunefaçonplusrobustededéfinirlespagesd
’
erreurs.Parexemple,pourlecasprécédent,une
pageWebindiquantunproblèmedeformat(Ex :Entrezuniquementdeschiffres)pourraitêtrecréée.Ilestensuite
possibledeconfigurerl
’
applicationtoujoursparl
’
intermédiairedesonfichierdegestionweb.xml.Pourcela,ilsuffit
d
’
utiliserl
’
élément
<error
page>
avecl
’
exceptionassociée.
Les exceptions sont placées après la gestion des sessions et des mappings de Servlets dans le fichier de
configurationweb.xml
.
...
<!-- fichier à afficher en cas d ’erreur d ’entier -->
<error-page>
<exception -type>java.lang.NumberFormatException</exception -type>
<location>/erreurchiffre.html</location>
</error -page>
...
Ilpeutêtreégalementtrèsintéressant
’’
d
’
attraper
’’
toutesleserreurssansengéreràchaquefoisletype.Chaque
exceptionlevée(conversion,calculs...)déclencheraalorslamêmepage.
...
<!-- fichier à afficher en cas d ’erreur -->
<error-page>
- 1 -© ENI Editions - All rigths reserved
<exception -type>java.lang.Throwable</exception -type>
<location>/erreurglobale.html</location>
</error -page>
...
Nouspouvonsvérifiercetteutilisationavecnotreapplicationd
’
authentificationenmodifiantundesparamètresdu
fichierweb.xmlpourdéclencherl
’
exceptionsuivante :
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
...
<servlet -mapping>
...
</servlet -mapping>
<error -page>
<exception -type>java.lang.Throwable</exception -type>
<location>/erreurglobale.html</location>
</error -page>
</web-app>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Erreur BetaBoutique</title>
</head>
<body><h1>Erreur : une erreur est survenue, recommencez votre
opération !</h1></body>
</html>
...
//vérifier l ’égalité des valeurs
if( (identifiant!=null && identifiant.equals(ident))
&& (motdepasse!=null && motdepasse.equals(mdp)) )
{
if(urlBienvenue==null)
{
throw new ServletException("Le paramètre
[urlBienvenue] n ’a pas été initialisé");
}
else
{
//dé clencher la Servlet qui permet de gérer la
partie Modèle de l ’application
request.setAttribute("identifiant",identifiant);
request.setAttribute("motdepasse",motdepasse);
getServletContext().getRequestDispatcher
(urlAuthentificationModele).forward(request, response);
}
}
//authentification incorrecte
else
{
erreursParametres.add("Les coordonnées de
l’utilisateur sont incorrectes");
}
...
S
’
ilyauneauthentificationcorrecte,lapagedebienvenueestdemandéemaismalorthographiéeparerreurpourle
développement de l
’
application, l
’
exception ServletException est déclenchée et provoque l
’
affichage de la page
d
’
erreurgénérale.Pourl
’
utilisateurc
’
esttotalementtransparent.Parcontre,l
’
exceptionestaffichéedanslaconsole
système.
Delamêmefaçon,ilestpossibledespécifierdespagesàafficherpourlescodesd
’
erreurHTTP,parexemplegérerles
erreurs404(pagenontrouvéeetinexistantesurleserveur).
Pourcela,nousajoutonslecodesuivantdansnotrefichierdeconfigurationweb.xml,nouscréonsunepageHTML
- 2 - © ENI Editions - All rigths reserved
d
’
erreur 404 (ex: 404.html) et nous déclenchons une URL inexistante sur notre serveur
(ex : http://localhost:8080/betaboutique/unepage).
...
<error-page>
<error -code>404</error -code>
<location>/404.html</location>
</error-page>
...
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Erreur BetaBoutique</title>
</head><body><h1>Erreur : cette page n ’est pas disponible !</h1></body>
</html>
Cettetechniquetrèspuissantepermetdegérertouslescodesd
’
erreurHTTPquisontconsultablesàcetteadresse :
http://www.w3.org/Protocols/rfc2616/rfc2616
sec10.html.Dansuneapplicationenproduction,leserreurs404(page
non trouvée) et 500 (erreur interne du serveur) doivent systématiquement être traitées. Le but est de toujours
fournirauclientunepagelisibleetcorrectementmiseenforme.
3.Gestiondelapaged
’
accueil
Lorsquenous visitons dessites au moyend
’
unnavigateur, nous précisonsuniquement lenom de domainepour
accéderàlapaged
’
accueil.Dansnotrecassinousprécisonsl
’
URLsuivantequicorrespondaucontexte,aucunepage
n
’
estaffichée(erreur404)(http://localhost:8080/betaboutique/).
Ilexistedesbalisesparticulièresdufichierdeconfigurationquipermettentdegérerlecontextedel
’
applicationquand
il n
’
yapasd
’
indicationdedocumentparticulier.Cesdocumentsassociéssontappelésdeswelcomefiles.Enrègle
générale,lesserveursWebsontpositionnésaveclespagessuivantes :index.html
,
index.php
,
index.jsp...Nousallons
modifiernotrefichierdedéploiementpourafficherleformulaired
’
authentificationlorsdel
’
arrivéesurlesite.
L
’
élément
<welcome
file
list>doitêtreplacéentreladéfinitiondessessionsetlespagesd
’
erreursdanslefichierde
configurationdel
’
applicationweb.xml
.
...
<!-- fichier de point de départ de l ’application -->
<!--
il interdit en plus l ’exploration de l ’arborescence -
attention, chemin toujours relatif -->
<welcome -file-list>
<welcome -file>authentification.html</welcome -file>
</welcome -file-list>
...
Dans ce cas lors de l
’
accès à l
’
application : http://localhost:8080/betaboutique/, le serveur renvoie le premier
documenttrouvéparmiceuxdelalisteindiquéedansl
’
élément
<welcome
file
list>
.
- 3 -© ENI Editions - All rigths reserved
Enrésumé
CechapitreaprésentélemécanismedeServletJavaEE.Lapremièrepartieaprésentélefonctionnementduprotocole
HTTPet l
’
utilisationdesServlets.LapartiesuivanteaintroduitleprojetBetaBoutiquedéveloppétoutaulongdece
guideetquipermetdemettreenapplicationtouteslestechnologiesévoquées.Leprojetcommenceparunepremière
Servlet simple et la mise en place d
’
un service d
’
authentification pour les clients du site. Afin de comprendre le
fonctionnementdesServlets,lesinterfaces
ServletConfig
etServletContextontétéprésentéesainsiqueletraitement
desrequêtesetdesréponsesHTTPaveclesméthodesdeservice
doGet(...)
et
doPost(...)
.Lechapitresuivantfaitun
rappelsurlasynchronisationdestraitementsaveclesServlets.Ensuite,l
’
étapesuivanteapermisdegérerl
’
étatdes
clientssachantqueleprotocoleHTTPfonctionneenmodedéconnecté.Parlasuite,lesfiltresontétédétaillésainsique
l
’
interfaceRequestDispatcherquiestutiliséepourleroutageHTTPetquiestlabasedumodèleMVCaveclesServlets.
Enfin,l
’
applicationaétémodifiéedanslesdeuxdernierschapitrespourcolleraumodèleMVCetpourgérerleserreurs
etexceptionsafind
’
avoirunservicefiableetrobusteproched
’
uneapplicationprofessionnelle.
- 1 -© ENI Editions - All rigths reserved
Travailleravecunebasededonnées
1.Présentation
LaplupartdesapplicationsJavaEEutilisentunebasededonnéespourlapersistancedesinformations.Lessitesde
commercecommeleprojetBetaBoutique,stockentlesinformationsconcernantlesclients,articlesoucommandes.
Pourrappel,unebasededonnéesestunensembledetablesorganiséespourstockerdesdonnéesmanipulablespar
uneouplusieursapplications.Unetableestunensembled
’
occurrencesd
’
unobjetdéfiniparsespropriétés(champs)
etdontchaqueenregistrement(record)représenteuneinstance(valorisationdeschampsdecetobjet).
La plupart des bases de données commercialisées sont de nature client
serveur et recourent au langage SQL
(
Structured Query Language)pourmanipulerlesdonnéesqu
’
ellescontiennent.JavapossèdeuneAPIpourtravailler
avec les bases de données. Cette technologie nommée JDBC (Java DataBase Connectivity) est une bibliothèque
d
’
interfacesetdeclasses,utiliséepouraccéderauxSGBDR(SystèmedeGestiondeBasedeDonnéesRelationnelles).
JDBCfournitauxdéveloppeurstouslesoutilsnécessairespourpermettreàdesprogrammesclientsdeseconnecterà
desbasesdedonnéesetdeleurenvoyerdesrequêtes.CesrequêtessontécritesenlangageSQL.
UnprogrammeécritenJDBCenvoieàunSGBDRdesrequêtesécritesenSQLetexploitelerésultatretournéenJava.
Eneffet,cesystèmecréeuneabstractiondesfonctionsd
’
unebasededonnéessousformed
’
ensembledeclasseset
deméthodes.Le codespécifiqueà unebasede données particulièreestcontenu dansunebibliothèque appelée
piloteoudriver.SinousutilisonsunebasededonnéespossédantunpiloteJDBC,ellepeutêtreemployéeavecl
’
API.
Ilexisteplusieursinconvénientsàl
’
utilisationdelatechnologieJDBC :
●Lesinstructionssontécritessousformedechaînesdecaractèresetleurexactitudenepeutêtrevérifiéenipar
JavaniparJDBC,maisuniquementparleSGBDaumomentdel
’
exécution.
●L
’
APIJDBCestlimitéeàl
’
utilisationdebasesdedonnéesrelationnelles.Iln
’
existepasd
’
implémentationpour
desbasesdedonnéesobjetsSGBDO(SystèmedeGestiondeBasedeDonnéesd
’
Objets).
●L
’
utilisationdel
’
APIJDBCnécessitedeconnaîtreunminimumdulangageSQLafinderéaliserlesrequêtes.
Lesavantagesd
’
utiliserlatechnologieJDBCsont :
●EncasdechangementdeSGBDR,ilsuffitdechangerdedriverenutilisantceluiquiestadapté.
●UneapplicationJDBCpeutseconnecteràplusieursSGBDRenmêmetemps.
●LesbibliothèquesJDBCreposentsurSQL,ellesbénéficientdetoutessesfonctionnalités(sélections,jointures,
transactions...).
●Laduréed
’
apprentissagedeJDBCestréduite,pourunepersonnequiconnaîtlelangageSQL.
●L
’
APIJDBCestportablesurlaplupartdessystèmesactuels.
Parlasuite,nousallonsutiliserl
’
APIJDBCetainsidévelopperdesméthodesdemanipulationdedonnéespersistantes
sansnoussoucierdelabasededonnéesutilisée.Nousutiliseronsparexemple,unebasededonnéesMySQLetle
driveradaptéàceSGBDR.Touteslesrequêtesd
’
interrogation,delecture,demiseàjouretdesuppressionseront
indépendantesdelabasededonnées.SinoussouhaitonspasseràunebasededonnéesPostgreSQLenproduction
parexemple,ilsuffiradechangerledriveretd
’
utiliserceluiadaptéàPostgreSQLsansretouchernotrecode.
CesystèmeportableestbeaucoupplusévoluéenJavaqu
’
aveclestechnologiesASPouPHP.
En effet, avec la technologie PHP par exemple, nous utiliserions les méthodes suivantes mysql_connect(...)
,
mysql_close(...)
,
mysql_fetch_array(...) qui sont propres au SGBDR MySQL. En cas de passage vers le SGBDR
PostgreSQLouOracle,ilseraitnécessairedecoderdenouveautoutel
’
application.
2.Connexionauxbasesdedonnées
Laconnexionàunebasededonnéesparl
’
intermédiairedel
’
APIJDBCreposesurplusieursétapes :
- 1 -© ENI Editions - All rigths reserved
●Ilestnécessairededéterminerlepiloteàutiliserpourcommuniqueraveclabasededonnées.
●IlestnécessairederéaliserlecodeJavaafind
’
établirlaconnexionaveclabasededonnées.
●Ilestnécessaired
’
utiliserunobjetspécifiquepourinsérer,mettreàjouroueffacerdesdonnées.
●Ilestnécessaired
’
utiliserunobjetspécifiquepourlirelesrésultatsd
’
unerequête.
La première étape, consiste à établir la connexion entre le programme et la base de données. Un programme
travaillant avec une base de données utilise l
’
API JDBC pour établir une connexion avec le serveur de base de
données.
Lecode spécifique àla base dedonnées utiliséeestcontenu danslepilote créépar l
’
éditeurdelabaseouune
société tierce. Le principal intérêt, est qu
’
un programme peut communiquer avec différentes bases de données
simplementenchangeantsonpilote.Deplus,lesprogrammessontsimplescarlesdétailsdesprocéduresdebas
niveausontentièrementgérésparlepilote.
LaspécificationJDBCproposequatretypesdepilotespouvantêtreemployéspourcommuniqueraveclesbasesde
données.
●Lespilotesdetype1appelés,pilotemiddlewareODBC :cetypedepiloteétablitlacorrespondanceentre
l
’
APIJDBCetuneautreAPI.LesystèmeODBCaétédéveloppépourlessystèmesd
’
exploitationWindows.
ODBCestuneAPIpermettantdecommuniqueraveclesbasesdedonnées.LepiloteJDBC
ODBCdetype1
fournituneinterfaceentrelesprogrammesJavaetl
’
APIODBC.Cettecouched
’
abstractionestcomposéedesa
propreAPI.LesappelsJDBCsontconvertisenappelsODBCavantd
’
êtrepassésàlabasededonnées,ce
pilote n
’
est pas très efficace et nécessite la configuration d
’
une source de données ODBC qui requiert
généralementuniquementlaprésenced
’
unserveurWindows.
●Lespilotesdetype2appelés,pilotemiddlewarenatif :cetypedepiloteestsemblableaupilotedetype1
carilcommuniqueaveclabasededonnéesparl
’
intermédiaired
’
uneAPInative.Unlogicielintermédiaireest
conçuspécialementpourseconnecteràunpiloteJDBC.Celogicielintermédiaireappelémiddleware,estécrit
spécialementpourlaplate
formeutilisée.Cetypedepiloteentraîneparfoisdesbaissesdeperformancesdans
lamesureoùlemiddlewaresetrouveentrelabasededonnéesetJDBC.Enoutre,iln
’
existepastoujoursla
possibilitédetrouverdespilotescorrespondantàtouteslesplates
formes.
●Lespilotesdetype3appelés,pilotemiddlewareJava :cetypedepiloteestsimilaireauxpilotesdetype2
mais le logiciel exécuté entre JDBC et la base de données est cette fois une application Java. Le pilote
communiqueaveclabasededonnéesgrâceàuncomposantintermédiaire.LeprogrammeJavacommunique
aveccecomposantparlebiaisd
’
unprotocoleréseauindépendant.CepiloteestécritenpurJava,ils
’
exécute
donc partout où Java peut être installé. Il peut donc être téléchargé et exécuté immédiatement, sans
configurationutilisateur.
●Lespilotesdetype4appelés,pilotenatifpurJava :cetypedepiloteseconnectedirectementàlabasede
données. Il bénéficie donc, comme le pilote de type 3, de la portabilité de Java. Ce type de pilote léger,
communique directement avec la base de données sans qu
’
une conversion soit nécessaire. Il traduit les
appelsJDBCenrequêteutilisantleprotocoledelabasededonnéessanspasserparODBCouuneAPInative.
Cetypedepiloteoffregénéralementlesmeilleuresperformances.Laplupartdesfournisseursdebasesde
donnéesproposentdésormaisdespilotesdetypes2adaptéscommeOracle,MySQL,PostgreSQL,Sybase,
InterBase... Si l
’
application doit pouvoir être installée sur différentes plates
formes il est pratiquement
indispensable d
’
utiliser un pilote de type 4. Il sera ainsi possible de déployer l
’
application sur différents
systèmessansmodifications.
Le plus souvent, nous utilisons des pilotes de types 3 ou 4. Les pilotes de type 1 et 2 ajoutent une couche de
communicationentrelacoucheJDBCetlabasededonnées,cequinuitàl
’
efficacité.Entermedeperformances,les
LespilotesJDBC
- 2 - © ENI Editions - All rigths reserved
pilotesdetype3et4sontpratiquementéquivalents.
MySQLestsupportéeparunlargeéventaild
’
outils.ElleestsoumiseàlalicenceGNUGPL.MySQLestsurtoutinstallée
pour les applications Web, elle est solide et utilisée par de grands groupes spécialisés dans l
’
Internet. Elle reste
cependantparfoislimitéeentermedefonctionnalitésavancéesmaiselleesttrèsévoluéeentermedeperformances.
Plusieurspilotesnatifsdetype4sontdisponiblespourMySQLetsontconseilléspouruneutilisationenJava.
PostgreSQL est disponible pour la plupart des systèmes d
’
exploitation modernes. PostgreSQL a été développé à
l
’
universitédeBerkleyenCaliforniesousladirectiond
’
ungroupededéveloppementetdiversautresparticipantsdans
lemondeentier.
OracleetDB2sontlesdeuxleaderssurlemarchédesbasesdedonnéescommerciales.Oracleoffredenombreuses
fonctionnalités comme l
’
intégration de code Java dans les procédures stockées. Ce SGBDR est robuste et très
performant. Cependant, cette base de données possède deux inconvénients majeurs, le prix des licences et la
complexitédu système.Oraclepossède unpilote JDBCde type 4utilisable avecles applications Java.DB2 estun
SGBDRquiestdéveloppéparlasociétéIBM.CesystèmetrèsperformantutiliseunpiloteJDBCdetype4pourles
applicationsdéveloppéesenJava.
3.Utilisationdel
’
APIJDBC
PourlamiseenapplicationdeJDBC,nousallonsutiliserleSGBDRMySQLdisponibleàcetteadresse :http://www
fr.mysql.com/.
Pourleprojet,nousallonsinstallerlepaquetEasyPHP(http://www.easyphp.org/)afindebénéficierdelabasede
donnéesMySQL(version5)maiségalementdel
’
outilPhpMyAdminafindecréerfacilementlestables,manipulerles
données,gérerlesencodages...
NousallonscommencerpardémarrerMySQLetcréerlabasededonnéesnommée :betaboutique
.
Danscettebasededonnées,nousallonscréerlatableclient,avecpourlemomentlesdonnéessuivantes :
LaclasseDriverManagerestresponsabledelagestiondespilotesJDBC.Cetteclassepermetdefournirlesconnexions
aucodeJava.Pourutiliserunebasededonnées,ilsuffitdepasseraugestionnairedepilotesDriverManageruneURL
afinquecelui
ciretourneuneconnexion.Lorsqu
’
unprogrammedemandeuneconnexion,legestionnairedepilotes
Lesbasesdedonnées
- 3 -© ENI Editions - All rigths reserved
interrogechaquepilotepoursavoirs
’
ilestcapabledetraiterl
’
URL.Dèsqu
’
unpilotecorrectesttrouvé,illuidemande
d
’
établiruneconnexionetlaretourneauprogrammeappelant.
QuellequesoitlabasededonnéesutiliséeavecJDBC,ilfautinstallersonpilotepourqu
’
ellepuissefonctionneravecla
programmationJava.Chaquedriverestspécifiqueàlabasededonnéesutilisée.PourMySQL,leconnecteurutilisé
dans ce guide est mysql
connector
java
3.1.11
bin.jar
. Il peut être téléchargé à cette adresse :
http://dev.mysql.com/doc/refman/5.0/fr/java
connector.html
CettelibrairieauformatJARcontientlesclassesquiserontutiliséesparlesapplicationsJava,afindefonctionneravec
le SGBDR MySQL. Une fois cette bibliothèque téléchargée, nous l
’
installons dans le répertoire
/lib
de Tomcat. Ce
répertoirecontientleslibrairiesutiliséesetpartagéesparleserveurd
’
applications.Cettelibrairieserasansdoute
utiliséeparplusieursprojets,ilestdoncconseillédel
’
installerdanslerépertoirepartagé.Toutefois,cetteopération
n
’
est pas obligatoire, et nous pouvons installer le pilote JDBC comme une autre librairie (installation dans le
répertoire/WEB
INF/lib
del
’
application).
PourfairefonctionneruneapplicationavecJDBC,ilfauteffectuerlestâchessuivantesdansl
’
ordreindiqué,quelleque
soitlabasededonnéesutilisée :
●Chargementdupilote/driverdelabasededonnées.
●Obtentiondelaconnexionàlabasededonnées.
●Préparationdelarequêted
’
accèsàlabasededonnées.
●Accèsauxdonnéesdelabasededonnées.
●Libérationdesressources/connexions.
Pour mettre en application JDBC avec la table client de la base betaboutique, nous allons développer la Servlet :
ServletListeClientModele
.
Lechargementdudriveresteffectuéavecl
’
instructionClass.forName(nomdudriver)
.
Pournotreprojet,nousallonsutiliserlaconfigurationsuivante :
...
<servlet>
<servlet -name>servletlisteclientmodele</servlet -name>
<servlet -class>betaboutique.servlets.client.ServletListe
ClientModele</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletlisteclientmodele</servlet -name>
<url -pattern>/listeclient</url -pattern>
</servlet -mapping>
...
Laméthode forName(String)delaclassejava.lang.Class permet d
’
indiquer à la JVM qu
’
elledoit trouver,charger et
initialiserlaclassedésignéeparleparamètre.Leprogrammeappelantn
’
adoncpasbesoindecréeruneinstancedela
classe.Laclassesechargedecréeruneinstanceets
’
occupeelle
mêmedel
’
enregistrement.Leparamètreestlenom
dudriverindiquéparlefournisseurpourlabasededonnéesconcernée.Ceparamètrecorrespondaunomdelaclasse
quiserachargéeenmémoirepar
Class.forName(...)
.PourMySQLceparamètreest :
com.mysql.jdbc.Driver
.
L
’
appeldecetteméthodepeutprovoqueruneexceptiondetypeClassNotFoundExceptionquenousdevonsattraper
avecunblocd
’
exceptiontrycatch
.
Eclipsepermetdedéclarerautomatiquementlesblocsd
’
exceptions.Pourcela,ilfautfaireunclicsurlacroix
rougequireprésentel
’
erreuretsélectionner :surround
.
Lecodedechargementdupilote,présentdanslaServletServletListeClientModele,estdonclesuivant :
package betaboutique.servlets.client;
Installationdudriverdelabasededonnées
Chargementdupilote/driverdelabasededonnées
- 4 - © ENI Editions - All rigths reserved
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Pilote MySQL JDBC chargé");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Unefoisledriverchargé avec succès,nouspouvonsutiliserl
’
APIJDBCpourobteniruneconnexionàlasourcede
données.LaconnexionauSGBDRnécessiteunnomd
’
utilisateuretunmotdepasse.Lesconnexionspeuventêtre
obtenuesàpartirdupilote.Laméthodepermettantd
’
obteniruneconnexionest
getConnection(...)
.
Ilexisteplusieursformesdecetteméthode,lesplusutiliséessont :getConnection(Stringurl)etgetConnection(String
url,Stringutilisateur,Stringmotdepasse)
Le paramètre URL est une URI HTTP avec l
’
adresse IP du serveur contenant la base de données (en local ou à
distance)etsonnom.
Pournotreprojetl
’
instructionestdelaformesuivante :
package betaboutique.servlets.client;
import java.io.IOException;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
Connection connection=null;
try {
//chargement du driver
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Pilote MySQL JDBC chargé");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
try {
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
Obtentiondelaconnexionàlabasededonnées
- 5 -© ENI Editions - All rigths reserved
System.out.println("Connexion opérationnelle");
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Erreur lors de
l’établissement de la connexion");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Dansnotreexemple,nousavonspréciséleprotocole
jdbc
,leserveurdebasededonnéesprésentsurlamachine
locale(localhost),leportMySQL(3306)etlabasededonnéesutilisée(betaboutique).Lenomd
’
utilisateurestrootet
cetutilisateurnepossèdepasdemotdepasse.
IlexisteégalementunetroisièmeetdernièreformedelaméthodegetConnection(Stringurl,Propertiesproprietes)avec
uneURLetunensembledepropriétésreprésentéesparunobjetinstanciéàpartirdelaclasseProperties.Cetobjet
permetdepasserdesparamètressupplémentairescommeletypedeconnexion,lecryptageutilisé,lecodagedela
base...Laconnexionouverteestutilisabletoutletemps,pourtouteslesrequêtesfuturesdansletemps,tantque
celle
cin
’
estpasfermée.
Deuxcassontenvisageablespourl
’
accèsauxdonnées.Soitlarequêteestunesélectiondedonnées(sélection),soit
uneautreaction(création,modificationousuppression).Danslepremiercas,lalistedesdonnéesestrenvoyéeetla
requête exécutée (executeQuery), dans le second cas, le nombre d
’
enregistrements affectés est retourné et la
modificationdel
’
étatdesdonnées(executeUpdate)estexécutée.
SELECT :ResultSetrs=statement.executeQuery(requete);
AUTRE :intnblignes=statement.executeUpdate(requete);
L
’
accèsauxdonnéesestréaliséàtraversl
’
objetResultSetobtenulorsdel
’
appelàstatement.executeQuery(requete)
.
Nousverronsparlasuiteunexemplecompletd
’
utilisation.
Lorsdudéveloppement,iln
’
estpasrared
’
arriveràlasaturationdel
’
applicationàcaused
’
unproblèmedefermeture
deconnexionsuiteàunemontéeencharge.Ilestimportantdevérifieravecdesoutilsadaptés(fournisaveclabase
dedonnées)silenombred
’
ouverturesetlenombredeconnexionslibéréessontidentiques.Sicesnombresdivergent,
mêmelentement,ilyaalorsunproblèmedeprogrammation.L
’
applicationvafinirparsebloquer,dansl
’
attentede
connexionsquin
’
arriverontjamais.IlestdonctrèsimportantenJavaEEdelibérercorrectementlesconnexions.
Lasimpleinvocationdelaméthode
close()
del
’
objetconnexionpermetlafermeture.
IlestégalementnécessairedelibérerlesressourcesmémoireoccupéesparlesobjetsStatementpourlapréparation
des requêtes et les objets ResultSet pour le parcours de ces requêtes. Pour pallier à certaines erreurs de
programmation,leramasse
miettesvasechargerdefermercorrectementcertainesressourcesmaisn
’
importequand.
Nousallonsmontrerunexempledechargementdudriver,deconnexionauSGBDRetdefermetureavecnotreServlet.
package betaboutique.servlets.client;
import java.io.IOException;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
Connection connection=null;
try {
Préparationdelarequêted
’accèsàlabasededonnées
Accèsauxdonnéesdelabasededonnées
Libérationdesressources/connexions
- 6 - © ENI Editions - All rigths reserved
//chargement du driver
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Pilote MySQL JDBC chargé");
} catch (ClassNotFoundException e) {
// TODO Auto -generated catch block
e.printStackTrace();
System.out.println("Erreur lors du chargement du pilote");
}
try {
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
//obtenir des informations sur la base de données
DatabaseMetaData dbmd = connection.getMetaData();
System.out.println("Nom de la base de données :
"+dbmd.getDatabaseProductName());
System.out.println("Version de la base de données :
"+dbmd.getDatabaseProductVersion());
System.out.println("Nom du pilote de la
base de données : "+dbmd.getDriverName());
System.out.println("Version du pilote de la
base de données : "+dbmd.getDriverVersion());
} catch (SQLException e) {
// TODO Auto -generated catch block
e.printStackTrace();
System.out.println("Erreur lors de
l’établissement de la connexion");
}
//fermer la connexion
try {
connection.close();
} catch (SQLException e) {
// TODO Auto -generated catch block
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Cecodeestfonctionnelmaisilcontientuneénormeerreurdeprogrammation.LesméthodesJDBCpeuventlancerdes
exceptions SQL. Le problème c
’
est que si une telle exception est lancée, avec la méthode getConnection(), un
Statement ou un ResultSet
, l
’
exception liée à la fermeture de la connexion ne sera pas déclenchée. Dans notre
exemple,silaconnexionestréaliséemaisqu
’
uneexceptionestdéclenchéeàlasuite,lecodesuivantneserajamais
déclenché :
...
//fermer la connexion
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
- 7 -© ENI Editions - All rigths reserved
...
Lafaçoncorrected
’
utiliserlaméthode
close()
,consisteàplacercetteinstructiondanslebloc
finally
d
’
unestructuretry
,
catch
,
finally
.Cettepartieducodeseratoujoursdéclenchéequoiqu
’
ilarrive,qu
’
uneexceptionsoitlevéeoupas.La
Servletdoitdoncavoirlecodesuivant :
...
try
{
//obtention de la connexion
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/
betaboutique","root","");
System.out.println("Connexion opérationnelle");
//obtenir des informations sur la base de données
DatabaseMetaData dbmd = connection.getMetaData();
System.out.println("Nom de la base de données :
"+dbmd.getDatabaseProductName());
System.out.println("Version de la base de données :
"+dbmd.getDatabaseProductVersion());
System.out.println("Nom du pilote de la base de données :
"+dbmd.getDriverName());
System.out.println("Version du pilote de la base de données :
"+dbmd.getDriverVersion());
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement de la connexion");
}
finally
{
//fermer la connexion
try
{
connection.close();
} catch (SQLException e)
{
e.printStackTrace();
}
}
...
Nousavonsvudanslecodeprécédentcommentétabliruneconnexionavecunebasededonnées.Toutefois,l
’
objet
connectionnefournitpasdeméthodespermettantd
’
utiliserlabasededonnées.Pourcréer,lire,mettreàjourou
effacerdesdonnées,nousutilisonslaclasseStatement
.
LesobjetsStatementreprésententl
’
interfaceprincipaleaveclabasededonnées.Unobjetdecetteclasseestcréé
parl
’
appeldelaméthodecreateStatement()del
’
objetConnection.Unefoisenpossessiond
’
unobjetStatement,nous
pouvons l
’
utiliser pour envoyer à la base de données des commandes SQL à l
’
aide d
’
une des trois méthodes
suivantes:
●executeQuery(requête SQL) :exécutelarequêteSELECTpourinterrogerlabasededonnées,etretourneun
ResultSetpourparcourirlerésultat.
●executeUpdate(requêteSQL) :exécutelarequêteSQLpourprovoquerunemodificationdel
’
étatdelabasede
données(création,modificationousuppression)etretournelenombred
’
enregistrementsconcernés.
●executeBatch() :exécuteuntraitementparlotdecommandesSQLenuneseuleopération.
Lorsquenousréalisonsunerequêtesurunebasededonnées,lesrésultatssontretournésdansunobjetdetype
ResultSet.Cetobjetpermetdelireleslignesdurésultatetdelirelesdonnéesdanslesdifférentescolonnes.Unobjet
decetteclasseestcréésuiteàl
’
appeldel
’
unedesdeuxméthodes :
L’
interfaceStatement
L’
interfaceResultSet
- 8 - © ENI Editions - All rigths reserved
●Statement.executeQuery(requêteSQL)
;
●PreparedStatement.executeQuery()
.
UnobjetResultSetcontientlerésultatdelarequêteSELECTeffectuéeverslabasededonnées.Lesméthodesde
l
’
objetpermettentdeparcourirleslignesobtenuesparlarequêteetd
’
extraireleschampsdeceslignes.
Ensupposantqu
’
il n
’
yaitpaseudeproblèmelorsdel
’
exécutiondelacommandeSQL,laméthodeexecuteQuery()
retournetoujoursunResultSetnonnul.Audépart,lecurseurestpositionnéavantlapremièreligne.Laméthodenext
()
permetd
’
atteindrelapremièreligne,cetteméthoderetournetrues
’
ilyaunevaleuretfalselorsdel
’
accèsàla
dernièreligne.
LesméthodessuivantespeuventêtreemployéesavecunResultSet
:
●next() :pourpasseràlalignesuivanteduResultSet
.
●previous() :pourpasseràlaligneprécédente.
●first() :poursepositionneràlapremièreligne.
●last() :poursepositionneràladernièreligne.
●beforeFirst() :poursepositionneravantlapremièreligne.
●afterLast() :poursepositionneraprèsladernièreligne.
●isFirst() :pourtestersilepositionnementestàlapremièreligne.
●isLast() :pourtestersilepositionnementestàladernièreligne.
●isBeforeFirst() :pourtestersilepositionnementestavantlapremièreligne.
●isAfterLast() :pourtestersilepositionnementestaprèsladernièreligne.
●absolute(index) :poursepositionneràl
’
indexprécisé.
●
close()
:pourfermerleResultSet
.
L
’
interfaceResultSetcontientdenombreusesméthodespermettantdelirelesdonnéesretournéesparunerequête.
Cesméthodescorrespondentauxcolonnesparleurnomouparleurposition.Ilexistedeuxméthodes
getXxx()
pour
toutes les primitives Java et pour certains objets. La première méthode prend en argument une valeur entière
correspondantàl
’
indiceduchampdelatable,etl
’
autreunechaînedecaractèrescorrespondantaunomduchampde
latable.
Lesnuméros de colonnescommencent à 1et non à0. Le choixd
’
unargument detype int est plusrapidequ
’
un
élémentdetypeStringmaiségalementmoinssouple.Eneffet,lesnumérosdecolonnespeuventchangeralorsque
c
’
estplusrarementlecasdesnoms.Nousretrouvonslesméthodessuivantesdel
’
interfaceResultSet
:
getString(...)
,
getArray(...)
,
getBigDecimal(...)
,
getBoolean(...)
,
getByte(...)
,
getDate(...)
,
getDouble(...)
,
getFloat(...)
,
getInt(...)
,
getLong
(...)
,
getShort(...)
,
getTime(...)
.
La valeurNULL a une signification particulière en SQL. En effet,NULL n
’
est pas la même chose que la chaîne de
caractèresvideoulavaleur0pourunnumérique.NULLsignifiequ
’
aucunedonnéen
’
aétédéfiniepourlavaleurdu
champ.Pourtouteslesméthodesretournantunobjet,laméthoderetourneNULLsilacolonnecontientNULLet0pour
lesprimitivesnumériques.Pourlestypesbooléens,lavaleurretournéeestFALSE
.
Siparexemplenousutilisons
getFloat()
etquelavaleurderetourest0commentsavoirsilacolonnecontientNULLou
0 ?L
’
interfaceResultSetproposealorsuneméthodepermettantd
’
obtenircetteinformation :
publicbooleanwasNull()
.
Cetteméthodeneprendpasd
’
indicationdecolonnepourargument,elleagitsurladernièrecolonnelue.Voiciun
exemplecompletd
’
utilisationdesinterfacesStatementetResultSetpourlirelesclientsdelabasededonnées.
package betaboutique.servlets.client;
import java.io.IOException;
import java.sql.*;
- 9 -© ENI Editions - All rigths reserved
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
Connection connection=null;
Statement st=null;
ResultSet rs=null;
try
{
//chargement du driver
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
//lire les données de la table client
st=connection.createStatement();
rs=st.executeQuery("SELECT * FROM client");
//affichage
while(rs.next())
{
System.out.println("Client :
"+rs.getInt("id_client")+" -"+rs.getString("identifiantclient")
+"-"+rs.getString("motdepasseclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
finally
{
//fermer la connexion
try
{
rs.close();
st.close();
connection.close();
} catch (SQLException e)
{
e.printStackTrace();
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
- 10 - © ENI Editions - All rigths reserved
}
}
Ilfautnoterdanslecodeprécédentquelesressourcessonttoujourslibéréesdansl
’
ordreinversedeleurcréation(rs
,
st
,
connection).Laconnexionestouverteenpremieretferméeendernier,parexemple.Ilfautprendrepourhabitude
dereprendrelecodeetdelefermer.
Lecodeprécédent fonctionne maisiln
’
estpas correct.Si ledéclenchement dela fonction rs.close()provoqueune
erreur,l
’
exceptionestattrapée(SQLException)etleslignesst.close()etconnection.close()neserontpasdéclenchées.
Résultat,laconnexionneserapasfermée.Lasolutionconsisteàlancerunbloctrycatchpourchaqueouverturede
ressource. Ainsi, une exception lancée dans la méthode n
’
empêchera pas l
’
appel de la méthode
close()
de la
connexion.
CetteerreurdefermetureesttrèscouranteenJavamêmeavecd
’
excellentsdéveloppeurs.Raressontles
livresoucoursquitraitentcetteerreuretilesttrèscourantdetrouverlafermeturedesressourcesdansle
mêmebloctrycatch
.
Pourrésoudreceproblème,ilfautentourerchaquefermetured
’
unbloctrycatch.Lecodeestainsiexécutéqu
’
ilyait
uneexceptionoupas.
...
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
finally
{
if(rs!=null)
{
try
{
rs.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
if(st!=null)
{
try
{
st.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
if(connection!=null)
{
try
{
connection.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
...
Lecodeestdésormaiscorrect,parcontre,laprogrammationestlourdeetellenécessitedesenchaînementsdeblocs
try catch. Pour avoir un code plus lisible et moins verbeux, il faut utiliser une classe statique (c
’
est
à
dire que les
instancesdecetteclassemanipulentlesmêmesparamètres)quivacorrectementfermerlesressources.Cetteclasse
nomméeOutilsBaseDeDonneesestplacéedanslepaquetagebetaboutique.boiteoutils
.
- 11 -© ENI Editions - All rigths reserved
Uneclassestatiquen
’
apasbesoind
’
êtreinstanciéepourpouvoirappelersesméthodes.Cetteclassen
’
a
d
’
ailleurspasbesoindeconstructeur.Danscegenredeclasse,plusieurs
’’
instances
’’
manipulentlesmêmes
paramètrespartagés.Ilfaututiliseralorslenomdelaclassedirectementàlaplaced
’
uneinstance.Àladifférence,
unsingletonnepossèdequ
’
uneseuleinstancedelaclasse.Demême,commetouteclassenonstatique,elledoit
obligatoirementêtreinstanciée.
VoicilecodedelaclasseOutilsBaseDeDonneesaveclesméthodesstatiquesgénériques(techniquedesurchargedes
signaturesdeméthodes).
package betaboutique.boiteoutils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class OutilsBaseDeDonnees
{
/***********************************************************
* fermer correctement un resultset
***********************************************************/
public static void fermerConnexion(ResultSet rs)
{
if(rs!=null)
{
try
{
rs.close();
}
catch(Exception e)
{
System.out.println("Erreur lors de la fermeture
d’une connexion dans fermerConnexion(ResultSet)");
}
}
}
/***********************************************************
* fermer correctement un statement
***********************************************************/
public static void fermerConnexion(Statement stmt)
{
if(stmt!=null)
{
try
{
stmt.close();
}
catch(Exception e)
{
System.out.println("Erreur lors de la fermeture
d’une connexion dans fermerConnexion(Statement)");
}
}
}
/***********************************************************
* fermer correctement une connection
***********************************************************/
public static void fermerConnexion(Connection con)
{
if(con!=null)
{
try
{
con.close();
}
catch(Exception e)
{
- 12 - © ENI Editions - All rigths reserved
System.out.println("Erreur lors de la fermeture
d’une connexion dans fermerConnexion(Connection)");
}
}
}
//fin de la classe
}
Lecodeprécédentestmodifiéavecl
’
utilisationdelaclassestatiqueOutilsBaseDeDonneesafindefermercorrectement
lesressources.Lecodeestpluslégeretplussûr.
...
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de
l’établissement de la connexion");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(st);
OutilsBaseDeDonnees.fermerConnexion(connection);
}
}
...
CetteServletpermettantdelisterlesclientsdelaboutiquefonctionnecorrectement.Maintenant,nousallonsajouter
lesenregistrementssuivantsdanslatableclientdelabasededonnéesbetaboutique
.
NousrelançonsànouveaulaServletafindevérifierqueleparcoursduResultSetestopérationnel.
NousallonsmaintenantmodifierlégèrementnotreServletafinderéaliserunerequêtepoursimulerparexemple,une
recherched
’
unclientdepuisl
’
interfaced
’
administration.
...
try
{
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
//client a rechercher
String recherche="mtissot";
//lire les données de la table client
st=connection.createStatement();
rs=st.executeQuery("SELECT * FROM client WHERE
identifiantclient=\""+recherche+"\"");
- 13 -© ENI Editions - All rigths reserved
//affichage
while(rs.next())
{
System.out.println("Client : "+rs.getInt("id_client")
+"-"+rs.getString("identifiantclient")+" -"+rs.getString
("motdepasseclient"));
}
}
...
La Servlet est exécutée sans problème et produit le résultat souhaité. Nous allons réaliser une recherche sur
l
’
identifiant
’’
avion
’’
.
Dansuncasréel,celapourraitcorrespondreàunerechercheprécise,parexempleunedescription
d
’
unarticle(ex :
’’
lechoixdesarmes
’’
).
...
try
{
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
//client a rechercher (les slashes permettent d ’échapper les guillemets)
String recherche="\"avion\"";
//lire les données de la table client
st=connection.createStatement();
rs=st.executeQuery("SELECT * FROM client WHERE
identifiantclient=\""+recherche+"\"");
//affichage
while(rs.next())
{
System.out.println("Client : "+rs.getInt("id_client")
+"-"+rs.getString("identifiantclient")+" -"+rs.getString
("motdepasseclient"));
}
}
...
Cette requête provoque une erreur. Le terme de la recherche contient des guillemets qui empêchent le
fonctionnementdelaméthode.L
’
erreuraffichée,detypejava.sql.SLQExceptionindiqueunproblèmedesyntaxeprès
de
’
avion
’’’’’
.
En effet, JDBC reçoit en fait, la requête incorrecte suivante : SELECT * FROM client WHERE
identifiantclient=
’’’’’
;
.
Unesolutionseraitbiensûr,dedévelopperuneméthodequipermettederéaliserdesremplacementsdecaractères,
ajouter des échappements ou remplacer les quotes comme dans d
’
autres langages (addslashes() de PHP par
exemple), mais il y aura forcément des cas qui ne fonctionneront pas et cela nécessiterait un code lourd à gérer
(échappementdescaractèresavantchaqueinsertionetaprèschaquelecture).Eneffet,aveccegenredeméthodes,
notreidentifiant
’’
avion
’’
seraitalorsmanipulésouslaformesuivante
\
’’
avion\
’’
.
LesrequêtesSQLsontcomplexesàécriresousformedeStringdufaitdumélangedelasyntaxeSQLetdesvariables
du programme. Pour pallier cet inconvénient, il existe une interface PreparedStatement dérivant de Statement qui
permetdeformaterlesrequêtesSQLdefaçonbeaucoupplussimple.
L
’
interfacePreparedStatement hérite deStatement.Toutesles méthodesde cette interface,déclenchent l
’
exception
SQLException de la classe
java.sql
. Les méthodes de cette interface permettent de répondre à des problèmes de
portabilité,decompatibilitédesbases,detempsd
’
exécutionetdeperformance.
Parexemple,lorsd
’
undéveloppementsousleSGBDMySQL,lesguillemetsdoublessontacceptés,maisunebasede
données comme Sybase n
’
accepte que les guillemets simples. D
’
ailleurs, sur les forums du Web la question des
guillements en SQL figure souvent :
’’
Comment puis
je insérer le titre
’’
l
’
alpagueur
’’
dans ma base de données sans
erreur?
’’
.
Uneréponsecourantededéveloppeurnonexpérimentéseraitdedoublerlesguillemetsoud
’
utiliserunantislash.
Nousaurionsalors,
’’
l\
’
alpagueur
’’
sousMySQLmaiscelanefonctionneraitpassousSybaseetPostgreSQL.
Parexemple,sousPostgreSQLnousaurionsalors
’’
l
’’
alpagueur
’’
(
deuxguillemetssimples).Ilestvisiblequecette
technique est lourde en terme de développement pour le respect des compatibilités. JDBC offre une couche
d
’
abstraction supplémentaire pour manipuler les bases de données, chaque constructeur gérant les guillemets et
quotesavecsonpilote.
Enfin, la plupart des bases de données évoluées gardent en mémoire cache les commandes SQL récemment
L’interfacePreparedStatement
- 14 - © ENI Editions - All rigths reserved
exécutées. Si une commande qui est en mémoire cache est utilisée, le résultat sera plus rapide car celle
ci est
compilée et optimisée. Toutefois, il est nécessaire que la commande envoyée corresponde exactement à celle se
trouvantencache.Supposonsl
’
exemplesuivantavecdeuxcommandes:
INSERT INTO client VALUES (’mtissot’,’marc’);
INSERT INTO client VALUES (’adurand’,’alain’);
Cesdeuxcommandessontpresqueidentiquespuisqu
’
ellesnediffèrentqueparlesvaleurslittérales.Dupointdevue
duSGBD,lesdeuxcommandessontquandmêmetotalementdistinctes.Siparcontre,nouspassonsàlabasede
donnéesdesrequêtesidentiquespuisqu
’
après,nousdéclenchonsdescommandescomportantdesvariables,alorsla
mêmerequêteestexécutéedanslesdeuxcas.C
’
estcequepermetl
’
interfacePreparedStatement
.
INSERT INTO client VALUES (?,?);
setString(1, ’mtissot ’);
setString(2, ’marc’);
INSERT INTO client VALUES (?,?);
setString(1, ’adurant ’);
setString(2, ’alain’);
Lacréationd
’
unPreparedStatementestpratiquementidentiqueàcelled
’
unobjetStatement.Ladifférencesesitueau
niveaudelaprécisiondelacommandeàexécuterparleSGBD.Unefoisl
’
objetPreparedStatementcréé,avantquela
commandeSQLpuisseêtreexécutée,lesmarqueurs(?)doiventêtreremplacés.
Les méthodes ci
dessous permettent de fournir des valeurs aux différents paramètres. Les paramètres sont
numérotésàpartirde1,dansl
’
ordred
’
apparitiondessymboles?.Nousretrouvonsalorsdesméthodesdelaforme
setXxx()oùXxxcorrespondautypededonnéesJava.
●setString(indice,chaine) :permetd
’
affecterunechaînedecaractèresauparamètred
’
indice.
●setInt(indice,entier) :permetd
’
affecterunentierauparamètred
’
indice.
●
setBoolean(indice,booléen)
:permetd
’
affecterunbooléenauparamètred
’
indice.
●setFloat(indice,float) :permetd
’
affecterunréelauparamètred
’
indice.
●
...
CommedanslecasdesStatements,laméthodeexecuteQuery()permetd
’
exécuterdesrequêtesSQLdetypeSELECT
(quiretournentunelistededonnées)etlaméthodeexecuteUpdate()permetd
’
exécuterdesrequêtesdemodification
del
’
étatdelabasededonnées(quiretournentlenombred
’
enregistrementsconcernés).LesméthodesgetConnection
()
et
close()
permettentrespectivementd
’
accéderàlaconnexionetdefermercelle
ci.
Nousallonsmodifiernotreexempleprécédent,nonfonctionnel,avecunPreparedStatementcompilé.
package betaboutique.servlets.client;
import java.io.IOException;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
Connection connection=null;
PreparedStatement requete=null;
ResultSet rs=null;
try
{
//chargement du driver
Class.forName("com.mysql.jdbc.Driver");
- 15 -© ENI Editions - All rigths reserved
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
//preparation de la requete
requete=connection.prepareStatement("SELECT * FROM client
WHERE identifiantclient=?");
requete.setString(1, "\"avion\"");
//executer la requete
rs=requete.executeQuery();
//affichage
while(rs.next())
{
System.out.println("Client : "+rs.getInt
("id_client")+" -"+rs.getString("identifiantclient")+" -"+
rs.getString("motdepasseclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(requete);
OutilsBaseDeDonnees.fermerConnexion(connection);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Larequêtefonctionnemaintenantparfaitementavecunechaînedecaractèrescomposéedequotesouguillemets,il
n
’
yaplusdesoucisdecompatibilitéentrelesSGBD.
L
’
instructionsetString(1,"\
’’
avion\"");indiquederemplacerlepremier
’
?
’
danslarequête,parlachaînedecaractères
’’
avion
’’
.
Larequête,beaucouppluslisible,estcompiléeparleSGBD,d
’
oùungaindetempsd
’
exécutionappréciable.
Deplus,larequêtesuivanteestcompilée :SELECT*FROMclientWHEREidentifiantclient=?
,c
’
est
à
direqueleprochain
appel,avecjustelechangementdel
’
identifiantdel
’
utilisateur,seraexécutétrèsrapidement.
Eneffet,Javatoujourssoucieuxdelaportabilité,utiliseunpilotepourchaquebasemaisparcontredéveloppeun
codegénériquequiseraensuiteinterprétéparlepiloteadapté.Ainsi,iln
’
yauraplusbesoind
’
utiliserdesméthodes
spécifiquesàunSGBDetdeséchappementsdechaînesplusoumoinssûrs.
NousallonsmodifiernotreServletafind
’
insérerégalementunnouveauclientlorsdudéclenchementdelaServlet.Cet
- 16 - © ENI Editions - All rigths reserved
exemplepermetdenousmontrerqueleprincipeestidentiquepouruneinsertionetunelecture,seuleslesfonctions
executeQuery()etexecuteUpdate()changent.
...
try
{
//obtention de la connexion
connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/betaboutique",
"root","");
//on insère un nouvel enregistrement
requetea=connection.prepareStatement("INSERT INTO
client(identifiantclient,motdepasseclient) VALUES (?,?)");
requetea.setString(1, "amartin");
requetea.setString(2, "martin");
int resinstrution=requetea.executeUpdate();
System.out.println("Insertion nbligne : "+resinstrution);
//preparation de la requete
requeteb=connection.prepareStatement("SELECT * FROM client WHERE
identifiantclient=?");
requeteb.setString(1, "amartin");
//executer la requete
rs=requeteb.executeQuery();
//affichage
while(rs.next())
{
System.out.println("Client : "+rs.getInt("id_client")+" -
"+rs.getString("identifiantclient")+" -"+rs.getString("motdepasseclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement de la
connexion");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(requeteb);
OutilsBaseDeDonnees.fermerConnexion(requetea);
OutilsBaseDeDonnees.fermerConnexion(connection);
}
...
Lerésultat del
’
instruction executeUpdate() est très important, il permet de réaliser des tests. Dans notre cas par
exemple,silavariableresinstructionestégaleà0celaindiquequ
’
aucuneligneneseramodifiéedanslabasede
données(icicréée),ilyauradoncuneerreur.
Cettetechniqued
’
utilisationd
’
uneconnexion,d
’
unobjetStatementouPreparedStatementetd
’
unobjetResultSetpeut
- 17 -© ENI Editions - All rigths reserved
paraître complexe, mais elle est en fait très naturelle et permet d
’
avoir une certaine rigueur dans le code de
l
’
application.
●Utilisationenpremierd
’
uneconnexionpourdialogueraveclabasededonnées.
●Utilisationensuited
’
unobjetStatementouPreparedStatement(depréférencecelui
ci)pourdialogueravecla
basededonnéesetexécuterdesrequêtes.
●DéclenchementdesrequêtesaveclesfonctionsexecuteQuery()ouexecuteUpdate()
.
●ParcouriraubesoinlerésultatdelarequêteavecunobjetResultSet
.
Pourlasuite,nousutiliseronsdepréférencedesrequêtescompiléesavecl
’
interfacePreparedStatement
.
PourinsérerunevaleurnulleaveclesPreparedStatement,ilnesuffitpasdemettrelavaleurvidepourcemarqueur,
cela cause le lancement d
’
une SQLException par le pilote JDBC. Pour l
’
éviter, nous devons utiliser une des deux
méthodessuivantes :
●setNull(int,int)
●setNull(int,int,String)
Le premier paramètre correspond à la position du marqueur. Le second paramètre est défini dans la classe
java.sql.Types.CetteclassecontientdesconstantespourlestypesJDBC.Siparexemple,noussouhaitonsaffecterla
valeurNULLàunecolonnedetypeString,nousutilisons
java.sql.Types.STRING
.Laversiondelaméthodeavec un
troisièmeparamètredetypeStringestutiliséepourdestypesdéfinisparlesutilisateurs.
Insertiondevaleursnulles
- 18 - © ENI Editions - All rigths reserved
Partagedeconnexions
1.Présentation
Les exemples précédents permettent de présenter l
’
utilisation des bases de données en Java, mais ne sont pas
envisageablesdansunprojetdegrandeenvergure.Eneffet,lecoden
’
estpastrèsclair,uneconnexionestcrééeà
chaqueappel,lesparamètresdeconnexionsontdanslaServletetlescodesdetraitementetd
’
accèsauxdonnéesse
chevauchent.
Nousallonsdoncmodifiernotreexempleetlatableclientafinderéaliserdesaccèspartagés,desrecherchesetdes
affichagesadaptés.
2.Initialisationd
’
uneconnexion
LecodedelaServletServletListeClientModelen
’
estpasconforme.Laconnexiondoitêtreouvertedanslaméthode
init()
etferméedanslaméthodedestroy().LecodeconformeaustandardJavaEEestprésentéci
dessous :
package betaboutique.servlets.client;
import java.io.IOException;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
Connection connection=null;
PreparedStatement requete=null;
ResultSet rs=null;
//initialisation de la connexion
public void init()
{
try
{
//chargement du driver
Class.forName("com.mysql.jdbc.Driver");
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
("jdbc:mysql://localhost:3306/betaboutique","root","");
System.out.println("Connexion opérationnelle");
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
}
- 1 -© ENI Editions - All rigths reserved
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
//preparation de la requete
requete=connection.prepareStatement("SELECT * FROM
client WHERE identifiantclient=?");
requete.setString(1, "amartin");
//executer la requete
rs=requete.executeQuery();
//affichage
while(rs.next())
{
System.out.println("Client : "+rs.getInt
("id_client")+" -"+rs.getString("identifiantclient")+" -"+
rs.getString("motdepasseclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
//fermeture des ressources
public void destroy(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
//fermeture
System.out.println("Connexion fermée");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(requete);
OutilsBaseDeDonnees.fermerConnexion(connection);
}
}
}
CetteServletestopérationnelle,lechargementdesressourcesestgérédanslaméthode
init()
audémarragedela
Servlet,etlalibérationdesressourcesdanslaméthodedestroy()
.
Nous allons légèrement modifier cette Servlet afin de positionner les paramètres de connexion dans le fichier de
configurationdel
’
application(web.xml).Lorsduchangementdelabasededonnéesoudesparamètresdeconnexion,
lecodeneseraainsipasretouchéetfonctionneranormalement.Lefichierweb.xmlestvolontairementsimplifiépour
netraiterquelesexemplesJDBC.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
- 2 - © ENI Editions - All rigths reserved
<web-app>
<!-- paramètres globaux -->
<context -param>
<param -name>pilotejdbc</param -name>
<param -value>com.mysql.jdbc.Driver</param -value>
</context -param>
<context -param>
<param -name>urlconnexionjdbc</param -name>
<param -value>jdbc:mysql://localhost:3306/betaboutique</param -value>
/context -param>
<context -param>
<param -name>utilisateurjdbc</param -name>
<param -value>root</param -value>
</context -param>
<context -param>
<param -name>motdepassejdbc</param -name>
<param -value></param -value>
</context -param>
<!-- servlets -->
<servlet>
<servlet -name>servletlisteclientmodele</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
ListeClientModele</servlet -class>
</servlet>
<servlet -mapping>
<servlet -name>servletlisteclientmodele</servlet -name>
<url -pattern>/listeclient</url -pattern>
</servlet -mapping>
</web-app>
...
//initialisation de la connexion
public void init()
{
ServletContext servletContext=getServletContext();
pilotejdbc=(String)servletContext.getInitParameter
("pilotejdbc");
urlconnexionjdbc=(String)servletContext.getInit
Parameter("urlconnexionjdbc");
utilisateurjdbc=(String)servletContext.getInit
Parameter("utilisateurjdbc");
motdepassejdbc=(String)servletContext.getInit
Parameter("motdepassejdbc");
try
{
//chargement du driver
Class.forName(pilotejdbc);
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
(urlconnexionjdbc,utilisateurjdbc,motdepassejdbc);
System.out.println("Connexion opérationnelle");
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
- 3 -© ENI Editions - All rigths reserved
}
}
...
Nousallonsmodifierlatableclientetajoutertousleschampsnécessairesauxtraitements(lastructurerespectela
maquetteduchapitre :LesServlets).Pourcela,nouspouvonssupprimerlatableactuelleetutiliserlecodesuivantqui
vacréerlanouvellestructureetajouterquelquesenregistrements.
--
-- Structure de la table `client`
--
CREATE TABLE `client` (
`id_client` int(10) unsigned NOT NULL auto_increment,
`identifiantclient` varchar(50) NOT NULL default ’’,
`motdepasseclient` varchar(50) NOT NULL default ’’,
`nomclient` varchar(50) NOT NULL default ’’,
`prenomclient` varchar(50) NOT NULL default ’’,
`emailclient` varchar(255) NOT NULL default ’’,
`telephoneclient` varchar(15) NOT NULL default ’’,
`datenaissanceclient` int(8) NOT NULL default ’0’,
`adresseclient` varchar(255) NOT NULL default ’’,
`villeclient` varchar(255) NOT NULL default ’’,
`codepostalclient` varchar(10) NOT NULL default ’’,
`paysclient` varchar(50) NOT NULL default ’’,
PRIMARY KEY (`id_client`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
--
-- Contenu de la table `client`
--
INSERT INTO `client` VALUES (1, ’amartin’, ’anne2de ’, ’martin’,
’anne’, ’anne.martin@betaboutique.com ’, ’0384465958 ’, 19821209,
’4 rue du haut ’, ’Poligny’, ’39800’, ’france’);
INSERT INTO `client` VALUES (2, ’pdurand’, ’p8fas’,
’durand’, ’pierre’, ’pierre.durand@betaboutique.com ’,
’0348759869 ’, 19760204, ’2 rue du bas ’, ’Lons-le-Saunier’,
’39000’, ’france’);
INSERT INTO `client` VALUES (3, ’dboisson’, ’doia9dz’,
’boisson ’, ’didier’, ’didier.boisson@betaboutique.com ’,
’0345968574 ’, 19681212, ’3 rue de la fontaine ’, ’L’’étoile’,
’39570’, ’france’);
Nous allons modifier notre Servlet ServletListeClientModele afin d
’
afficher la liste complète des clients et créer une
nouvelleServletnomméeServletListeIdentifiantClientModele,quivaaffichertouslesidentifiantsetmotsdepassedes
utilisateurs.Demême,lesServletsafficherontdésormaislesrésultatsdansunepageWebgrâceauPrintWriter
.
...
try
{
//flux de sortie vers le navigateur
PrintWriter out=response.getWriter();
//preparation de la requete
requete=connection.prepareStatement("SELECT * FROM client ORDER BY
client.id_client");
//executer la requete
rs=requete.executeQuery();
//affichage
while(rs.next())
{
out.println("Client : "+rs.getInt("id_client")+" -
"+rs.getString("nomclient")+" -"+rs.getString("prenomclient")+" -"+rs.getString
("emailclient"));
}
- 4 - © ENI Editions - All rigths reserved
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’exécution de la requete");
}
}
...
LecodedelanouvelleServletServletListeIdentifiantClientModeleestprésentéci
dessous.Lefichierweb.xmlcontientla
déclarationdecetteServlet.
...
<!-- servlets -->
<servlet>
<servlet -name>servletlisteclientmodele</servlet -name>
<servlet -class>betaboutique.servlets.client.ServletListe
ClientModele</servlet -class>
</servlet>
<servlet>
<servlet -name>servletlisteidentifiantclientmodele
</servlet -name>
<servlet -class>betaboutique.servlets.client.ServletListe
IdentifiantClientModele</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletlisteclientmodele</servlet -name>
<url -pattern>/listeclient</url -pattern>
</servlet -mapping>
<servlet -mapping>
<servlet -name>servletlisteidentifiantclientmodele
</servlet -name>
<url -pattern>/listeidentifiantclient</url -pattern>
</servlet -mapping>
...
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
@SuppressWarnings("serial")
public class ServletListeIdentifiantClientModele extends
HttpServlet {
Connection connection=null;
PreparedStatement requete=null;
- 5 -© ENI Editions - All rigths reserved
ResultSet rs=null;
//parametres de connexion
String pilotejdbc=null;
String urlconnexionjdbc=null;
String utilisateurjdbc=null;
String motdepassejdbc=null;
//initialisation de la connexion
public void init()
{
ServletContext servletContext=getServletContext();
pilotejdbc=(String)servletContext.getInitParameter
("pilotejdbc");
urlconnexionjdbc=(String)servletContext.getInit
Parameter("urlconnexionjdbc");
utilisateurjdbc=(String)servletContext.getInit
Parameter("utilisateurjdbc");
motdepassejdbc=(String)servletContext.getInit
Parameter("motdepassejdbc");
try
{
//chargement du driver
Class.forName(pilotejdbc);
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
(urlconnexionjdbc,utilisateurjdbc,motdepassejdbc);
System.out.println("Connexion opérationnelle");
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
}
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
//flux de sortie vers le navigateur
PrintWriter out=response.getWriter();
//preparation de la requete
requete=connection.prepareStatement("SELECT
id_client,identifiantclient,motdepasseclient FROM client
ORDER BY client.id_client");
//executer la requete
rs=requete.executeQuery();
//affichage
while(rs.next())
{
out.println("Client : "+rs.getInt
("id_client")+" -"+rs.getString("identifiantclient")+" -"+
- 6 - © ENI Editions - All rigths reserved
rs.getString("motdepasseclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’exécution de la requete");
}
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
//fermeture des ressources
public void destroy(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
try
{
//fermeture
System.out.println("Connexion fermée");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(requete);
OutilsBaseDeDonnees.fermerConnexion(connection);
}
}
}
Cesdeuxexemplessontcorrectsetaffichentlesrésultatssouhaités.Parcontre,lecodeestlourd.Ilfautdéclarerla
méthode
init()
etlaméthodedestroy()danschaqueServletafindegéreruneconnexionàlabasededonnées.Une
solutionseraitdedéclarerlaconnexiondansuneseuledesServlets.Ensuite,ilfaudraitdéclarerunevariabledetype
objetquiseraitinséréedansledescripteurdedéploiement(web.xml)pourlepartage.
Lecodeseraitalorssemblableàcelui
ci :
...
connection = DriverManager.getConnection(urlconnexionjdbc,
utilisateurjdbc,motdepassejdbc);
ServletContext servletContext=servlet.getServletContext();
servletContext.setAttribute("connection",connection);
...
LavariableconnectionestainsipartagéeetpourraêtreréutiliséeparlesautresServletsaveclecodesuivant :
- 7 -© ENI Editions - All rigths reserved
...
ServletContext servletContext=servlet.getServletContext();
connection = (Connection)servletContext.getAttribute("connection");
...
Cettetechniquetrèsutileprésenteunproblème.QuelleestlaServletquidoitlancerlacréationdelaconnexionàla
ressource?Aveccettetechnique,laServletdeconnexiondoitêtrelapremièreappeléeafind
’
initialiserlaconnexion.Il
serait alors possible de déclencher cette Servlet de connexion avec le paramètre
<welcome
file>
du fichier de
configurationdel
’
applicationweb.xml
.
Cette technique bien que fonctionnelle n
’
est pas très pratique et surtout illogique du point de vue de la
programmationdanslecasoùl
’
utilisateurn
’
accèdepasdirectementàlapaged
’
accueildusiteetnedéclenchedonc
paslefichierdéclarédanslabalise
<welcome
file>
.
Nousallonsaborderdansleparagraphesuivantunesolutionàceproblème,lesécouteurs.
- 8 - © ENI Editions - All rigths reserved
Écouteurs/listenersetcycledevie
1.Présentation
Lesévénementsétudiésjusqu
’
iciaffectenttouslaServlet,puisqu
’
ilsappellentdesméthodessurelle.Leconteneur
deServletsgèreplusieursopérationsintéressantes.Siunévénementnousintéresse,nouscréonsuneclassequi
implémentel
’
interfaced
’
écoutedecelui
cietnousl
’
enregistronsaveclemoteurdeServlets.Laspécification2.3des
Servletsprésentequatreinterfacesd
’
écouted
’
événements.Cesinterfacessontrelativementsimplesetaucunene
requiertl
’
implémentationdeplusdetroisméthodes.
2.Utilisation
L
’
interfaceServletContextListenerécoutel
’
undesdeuxévénementsliésaucontexte :lorsdesacréationetlorsdesa
destruction.Cetteinterfacesertàgérerl
’
initialisationetl
’
arrêtdesservicessous
jacents,telsquelesconnexionsà
desbasesdedonnéesàpartagerpartouteslesServletsdel
’
application.
Voiciunexempled
’
uneclassenomméeInitialisationContextprésentedanslepaquetageboiteoutilsquiimplémente
cetteinterface,aveclapremièreméthode
contextInitialized(...)
quiestdéclenchéeauchargementdel
’
application,et
lasecondeméthodecontextDestroyed(...),déclenchéelorsdelasuppressionoul
’
arrêtdel
’
application.
package betaboutique.boiteoutils;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class InitialisationContext implements ServletContextListener{
//action déclenchée lors du chargement du context
public void contextInitialized(ServletContextEvent event)
{
System.out.println(" ----------- Context initialisé ----------- ");
}
//action qui permet de détruire le filtre
public void contextDestroyed(ServletContextEvent event)
{
System.out.println(" ----------- Context détruit ----------- ");
}
//fin de la classe
}
Chaque méthode est appelée lorsque l
’
événement spécifié se produit et un objet d
’
événement est transmis en
paramètre.Ladeuxièmeétapeconsisteàinstallercetteclasseécouteurdansledescripteurdedéploiement.Pour
installerunécouteurauseindel
’
applicationWeb,nousdéclaronscetécouteurendébutdefichierweb.xmlaprèsles
variables<context
param>
.
Chaqueclassed
’
écouteestsimplementdéclaréedansunélément<listener
class/>.Unefoislefichiercorrectement
installé,etlorsquelecontexteestredémarré,l
’
écouteurpropreaucontexteestdéclenché.
...
<!-- écouteurs -->
<listener>
<listener -class>betaboutique.boiteoutils.InitialisationContext
</listener -class>
</listener>
...
L’interfacejavax.servlet.ServletContextListener
- 1 -© ENI Editions - All rigths reserved
Cettetechniqueestdoncidéalepourchargerdesressourcesoudéclencherdestracesoufichiersjournauxlorsdu
chargementdesapplications.
Parmilesautresécouteurs,ilexistel
’
interfacejavax.servlet.ServletContextAttributeListenerquipermetdesavoirquand
les attributs de contexte sont ajoutés, supprimés ou remplacés. Cette interface déclare trois méthodes qui sont
attributeAdded()
,
attributeRemoved()et
attributeReplaced()
.
L
’
interface javax.servlet.http.HttpSessionListener écoute les événements liés à la création et à la destruction des
sessions.CetteinterfacedéclaredeuxméthodesquisontsessionCreated()etsessionDestroyed()etquicorrespondent
àlacréationetdestructiondessessions.
Enfin,laquatrièmeetdernièreinterfaceestjavax.servlet.http.HttpSession.AttributeListenerquiécoutelesévénements
d
’
ajout, de suppression ou de modification d
’
attributs de session. Cette interface déclare alors trois méthodes :
attributeAdded()
,
attributeRemoved() et
attributeReplaced()
, chacune prend comme paramètre une instance de
l
’
événementHttpSessionBindingEvent
.
3.Miseenplaced
’
uneconnexionpartagée
Notreclassebetaboutique.boiteoutils.InitialisationContextseradoncdéclenchéeauchargementdel
’
applicationavantle
premierdéclenchementdeServlet,JSPoupages.NousallonscréernotreconnexionauSGBDdanscetteclasseet
partagercetteconnexiongrâceaudescripteurdedéploiement.Danslaméthodededestructionducontexte,nous
allonsmettrelecodedelibérationdesressources.Nousauronsainsiuneseuleconnexionproprementpartagéepar
l
’
ensembledespagesdel
’
application.
package betaboutique.boiteoutils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class InitialisationContext implements ServletContextListener{
//parametres de connexion
Connection connection=null;
String pilotejdbc=null;
String urlconnexionjdbc=null;
String utilisateurjdbc=null;
String motdepassejdbc=null;
//action déclenchée lors du chargement du context
public void contextInitialized(ServletContextEvent event)
{
System.out.println(" ----------- Contexte initialisé ----------- ");
//lire le contexte
ServletContext servletContext=event.getServletContext();
pilotejdbc=(String)servletContext.getInitParameter
("pilotejdbc");
L’interfacejavax.servlet.ServletContextAttributeListener
L’
interfacejavax.servlet.http.HttpSessionListener
L’
interfacejavax.servlet.http.HttpSession.AttributeListener
- 2 - © ENI Editions - All rigths reserved
urlconnexionjdbc=(String)servletContext.getInit
Parameter("urlconnexionjdbc");
utilisateurjdbc=(String)servletContext.getInit
Parameter("utilisateurjdbc");
motdepassejdbc=(String)servletContext.getInit
Parameter("motdepassejdbc");
try
{
//chargement du driver
Class.forName(pilotejdbc);
System.out.println("Pilote MySQL JDBC chargé");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
System.out.println("Erreur lors du chargmement du pilote");
}
try
{
//obtention de la connexion
connection = DriverManager.getConnection
(urlconnexionjdbc,utilisateurjdbc,motdepassejdbc);
//sauvegarder la connexion dans le context
servletContext.setAttribute("connection",connection);
System.out.println("Connexion opérationnelle");
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’établissement
de la connexion");
}
}
//action qui permet de détruire le filtre
public void contextDestroyed(ServletContextEvent event)
{
System.out.println(" ----------- Contexte détruit ----------- ");
try
{
//fermeture
System.out.println("Connexion fermée");
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(connection);
}
}
//fin de la classe
}
Auchargementdenotreapplicationbetaboutique,laconnexionestalorsdéposéedanslecontexte.Nouspouvons
désormaisl
’
utiliseraveclecodetrèssimplesuivantprésentdanslaméthode
init()
delaServletServletListeClient
.
- 3 -© ENI Editions - All rigths reserved
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
Connection connection=null;
PreparedStatement requete=null;
ResultSet rs=null;
//initialisation de la connexion
public void init()
{
ServletContext servletContext=getServletContext();
connection=(Connection)servletContext.getAttribute("connection");
}
...
}
Il existe une autre solution qui permet de charger une Servlet au démarrage de l
’
application plutôt que lors du
déclenchementdelapremièrerequête(URL).Ceparamètreestplacédanslefichierdeconfigurationdel
’
application
etsenomme
<load
on
startup/>
.
<servlet>
<servlet -name>…</servlet-name>
<servlet -class>…</servlet-class>
<load-on-startup>1</load -on-startup>
</servlet>
LesServletsdoiventêtrechargéesencommençantparl
’
instancedontlenuméroestleplusfaible.Lesinstances
sans numéro ou avec valeurs entières positives peuvent être chargées à tout moment lors du démarrage, sur
décisionduconteneur.
Cettetechniquebienquepratiquen
’
estpastrèsutiliséepourlesaccèsauxbases de données,dufaitdecette
notiondepriorité,etsurtoutdel
’
absencedenotiondefinoudestruction.Eneffet,ilestfacilededéclencherune
ServletàlacréationducontextequivainstancieruneconnexionauSGBDmaislafermeturecorrectedelaconnexion
n
’
estpascertaineaveccettetechnique.
- 4 - © ENI Editions - All rigths reserved
Sourcesdedonnéesetpoolsdeconnexion
1.Présentation
La technique présentée précédemment avec plusieurs connexions, ou une connexion partagée, est fonctionnelle
danslecadred
’
unpetitprojet,maisn
’
estpasadaptéeàdesprojetsdeplusgrandeenvergure.Eneffet,dansles
premiers exemples, la connexion doit être établie avec chaque Servlet d
’
où facilement des problèmes en cas de
grandecharge.Enfin,lasecondeméthodeutiliseunesimpleetuniqueconnexionpartagée,quidevienttrèsvitenon
opérationnelleau
delàdecentaccèssimultanées.
Pourrépondreàcesproblèmes,laspécificationJDBC2.0aintroduitlanotiondesourcesdedonnées.Désormais,
c
’
estlaméthoderecommandéepourétabliruneconnexionàunebasededonnées.L
’
interfaceDataSourcefournitune
architectureplussouplequeDriverManagerpourlacréationetlagestiondesconnexions.UnobjetDataSourcepermet
d
’
accéderàdifférentesbasesdedonnéesenmodifiantlecoded
’
uneapplicationenunseulendroit.Cetobjetpermet
demasquerauprogrammeurlesdétailsdeprogrammationetd
’
accès,afinqu
’
iln
’
aitplusqu
’
àsepréoccuperdel
’
URL,
del
’
hôte,duportetdel
’
utilisateurSGBD.
Unpointessentieldespoolsdeconnexionetqu
’
ilspermettentdecréeràl
’
avanceuncertainnombredeconnexions.
De cette façon, l
’
obtention d
’
une connexion est beaucoup plus rapide. La différence concerne la création et le
recyclage des connexions. La création et la libération d
’
uneconnexion pourchaque Servlet supposentun certain
nombred
’
opérationspeurapides,àsavoirl
’
allocationdesressources,lanégociationdelaconnexionavecleSGBD,
l
’
authentification et enfin la libération des ressources. Une source de données est généralement obtenue en
effectuantunerecherchedansuncontexte.Unmoyend
’
associerunnomàuneressourceestdoncdéfini.
2.JNDI
Javadisposed
’
uneinterfaceneutredeconnexion.CetteinterfacenomméeJNDI(JavaNamingandDirectoryInterface
)
définitunensembledefonctionspermettantd
’
accéderauxservicesderépertoires(noms).Pourutiliseruntelservice,
nosprogrammesdoiventutiliserl
’
APIJNDI.CetteAPIpermetd
’
accéderàdifférentsservicesdenommagedefaçon
uniforme.Ellepermetégalementd
’
organiseretrechercherdesinformationsavecunetechniquedenommage.Cette
interfacepermetdegérerlesconnexionsàdessourcesdedonnéesquipeuventêtredesrépertoires,desbasesde
donnéesmaisaussidesannuaires(DNS,systèmesdefichiers,annuaireLDAP,NIS...).
3.Utilisationd
’
unobjetDataSource
UnobjetDataSourcefournittouteslesméthodespermettantd
’
obteniruneconnexionàunebasededonnéespar
l
’
intermédiaire d
’
un service de nommage JNDI. La méthode principale de l
’
objet DataSource est getConnection()
.
Toutefois,avantqu
’
unclientpuisseobteniruneconnexion,leserveurdoitcréercetobjet,leplacerdanslecontexte
etl
’
associeràunnom.Pourobteniruneconnexionàunesourcededonnées,leclientJDBCn
’
abesoind
’
aucune
informationauniveaudelastructuredelabasededonnées.
L
’
obtentiond
’
unesourcededonnéessedérouleendeuxétapes :
●Ilfauttoutd
’
abordcréerunobjetInitialContextquipermetderechercherdanslecontextedel
’
application.
●Ilfautensuiteappelerlaméthode
lookup()
quipermetderechercherlaconnexiondanslecontexteJNDI.
La chaîne de caractères passée à la méthode
lookup()
est le nom associé à la source de données. Une fois la
connexionobtenue,leclientpeutl
’
utiliserdelamêmefaçonquesielleavaitétéfournieparunDriverManager
.
NousallonsmodifiernotreclasseInitialisationContextafind
’
utiliserunesourcededonnées.Pourcela,nousallons
créeruneinstancedelaclasseInitialContextpoureffectuerunerecherchedelaressourcenomméedanslecontexte.
Le préfixe
java:comp/env
est utilisé pour rechercher une ressource se trouvant sur le même serveur que le
composant.
package betaboutique.boiteoutils;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
- 1 -© ENI Editions - All rigths reserved
public class InitialisationContext implements ServletContextListener{
//action déclenchée lors du chargement du context
public void contextInitialized(ServletContextEvent event)
{
//initaliser le contexte
Context initCtx=null;
try
{
//initaliser le contexte
initCtx=new InitialContext();
if(initCtx==null)
{
throw new Exception ("Il n ’y a pas de contexte !");
}
else
{
System.out.println("Contexte chargé !");
}
//se connecter au JNDI
Context envCtx=(Context) initCtx.lookup
("java:comp/env");
DataSource ds=(DataSource) envCtx.lookup
("jdbc_betaboutiquemysql");
if(ds==null)
{
throw new Exception ("Il n ’y a pas de DataSource !");
}
else
{
System.out.println("DataSource chargée !");
}
//stocker la DataSource dans un attribut nommé
’’datasource ’’ du context
ServletContext servletContext=event.getServletContext();
servletContext.setAttribute("datasource",ds);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
finally
{
try
{
//fermer le contexte
if(initCtx!=null)
{
initCtx.close();
System.out.println("initCtx correctement déchargé !");
}
}
catch(Exception e)
{
System.out.println("Erreur lors de initCtx !");
}
}
}
//action qui permet de détruire le filtre
public void contextDestroyed(ServletContextEvent event)
{
System.out.println(" ----------- Contexte détruit ----------- ");
try
{
//fermeture
System.out.println("DataSource fermée");
}
- 2 - © ENI Editions - All rigths reserved
catch (Exception e)
{
e.printStackTrace();
}
finally
{
}
}
//fin de la classe
}
Pour que cet exemple fonctionne, nous devons créer le descripteur de déploiement adapté. Cette configuration
permetd
’
associerlenomutilisépourlarechercheetlenomdelaressourceJNDI.
Cetteopérationestréaliséeendeuxétapes :
●Tout d
’
abord, dans le descripteur de déploiement, nous devons indiquer la ressource nommée
jdbc_betaboutiquemysqlquiestuneinstancedejavax.sql.DataSource.Cettedéclarationestréaliséeenfinde
fichierweb.xmletpossèdelastructuresuivante :
...
<!-- datasource a la base de donnees -->
<resource -ref>
<description>DB Connection</description>
<res -ref-name>jdbc_betaboutiquemysql</res -ref-name>
<res -type>javax.sql.DataSource</res -type>
<res -auth>Container</res -auth>
</resource -ref>
...
●Ensuite,ilfautparamétrerlaconnexionàlabasededonnéesparlepooldeconnexion.Pourcela,ilfaut
préciserauserveurJavaEElesinformationsdeconnexions.Cetteconfigurationpeutêtrealorsdéclaréedans
lefichier/conf/server.xmldeTomcatoumieux,danslefichierspécifiqueàl
’
application.Dansnotrecas,nous
inséronslecodesuivantdanslefichier/conf/Catalina/localhost/betaboutique.xml
.
<Context path="/betaboutique" reloadable="true"
docBase="E:\PROJETWEB\betaboutique"
workDir="E:\PROJETWEB\betaboutique\work">
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10"/>
</Context>
UneressourceJNDIestdéclaréeaveclapartie
<Resourcename.../>
.Danscettedéclaration,nousremarquonsque
notreprojetpossèdeuneconnexionnomméejdbc_betaboutiquemysqlquiestunesourcededonnées(type),avec
l
’
utilisateur root pour la connexion, sans mot de passe, avec un pilote JDBC MySQL et l
’
utilisation de la base de
données betaboutique. Le paramètre important est maxActive qui permet de gérer le nombre de connexions
actives/ouvertes en même temps. Cette technique permet de déclarer plusieurs pools de connexions, ce qui est
extrêmementpuissant.Eneffet,cettedéclarationpermetd
’
utilisernotrebasededonnéesMySQLpourleprojetet
uneautreconnexionpourlagestiondesutilisateurs(parexemple)avecunautreSGBD.
Une astuce afin de vérifier la syntaxe d
’
un fichier de configuration consiste à ouvrir ce fichier dans un
navigateurWeb.Sil
’
affichageestcorrect,lefichierestalorsopérationneldupointdevuesyntaxique.
Voicici
aprèsunexempledeconfigurationpourl
’
utilisationdedeuxpôlesdeconnexion.
<Context path="/betaboutique" reloadable="true"
docBase="E:\PROJETWEB\betaboutique"
workDir="E:\PROJETWEB\betaboutique\work">
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10"/>
- 3 -© ENI Editions - All rigths reserved
<Resource name="jdbc_betaboutiqueutilisateurnmysql"
auth="Container" type="javax.sql.DataSource" username="jerome"
password="jerome" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutiqueutilisateur"
maxActive="20" maxIdle="10"/>
</Context>
Les fichiers de l
’
application betaboutique.xml (ou server.xml) et web.xml permettent de définir les paramètres
nécessaires afin d
’
habiliter le serveur Java EE à établir une connexion avec la base de données. Dans le fichier
betaboutique.xmlla sourcededonnées jdbc_betaboutiquemysql est déclarée et dans le fichier de configuration de
l
’
applicationweb.xml,lelienaveccetteconnexionestréalisé.
Lesdifférentsparamètressontprésentésdanscetteliste :
●driverClassName :nomqualifiédelaclassedupiloteJDBC.
●maxActive :nombremaximaldeconnexionsactivesdanslepool.
●maxIdle :nombremaximaldeconnexionsenattentedanslepool.
●maxWait :délaimaximal,enmillisecondesd
’
attented
’
uneconnexion.Au
delàdecedélai,siaucuneconnexion
n
’
estdisponible,uneexceptionestlancée.
●user :nomdel
’
utilisateurpourlabasededonnées.
●password :motdepassedel
’
utilisateurpourl
’
accèsàlabasededonnées.
●url :URLd
’
accèsàlabasededonnées.
●validationQuery :requêtedetestenvoyéeàlabasededonnéespours
’
assurerqu
’
uneconnexionestvalide.
LorsqueTomcatdémarre,ilanalyselefichierserver.xmlettouslesfichiersdeconfiguration,etcréeunesourcede
donnéesenfonctiondesparamètrescontenusdansl
’
élémentResourceParams.Illametensuiteàladispositiondes
clientsparl
’
intermédiaired
’
uneinterfaceJNDI.
4.Miseenplace
Nous allons maintenant utiliser un pool de connexion pour notre projet betaboutique. Le
fichier/conf/Catalina/host/betaboutique.xmlpossèdelasyntaxesuivante :
<Context path="/betaboutique" reloadable="true"
docBase="E:\PROJETWEB\betaboutique"
workDir="E:\PROJETWEB\betaboutique\work">
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"/>
</Context>
Lefichierweb.xmlpossèdelasyntaxesimplifiéesuivante :
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- écouteurs -->
<listener>
<listener -class>betaboutique.boiteoutils.Initialisation
Context</listener -class>
</listener>
<!-- servlets -->
<servlet>
<servlet -name>servletlisteclientmodele</servlet -name>
- 4 - © ENI Editions - All rigths reserved
<servlet -class>betaboutique.servlets.client.Servlet
ListeClientModele</servlet -class>
</servlet>
<servlet>
<servlet -name>servletlisteidentifiantclientmodele
</servlet -name>
<servlet -class>betaboutique.servlets.client.Servlet
ListeIdentifiantClientModele</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletlisteclientmodele</servlet -name>
<url -pattern>/listeclient</url -pattern>
</servlet -mapping>
<servlet -mapping>
<servlet -name>servletlisteidentifiantclientmodele
</servlet -name>
<url -pattern>/listeidentifiantclient</url -pattern>
</servlet -mapping>
<!-- datasource a la base de donnees -->
<resource -ref>
<description>DB Connection</description>
<res -ref-name>jdbc_betaboutiquemysql</res -ref-name>
<res -type>javax.sql.DataSource</res -type>
<res -auth>Container</res -auth>
</resource -ref>
</web-app>
Enfin,laServletServletListeClientModelepossèdelecodeci
dessous.Désormaisuneseuleligneestnécessairepour
obteniruneconnexionàlasourcededonnées.
package betaboutique.servlets.client;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
@SuppressWarnings("serial")
public class ServletListeClientModele extends HttpServlet {
//variables de la classe
DataSource ds=null;
Connection connection=null;
PreparedStatement requete=null;
ResultSet rs=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
try
{
//ouvrir une connexion
connection=ds.getConnection();
//flux de sortie vers le navigateur
PrintWriter out=response.getWriter();
//preparation de la requete
requete=connection.prepareStatement("SELECT * FROM
client ORDER BY client.id_client");
//executer la requete
rs=requete.executeQuery();
- 5 -© ENI Editions - All rigths reserved
//affichage
while(rs.next())
{
out.println("Client : "+rs.getInt
("id_client")+" -"+rs.getString("nomclient")+" -"+
rs.getString("prenomclient")+" -"+rs.getString("emailclient"));
}
}
catch (SQLException e)
{
e.printStackTrace();
System.out.println("Erreur lors de l ’exécution de la requete");
}
finally
{
OutilsBaseDeDonnees.fermerConnexion(rs);
OutilsBaseDeDonnees.fermerConnexion(requete);
OutilsBaseDeDonnees.fermerConnexion(connection);
ds=null;
}
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
LecodeJDBCestidentiqueauxexemplesprécédents.Leclientnesaitpasqu
’
ilobtientuneconnexionprovenantd
’
un
pool. Il continue d
’
utiliser directement la même interfaceConnection. La méthode
close()
de l
’
objet Connectionest
toujoursappelée,maisellenefermepasphysiquementlaconnexion,ellelaremetàladispositiondupool.Dupoint
devueduclient,iln
’
yapasdedifférenceentrelesconnexionsprovenantd
’
unpooletcellescrééesdirectement.
CettetechniquepeutêtreemployéedansdesJSPoudesServlets.
Cettetechniqueestplussimple,fiable,etadaptéeauxprojetsdegrandestailles.Eneffet,ilfautdéclarerlasource
dedonnéesdanslefichierdeconfigurationdel
’
application(server.xmlounomapplication.xml),préciserdanslefichier
de description de l
’
application (web.xml) le lien vers cette ressource, et enfin réaliser une classe qui gère cette
connexionindépendammentduSGBDutilisé.
Ainsi,il n
’
yaplusqu
’
àsesoucierdelalibérationdesressources,touteslesbasesdedonnéescompatiblesJDBC
peuventêtreutiliséessanstoucheraucodesourceetilestpossibled
’
optimiserlesaccèsdefaçontrèspréciseavec
les attributs de la balise
<Resource.../>
(nombre de connexions actives, temps d
’
attente maximal, nombre de
connexions en attente...). Toutes ces variables pourront ainsi être adaptées en fonction du projet, serveur et
populationdusitesansretoucheraucode.
- 6 - © ENI Editions - All rigths reserved
BasesdedonnéesetMVC
1.Présentation
Le design pattern ou modèle MVC (Modèle Vue Contrôleur) est très utilisé dans le domaine de l
’
informatique et
notamment avec les technologies Java EE. Nous avons étudié jusqu
’
à maintenant la partie Contrôleur avec les
ServletsetlestraitementsassociésetlapartieVueavectouteslespagesJSPutiliséespourl
’
affichage.
LapartieModèleconcernelapersistancedesdonnées.Nousutilisonsunebasededonnéespoursauvegarderles
données.Lesnouveauxexemplessontbaséssurlesprécédents(listedesclientsetlistedesidentifiantsclients)et
vontêtreadaptéspourcorrespondreaumodèleMVC.NousallonsdévelopperuneServletnomméeServletListeArticles
quipermetd
’
afficherlalistedesarticlesdelabasededonnées.
Dupointdevuedel
’
architecture,nousauronstroispaquetages :
●betaboutique.boiteoutils
.
●betaboutique.modeles
.
●betaboutique.servlets
.
LepremierpaquetagecorrespondàlaconnexionauSGBD,auxclassesstatiquespourlesressourcesetbasesde
données,auxJavaBeans...Lesecondpaquetagecorrespondauxclassesmodèlesquipossèdentuniquementdes
accèsauxdonnées.LetroisièmeetdernierpaquetagecorrespondauxServlets(Contrôleur)pourletraitementdes
requêtesclients.
Nous allons volontairement supprimer les autres paquetages et classes du projet betaboutique pour commencer
l
’
applicationdepersistancedesdonnéesavecunprojetsimpleetclair.
LeprincipedefonctionnementdumodèleMVCpourleprojetbetaboutiqueestexpliquédansleschémaci
dessous :
LeclientdéclenchelaServletdelistedesarticles.CetteServletquiestlapartieContrôleurdumodèleMVCréalise
destraitementssimplesetdéclenchelemodèle
ArticleModele
quigèrelapersistancedesdonnéesdesarticles.Les
articlessontlusdanslabasededonnéesetretournéssouslaformed
’
unecollectiond
’
objetsJavaBean
Article
.Cette
collectionestensuiterenvoyéeàlavueadaptéelistearticles.jsppourl
’
affichagedesenregistrements.
2.Modèleconceptueldesdonnéesetbasededonnées
LemodèleconceptueldesdonnéespropreàlaméthodologieMERISEestprésentédansleschémaci
dessous.Il
comprend la table
article
avec sa clé primaire et ses différents champs. Ce modèle conceptuel de données est
volontairementsimplifiémaispermetlamiseenplaced
’
unprojetsanserreur.
Article
: le champ
datearticle
correspond à la date d
’
insertion de l
’
article dans la base de données. Le champ
photoarticlecorrespondauchemindel
’
imagegrandformatdelapochetteduDVD.Lechampvignettearticlecorrespond
auchemindel
’
imagepetitformat(ouvignette)duDVD.Enfin,l
’
attributetatarticleestpositionnépardéfautà0dans
labasededonnéesetpermetdemettreenligneoupasl
’
articleconcerné.
Catégorie:unarticleestreliéàuneetuneseulecatégorie.Unecatégorieestreprésentéeparsonnom.
Commande
:unecommandecorrespondàaumoinsunarticleouplusieurs.Ladatedelacommandeetletotaldela
commandesontconservés.Leprixdel
’
articleaumomentdelacommandeestégalementconservépourl
’
affichagede
lafactureexacte.
- 1 -© ENI Editions - All rigths reserved
Client
:unclientpeutpasserplusieurscommandes,maisilpeuttrèsbiennerienachetersurlesite.Unclientpeut
égalementnotertouslesarticlesdusiteounejamaisnoterunarticle.Parcontre,unclientnepeutdonnerqu
’
une
seulenotepararticle.
Administrateur/Rôle
:unadministrateurdelaplate
formebetaboutiquedisposed
’
unidentifiantetd
’
unmotdepasse
etpossèdeunrôleuniquepourl
’
accès.Lerôleestutiliséuniquementpourl
’
authentificationetlagestiondesdroits.
Latable"Roles"estmodéliséedelasortepourl
’
utilisationaveclesREALMSdansunprochainparagraphe.Cette
table doit respecter la spécification des REALMS. La clé primaire de la table "roles" est composée des attributs
"nomadministrateur"et"role"pourpermettreàunutilisateurd
’
avoirplusieursrôles.
Latable
article
possèdelastructuresuivante :
LapremièreétapeconsisteàcréerleJavaBean
Article
adaptéaveclamêmestructurequelatable
article
.
package betaboutique.boiteoutils;
@SuppressWarnings("serial")
public class Article implements java.io.Serializable {
private String id_article=null;
private String nomarticle=null;
private String descriptionarticle=null;
private String prixarticle=null;
private String datearticle=null;
private String photoarticle=null;
private String vignettearticle=null;
private String etatarticle=null;
private String id_categoriearticle=null;
//Constructeur par défaut (sans paramètre)
public Article()
{
}
public String getDatearticle() {
- 2 - © ENI Editions - All rigths reserved
return datearticle;
}
public void setDatearticle(String datearticle) {
this.datearticle = datearticle;
}
public String getDescriptionarticle() {
return descriptionarticle;
}
public void setDescriptionarticle(String descriptionarticle) {
this.descriptionarticle = descriptionarticle;
}
public String getEtatarticle() {
return etatarticle;
}
public void setEtatarticle(String etatarticle) {
this.etatarticle = etatarticle;
}
public String getId_article() {
return id_article;
}
public void setId_article(String id_article) {
this.id_article = id_article;
}
public String getId_categoriearticle() {
return id_categoriearticle;
}
public void setId_categoriearticle(String id_categoriearticle) {
this.id_categoriearticle = id_categoriearticle;
}
public String getNomarticle() {
return nomarticle;
}
public void setNomarticle(String nomarticle) {
this.nomarticle = nomarticle;
}
public String getPhotoarticle() {
return photoarticle;
}
public void setPhotoarticle(String photoarticle) {
this.photoarticle = photoarticle;
}
public String getPrixarticle() {
return prixarticle;
}
public void setPrixarticle(String prixarticle) {
this.prixarticle = prixarticle;
}
public String getVignettearticle() {
return vignettearticle;
}
public void setVignettearticle(String vignettearticle) {
this.vignettearticle = vignettearticle;
}
}
Danscetexemple,leJavaBean
Article
n
’
utilisequedestypesStringpourlesparamètres.Cettetechniquede
programmationpermetjusted
’
éviterdesconversionsetdecoderplusrapidement.Dansuncasréel,ilserait
préférabled
’
utiliserdestypesencorrespondanceavecceuxdelabasededonnées(ex :
prixarticle
estunréel).
Le code de la Servlet contrôleur est désormais très simple, il utilise uniquement le modèle pour récupérer des
données.
package betaboutique.servlets;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
- 3 -© ENI Editions - All rigths reserved
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.modeles.ArticleModele;
@SuppressWarnings("serial")
public class ServletListeArticles extends HttpServlet {
//variables de la classe
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le context de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
//créer le modèle
ArticleModele articlemodele=new ArticleModele(ds);
//redonner la datasource
this.ds=null;
//retourner la liste des articles
ArrayList listearticles=(ArrayList)articlemodele.ListeArticle();
request.setAttribute("listearticles",listearticles);
//retourner sur la page d ’affichage des articles
getServletContext().getRequestDispatcher
("/vues/article/listearticles.jsp").forward(request, response);
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Laclassemodèle
ArticleModele
possèdeuneseulefonctionquipermetderetournerlacollectiond
’
objetsJavaBean
présentsdanslabasededonnées.
package betaboutique.modeles;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import betaboutique.boiteoutils.Article;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
import java.util.ArrayList;
public class ArticleModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//liste des objets
ArrayList<Article> listearticle=new ArrayList<Article>();
/***********************************************************
* constructeur
***********************************************************/
public ArticleModele(DataSource ds)
{
//récupérer la DataSource de la servlet
this.ds=ds;
}
- 4 - © ENI Editions - All rigths reserved
/***********************************************************
* liste complète des articles
***********************************************************/
public ArrayList ListeArticle()
{
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM
article ORDER BY article.nomarticle");
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//stocker tous les articles dans une liste
while(rs.next())
{
//créer un objet article
Article article=new Article();
//renseigner l ’objet article avec ses accesseurs
if(rs.getString("id_article")==null)
article.setId_article("0");
else article.setId_article(rs.getString
("id_article"));
if(rs.getString("nomarticle")==null)
article.setNomarticle("");
else article.setNomarticle(rs.getString
("nomarticle"));
if(rs.getString("descriptionarticle")==null)
article.setDescriptionarticle("")
else article.setDescriptionarticle(rs.getString
("descriptionarticle"));
if(rs.getString("prixarticle")==null)
article.setPrixarticle("0");
else article.setPrixarticle(rs.getString
("prixarticle"));
if(rs.getString("datearticle")==null)
article.setDatearticle("0");
else article.setDatearticle(rs.getString
("datearticle"));
if(rs.getString("photoarticle")==null)
article.setPhotoarticle("");
else article.setPhotoarticle(rs.getString
("photoarticle"));
if(rs.getString("vignettearticle")==null)
article.setVignettearticle("");
else article.setVignettearticle(rs.getString
("vignettearticle"));
if(rs.getString("etatarticle")==null)
article.setEtatarticle("0");
else article.setEtatarticle(rs.getString
("etatarticle"));
//stocker l ’objet article dans la liste desarticles
listearticle.add((Article)article);
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction ListeArticle");
}
- 5 -© ENI Editions - All rigths reserved
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction ListeArticle");
}
}
//retourner la liste des articles
return listearticle;
}
//fin de la classe
}
NousallonsdésormaisdéclarernotreServletcontrôleurdanslefichierdeconfigurationdel
’
application.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- url de l ’application -->
<context-param>
<param -name>urlapplication</param -name>
<param -value>http://localhost:8080/betaboutiquemvc/</param -value>
</context -param>
<!-- écouteurs -->
<listener>
<listener -class>betaboutique.boiteoutils.Initialisation
Context</listener -class>
</listener>
<!-- servlets -->
<servlet>
<servlet -name>servletlistearticles</servlet -name>
<servlet -class>betaboutique.servlets.ServletListe
Articles</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletlistearticles</servlet -name>
<url -pattern>/listearticles</url -pattern>
</servlet -mapping>
<!-- datasource a la base de donnees -->
<resource -ref>
<description>DB Connection</description>
<res -ref-name>jdbc_betaboutiquemysql</res -ref-name>
<res -type>javax.sql.DataSource</res -type>
<res -auth>Container</res -auth>
</resource -ref>
</web-app>
Nousretrouvonslaconnexionàlabasededonnéesaveclelistenerpourdéclencherlaconnexionauchargementdu
contexte,ladéclarationdelaServletcontrôleuretunevariableglobaleutiliséepourréférencerl
’
URLdel
’
application
(pourlesimages,cheminsdivers,feuillesdestyleetliens).
Ilneresteplusqu
’
àdévelopperlapagelistearticles.jsp.Cettepagetrèssimplenegèrequel
’
affichagedelacollection
d
’
objets.NousremarquonsqueleprincipeMVCrequiertbeaucoupdefichiersmaislecodeestbiendécoupéettrès
simple(traitement,gestiondelapersistancedesdonnéesetaffichage).
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<%@page import="betaboutique.boiteoutils.Article;"%>
- 6 - © ENI Editions - All rigths reserved
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>LISTE DES ARTICLES</title>
</head>
<body>
<%
//context de l ’application
String urlapplication=getServletContext().getInitParameter("urlapplication");
//récupérer les articles dans la requête
ArrayList listearticles=(ArrayList)request.getAttribute("listearticles");
%>
<h2>Liste des articles de la boutique</h2>
<table border="1" cellspacing="0" cellpadding="0">
<tr><td>Id article</td><td>Nom</td><td>Description</td>
<td>Prix</td><td>Date</td><td>Vignette</td></tr>
<%
for(int i=0;i<listearticles.size();i++)
{
Article article=(Article)listearticles.get(i);
out.println("<tr>");
out.println("<td>"+article.getId_article()+"</td>");
out.println("<td>"+article.getNomarticle()+"</td>");
out.println("<td>"+article.getDescriptionarticle()+"</td>");
out.println("<td>"+article.getPrixarticle()+" Euros</td>");
out.println("<td>"+article.getDatearticle()+"</td>");
out.println("<td><img src=\""+urlapplication+"/
imgarticles/"+article.getVignettearticle()+"\"/></td>");
out.println("</tr>");
}
%>
</table>
</body>
</html>
L
’
arborescenceduprojetàcetteétapeestlasuivante :
- 7 -© ENI Editions - All rigths reserved
Nousdéclenchonsalorsl
’
URL :http://localhost:8080/betaboutiquemvc/listearticles
Le résultat produit est correct. Nous allons cependant apporter quelques améliorations à la classe modèle afin
d
’
affichercorrectement ladate, lacatégorie associéeà l
’
article et gérer l
’
état de l
’
article(enligne oupas)et les
recherchessurlenomouladescriptiondel
’
article.
3.Optimisations
a.Informationsliéesetmiseenforme
Notreservicedelistedesarticlesestdésormaisfonctionnelmaisilmanquequelquesoptimisationsquipermettent
deproposerunserviceprofessionnel.Dansnotrecas,nousdevonsd
’
aborddévelopperunefonctionpourmettrela
dateauformatfrançais(aaaammjjdevientjj/mm/aaaa).
Il manque également l
’
affichage de la catégorie de l
’
article. Cette information est liée à la table
categorie
par
l
’
intermédiairedelacléexterne
id_categorie
.Cesfonctionnalitéssontutiliséesdanslamajoritédesprojetsquece
soientdescatégoriesassociées,desgestionsdedates,ledécoupagedechaînesdecaractères...
CesoptimisationssontliéesauxdonnéesAVANTouAPRESinsertiondanslabasededonnées.Demême,ellessont
- 8 - © ENI Editions - All rigths reserved
déclenchéesparlaplupartdesclassesmodèles.Afind
’
utiliserunsystèmesoupleetfacilementmaintenable,nous
allonsdévelopperuneclassenommée
Modele
quiseraplacéeenhautdel
’
arbred
’
héritagedesmodèles.Ainsila
classe modèle
ArticleModele
hérite de la classe principale
Modele
qui contient les fonctionnalités générales et
communes(principed
’
héritage).
Nousallonsdoncajouterpourlemomentdeuxfonctionsàlaclasse
Modele
àsavoir :miseEnFormeDate()quipermet
demettreladateauformatfrançaisetlafonction
getNomCategorieArticle()
quipermetderetournerlenomd
’
une
catégorieenfonctiondesonidentifiant.Danscetteclasse,nousutilisonsunautrenomdeconnexion(connection1à
laplacede connection)etégalementunautrenomdeResultSet (rs1)afind
’
éviter d
’
éventuelsconflitsentreles
objetsConnectionetResultSetdelaclassefille(fermetureouaccèssimultanés)etceuxdelaclassemère.
Ledéclenchement du constructeur delaclasse mèrese feraavec laméthode super()dansleconstructeur des
classesfilles,etl
’
utilisationdesméthodesdelaclassemèreseferaégalementaveclemotclésuper.Nousallons
modifierleJavaBean
Article
afin d
’
ajouterunattributpourlenomde la catégorie.Unedernièremodificationest
réalisée dans la requête de liste des articles afin d
’
afficher uniquement les articles validés en administration
(
etatarticle=1
).
package betaboutique.boiteoutils;
@SuppressWarnings("serial")
public class Article implements java.io.Serializable {
...
private String nomcategoriearticle=null;
...
package betaboutique.modeles;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
public class Modele
{
//variables de classe
DataSource ds=null;
Connection connection1=null;
ResultSet rs1=null;
/***********************************************************
* constructeur
***********************************************************/
public Modele(DataSource ds)
{
//récupérer la DataSource de la servlet
this.ds=ds;
}
/***********************************************************
* mise en forme de la date (aaaammjj -> jj/mm/aaaa)
***********************************************************/
- 9 -© ENI Editions - All rigths reserved
public String miseEnFormeDate(String date)
{
if(date.length()>=4)
{
String jour=date.substring((date.length() -2),date.length());
String mois=date.substring((date.length() -4),(date.length() -2));
String annee=date.substring(0,4);
date=jour+"/"+mois+"/"+annee;
}
//retourner la date
return date;
}
/***********************************************************
* récupérer le nom de la catégorie de l ’article
***********************************************************/
public String getNomCategorieArticle(String id_categorie)
{
String nomcategorie=null;
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection1=ds.getConnection();
requete=connection1.prepareStatement("SELECT * FROM
categorie WHERE categorie.id_categorie=?");
requete.setString(1,(String)id_categorie);
rs1=requete.executeQuery();
//exécuter la requête
if(rs1!=null)
{
//stocker tous les types de médias dans une liste
if(rs1.next())
{
if(rs1.getString("nomcategorie")==null)nomcategorie="";
else nomcategorie=rs1.getString("nomcategorie");
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe Modele.java
fonction getNomCategorieArticle");
}
finally
{
try
{
//fermer la connexion
if(rs1!=null)OutilsBaseDeDonnees.fermerConnexion(rs1);
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection1!=null)OutilsBaseDeDonnees.fermerConnexion
(connection1);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
Modele.java fonction getNomCategorieArticle");
}
}
//retourner le nom
return nomcategorie;
}
//fin de la classe
}
- 10 - © ENI Editions - All rigths reserved
...
public class ArticleModele extends Modele
{
...
if(rs.getString("datearticle")==null)article.setDatearticle("0");
else article.setDatearticle(super.miseEnFormeDate(rs.getString
("datearticle")));
if(rs.getString("id_categorie")==null)article.setNomcategoriearticle("");
else
article.setNomcategoriearticle(super.getNomCategorieArticle(rs.getString
("id_categorie")));
...
//fin de la classe
}
<%@ page language="java" contentType="text/html;
charset=ISO -8859-1" pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<%@page import="betaboutique.boiteoutils.Article;"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html;
charset=ISO -8859-1"><title>LISTE DES ARTICLES</title>
</head>
<body>
<%
//context de l ’application
String urlapplication=getServletContext().getInitParameter
("urlapplication");
//récupérer les articles dans la requête
ArrayList listearticles=(ArrayList)request.getAttribute
("listearticles");
%>
<h2>Liste des articles de la boutique</h2>
<table border="1" cellspacing="0" cellpadding="0">
<tr><td>Id article</td><td>Nom</td><td>Description</td>
<td>Prix</td><td>Date</td><td>Vignette</td>
<td>Catégorie</td></tr>
<%
for(int i=0;i<listearticles.size();i++)
{
Article article=(Article)listearticles.get(i);
out.println("<tr>");
out.println("<td>"+article.getId_article()+"</td>");
out.println("<td>"+article.getNomarticle()+"</td>");
out.println("<td>"+article.getDescriptionarticle()+"</td>");
out.println("<td>"+article.getPrixarticle()+" Euros</td>");
out.println("<td>"+article.getDatearticle()+"</td>");
out.println("<td><img src=\""+urlapplication+"
/imgarticles/"+article.getVignettearticle()+"\"/></td>");
out.println("<td>"+article.getNomcategoriearticle()+"</td>");
out.println("</tr>");
}
%>
</table>
</body>
</html>
- 11 -© ENI Editions - All rigths reserved
b.Gestiondesrecherches
NousallonsaméliorerleservicedelistedesarticlesafinderéaliserdesrecherchessurlenomduDVDetsursa
description.Pourcela,nousajoutonsunpetitformulaireHTMLendébutdefichieravecunchampdesaisieetun
boutondevalidation.LaServletcontrôleurestlégèrementmodifiéepourlireetrenvoyerlarechercheeffectuée.
Enfin,larequêteSQLdelaclassemodèleestmodifiéeavecl
’
instructionSQLLIKE
.
...
<%@ page language="java" contentType="text/html; charset=ISO -8859-1"
pageEncoding="ISO -8859-1"%>
<%@ page import="java.util.ArrayList" %>
<%@page import="betaboutique.boiteoutils.Article;"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http -equiv="Content -Type" content="text/html; charset=ISO -8859-1">
<title>LISTE DES ARTICLES</title>
</head>
<body>
<%
//context de l ’application
String urlapplication=getServletContext().getInitParameter
("urlapplication");
//récupérer les articles dans la requête
ArrayList listearticles=(ArrayList)request.getAttribute("listearticles");
//récupérer la recherche de l ’utilisateur
String recherche=(String)request.getAttribute("recherche");
%>
<h2><a href="listearticles">Liste des articles de la boutique</a></h2>
<form action="listearticles" methode="post">
<p><input type="text" name="recherche" id="recherche" size="30"/><input
type="submit" value="Rechercher"/></p>
<%
//afficher la recherche si présente
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
out.println("<b>Votre recherche : "+recherche+"</b><br/><br/>");
}
%>
</form>
...
package betaboutique.servlets;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.modeles.ArticleModele;
@SuppressWarnings("serial")
public class ServletListeArticles extends HttpServlet {
//variables de la classe
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
//créer le modèle
- 12 - © ENI Editions - All rigths reserved
ArticleModele articlemodele=new ArticleModele(ds);
//redonner la datasource
this.ds=null;
//recherche
String recherche=(String)request.getParameter("recherche");
//retourner la liste des articles
ArrayList listearticles=(ArrayList)
articlemodele.ListeArticle(recherche);
request.setAttribute("listearticles",listearticles);
request.setAttribute("recherche",recherche);
//retourner sur la page d ’affichage des articles
getServletContext().getRequestDispatcher
("/vues/article/listearticles.jsp").forward(request, response);
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
...
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
String requete="SELECT * FROM article WHERE etatarticle=1";
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requete+=" AND (nomarticle LIKE ? OR descriptionarticle LIKE ?)";
}
//preparer la requete
requetea=connection.prepareStatement(requete);
//recherche
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requetea.setString(1,(String)"%"+recherche+"%");
requetea.setString(2,(String)"%"+recherche+"%");
}
//exécuter la requete
rs=requetea.executeQuery();
...
Larechercheestdésormaisfonctionnelle.Nouspouvonsessayeraveclachaîne
’’
balance
’’
quidanscecasvaporter
surlenomdel
’
articleouaveclachaîne
’’
nathaliebaye
’’
quivaportersurladescription(acteurs).Cetterequêteest
opérationnelle mais très lente lors de l
’
utilisation de nombreux enregistrements et surtout sur des champs de
grandetaillecommeladescription.Nouspouvonsoptimisercesystèmeavecl
’
utilisationd
’
expressionsrégulièresen
SQL.
...
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
String requete="SELECT * FROM article WHERE etatarticle=1";
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requete+=" AND (nomarticle REGEXP ? OR
descriptionarticle REGEXP ?)";
}
//preparer la requete
requetea=connection.prepareStatement(requete);
//recherche
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requetea.setString(1,(String)"("+recherche+")");
- 13 -© ENI Editions - All rigths reserved
requetea.setString(2,(String)"("+recherche+")");
}
//exécuter la requete
rs=requetea.executeQuery();
...
Il existe une dernière technique de recherche plus poussée qui permet de réaliser des affichages précis à la
manièredesmoteursderecherches.CetteinstructionSQLnommée
MATCH
permetdefairedespondérations(ajout
d
’
opérateurs).Parcontre,ilestparfoisnécessairederéaliserunindexFULLTEXTsurleschampsderecherchepour
améliorerletempsd
’
exécution.Dansnotreexemple,nousnemodifionsàchaquefoisquelaclassemodèle(d
’
où
l
’
intérêtduMVC).Nouspouvonsainsiapporterdesaméliorationsausitesanscoderànouveaul
’
ensembled
’
une
fonctionnalité.
Voicilecodemodifiédelarequêteaveclaclause
MATCH
.
...
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
String requete="SELECT * FROM article WHERE etatarticle=1";
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requete+=" AND (MATCH (nomarticle) AGAINST (? IN BOOLEAN MODE)
OR MATCH (descriptionarticle) AGAINST (? INBOOLEAN MODE))";
}
//preparer la requete
requetea=connection.prepareStatement(requete);
//recherche
if(recherche!=null && !recherche.equalsIgnoreCase(""))
{
requetea.setString(1,(String)recherche);
requetea.setString(2,(String)recherche);
}
//exécuter la requete
rs=requetea.executeQuery();
...
Avec cette dernière requête, nos recherches sont très puissantes. Il est possible d
’
utiliser l
’
opérateur + pour
indiquerquelemotàlasuitedusignedoitêtreprésentdansunelignedesenregistrements.L
’
opérateur
indique
quelemotàlasuitedusignenedoitpasêtreprésentdansunelignedesenregistrements.L
’
opérateur*permet
defairedesrecherchessurdesportionsdemots.L
’
opérateur
’’
(
guillemets)permetdefairedesrecherchesexactes
dansunchamp(ex :citationentreguillemets).
Nouspouvonstestercesopérateursaveclesrecherchessuivantes :
●nathaliebaye :touslesarticlesavecl
’
actriceNathalieBayedansletitreouladescriptionserontretrouvés.
●
baye
:touslesarticlesavecl
’
actriceBayedansletitreouladescriptionserontretrouvés.
●
baye
léotard
:lesarticlescontenantNathalieBayemaispasPhilippeLéotardserontretrouvés.
●
pier*
:lesarticlescontenantletermepier(PierrePalmade,PierreDesprogesetJean
PierreMelville)seront
retrouvés.
Ces exemples permettent de mettre en évidence l
’
utilisation d
’
une classe modèle et de requêtes SQL
évoluéesparrapportàunsystèmedepersistance(Hibernateouautre).Eneffet,lecodeestparfoisplus
longàécriremaislesrequêtessontoptimiséesetutilisentTOUTESlesinstructionsetlapuissancedulangage
SQL.
- 14 - © ENI Editions - All rigths reserved
Classemodèle
1.Présentation
L
’
affichagedesarticlesetlarecherchesontdésormaisopérationnels.Nousallonsmaintenantdévelopperuneclasse
modèlecomplètequipermetdegérertouteslesopérationsd
’
administrationpourunservice(gestiondesarticles)et
quiservirad
’
exemplepourlaconstructiondesautresservicesdelaboutique.
Laclassemodèleauradoncunefonctionpourlistertouslesarticlesavecdesrecherches,unefonctionpourrécupérer
touteslesinformationsd
’
unarticleprécis,unefonctionpourmodifierunarticle,unefonctionpourlacréationetenfin,
unefonctionpourlasuppression.
Nousallonségalementgérerlapaginationdansleslistesetlesrecherchesavecdescontraintessurlestypes(ex :
recherchesurlenomousurladescription).
2.Miseenplace
NouscommençonsparcréerunenouvelleServletnomméeServletGestionArticles.CetteServletpermettraderéaliser
touteslesopérationssurlesarticlesenadministration.Sadéfinitiondanslefichierweb.xmlestprésentéeci
dessous.
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
...
<!-- servlets -->
<servlet>
<servlet -name>servletgestionarticles</servlet -name>
<servlet -class>betaboutique.servlets.ServletGestion
Articles</servlet -class>
</servlet>
<!-- mapping des servlets -->
<servlet -mapping>
<servlet -name>servletgestionarticles</servlet -name>
<url -pattern>/admin/gestionarticles/*</url -pattern>
</servlet -mapping>
...
</web-app>
Nous retrouvons la déclaration de la Servlet de gestion des articles en administration (/admin/gestionarticles/*
).
Ensuite,ilfautréaliserlecodeducontrôleur(Servlet)afindepréparerlagestiondesarticles.
package betaboutique.servlets;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.modeles.ArticleModele;
@SuppressWarnings("serial")
public class ServletGestionArticles extends HttpServlet {
//variables de la classe
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
- 1 -© ENI Editions - All rigths reserved
//créer le modèle
ArticleModele articlemodele=new ArticleModele(ds);
//redonner la datasource
this.ds=null;
//action a réaliser (liste des articles, consultation,
modification, suppression ou création)
String action=(String)request.getParameter("action");
//action par défaut (liste)
if(action==null || action.equalsIgnoreCase(""))
{
action="liste";
}
//liste des articles
if(action.equals("liste"))
{
//recherche
String typerecherche=(String)request.getParameter("typerecherche")
String recherche=(String)request.getParameter("recherche");
//informations pour les tris et l ’affichage paginé
String pagecourante=(String)request.getParameter("p");
String nomtri=(String)request.getParameter("s");
String tri=(String)request.getParameter("tri");
//valeurs par défaut
if(pagecourante==null)pagecourante="0";
if(nomtri==null)nomtri="id_article";
if(tri==null)tri="ASC";
String maxparpage="5";
//retourner la liste des articles
ArrayList listearticles=(ArrayList)
articlemodele.ListeArticleAdmin(pagecourante,nomtri,
tri,maxparpage,typerecherche,recherche);
//tri en cours
//changer l ’ordre de tri
if(tri.equals("ASC"))tri="DESC";
else if(tri.equals("DESC"))tri="ASC";
request.setAttribute("nomtri",nomtri);
request.setAttribute("tri",tri);
//les recherches
request.setAttribute("typerecherche",typerecherche);
request.setAttribute("recherche",recherche);
//retourner le maximum d ’enregistrement par page
request.setAttribute("maxparpage",(String)
articlemodele.getMaxparpage());
//retourner la page courante de l ’affichage
request.setAttribute("pageencours",pagecourante);
//retourner le nombre d ’enregistrement trouvé
request.setAttribute("compteurenregistrement",
articlemodele.getCompteurenregistrement());
//retourner le nombre total d ’enregistrement possibles
request.setAttribute("totalenregistrement",
articlemodele.getTotalenregistrement());
//retourner la liste des articles paginés
request.setAttribute("listearticles",listearticles);
//vider par sécurité
listearticles=null;
//retourner sur la page d ’affichage des articles
en administration
getServletContext().getRequestDispatcher("/admin/vues/
article/listearticles.jsp").forward(request, response);
}
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
- 2 - © ENI Editions - All rigths reserved
doGet(request, response);
}
}
Par défaut, la Servlet réalise un affichage sous forme de liste paginée des articles. Nous retrouvons plusieurs
paramètresquicorrespondentàlapagination,auxrecherchesetauxcalculsd
’
affichage.
Ci
dessous,lafonctiondelistedelaclassemodèle
ArticleModele
.
...
public class ArticleModele extends Modele
{
...
/***************************************************************************
* liste complete des articles
****************************************************************************/
public ArrayList ListeArticleAdmin(String pagecourante,String nomtri,
String tri,
String maxparpage,String typerecherche,String recherche)
{
//informations pour la pagination
this.maxparpage=Integer.parseInt(maxparpage);
this.position=this.maxparpage*Integer.parseInt(pagecourante);
this.compteurenregistrement=0;
PreparedStatement requetea=null,requeteb=null;
String requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//requete de comptage pour la pagination
requete="SELECT COUNT(DISTINCT(article.id_article)) AS
totalenregistrement FROM article WHERE 1";
if((typerecherche!=null && !typerecherche.equalsIgnoreCase("")) &&
(recherche!=null && !recherche.equalsIgnoreCase("")))requete+=" AND
"+typerecherche+" LIKE ? ";
//compter combien on a d ’enregistrement sans les conditions de
pagination
requetea=connection.prepareStatement(requete);
if((typerecherche!=null && !typerecherche.equalsIgnoreCase("")) &&
(recherche!=null && !recherche.equalsIgnoreCase("")))requetea.setString(1,
(String)"%"+recherche+"%");
rs=requetea.executeQuery();
//exécuter la requête
if(rs!=null)
{
//total d ’enregistrements trouves
if(rs.next())
{
if(rs.getString("totalenregistrement")!=null) this.
totalenregistrement=Integer.parseInt(rs.getString("totalenregistrement"));
}
}
//enregistrements avec pagination
requete="SELECT * FROM article WHERE 1";
if((typerecherche!=null && !typerecherche.equalsIgnoreCase("")) &&
(recherche!=null && !recherche.equalsIgnoreCase("")))requete+=" AND
"+typerecherche+" LIKE ? ";
requete+=" ORDER BY "+nomtri+" "+tri+" LIMIT "+this.position+",
"+this.maxparpage+"";
requeteb=connection.prepareStatement(requete);
if((typerecherche!=null && !typerecherche.equalsIgnoreCase("")) &&
(recherche!=null && !recherche.equalsIgnoreCase("")))requeteb.setString(1,
(String)"%"+recherche+"%");
rs=requeteb.executeQuery();
//executer la requete
if(rs!=null)
- 3 -© ENI Editions - All rigths reserved
{
//stocker tous les articles dans une liste
while(rs.next())
{
//un enregistrement de plus
this.compteurenregistrement++;
//creer un objet article
Article article=new Article();
//renseigner l ’objet article avec ses accesseurs
if(rs.getString("id_article")==null)article.setId_article("0");
else article.setId_article(rs.getString("id_article"));
if(rs.getString("nomarticle")==null)article.setNomarticle("");
else article.setNomarticle(rs.getString("nomarticle"));
if(rs.getString("descriptionarticle")==null)article.
setDescriptionarticle("");
else article.setDescriptionarticle(rs.getString
("descriptionarticle"));
if(rs.getString("prixarticle")==null)article.setPrixarticle("0");
else article.setPrixarticle(rs.getString("prixarticle"));
if(rs.getString("datearticle")==null)article.setDatearticle("0");
else article.setDatearticle(super.miseEnFormeDate(rs.getString
("datearticle")));
if(rs.getString("photoarticle")==null)article.setPhotoarticle("");
else article.setPhotoarticle(rs.getString("photoarticle"));
if(rs.getString("vignettearticle")==null)article.
setVignettearticle("");
else article.setVignettearticle(rs.getString("vignettearticle"));
if(rs.getString("etatarticle")==null)article.setEtatarticle("0");
else article.setEtatarticle(rs.getString("etatarticle"));
if(rs.getString("id_categorie")==null)article.
setNomcategoriearticle("");
else article.setNomcategoriearticle(super.getNomCategorieArticle
(rs.getString("id_categorie")));
//stocker l ’objet article dans la liste des articles
listearticle.add((Article)article);
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe ArticleModele.java fonction
ListeArticleAdmin");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(requetea!=null)OutilsBaseDeDonnees.fermerConnexion(requetea);
if(requeteb!=null)OutilsBaseDeDonnees.fermerConnexion(requeteb);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe ArticleModele.java
fonction ListeArticleAdmin");
}
}
//retourner la liste des articles
return listearticle;
}
...
//fin de la classe
}
La fonction est plus complexe car elle permet de gérer la pagination complète du système avec le nombre
d
’
enregistrements, la recherche et les tris. Il ne reste plus qu
’
à réaliser le codage de la vue listearticles.jsp
(/admin/vues/articles/listearticles.jsp).Cette vue estcomposée deplusieurs pagesJSPF pour lamise enpage et la
- 4 - © ENI Editions - All rigths reserved
pagination.
<%@ page import="java.util.ArrayList" %>
<%@ page import="betaboutique.boiteoutils.Article" %>
<%
//url de l ’application
String urlapplication=(String)getServletContext().getInit
Parameter("urlapplication");
//liste des articles
ArrayList listearticles=(ArrayList)request.getAttribute
("listearticles");
//action a appeler
String action=urlapplication+"admin/gestionarticles";
%>
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<!-- inclure la pagination -->
<%@ include file="../outils/pagination.jspf" %>
<!-- formulaire de recherche -->
<div id="rechercher"><img src="<%= urlapplication
%>img/louperecherche.png" title="Rechercher"
alt="Rechercher" align="absmiddle" border="0"/></div>
<form name="formulairerecherche" id="formulairerecherche"
action="<%= action %>" method="post">
<table cellspacing="4" cellpadding="0" id="tableau">
<tr>
<td><input type="text" class="input" id="recherche"
name="recherche" value="<%= recherche %>"/></td>
<td>
select name="typerecherche" id="typerecherche">
<option value="article.id_article"<%
if(typerecherche.equals("article.id_article"))out.print
("selected=\"selected\"");%>>Id</option>
<option value="article.nomarticle"<%
if(typerecherche.equals("article.nomarticle"))out.print
("selected=\"selected\"");%>>Nom</option>
<option value="article.prixarticle"<%
if(typerecherche.equals("article.prixarticle"))out.print
("selected=\"selected\"");%>>Prix</option>
</select>
</td>
<td>
<input type="submit" value="Rechercher" id="boutonrecherche"/>
</td>
</tr>
</table>
</form>
<br/><div class="titre"><a href="<%= action %>"><img
src="<%= urlapplication %>/img/articleadmin.png" border="0"
align="absmiddle"/> Liste des articles</a></div>
<table border="0" id="tableaubordure" cellspacing="0"cellpadding="0">
<tr align="center" class="entetetableau">
<td><a href="<%= action %>?p=<%= pageencours
%>&s=id_article&tri=<%= tri %><%= lienrecherche %>">Id<%
if(nomtri.equals("id_article") && tri.equals("ASC"))
{out.println("<img src=\"../img/asc.gif\" border=\"0\"/>");}
if(nomtri.equals("id_article") && tri.equals("DESC"))
{out.println("<img src=\"../img/desc.gif\"
border=\"0\"/>");}%></a></td>
<td><a href="<%= action %>?p=<%= pageencours
%>&s=nomarticle&tri=<%= tri %><%= lienrecherche %>">Nom<%
if(nomtri.equals("nomarticle") && tri.equals("ASC"))
{out.println("<img src=\"../img/asc.gif\" border=\"0\"/>");}
if(nomtri.equals("nomarticle") && tri.equals("DESC"))
{out.println("<img src=\"../img/desc.gif\"
border=\"0\"/>");}%></a></td>
<td><a href="<%= action %>?p=<%= pageencours %>
- 5 -© ENI Editions - All rigths reserved
&s=prixarticle&tri=<%= tri %><%= lienrecherche %>">Prix<%
if(nomtri.equals("prixarticle") && tri.equals("ASC"))
{out.println("<img src=\"../img/asc.gif\" border=\"0\"/>");}
if(nomtri.equals("prixarticle") && tri.equals("DESC"))
{out.println("<img src=\"../img/desc.gif\" border=\"0\"/>");}%>
</a></td>
<td><a href="<%= action %>?p=<%= pageencours
%>&s=datearticle&tri=<%= tri %><%= lienrecherche %>">Date<%
if(nomtri.equals("datearticle") && tri.equals("ASC"))
{out.println("<img src=\"../img/asc.gif\" border=\"0\"/>");}
if(nomtri.equals("datearticle") && tri.equals("DESC"))
{out.println("<img src=\"../img/desc.gif\" border=\"0\"/>");}%>
</a></td>
<td>Vignette</td>
<td>Catégorie</td>
<td><a href="<%= action %>?p=<%= pageencours
%>&s=etatarticle&tri=<%= tri %><%= lienrecherche %>">Etat<%
if(nomtri.equals("etatarticle") && tri.equals("ASC"))
{out.println("<img src=\"../img/asc.gif\" border=\"0\"/>");}
if(nomtri.equals("etatarticle") && tri.equals("DESC"))
{out.println("<img src=\"../img/desc.gif\" border=\"0\"/>");}%>
</a></td>
<td colspan="3" width="130">Gestion</td>
</tr>
<%
//entete pagination
out.println(pagination);
%>
<%
for(int i=0;i<listearticles.size();i++)
{
//récupérer l ’objet dans la liste
Article article=(Article)listearticles.get(i);
if(i%2==0)out.println("<tr align=\"center\" class=\"ligneclaire\">");
else out.println("<tr align=\"center\" class=\"lignefoncee\">");
out.println("<td>"+article.getId_article()+"</td>");
out.println("<td>"+article.getNomarticle()+"</td>");
out.println("<td>"+article.getPrixarticle()+" Eur</td>");
out.println("<td>"+article.getDatearticle()+"</td>");
out.println("<td><img src=\"../imgarticles/"
+article.getVignettearticle()+"\" width=\"60\" height=\"60\"/></td>");
out.println("<td>"+article.getNomcategoriearticle()+"</td>");
if(article.getEtatarticle().equals("1"))
{
out.println("<td><img src=\"../img/actif.gif\"
alt=\"Actif\" title=\"Actif\"/></td>");
}
else
{
out.println("<td><img src=\"../img/inactif.gif\"
alt=\"Inactif\" title=\"Inactif\"/></td>");
}
out.println("<td><a href= ’"+action+"?action=
consulter&id_article="+article.getId_article()+" ’>
<img src=\"../img/consulteradmin.png\" border=\"0\"
align=\"absmiddle\" title=\"Consulter\"/></a></td>");
out.println("<td><a href= ’"+action+"?action=
modifier&id_article="+article.getId_article()+" ’>
<img src=\"../img/modifieradmin.png\" border=\"0\"
align=\"absmiddle\" title=\"Modifier\"/></a></td>");
out.println("<td><a href= ’javascript:
confirmerSuppressionArticle("+article.getId_article()+"); ’>
<img src=\"../img/supprimeradmin.png\" border=\"0\"
align=\"absmiddle\" title=\"Supprimer\"/></a></td>");
out.println("</tr>");
}
%>
<%
- 6 - © ENI Editions - All rigths reserved
//piedpage pagination
out.println(pagination);
%>
</table>
<%@ include file="../outils/piedpage.jspf" %>
Lecodepourlapaginationestci
après.Cecodegénériqueestcommunàplusieurspages,ilestdoncplusintéressant
d
’
utiliserunepagefragment(JSPF)(/admin/vues/outils/pagination.jspf
).
<%
//pour les recherches
String typerecherche=(String)request.getAttribute
("typerecherche");
if(typerecherche==null)typerecherche="";
String recherche=(String)request.getAttribute("recherche");
if(recherche==null)recherche="";
String lienrecherche="&typerecherche="+typerecherche+"&recherche="+recherche;
//récupérer les informations de pagination
String nomtri=(String)request.getAttribute("nomtri");
String tri=(String)request.getAttribute("tri");
String maxparpage=(String)request.getAttribute("maxparpage");
String pageencours=(String)request.getAttribute("pageencours");
String compteurenregistrement=(String)request.getAttribute
("compteurenregistrement");
String totalenregistrement=(String)request.getAttribute
("totalenregistrement")
//calculs pour la pagination
int positiondebut=(int)(Integer.parseInt(maxparpage)
*Integer.parseInt(pageencours))+1;
int positionfin=(int)(positiondebut+Integer.parseInt
(compteurenregistrement)*1) -1;
int maxdepage=(int)Math.ceil((Double.parseDouble
(totalenregistrement)/Double.parseDouble(maxparpage)));
if(maxdepage==0)maxdepage=1;
int pageprecedente=Integer.parseInt(pageencours) -1;
int pagesuivante=Integer.parseInt(pageencours)+1;
int dernierepage=maxdepage -1;
int premierepage=0;
String tripagination="ASC";
if(tri.equalsIgnoreCase("ASC"))tripagination="DESC";
else tripagination="ASC";
StringBuffer pagination=new StringBuffer();
pagination.append("<tr class=\"fondblanc\"><td
colspan=\"17\">Enregistrements "+positiondebut+" -
"+positionfin+" sur "+totalenregistrement+" (Page :
"+pagesuivante+" sur "+maxdepage+")");
//afficher les boutons précédents que si nécessaire
if(!pageencours.equals("0"))
{
pagination.append(" <a id=\"btnFirst\"
href=\""+action+"?p="+premierepage+"&tri="+tripagination+"\"
title=\"première page\"><img src=\"../img/premierepage.gif\"
border=\"0\" align=\"absmiddle\"/></a>");
pagination.append(" <a id=\"btnPrev\"
href=\""+action+"?p="+pageprecedente+"&tri="+tripagination+"\"
title=\"Page précédente\"><img src=\"../img/pageprecedente.gif\"
border=\"0\" align=\"absmiddle\"/></a>");
}
//afficher les boutons suivants que si nécessaire
if(pagesuivante<maxdepage)
{
pagination.append(" <a id=\"btnNext\"
href=\""+action+"?p="+pagesuivante+"&s="+nomtri+"&tri=
"+tripagination+"\" title=\"NPage suivante\"><img src=\"../img
/pagesuivante.gif\" border=\"0\" align=\"absmiddle\"/></a>");
pagination.append(" <a id=\"btnLast\"
href=\""+action+"?p="+dernierepage+"&s="+nomtri+"&tri=
"+tripagination+"\" title=\"Dernière page\"><img src=\"../img
/dernierepage.gif\" border=\"0\" align=\"absmiddle\"/></a>");
- 7 -© ENI Editions - All rigths reserved
}
pagination.append("</td></tr>");
%>
Deuxvariablessontutiliséespourlesliensetlamaintenabilité.Lavariableactionpermetderéaliserlesliens
verslaServletadaptéeetlavariable
urlapplication
permetde réaliserlesliens,insérer lesimages...Enfin,
danslefichierweb.xmlladéclarationdel
’
URLdelaServletestfaiteaveclemodèle*afindepouvoirpasserdes
paramètres :
<url
pattern>/admin/gestionarticles/*</url
pattern>. (ex : /admin/gestionarticles?
action=modifier&id_article=4).
3.OptimisationavecJavaScript
Lapartieadministrationcommenceàêtrefournieentermedefonctionnalités.Nousallonsutiliseràcetteétapedu
développementlelangageJavaScriptpouraugmenterl
’
ergonomiedel
’
ensemble.Pourcommencer,nousallonsajouter
unfichierJavaScriptquiserviradeboîteàoutilstoutaulongduprojet(découpagedechaînes,messagesd
’
alertes...).
Pourcela,nousutilisonslecodesuivantàenregistrersouslenom :/javascript/boiteoutils.js
.
//supprimer un article en administration
function confirmerSuppressionArticle(id_article)
{
if(confirm("Voulez -vous supprimer cet article ?"))
{
chemin="gestionarticles?action=supprimer&id_article="+id_article;
document.location.href=chemin;
}
else
{
return;
}
}
Lecodecontientpourlemomentuniquementunefonctionpourvaliderlesconfirmationsdesuppression.Ilresteà
inclurecefichierdansl
’
en
têteJSPFaveclalignesuivante :
<head>
<title>Administration - BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
...
<!-- boite a outils -->
<script type="text/javascript" src="<%= urlapplication
%>/javascript/boiteoutils.js"></script>
</head>
Désormaissinouscliquonssurl
’
imagedesuppressiond
’
unarticle,uneboîtedeconfirmationestaffichée.
- 8 - © ENI Editions - All rigths reserved
Cefichierboiteoutils.jsseraincrémentédefonctionsaufuretàmesuredudéveloppementdel
’
application.Toutefois,il
existeactuellementtroisgrandeslibrairiesJavaScriptquipermettentderéaliserdesservicesintéressants.Lestrois
principaleslibrairiesactuellesenmatièredeJavaScriptsont :
●JQuery(http://jquery.com/).C
’
estlalibrairielapluslégèreetlaplussimpleàutiliser.Ellepossèdeunfichier
trèslégerpourlesprincipalesfonctionnalitésetdesfichiersplug
inpourdesservicesadaptés.
●ScriptAculous(http://script.aculo.us/).Cettelibrairieesttrèscomplètemaisassezlourde.Sonutilisationest
simple mais il y a parfois des conflits de noms entre les fonctions de la librairie et les fonctions d
’
autres
librairies.
●ExtJS(http://extjs.com/). C
’
estactuellement la librairie la pluspuissante. Sonextrême lourdeur (400 Ko de
librairies)etsacomplexitésontsesprincipauxdéfauts.LesfonctionnalitésproposéesetsondesignWeb2.0
sontsesavantages.
Pournotreprojet,nousallonsutiliserlalibrairieJQuerydebaseainsiquecertainsplug
insaubesoin.Pourinstallerla
librairie,ilsuffitdelatélécharger,del
’
installerdanslerépertoire/javascriptetderéaliserlesinclusionsnécessaires
danslefichierd
’
en
têteJSPF.
<!-- JQUERY -->
<script type="text/javascript" src="<%= urlapplication
%>/javascript/jquery/jquery.js"></script>
Nousallonségalementutiliserleplug
inautocompletepourgérerles"auto
complétions".Pourcela,ilestnécessairede
copier la librairie dans le répertoire /javascript/jquery/plugin/autocomplete. Il faut aussi créer un fichier nommé
recherche.jsdanslerépertoire/javascript/jquery/plugin/recherche.Lesinclusionssontréaliséesaveclesinstructions
suivantes :
<!-- boîte a outils -->
<script type="text/javascript" src="<%= urlapplication
%>/javascript/boiteoutils.js"></script>
<!-- plug-in pour les recherches -->
<script type="text/javascript" src="<%= urlapplication
%>/javascript/jquery/plugin/recherche/recherche.js"></script>
<!-- pour l’autocompletion -->
<script type="text/javascript" src="<%= urlapplication
%>/javascript/jquery/plugin/autocomplete/jquery.bgiframe.min.js">
</script>
<script type="text/javascript" src="<%= urlapplication
%>/javascript/jquery/plugin/autocomplete/dimensions.js"></script>
<script type="text/javascript" src="<%= urlapplication
%>/javascript/jquery/plugin/autocomplete/jquery.autocomplete.js">
</script>
Danslefichierrecherche.js,nousallonspourlemomentjustegérerl
’
affichageduformulairederecherche.Surl
’
image
delaloupe,nousajoutonslecodequipermetdedéclencherlafonctionJavaScriptrecherche()
.
<div id="rechercher"><a href="javascript:recherche();"><img
src="<%= urlapplication %>img/louperecherche.png"
title="Rechercher" alt="Rechercher" align="absmiddle"
border="0"/></a></div>
Nousallonségalementmodifiernotrefeuilledestylesurl
’
objetformulairerecherche
(
<formname="formulairerecherche"
id="formulairerecherche"action="<%=action%>" method="post">)afindenepasafficherleformulairederecherche
pardéfaut.
#formulairerecherche
- 9 -© ENI Editions - All rigths reserved
{
display:none;
}
Nous pouvons écrire le code de la fonction recherche() présente dans le
fichier/javascript/jquery/plugin/recherche/recherche.js
.
//afficher ou cacher le formulaire de recherche
function recherche()
{
//formulaire cache, l ’afficher
if($("#formulairerecherche").css("display")=="none")
{
$("#formulairerecherche").slideDown(500);
}
//formulaire affiche, le cacher
else
{
$("#formulairerecherche").slideUp(500);
}
}
LecodeesttrèssimpleetutiliselesfonctionnalitésdelabibliothèqueJQuery.SilestyledelafeuilleCSSestcaché
(
display:none)alorsilestmontréaveclafonctionslideDown(),sinonilestcachéaveclafonction
slideUp()
.Nousavons
doncuneboîtederecherchequis
’
ouvredemanièredynamiqueetergonomique.
4.OptimisationavecAjax
Asynchronous JavaScript and XML(XMLet JavaScript asynchrones)estune solutionlibrepour le développementde
fonctionnalitésWeb.Ajaxestunframework(ensembledetechnologiesetlibrairies)quiregroupeHTML/XHTML,XML,
CSS,JavaScriptetl
’
objetXMLHttpRequestpourl
’
échangededonnéesenasynchrone.Cettetechnologietrèsutilisée,
n
’
est pas nouvelle et l
’
objet XMLHttpRequest est apparu en 2001 avec Internet Explorer 5.0. AJAX permet de
déclencher de façon asynchrone en arrière plan, une page Web sans recharger la page courante. L
’
objet
XMLHttpRequestesttrèslourdàmanipuler,c
’
estpourquoi,beaucoupdeconcepteursetsociétésontdéveloppédes
librairiesplussimplespourutiliserAjaxtellesqueScriptAculous,AdvajaxouJQuery.
Eneffet,lalibrairieJQuerydebasepossèdetouteslesfonctionnalitéspourutiliserlatechnologieAjax.Nousallons
ainsi mettre enœuvrela technologieAjax pour l
’
auto
complétiondes formulaires de recherche. Ceservice d
’
auto
complétionseragénériqueàtoutesnospages,c
’
est
à
direqu
’
ilseraopérationnelsansmodificationducode,sans
développementd
’
uneServletspécifiqueàchaquefois...
Pourcela,lenomdelatableetlenomduchampsurlequelréaliserlarechercheserontutilisésenparamètre.
Voicilesétapesàsuivrepourinstallerl
’
auto
complétion :
1
UtilisationdelalibrairieAjax(JQuery
).
2
Téléchargementdeplug
inautocomplete
.
3
Créationd
’
unfichierrecherche.jsavecnotrecodepersonnel.
4
Insertiondeslibrairiesdansnotreapplication(<scriptsrc=...).
5
Développementd
’
uneServletgénériquederechercheasynchrone(ServletAutoComplete
).
Lesrequêtesutilisateursserontréaliséesenarrière
planetlaréponseseraeffectuéesansrechargementdelapage.
Nousallonscommencerpartéléchargeretinstallerleslibrairiesnécessairesàl
’
utilisationd
’
Ajaxetàl
’
auto
complétion.
Puis,nousallonscoderlefichierderecherche(recherche.js)pourl
’
utilisationd
’
Ajax.
//attribut
- 10 - © ENI Editions - All rigths reserved
var attribut=null;
var table=null;
var url=null;
//au chargement de la page
$(document).ready(function(){
//ecouter la liste pour le type de recherche souhaitee
$("#typerecherche").attr("onChange","changerTypeRecherche()");
//declencher la fonction pour le premier type
changerTypeRecherche();
//afficher le formulaire de recherche si il contient
une recherche, sinon le cacher
var recherche=$( ’#recherche ’).val();
if(recherche!="")
{
//montrer le formulaire de recherche
$("#formulairerecherche").show();
}
else
{
//cacher le formulaire de recherche
$("#formulairerecherche").hide();
}
});
//changer le type de recherche
function changerTypeRecherche()
{
//recuperer le type de recherche souhaitee
attribut=$( ’#typerecherche ’).val();
if(attribut!=null)
{
//recuperer la table (premiere partie de l ’attribut)
var tab=attribut.split( ’.’);
table=tab[0];
//enlever les espaces
table=jQuery.trim(table);
attribut=jQuery.trim(attribut);
//declencher l ’autocompletion
if(attribut!= ’’ && table!=’’){
//mettre en forme l ’url
url="autocomplete?attribut="+attribut+"&table="+table;
$("#recherche").autocomplete(url, {
delay: 400,
width:400,
cacheLength:1,
matchSubset:false,
mustMatch : true,
minChars:1,
autoFill: true
});
}
}
}
//afficher ou cacher le formulaire de recherche
function recherche()
{
//formulaire cache, l ’afficher
if($("#formulairerecherche").css("display")=="none")
{
$("#formulairerecherche").slideDown(500);
}
//formulaire affiche, le cacher
- 11 -© ENI Editions - All rigths reserved
else
{
$("#formulairerecherche").slideUp(500);
}
}
Dans le code précédent, il faut d
’
abord ajouter un écouteur sur la liste déroulante des recherches. Ainsi, lors du
changement dans la liste nous aurons automatiquement le nom du champ sur lequel réaliser la recherche
(
article.nomarticle
,
article.id_article
...).
Ensuite,ilfautdéclencherlafonctionchangerTypeRecherche()quipermetdedéclarerl
’
auto
complétionsurlechamp
nommérecherchedelapage.Lecodequiréalisel
’
opérationAjaxestlesuivant :
//declencher l ’autocompletion
if(attribut!= ’’ && table!=’’){
//mettre en forme l ’url
url="autocomplete?attribut="+attribut+"&table="+table;
$("#recherche").autocomplete(url, {
delay: 400,
width:400,
cacheLength:1,
matchSubset:false,
mustMatch : true,
minChars:1,
autoFill: true
});
}
Pourrésumer,sinousrecevonsunattribut(ex :article.nomarticle),alorsnousmettonsenformel
’
URLàdéclencheret
nousprécisonslesparamètresdel
’
auto
complétion.NotreURLseraappeléeenarrière
plandansundélaide400ms,
laréponseseraaffichéedansuneboîtede400pxdelargeetnousdéclenchonsl
’
auto
complétionAjaxàpartirdela
saisied
’
uncaractère(minChars:1)etnousautorisonslechoixdanslalisterésultat(autoFill:true
).
Nous avons déclaré les librairies JavaScript, développé notre fonction de gestion de l
’
auto
complétion, codé notre
formulairedanslapageJSP(codeci
après),ilnenousresteplusquelacréationdelaServletServletAutoComplete
.
Ladéclarationestréaliséecommetoujoursdanslefichierdegestiondel
’
application(web.xml
).
<?xml version="1.0" encoding="ISO -8859-1"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
...
<servlet>
<servlet -name>servletautocomplete</servlet -name>
<servlet -class>betaboutique.servlets.ServletAutoComplete
</servlet -class>
</servlet>
...
<servlet -mapping>
<servlet -name>servletautocomplete</servlet -name>
<url -pattern>/admin/autocomplete/*</url -pattern>
</servlet -mapping>
...
</web-app>
PuisnousréaliseronslaServletServletAutoCompleteavecunmessagetrèssimpleaffichédanslaconsoleencasde
recherchedansleformulaire.
package betaboutique.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
@SuppressWarnings("serial")
public class ServletAutoComplete extends HttpServlet {
//variables de la classe
- 12 - © ENI Editions - All rigths reserved
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException,
IOException
{
System.out.println("Dans la Servlet d ’auto-completion");
}
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Ensuite, nous rechargeons l
’
application et nous vérifions qu
’
une recherche avec au moins un caractère dans le
formulaireadapté,déclencheunmessagedanslaconsoleenarrière
plan,sansrechargerlapage(voicilapuissance
d
’
Ajax).
Nous allons maintenant réaliser le code de notre contrôleur (ServletAutoComplete) afin de vérifier l
’
intégrité des
donnéesreçuesetréaliserunerecherchedanslabasededonnéesaveclesinformationsadaptées.Pourcela,nous
créonsuneclasse modèlenomméeAutoCompleteModelequi contientuniquementune fonctionavec la requêteSQL
associée.LarequêteSQLreçoitenparamètrelenomduchampàrechercheretlatableconcernée.Nousavonsbien
dans ce cas un moteur d
’
auto
complétion générique (fonctionnel pour n
’
importe quel service : articles, clients et
commandes).
package betaboutique.modeles;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import betaboutique.boiteoutils.OutilsBaseDeDonnees;
import java.util.ArrayList;
public class AutoCompleteModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//liste des objets
ArrayList<String> listeautocomplete=new ArrayList<String>();
/***********************************************************
* constructeur
***********************************************************/
public AutoCompleteModele(DataSource ds)
{
//récupérer la DataSource de la servlet
this.ds=ds;
}
/***********************************************************
* liste des données demandées en auto -complétion
***********************************************************/
public ArrayList ListeAutoCompleteAdmin(String attribut,
String table, String saisie, String limit)
{
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT
DISTINCT("+attribut+") FROM "+table+" WHERE "+attribut+"
- 13 -© ENI Editions - All rigths reserved
LIKE ? ORDER BY "+attribut+" LIMIT 0,"+limit+"");
requete.setString(1,(String)"%"+saisie+"%");
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//stocker toutes les reponses dans une liste
while(rs.next())
{
if(rs.getString(attribut)!=null)
{
listeautocomplete.add((String)
rs.getString(attribut));
}
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
AutoCompleteModele.java fonction ListeAutoCompleteAdmin");
}
finally
{
try
{
//fermer la connexion
if(rs!=null) OutilsBaseDeDonnees.fermerConnexion(rs);
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
AutoCompleteModele.java fonction ListeAutoCompleteAdmin");
}
}
//retourner la liste
return listeautocomplete;
}
//fin de la classe
}
LecodedelaServletcontrôleurestégalementtrèssimple,ilfautréaliseruncontrôledesdonnées,déclencherle
modèlepourrécupérerunelistededonnéesetsedirigerverslavuelisteautocomplete.jsppourl
’
affichage.
package betaboutique.servlets;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.modeles.AutoCompleteModele;
@SuppressWarnings("serial")
public class ServletAutoComplete extends HttpServlet {
//variables de la classe
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
- 14 - © ENI Editions - All rigths reserved
présent dans le contexte de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
//créer le modèle
AutoCompleteModele autocompletemodele=new AutoCompleteModele(ds);
//fermer la datasource
this.ds=null;
//recuperer les attributs
String attribut=(String)request.getParameter("attribut").trim();
String table=(String)request.getParameter("table").trim();
String q=(String)request.getParameter("q").trim();
String limit=(String)request.getParameter("limit").trim();
//verifier les parametres
if( (attribut!=null && !attribut.equals("")
&& attribut.length()>0) && (table!=null && !table.equals("")
&& table.length()>0) && (q!=null && !q.equals("") &&
q.length()>0) && (limit!=null && !limit.equals("") &&
limit.length()>0) )
{
//recuperer la liste des données qui correspondent
ArrayList listeautocomplete=autocompletemodele.Liste
AutoCompleteAdmin(attribut,table,q,limit);
//retourner la liste
request.setAttribute("listeautocomplete",listeautocomplete);
//vider par sécurité
listeautocomplete=null;
//retourner sur la page d ’affichage des enregistrements trouvés
getServletContext().getRequestDispatcher
("/admin/vues/outils/listeautocomplete.jsp").forward(request,
response);
}
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
Leserviced
’
auto
complétionestquasimentterminé,ilrestelecodedelavuelisteautocomplete.jsp.Lecodeassocié
récupèrelalistedesdonnées trouvées,etpourchaqueattribut,affiche savaleuravecunretourà laligne.C
’
est
ensuite,etdemanièretransparente,lecodeJavaScriptdeJQuery(autocomplete)quivamettreenformelaréponse
pourl
’
afficherdanslalistedéroulantederecherche.
<%@ page import="java.util.ArrayList" %>
<%
//liste des valeurs retournés
ArrayList listeautocomplete=(ArrayList)request.getAttribute
("listeautocomplete");
%>
<%
for(int i=0;i<listeautocomplete.size();i++)
{
//récupérer l ’objet dans la liste
String attribut=(String)listeautocomplete.get(i);
out.println(attribut);
//autre syntaxe out.println(cle+"|valeur non affichee");
}
%>
Ilresteenfinunedernièreétape,l
’
intégrationdelafeuilledestyleJQuerypourlamiseenpageetl
’
affichagedel
’
auto
complétion.Cetteopérationestsimple,ilfautajouterlafeuilledestyleaveclecodesuivantdanslapageentete.jspf
.
<html>
<head>
<title>Administration - BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
...
- 15 -© ENI Editions - All rigths reserved
<!-- feuilles de style pour autocomplete -->
<link rel="stylesheet" type="text/css" href="<%= urlapplication
%>/javascript/jquery/plugin/autocomplete/jquery.autocomplete.css" />
ChaquesaisiedetexteentraîneunerequêteadaptéedansleSGBDetretrouvelesréponsessansrechangerlapage
etsonformulairederecherche.
Nouspouvonsdésormaisréaliserdesrecherchesetbénéficierdel
’
auto
complétionenfonctiondutypederecherche
(ex :nom,id,prix...)présentdanslalistedéroulante.
5.OptimisationdesServlets
Leprincipededéclarationd
’
uneServletpourchaqueactionassociéeesttrèslourdentermedeprogrammation,de
lignedecodemaisaussid
’
homogénéité.Pourévitercela,latechniqueconsisteàutiliserunparamètresupplémentaire
danslesServlets(ex :action,mode,role...)etdedéclareruneseuleServletdegestionparservice.Dansnotrecas,la
ServletServletGestionArticlespossèdeunattributsupplémentairenomméactionquipermetdesavoirsinousvoulons
lalistedesarticles,consulterunarticle,modifieruneficheousupprimerunproduit.
VoicilesdifférentesURLpossiblesdansnotrecas :
●http://localhost:8080/betaboutiquemvc/admin/gestionarticles
●http://localhost:8080/betaboutiquemvc/admin/gestionarticles?action=consulter&id_article=1
●http://localhost:8080/betaboutiquemvc/admin/gestionarticles?action=modifier&id_article=1
●http://localhost:8080/betaboutiquemvc/admin/gestionarticles?action=supprimer&id_article=1
DanslecodedelaServletdegestion,leparamètreactionaunevaleurpardéfaut(listedesarticlesdansnotrecas)et
untraitementadaptéenfonctiondel
’
actionassociée.
package betaboutique.servlets;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import betaboutique.modeles.ArticleModele;
@SuppressWarnings("serial")
public class ServletGestionArticles extends HttpServlet {
//variables de la classe
DataSource ds=null;
//traitements
public void doGet(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)getServletContext().getAttribute("datasource");
- 16 - © ENI Editions - All rigths reserved
//créer le modèle
ArticleModele articlemodele=new ArticleModele(ds);
//redonner la datasource
this.ds=null;
//action a réaliser (liste des articles,
consultation, modification, suppression ou création)
String action=(String)request.getParameter("action");
//action par défaut (liste)
if(action==null || action.equalsIgnoreCase(""))
{
action="liste";
}
//liste des articles
if(action.equals("liste"))
{
...
}
//consulter un article
if(action.equals("consulter"))
{
...
}
...
}
//traitements
public void doPost(HttpServletRequest request, HttpServletResponse
response)throws ServletException, IOException
{
doGet(request, response);
}
}
NotreServletestoptimisée,ilnenousresteplusqu
’
àcréerlecodepourlaconsultation,modificationetsuppression
d
’
unarticle.
Nouscommençonsparlecodedeconsultationd
’
unarticle.Pourréaliserlaconsultation,lesopérationssontassez
simples. Il faut récupérer l
’
id de l
’
article à consulter, déclencher la classe modèle afin de récupérer toutes les
informationssurl
’
articlesélectionné,etretournersurlavuequisertd
’
affichage.
...
//consulter un article
if(action.equals("consulter"))
{
//récupérer le numéro de l ’article à consulter
String id_article=(String)request.getParameter("id_article");
if(id_article!=null && !id_article.equals(""))
{
//récupérer l ’article dans la base de données
avec la classe modele
Article article=(Article)articlemodele.getArticle(id_article);
//retourner l ’objet article a la vue
request.setAttribute("article",article);
//retourner sur la page d ’affichage des articles
en administration
getServletContext().getRequestDispatcher("/admin/vues/article/
consulterarticle.jsp").forward(request, response);
}
}
...
LecodedelaclassemodèleutiliseunobjetJavaBeaninstancedelaclasse
Article
pourstockerlesinformations.
...
Consultationd
’
unarticle
- 17 -© ENI Editions - All rigths reserved
/***********************************************************
* récupérer un article
***********************************************************/
public Article getArticle(String id_article)
{
PreparedStatement requetea=null;
//créer un objet article
Article article=new Article();
try
{
//ouvrir une connexion
connection=ds.getConnection();
requetea=connection.prepareStatement("SELECT *
FROM article WHERE article.id_article=?");
requetea.setString(1,(String)id_article);
rs = requetea.executeQuery();
//exécuter la requête
if(rs!=null)
{
if(rs.next())
{
//renseigner l ’objet article avec ses accesseurs
if(rs.getString("id_article")==null)
article.setId_article("0");
else article.setId_article(rs.getString
("id_article"));
if(rs.getString("nomarticle")==null)
article.setNomarticle("");
else article.setNomarticle(rs.getString
("nomarticle"));
if(rs.getString("descriptionarticle")==null)
article.setDescriptionarticle("");
else article.setDescriptionarticle
(rs.getString("descriptionarticle"));
if(rs.getString("prixarticle")==null)
article.setPrixarticle("0");
else article.setPrixarticle(rs.getString
("prixarticle"));
if(rs.getString("datearticle")==null)
article.setDatearticle("0");
else article.setDatearticle
(super.miseEnFormeDate(rs.getString("datearticle")));
if(rs.getString("photoarticle")==null)
article.setPhotoarticle("");
else article.setPhotoarticle(rs.getString
("photoarticle"));
if(rs.getString("vignettearticle")==null)
article.setVignettearticle("");
else article.setVignettearticle(rs.getString
("vignettearticle"));
if(rs.getString("etatarticle")==null)
article.setEtatarticle("0");
else article.setEtatarticle(rs.getString
("etatarticle"));
if(rs.getString("id_categorie")==null)
article.setNomcategoriearticle("");
else article.setNomcategoriearticle
(super.getNomCategorieArticle(rs.getString("id_categorie")));
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction getArticle");
}
finally
{
- 18 - © ENI Editions - All rigths reserved
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(requetea!=null)OutilsBaseDeDonnees.fermerConnexion(requetea);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction getArticle");
}
}
//retourner l ’objet article
return article;
}
Enfin, la vue /admin/vues/article/consulterarticle.jsp permet d
’
afficher l
’
article et possède un lien pour accéder au
formulaireenmodification.
<%@ page import="betaboutique.boiteoutils.Article" %>
<%
//url de l ’application
String urlapplication=(String)getServletContext().getInit
Parameter("urlapplication");
//article
Article article=(Article)request.getAttribute("article");
//action a appeler
String action=urlapplication+"admin/gestionarticles";
%>
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="<%= action %>"><img
src="<%= urlapplication %>/img/articleadmin.png" border="0"
align="absmiddle"/> Liste des articles</a></div>
<form action="<%= action %>?action=modifier&id_article=<%=
article.getId_article() %>" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0"
width="600">
<tr><td>Id</td><td><%= article.getId_article() %></td></tr>
<tr><td>Nom</td><td><%= article.getNomarticle() %></td></tr>
<tr><td valign="top">Description</td><td><%=
article.getDescriptionarticle() %></td></tr>
<tr><td>Prix</td><td><%= article.getPrixarticle() %>
Euros</td></tr>
<tr><td>Date création</td><td><%=
article.getDatearticle() %></td></tr>
<tr><td>Photo</td><td><img src="../imgarticles/<%=
article.getPhotoarticle() %>"/></td></tr>
<tr><td>Vignette</td><td><img src="../imgarticles/<%=
article.getVignettearticle() %>"/></td></tr>
<tr><td>Etat</td><td><%= article.getEtatarticle()
%></td></tr>
<tr><td>Catégorie</td><td><%=
article.getNomcategoriearticle() %></td></tr>
<tr><td align="center" colspan="2"><input type="submit"
name="modifier" value="Modifier" class="bouton"/></td></tr>
</table>
</form>
<%@ include file="../outils/piedpage.jspf" %>
Nousallonsréaliserleformulairedemodificationselonlemêmeprincipequeleformulairedeconsultation.Cependant,
il existe une petite différence. Il faut commencer par récupérer les informations de l
’
article, afficher le formulaire
completetensuite,validerlesmodifications.
Modificationd
’
unarticle
- 19 -© ENI Editions - All rigths reserved
Nousmodifionslafonction
getArticle()
delaclassemodèleafindegérerl
’
iddelacatégorie.
...
if(rs.next())
{
//renseigner l ’objet article avec ses accesseurs
if(rs.getString("id_article")==null)article.setId_article("0");
else article.setId_article(rs.getString("id_article"));
...
if(rs.getString("id_categorie")==null)article.setId_categoriearticle("0");
else article.setId_categoriearticle(rs.getString("id_categorie"));
if(rs.getString("id_categorie")==null)article.setNomcategoriearticle("");
else article.setNomcategoriearticle(super.getNomCategorie
Article(rs.getString("id_categorie")));
}
...
IlfautensuiteajouterunenouvellefonctionnomméegetListeCategorieArticle()danslaclassemodèlemère(méthode
utiliséeparplusieurssous
classesparlasuite)afinderetourneruniquementlalistedescatégoriesd
’
articlesetune
classeJavaBeanpourlagestiondescatégories.
...
public ArrayList getListeCategorieArticle()
{
//vider la liste
ArrayList<Categorie> listeobjetcategoriearticle=new
ArrayList<Categorie>();
String requete=null;
//Récupérer tous les médias mis en vente par
l’utilisateur en présence
try
{
//ouvrir une connexion
connection1=ds.getConnection();
instruction1=connection1.createStatement();
//liste des catégories
requete="SELECT DISTINCT(id_categorie),nomcategorie
FROM categorie ORDER BY categorie.nomcategorie";
rs1=instruction1.executeQuery(requete);
//exécuter la requête
if(rs1!=null)
{
//stocker tous les catégories dans une liste
while(rs1.next())
{
//créer un objet categorie
Categorie categorie=new Categorie();
if(rs1.getString("id_categorie")==null)
categorie.setId_categorie("0");
else categorie.setId_categorie(rs1.getString
("id_categorie"));
if(rs1.getString("nomcategorie")==null)
categorie.setNomcategorie("");
else categorie.setNomcategorie(rs1.getString
("nomcategorie"));
//ajouter à la liste
listeobjetcategoriearticle.add((Categorie)
categorie);
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
Modele.java fonction getListeCategorieArticle");
}
finally
{
- 20 - © ENI Editions - All rigths reserved
try
{
//fermer la connexion
if(rs1!=null)OutilsBaseDeDonnees.fermerConnexion(rs1);
if(instruction1!=null)OutilsBaseDeDonnees.fermerConnexion
(instruction1);
if(connection1!=null)OutilsBaseDeDonnees.fermerConnexion
(connection1);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
Modele.java fonction getListeCategorieArticle");
}
}
//retourner la liste des objets categorie
return listeobjetcategoriearticle;
}
...
LaServletdegestiondesarticlesestadaptéepourtraiterlamodificationd
’
unproduit.Lalistedescatégoriesest
récupérée(pourlechoixfuturdansunelistedéroulante).
...
//modifier un article
else if(action.equals("modifier"))
{
//récupérer le numéro de l ’article à modifier
String id_article=(String)request.getParameter("id_article");
if(id_article!=null && !id_article.equals(""))
{
//récupérer l ’article dans la base de données avec la classe modele
Article article=(Article)articlemodele.getArticle(id_article);
//récupérer la liste des catégories d ’articles
ArrayList listecategoriearticles=(ArrayList)
articlemodele.getListeCategorieArticle();
//retourner l ’objet article a la vue
request.setAttribute("article",article);
//retourner la liste des catégories d ’articles à la vue
request.setAttribute("listecategoriearticles",listecategoriearticles);
//retourner sur la page d ’affichage des articles en administration
getServletContext().getRequestDispatcher("/admin/vues/article/
modifierarticle.jsp").forward(request, response);
}
}
...
Enfin, il reste le code de la vue
modifierarticle.jsp
qui permet d
’
afficher les informations et de déclencher l
’
action
validermodifier
pourinsérerlesnouvellesdonnéesmodifiéesdanslabasededonnées.
<%@ page import="java.util.ArrayList" %>
<%@ page import="betaboutique.boiteoutils.Article" %>
<%@ page import="betaboutique.boiteoutils.Categorie" %>
<%
//url de l ’application
String urlapplication=(String)getServletContext().getInit
Parameter("urlapplication");
//article
Article article=(Article)request.getAttribute("article");
//liste des categories
ArrayList listecategoriearticles=(ArrayList)request.get
Attribute("listecategoriearticles");
//action a appeler
String action=urlapplication+"admin/gestionarticles";
%>
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="<%= action %>"><img
- 21 -© ENI Editions - All rigths reserved
src="<%= urlapplication %>/img/articleadmin.png" border="0"
align="absmiddle"/> Liste des articles</a></div>
<form action="<%= action %>?action=validermodifier"
method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0"
width="50%">
<tr><td>Id</td><td><input type="text" name="id_article"
class="input" value="<%= article.getId_article() %>"
readonly="readonly"/></td></tr>
<tr><td>Nom</td><td><input type="text" name="nomarticle"
class="input" value="<%= article.getNomarticle()
%>"/></td></tr>
<tr><td valign="top">Description</td><td><textarea
class="textarea" name="descriptionarticle"><%=
article.getDescriptionarticle() %></textarea></td></tr>
<tr><td>Prix</td><td><input type="text" name="prixarticle"
class="input" value="<%= article.getPrixarticle() %>"/>
Euros</td></tr>
<tr><td>Date</td><td><input type="text" name="datearticle"
class="input" value="<%= article.getDatearticle()
%>"/></td></tr>
<tr><td>Photo</td><td><input type="text" name="photoarticle"
class="input" value="<%= article.getPhotoarticle() %>"/>
<img src="../imgarticles/<%= article.getPhotoarticle() %>"
width="60" align="absmiddle"/></td></tr>
<tr><td>Vignette</td><td><input type="text"
name="vignettearticle" class="input" value="<%=
article.getVignettearticle() %>"/> <img
src="../imgarticles/<%= article.getVignettearticle()
%>" width="60" align="absmiddle"/></td></tr>
<tr><td>Etat</td>
<td>
<%
out.println("<select name=\"etatarticle\" class=\"select\">");
out.println("<option value=\"1\"");
if(article.getEtatarticle().equals("1")) out.println("
selected=\"selected\" ");
out.println(">Actif</option>");
out.println("<option value=\"0\"");
if(article.getEtatarticle().equals("0")) out.println("
selected=\"selected\" ");
out.println(">Inactif</option>");
out.println("</select>");
%>
</td></tr>
<tr><td>Catégorie</td>
<td>
<%
//liste des categories
out.println("<select name=\"id_categorie\" class=\"select\">");
for(int i=0;i<listecategoriearticles.size();i++)
{
//récupérer l ’objet dans la liste
Categorie categorie=(Categorie)listecategoriearticles.get
(i);
out.println("<option value=\""+categorie.getId_categorie()
+"\"");
if(categorie.getId_categorie().equals(article.get
Id_categorie())) out.println(" selected=\"selected\" ");
out.println(">"+categorie.getNomcategorie()+"</option>");
}
out.println("</select>");
%>
</td></tr>
<tr><td align="center" colspan="2"><input type="submit"
name="modifier" value="Modifier" class="bouton"/></td></tr>
</table><br/>
</form>
<%@ include file="../outils/piedpage.jspf" %>
- 22 - © ENI Editions - All rigths reserved
Un clic sur le bouton de modification va déclencher l
’
envoi des données (avec la méthode POST) à la Servlet afin
d
’
opérerlesmodifications.
Danslesexemplesdeceguide(notammentl
’
exempleprécédent),iln
’
yapasdevérificationdesdonnéesau
momentdelacréationetdelamodification.Ilestévidentquedansuneapplicationprofessionnelle,ilfaudrait
vérifierlasyntaxedeladate,laprésenceduprix,laprésencedunomdel
’
article...ducôtéserveurauseindela
Servletadaptéeetdansl
’
idéal,côtéserveuretcôtéclient(aveclelangageJavaScript).
Ilneresteplusqu
’
àdévelopperlecodedel
’
action
validermodifier
afind
’
insérerlesnouvellesdonnéesdel
’
articledans
labasededonnées.Pourcela,nousallonsutiliserlalibrairiecommons.beanutils.BeanUtilspourgérerlesJavaBeans.
Parcontre,cetteutilisationnécessiteunerigueurdansl
’
utilisationdesnomsdesattributs.Eneffet,ilestnécessaire
deconserverlacorrespondanceexacteentrelesattributsdelatableetduJavaBean.Danslecodedelaclasse
Article
,
lenomdelacatégorien
’
estpascorrect,ilfaututiliser :
private String id_categorie=null;
àlaplacede:
private String id_categoriearticle=null;
CechangemententraînequelquesmodificationsdanslecodedesclassesmodèlesetdesvuesJSP.Ilestégalement
nécessairedemodifierlaméthode
getArticle()
delaclassemodèlepourgérerl
’
iddelacatégorie.
...
if(rs.getString("id_categorie")==null)article.setId_categorie("0");
else article.setId_categorie(rs.getString("id_categorie"));
...
- 23 -© ENI Editions - All rigths reserved
LecodedelaServletpermetderenseignerleJavaBeanetinsèrelesdonnéesduJavaBeandanslabasededonnées.
Larequêtedemodificationretourneunevaleurquivaservirpourlagestiondesmessagesd
’
erreursoudesuccès.Le
codecompletdelaServletestalorslesuivant :
...
public class ServletGestionArticles extends HttpServlet {
...
//pour la gestion des messages d ’erreurs
ArrayList<String> erreurs=new ArrayList<String>();
//pour la gestion des messages de succès
ArrayList<String> succes=new ArrayList<String>();
...
//on veut modifier un article
else if(action.equals("modifier"))
{
//récupérer le numéro de l ’article à modifier
String id_article=(String)request.getParameter("id_article");
if(id_article!=null && !id_article.equals(""))
{
//récupérer l ’article dans la base de données avec la classe modele
Article article=(Article)articlemodele.getArticle(id_article);
//récupérer la liste des catégories d ’articles
ArrayList
listecategoriearticles=(ArrayList)articlemodele.getListeCategorieArticle();
//retourner l ’objet article a la vue
request.setAttribute("article",article);
//retourner la liste des catégories d ’articles à la vue
request.setAttribute("listecategoriearticles",
listecategoriearticles);
//retourner sur la page d ’affichage des articles en administration
getServletContext().getRequestDispatcher("/admin/vues/article/
modifierarticle.jsp").forward(request, response);
}
}
//on veut valider la modification d ’ un article
else if(action.equals("validermodifier"))
{
//on récupère les données dans la requête et on les insère
dans le JavaBean article
Article article=new Article();
//création d ’une collection des paramètres reçus par la requête
HashMap map=new HashMap();
Enumeration names=request.getParameterNames();
while (names.hasMoreElements())
{
//on met chaque paramètre de la requête dans une collection
String name=(String)names.nextElement();
map.put(name,request.getParameterValues(name));
}
try
{
// on insère toutes nos données dans le Bean article en une seule
étape
BeanUtils.populate(article, map);
System.out.println("description :
"+article.getDescriptionarticle());
//modifier l ’article dans la base de données
int resinstruction=articlemodele.modifierArticle(article);
//en cas d ’erreur avec la base de données, ajouter un message
d’erreur
if(resinstruction!=1)
{
//ajouter un message d ’erreur
erreurs.add("Erreur lors de la modification de l ’article");
}
//tout est OK
else
{
- 24 - © ENI Editions - All rigths reserved
//ajouter un message de succes
succes.add("Modification de l ’article réalisée avec succès");
}
//envoyer les liste de messages à la vue et retourner sur la vue
principale
request.setAttribute("erreurs",erreurs);
request.setAttribute("succes",succes);
//retourner sur la page d ’affichage des articles en
administration
getServletContext().getRequestDispatcher("/admin/gestionarticles?action=
liste").forward(request, response);
}
catch(Exception e)
{
System.out.println("Erreur lors de la lecture des informations");
}
...
}
/***********************************************************
* modifier un article
***********************************************************/
public int modifierArticle(Article article)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("UPDATE article SET
nomarticle=?,descriptionarticle=?,prixarticle=?,datearticle=?,
photoarticle=?,vignettearticle=?,etatarticle=?,id_categorie=?
WHERE article.id_article=?");
requete.setString(1,(String)article.getNomarticle());
requete.setString(2,(String)article.get
Descriptionarticle());
requete.setString(3,(String)article.getPrixarticle());
requete.setString(4,(String)super.miseEnFormeDatevers
Base(article.getDatearticle()));
requete.setString(5,(String)article.getPhotoarticle());
requete.setString(6,(String)article.getVignettearticle());
requete.setString(7,(String)article.getEtatarticle());
requete.setString(8,(String)article.getId_categorie());
requete.setString(9,(String)article.getId_article());
resinstruction=requete.executeUpdate();
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction modifierArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction modifierArticle");
}
}
//résultat de la modification
- 25 -© ENI Editions - All rigths reserved
return resinstruction;
}
/***********************************************************
* mise en forme de la date (jj/mm/aaaa -> aaaammjj )
***********************************************************/
public String miseEnFormeDateversBase(String date)
{
if(date.length()>=10)
{
String jour=date.substring(0,2);
String mois=date.substring(3,5);
String annee=date.substring(6,10);
date=annee+mois+jour;
}
//retourner la date
return date;
}
Il ne reste désormais plus qu
’
à modifier la page JSP d
’
en
tête pour afficher les messages de succès et d
’
erreurs
éventuelsdelarequête.
...
<%@ page import="java.util.ArrayList" %>
<html>
<head>
<title>Administration - BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
...
<!-- gestion des messages de succès -->
<%
ArrayList succes=(ArrayList)request.getAttribute("succes");
if(succes!=null && succes.size()>0)
{
out.println("<div id=\"message_information\"><ul>");
for(int i=0;i<succes.size();i++)
{
out.println("<li>"+(String)succes.get(i)+"</li>");
}
out.println("</ul></div>");
}
%>
<! -- gestion des messages d ’erreur -->
<%
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
if(succes!=null && erreurs.size()>0)
{
out.println("<div id=\"message_erreur\"><ul>");
for(int i=0;i<erreurs.size();i++)
{
out.println("<li>"+(String)erreurs.get(i)+"</li>");
}
out.println("</ul></div>");
}
%>
...
- 26 - © ENI Editions - All rigths reserved
Danscertainscas,iln
’
estpaspossibled
’
utiliserlaclassestatiqueBeanUtilsdegestiondesJavaBeanspour
insérerdirectementdesinformationsd
’
unformulaire.Eneffet,siparexempledansleformulaireilyavaitdes
informationsquidoiventêtreinséréesdanslabasededonnéesmaisquinesontpasdansleJavaBean
Article
,ces
informations seraient perdues. Pour résoudre ce problème il faut utiliser plusieurs JavaBeans ou utiliser un
mélangedeJavaBeansetdesinformationsreçuesparlarequêteavecrequest.getParameter(...).Rienn
’
empêchele
développeurd
’
utiliserplusieurstechniques.
Certainsdéveloppeurs,poursimplifierlesServletsetdiminuerleslignesdecodeutilisentlesmêmesfichiers
en consultation et modification. Le client consulte le formulaire de modification sans apporter de
changement.
Ilresteàréaliserl
’
étapedesuppressiond
’
unarticle.Cetteétapeestlaplussimple,ilsuffitdelanceruneméthodedu
modèlequipermetlasuppressiondel
’
article.
LaportiondecodeàrajouterdanslaServletestlasuivante :
...
//supprimer un article
else if(action.equals("supprimer"))
{
//récupérer le numéro de l ’article à supprimer
String id_article=(String)request.getParameter("id_article");
if(id_article!=null && !id_article.equals(""))
{
//récupérer l ’article dans la base de données
avec la classe modele
int resinstruction=articlemodele.supprimerArticle(id_article);
//en cas d ’erreur avec la base de données, ajouter
un message d ’erreur
if(resinstruction!=1)
{
//ajouter un message d ’erreur
erreurs.add("Erreur lors de la suppression de l ’article");
}
//tout est OK
else
{
//ajouter un message de succes
succes.add("Suppression de l ’article réalisée avec succès");
}
//envoyer les listes de messages à la vue et retourner
sur la vue principale
request.setAttribute("erreurs",erreurs);
request.setAttribute("succes",succes);
//retourner sur la page d ’affichage des articles en administration
getServletContext().getRequestDispatcher("/admin/
Suppressiond
’
unarticle
- 27 -© ENI Editions - All rigths reserved
gestionarticles?action=liste").forward(request, response);
}
}
...
Enfin,lecodedelaclassemodèleesttrèssimple :
...
/***********************************************************
* supprimer un article
***********************************************************/
public int supprimerArticle(String id_article)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("DELETE FROM
article WHERE article.id_article=?");
requete.setString(1,id_article);
resinstruction=requete.executeUpdate();
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction supprimerArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction supprimerArticle");
}
}
//retourner le résultat de la suppression
return resinstruction;
}
...
Commepourl
’
étapedemodification,l
’
étapedecréationnécessitelavalidationdesdonnéesavantl
’
insertiondansla
basededonnées.
Dans un cas réel, nous utiliserions plutôt un formulaire d
’
upload pour le chargement des pochettes des
articlesàlaplaced
’
unchamptextequicontientlechemindel
’
imageàafficher.Cesimagessontchargéespar
exempleàl
’
aided
’
unclientFTP.
LaServletestlégèrementmodifiépourafficherleformulairedecréation(formulaireHTML).
...
//créer un article
else if(action.equals("creer"))
{
//récupérer la liste des catégories d ’articles
ArrayList listecategoriearticles=(ArrayList)
Créationd
’
unarticle
- 28 - © ENI Editions - All rigths reserved
articlemodele.getListeCategorieArticle();
//retourner la liste des catégories d ’articles à la vue
request.setAttribute("listecategoriearticles",
listecategoriearticles);
//retourner sur la page d ’affichage des articles en
administration
getServletContext().getRequestDispatcher("/admin/vues/article/
creerarticle.jsp").forward(request, response);
}
...
Nousmodifionsaussilavuelistearticles.jspafind
’
insérerleliendecréation.
...
<br/><div class="titre"><a href="<%= action %>"><img
src="<%= urlapplication %>/img/articleadmin.png"
border="0" align="absmiddle"/> Liste des
articles</a></div>
<a href="<%= action %>?action=creer">Créer un
nouvel article</a><br/>
...
Voicici
aprèslavue
creerarticle.jsp
pourl
’
étapedecréationd
’
unarticle.
<%@ page import="java.util.ArrayList" %>
<%@ page import="betaboutique.boiteoutils.Article" %>
<%@ page import="betaboutique.boiteoutils.Categorie" %>
<%
//url de l ’application
String urlapplication=(String)getServletContext().getInit
Parameter("urlapplication");
//liste des categories
ArrayList listecategoriearticles=(ArrayList)request.get
Attribute("listecategoriearticles");
//action a appeler
String action=urlapplication+"admin/gestionarticles";
%>
<!-- inclure l ’en-tête de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="<%= action %>"><img src="<%=
urlapplication %>/img/articleadmin.png" border="0"
align="absmiddle"/> Liste des articles</a></div>
<form action="<%= action %>?action=validercreer" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0"
width="50%">
<tr><td>Nom</td><td><input type="text" name="nomarticle"
class="input" value=""/></td></tr>
<tr><td valign="top">Description</td><td><textarea
class="textarea" name="descriptionarticle"></textarea>
</td></tr>
<tr><td>Prix</td><td><input type="text" name="prixarticle"
class="input" value=""/> Euros</td></tr>
<tr><td>Date</td><td><input type="text" name="datearticle"
class="input" value=""/></td></tr>
<tr><td>Photo</td><td><input type="text" name="photoarticle"
class="input" value=""/></td></tr>
<tr><td>Vignette</td><td><input type="text"
name="vignettearticle" class="input" value=""/></td></tr>
<tr><td>Etat</td>
<td>
<%
out.println("<select name=\"etatarticle\" class=\"select\">");
out.println("<option value=\"1\">Actif</option>");
out.println("<option value=\"0\">Inactif</option>");
out.println("</select>");
%>
</td></tr>
<tr><td>Catégorie</td>
- 29 -© ENI Editions - All rigths reserved
<td>
<%
//liste des categories
out.println("<select name=\"id_categorie\" class=\"select\">");
for(int i=0;i<listecategoriearticles.size();i++)
{
//récupérer l ’objet dans la liste
Categorie categorie=(Categorie)listecategoriearticles.get(i);
out.println("<option value=\""+categorie.getId_categorie()
"\">"+categorie.getNomcategorie()+"</option>");
}
out.println("</select>");
%>
</td></tr>
<tr><td align="center" colspan="2"><input type="submit"
name="creer" value="Créer" class="bouton"/></td></tr>
</table><br/>
</form>
<%@ include file="../outils/piedpage.jspf" %>
LecodedelaServlet,déclenchéparl
’
action
validercreer
esttrèsprocheducodedemodificationd
’
unarticle.
...
//valider la création d ’un article
else if(action.equals("validercreer"))
{
//récupérer les données dans la requête et les
insérer dans le JavaBean article
Article article=new Article();
//création d ’une collection des paramètres reçus par la requête
HashMap map=new HashMap();
Enumeration names=request.getParameterNames();
while (names.hasMoreElements())
{
// chaque paramètre de la requête dans une collection
String name=(String)names.nextElement();
map.put(name,request.getParameterValues(name));
}
try
{
// insérer toutes nos données dans le Bean
article en une seule étape
BeanUtils.populate(article, map);
//modifier l ’article dans la base de données
int resinstruction=articlemodele.creerArticle(article);
//en cas d ’erreur avec la base de données,
ajouter un message d ’erreur
if(resinstruction!=1)
{
//ajouter un message d ’erreur
erreurs.add("Erreur lors de la création de l ’article");
}
//tout est OK
else
{
//ajouter un message de succes
succes.add("Création de l ’article réalisée avec succès");
}
//envoyer les listes de messages à la vue et
retourner sur la vue principale
request.setAttribute("erreurs",erreurs);
request.setAttribute("succes",succes);
//retourner sur la page d ’affichage des
articles en administration
getServletContext().getRequestDispatcher
("/admin/gestionarticles?action=liste").forward(request,
response);
}
catch(Exception e)
{
- 30 - © ENI Editions - All rigths reserved
System.out.println("Erreur lors de la lecture
des informations");
}
}
...
Enfin,lecodedelaméthode
creerArticle()
delaclassemodèleutiliseunerequêtepré
compilée.
/***********************************************************
* créer un article
***********************************************************/
public int creerArticle(Article article)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//insérer l ’article
requete=connection.prepareStatement("INSERT INTO article
(nomarticle,descriptionarticle,prixarticle,datearticle,photoarticle,
vignettearticle,etatarticle,id_categorie) VALUES (?,?,?,?,?,?,?,?)");
requete.setString(1,(String)article.getNomarticle());
requete.setString(2,(String)article.getDescriptionarticle());
requete.setString(3,(String)article.getPrixarticle());
requete.setString(4,(String)super.miseEnFormeDatevers
Base(article.getDatearticle()));
requete.setString(5,(String)article.getPhotoarticle());
requete.setString(6,(String)article.getVignettearticle());
requete.setString(7,(String)article.getEtatarticle());
requete.setString(8,(String)article.getId_categorie());
resinstruction=requete.executeUpdate();
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction creerArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction creerArticle");
}
}
//résultat de la création
return resinstruction;
}
- 31 -© ENI Editions - All rigths reserved
ModèleetJavaBean
1.Présentation
Nousavonsgéréjusqu
’
àmaintenantlesaccèsauxdonnéesaveclaméthode
getString(...)
duResultSet.Commepour
lagestiondesformulairesoulesfonctionnalitésXML,ilexisteunelibrairiequipermetdemanipulerlesResultSetavec
desJavaBeans.
CettelibrairienomméeDbUtilsestdéveloppéeparleconsortiumApache/Tomcat.CetteAPIestrelativementlégèreet
simpled
’
utilisation,ellepermetdesimplifierletravaildesdéveloppeurslorsducodagedesclassesmodèles.
2.Utilisation
Il faut d
’
abord télécharger la librairie à cette adresse : http://commons.apache.org/dbutils/. Son installation est
identiqueauxautreslibrairiesduprojet.Ilfautchargerlalibrairiedanslerépertoire/WEB
INF/lib
del
’
applicationet
insérersonchemindansleclasspath(ProjectProperties JavaBuildPath).CetteAPInécessiteunJDKversion
1.2aumoinsetJDBC2.0ousupérieur.
Nous allons utiliser les fonctionnalités de cette librairie dans la méthode ListeArticleAdmin de la classe modèle
ArticleModele
. Son utilisation est simple, il suffit de préciser la liste d
’
objets (ou l
’
objet) à renseigner à partir du
ResultSet.Laclassedbutilsvaensuitesechargerdegérerlesassociationsentrelesnomsdepropriétésdel
’
objet
concerné(
article
)etlesattributsdelarequête.
Lecodeprésentéci
dessousestdoncextrêmementsimplifié.
...
//exécuter la requête
if(rs!=null)
{
//stocker tous les articles dans une liste
while(rs.next())
{
//un enregistrement de plus
this.compteurenregistrement++;
//créer un objet article
Article article=new Article();
//renseigner l ’objet article avec ses accesseurs
if(rs.getString("id_article")==null)
article.setId_article("0");
else article.setId_article(rs.getString
("id_article"));
if(rs.getString("nomarticle")==null)
article.setNomarticle("");
else article.setNomarticle(rs.getString
("nomarticle"));
if(rs.getString("descriptionarticle")==null)
article.setDescriptionarticle("");
else article.setDescriptionarticle(rs.getString
("descriptionarticle"));
if(rs.getString("prixarticle")==null)
article.setPrixarticle("0");
else article.setPrixarticle(rs.getString
("prixarticle"));
if(rs.getString("datearticle")==null)
article.setDatearticle("0");
else article.setDatearticle
(super.miseEnFormeDate(rs.getString("datearticle")));
if(rs.getString("photoarticle")==null)
article.setPhotoarticle("");
else article.setPhotoarticle(rs.getString
("photoarticle"));
if(rs.getString("vignettearticle")==null)
article.setVignettearticle("");
else article.setVignettearticle(rs.getString
("vignettearticle"));
- 1 -© ENI Editions - All rigths reserved
if(rs.getString("etatarticle")==null)
article.setEtatarticle("0");
else article.setEtatarticle(rs.getString
("etatarticle"));
if(rs.getString("id_categorie")==null)
article.setNomcategoriearticle("");
else article.setNomcategoriearticle
(super.getNomCategorieArticle(rs.getString("id_categorie")));
//stocker l ’objet article dans la liste des articles
listearticle.add((Article)article);
}
}
...
Lenouveaucodeestalorsci
après.
...
BeanProcessor bp=new BeanProcessor();
listearticle = (ArrayList)bp.toBeanList(rs, Article.class);
//total des enregistrements
this.compteurenregistrement=listearticle.size();
...
Nous utilisons une instance de la classe BeanProcessor et sa fonction toBeanList() qui permet de transformer le
ResultSet(rs)enunecollection/liste(listearticle)d
’
objetsdelaclasse
Article(Article.class)
.
L
’
utilisationdeJavaBeanpermetdesimplifiertrèsclairementlecode.Lestestslourds,sourcesd
’
erreurssontainsi
évités.L
’
écritureducodeJDBCn
’
estpastrèsdifficilemaiscettetâcheesttrèsrépétitive.L
’
APIDbUtilsestsimpleà
utiliser, elle utilise le standard JavaBean mais nécessite une rigueur de programmation. Dans notre exemple de
gestiondesarticles,nousremarquonsquelenomdecatégorien
’
estplusaffiché.Eneffet,danslarequête,lenomde
lacatégorie de l
’
articlen
’
estpasretournémais sonidentifiant(
id_categorie
).À aucun momentla requête SQLne
retourne un attribut nommé nomcategoriequipermet de renseigner le JavaBean article parl
’
intermédiairedeson
setter()
.
Pourremédieràcela,ilfaudraitréaliserunejointuredanslarequêteSQLentrelatable
article
etlatable
categorie
.
Ensuite,ilfaudraitquelesnomsdesaccesseurssoientstrictementlesmêmesquelesnomsdesattributsdestables.
Enfin,avec cette technique,il nedoit pas yavoir deconflitsde nomsentre attributs.Eneffet, celanécessite de
nommerchaqueattributdelatabledemanièreunique(ex :
datearticle
,
datecategoriearticle
,
nomcategoriearticle
...).
Si par exemple, il existe un attribut
date
dans la table
article
et un attribut
date
dans la table
categorie
, lors de
l
’
exécutiondelarequêteetdurenseignementduResultSet,cederniernesaurapascommentgérerl
’
attributqu
’
il
trouveraendouble.
Ilexisteégalementunautreproblèmeàl
’
utilisationdesJavaBeansetdesResultSet
c
’
estlamodificationdesdonnées
lorsdesaccès.Eneffet,nousinséronsdesdonnéesbrutes,c
’
est
à
diretellesqu
’
ellessontprésentesdanslabase
de données. Dans notre gestion des articles, nous voyons que la date n
’
est pas dans le format correct.
Précédemment, nous avons réalisé une modification de la syntaxe dans la classe modèle lors de la lecture du
ResultSet
.
...
if(rs.getString("datearticle")==null)article.setDatearticle("0");
else article.setDatearticle(super.miseEnFormeDate(rs.getString
("datearticle")));
...
AveclesJavaBeans,cettemodificationseraitdoncopéréeducôtédelavueaumomentdel
’
affichage(cequid
’
ailleurs
n
’
estpasunemauvaisetechnique,ilestainsipossibledegérerdifférentesformesd
’
affichagesuivantlalanguepar
exemple :dateauformatanglaisoufrançais).
- 2 - © ENI Editions - All rigths reserved
3.Conclusion
L
’
utilisationdeJavaBeanetdebibliothèquesadaptéesdupointdevuedumodèleestparfoistrèsutileentermede
performancesetdelignedecode,maispourdescasévolués,ellepeutparcontrecomplexifierlecodeetnepasêtre
adaptée.
Dansnotreexempledegestiondesarticles,nousaurionspuutiliserlasolution100%JavaBeanenmodifiantchaque
nom d
’
attribut des tables, en utilisant une jointure sur la table
categorie
afin de retourner tous les attributs
nécessairesàl
’
affichageetenréalisantquelquesmodificationsdesyntaxeàl
’
affichage.
Maisledéveloppeurnegagnepasforcémentdetempsetcomplexifiel
’
ensemble.Parcontre,pourdesobjetssimples
(ex :mots
clés,notes,types,nomdecatégories,famillesouformats)àmanipuler,cettetechniqueesttrèsutile.Il
s
’
agiradoncdebienanalyserlediagrammedeclassesduprojetetlemodèleconceptueldesdonnéesafind
’
évaluer
quelletechniqueestlaplusadaptéeetdemélangerlesdeuxtechniquessuivantlapertinence(librairiesoucodepur
Java).
- 3 -© ENI Editions - All rigths reserved
Lestransactions
1.Présentation
Jusqu
’
àprésenttouteslescommandesSQLenvoyéesàlabasededonnéesétaientexécutéesimmédiatementetles
modificationsétaienteffectuéesdefaçondéfinitive(insertion,création,suppression...).
Supposonsdésormaisquenousinsérionsunarticleetunenotede5/10aumomentdelacréation.Siaumomentde
l
’
insertiondelanote,unepanned
’
alimentationsurvient,alorsilyauraunenregistrementincorrectdanslabasede
données(unarticlesansnote).
Danscetexemple,celan
’
auraitpasbeaucoupdeconséquencesmaisl
’
articleauraitunenotede0/10àl
’
affichage.En
revanche,pourlagestiondescommandesetdesventes,ilneseraitpasenvisageabled
’
avoirunefacturevide,de
l
’
argentencaissésansdonnéesassociées,desarticlesencommandesansfactureassociée...Danstouscescas,la
basededonnéessetrouveraitdansunétatinvalide.
Cessituationssontgéréesàl
’
aidedetransactions.Lerôled
’
unetransactionestdepasserlabasededonnéesd
’
un
état valide à un autre. Lorsque les opérations en cours sont validées, nous sommes certains que toutes les
modificationsontétéeffectuées.Siuneseuledesopérationséchoue,touteslesopérationssontannuléesetaucune
modificationn
’
estréalisée.
2.Utilisation
Nousallonsaborderlagestiondestransactionsavecleservicedenotationdesarticles.Enadministration,lorsdela
créationd
’
unarticle,nousallonsluiassocierunenotepardéfautde5/10.Nouscommençonsparréaliser/modifierla
fonction
creerArticle()
sanslagestiondestransactionsmaisaveclanotationassociéepardéfaut.
/***********************************************************
* créer un article
***********************************************************/
public int creerArticle(Article article)
{
int resinstruction=0;
int id_article=0;
PreparedStatement requete=null;
ResultSet clef=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//insérer la profession
requete=connection.prepareStatement("INSERT INTO article
(nomarticle,descriptionarticle,prixarticle,datearticle,photoarticle,
vignettearticle,etatarticle,id_categorie) VALUES (?,?,?,?,?,?,?,?)");
requete.setString(1,(String)article.getNomarticle());
requete.setString(2,(String)article.getDescriptionarticle());
requete.setString(3,(String)article.getPrixarticle());
requete.setString(4,(String)super.miseEnForme
DateversBase(article.getDatearticle()));
requete.setString(5,(String)article.getPhotoarticle());
requete.setString(6,(String)article.getVignettearticle());
requete.setString(7,(String)article.getEtatarticle());
requete.setString(8,(String)article.getId_categorie());
resinstruction=requete.executeUpdate();
//récupérer le numéro de la clé générée pour cet article
clef=requete.getGeneratedKeys();
if(clef.next())
{
//id_article inséré lors de la création
id_article=Integer.parseInt(clef.getObject(1).toString());
}
System.out.println("nous venons d ’insérer l ’article : "+id_article);
}
- 1 -© ENI Editions - All rigths reserved
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction creerArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction creerArticle");
}
}
//résultat de la création
return resinstruction;
}
Nous avons ajouté une partie importante du code qui est très souvent utilisée lors des développements et qui
permetderetournerlenumérodel
’
enregistrementquivientd
’
êtrecréé.LaméthodegetGeneratedKeys()del
’
objet
PreparedStatementpermetderéalisercetteopération.Nousinséronsdoncnotrenouvelarticle,nousrécupéronsle
numérodel
’
enregistrementetnousinséronsensuitesanotepardéfaut.
Lecodefonctionneldelaclasse
ArticleModele
estprésentéci
dessous :
...
/***********************************************************
* créer la note par défaut pour l ’article
***********************************************************/
public int ajouterNoteDefautArticle(String id_article,String id_client)
{
int resinstruction=0;
PreparedStatement requetea=null;
PreparedStatement requeteb=null;
try
{
//ouvrir une connexion
connection1=ds.getConnection();
//vérifier si la note n ’est pas déjà insérée
requetea=connection1.prepareStatement("SELECT note FROM notearticle
WHERE notearticle.id_article=? AND notearticle.id_client=?");
requetea.setString(1,(String)id_article);
requetea.setString(2,(String)id_client);
rs=requetea.executeQuery();
//exécuter la requête
if(rs!=null)
{
//on n ’a pas de note, l ’insérer
if(!rs.next())
{
//insérer la note de l ’article
requeteb=connection1.prepareStatement("INSERT INTO
notearticle(id_article,id_client,note) VALUES (?,?,?)");
requeteb.setString(1,(String)id_article);
requeteb.setString(2,(String)id_client);
requeteb.setString(3,(String)"5");
resinstruction=requeteb.executeUpdate();
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe ArticleModele.java fonction
- 2 - © ENI Editions - All rigths reserved
ajouterNoteDefautArticle");
}
finally
{
try
{
//fermer la connexion
if(requetea!=null)OutilsBaseDeDonnees.fermerConnexion(requetea);
if(requeteb!=null)OutilsBaseDeDonnees.fermerConnexion(requeteb);
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection1!=null)OutilsBaseDeDonnees.fermerConnexion
(connection1);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe ArticleModele.java
fonction ajouterNoteDefautArticle");
}
}
//résultat de la création de la note
return resinstruction;
}
...
Lecodepermetainsid
’
insérerl
’
articledanslabasededonnéesetdeluiassocierunenotepardéfautde5/10.
3.Gestiondestransactions
Dans certains systèmes SQL, il est nécessaire d
’
indiquer explicitement à la base de données qu
’
une transaction
commenceavantd
’
exécuterdescommandes.AvecJDBC,celan
’
estpasnécessaire,celui
cidémarreunetransaction
automatiquement.Latransactionpeutêtreterminéeautomatiquementoumanuellementparleprogrammeur.
Lechoixentrecesdeuxoptionsestdéterminéparlaconfigurationdelapropriétéautocommitdelaconnexion.Avec
JDBC,lavaleurpardéfautesttrue,lestransactionssontdoncvalidéesautomatiquementparlaconnexion.Lorsque
autocommitvautfalse,ilestdelaresponsabilitéduprogrammeurdeterminerexplicitementlestransactions.Laclasse
Connectionpossèdeplusieursméthodespermettantdemanipulerlestransactions :
●setAutoCommit(boolean) :permetlavalidationautomatiqueounondestransactions.
●commit() :validelatransactionencours.
●
close()
:permetdefermerlaconnexion(méthodeétudiéejusqu
’
ici).
●
rollback()
:permetd
’
annulerlatransactionenréalisantunretourarrièreàl
’
étatinitial.
Lorsque le code obtient une connexion d
’
un gestionnaire de pilotes, d
’
une source de données ou d
’
un pool de
connexions,JDBCexigequecelle
cisoitenmodeautocommit.Decefait,danslaplupartdesapplications,lapremière
méthodeappeléeaprès l
’
obtention d
’
une connexion estsetAutoCommit(boolean). Après l
’
exécution de ce code, la
gestion des transactions appartient au client. Le développeur peut créer des transactions contenant toutes les
requêtes.LescommandesSQLsontenvoyéesàlabasededonnéesetexécutées,maisellesnesontpasvalidées
tantquelatransactionn
’
estpasterminée.
Lavalidation intervient alorslorsque la méthode commit()del
’
objet Connectionestappelée etquele paramètre
autocommitestpositionnéàfalse.Latransactionpeutégalementêtreannuléesileprogrammeappellelaméthode
rollback()
.
Nous allons reprendre notre exemple précédent de création des articles et de l
’
insertion de la note avec les
transactions.
Pour le moment, nous modifions juste notre connexion JDBC afin d
’
indiquer que nos transactions doivent être
validéesmanuellementparlaméthodecommit().Dansnotreexemple,lorsd
’
uneinsertion,celle
cinedoitpasêtre
réaliséecariln
’
yapasdeméthodecommit()déclenchée.
Orsinousdéclenchonsunecréationd
’
article,nousremarquonsquel
’
articleestbiencréédanslabasededonnées.
Lagestiondestransactionsn
’
estdoncpasfonctionnelle.JDBCnedéclenchepasd
’
erreurmaisnegèrepaslemode
transactionnelcorrectement.Enfait,leproblèmenevientpasdeJDBCmaisdelabasededonnéesMySQL.Eneffet,
pardéfautMySQLutilisedestablesavecletypeMyISAM.LetypeMyISAMnepermetpasdegérerlestransactions.
Cependant, InnoDB fournit à MySQL un gestionnaire transactionnel avec un verrouillage de lignes. InnoDB a été
- 3 -© ENI Editions - All rigths reserved
conçupourmaximiserlesperformanceslorsdutraitementdegrandesquantitésdedonnées.
Afind
’
activerlagestiondestransactions,ilestdoncnécessairedeconvertirnostablesentypeInnoDB.Parcontre,
lestablesInnoDBnesupportentpaslesindexFULLTEXT,ilestdoncnécessairedelessupprimeraupréalable.
Nous allons changer le type de nos tables
article
et notearticle afin de passer de MyISAM à InnoDB avec les
commandesSQLsuivantes :
ALTER TABLE `article` TYPE = INNODB
ALTER TABLE `notearticle` TYPE = INNODB
Nouspouvonsvérifierlestypesdestablesenaffichantlastructure :
NosdeuxtablessontmaintenantdetypeInnoDB,nousallonsvérifierlagestiondestransactionsendéclenchantune
nouvellecréationd
’
article.Désormais,sinousdéclenchonsunecréationaveclecodeprécédent,celle
ciestlancée
correctement (requêtes SQL exécutées) mais les requêtes SQL ne sont pas validées, l
’
article n
’
est donc pas
véritablementinsérédansleSGBD.
Nousallonsmaintenantvalidermanuellementlestransactionsafindepouvoirréaliserunretourarrièreencasde
problème.
...
/***********************************************************
* créer un article
***********************************************************/
public int creerArticle(Article article)
{
int resinstruction=0;
String id_article=null;
PreparedStatement requete=null;
ResultSet clef=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//passer en mode manuel de gestion des transactions (autocommit
désactivé)
connection.setAutoCommit(false);
//insérer la profession
requete=connection.prepareStatement("INSERT INTO
article(nomarticle,descriptionarticle,prixarticle,datearticle,photoarticle,
vignettearticle,etatarticle,id_categorie) VALUES (?,?,?,?,?,?,?,?)");
requete.setString(1,(String)article.getNomarticle());
requete.setString(2,(String)article.getDescriptionarticle());
requete.setString(3,(String)article.getPrixarticle());
requete.setString(4,(String)super.miseEnFormeDateversBase
(article.getDatearticle()));
requete.setString(5,(String)article.getPhotoarticle());
requete.setString(6,(String)article.getVignettearticle());
requete.setString(7,(String)article.getEtatarticle());
requete.setString(8,(String)article.getId_categorie());
resinstruction=requete.executeUpdate();
//récupérer le numéro de la clé générée pour cet article
- 4 - © ENI Editions - All rigths reserved
clef=requete.getGeneratedKeys();
if(clef.next())
{
//id_article inséré lors de la création
id_article=clef.getObject(1).toString();
//insérer la note associée (id_article et client=0 soit
administration)
if(id_article!=null)
{
resinstruction=this.ajouterNoteDefautArticle(id_article,"0");
}
}
//exécuter la requete de création de l ’article si la tout est ok
if(resinstruction==1)
{
connection.commit();
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe ArticleModele.java fonction
creerArticle");
try
{
connection.rollback();
}
catch(Exception c)
{
System.out.println("Erreur de rollback de la transaction");
}
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe ArticleModele.java
fonction creerArticle");
}
}
//résultat de la création
return resinstruction;
}
Sila requête est correctement exécutée,un numérode clé d
’
articleest alorsrécupéré etla notepar défautest
ajoutée.Siuneerreuralieulorsdelacréationdelanote,l
’
instructioncommit()quivalideréellementlarequêtene
serapasexécutée,iln
’
yauradoncpasd
’
articlesansnoteassociéedanslabasededonnées.
L
’
instruction
connection.rollback()
permetderevenirenarrièreencasdedéclenchementd
’
exception.Pourtesterce
principe, nous allons volontairement modifier la requête d
’
ajoutde la noteafin d
’
insérer une erreur. Nous allons
simuleruneerreurSQLavecunaccèsàlatablenotearticleERREURquin
’
existepas.
...
/***********************************************************
* créer la note par défaut pour l ’article
***********************************************************/
public int ajouterNoteDefautArticle(String id_article,String id_client)
{
...
//vérifier si la note n ’est pas déjà insérée
requetea=connection1.prepareStatement("SELECT note FROM notearticleERREUR
WHERE notearticle.id_article=? AND notearticle.id_client=?");
- 5 -© ENI Editions - All rigths reserved
...
}
Désormais,sinousinséronsunnouvelarticleavecleformulairedecréation :nousréalisonsl
’
insertiondel
’
articleen
premier, ensuite nous ajoutons sa note par défaut. L
’
ajout de la note va déclencher une exception car la table
notearticleERREURn
’
existepas.Laméthode
rollback()
del
’
ajoutvadoncêtredéclenchée.Danslaméthodedecréation
del
’
articlelecommit()(resinstruction=0)n
’
estpasvalidé,l
’
articleseulsanssanoteneseradoncpasinséré.
Maintenantsinousvérifionsdanslabasededonnées,l
’
articlen
’
estpasinsérémalgrélefaitquesoninsertionétait
correcte.Nousavonsdoncbiengérédanslatotalitélatransactiondelacréationd
’
unarticle.
Dansunserviceprofessionnel,lestransactionsserontutiliséesdepréférence.Parcontre,ilestimportantdetester
chaquecas(créationcorrecte,incorrecte...)afindevérifierl
’
efficacitédel
’
ensembleetl
’
ordredesdéclenchements.
Lorsducodagedelagestiondestransactions,c
’
estessentiellementcetteétapequiestcompliquée.
LatechniquedegestiondestransactionsavecJavaJDBCesttrèsperfectionnéeetpermetdecontrôlerdespointsde
sauvegarde(annulationpartielle),decombinerlestransactionsetprocéduresstockées...
4.Optimisations
Nousallonsapporterquelquesoptimisationsconcernantlagestiondestransactions.Eneffet,laméthode
rollback()
nécessitelagestiond
’
unbloctrycatch.Nousallonsdoncutiliseruneméthodespécifiquedansnotreclassestatique
OutilsBaseDeDonnees.Nouspouvonsréaliserlamêmeopérationspourlaméthodecommit()
.
...
/***********************************************************
* commit Valide la transaction en cours
***********************************************************/
public static void commit(Connection con)
{
if(con!=null)
{
try
{
con.commit();
}
catch(Exception e)
{
System.out.println("Erreur lors du commit d ’une
connexion dans commit(Connection)");
}
}
}
/***********************************************************
* rollback Annule la transaction en cours
***********************************************************/
public static void rollback(Connection con)
{
if(con!=null)
{
try
{
con.rollback();
- 6 - © ENI Editions - All rigths reserved
}
catch(Exception e)
{
System.out.println("Erreur lors du rollback d ’une
connexion dans rollback(Connection)");
}
}
}
...
Unautreélémentimportantetlourdàgérerlorsdel
’
utilisationdestransactionsestlagestiondumodecommit.Nous
retrouvonsaprèschaquerécupérationdelaconnexionlaméthodesuivante :
//passer en mode manuel de gestion des transactions (autocommit
désactivé)
connection.setAutoCommit(false);
Lagestiondumodetransactionnelpeutêtregéréedirectementauniveaudupooldeconnexiondanslefichierde
déclaration de l
’
application(/Tomcat 6.0/conf/Catalina/localhost/betaboutiquemvc.xml). Il existe pour cela un attribut
nommé defaultAutoCommit qui permet de préciser le niveau de gestion de façon globale.
http://commons.apache.org/dbcp/configuration.html.
Pardéfaut,defaultAutoCommitestàtrueetpermetdevaliderautomatiquementlesrequêtes.Nousallonsdoncle
passeràfalseafindegérermanuellementlesexécutionsderequêtes.
<Context path="/betaboutiquemvc" reloadable="true"
docBase="E:\PROJETWEB\betaboutiquemvc"
workDir="E:\PROJETWEB\betaboutiquemvc\work" >
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"
defaultAutoCommit="false"/>
</Context>
Lestransactionsserontdoncgéréesmanuellement(sansêtreobligédepréciserlemodeautocommitàchaquefois)
etlesrequêtesnontransactionnellesserontexécutéessansproblèmemaisneserontpasdéclenchées.Ilestdonc
primordialdechoisirlemodeleplusimportantauniveaudupool.Sinotreapplicationutiliseplusdetransactionsnous
placeronsalorsdefaultAutoCommitàfalseetnousutiliseronslaméthodesetAutoCommit()auniveaudesrequêtes
nontransactionnelles.
Pour notre projet, nous utiliserons plus de requêtes non transactionnelles. Le paramètre defaultAutoCommitsera
donc placé à true (valeur par défaut) et nous utiliserons la méthode setAutoCommit(false) au niveau de chaque
méthodetransactionnelle.
Ilnousresteunedernièreméthodeàmodifier,quiconcernelasuppressiond
’
unarticle.Lorsdeladestructiond
’
un
enregistrement, il ne faut pas oublier de supprimer les notes associées et donc de rajouter une requête de
suppression.
/***********************************************************
* supprimer un article
***********************************************************/
public int supprimerArticle(String id_article)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("DELETE FROM
article WHERE article.id_article=?");
requete.setString(1,id_article);
resinstruction=requete.executeUpdate();
//supprimer les notes associées
this.supprimerNoteArticle(id_article);
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
- 7 -© ENI Editions - All rigths reserved
ArticleModele.java fonction supprimerArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction supprimerArticle");
}
}
//retourner le résultat de la suppression
return resinstruction;
}
/***********************************************************
* supprimer les notes de l ’article concerné
***********************************************************/
public void supprimerNoteArticle(String id_article)
{
PreparedStatement requete=null;
//Supprimer les notes associées à un article
try
{
//ouvrir une connexion
connection1=ds.getConnection();
requete=connection1.prepareStatement("DELETE FROM
notearticle WHERE id_article=?");
requete.setString(1,(String)id_article);
requete.executeUpdate();
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction supprimerNoteArticle");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection1!=null)OutilsBaseDeDonnees.fermerConnexion
(connection1);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ArticleModele.java fonction supprimerNoteArticle");
}
}
}
- 8 - © ENI Editions - All rigths reserved
MultilingueetJDBC
1.Présentation
Jusqu
’
àprésentnousavonstravaillésurdesdonnéesenfrançaismaisilestparfoisnécessairedegérerdesdonnées
dansd
’
autreslangues.S
’
ilfautgérerunsiteenfrançaisetenanglais,leproblèmeneseposepascarlejeude
caractèresfrançais/anglaisestidentique.
Maisquesepasse
t
ilquandondoitgérerparexemplelalanguearabeavecdeschaînescomplexes :
Pardéfaut,lessitesWeb(pagesHTML,pagesJSP,tablesMySQL...)sontréalisésaveclanormeISO
8859
1quiest
souventappeléeLatin
1.Cecodagesimplemaislimitéennombredecaractèresetnepermetpasdereprésenterles
caractèresd
’
autreslangues.Lavueci
dessousfaitréférenceaucodageISO
8859
1.
LecodageUTF
8estdéfinipourlescaractèresUnicode.Chaquecaractèreestcodésurunesuitedeunàquatre
octets.UTF
8eststandardisédanslaRFC3629.Cecodageestuniverseletpermetdereprésenterdesmilliersde
caractèresUnicode.Cecodageestégalementtrèsrapideetnécessitemoinsd
’
octetsquel
’
UTF
16.
2.Miseenplace
Si nous reprennons notre formulaire de création d
’
un nouvel article en administration, nous pouvons vérifier
l
’
utilisationdel
’
encodagecourantLatin
1(ISO
8859
1)aveclenavigateur(
Affichage
Encodagedescaractères
).
Maintenantnousallonsessayerd
’
insérerunnouvelarticleenArabe(doncaveclescaractèresUTF
8).Pourcela,nous
utilisons un fichier avec un texte d
’
exemple encodé en UTF
8. Pour le projet, nous utilisons un fichier nommé
langue.dat à la racine du projet. Pour changer l
’
encodage d
’
un fichier avec Eclipse il faut utiliser le menu File
Properties
Textfileencoding
.
- 1 -© ENI Editions - All rigths reserved
Nous utilisons un formulaire HTML en Latin
1 et une base de données en Latin1_swedish_ci. Nous faisons un
copié/collédutextearabedansleformulairedesaisieetnousvalidonslacréation.
Si nous vérifions notre saisie dans la table MySQL, nous remarquons que les données ne sont pas interprétées
correctement,lescaractèresontététransformésenentitésnumériques.Donc,lorsdel
’
affichageilyadesproblèmes
demiseenforme,lesrecherchesnefonctionnentplus,lestockagen
’
estpasaubonformat...
- 2 - © ENI Editions - All rigths reserved
Pour résoudre ce problème, il faut donc utiliser le format UTF
8 de A à Z lors des développements. Cette
programmationeststricteetnetolèreaucunoubli.Voicilesprécautionsàprendre :
●Utiliserunéditeurdedéveloppement100%UTF
8(EclipseestcompatibleUTF
8).
●EncoderlesfichiersdedéveloppementenUTF
8.
●Mettredesen
têtesUTF
8dansnospagesWeb.
●UtiliserunebasededonnéesenmodeUTF
8.
Pourl
’
instant nous avons bien respecté l
’
encodage de nos fichiers et nous utilisons un éditeur 100% UTF
8.Par
contre,nospagesnepossèdentpasd
’
en
têtesUTF
8.NousallonsmodifiernotrepageJSPentete.jspf.Eneffet,nous
utilisonspourlemomentl
’
encodagepardéfautLatin
1 :
<meta http -equiv="Content -Type" content="text/html;
charset=iso -8859-1">
NousmodifionslecharsetenutilisantUTF
8.
<meta http -equiv="Content -Type" content="text/html;
charset=utf -8">
Ensuite,nousinséronsentoutdébutdufichierentete.jspfunen
têteUTF
8afindeforcerl
’
encodage.
<%@ page contentType="text/html;charset=utf -8" %>
NousvenonsdepréciserànotrenavigateurquelesiteestenUTF
8.Nouspouvonsvérifiercetteopérationavecle
navigateurFirefoxenutilisantlemenu
Affichage
Encodagedescaractères,ilfautretrouverUnicode(UTF
8)coché.
LesiteestdésormaisenUTF
8dupointdevuedel
’
affichageetdelasaisiedesdonnées(formulaire).Parcontre,cet
encodagenécessitel
’
utilisationparprécaution,desentitésUTF
8danslespagesàlaplacedescaractèresspéciaux.
Ilneresteplusqu
’
àutiliserunebasededonnéesenmodeUTF
8.Pourcela,nousallonsconvertirnotrebasede
donnéesbetaboutique et ses tables en UTF
8.Nous utilisons leformat utf8_general_ci afinde gérer lacasse des
caractèresetlemaximumdesymboles.
Nousmodifionsl
’
encodagedelabasededonnéesaveclacommandesuivante :
ALTER DATABASE `betaboutique` DEFAULT CHARACTER SET utf8
COLLATE utf8_general_ci
Nousmodifionsensuitel
’
interclassementdechaquetabledelabasededonnéesafindemettreleschampsdela
tableenUTF
8.
- 3 -© ENI Editions - All rigths reserved
ALTER DATABASE `betaboutique` DEFAULT CHARACTER SET utf8
COLLATE utf8_general_ci
ALTER TABLE `article` DEFAULT CHARACTER SET utf8 COLLATE
utf8_general_ci
ALTER TABLE `categorie` DEFAULT CHARACTER SET utf8 COLLATE
utf8_general_ci
ALTER TABLE `client` DEFAULT CHARACTER SET utf8 COLLATE
utf8_general_ci
ALTER TABLE `commande` DEFAULT CHARACTER SET utf8 COLLATE
utf8_general_ci
ALTER TABLE `commandearticle` DEFAULT CHARACTER SET utf8
COLLATE utf8_general_ci
ALTER TABLE `notearticle` DEFAULT CHARACTER SET utf8
COLLATE utf8_general_ci
NotrebasededonnéesestmaintenantcompatibleUTF
8.
IlresteenfinàpasserlestypesdeschampsenUTF
8.Sinousprenonslatable
article
parexemple,celle
ciestbien
enUTF
8maisseschampssontencoreenLatin
1,nousutilisonsdoncphpmyadminpourchangerl
’
interclassement
deschamps.
ALTER TABLE `categorie` CHANGE `nomcategorie` `nomcategorie`
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `article` CHANGE `nomarticle` `nomarticle`
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `article` CHANGE `descriptionarticle`
`descriptionarticle` TEXT CHARACTER SET utf8 COLLATE
utf8_general_ci NOT NULL
ALTER TABLE `article` CHANGE `photoarticle` `photoarticle`
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `article` CHANGE `vignettearticle`
`vignettearticle` VARCHAR( 255 ) CHARACTER SET utf8
COLLATE utf8_general_ci NOT NULL
ALTER TABLE `client` CHANGE `identifiantclient`
`identifiantclient` VARCHAR( 50 ) CHARACTER SET utf8
COLLATE utf8_general_ci NOT NULL
ALTER TABLE `client` CHANGE `motdepasseclient`
`motdepasseclient` VARCHAR( 50 ) CHARACTER SET utf8
COLLATE utf8_general_ci NOT NULL
ALTER TABLE `client` CHANGE `nomclient` `nomclient`
VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `prenomclient` `prenomclient`
VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `emailclient` `emailclient`
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `telephoneclient` `telephoneclient`
VARCHAR( 15 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `adresseclient` `adresseclient`
- 4 - © ENI Editions - All rigths reserved
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `villeclient` `villeclient`
VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `client` CHANGE `codepostalclient`
`codepostalclient` VARCHAR( 10 ) CHARACTER SET utf8 COLLATE
utf8_general_ci NOT NULL
ALTER TABLE `client` CHANGE `paysclient` `paysclient`
VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
ALTER TABLE `commande` CHANGE `totalcommande` `totalcommande`
VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT
NULL
Nousallonsvérifierlefonctionnementdel
’
ensembleenréalisantunenouvellesaisied
’
unarticleparcopié
collé.
Nousvoyonsquel
’
insertionestincorrecte.Lesdonnéesontététraitéessansprécisiondel
’
encodagereçu.Lecode
reçuestenUTF
8souslaformed
’
unfluxd
’
octetsISO
8859
1.
Pourrésoudreceproblème,nousdevonsdonctravailleraveclesfluxaumomentdel
’
insertion(oumodification)des
donnéesdanslabase.Nousallonsmodifiernotrerequêtedecréationafindetravailleravecunfluxd
’
octetsetnon
descaractères.
...
//inserer l ’article
requete=connection.prepareStatement("INSERT INTO
article(nomarticle,descriptionarticle,prixarticle,datearticle,
photoarticle,vignettearticle,etatarticle,id_categorie) VALUES
(?,?,?,?,?,?,?,?)");
requete.setString(1,(String)article.getNomarticle());
requete.setString(2,(String)article.getDescriptionarticle());
Codeavecfluxd
’
octets:
...
//inserer l ’article
requete=connection.prepareStatement("INSERT INTO
article(nomarticle,descriptionarticle,prixarticle,datearticle,
- 5 -© ENI Editions - All rigths reserved
photoarticle,vignettearticle,etatarticle,id_categorie) VALUES
(?,?,?,?,?,?,?,?)");
requete.setBytes(1,article.getNomarticle().getBytes
("ISO-8859-1"));
requete.setBytes(2,article.getDescriptionarticle().getBytes
("ISO-8859-1"));
Nousgéronsdansnotreexemple,lenomdel
’
articleetsadescriptionenUnicodeUTF
8.Nousrecommençonslasaisie
etnousvérifionsquelesopérationssontcorrectes.
Lesdonnéessont correctement inséréesdansla base dedonnées(nous pourrons ainsiréaliserdes recherches,
testsetcomparaisonssurlachaînecorrecte)etaffichéesauformatUnicodedanslenavigateur.Ilfautaussimodifier
notrerequêtedemodification(pasbesoindemodifierlesrequêtesdelecturecarlesdonnéessontluesauformat
Unicode)pourlenometladescriptiondel
’
article.
Notresystèmedegestiondesarticles (nometdescriptiondanscecas)est désormais multilingue.Nouspouvons
vérifierl
’
intégritédel
’
ensembleenchangeantlecodagedelalanguedunavigateur.Sinouspassonsde
Affichage
Encodagedescaractères Unicode(UTF8)àOccidental(ISO88591),nousretrouvonslachaînedecaractères
quicorrespondauproblèmed
’
insertionprécédent(codageISO
8859
1).
Parfois,avecl
’
utilisationd
’
unpooldeconnexionJDBC,lemécanismededonnéesenUTF
8nefonctionnepas.
Pour cela, il faut ajouter les paramètres suivants au pool de connexion :
useUnicode=yes&;characterEncoding=UTF
8. Il faut préciser cela dans la partie de connexion à la base de
donnéesdel
’
applicationdanslefichierdegestiondel
’
applicationurl="jdbc:mysql://localhost:3306/betaboutique?
useUnicode=yes&;characterEncoding=UTF
8"(server.xmloufichierspécifiqueàl
’
application).
Nouspouvonsterminercettesectionenapportantquelquesaméliorationsàl
’
ensemble.Ilestpossibleparexemple
d
’
ajouterunfichierdetraductiondesmessages(encasdesuccèsetd
’
échec).Ainsi,suivantlalanguelesmessages
serontautomatiquementtraduits.
- 6 - © ENI Editions - All rigths reserved
AuthentificationetRealms
1.Présentation
LeprotocoleHTTPfonctionnesouslaformederequête
réponsepermettantdedemanderlesidentitésdesutilisateurs
et de fournir l
’
accès aux ressources sur la base de ces identités. Lorsqu
’
une requête arrive à destination d
’
une
ressourceprotégée,leserveurWebvérifiesilenavigateuraenvoyélesinformationsd
’
authentification.Siteln
’
estpas
le cas, le serveur envoie une page d
’
erreur avec le code 401 au navigateur en précisant le type d
’
information
d
’
authentificationqu
’
ilattend.
Le navigateur affiche alors une boîte ou un formulaire d
’
authentification afin de demander à l
’
utilisateur les
informations.Si l
’
utilisateurestprécédemmentauthentifié,lenavigateurutilisealorssoncachelocals
’
ildétientles
renseignementsd
’
uneprécédenterequête.
2.Lestypesd
’
authentification
a.Authentificationdebase
Leschémad
’
authentificationHTTPleplussimplesenommeautorisationdebase(BasicAuthorization).Cemécanisme
consisteàutiliserunnomd
’
utilisateur(identifiant)etunmotdepasseenclair.Lorsqu
’
unutilisateurconsultepourla
premièrefoisuneressourceprotégée,ildoitalorssaisirsesinformationsdeconnexiondansuneboîtededialogue
semblableàcelleci
dessous.
b.AuthentificationparDigest
Avecletyped
’
authentificationbasique,l
’
identifiantdel
’
utilisateuretlemotdepassesontenvoyésenclairparle
navigateuravecchaquerequêtedestinéeauserveur.Ils
’
agitlàd
’
unmoyentrèssimplepourgérerl
’
authentification
utilisateur.Ilexisteégalement unautreschémad
’
authentificationquiautoriseles transmissionscodées.Avecce
typed
’
autorisation,l
’
utilisateursaisitsonmotdepasseenclair,maiscelui
ciestenvoyésousformecryptéàpartir
d
’
unechaînedecaractèressurleréseau.DanscecascetteauthentificationestappeléeauthentificationparDigest
(
DigestAuth)etutilisel
’
algorithmedehachageunilatéralSHAouMD5.
c.Authentificationparcertificat
Le troisième schéma d
’
authentification principal se fonde sur les certificats. Avec ce type d
’
authentification,
l
’
utilisateurdoitposséderuncertificatclientpouraccéderauserveur.Cetteapprocheestlaplussûrepuisqueles
certificatssontgérésdefaçoncentraliséepardesautoritésspécialiséestelqueVeriSignouThawte.
3.GestiondesRealms
LeprotocoleHTTPmetenœuvreuneauthentificationàbasedeRealm.UnRealmestuneentitévérifiantlesdroitsdes
utilisateurspourdesaccèsàdesressourcesprotégées.
Sous Apache et autres serveurs Web, la configuration des Realms est gérée en ajoutant un fichier .htaccess au
répertoiredebasedelazoneàsécuriser.Lesinformationsrelativesauxutilisateursetauxrôlessontstockéesdans
unfichiertexteouencoredansunebasededonnées.
Un Realm est un dispositif normalisé qui permet d
’
identifier les utilisateurs. Il effectue ainsi l
’
association entre
l
’
identifiantetlemotdepasseafin de déterminersil
’
utilisateurestcorrectementauthentifiéounon.Pourchaque
utilisateur,leRealmconnaîtlalistedesrôlesassociés.Lesrôlessontquantàeux,lesresponsabilitésattribuéesà
chaqueutilisateur.
Laprotectiondesressourcesestréaliséeenfonctiondesrôles,c
’
est
à
direqu
’
ilfautpréciserlerôledontdoitdisposer
unutilisateurpouraccéderàlaressourcedemandée.UnRealmpeutêtreparamétréauniveaudelabalise
<Engine>
,
c
’
est
à
direpartagépartouteslesapplications,auniveau<Host>pourlesapplicationsd
’
unhôtevirtuelouauniveau
<Context>
pouruneapplicationspécifique.
LeserveurJavaTomcatdisposed
’
unmécanismeintégrétrèssouplepourgérerlesRealms.Ilestpossibled
’
utiliserle
fichierserver.xml,lefichierdeconfigurationspécifiqueàl
’
application,unebasededonnées,lesdonnéesd
’
unserveur
LDAPouunesourceJNDI.Touscesmécanismesoffrentlemoyend
’
authentifierlesutilisateursetdelesassocieràdes
rôles.
Lesrèglesd
’
utilisationdesRealmssontlessuivantes :
- 1 -© ENI Editions - All rigths reserved
●Toututilisateurpeutêtremembred
’
aucunoudeplusieursrôles.
●Touslesrôlespeuventconteniraucunouplusieursutilisateurs.
●LesRealmspermettentdedéfinirlesutilisateursquipeuventaccéderàdesressourcesenfournissantlaliste
d
’
unoudeplusieursrôle(s)disposantd
’
undroitd
’
accès.
4.MiseenplacedesRealms
Lefichierquicontientlesinformationsd
’
authentificationrelativesàTomcatestprésentdanslerépertoire
/conf
etse
nommetomcat
users.xml.Voiciunexempledefichierdeconfigurationaveclesutilisateurstomcatetadmin.L
’
utilisateur
tomcatpossèdelerôletomcatetl
’
utilisateuradminpossèdelesrôlesadminetmanager
.
<?xml version= ’1.0’ encoding= ’utf-8’?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="admin" password="admin" roles="admin,manager"/>
</tomcat -users>
CefichiertexteauformatXMLpermetdedéfinirlesrôlesmaisilresteencoreàconfigurerl
’
applicationafind
’
associer
cesinformationsd
’
authentificationàunRealm.CettemiseenplaceestréaliséeparlabaliseXML
<Realm>
présente
danslefichierdeconfigurationduserveur(server.xml)oudanslefichierdeconfigurationpropreàl
’
application.
Voiciunexempledeconfigurationprésentdanslefichierserver.xml
:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
Ladernièreétapeconsisteàactiverl
’
authentificationpouruneressourcedonnée.Cetteactionestréaliséeauseindu
fichierdeconfigurationdel
’
applicationweb.xml.Lenœud<security
constraint>estl
’
élémentdeconfigurationquidoit
êtreplacéàlafindudescripteurdedéploiementdel
’
application.
...
<!-- définir les accès sécurisés -->
<security -constraint>
<display -name>Authentification</display -name>
<web -resource -collection>
<web -resource -name>page securisee</web -resource -name>
<url -pattern>/admin/*</url -pattern>
</web -resource -collection>
<auth -constraint>
<role -name>admin</role -name>
</auth -constraint>
</security -constraint>
<login-config>
<auth -method>BASIC</auth -method>
<realm -name>Espace administration</realm -name>
</login-config>
<security -role>
<description>Administrateur</description>
<role-name>admin</role -name>
</security -role>
...
La définition précédente permet de protéger tous les accès aux pages incluses dans l
’
application à partir de
l
’
URL/admindel
’
application.Cettedéfinitionexigequelesutilisateurssoientmembredurôleadmin.L
’
authentification
utiliséeestdetypebasique.
Nouspouvonsautoriserdifférentescontraintespourdifférentesméthodesd
’
accès.Ainsilesméthodesd
’
accèsHTTP
peuventêtrelimitées.Nouspouvonsainsiutiliserunecontraintedesécuritécommepourlimiterl
’
accèsauxméthodes
PUT,DELETE,GETetPOST.Lenœud
<login
config>
permetdepréciserletyped
’
authentificationetlemessageaffiché
danslaboîted
’
authentification(
<realm
name>
).
- 2 - © ENI Editions - All rigths reserved
Pourrésumer,lamiseenplaced
’
unsystèmed
’
authentificationbasésurunRealm,nécessitelesétapessuivantes :
●Définitiondesutilisateursetdesrôlesdansunesourcededonnées(fichier,annuaire,basededonnées...).
●Définitiondelamiseenplacedesaccèssécurisésauseind
’
unfichierdeconfiguration.
●Définitiondesressourcesàprotéger.
5.Realmetbasededonnées
Lamiseenplaced
’
unsystèmed
’
authentificationbasésurdesfichiersestsimplemaiscetteapprochen
’
estpastrès
soupleentermedesécuritéetdemiseàjour.Unesimplecréationoumodificationdanslefichierdeconfiguration(ex :
tomcat
users.xml)peutcorromprelesystèmed
’
authentification.Ilestégalementpeuprudentdelaisserlesmotsde
passefigurerenclairsurunfichierlisibleparcertainsutilisateurs.
Il existe deux adaptateurs qui permettent de gérer les authentifications à partir d
’
une base de données. L
’
objet
JDBCRealm
stockelesinformationsrelativesàl
’
utilisateuretaurôledansunebasededonnéesetyaccèdeviaJDBC.
L
’
objetJNDIRealmreposesurlemêmeprincipemaisutiliseunpooldeconnexionJNDI.
Cesobjetssonttrèssouplesetpermettentdedéfinirdestablesspécifiquespourgérerlesutilisateursetlesrôles,
maisaussid
’
accéderauxinformationsrelativesauxutilisateursetauxrôlesdansunebasededonnéesexistante.
Nousallonsmettreenplaceunsystèmed
’
authentificationpourleprojetbetaboutiqueavecunesourcededonnées
JNDI. L
’
utilisation des Realms avec une base de données nécessite deux tables. La première contient une
correspondanceentrelenomd
’
utilisateuretlemotdepasse.Lasecondetablecontientlacorrespondanceentreles
utilisateursetlesrôlesauxquelsilsappartiennent.Lenomdescolonnesainsiquel
’
existenced
’
autrescolonnesdans
les tables ou d
’
autres tables dans la base de données, importent peu. Il est tout à fait possible d
’
adapter une
application existante pour utiliser les Realms. Il suffit pour cela de bien déclarer l
’
application et de respecter les
directivesdeTomcat.
Pournotreprojet,nousallonscréerdeuxtablesafindegérerl
’
authentificationenadministration.Lapremièretable
contient les informations sur les utilisateurs/administrateurs de l
’
application. Cette table possède la structure
suivante :
Lechampnomadministrateurcorrespondàl
’
identifiantdeconnexionquiserautilisédanslaboîted
’
authentification.La
seconde table roles possède les différents rôles de la base de données avec la correspondance des
utilisateurs/administrateurs.
- 3 -© ENI Editions - All rigths reserved
LastructureminimaleimposéeparTomcatpossèdetroischampsquisontlenomd
’
utilisateur,sonmotdepasseetun
rôle.Ilest fortement conseillédemettre le nomd
’
utilisateurencléprimaireafin d
’
avoirunindexuniquesurcette
table.Pourlecasdel
’
utilisationdeplusieursrôlesaveclemêmeutilisateur,lacléprimairepeutêtrelaconcaténation
deschampsnometrôle.
Nousreprenonslefichierdeconfigurationdenotreapplication(betaboutiquemvc.xml
):
<Context path="/betaboutiquemvc" reloadable="true"
docBase="C:\PROJETWEB\applications\betaboutiquemvc"
workDir="C:\PROJETWEB\applications\betaboutiquemvc\work">
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root"
password="" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10" />
</Context>
NousretrouvonsladéfinitiondelasourcededonnéesJNDIainsiquelaconfigurationdel
’
applicationbetaboutiquemvc
.
NousallonsmaintenantdéfinirnotreRealmafind
’
utiliserlesinformationsd
’
authentificationprésentesdanslabasede
donnéesassociéeaupooldeconnexion.
<Context path="/betaboutiquemvc" reloadable="true"
docBase="C:\PROJETWEB\applications\betaboutiquemvc"
workDir="C:\PROJETWEB\applications\betaboutiquemvc\work">
<Resource name="jdbc_betaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root"
password="" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/betaboutique"
maxActive="20" maxIdle="10" />
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc_betaboutiquemysql"
localDataSource="true"
userTable="administrateur"
userNameCol="nomadministrateur"
userCredCol="motdepasseadministrateur"
userRoleTable="roles"
roleNameCol="role"
/>
</Context>
La balise
<Realm>
précise la source de données utilisée (dataSourceName), le nom de la table qui contient les
utilisateurs(userTable),lechampassocié(userNameCol),lechampdesmotsdepasseutilisateur(userCredCol)etenfin
la table de gestion des rôles (userRoleTable) et la colonne correspondante (
roleNameCol
). L
’
attribut
localDataSource=
’’
true
’’
permetdepréciserquelasourcededonnéesJNDIestdéclaréeauniveauduContextetnon
dansunezoneglobaledelaconfigurationduserveur.
LeRealmestmaintenantopérationnel,ilrestejusteladernièreétapequiconsisteàprotégerlespages/ressources
pourlesaccès.Commeindiquéprécédemment,laconfigurationdelasécuritéetdel
’
accèsauxressourcesestréalisée
danslefichier web.xmldel
’
application.Lespagesàprotégeretlesrôlesnécessairespouryaccédersontdéclarés
danslesbalises <security
constraint>.Labalise
<display
name>permetde définirlenom delacontrainte poursa
gestionpardesoutilsexternes(logicielspécialisé,applicationJWS...).Lespagessontprotégéesparl
’
intermédiairede
labalise
<web
resource
name>etuneouplusieursbalises
<url
pattern>quipermettentdespécifierlemotifdesURLà
protéger.Labalise<auth
constraint>indiquelesrôlesàavoiretéventuellementlesméthodesHTTPquinécessitent
une authentification. La balise
<login
config>
définit la façon dont s
’
effectue la connexion. La balise
<real
name>
indiquelenomdelazoneàprotégeretlabalise<auth
method>définitletypedeconnexion.Nousretrouvonsles
quatretypesd
’
authentificationquesont :
●BASIC(authentificationcouranteparboîtededialogue).
- 4 - © ENI Editions - All rigths reserved
●DIGEST(lemotdepasseestcryptéavantl
’
envoiparlenavigateur).
●FORM(l
’
authentificationesteffectuéeviaunformulairecrééparledéveloppeur).
●CLIENT
CERT(authentificationSSLbaséesuruncertificatclient).
Pournotreprojetbetaboutique,nousallonsdéfiniruneauthentificationbaséesurunRealmnomméAuthentificationqui
estpositionnépourlespagesaccessiblesparl
’
URL/admin.Lerôlenécessairepouraccéderàcetteapplicationest
adminetl
’
authentificationestdetype
BASIC
.
...
<!-- définir les accès sécurisés -->
<security -constraint>
<display -name>Authentification</display -name>
<web -resource -collection>
<web -resource -name>page securisee</web -resource -name>
<url -pattern>/admin/*</url -pattern>
</web -resource -collection>
<auth -constraint>
<role -name>admin</role -name>
</auth -constraint>
</security -constraint>
<login-config>
<auth -method>BASIC</auth -method>
<realm -name>Espace administration</realm -name>
</login-config>
<security -role>
<description>Administrateur</description>
<role -name>admin</role -name>
</security -role>
...
Sinousutilisonsnotreapplicationetquenousaccédonsàlapartieadministrationduprojetouàunesous
page,la
fenêtred
’
authentificationestalorsaffichée.
Si l
’
authentification n
’
est pas correcte, nous pouvons utiliser les
Logger
Tomcat afin de connaître la source du
problème.Labalise
Logger
estalorsplacéeauniveauduRealmdanslaconfiguration.
<Logger className="org.apache.catalina.logger.SystemOutLogger"
verbosity="99"/>
Lajournalisation esteffectuée versla sortie standard qui est la consoleEclipse pournotre projet.Ce mécanisme
d
’
authentificationtrèspuissant,permetdegérerlesressourcesdefaçonsimpleetsécurisée.Lorsqu
’
unutilisateur
tented
’
accéderàuneressourceprotégéepourlapremièrefois,laméthodeauthenticate()duRealmestdéclenchée.
Ainsi,tousleschangements effectués directementsurlabase de données,telsquela configuration denouveaux
comptes,lamodificationdemotsdepasse,lesrôles...sontimmédiatementprisencompte.
Unefoisl
’
authentificationréalisée,l
’
utilisateuretsesrôlesassociéssontmisencachedansTomcatduranttoutela
sessiondel
’
utilisateur.Cettesessiondurejusqu
’
àcequel
’
utilisateurfermesonnavigateur.Touteslesmodifications
effectuéessurlesinformationsdelabasededonnéesrelativesàl
’
utilisateurdéjàauthentifiéneserontpasprisesen
compteavantlaprochaineconnexiondecemêmeutilisateur.
- 5 -© ENI Editions - All rigths reserved
6.InformationsrelativesauRealm
Ledéveloppeurdisposedeplusieursmécanismespourmanipulerlesinformationssuiteàuneauthentificationbasée
surlesRealms.L
’
objetrequestpossèdeplusieursméthodespourtesterlesrôlesetlesaccès.
//accès qu ’avec le rôle administrateur
if(!request.isUserInRole("admin")) return mapping.findForward
("accueiladmin");
Le code présenté ci
dessus permet par exemple, de vérifier que l
’
utilisateur possède bien le rôle admin avant
d
’
accéderàlapage.Sinon,uneredirectionestréaliséesurlapaged
’
accueil(déclaréeavecunevariableglobale).Ce
codepeutparlasuiteêtreinsérédansunfiltreafindevérifierl
’
authentificationsurlesservicesd
’
unsiteInternet.
Nouspouvonségalementrécupérerlesinformationsdeconnexionavecl
’
objetrequest
.
//récupérer l ’identifiant/nom de l ’utilisateur connecté
String identifiant=request.getRemoteUser();
String role="";
if(request.isUserInRole("admin"))role="Administrateur";
else if(request.isUserInRole("client"))role="Client";
Silabasededonnéesouleserveurd
’
annuaireneseconformentpasauxexigencesdesspécifications,nouspouvons
écrirenotrepropreclasseRealmenimplémentantsimplementl
’
interface
org.apache.catalina.Realm
.
7.Realmetsécurité
L
’
authentificationdebaseestfacileàimplémenter.Lesmotsdepassepeuélaboréssontfacilementdevinésetilest
alorsimpossiblededistinguerl
’
utilisateurréeld
’
uneautrepersonnequiutiliselesmêmesnomd
’
utilisateuretmotde
passe.
Letraficnoncryptépeutfacilementêtremisendangerpartoutepersonneconnectéesurlemêmeréseau.Lerecours
auréseauHTTPSpermetdepalliercettelimitationencodantchaquepaquetavantdel
’
envoyersurleréseauInternet.
L
’
accèsauxnomsd
’
utilisateuretauxmotsdepassetransmiss
’
avèrealorsbeaucoupplusdifficile.
Un moyen sécurisé pour gérer l
’
authentification consiste à utiliser des algorithmes de cryptage. Ces algorithmes
permettentdecrypterlemotdepasseàl
’
aided
’
unalgorithmedehachageunilatéraltelqueMD5ouSHA.Lerésultat
delachaîneneprésentealorsaucuneressemblanceaveclachaîned
’
origine.Côté client,le même algorithmeest
utilisépourtraiterlemotdepasseaveclequelleclienttentedeseconnecteretlachaînecodéeestenvoyéeau
serveurpourcomparaison.Sileschaînescorrespondent,lesmotsdepassesontalorsconsidéréscommeidentiques.
Tomcat offre des outils permettant de générer les mots de passe cryptés manuellement à partir de lignes de
commandesoudefaçondynamique.PourgénérerunchiffrementdynamiqueMD5,nouspouvonsutiliserlaméthode
statiqueDigest()delaclasseRealmBase
.
Lecodesuivantpermetdegénérerdesmotsdepassecryptésselonl
’
algorithmepréciséenparamètre.
import org.apache.catalina.realm.RealmBase;
...
String motDePasseEnClair="monmotdepasse";
String motDePasseCrypte= RealmBase.Digest(motDePasseEnClair,"MD5");
...
Pourutiliserdesmotsdepassecryptésdansl
’
implémentationduRealm,nousdevonsstockercesdonnéeschiffrées
dans une base de données ou un fichier texte. Ensuite, lors de la configuration du Realm, nous devons ajouter
l
’
attributdigestdéfinissantl
’
algorithmeutilisépourlecodagedesmotsdepasse.
<Realm className="org.apache.catalina.realm.DataSourceRealm"
digest="MD5"
dataSourceName="jdbc_betaboutiquemysql"
localDataSource="true"
userTable="administrateur"
userNameCol="nomadministrateur"
userCredCol="motdepasseadministrateur"
userRoleTable="roles"
roleNameCol="role"
/>
D
’
autresoptionsexistent,outrelesméthodesd
’
authentificationstandards,unetechniquetrèssécuriséeconsisteà
utiliserdessystèmesdeconnexionbaséssurdesmotsdepasseàusageuniqueetdesmotsdepasselimitésdansle
tempstelsqueSecurID.Lacombinaisond
’
unmotdepassequel
’
utilisateurconnaît,avecunnombregénéréparla
- 6 - © ENI Editions - All rigths reserved
machinequel
’
utilisateurpossèdepermetdecréerunschémad
’
identificationtrèsfort.
8.Realmetformulairepersonnalisé
L
’
authentification de type
BASIC
présentée dans la section précédente est très simple à mettre en œuvre mais
présentel
’
inconvénientdenepasêtregéréeavecunformulairedéveloppéparleprogrammeurdel
’
application(moins
desouplesse,moinsergonomique,pasledesigndusite...).
AveclesRealms,nouspouvonsmettreenplacelemêmetyped
’
authentificationavecunformulaireJSP(HTML).Ce
formulairedoitêtreconformeauxspécificationsdeTomcatetposséderleschampssuivants :
●Lechamppourlenomd
’
utilisateur(identifiant)quidoits
’
appelerj_username
.
●Lechamppourlemotdepassequidoits
’
appelerj_password
.
●L
’
actionduformulairequidoitêtrej_security_check
.
Nous allons donc créer notre propre formulaire d
’
authentification nommé authentification.jsp présent dans le
répertoire/vues
.
<form action="j_security_check" method="POST">
<input type="text" name="j_username"/>
<input type="password" name="j_password"/>
<input type="submit" name="Connexion"/>
</form>
Nousdevonsensuitecréerunepaged
’
erreurquiseraréférencéedanslefichierdeconfiguration.Cesdeuxpages
(
authentification.jspeterreurauthentification.jsp)peuventsetrouverdansunrépertoireprotégé.
Nous devons maintenant configurer le Realm au sein du fichier web.xml afin d
’
utiliser notre formulaire
d
’
authentification.Labalise
<login
config>
permetdeparamétrerlesauthentificationsbaséessurunformulaire.
...
<!-- définir les accès sécurisés -->
<security -constraint>
<display -name>Authentification</display -name>
<web -resource -collection>
<web -resource -name>page securisee</web -resource -name>
<url -pattern>/admin/*</url -pattern>
</web -resource -collection>
<auth -constraint>
<role -name>admin</role -name>
</auth -constraint>
</security -constraint>
<login-config>
<auth -method>FORM</auth -method>
<realm -name>default</realm -name>
<form -login-config>
<form -login-page>/vues/authentification.jsp</form -login-page>
<form -error-page>/vues/erreurauthentification.jsp</form -error-page>
</form -login-config>
</login-config>
<security -role>
<description>Administrateur</description>
<role -name>admin</role -name>
</security -role>
...
- 7 -© ENI Editions - All rigths reserved
Lesnomsdespagesd
’
authentificationetd
’
erreursontlibresetsontparamétrésaveclesbalises
<form
login
page>
et
<form
error
page>
.
CettemiseenplacenécessiteunredémarragedeTomcat.Eneffet,lesconfigurationsdelaconnexionsont
conservées dans le cache local et sont donc présentes durant toute la durée de vie de l
’
application.
L
’
utilisationdesRealmsàpartird
’
unformulairepersonnalisépermetdegérerladéconnexiondesutilisateurs.En
effet,lesRealmsutilisentlemécanismedesessionpour stocker lesinformations.Ladestructiond
’
unesession
permetainsidedéconnecterl
’
utilisateurcourant.
Sinousessayonsd
’
accéderdésormaisàunepageprotégéedel
’
application,leformulaired
’
authentificationestalors
affichéenlieuetplacedelafenêtreHTTP.
Encasdemauvaisesaisie,c
’
estnotrepaged
’
erreurquiestdéclenchée.
Enfin,siunutilisateurcorrectestutilisépourl
’
authentification,l
’
accèsauxressourcesprotégéesestalorsautorisé.
9.Enrésumé
CechapitreaprésentéJavaetl
’
utilisationdesbasesdedonnées.Lapremièrepartieaconcernélaprésentationdes
différentstypesdepilotesJDBCetl
’
utilisationd
’
uneconnexionassociée.Lesinterfacesutilitairesontétéexpliquées
pourlapréparationdesrequêtes,l
’
exécutionderequêtespré
compiléesetleparcoursdesrésultats.
Dansundeuxièmetemps,le guideaintroduitleprincipedu partagedeconnexionsparl
’
intermédiaired
’
exemples
concrets.
La troisième partie a introduit la notion d
’
écouteur afin de mettre en place des connexions partagées. La partie
suivanteadéveloppélanotiondepooldeconnexionsquisontutiliséslorsdedéveloppementsdeprojetscomplexes.
LacinquièmepartieainsistésurlemodèleMVCetl
’
utilisationdesbasesdedonnéespourcemodèledeconception.
Ledéveloppementdoitalorssuivreunprotocolededéveloppementadapté,afinderespecterlestandard.Lemodèle
a été mis en application par l
’
intermédiaire de la gestion des articles de la boutique betaboutique. Ce chapitre a
permisdemanipulerlesServlets,lesclassesmodèlesmaisaussiJavaScriptetlatechnologieAjax.
NousavonsensuiteabordédefaçondétailléelesclassesmodèlesduprojetaveclesJavaBeans,lestransactionset
lesdéveloppementsmultilingues.Lesportionsdecodesonttoujoursdétailléesàpartirduprojetdelaboutiqueetle
serviced
’
administrationdesarticlesaétéréalisédanssatotalité.
- 8 - © ENI Editions - All rigths reserved
EnfinladernièrepartieaétéaxéesurlamiseenplacedelasécuritéàbasedeRealmetdecomptesutilisateur.Ce
dernierparagrapheaainsipermisd
’
utiliserl
’
authentificationàpartirdefichiersmaisaussidebasesdedonnées.
- 9 -© ENI Editions - All rigths reserved
Framework
1.Présentation
Ilexisteenprogrammationdeuxtypesd
’
individus :
●lesprogrammeurssystème;
●lesprogrammeursd
’
applications.
Lesprogrammeurssystèmeécriventlecodequiserautiliséparlesprogrammeursd
’
applications.Lesprogrammeurs
système développent les langages Java, PHP, C ou encore C++ et les programmeurs d
’
applications utilisent ces
langages et outils pour créer de la valeur ajoutée à des fins commerciales. Les programmeurs d
’
applications se
concentrent sur leurs projets sans se soucier des techniques et mécaniques de bas niveaux. Les programmeurs
d
’
applicationsutilisentdesoutilsoubibliothèquesappeléesframework.
Un framework est un ensemble de bibliothèques, d
’
outils et de règles à suivre qui aident au développement
d
’
applications.Les frameworkssont développéspar desprogrammeurs systèmes.Un frameworkest composéde
plusieursbriques/composantsquisonteninteractionlesunsaveclesautres.Lesapplicationspeuventêtreécritesde
manièreplusefficacesileframeworkutiliséestadaptéauprojetaulieud
’
êtreobligéderéinventeràchaquefoisla
roue. Un framework fournit un ensemble de fonctionnalités à partir d
’
une implémentation objet. Lors de
développements à grande échelle et de conception par équipe, les frameworks sont alors très utiles, voire
indispensables.Actuellement,ilexistedifférentstypesdeframeworks :
●les frameworks d
’
infrastructure système, qui permettent de développer des systèmes d
’
exploitation, des
outilsgraphiquesetdesplates
formesWeb(Struts,Spring...);
●lesframeworkscommunicants(appelésintergiciels);
●lesframeworksd
’
entreprise(développementsspécifiques);
●lesframeworksdegestiondecontenu(typeCMS).
Lesframeworkspermettentlaréutilisationdecode,lastandardisationdudéveloppementetl
’
utilisationducyclede
développementdetypeitératif
incrémental(spécification,codage,maintenanceetévolution).Unframeworkavecson
cycledevieestaussidésignécommeunprogicielévolué.Actuellement,ilexistebeaucoupdeframeworksdanstous
lesdomainesd
’
applicationetavecpratiquementtousleslangages.Voiciunelistenonexhaustivedesframeworks
utilisésainsiqueleslangagesassociés :
●ApacheStruts
JavaEE
●JSF(JavaServerFaces)
JavaEE
●Spring
JavaEE
●ZendFramework
PHP
●Jelix
PHP
2.Pourquoiutiliserunframework
LesServletsontétédéfiniesen1998etdeuxansaprès,degrandesentreprisesavaientdéjàmisésurJavapour
leursapplicationsWeb.Pendantplusieursannées,cesentreprisesontdéveloppéleursprojetsdefaçonautonome
sansstandards.Aujourd
’
hui,toutescessociétésmesurentl
’
importancedesframeworks.Lechoixduframeworkde
développementeststratégiquepouruneentreprise,ilseradéterminantpourlaqualité,laproductivitéetlapérennité
desprojets.
Normesetstandards
Le développement informatique avec l
’
utilisation de normes permet de généraliser les bonnes pratiques et
- 1 -© ENI Editions - All rigths reserved
d
’
harmoniserlesdéveloppementsauseindel
’
entreprise,cequifacilitelamaintenance.L
’
utilisationdenormesau
seindesentreprisesestenplacedepuisplusieursannées,toutefoisledéveloppementJavaEEpermetd
’
utiliserdes
normes,maiségalementdesoutilscomplexeseux
mêmesnormalisés.
FrameworketdéveloppementWeb
La définition initiale de l
’
API Servlet est trop faible pour envisager un développement complexe d
’
application
totalementbasé surdes Servlets.Au départ,les applications Java étaient basées sur leprincipe del
’
API CGI et
progressivementlesframeworksJavasontapparuspourcomblerlesmanquesdel
’
APIServletetJSP.Lechoixde
l
’
APIauraunimpactnonnégligeablesurlesperformances,laréalisation,laqualitéetlamaintenancedel
’
application.
Demême,puisqueleframeworkseralesocledebasesurlequellelogicielseraconstruit,sapérennitéseraelle
mêmefondamentale.
3.Lesdifférentsframeworks
Ilexisteplusieurstypesd
’
outilspourledéveloppementd
’
application.Unframeworkdetype
’’
maison
’’
,
c
’
est
à
dire
développé par l
’
entreprise, n
’
est pas la meilleure solution. Dès les premières années de Java, les équipes
d
’
informaticiens ont développé leurs propres outils pour le développement et de grandes entreprises ont parfois
construit leur propre framework. Ces développements sont à éviter car aucune entreprise ne pourra consacrer
suffisamment d
’
efforts nécessaires pour la maintenance et l
’
évolution du framework. De plus, les frameworks
OpenSourcedeviennentdesstandardsetsonttestés,validésàuneéchellemondialeparl
’
intermédiairedesprojets
réalisés.
Lesframeworksd
’
éditeurprésententunrisquepourlesentreprisesd
’
unpointdevuedéveloppement.Eneffet,ils
possèdenttoujoursunobjectifcachéquiestlafidélisationdel
’
entreprisesurlesoutilsdel
’
éditeur.
Les frameworks OpenSource sont actuellement les plus nombreux et les plus aboutis. Ils intègrent la qualité du
travailetlamêmedynamiquequeleprojetApache.Unebonnepartdesprojetsdeframeworksestd
’
ailleursissuedu
consortiumApache.Lesframeworkssontdesoutilscomplexesquellequesoitlaqualitédedéveloppementetl
’
origine
des projets. Il n
’
est pas nécessaire de maîtriser tous les frameworks existants mais ils doivent être utilisés
correctement. Une fois le framework choisi, il est alors nécessaire de se former et de constituer une cellule
d
’
assistanceauxéquipesdedéveloppement.
4.Quelframeworkchoisir?
LedéveloppementInternetbasésurlatechnologieJavaaétésubmergépardesAPIetoutilsdetoutessortes.Le
choixd
’
unframeworkestbasésurdifférentscritères :
●Est
cequel
’
ondoittoutconcevoirdeAàZ ?
●Est
cequeledéveloppementpermetl
’
utilisationd
’
uneapplicationprécédemmentdéveloppéeouunepartie ?
●Est
cequ
’
ilestpossibled
’
utiliserunenvironnementcommefondementdel
’
application ?
La conception de A à Z permet de parfaitement maîtriser une technologie mais nécessite beaucoup de temps et
d
’
argent.Ledéveloppementàpartird
’
applicationsexistantesestintéressantuniquementsilesdéveloppeursdes
projets antérieurs sont présents. La troisième approche (utiliser un environnement comme fondement de
l
’
application)estsansaucundoutelameilleuredanslaplupartdescas.
Lechoixdel
’
environnementdedéveloppementouframeworkentraîneplusieursquestions :
●Est
cequel
’
environnementprendenchargetouteslestechnologiesutilisées ?
●Est
cequel
’
environnements
’
appuiesurunecommunautéimportanteetfiabled
’
utilisateurs ?
●Quelestletempsdeformationetdeparfaitemaîtrisedel
’
environnement ?
Le temps de formation est un élément important. Les développeurs ne doivent pas changer de framework pour
chaqueprojetetlefaitderesterfidèleàunoutiletdeparfaitementlemaîtriserpermetdesuivresonévolutionet
pourquoipasd
’
apportersaproprepersonnalisationàl
’
outil.Lechoixfinald
’
unframeworkdoitêtreétudiéparun
architectededéveloppement.Lasolutionlaplusadaptéeseracellequiutiliseralesarchitecteslespluscompétents
etlemeilleurframework.
- 2 - © ENI Editions - All rigths reserved
ApacheStruts
1.Présentation
LeprojetOpenSourceJakarta
Strutspermetlamodificationducodeafindecorrespondreànosexigences.Strutsest
quasimentdevenulestandarddefaitpourlesprojetsJavaEE.Strutsestunenvironnementagréableetpuissantqui
gèrel
’
applicationainsiquelestâchescourantes(routage,actions,validations...).Unautreavantagedesonutilisation
estlenombrecroissantd
’
utilisateursquitendentàpérenniserleprojet.Actuellementbeaucoupd
’
environnementsde
développementcommeEclipseproposentdesoutilspourlaprogrammationStruts.
Struts est un framework qui offre des outils de validation des entrées utilisateurs (saisies et formulaires), des
bibliothèquesdebalisesJSPpourlacréationrapidedepages,unetechniquederoutagepourlespagesetaccèsWeb
etunprocessusdecréationdeformulairessansutiliserdecodeJava.Strutsoffreégalementd
’
autresavantages :
●StrutsfonctionneavectouslesserveursJavaEE(Tomcat,WebSphere,Weblogic...).
●Strutsestunearchitecturesolideetstable(projetApache).
●StrutsestadaptéauxapplicationsWebdegrandetaille.
●Strutspermetdedécomposeruneapplicationcomplexeencomposantsplussimples.
●Strutsgarantitundéveloppementsimilaireparleséquipesdeprogrammeurs.
●Strutspossèdeunedocumentationabondante.
●Strutspermetundéveloppementrapideetpeuonéreux.
Strutsproposeuneméthodededéveloppementquis
’
appuiesurunfichierdeconfigurationauformatXMLquiesttrès
simpleàcomprendreetàutiliser.
2.StrutsetMVC
LemodèleMVC(ModèleVueContrôleur)estundesignpatternoupatrondeconception.Lesdesignspatternssont
issus du langage UML et permettent de représenter sous forme de diagrammes de classes une solution à un
problème.LeMcorrespondauModèleetreprésentelesinformationsquel
’
applicationmanipulepourlesaffichages,
enregistrements et lectures. Le modèle est représenté par des objets simples instanciés à partir d
’
une classe
JavaBean. Les objets JavaBean sont alimentés à l
’
aide d
’
un code de persistance. Le V correspond à la Vue et
représentelamanièredontlesinformationssontaffichées.EnprogrammationJavaEElesvuescorrespondentaux
pagesJSP.Enfin,leCcorrespondauContrôleuretpermetdegérerletravaildesvuesetdumodèle.Lecontrôleurest
développéavecuneServletquiestappeléeparunepageHTMLouJSP.
LemodèleMVCestdonccomposédestroisélémentssuivants :
●lecontrôleur :Servlets;
●lemodèle :JavaBeanetoutilsdepersistancedesdonnées;
●lavue :pagesJSP.
PourchaquecouchedumodèleMVCilexistedesframeworks.Strutsestunframeworkquifaitpartiedelacouche
présentation.Ilpermetdegérerlesactions,leroutageetl
’
affichagedesdonnéesmaispaslapersistance.Pourla
couchedepersistancedesdonnées,ilestpossibled
’
utiliserJDBCouunframeworkdepersistancecommeJDOou
Hibernatequisontactuellementlespluspopulaires.
LorsdudéveloppementMVC,pourchaqueactionilexisteuneServlet,unJavaBeanetunepageJSPassociée,cequi
rendledéveloppementrelativementlongetcomplexe.Pourquoialors,nepasutiliseruneseuleServletquicontrôlerait
toutelachaînedeproductionàpartird
’
actions ?
Cemodèle(designpattern)existe,c
’
estlemodèleMVCII.
Dans le schéma ci
dessous MVC I, plusieurs Servlets jouent le rôle de contrôleur (ex: créer le client, supprimer le
- 1 -© ENI Editions - All rigths reserved
client...),chacunecontrôlantunJavaBean(ouplusieurs)etunepageJSP(ouplusieurs).Leseulfichierdeconfiguration
estledescripteurdedéploiementdel
’
applicationweb.xml.LaServletassociéeàuneaction/traitements
’
occupedes
traitementsetduroutagedel
’
utilisateurverslesvuesadaptées.
Danslesecondschéma,detypeMVCII,iln
’
yaqu
’
uneseuleServletquijouelerôledecontrôleur.Celle
cis
’
appuiesur
unfichierdeconfigurationauformatXMLquifaitlelienentrelesJavaBeanactionsdeprésentationetlespagesJSP.
LecodeàajouterhabituellementdanslesServletsMVCIdoitêtrecomplétédansdesclassesadditionnellessimples
quisontlesactions.
L
’
avantageduMVCIIparrapportauMVCIestl
’
utilisationdufichierdeconfigurationquipermetd
’
éviterdetoucherau
code.D
’
autrepart,larapiditédedéveloppementestaccrueaveccetypedeconception.LemodèleMVCIIlaissealors
librechoixd
’
utilisertouteslestechnologiesJavadisponibles :ServicesWeb,JavaBean,EJB...
StrutsrespectelemodèleMVCIIaveclesélémentssuivants :
●Lavue :elleestreprésentéepardespagesJSP.
●Le
contrôleur
: il est constitué d
’
une seule Servlet ActionServlet qui est déjà codée par les développeurs
Struts.UneclassequihéritedelaclasseStrutsActionServletdoitalorsêtrecréée.
●Le
modèle
:iln
’
estpasimplémentéavecStruts.LesJavaBeanetdestechniquesassociées(EJB,XML,outils
depersistanceouJDBC)sontgénéralementutilisés.
●Le
fichierdeconfiguration
:lefichierstruts
config.xml
proposéparStrutspermetd
’
assembleretdeconfigurer
touslescomposantsduprojet.
TouslesélémentsdumodèleMVCIIsontreliésetconfigurésparl
’
intermédiairedufichierdeconfigurationstruts
config.xml
.
3.Installationduframework
Avantdecommencerlesdéveloppements,ilfautinstallerleframeworkàpartirdessourcesdisponiblessurlesite
d
’
Apache(http://struts.apache.org/).
Actuellement,ilexistedeuxversionsdestruts :
- 2 - © ENI Editions - All rigths reserved
●StrutsV1.XquiestlestandardetquipermetledéveloppementMVCII.
●StrutsV2.Xquiestladernièreversion,baséesurl
’
associationdesframeworksWebWorketStrutsV1.X.
Pourceguide,nousutiliseronslaversionV1.2(1.2.8)deStrutsquiestactuellementlestandardenentreprise.La
dernière version très récente, ne bénéficie pas encore d
’
assez de recul. Il n
’
y a pas encore assez de projets
complexesdéveloppéspardesentreprisespérennespourtirerunegrandeexpériencedecettenouvelleversion.Les
guidesdedéveloppementsettutoriauxdesentreprisessonttousbaséssuruneversionprécédenteàV1.3.Eneffet,
beaucoupdechangementssontapparusdepuiscetteversionetsontsurtoututiliséspourréaliserlatransitionvers
StrutsV2.X.
L
’
installationdel
’
APIestsimple,ilsuffitdecopierlesfichierstéléchargésdanslesrépertoiresd
’
uneapplicationWeb
JavaEEtraditionnelle.Ilserapossibledecréerunnouveauprojetetdecopierlesfichiersnécessaires(librairies
.jar
et
fichiers .xml) ou alors d
’
installer le framework en utilisant une application Struts vierge livrée avec le framework
(http://struts.apache.org/download.cgihttp://archive.apache.org/dist/struts/binaries/struts
1.2.8
bin.zip).
LeframeworkStrutsV1.2.8estcomposédeplusieursfichiers.
LeslibrairiesJava(
.jar
)quicontiennenttouteslesclassesutiliséesparleframework:
●antlr.jar(librairiededéveloppementàpartird
’
unedescriptiongrammaticale).
●common
beanutils.jar(librairiedemanipulationdeJavaBean).
●commons
digester.jar(librairiedegestiondumappingXMLversdesobjetsJava).
●commons
fileupload.jar
(librairiedegestiondel
’
uploadenJava).
●commons
logging.jar
(librairiedeloggin/traces).
●commons
validator.jar
(librairiedevalidationetderèglesàpartird
’
unfichierXML).
●jakarta
oro.jar
(librairiedegestiondesexpressionsrégulièresPerl).
●struts.jar(librairiecomplètedeStruts).
Lesbibliothèquesdetagsoutaglibs(.tld)quipermettentdegérerlesbalisesStrutsdanslesJSP:
●struts
bean.tld
●struts
html.tld
●struts
logic.tld
●struts
nested.tld
●struts
tiles.tld
DepuislaversionV1.3deStruts,lesgrammaires
.tld
nesontplusutiliséesdirectementmaisréférencéesà
partird
’
URLliéesausited
’
Apache.Ex :<%@tagliburi="http://struts.apache.org/tags
html"prefix="html"%>
.
Lesfichiersdeconfigurationdel
’
application(.xmlet
.properties
):
●struts
config.xml
(configurationdel
’
applicationMVC).
●
validator
rules.xml(configurationdesrèglesdevalidation).
●web.xml(fichierdeconfigurationdel
’
application).
- 3 -© ENI Editions - All rigths reserved
●MessageResources.properties(fichierdegestiondespropriétésetdestraductionspourlessitesmultilingues).
Ilexistedeuxfaçonsd
’
installerleframeworkStruts :
●La première installation de Struts consiste à copier les librairies
.jar
dans le répertoire /WEB
INF/lib
de
l
’
application.
Lesfichiers.xmldoiventêtrecopiésdanslerépertoire/WEB
INFdel
’
application.
Lesfichiers
.properties
doiventêtrecopiésdanslerépertoire/WEB
INF/srcduprojet.
●IlexisteégalementuneapplicationStrutsvide,livréeenstandardquipermetd
’
installerleframework.Cette
applicationvideportelenomdestruts
blank.warpourindiquerqu
’
elleestvierge.
Pourinstallercetteapplication,ilestnécessairede :
●Copierl
’
archivestruts
blank.wardansunrépertoire(ex :installationstruts
).
●Décompressersoncontenu.
●OuvrirEclipseetcliquersurFichier
Nouveau
ProjetJava
ProjetTomcat
.
●Nommerleprojet(ex :installationstruts)etsélectionnerlerépertoireprécédent.
●DémarrerTomcatetunnavigateur.
●Saisirl
’
URLsuivantedanslenavigateurhttp://localhost:8080/installationstruts/
Au lancement, une erreur est affichée à l
’
écran. En effet, la page d
’
accueil utilise un fichier pour les messages
(propriétés et traductions). Ce fichier de traduction est présent dans le paquetage nommé
java
. Il faut donc
correctementréférencerleprojetpourindiquerlenomdupaquetage.
●Éditerlefichierstruts
config.xml
etmodifierlabalise<message
resources.../>
.
●Ajouterlenomdupaquetageàlabalise<message
resourcesparameter=
’’
java.MessageResources
’’
/>
.
<!--
================================= Message ressources Definitions -->
<message-resources parameter="java.MessageResources" />
<!-- ========================================== Plug Ins Configuration -->
●Relancerl
’
applicationaveclemanagerTomcatetactualiserlapage.
Lerésultataffichéestlesuivant :
- 4 - © ENI Editions - All rigths reserved
LeprojetWebStrutspossèdelastructuresuivante :
Nousretrouvonslenomduprojet(installationstruts), leslibrairieschargéeset utiliséesparleframework (antlr.jar
,
commons
beanutils.jar
,
commons
digester.jar...),le répertoire dessources du projet (/WEB
INF/src), le répertoire de
configuration du projet (/WEB
INF) et le répertoire /META
INF, qui n
’
est pas utile pour le moment et qui permet
uniquementdedéployerl
’
application.
Lerépertoire/WEB
INFcontientlestroisfichiersdeconfigurationdel
’
applicationetlefichierdesrèglesdevalidation.
Lerépertoire
/pages
contientlesvuesJSP.Enfin,lerépertoire/WEB
INF/srccontientunpaquetageJavanommé
java
aveclefichierdespropriétés/traductionsMessageResources.properties,unfichierexplicatifREADME.txtetunfichierANT
- 5 -© ENI Editions - All rigths reserved
(
AnotherNeatTool
)
build.xml
pourledéploiementdel
’
application.
L
’
application Web est désormais correctement configurée pour utiliser le framework Struts V1.X. Nous pouvons
maintenantcommenceràétudierledéveloppementavecStruts.
- 6 - © ENI Editions - All rigths reserved
ProjetWeb
1.Présentation
Pourcettepartieduguide,nousallonsdévelopperuneapplicationStrutsdegestionetd
’
administrationd
’
unchat
utilisateur(applicationdecommunicationinstantanée).Cechatliéauprojetbetaboutiqueestdéveloppésuiteàune
demandedesutilisateurs/acheteursquisouhaitentdialoguerendirectavecleserviceaprès
ventedelaboutique.
Cetteapplicationnomméechatbetaboutique,permetainsiauxutilisateursdeposerentempsréeldesquestionssur
lesnouveautés,arrivagesouremboursementsdelaboutique.
Seule la partie administration et gestion du chat sera traitée dans cet ouvrage sans se soucier des outils de
communicationduchat(d
’
oùl
’
intérêtd
’
undéveloppementàpartirdecouches).
L
’
applicationWebdegestionduchatbetaboutiqueseradéveloppéeentroisétapesoubriques :
1
La première brique qui sera développée, consiste à programmer une application Web Struts nommée
chatbetaboutique.Ceprojets
’
occupedelagestiondescomptesutilisateur(création,modification,suppression),des
salons (création, modification et suppression) et des inscriptions des utilisateurs aux salons. Cette application
reprendlestylegraphiquedelaboutique(partieadministration)maisutiliseleframeworkStrutsenlieuetplacedes
ServletsetpagesJSPtraditionnelles.
2
L
’
applicationWebStrutschatbetaboutiquedéveloppéedanslapartie1pourraitêtreagrémentéed
’
unensemblede
services liés aux dialogues et aux synchronisations des messages. En effet, lors de cette seconde brique, nous
pourrionsdévelopperdesservicesetunensembledepagesJSPpourposterdesmessagesdynamiquesquiseraient
stockésdansunebasededonnées.Demême,cetteétapenécessiteraitledéveloppementd
’
unensembledepages
JSPpourlirelesmessages,lesutilisateursinscritsetlessalons,auformatXML.
Ilexisteunemultitudedetechniquespourgérerunchat.Nousallonspournotreprojetutiliserunserveur
JavaetlatechnologieServletpourlagestiondesactionsdecommunicationduchat(insertiond
’
unnouveau
message, lecture des messages, liste des utilisateurs inscrits...). Le plus complexe, lors du développement de
servicesde cetype estla gestiondes accès concurrents et des temps deréponse. Lessockets sontsouvent
utilisésenprogrammation.L
’
utilisationdestechnologiesJavaEEpermetdegérerlesaccèsconcurrents,lestemps
deréponseetlafiabilitédel
’
ensemble.
3
Latroisièmeetdernièrebrique,concernel
’
interfacegraphiquequiestunecouchesupplémentaireetquipourrait
êtredéveloppéeaveclaversionJavaSE.LesaffichagesseraientparexempleréalisésavecuneapplicationSWINGet
unsystèmedeparsagedesdonnéesreçuesauformatXML.
C
’
estlàtoutl
’
intérêtd
’
utiliserdestechnologiesadaptéesetportables.Eneffet,rienn
’
empêchededévelopperlechat
avecleframeworkStrutsafind
’
optimiserl
’
applicationetdebénéficierdesoutilsactuels.Enutilisantunaffichagedes
donnéesauformatXML,uneinterfacegraphiquepourraparlasuiteêtredéveloppéeenJ2SE(JavaStandard),en
Flash,enJavaScriptouavecn
’
importequelautrelangagecapabledeparserducodeXML.
Nousallonsdoncnousintéresserauxdeuxpremièresbriquesduprojet(n°1etn°2)indépendammentdelatroisième
etdernièrebriqueliéeàl
’
interfacegraphique.Cettefaçondeprogrammerparbriqueoucouchepermetdemieux
développerchaquepartie,d
’
utiliserlesmeilleurs développeurspourchaqueservice etdetester très précisément
chaque module (ex : tests pour la création d
’
un nouveau message, tests pour l
’
affectationd
’
un utilisateur à un
salon...).Enfin,lorsdudébuggageoudelamaintenance,ilserabeaucoupplusaisédetrouverlabriqueconcernée
parleproblèmeoul
’
évolutiondusystème.
2.Spécificationdel
’
applicationdechat
Il existe évidemment plusieurs implémentations envisageables lors du développement d
’
un projet. Pour le projet
- 1 -© ENI Editions - All rigths reserved
chatbetaboutique,nousallonsprésenterleModèleConceptueldesDonnéesMERISEafind
’
introduirelesinformations
àgérerparlasuite.
Lesutilisateursdelabasededonnéeschatbetaboutiquecorrespondentenfaitauxutilisateursdelabase
betaboutique.Nousutilisonsunenouvellebasededonnéesafindebienséparerlesdeuxprojetsetd
’
éviter
demélangerlesservices.
Lanouvellebasededonnéescrééeestnomméechatbetaboutique.Cettebasededonnéespossèdecinqtablesqui
sontutilisateur
,
salon
,
utilisateursalon
,
messageetserveur
.
LastructuredelabasededonnéesestreprésentéeavecleMCDsuivant :
Latableutilisateurestsimpleetpermetdereprésenterunepersonneàpartird
’
unidentifiantunique(id_utilisateur
)
etd
’
unpseudonyme(pseudonyme).Lacléestcomposéedupseudonymedel
’
utilisateuretdesonid(id_utilisateur).
Lepseudonymefaitpartiedelacléafind
’
avoirunidentifiantuniquedanslabaseetl
’
idestuniquementutiliséepour
améliorerledéveloppement(partiegestiondescomptes).Lemotdepassedel
’
utilisateur(motdepasse)eststocké
afindepermettreàl
’
utilisateurdes
’
authentifierparlasuitelorsdel
’
utilisationduchat.Enfin,ilyadesinformations
simplescommelenometleprénom(nometprenom
).L
’
image(
image
)permetd
’
affecterunavataràl
’
utilisateurafin
d
’
améliorerl
’
ergonomiedel
’
ensemble.
Latablesalonestcomposéed
’
unecléautomatique(id_salon)etd
’
unchamptexte(theme)pourenregistrerlethème
ounomdusalon.Lechamp
date
permetdespécifieràquelledateauralieulechatpourlesalonindiquéetenfin,le
champ
actif
permettra d
’
activer ou pas un salon avec l
’
interface graphique. Un chat pourra être ainsi arrêter
dynamiquement(pasd
’
authentification,delectureoud
’
insertiondemessagesilechatestdésactivé).
La table utilisateursalon, qui correspond à la relation insérée, est composée d
’
une clé primaire qui est la
concaténationdupseudonymeutilisateuretdel
’
identifiantdusalon(pseudonymeetid_salon).Le champconnecte
permetd
’
indiquersiunutilisateurinscritestconnectéoupasausalon.Enfin,lechamp
couleurpolice
serautiliséparla
suiteafind
’
affecterunecouleurdepolicepourletextedel
’
utilisateur.
Latablemessagepermetdestockerlesmessagesduchat.Seschampssontl
’
identifiantdumessage(
id
)quiestun
entierincrémentéautomatiquement,l
’
auteurdusalon(auteur)quiestenfaitsonpseudonyme(afind
’
accélérerles
lectures, pas besoin de jointure), la date de création du message au format timestamp (datecreation) avec les
millisecondes (afin de gérer l
’
ensemble avec précision), le texte du message (texte) et le salon concerné par le
message(id_salon
).
Latableserveurpermetdedésactiverenunseulcliclatotalitéduchatavecsonchampetatserveur
.
LabasededonnéeschatbetaboutiqueseraauformatUTF
8afindegérerlesconversationsmultilingues.Lastructure
MySQLetlescriptdecréationdel
’
ensemblesontprésentésci
dessous.
- 2 - © ENI Editions - All rigths reserved
-- Base de données: `chatbetaboutique`
--
--------------------------------------------------------
-- Structure de la table `message`
CREATE TABLE `message` (
`id` int(11) NOT NULL auto_increment,
`auteur` varchar(30) default NULL,
`datecreation` varchar(30) default NULL,
`texte` varchar(250) default NULL,
`id_salon` int(11) NOT NULL default ’0’,
PRIMARY KEY (`id`),
KEY `datecreation` (`datecreation`),
KEY `id_salon` (`id_salon`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=229 ;
--
--------------------------------------------------------
-- Structure de la table `salon`
CREATE TABLE `salon` (
`id_salon` int(11) NOT NULL auto_increment,
`theme` varchar(255) NOT NULL default ’’,
`date` timestamp NOT NULL default CURRENT_TIMESTAMP on
update CURRENT_TIMESTAMP,
`actif` char(1) NOT NULL default ’’,
PRIMARY KEY (`id_salon`),
KEY `date` (`date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
--
--------------------------------------------------------
-- Structure de la table `serveur`
CREATE TABLE `serveur` (
`etatserveur` char(1) collate utf8_bin NOT NULL default ’0’
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
--------------------------------------------------------
-- Structure de la table `utilisateur`
CREATE TABLE `utilisateur` (
`id_utilisateur` int(11) unsigned NOT NULL auto_increment,
`pseudonyme` varchar(30) NOT NULL default ’’,
`motdepasse` varchar(15) default NULL,
`nom` varchar(255) NOT NULL default ’’,
`prenom` varchar(255) NOT NULL default ’’,
`autorisation` char(1) NOT NULL default ’0’,
`image` varchar(255) default NULL,
PRIMARY KEY (`id_utilisateur`,`pseudonyme`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=375 ;
--
--------------------------------------------------------
-- Structure de la table `utilisateursalon`
CREATE TABLE `utilisateursalon` (
`pseudonyme` varchar(30) NOT NULL default ’’,
`id_salon` int(11) NOT NULL default ’0’,
`connecte` char(1) NOT NULL default ’0’,
`couleurpolice` varchar(50) NOT NULL default ’0’,
- 3 -© ENI Editions - All rigths reserved
PRIMARY KEY (`pseudonyme`,`id_salon`),
KEY `pseudonyme` (`pseudonyme`),
KEY `id_salon` (`id_salon`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Sinousconsidéronsunexempled
’
utilisation,nousauronsl
’
utilisateurjlafossinscritauxsalonssavetnouveautés
.Il
seraactuellementconnectéausalonsavavecunepolicedecouleur
13023407quicorrespondauformatnumérique
delacouleurRVBetilaurapostédeuxmessagespourcesalonaveclesdatesprécisesauformattimestamp.
Nous allons donc aborder l
’
utilisationdu framework Strutsavec les exemplesliés au développementde la partie
administration du chat utilisateur. Nous allons concevoir la première brique du projet qui consiste à gérer les
utilisateurs, les salons et les inscriptions des utilisateurs aux salons. Les techniques, validations, actions,
bibliothèquesdetagsetformulairesStrutsserontprésentésàtraversledéveloppementdecesservices.
- 4 - © ENI Editions - All rigths reserved
FormulairesStruts
1.Présentation
AveclemodèleMVCII,lavueestreprésentéepardeuxcomposantsetnonparunseulcommedanslepatternMVCI.
Ces deux composants sont une page JSP et une classe qui est un objet JavaBean. Les pages JSP Struts sont
composéesdetaglibsdéveloppéesparStruts.CesbalisespermettentdenepasinsérerdecodeJavadanslespages
JSPenconformitéaveclepatternMVC.CesbibliothèquesdetagssontenfaitdesbalisesHTMLsupplémentairesqui
aidentlesdéveloppeursdepagesWeb.PourutilisercesbibliothèquesdetagsdanslespagesJSP,ilfautlesdéclarer
en début de page. La déclaration simple est réalisée avec la directive
<%@ taglib uri=
’’
...
’’
prefix=
’’
...
’’
%>
.
Le
paramètre uri permet de définir le lien vers le fichier
.tld
qui est la grammaire de la bibliothèque de balises. Le
paramètre
prefix
définitlepréfixedesbalisesdanslapageJSPencours.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
Danslesdéfinitionsprécédentes,nousretrouvonsleparamètreuriquifaitréférenceàunegrammaireprésentesurle
sitedeStruts.Lepréfixeutiliséestbean,doncplusieursbalises
<bean:.../>
pourrontêtreutiliséescommelabalise
ci
dessousquipermetd
’
afficherunmessageprésentdanslefichierdepropriétés :
<bean:message key="welcome.title"/>
LadéclarationdestaglibsStrutsestrelativementsimpleetchaquepageJSPdéveloppéeavecStrutsdevrainclureau
moinslestroislibrairiesprécédentes(bean
,
htmlet
logic
).Afindemettreenapplicationetdedétaillerl
’
utilisationdes
formulairesavecStruts,nousallonsutiliserleprojetinstallationstrutsprécédemmentcréé.Leprojet,àcetteétapedu
guidedoitêtresemblableàlavueci
après.
Parconvention,lespagesJSPquiutilisentdesformulairesavecStrutssontsuffixéesparletermeForm
.
NousallonscréerunnouveauformulairenomméauthentificationForm.jspdanslerépertoire
/pages
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification</title>
<html:base/>
</head>
<body>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Les balises Struts <html:html>
,
<html:form>
,
<html:text>... sont utilisées. Ces balises sont interprétées par le
serveurettransforméesencodeHTMLcompatibleXHTML.
Si nous essayons d
’
exécuter cette page avec l
’
URL suivante
- 1 -© ENI Editions - All rigths reserved
(http://localhost:8080/installationstruts/pages/authentificationForm.jsp),nousremarquonsqu
’
ilyaplusieurserreurs.
Eneffet,leformulaireStrutsn
’
estpascomplet,nousavonspréciséqu
’
aveclemodèleMVCII,nousdevonsassocierà
chaquepageJSPunJavaBeanActionFormpourfairelelienaveclespropriétésduformulairedelapage(identifiantet
motdepassedansl
’
exemple).
2.JavaBeanActionForm
UnobjetActionFormestunJavaBeandeprésentationinclusdansunepageJSP.CeJavaBeanestuneclassequi
héritedelaclasseActionFormdeStruts.Lorsdelagestionduformulaire,leJavaBeanassociéestcrééàpartirde
cetteclasse.Dansnotreexemple,lapageauthentificationForm.jspdoitdoncêtreaccompagnéed
’
uneclasseJavaBean
AuthentificationForm
.
Nousallonscréercettenouvelleclassedansunpaquetagenomméactionform
.
package actionform;
import org.apache.struts.action.ActionForm;
@SuppressWarnings("serial")
public class AuthentificationForm extends ActionForm {
private String identifiant=null;
private String motdepasse=null;
public String getIdentifiant() {
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
}
Cetteclassepermetd
’
écrireetlirelesinformationsentréesparl
’
utilisateuretdelesenvoyerparlasuiteversl
’
action
quivas
’
occuperdutraitement.
3.LecontrôleurAction
LavueestcrééeenconformitéaveclemodèleMVCII.Nouspouvonsmaintenantdévelopperuneactionassociéeau
contrôleur.Pourrappel,dansMVCII,ilexisteunseulcontrôleurquiesttoujoursuneServletetquisenommeavec
StrutsActionServlet.LesdéveloppeursStrutsontdéjàdéveloppécetteServlet,elleestd
’
ailleurslivréeenstandard
danslepaquetageorg.apache.struts.actiondelalibrairiestruts.jar
.
NousallonsmaintenantprogrammernosactionsavecunenouvelleclassequihéritedelaclasseActiondeStruts.La
classeActiondéfinitplusieursméthodes,laplusutiliséeétantexecute().Nousallonscréerlaclassed
’
actionnommée
AuthentificationActionprésentedansunpaquetagenomméaction
.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import actionform.AuthentificationForm;
@SuppressWarnings("serial")
public class AuthentificationAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
- 2 - © ENI Editions - All rigths reserved
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm)
form;
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(authentificationform.getIdentifiant().equals("jlafoss") &&
authentificationform.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page
d’erreurs
return mapping.findForward("erreurs");
}
}
Le code ci
dessus permet de vérifier les saisies de l
’
utilisateur à partir du JavaBean AuthentificationForm qui est
automatiquement renseigné. En cas de succès de l
’
authentification l
’
utilisateur est redirigé vers la page
adaptée/pages/succes.jspetencasd
’
erreurlesinformationssontaffichéesparlapage/pages/erreurs.jsp
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Erreurs</title>
<html:base/>
</head>
<body>
<h3>Erreur lors de l ’authentification</h3>
</body>
</html:html>
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Succès</title>
<html:base/>
</head>
<body>
<h3>Authentification réalisée avec succès</h3>
</body>
</html:html>
Laméthodeexecute()delaclasseAuthentificationActionpossèdequatreparamètres :
●
ActionMappingmapping
:cetélémentpermetdegérerleroutagedel
’
utilisateurparl
’
intermédiairedufichier
struts
config.xml
.Lesredirectionsserontd
’
ailleursréaliséesaveclafonction
findForward()
del
’
objet
mapping
.
●ActionFormform :cetobjetreprésentelesvaleursduformulaireparl
’
intermédiaireduJavaBeanassocié.
●HttpServletRequestrequest :cetobjetreprésentelarequêteHTTPdanslaquellenouspouvonsrécupérerles
valeurs(identiqueàl
’
utilisationdesServlets).
●HttpServletResponse response : cet objet représente le flux de sortie de l
’
application dans lequel nous
pouvonsenvoyerdesinformations(identiqueàl
’
utilisationdesServlets).
Notre premier exemple simple est quasiment terminé. Nous avons défini la vue avec la
page /pages/authentificationForm.jsp et son JavaBean associé actionform.AuthentificationForm.java. Ensuite, nous
avons développé l
’
action qui sera réalisée action.AuthentificationAction.java. Maintenant, il ne reste plus qu
’
à
assemblertouscesélémentsparl
’
intermédiairedufichierstruts
config.xml
.
- 3 -© ENI Editions - All rigths reserved
4.Lefichierdeconfigurationstruts
config.xml
Ledescripteurdedéploiementweb.xmlutiliséaveclesServletsetpagesJSPesttoujoursprésentavecStrutsmaisil
n
’
yaparcontreplusqu
’
uneseuleServletdéclaréedanscefichier,laServletcontrôleurActionServlet
.
<!DOCTYPE web -app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<display -name>Struts Blank Application</display -name>
<!-- Standard Action Servlet Configuration -->
<servlet>
<servlet -name>action</servlet -name>
<servlet -class>org.apache.struts.action.ActionServlet</servlet -class>
<init -param>
<param -name>config</param -name>
<param -value>/WEB -INF/struts -config.xml</param -value>
</init -param>
<load -on-startup>2</load -on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet -mapping>
<servlet -name>action</servlet -name>
<url -pattern>*.do</url -pattern>
</servlet -mapping>
<!-- The Usual Welcome File List -->
<welcome -file-list>
<welcome -file>index.jsp</welcome -file>
</welcome -file-list>
</web-app>
Touteslesactionsd
’
extension
.do
seronttraitéesparlaServletActionServletdeStrutsquiestchargéeaudémarrage
(
<load
on
start
up>
)del
’
applicationetquipossèdeunfichierdeconfiguration/WEB
INF/struts
config.xml
.Lefichier
struts
config.xml
est un fichier de configuration d
’
application Struts au format XML. C
’
est également un fichier
d
’
assemblagequipermetdefairelelienentrelesActionForm,lesclassesActionetlesvues.
Nousallonsréaliserlaconfigurationdenotreapplicationparl
’
intermédiairedecefichier.
●Labalise
<form
bean>
permetdedéclarerunobjetJavaBeandetypeActionFormprésentdanslavue.Les
attributsnameettypepermettentdedonnerunnomauJavaBeanetsontypeassocié.
●Les balises
<action
mappings> et
<action>
permettent de déclarer les objets contrôleurs de type Action
.
L
’
attribut name permet de faire le lien avec l
’
ActionForm de la vue. L
’
attribut scope permet d
’
indiquer où
récupérerl
’
objetActionForm.L
’
attributpathestutilisépourfairelelienentreleformulaireetlaclasseActionà
déclencher.
Siparexempleleformulairecontientlavaleursuivante :<formmethod=
’’
post
’’
action=
’’
Authentification.do
’’
>
,
l
’
attribut
pathauralavaleursuivante
path=
’’
/
Authentification
’’
.
L
’
attributtypepermetdefairelelienaveclaclasseassociéeà
l
’
action.Enfin,labalise
<action>
peutengloberdesbalises
<forward>
quicorrespondentauxredirectionsutiliséespar
laclasseAction.L
’
attributnamepermetdedonnerunnomàlaredirection.
Labalise
<form
bean>
permetdedéfinirlesformulairesprésentsdanslesvuesJSP.L
’
attributtypedecette
balisepermetdedonnerletypeliéàl
’
attributname.DansnotreexempleletypeestuneclasseJava,mais
celapeuttrèsbienêtreuntypesimplecommeunString
,
String[]
,
int,ouencore
float
.
Voicilefichierdeconfigurationstruts
config.xml
utilisépournotreexemplesimpled
’
authentification.
<?xml version="1.0" encoding="ISO -8859-1" ?>
<!DOCTYPE struts -config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts -config_1_2.dtd">
<struts-config>
<!-- ================================================ Form
Bean Definitions -->
<form-beans>
<! -- bean d ’authentification -->
- 4 - © ENI Editions - All rigths reserved
< form-bean
name="authentificationForm"
type="actionform.AuthentificationForm"/>
</form -beans>
<!-- ========================================= Global
Exception Definitions -->
<global -exceptions>
<! -- sample exception handler
<exception
key="expired.password"
type="app.ExpiredPasswordException"
path="/changePassword.jsp"/>
end sample -->
</global -exceptions>
<!-- =========================================== Global
Forward Definitions -->
<global -forwards>
<! -- Default forward to "Welcome" action -->
<! -- Demonstrates using index.jsp to forward -->
<forward
name="welcome"
path="/Welcome.do"/>
</global -forwards>
<!-- =========================================== Action Mapping
Definitions -->
<action -mappings>
<action
path="/Welcome"
forward="/pages/Welcome.jsp"/>
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentification.jsp">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
</action -mappings>
<!-- ======================================== Message
Resources Definitions -->
<message -resources parameter="java.MessageResources" />
<!-- =================================================== Validator plugin -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set -property property="pathnames"
value="/WEB -INF/validator -rules.xml,/WEB -INF/validation.xml"/>
</plug -in>
</struts -config>
Lamiseenplacedenotrepremierexempleestterminée.Nouspouvonsrechargerlecontextedel
’
applicationavecle
managerTomcatettesterleservice.Pourrésumer,lorsdelamiseenplaced
’
unnouveauservice,ilestnécessairede
créerlesdifférentsélémentsd
’
uneapplicationStruts,àsavoiruneclassedetypeActionFormpourlespagesJSPavec
formulaire,uneclassetypeActionpourgérerl
’
actionducontrôleur,uneclassemodèlesupplémentairepourlagestion
éventuelledelapersistancedesdonnéesetenfindespagesJSPderésultat.Toutceciestliéetassembléparunseul
etuniquefichierdeconfigurationnomméstruts
config.xml
.
NotreschémaprécédentconcernantlemodèleMVCIIadaptéànotreexempleestlesuivant :
- 5 -© ENI Editions - All rigths reserved
L
’
arborescenceduprojetestlasuivante :
- 6 - © ENI Editions - All rigths reserved
VuesetStruts
1.Présentation
Les applications Web actuelles bénéficient d
’
interfaces graphiques très évoluées. L
’
interface graphique encore
appeléeIHMouGUI,permetd
’
utiliserl
’
applicationparlebiaisdezonesdetextes,formulaires,boutons,calendriersou
toutautreobjetgraphiquequipermetderéaliserdesactions.TouscesobjetsfontpartiedelaVueutilisateur.
Pour les développements Web, la vue est représentée par des pages HTML/XHTML. Ces pages sont également
composées de code JavaScript pour enrichir l
’
ensemble. Avec le framework Struts, les vues sont également
représentées avec du code HTML/XHTML, le langage JavaScript, mais également un ensemble de balises/taglibs
dédiées.Cesbalisessontfourniessousformedetaglibsetrépondentauxbesoinsdesprogrammeursd
’
applications
Internet.Lestaglibssontinterprétéesparleserveurettraduitesducôtéserveuravantaffichage.L
’
intérêtdecette
techniqueestderéaliserdespages/vuessimplesavecunminimumdecodeJava.
LavueenStrutsestcomposéed
’
unepageJSPetd
’
unJavaBeaninstancedelaclasseActionForm.Cetteclassepermet
dereprésenterlesinformationssaisiesparunutilisateurdansunformulaireHTMLcontenudanslapageJSP.Ilest
égalementpossibledenepascréerunJavaBeaninstancedelaclasseActionFormmaisd
’
utiliserunoutildynamique
grâceauxDynaFormsquipermettentdelaisserleserveurJavacréerleJavaBeanàpartird
’
informationsrenseignées
danslefichierdeconfigurationstruts
config.xml
.
Ilexisteenfinunfichierdepropriétésquipermetdegérerdesinformationsadditionnellesetl
’
internationalisation.Il
serapossibled
’
utiliserlestaglibsassociéspourgérerdespropriétés,labelsdechamps,textesinformatifs,traductions
demenus...
2.LestaglibsStruts
Nousavonsétudiédansl
’
exempleprécédentlestroistaglibsStrutslesplusutilisées.LesbibliothèquesdetagsStruts
sontaunombredecinq :
●struts
bean.tld
●struts
html.tld
●struts
logic.tld
●struts
nested.tld
●struts
tiles.tld
Lesdéclarationsdestaglibssontréaliséesavecleslignessuivantes :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags -nested" prefix="nested" %>
<%@ taglib uri="http://struts.apache.org/tags -tiles" prefix="tiles" %>
LadocumentationdesbibliothèquesdetagsStrutsestprésenteàcetteadresse :http://struts.apache.org/1.x/struts
taglib/index.html
IlesttoutàfaitpossibledechangerlespréfixesdesbalisesStruts.Maisparconvention,ilestpréférablede
conserverlesnomsproposés.Ex :<%@tagliburi="http://struts.apache.org/tags
logic"prefix="programmation"
%>
.
<programmation:notEmpty name=
’’
monbean
’’
scope=
’’
request
’’
></
programmation:notEmpty>.Iln
’
estégalement
pasnécessairededéclarerlestaglibsquinesontpasutiliséesdansunepageJSP.
Sinousreprenonsleformulaired
’
authentificationprécédent,nousremarquonsl
’
utilisationdelabibliothèquedetags
html.Seulslestags
<html:...>
sontutilisésdanscetexemple,lecodeauraitdoncpuêtresimplifiéensupprimantles
lignes
<%@taglib...>
avecbeanet
logic
.
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<html:html>
- 1 -© ENI Editions - All rigths reserved
<head><title>Authentification</title><html:base/></head>
<body>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Nousallonsàcetteétapedecechapitre,étudierlapartieVUEdumodèleMVCII.
3.Laclassedegestiondesformulaires(ActionForm)
AveclemodèleMVCII,lavueestcomposéededeuxéléments :
●LapageJSPquicontientducodeHTML,desbalisesStrutsetdesformulairesdedonnées.
●DesclassesquiaccompagnentchaqueformulairedanslespagesJSP.CesclassesJavaBeanhéritentdela
classeStruts :org.apache.struts.action.ActionForm
.
LaclasseabstraiteActionFormdéclareneufméthodesqu
’
ilestpossibledesurcharger :
●
getServlet()
:retournel
’
instancedelaservletattachée.
●
getServletWrapper()
:retournel
’
instanceducontrôleurattaché.
●getMultipartRequestHandler():retournelarequêteduformulaire.
●setServlet(ActionServletservlet):retournel
’
instancedelaservletattachée.
●setMultipartRequestHandler(MultipartRequestHandler multipartRequestHandler) : utilisé pour les requêtes
Multipointcommel
’
uploaddefichiers.
●reset(ActionMappingmapping,ServletRequestrequest):videtouslesJavaBeans.
●reset(ActionMappingmapping,HttpServletRequestrequest):videlespropriétésduJavaBeandelarequête.
●validate(ActionMappingmapping,ServletRequestrequest):validelespropriétésduformulaire.
- 2 - © ENI Editions - All rigths reserved
●validate(ActionMappingmapping,HttpServletRequestrequest):validelespropriétésduformulairedelarequête.
Touteclassequihérited
’
ActionFormestunJavaBeanquicorrespondauxchampsd
’
unformulaireHTMLStruts.Dans
notreexempleprécédentd
’
authentification,leformulaireauthentificationForm.jspillustrecepropos.
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<html:html>
<head>
<title>Authentification</title>
<html:base/>
</head>
<body>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
CettepageJSPdéclarebienlestaglibsStrutsafind
’
utiliserlesbalisesadaptées.Parconvention,pourceformulaire,il
fautdévelopper une classeJavaBean nomméeauthentificationForm.java,dans laquelleun attribut estdéclaré pour
chaque champ présent dans le formulaire JSP. Ces attributs sont déclarés comme privés ou protégés (
private
ou
protected
)afinderespecterl
’
encapsulationdesJavaBeans.CetermeusuelseraalorsBeanouJavaBeandeformulaire.
LaclasseJavaBeanformulairepourlapageJSPprécédenteestprésentéeci
après.
package actionform;
import org.apache.struts.action.ActionForm;
@SuppressWarnings("serial")
public class AuthentificationForm extends ActionForm {
private String identifiant=null;
private String motdepasse=null;
public String getIdentifiant() {
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
}
LorsquelapageJSPauthentificationForm.jspestrenseignéeetsoumiseauserveur,celui
cicréeuneinstancedela
classe AuthentificationForm.java à partir des valeurs saisies dans le formulaire de la page JSP. L
’
instance est
référencéeparl
’
intermédiaire du fichier de configuration struts
config.xml
.Cetteréférenceréaliséeaveclesbalises
Miseenplaced
’
unformulaire
- 3 -© ENI Editions - All rigths reserved
<form
beans>et
<form
bean>
estensuiteutiliséepardifférentsélémentscommeActionServletpourlestraitementsà
réaliser.
Lefichierstruts
config.xml
comporteplusieursbalisesréservéesàladéclarationd
’
uneinstanced
’
ActionForm.Labalise
<form
beans>permetdedéclarerdesJavaBeansformulairesetlabalise
<form
bean>
permetdedéclarerunJavaBean
spécifique d
’
un formulaire. L
’
élément
<form
bean>
comporte l
’
attribut name qui permet d
’
identifier le JavaBean et
l
’
attribut type qui définit le type d
’
instanciation du JavaBean. L
’
élément
<form
bean>
peut également contenir des
éléments
<form
property>
pourlesDynaForms
.
...
<form-beans>
<!-- bean d ’authentification -->
<form-bean
name="authentificationForm"
type="actionform.AuthentificationForm"/>
</form-beans>
...
Dansl
’
exempleprécédent,leBeanformulaireestidentifiéparlenomauthentificationForm.Ilestinstanciéàpartirdela
classeAuthentificationFormprésentedanslepaquetageactionform.UnefoisleformulaireJSPdéclaréetleJavaBean
formulairemisenplace,nouspouvonsutiliserl
’
instanceduJavaBeanformulairedanslaServletd
’
actionActionServlet
.
PourutiliserleJavaBeanformulaire,nousdevonscréeruneclassed
’
action.Ilexisteplusieursclassesd
’
actionavec
Struts (classe d
’
action simple : org.apache.struts.actions.Action, classe de redirection :
org.apache.struts.actions.ForwardAction...).Demême,cetteclassed
’
actionestdéclaréedanslefichierdeconfiguration
struts
config.xml
.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import actionform.AuthentificationForm;
@SuppressWarnings("serial")
public class AuthentificationAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm)
form;
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(authentificationform.getIdentifiant().equals("jlafoss") &&
authentificationform.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
}
...
<action-mappings>
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentification.jsp">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
</action -mappings>
- 4 - © ENI Editions - All rigths reserved
...
Pourrésumer,l
’
utilisationd
’
unformulairesimpleavecStrutsnécessitelesétapessuivantes :
●DéclarationdeslibrairiesdetagsStruts.
●CréationduformulairedanslapageJSPnomméexxxForm.jsp
.
●CréationdelaclasseJavaBeanassociéeauformulairexxxForm.java
.
●DéclarationduJavaBeandeformulairedanslefichierdeconfigurationstruts
config.xml
.
●Création(aubesoin)d
’
uneclassed
’
actionpourlestraitementsxxxAction.java
.
●Déclarationdel
’
actiondanslefichierdeconfigurationstruts
config.xml
.
●Testsdelamiseenplacetotale.
4.Lesformulairesdynamiques(DynaForms)
Laméthodeprécédentedecréationdeformulaireestfiablemaisassezlourdeàmettreenplace.Lacréationd
’
un
JavaBeanassociéàchaqueformulaireestassezcontraignante.Ilexisteuneautreméthodetrèsrapidepourcréerun
JavaBeanformulairesanstaperuneseulelignedecodeJavamaisendéclarantcelui
cidanslefichierstruts
config.xml
.
Labalise
<form
bean>
du fichier deconfiguration struts
config.xml
estconstituéed
’
un élément
<form
property>
qui
permetlacréationdepropriétésaidantàlacréationdeJavaBeanformulairedynamique.Cettebalise
<form
property>
possèdelesattributsname(identifiantdelapropriété),type(typedelapropriété)et
initial
(valeurpardéfautdela
propriété). Les DynaForms sont créés à partir de la classe DynaActionForm qui est contenue dans le paquetage
org.apache.struts.action. Lorsque le serveur, au moment de l
’
analyse du fichier struts
config.xml
rencontre la balise
<form
bean>
,ilcréeuneclasseJavaBeandynamiquementaveclespropriétésdéclaréesetuneinstancedirectement
utilisable.
Nousallonsreprendrenotreexempled
’
authentificationavecl
’
utilisationd
’
unDynaForms.Pourcelanouscommençons
parcréerunepageJSPnomméeauthentificationDynaforms.jspaveclecodesuivant :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification DynaForms</title>
<html:base/>
</head>
<body>
<html:form action="/AuthentificationDynaForms">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
SinousessayonsdedéclenchercettepageJSP,lenavigateurnousindiquel
’
absencedemappingpourleformulaireà
l
’
adresse /AuthentificationDynaForms. Nous devons donc définir cette action dans le fichier de configurationstruts
config.xml
.
...
<action-mappings>
<action
path="/AuthentificationDynaForms"
name="authentificationDynaForms"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/bienvenueDynaForms.jsp">
</action>
</action -mappings>
- 5 -© ENI Editions - All rigths reserved
...
NousindiquonsquelenomduformulairedynamiqueseraauthentificationDynaFormsetquelecheminquipermetde
déclencher cette action est /AuthentificationDynaForms. L
’
action liée à cette URL sera la classe
org.apache.struts.actions.ForwardAction de Struts. La redirection est réalisée vers la
page /pages/bienvenueDynaForms.jsp. Si nous déclenchons à nouveau l
’
URL
http://localhost:8080/installationstruts/pages/authentificationDynaForms.jsp, après rafraîchissement de l
’
application
par l
’
intermédiaire du manager Tomcat, nous remarquons une erreur liée au JavaBean formulaire
authentificationDynaForms qui n
’
existe pas. Nous allons donc créer ce formulaire dynamique dans le fichier de
configurationstruts
config.xml
.
...
<form-beans>
<! -- bean d ’authentification dynaforms -->
<form-bean name="authentificationDynaForms"
type="org.apache.struts.action.DynaActionForm">
<form -property name="identifiant"
type="java.lang.String" initial=""/>
<form -property name="motdepasse"
type="java.lang.String" initial=""/>
</form -bean>
</form-beans>
...
LeJavaBeanformulairenomméauthentificationDynaFormsestcrééetpossèdelespropriétésidentifiantetmotdepasse
.
Une fois la déclaration réalisée, ce JavaBean s
’
utilise comme un Bean de formulaire classique. Nous pouvons
maintenantdévelopperlapagederedirectionbienvenueDynaForms.jspquipermetuniquementdelireleDynaForms
danslarequêteHTTP.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Succès</title><html:base/></head>
<body>
<h3>Authentification réalisée avec succès</h3>
<bean:write name="authentificationDynaForms" property="identifiant"/><br/>
<bean:write name="authentificationDynaForms" property="motdepasse"/><br/>
</body>
</html:html>
- 6 - © ENI Editions - All rigths reserved
NousavonsmanipulédesobjetsdetypechaînesdecaractèresavecleDynaFormsprécédent.Commenouspouvons
manipuler tous les objets avec cette classe, nous allons créer une classe nommée Utilisateur avec les propriétés
identifiantetmotdepasse.Pourcelanousallonsajouterunnouveaupaquetagenomméboiteoutilsquicontiendrales
classesJavaBeansimples.ChaqueclassemanipuléeparunDynaFormsdoitêtreunJavaBeanstandard,c
’
est
à
dire
sérialisable.
package boiteoutils;
import java.io.Serializable;
public class Utilisateur implements Serializable
{
//variables de classe
private String identifiant=null;
private String motdepasse=null;
//java 5.0 pour avoir un identifiant unique de la sérialisation
private static final long serialVersionUID = 1L;
public Utilisateur()
{
}
public String getIdentifiant() {
return identifiant;
}
public void setIdentifiant(String identifiant) {
this.identifiant = identifiant;
}
public String getMotdepasse() {
return motdepasse;
}
public void setMotdepasse(String motdepasse) {
this.motdepasse = motdepasse;
}
//fin de la classe
}
Nousallons maintenantcréer unepage JSP nomméeauthentificationDynaFormsClasse.jsppour gérerunDynaForms
avecnotreclasseUtilisateur.Maintenant,sinousdémarronsl
’
applicationenappelantlapageJSP,nousremarquons
une erreur liée à la déclaration du DynaForms dans le fichier de configuration struts
config.xml
et au
mapping/AuthentificationDynaFormsClasse
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification Dynaforms</title>
<html:base/>
</head>
<body>
<html:form action="/AuthentificationDynaFormsClasse">
Identifiant : <html:text property="utilisateur.identifiant"/><br/>
Mot de passe <html:text property="utilisateur.motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form></body>
</html:html>
- 7 -© ENI Editions - All rigths reserved
NousallonsdoncréaliserladéclarationduformulaireetdumappingdanslefichierdeconfigurationStruts.
...
<form-beans>
...
<!-- ========== Formulaire de gestion des utilisateurs ========== -->
<form -bean name="utilisateurDynaForms"
type="org.apache.struts.validator.DynaValidatorForm">
<form -property name="utilisateur" type="boiteoutils.Utilisateur" />
</form -bean>
...
</form-beans>
<action-mappings>
...
<!-- ===action qui permet de gérer les utilisateurs=== -->
<action
path="/AuthentificationDynaFormsClasse"
name="utilisateurDynaForms"
scope="request"
input="/pages/authentificationDynaFormsClasse.jsp"
type="action.AuthentificationActionUtilisateur">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
...
</action -mappings>
...
Nous remarquons la définition du formulaire nommé utilisateurDynaForms qui est une instance de la classe
org.apache.struts.validator.DynaValidatorForm et qui possède une propriété nommée utilisateur qui est un objet,
instance de la classe boiteoutils.Utilisateur. Ensuite, nous déclarons le mapping avec l
’
accès à
l
’
URL /AuthentificationDynaFormsClasse. Cette URL déclenche l
’
objet formulaire utilisateurDynaForms. Les données
possèdentuneportéerequestetencasd
’
erreur,ondéclenchelapage/pages/authentificationDynaFormsClasse.jsp
.
Nousallonsdésormaiscodercetteclasseafindefaireuntestsurlessaisiesdel
’
utilisateur.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import actionform.AuthentificationForm;
@SuppressWarnings("serial")
public class AuthentificationActionUtilisateur extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
System.out.println("Dans l ’action AuthentificationActionUtilisateur
du contrôleur");
return null;
}
}
L
’
action du formulaire est codée simplement dans un premier temps en rapport avec la valeur path
(path=
’’
/
AuthentificationDynaFormsClasse
’’
)
dufichierdeconfigurationstruts
config.xml
.
- 8 - © ENI Editions - All rigths reserved
Nouspouvonsmaintenantpasseràl
’
étapedevérificationdel
’
authentificationutilisateur.Latechniqueestlamême
qu
’
avec les ActionForms mais il faut remarquer la simplicité d
’
utilisation de l
’
ensemble avec les DynaForms. Il faut
d
’
abordrécupérerleDynaFormsetréaliserlacréationdel
’
objetutilisateurdirectementàpartirduDynaForms.L
’
objet
utilisateurpourraêtreutilisénormalement.Ilfautnoterégalementl
’
utilisationdesaccèsauxpropriétésdelaclasse
Utilisateuraveclanotationpointée(utilisateur.identifiant
).
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
mport org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import boiteoutils.Utilisateur;
@SuppressWarnings("serial")
public class AuthentificationActionUtilisateur extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//le bean formulaire
DynaActionForm authentificationdynaform=(DynaActionForm)form;
//récupérer l ’objet du formulaire
Utilisateur utilisateur=(Utilisateur)
authentificationdynaform.get("utilisateur");
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(utilisateur.getIdentifiant().equals("jlafoss") &&
utilisateur.getMotdepasse().equals("jerome"))
{
redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
}
5.Lefichierdepropriétésetdelangues
UnepageJSPcontientunensembled
’
élémentsenplusducodeHTML,commedutexte,desimages,deschemins
d
’
accèspourlesliens...Danslaplupartdescas,letexteeststatiqueetinséréendurdanslespages.L
’
utilisationde
- 9 -© ENI Editions - All rigths reserved
textestatiquedansunepageJSPnepermetpasl
’
évolutionrapided
’
uneapplication.Demême,sil
’
applicationest
accessibledansplusieurslangues,ilestintéressantd
’
utiliserunoutiladaptéàcesbesoins.
LestechnologiesJavautilisentdesfichierstextepourgérerlespropriétésetlanguesdesapplications.Unfichierde
propriétés est un simple fichier texte portant l
’
extension
.properties
. Ce fichier permet de déclarer des constantes
utilisablesdanstoutel
’
application.Ilestpossibled
’
utiliserplusieursfichiersdeconfigurationpourgérerlespropriétés
etlalocalisation(langues).
Ceoucesfichiersdepropriétésdoiventêtreplacésdansunpaquetagespécifique(ex :ressources).Ensuite,onutilise
labalise<message
resources>dufichierdeconfigurationStrutspourgérercesfichiers.
<message-resources parameter="java.MessageResources" />
Danscetexemple, le fichierdepropriétés se nomme MessageResourcesetsetrouvedanslepaquetage
java
.Une
utilisationpluscouranteseraitalors :
<message-resources parameter="ressources.MessageResources" />
Onremarquequel
’
extension
.properties
n
’
estpasdéclaréedansl
’
attributparameter
.
Unefoislefichierdepropriétéscréé,nouspouvonsinsérerdesinformationssousformedeconstantesavecleformat :
nomconstante=valeur constante
Lapremièrepartieestl
’
identifiantdelaconstante,ellenedoitpascontenird
’
espace.Lapartiedroitedel
’
expression
correspondàlavaleur.Danscettepartie,ilestpossibled
’
utilisertouslestypesdecaractères,mêmelesespaceset
caractèresspéciaux.Parconventiondestermesexplicitesséparéspardespointssontutilisés.
utilisateur.labelpseudonyme=Pseudonyme de l’utilisateur
Il est vivement conseillé d
’
organiser ce fichier de propriétés par groupe de messages communs afin de retrouver
rapidementlesinformations.Unefoisrenseignées,lespropriétéssontaccessiblespartouteslespagesJSP(grâceaux
tagsStrutsetJSTL)maisaussiparlesServlets.
Pourlalocalisation(leslangues),letag<bean:message>permetderécupérerlavaleurd
’
unepropriétéetdel
’
afficher
dansunepageJSP.Cetagcomporteunattributnommékeyquipermetdefaireréférenceàunidentifiantdufichierde
propriétésMessageResources.properties
.
Sinousreprenonsl
’
exempledelapageJSPd
’
authentificationavecleDynaFormsauthentificationDynaForms.jsp,nous
allonsinsérerlesinformationstextuellesàpartirdufichierdepropriétés.Lecodesourceci
dessousestalorsmodifié
aveclesbalisesStruts.
...
<html:form action="/AuthentificationDynaForms">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
...
...
<html:form action="/AuthentificationDynaForms">
<bean:message key="utilisateur.identifiant"/><html:text
property="identifiant"/><br/>
<bean:message key="utilisateur.motdepasse"/><html:text
property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
...
L
’
affichagetextuelestassociéauxinformationsprésentesdanslefichierMessageResources.properties
.
- 10 - © ENI Editions - All rigths reserved
# -- gestion des utilisateurs --
utilisateur.identifiant=Identifiant de l ’utilisateur :
utilisateur.motdepasse=Mot de passe de l ’utilisateur :
Ceprincipeestapplicablepourtoutletexte(lesimages,lessourcesflash...)présentdanslapageJSP,cettedernière
seraalorstotalementparamétrableàpartirdufichierdepropriétés.Pourlagestiondel
’
internationalisation,ilnereste
plusqu
’
àcréerautantdefichiersdepropriétésquedelanguesàsupporterenrespectanttoujourslesidentifiants
définisdanslesfichiers.
Le fichier d
’
une langue spécifique doit porter l
’
extension _xx.properties où xx identifie les lettres de la langue
souhaitée,exemples :
●français :ressources_fr.properties
●anglais :ressources_en.properties
Lefichierdepropriétéspeutêtresuffixédelalocalesouslaformecodelangueetcodepaysafind
’
identifier
unpays,unelangueetundialecte.
Nous allons définir deux fichiers de propriétés, un en français et un en anglais. Nous allons renommer le fichier
MessageResources.propertiesen ressources.properties, cela sera le fichier par défaut (langue française). Nous allons
égalementmettreàjourlaconfigurationdanslefichierstruts
config.xml
.
...
<!-- ================================== Message Resources Definitions -->
<message -resources parameter="ressources.ressources" />
...
Ensuite,nousallonscréerunfichiernomméressources_en.properties.Cefichiercontientlesmêmesinformationsmais
enanglais.
# -- gestion des utilisateurs --
utilisateur.identifiant=User login :
utilisateur.motdepasse=User password :
Notrearborescenceduprojetestalorslasuivante :
- 11 -© ENI Editions - All rigths reserved
Pour tester l
’
application, nous pouvons modifier la langue du navigateur (Outils Options Avancé Général
Langues
Choisir...
Anglais/Etats
Unis
Ajouter
)etsélectionnerl
’
anglais.
Nouspourronsévidemmentdévelopperunepagespécifiquepourpasserd
’
unelangueàl
’
autreparl
’
intermédiairede
liensenmodifiantlalocaleenprogrammation.
- 12 - © ENI Editions - All rigths reserved
Lesvalidationsetvérificationsdedonnées
1.Présentation
NousavonsétudiédanslasectionprécédentelesActionsFormsetDynaFormsainsiqueplusieursbalisesStrutsetle
fichier de propriétés. Ces éléments permettent de créer des services d
’
applications Internet mais rien n
’
interdit
jusqu
’
iciàunutilisateurderentrer,parexemple,unemaildansunchampdate.
Afind
’
évitercetyped
’
erreur,ilestrecommandédeprévoirdestraitementsspécifiquesetdeprévenirl
’
utilisateurque
letyped
’
informationn
’
estpascompatibleaveclasaisiequ
’
ilvientd
’
effectuer.
DanslesapplicationsWebtraditionnelles,ilestnécessairedecodersespropresvalidationsenJavaScriptet/oudu
côtéserveuravecdestraitementsspécifiques.AveclemodèleMVCI,unepageJSPestassociéeàuneServlet,la
validationpeutalorsreprésenterunvolumedetravailconsidérablesil
’
applicationestdetailleconséquente.
Struts propose les deux types de validations, client et serveur, en utilisant différentes techniques selon que les
composants ActionForm ou DynaActionForm sont utilisés. La méthode conventionnelle consiste à implémenter la
méthode
validate()
dansunBeandeformulaire.Lasecondealternativeconsisteàutiliserleplug
inValidatorsquimetà
dispositiondesprogrammeursdesrèglesdevalidation.
2.Validationparméthode(reset()etvalidate())
Dans la partie précédente, nous avons utilisé le formulaire d
’
authentification authentificationForm.jsp de type
ActionForm.LeBeandeformulairehéritedelaclasse abstraiteActionForm,ilpeutdoncimplémenterlesméthodes
déclaréesdansActionForm.Lesdeuxméthodesintéressantessontreset(),pourinitialiserdesdonnées,et
validate()
pourvaliderlessaisiesdel
’
utilisateur.
a.Laméthodereset()
Cette méthode permet d
’
initialiser les propriétés du Bean formulaire. La signature de la méthode reset() est la
suivante :
publicvoidreset(ActionMappingmapping,HttpServletRequestrequest)
●
ActionMapping
:représentel
’
objetquipermetderécupérerleJavaBeandeformulaire.
●HttpServletRequest :représentelarequêteHTTP.
Reprenons notre exemple d
’
authentification avec un formulaire ActionForm. La page JSP nommée
authentificationForm.jspest liée avecla classeactionform.AuthentificationForm. Nous pouvons donc implémenter la
méthodereset()danscetteclassepourinitialiserdesvaleurs.
package actionform;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
@SuppressWarnings("serial")
public class AuthentificationForm extends ActionForm {
private String identifiant=null;
private String motdepasse=null;
public String getIdentifiant() {
return identifiant;
}
...
public void reset(ActionMapping mapping, HttpServletRequest request)
{
this.identifiant="jlafoss";
this.motdepasse="jerome";
}
}
Maintenant,sinousrelançonsnotrenavigateuraveclapaged
’
authentification,nousretrouvons bienlesvaleurs
initialisées.
- 1 -© ENI Editions - All rigths reserved
b.Laméthodevalidate()
Laméthode
validate()
estunedesméthodesdelaclasseabstraiteActionFormqu
’
ilestpossibled
’
implémenterdans
une classe concrète. Cette méthode permet de vérifier la saisie d
’
un utilisateur dans un champ (texte, liste
déroulante...).Siunepropriétén
’
estpasvalide,alorsleformulairerenvoieuneerreursurunepageJSPadaptée.Il
estalorspossibled
’
afficherleserreursavecletagadapté<html:errors>
.
publicActionErrorsvalidate(ActionMappingmapping,HttpServletRequestrequest)
●
ActionMapping
:représentel
’
objetquiapermisderécupérerleJavaBeandeformulaire.
●HttpServletRequest :représentelarequêteHTTP.
Laméthode
validate()
renvoieunobjetdetypeActionErrors
,c
’
estunecollectiondetypejava.util.HashMapquicontient
lalistedeserreurs.
Pourremplircettecollectiond
’
erreurs,ilfaututiliserlaméthode
add()
.
publicvoidadd(Stringproperty,
ActionMessagemessage)
●
String property
: représente une chaîne de caractères qui est la clé d
’
un message d
’
erreur associé à la
propriétéduBeandeformulaireposantproblème.
●ActionMessagemessage :représentelemessagequiestludanslefichierdepropriétésetquiestrenvoyé.
NousallonsmodifiernotreclasseAuthentificationFormpourgérerlavalidationdesdonnées.
package actionform;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
@SuppressWarnings("serial")
public class AuthentificationForm extends ActionForm {
private String identifiant=null;
private String motdepasse=null;
public String getIdentifiant() {
return identifiant;
}
...
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request)
{
ActionErrors erreurs=new ActionErrors();
if(this.identifiant==null || this.identifiant.equals("".trim()))
{
erreurs.add("identifiant", new ActionMessage
- 2 - © ENI Editions - All rigths reserved
("erreur.utilisateur.identifiant"));
}
if(this.motdepasse==null || this.motdepasse.equals("".trim()))
{
erreurs.add("motdepasse", new ActionMessage
("erreur.utilisateur.motdepasse"));
}
return erreurs;
}
}
Danscetexemple,silesattributsidentifiantetmotdepassenesontpasrenseignés,uneerreurestrenvoyéesurla
pageJSP.Lesattributssonttestés,lesvaleursnedoiventpasêtrenullouégalesàunechaînevide.Danslecas
contraire,uneerreurestajoutéedanslacollectionaveccommeclélenomdel
’
attributduJavaBeandeformulaireet
pourvaleur,lemessaged
’
erreurrenseignédanslefichierdepropriétés.
Si une erreur survient sur le champ identifiant par exemple, l
’
entrée erreur.utilisateur.identifiant du fichier de
propriétésserautilisée.
# -- gestion des erreurs
erreur.utilisateur.identifiant=Le champ identifiant doit être renseigné
erreur.utilisateur.motdepasse=Le champ mot de passe doit être renseigné
Lessaisiesserontainsivalidées.Ilrestedésormaisàaffichertouslesmessagesd
’
erreursdansunmêmeblocau
sein d
’
une page JSP avec le tag<html:errors>. Nous allons également paramétrer le routage dans le fichier de
configurationdeStruts(struts
config.xml
).
<action-mappings>
...
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
...
</action -mappings>
Dansceroutage,lavalidationduformulaireparl
’
utilisateur,déclenchel
’
URL/Authentificationaveclaclasseassociée
action.AuthentificationAction. En cas d
’
erreur dans la classe d
’
action, c
’
est la page /pages/authentificationForm.jsp
(paramètreinput)quiestappelée.Doncsilaméthode
validate()
déclencheuneerreur,c
’
estlapaged
’
authentification
qui est affichée. Nous pouvons donc ajouter dans cette page la balise d
’
affichage de toutes les erreurs
<html:errors/>
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification</title>
<html:base/>
</head>
<body>
<html:errors/>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Maintenant,nouspouvonsutiliserlapropriété
property
pourindiquerd
’
afficherunmessagespécifique.
...
Identifiant : <html:text property="identifiant"/><html:errors
property="identifiant"/><br/>
...
- 3 -© ENI Editions - All rigths reserved
Nous remarquons que les erreurs sont affichées sous la forme d
’
une liste à puces. Cela provient des entrées
suivantesdanslefichierdepropriétésquienglobentchaquemessaged
’
erreur.
...
# -- standard errors --
errors.header=<UL>
errors.prefix=<LI>
errors.suffix=</LI>
errors.footer=</UL>
...
Eneffet,labalise
<html:error>
possèdequatreattributsintéressants :
●header :permetd
’
identifierlacléàutiliserpourformaterl
’
en
têtedumessaged
’
erreur.
●
footer
:permetd
’
identifierlaclédufichierdepropriétésàutiliserpourformaterlepieddumessaged
’
erreur.
●
prefix
:permetd
’
identifierlaclédufichierdepropriétésàutiliserpourformaterlepréfixedechaquemessage
d
’
erreur.
●suffix :permetd
’
identifierlaclédufichierdepropriétésàutiliserpourformaterlesuffixedechaquemessage
d
’
erreur.
La syntaxe est la suivante : <html:errors property=
’’
nompropriété
’’
header=
’’
erreurs.entete
’’
footer=
’’
erreurs.pied
’’
prefix=
’’
erreurs.nomprefixe
’’
suffix=
’’
erreurs.nomsuffixe
’’
/>
.
3.LesValidators
Laméthode
validate()
présentéeprécédemmentpermetdegérerlesvalidationsd
’
entréesdansuneclasseActionForm
.
IlexisteunesecondeméthodepourvaliderlesentréesàpartirdefichiersXML.Pourmettreenplacelesvalidationsou
Validators,ilfaututiliserle plug
inStruts qui permetd
’
utiliserdeuxfichiersXMLquisont
validation.xml
et
validator
rules.xml
.
Toutd
’
abord,ladéclarationduplug
ins
’
effectueàlafindufichierstruts
config.xml
:
...
<!-- =================================================== Validator plugin -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB -
INF/validator -rules.xml,/WEB -INF/validation.xml"/>
</plug-in>
...
Dans cette déclaration, nous indiquons que les deux fichiers de gestion des Validators sont présents dans le
répertoire /WEB
INF sous les noms suivants :
validator
rules.xml et
validation.xml
. Il faut ensuite déclarer nos
validationsdanslefichier
validation.xml
etutiliserlesvalidationsStrutsdéclaréesdanslefichier
validator
rules.xml
.
Lefichierdedéclarationdesvalidations(
validation.xml
)possèdelastructuresuivante :
<!DOCTYPE form -validation PUBLIC" -//Apache Software
Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<form-validation>
....
</form-validation>
Labalise
<form
validation>
contientlesélémentsquisontutiliséspourréaliserlesvalidationsdansunformulaire.Les
élémentsquel
’
onpeutimplémenterdanscefichiersontlessuivants :
●
<global>
:permetdedéclarerdesconstantesglobalesaufichier
validation.xml
.
●<constant> : permet de déclarer des constantes avec les sous
éléments <constant
name> et <constant
value>
.
- 4 - © ENI Editions - All rigths reserved
Nous pouvons alors déclarer des constantes globales à base d
’
expressions régulières pour gérer la syntaxe des
numérosdetéléphone,desmotsdepasse,dessitesWeb...
...
<global>
<constant>
<constant -name>entierpositif</constant -name>
<constant -value>^\d+$</constant -value>
</constant>
<constant>
<constant -name>codepostal</constant -name>
<constant -value>^[0 -9a-zA-Z]{5}$</constant -value>
</constant>
</global>
...
Pour valider un JavaBean de formulaire, il faut déclarer le formulaire et les propriétés à valider dans le fichier
validation.xml
àpartirdelabalise<formset>quicontientdessous
éléments.Labalise<formset>possèdelessous
élémentssuivants :
●<formname=
’’
nomduformulaire
’’
>
:
permetd
’
identifierleJavaBeandeformulaire.
●
<fieldproperty=
’’
nomduchamp
’’
/>
:
permetd
’
identifierlenomduchampàvalider.
●
<argkey=
’’
errors.nomdelacle
’’
/>
:
permetd
’
identifierunecléprovenantdufichierdepropriétésafindedéfinirle
texteàafficherdanslemessaged
’
erreur.
●
<varname=
’’
nomdelavariable
’’
/>
:
permetd
’
identifierlesvariablespourlesmasquesdevalidation.
Nousallonsmettreenœuvrel
’
applicationdesvalidationsàpartirdenotreexempled
’
authentification.Pourrappel,le
fichierstruts
config.xml
contientladéclarationduformulairesouslaformesuivante :
...
<!-- bean d’authentification actionform -->
<form-bean name="authentificationForm"
type="actionform.AuthentificationForm"/>
...
Le formulaire d
’
authentification est donc nommé authentificationForm. Nous pouvons alors déclarer ses règles de
validationsdanslefichier
validation.xml
enréalisantuneréférenceàcenom.
...
<formset>
<!-- ========== Formulaire authentification utilisateur ========== -->
<form name="authentificationForm">
field property="identifiant" depends="required,minlength,
maxlength,mask">
<arg0 key="identifiantutilisateur"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<arg3 key="identifiantutilisateur"/>
<var>
<var -name>minlength</var -name>
<var -value>4</var -value>
</var>
<var>
<var -name>maxlength</var -name>
<var -value>7</var -value>
</var>
<var>
<var -name>mask</var -name>
<var -value>^[a -zA-Z]{1}[a-zA-Z]*$</var -value>
</var>
</field>
</form>
</formset>
- 5 -© ENI Editions - All rigths reserved
...
Nousdéclaronsnotreformulaireparl
’
intermédiairedelabalise
<form/>
.Ensuite,nousprécisonslesrèglespourle
champidentifiant. L
’
attributdependsdel
’
élément
field
permet de déterminerquelles sont lesvalidations àréaliser.
L
’
attribut depends peut avoir plusieurs valeurs qui sont des références à des méthodes déclarées dans le fichier
validator
rules.xml
.
Strutsproposedéjàplusieursrèglesdevalidationsquenouspouvonsutiliserdirectementsansajouterdedéclaration
personnalisée.
●
required
:permetdevérifiersilechampn
’
estpasnullouvide.
●validwhen :permetdevérifiersiunchampalamêmevaleurqu
’
unautre.
●maxlength :permetdevérifierlataillemaximumd
’
unchamp.
●minlength :permetdevérifierlatailleminimumd
’
unchamp.
●mask :permetdevérifiersilavaleurrespecteuneexpressionrégulièrepersonnelle.
●byte :permetdevérifiersilavaleurpeutêtreconvertieentypeByte
.
●short :permetdevérifiersilavaleurpeutêtreconvertieentypeShort
.
●integer :permetdevérifiersilavaleurpeutêtreconvertieentypeInteger
.
●
long
:permetdevérifiersilavaleurpeutêtreconvertieentypeLong
.
●
float
:permetdevérifiersilavaleurpeutêtreconvertieentypeFloat
.
●
double
:permetdevérifiersilavaleurpeutêtreconvertieentype
Double
.
●
date
:permetdevérifiersilavaleurpeutêtreconvertieentypeDate
.
●intRange :permetdevérifiersilavaleurestcontenuedanslafourchettedetypeInteger
.
●
floatRange
:permetdevérifiersilavaleurestcontenuedanslafourchettedetypeFloat
.
●
doubleRange
:permetdevérifiersilavaleurestcontenuedanslafourchettedetype
Double
.
●
creditCard
:permetdevérifiersilavaleurrespecteleformatcartedecrédit.
●email :permetdevérifiersilavaleurrespecteleformatemail.
●url :permetdevérifiersilavaleurrespecteleformatdesliensInternetouURL.
Dans notre exemple, la validation précise que la saisie est obligatoire (
required
), la taille mini est de 4 caractères
(
minlength), la taille maxi est de 7 caractères (maxlength) et que la saisie doit respecter le masque indiqué par
l
’
expression régulière (mask). Chacune des méthodes de validation est associée à un message dans le fichier de
propriétés.Cettetechniquepermetdegérerdefaçoncomplexelesparamètresetlangues.
Reprenonsl
’
exempledelaméthode
required
.
errors.required=Attention, le champ [{0}] doit être renseigné !
Nousremarquonsl
’
utilisationdesaccoladesaveclavaleurzéro{0}.Enfait,cettedéclarationestliéeàunattribut
<arg key=
’’
...
’’
/>
présent dans le ficher
validation.xml
. Ainsi, le {0} est remplacé par la valeur de la clé
identifiantutilisateurcontenuedanslefichierdepropriétés.
# -- standard errors --
errors.header=<UL>
- 6 - © ENI Editions - All rigths reserved
errors.prefix=<LI>
errors.suffix=</LI>
errors.footer=</UL>
errors.required=Attention, le champ [{0}] doit être renseigné !
errors.minlength=Attention, le champ [{0}] doit avoir au moins
{1} caractères !
errors.maxlength=Attention, le champ [{0}] ne peut avoir plus de
{1} caractères !
errors.invalid=Attention, le champ [{0}] est incorrect!
errors.date=Attention, le champ [{0}] n ’est pas une date valide !
errors.byte=Attention, le champ [{0}] doit être un octet !
errors.date=Attention, le champ [{0}] doit être une date !
errors.double=Attention, le champ [{0}] doit être un double !
errors.float=Attention, le champ [{0}] doit être un réel !
errors.integer=Attention, le champ [{0}] doit être un entier !
errors.long=Attention, le champ [{0}] doit être un entier long !
errors.short=Attention, le champ [{0}] doit être un entier court !
errors.range=Attention, le champ [{0}] doit être dans l ’intervalle
{1} et {2} !
errors.creditcard=Attention, le champ [{0}] n ’est pas un numéro de
carte valide !
errors.email=Attention, le champ [{0}] n ’est pas une adresse
électronique valide !
# -- gestion des utilisateurs --
utilisateur.identifiant=Identifiant :
utilisateur.motdepasse=Mot de passe :
identifiantutilisateur=identifiant
motdepasseutilisateur=mot de passe
# -- gestion des erreurs
erreur.utilisateur.identifiant=Le champ identifiant doit être renseigné
erreur.utilisateur.motdepasse=Le champ mot de passe doit être renseigné
Nous pouvons enfin mettre en place les validations à partir des accesseurs pour chaque champ du formulaire en
utilisantlesActionForm.Cettetechniquetrèslourde,nécessitebeaucoupdecode.Lesvalidationslesplusutilisées
étantlesValidatorssurlesDynaForms
.
4.LesDynaFormsetValidators
IlesttrèssimpledecréerdesvalidationscomplexesaveclesformulairesdynamiquesStruts.Ilsuffitdedéclarerle
formulairedanslefichierstruts
config.xml
etdecréerlesValidatorsassociés.
SinousreprenonsnotrefichierauthentificationDynaForms.jsp,noustrouvonsladéclarationsuivante :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Authentification Dynaforms</title><html:base/></head>
<body>
<html:errors/>
<html:form action="/AuthentificationDynaForms">
<bean:message key="utilisateur.identifiant"/><html:text
property="identifiant"/><br/>
<bean:message key="utilisateur.motdepasse"/><html:text
property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Ensuite, nous retrouvons la déclaration associée dans le fichier struts
config.xml
. Nous remarquons que la classe
utiliséen
’
estplusorg.apache.struts.action.DynaActionFormmaisorg.apache.struts.validator.DynaValidatorForm
.
...
<form-beans>
<!-- bean d’authentification dynaforms -->
<form-bean name="authentificationDynaForms"
type="org.apache.struts.validator.DynaValidatorForm">
- 7 -© ENI Editions - All rigths reserved
<form -property name="identifiant" type="java.lang.String" initial=""/>
<form -property name="motdepasse" type="java.lang.String" initial=""/>
</form-bean>
</form-beans>
...
<action
path="/AuthentificationDynaForms"
name="authentificationDynaForms"
validate="true"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/bienvenueDynaForms.jsp"
input="/pages/authentificationDynaForms.jsp"
scope="request">
</action>
...
Leparamètre
validate
permetdeprécisers
’
ilfautvérifierlessaisiesounon.Leparamètreinputesttrèsimportant,il
permetd
’
indiquersurquellepageretournerencasd
’
erreur.LenomdenotreformulaireestauthentificationDynaForms
,
ilneresteplusqu
’
àcréerlesvalidationsdanslefichier
validation.xml
.
...
<!-- ========== Formulaire authentification utilisateur ========== -->
<form name="authentificationDynaForms">
<field property="identifiant" depends="required,minlength,
maxlength,mask">
<arg0 key="identifiantutilisateur"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var -name>minlength</var -name>
<var -value>4</var -value>
</var>
<var>
<var -name>maxlength</var -name>
<var -value>7</var -value>
</var>
<var>
<var -name>mask</var -name>
<var -value>^[a -zA-Z]*$</var -value>
</var>
</field>
</form>
...
Dans ce cas, nous indiquons que le champ est obligatoire, qu
’
il doit contenir une taille mini et maxi et qu
’
il doit
respecter l
’
expression régulière précisée dans le masque. Le paramètre
<arg0 key=
’’
...
’’
>
permet de réaliser les
remplacementsdesnomsdepropriétés.Dansnotrecas,lavaleurdelacléidentifiantutilisateurseraremplacéedans
lemessaged
’
erreur.
Leformulairedynamiqueestopérationnel.Nousvenonsdemettreenplaceunevérificationtrèspointueavecquatre
syntaxesdifférentessur un mêmechampde formulaire. Ceservicepermet de mettreenévidence l
’
utilisationd
’
un
frameworkpourfaciliterlatâcheduprogrammeur.Nousimaginonstoutdesuitelaquantitédetravailàréaliserau
seind
’
uneactiondelaServletpourgérercesvérifications.Demême,lesrèglessontdéclaréesdansunfichierstatique
- 8 - © ENI Editions - All rigths reserved
auformatXML.Nouspourronsainsimodifierunerèglesanscompilerànouveaul
’
applicationnitoutredéployer.
Nouspouvonsenfinterminerparlamiseenplacederèglessurlemotdepasseutilisateur.
<!DOCTYPE form -validation PUBLIC
" -//Apache Software Foundation//DTD Commons Validator Rules
Configuration 1.1.3//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<form-validation>
<global>
<constant>
<constant -name>password</constant -name>
<constant -value>^[0 -9a-zA-Z]*$</constant -value>
</constant>
</global>
<formset>
<! -- ========== Formulaire authentification utilisateur ========= -->
<form name="authentificationDynaForms">
<field property="identifiant" depends="required,minlength,
maxlength,mask">
<arg0 key="identifiantutilisateur"/>
<arg1 name="minlength" key="${var:minlength}"
resource="false"/>
<arg1 name="maxlength" key="${var:maxlength}"
resource="false"/>
<var>
<var -name>minlength</var -name>
<var -value>4</var -value>
</var>
<var>
<var -name>maxlength</var -name>
<var -value>7</var -value>
</var>
<var>
<var -name>mask</var -name>
<var -value>^[a -zA-Z]*$</var -value>
</var>
</field>
<field property="motdepasse" depends="mask">
<arg0 key="motdepasseutilisateur"/>
<var>
<var -name>mask</var -name>
<var -value>${password}</var -value>
</var>
</field>
</form>
</formset>
</form-validation>
Nousremarquonsaucoursdelaprésentationdesexemples,unélémenttrèsimportantdel
’
utilisationduframework
Struts, la conservation des données. En effet, nous n
’
avonspas besoin de gérer leretour des donnéeslors des
saisies.Chaquesaisieetchoixdel
’
utilisateursontconservésencasd
’
erreur:lesinformationsdanslesformulaires
n
’
ontpasbesoind
’
êtredenouveausaisies.Cettetechniqueestextrêmementsoupleetpratiqueparrapportaux
langagesInternetcourants.
Nous allons présenter le principe de validation avec un formulaire dynamique de type classe. Pour mettre en
applicationcettetechnique,nouspouvonsreprendreleDynaFormssuivant :
<!-- ========== Formulaire de gestion des utilisateur ========== -->
<form-bean name="utilisateurDynaForms"
type="org.apache.struts.validator.DynaValidatorForm">
<form -property name="utilisateur" type="boiteoutils.Utilisateur" />
</form-bean>
Danscettedéclaration,leformulaireestassociédirectementàlaclasseUtilisateur.Cependant,Strutspermetdegérer
entièrementdesformulairesassociésàdesclassesJava.
- 9 -© ENI Editions - All rigths reserved
Leformulaired
’
authentificationavecunDynaFormsliéàlaclasseestprésentéci
dessous :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification Dynaforms</title>
<html:base/>
</head>
<body>
<html:errors/>
<html:form action="/AuthentificationDynaFormsClasse">
Identifiant : <html:text property="utilisateur.identifiant"/><br/>
Mot de passe <html:text property="utilisateur.motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Ce formulaire déclenche l
’
action /AuthentificationDynaFormsClasse déclarée dans le fichier de configuration struts
config.xml
.
<!-- ===action qui permet de gérer les utilisateurs=== -->
<action
path="/AuthentificationDynaFormsClasse"
name="utilisateurDynaForms"
scope="request"
validate="true"
input="/pages/authentificationDynaFormsClasse.jsp"
type="action.AuthentificationActionUtilisateur"
>
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
Ledéclenchementduformulairepermetd
’
appelerl
’
actionexecute()delaclasseAuthentificationActionUtilisateur.Par
contre,leparamètre
validate
deladéclarationdel
’
actionestdésormaisplacéàtrueafindevaliderlavérificationdes
saisiesutilisateur. Encasd
’
erreur,c
’
estla page/pages/authentificationDynaFormsClasse.jsp quiseraappelée.Nous
allonsréaliserunevérificationsurlaprésenceobligatoiredesinformations(
required
).
<!-- ======= Formulaire authentification utilisateur avec classe ======== -->
<form name="utilisateurDynaForms">
<field property="utilisateur.identifiant" depends="required">
<arg0 key="identifiantutilisateur"/>
</field>
<field property="utilisateur.motdepasse" depends="required">
<arg0 key="motdepasseutilisateur"/>
</field>
</form>
Lasyntaxedufichier
validation.xml
n
’
estpastoutàfaitlamêmequ
’
avecl
’
utilisationdetypessimples.Eneffet,ilfaut
préfixerchaquechampdelaclasseparlenomdel
’
instancecréée.Danslefichierstruts
config.xml
,nousavonsdéclaré
leformulairedecettemanière :
<form-property name="utilisateur" type="boiteoutils.Utilisateur" />
Lenomdel
’
instanceestdoncutilisateur.Ainsi,lorsdesvalidationsouaccès,nousutiliseronslepréfixeutilisateurpour
faireréférenceauxattributsdelaclasseboiteoutils.Utilisateur.Maintenant,dupointdevuedel
’
utilisationdecetobjet,
nousrécupéronsleDynaFormsdanslaclassed
’
actionaprèsavoirréaliséuntranstypage.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
- 10 - © ENI Editions - All rigths reserved
import boiteoutils.Utilisateur;
@SuppressWarnings("serial")
public class AuthentificationActionUtilisateur extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//le bean formulaire
DynaActionForm authentificationdynaform=(DynaActionForm)form;
//récupérer l ’objet du formulaire
Utilisateur utilisateur=(Utilisateur)authentificationdynaform.get
("utilisateur");
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(utilisateur.getIdentifiant().equals("jlafoss") &&
utilisateur.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
}
Cettetechniqued
’
utilisationdeformulairesdynamiquesàpartirdeclasseesttrèspratiqueetsoupleàutiliser.Dansla
plupartdescas,c
’
estcetypedeprogrammationquiestutiliséavecStruts.
5.Miseenforme
Nousavonsvutoutl
’
intérêtd
’
utiliserlesvalidationsdynamiquesaveclesValidatorsetlefichierdepropriétés.Comme
les messages affichés en cas d
’
erreur sont gérés à partir du fichier de propriétés, nous pouvons améliorer ces
affichagespourrespecterlesstandardsHTML.
Eneffet,enHTML,labalise
<labelfor=
’’
idchamp
’’
/>
permetdedonnerunnom/labeletderéaliserunlienautomatique
sur le champ indiqué par l
’
attribut
for
.Nous pouvonsreprendre notre pageJSP authentificationDynaForms.jsppour
gérerl
’
attribut
id
d
’
unebaliseHTML.Pourlemoment,latraductiondutag<html:textproperty="identifiant"/>produitle
codesourcesuivant :<inputtype="text"name="identifiant"value="">.Ilmanquedanslabalise
<input/>
l
’
attribut
id
aveclenomduchamp.Pourcela,lalibrairiedetagStrutsproposel
’
attribut
errorStyleId
.
Lenouveaufichierestdonclesuivant :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Authentification Dynaforms</title><html:base/></head>
<body>
<html:errors/>
<html:form action="/AuthentificationDynaForms">
<bean:message key="utilisateur.identifiant"/>
<html:text property="identifiant" errorStyleId="identifiant"/><br/>
<bean:message key="utilisateur.motdepasse"/>
<html:text property="motdepasse" errorStyleId="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Ensuite,nousallonsmodifierlefichierdepropriétéspourretournerlabalise
<label/>
danslemessaged
’
erreur.
errors.required=<label for="{0}">Attention, le champ [{0}]
doit être renseigné !</label>
errors.minlength=<label for="{0}">Attention, le champ [{0}]
doit avoir au moins {1} caractères !</label>
errors.maxlength=<label for="{0}">Attention, le champ [{0}]
ne peut avoir plus de {1} caractères !</label>
- 11 -© ENI Editions - All rigths reserved
Nousréalisonsensuiteuneauthentificationincorrectepourtesternotreexemple.Lemessaged
’
erreurenretourest
alors dynamique, nous pouvons cliquer sur celui
ci pour se positionner automatiquement dans le champ de saisie
concernéetafficherlesdernièrespropositions.
Nouspouvonsencoreaméliorerlesaffichagesavecl
’
utilisationdestylesCSS.Nousallonsdéfiniruneclassed
’
erreur
enstyleavecunfondrouge.Lapaged
’
authentificationestalorslasuivante :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Authentification Dynaforms</title>
<style type="text/css">
.erreur
{
background -color:#FF0000;
}
</style>
<html:base/>
</head>
<body>
<html:errors/>
<html:form action="/AuthentificationDynaForms">
<bean:message key="utilisateur.identifiant"/>
<html:text property="identifiant" errorStyleId="identifiant"
errorStyleClass="erreur"/><br/>
<bean:message key="utilisateur.motdepasse"/>
<html:text property="motdepasse" errorStyleId="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Encasd
’
erreurdesaisie,laclasseCSSerreurestappliquéegrâceàl
’
attributerrorStyleClassdelabibliothèquedetag
Struts(http://struts.apache.org/1.x/struts
taglib/tagreference.html).
LestechniquesdemiseenformeavecStrutssonttrèspuissantes,lesseuleslimitessontnotreimagination.
6.ValidationsenJavaScript
Jusqu
’
àprésentnousavonsréalisélesvalidationscôtéserveuraveclestechnologiesStrutsouJava.Lesinformations
sontenvoyées,analysées,validéespuisretournéesversl
’
utilisateurpourcorrectionsibesoin.Pouréviterlesallers
retoursentreleclientetleserveuretajouterdelasouplesseàl
’
application,nouspouvonsinsérerdesvalidations
côté client. En développement Web traditionnel, il est nécessaire de développer ses propres scripts JavaScript en
- 12 - © ENI Editions - All rigths reserved
rapportaveclestestscôtéserveur.AvecStruts,ilestpossibled
’
utiliserlesValidatorsetd
’
ajouteruneseulebalisede
tagdanslapageconcernée.
SinousvoulonsajouterlavalidationJavaScriptsurnotrepaged
’
authentificationprécédente,nousdevonsajouterla
balise<html:javascriptforName=
’’
nomduformulaire
’’
/>
dansl
’
en
têtedelapageJSP.Lasecondeinstructiondoitêtre
ajoutée dans la balise
<html:form/>
pour indiquer la validation en JavaScript (onsubmit=
’’
return
validatenomduformulaire(this)
’’
)
surleformulaire.Lapremièreinstructionutilisel
’
attributformName,ildoitavoirpour
valeurlenomduJavaBeandeformulaire.LasecondeinstructionutiliselaméthodeJavaScriptcommençantpar
validate
etseterminantparlenomduJavaBeandeformulaire.
Sinousprenonsnotrepaged
’
authentificationnomméeauthentificationDynaForms.jsp,voicilenouveaucode :
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head>
<title>Authentification Dynaforms</title>
<style type="text/css">
.erreur
{
background -color:#FF0000;
}
</style>
<html:javascript formName="authentificationDynaForms"/>
<html:base/>
</head>
<body>
<html:errors/>
<html:form action="/AuthentificationDynaForms" onsubmit="return
validateAuthentificationDynaForms(this)">
<bean:message key="utilisateur.identifiant"/><html:text property=
"identifiant"
errorStyleId="identifiant" errorStyleClass="erreur"/><br/>
<bean:message key="utilisateur.motdepasse"/><html:text property="motdepasse"
errorStyleId="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
</body>
</html:html>
Leparamètreonsubmit=
’’
...
’’
contienttoujoursuneméthodeJavaScriptaveclapremièrelettredupréfixeen
majuscule.Dansnotrecas,leformulairesenommeauthentificationDynaFormsmaislaméthodeestappelée
aveclenomvalidateAuthentificationDynaForms
.
Nous pouvons désormais actualiser la page et Struts va automatiquement générer les validations JavaScript côté
clientenaccordaveclesvalidationsJavacôtéServeur.
Pardéfaut,avecStruts,c
’
estunefenêtred
’
alertequiestaffichéepourlesvalidationsJavaScript.Ilestégalement
possibledechangercetteprésentationenmodifiantlesscriptsprésentsdanslalibrairie commons
validator.jar
etle
paquetageorg.apache.commons.validator.javascript
.
- 13 -© ENI Editions - All rigths reserved
Pour résumer, le principe d
’
utilisation de Struts est assez simple une fois que tous les services et fichiers de
configurationssontbienassimilés.NousutiliseronsessentiellementlesDynaFormsavecdesvalidationsparfichierde
configuration(
validation.xml
)etJavaScript.
Voici ci
aprèsunschémarécapitulatifdufonctionnementglobaletdesfichiersàconnaîtrepourmettreenplaceles
servicesStruts.
- 14 - © ENI Editions - All rigths reserved
LecontrôleurStruts
1.Présentation
AvecJavaEElemodèleoudesignpatternMVCIpourModèle
Vue
Contrôleurestsouventutilisé.Chacundeséléments
decemodèleestassociéàunrôleparticulier :
●UnJavaBeanquijouelerôledemodèleetquiestrendupersistantdansunebasededonnées.
●UnepageJSPquijouelerôledevueetquipermetd
’
afficheràl
’
écranlesattributsduJavaBean.
●UneServletquijouelerôledecontrôleur,ellegèrelestraitementsetactionsentrelemodèle,leJavaBeanet
lesystèmedepersistance.
AveccemodèleMVCI,ilexistequasimentautantdeServletscontrôleurquedepagesJSPetdeJavaBeans.Chaque
Servlet déclarée dans le fichier web.xml réalise une action (ou un ensemble). Pour alléger le code et éviter ces
redondances,lemodèleaévoluéenmodèleMVCII.Iln
’
existealorsplusqu
’
uneseuleServletpourtouteslespages
JSPetlesJavaBeans.SeulecetteServletestdéclaréedanslefichierweb.xmletgèrelatotalitédesdemandesclients.
Strutss
’
appuiesurcemodèleMVCIIetpossèdedoncuneseuleServletdegestion.
La Servlet principale utilisée par Struts est ActionServlet, elle fonctionne en accord avec le fichier de configuration
struts
config.xml
. Dans ce fichier, toutes les actions associées au contrôleur sont déclarées. Ces actions sont des
classesdetypeActionquienfait,remplacentlesServletsdumodèleMVCI.
2.UtilisationetdéclarationdelaclasseActionServlet
La classe org.apache.struts.action.ActionServlet est la Servlet principale de toute application Struts. Cette Servlet
possèdedonclaméthode
init()
quiestlancéeaudémarragedel
’
application,destroy()quiestlancéelorsdel
’
arrêtde
l
’
application,
doGet()
quiestlaméthodedeserviceassociéeàlaméthodeGETHTTPet
doPost()
quiestlaméthodede
serviceassociéeàlaméthodePOSTHTTP.
CetteclasseActionServletestlaServletcontrôleurdel
’
applicationStruts,elles
’
appuiesurlefichierdeconfiguration
struts
config.xml
pourretrouverlesdifférentsélémentsdeconfigurationdontelleabesoinafinquel
’
applicationWeb
fonctionnecorrectement.
La déclaration de cette Servlet principale est réalisée dans le fichier web.xml de l
’
application. Il faut remarquer la
déclarationduparamètredeconfigurationquiestlefichierdegestionStrutsetlaprioritéduchargementaveclabalise
<load
on
startup/>
.
<!-- Standard Action Servlet Configuration -->
<servlet>
<servlet -name>action</servlet -name>
<servlet -class>org.apache.struts.action.ActionServlet</servlet -class>
<init -param>
<param -name>config</param -name>
<param -value>/WEB -INF/struts -config.xml</param -value>
</init -param>
<load -on-startup>2</load -on-startup>
</servlet>
LaServletActionServletestlecontrôleurprincipalStrutsmaisnouspouvonstrèsbiendéclarerd
’
autresServlet
danslefichierweb.xmlavecleprincipedumodèleMVCIetmixerlesdeuxtechnologies.
Pourrappel,labalise<servlet
name>permetdedonnerunnomàlaServletet<servlet
class>identifielaclassedela
Servlet Struts, toujours org.apache.struts.action.ActionServlet. La Servlet possède également, comme pour toute
applicationWeb,unaliasdéclaréaveclabalise<servlet
mapping>
.
TouteslesURLseterminantparlesuffixe
.do
serontassociéesetdéclencherontlaServletActionServlet
.
<!-- Standard Action Servlet Mapping -->
<servlet -mapping>
<servlet -name>action</servlet -name>
<url -pattern>*.do</url -pattern>
- 1 -© ENI Editions - All rigths reserved
</servlet -mapping>
Parexemple,siunepageJSPpossèdeunlienouunformulaireavecl
’
URL/authentification.do
, l
’
ActionServletStruts
cherchera dans son fichier struts
config.xml
une action dont le paramètre path aura pour valeur
path=
’’
/
authentification
’’
.
Parconvention avecStruts le suffixe
.do
est utilisémais rienn
’
empêched
’
utiliserunautre
suffixecomme.jss
,
.jst
...
3.LesclassesActions
LaclasseActionServletestdéveloppéeparlafondationStrutsetnedoitpasêtremodifiée.ToutlecodeJavadoitêtre
implémentédansunedesclassesd
’
action.Uneclassed
’
actionestuneclasseJavaquihéritedelaclasseAction.Cette
classe Action, implémente des méthodes qui peuvent être redéfinies dans la classe d
’
action développée par le
programmeur,laplusimportanteétantlaméthodeexecute()quiestletraitementdel
’
action.Cetteméthodeestle
pointd
’
entréedelaclassed
’
action,c
’
estl
’
équivalentdelaméthode
doGet()
ou
doPost()
d
’
uneServlet.
Toute classe d
’
action doit être déclarée dans le fichier de configuration de Struts,struts
config.xml
. La déclaration
d
’
uneactions
’
effectueparl
’
intermédiairedeséléments
<action>
présentsdanslabalise
<action
mappings>.L
’
élément
<action>
possèdeplusieursattributsquipermettentdeconfigureruneaction :
●path :ceparamètrepermetd
’
identifierlenomdel
’
actionsanslesuffixe.Cenomestliéàl
’
attributactiond
’
un
formulaireouàl
’
URLd
’
unlien.
●type : ce paramètre permet d
’
identifier le nom complet de la classe d
’
action que l
’
ActionServlet doit utiliser
commeaction.Lenomestpréciséaveclepaquetagedelaclasse.
●name :ceparamètreestlenomduJavaBeandeformulairequivarecevoirlesinformationsdel
’
action.
●scope :ceparamètredéfinitlaportéeduJavaBeandeformulaire(requêteousession).Silavaleurestsession
,
lesinformationssaisiesparl
’
utilisateursontconservéestoutletempsdelasessionutilisateur.
●attribute :ceparamètrepermetd
’
identifierleJavaBeandeformulaireparunnom.Sicetattributestabsentde
ladéclaration,lenomduJavaBeanestlemêmequeceluidéfinidansl
’
attributname
.
●input :ceparamètrepermetd
’
identifierlavueàl
’
originedel
’
actionmaiségalementlavueouactionquisera
appeléeencasd
’
erreurdesaisie.
●
forward
:ceparamètrepermetderéaliserdesliensversuneautrevueouaction.
●include :ceparamètrepermetd
’
inclureuneautrevuedanslavueàl
’
originedel
’
action.
Labalise
<action>
possèdeégalementdessous
éléments :
●exceptions :permetd
’
identifieruntyped
’
exceptionetdeconfigurerlesopérationsàeffectuerencasd
’
erreur.
●set
property
:permetdetransmettredesinformationsàl
’
action.
●
forward
:permetderéaliseruneredirectionversunevueouactionspécifique.Unedéclarationd
’
actionpeut
êtreassociéeàplusieursbalises
<forward/>
.
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
Dansl
’
exempleprécédent,l
’
attributpathidentifiel
’
actionappeléeparunlienouunformulaire.Nousretrouveronsalors
unliencommececi
<ahref=
’’
/
Authentification.do
’’
>
lien</a>
ouunformulaire
<formaction=
’’
/
Authentification.do
’’
>
ouun
formulaireStruts<html:formaction=
’’
/
Authentification.do
’’
>
.
L
’
attributtypepermetd
’
identifierlaclassedel
’
application
quiréaliseral
’
actionassociée(action.AuthentificationAction
). L
’
attributnamedéfinitleJavaBeandeformulairedéclaré
- 2 - © ENI Editions - All rigths reserved
danslefichierdeconfigurationstruts
config.xml
.
<!-- bean d’authentification actionform -->
<form-bean name="authentificationForm"
type="actionform.AuthentificationForm"/>
Les attributs name de la déclaration du JavaBean de formulaire et de la déclaration de l
’
action sont strictement
identiques.L
’
attributscopeindiquequeleJavaBeanestrenseignéseulementpourlarequêtedel
’
utilisateur.L
’
attribut
attributen
’
est pas précisé car le nom du JavaBean de formulaire est identique à l
’
attributname de l
’
action.Enfin,
l
’
attribut input indique que la page de soumission est la JSP /pages/authentificationForm.jsp. Si des erreurs de
validation sont déclenchées, la page déclarée à input sera alors affichée en retour sans perte d
’
informations
(éventuellessaisiesdel
’
utilisateur).
Ilexisteégalement deuxsous
éléments
<forward>
,ceséléments permettentderediriger l
’
utilisateurversd
’
autres
vuesouactionsenconservantoupaslesinformationsprésentesdanslarequête.Sonfonctionnementestidentiqueà
laclasseRequestDispatcheraveclaméthode
forward()
.
L
’
élément
<forward>
comportelesattributssuivants :
●name :ceparamètrepermetdedonnerunnomàlaredirectionquiserautiliséedansl
’
action.
●path :ceparamètrepermetd
’
identifierlavueoul
’
actionverslaquelleréaliserletransfert.
●
redirect
:ceparamètreoptionnelattendunevaleurbooléennepoursavoirsilesinformationsdelarequête
sontconservéesounondanslaredirection(classeRequestDispatcheravec
forward()
ouresponse.sendRedirect
()
).
Ilestégalementpossiblededéfinirdesredirectionsglobalesàtouteslesactionsdel
’
application.Cesredirections
doiventêtredécritesendehorsdel
’
élément
<action
mapping>
danslabalise
<global
forwards>
.
<global-forwards>
<forward name="welcome" path="/Welcome.do"/>
</global -forwards>
Cette technique permet de définir des redirections globales comme la page d
’
accueil de l
’
application, la page
d
’
erreurs...Ledéveloppementetlamaintenancesontalorsfacilités.
L
’
élément
<exception>
comporteégalementplusieursattributs.Cettebalisepermetdegérerleserreursquipeuvent
survenirdansl
’
application.Commepourlabalise
<forward>
,ilestpossiblededéclarerdesexceptionsdansuneaction
oudefaçonglobale.
●key :ceparamètrepermetd
’
identifierlemessaged
’
erreuràutiliserdanslefichierdepropriétés.
●path :ceparamètreidentifielavueoul
’
actionutiliséepourafficherl
’
erreur.
●type :ceparamètrepermetdedéclarerletyped
’
exceptionàtraiter.
●scope : ce paramètre indique si les erreurs doivent être enregistrées dans la session ou la requête de
l
’
utilisateur.
4.Laméthodeexecute()
Uneactionestcomposéedesadéclarationdanslefichierstruts
config.xml
etd
’
uneclassed
’
actionquiimplémentela
méthodeexecute()
.
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception
●
ActionMappingmapping
:ceparamètrereprésentel
’
instancedel
’
actionencours.
●ActionFormform :ceparamètrereprésenteleJavaBeandeformulairesoumisàl
’
action.
●ServletRequestrequest :ceparamètrereprésentelarequêteHTTPdel
’
utilisateur.
- 3 -© ENI Editions - All rigths reserved
●ServletResponseresponse :ceparamètrereprésentelaréponseHTTPdel
’
utilisateur.
Cetteméthodeexecute()estlancéelorsquel
’
actionestinstanciéeparl
’
ActionServlet.Ilestalorspossiblederécupérer
les informations du JavaBean de formulaire grâce au paramètre, objet form. On peut ainsi récupérer par simple
transtypage le JavaBean de formulaire. Le principe est le même, que cela soit avec un ActionForm ou un
DynaActionForm
.
//lire le JavaBean
AuthentificationForm authentificationform=(AuthentificationForm)form;
//lire le JavaBean
DynaActionForm authentificationdynaform=(DynaActionForm)form;
Laclasse
ActionMapping
possèdeuneméthode
findForward()
quipermetderetrouveruneredirectiondéfinieparleou
lesélément(s)
<forward>
del
’
actionouuneredirectionglobaledéfiniedans
<global
forwards>
.Enrésumé,laméthode
execute()estletraitementàréaliserparl
’
action.Ellepermetderécupérerlesinformationstransmisesparl
’
utilisateur
dansleJavaBeandeformulaire,deréaliseruneactionspécifiqueetderedirigerl
’
utilisateur.
La classeAction possède des classes filles ou classes dérivées. Ces classes permettent de simplifier le travail du
programmeurauseind
’
uneaction.
La classe ForwardAction est l
’
action la plus simple et une des plus utilisées. Cette classe permet de réaliser des
redirectionssansimplémentationdecodeJava.Ellefonctionnedelamêmefaçonquelaméthode
forward()
delaclasse
RequestDispatcher.Cetteclasseestsurtoututiliséepourfaireduroutage,lorsqu
’
unepageJSPsouhaitetransférerses
donnéesversuneautrepageJSP,actionouServlet.
<action
path="/AuthentificationDynaForms"
name="authentificationDynaForms"
validate="true"
type="org.apache.struts.actions.ForwardAction"
parameter="/pages/bienvenueDynaForms.jsp"
input="/pages/authentificationDynaForms.jsp"
scope="request">
</action>
Dansl
’
exempleprécédent,lorsquel
’
utilisateursoumetleformulairedynamique,lessaisiessontvalidées.Encasde
succès, l
’
utilisateur est redirigé vers la page/pages/bienvenueDynaForms.jsp par l
’
intermédiaire de la configuration
type=
’’
org.apache.struts.actions.ForwardAction
’’
et
parameter=
’’
/
pages/bienvenueDynaForms.jsp
’’
.
LaclasseIncludeActionestuneactionsimple.Ellenenécessitepasd
’
implémentationdecode.Ledéveloppeurn
’
adonc
pasbesoindecréersapropreclasse.Elleestutiliséelorsqu
’
undéveloppeursouhaitetransférersesdonnéesversune
autrepageJSPetqu
’
unebalise
<jsp:include...>
existedanslapageappelée.Ellefonctionnedelamêmefaçonquela
méthodeRequestDispatcher.include(request,response)
.
La classe DispatchAction permet de déclarer plusieurs méthodes execute() dans une même classe d
’
action. Ces
méthodessontensuitedéclenchéesdanslespagesJSPparl
’
intermédiairedel
’
attributactiondutag
<html:form>
avec
le nom de la méthode en paramètre. Si nous reprenons notre exemple avec la page
d
’
authentification /pages/authentificationForm.jsp, nous pouvons modifier l
’
action du formulaire avec un paramètre
supplémentaire.
<html:form action="/Authentification.do?methode=verifierAuthentification">
Nousremarquonsqu
’
avecl
’
utilisationd
’
unparamètrepourleDispatchAction,nousprécisonsalorsl
’
extension
duchemindéclenché.Ex :Authentification.doàlaplacedeAuthentification
.
Dans ce cas, le formulaire est soumis à l
’
action Authentification et utilise la méthode verifierAuthentification(). Les
méthodes doivent avoir un nom différent et la méthode execute()n
’
est pas implémentée dans ce cas. Toutes les
méthodesdoiventavoirlamêmesignaturequelaméthodeexecute()
.
Voiciunexempled
’
authentificationquipourraitêtrecomposéedeplusieursétapes.Cettetechniqueestidéalepour
gérerdesformulairesparétapesurplusieurspages.
ForwardAction
IncludeAction
DispatchAction
- 4 - © ENI Editions - All rigths reserved
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="methode">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
La déclaration d
’
uneaction detypeDispatchAction ne diffère pas d
’
une action courante. Nous pouvons remarquer
cependant l
’
utilisation du paramètre nommé parameter qui correspond au nom utilisé comme référence dans les
actionsouliens.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import actionform.AuthentificationForm;
@SuppressWarnings("serial")
public class AuthentificationAction extends DispatchAction {
public ActionForward verifierAuthentification(ActionMapping
mapping, ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm)
form;
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(authentificationform.getIdentifiant().equals("jlafoss") &&
authentificationform.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
}
La classe LookUpDispatchAction est semblable à la classe DispatchAction, elle implémente des méthodes qui sont
identiquesàlasignaturedelaméthodeexecute().LadifférenceentreDispatchActionetLookUpDispatchActionréside
danslamanièred
’
appelerlesméthodes.AvecLookUpDispatchAction
,iln
’
estpasnécessaired
’
ajouterdesparamètres
auxURLmaisl
’
appeld
’
uneméthodes
’
effectuegrâceauxboutonsdesoumissioninclusdansunepageJSP.
Eneffet,avecuneclassedetypeLookUpDispatchAction,lesboutons<html:submit>doiventavoirunattribut
property
supplémentaire.
<html:submit property="action">
<bean:message key="action.verifierauthentification"/>
</html:submit>
L
’
élément
<bean>
présentdansl
’
élément<html:submit>permetdedonnerunnomauboutondetypesubmitetà
l
’
action.L
’
attribut
property
dechaqueboutondetypesubmitfaitréférenceàunevaleurnomméeactionquidoitêtre
identiqueàl
’
attributparameterdel
’
actiondéclaréedanslefichierdeconfigurationdeStruts.
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
LookUpDispatchAction
- 5 -© ENI Editions - All rigths reserved
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="action">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
Laclassed
’
actiondoithériterdelaclasseLookUpDispatchActionetdoitimplémenterlaméthode
getKeyMethodMap()
qui
renvoieunecollectionaveclesdifférentesactionsàréaliser.Dupointdevueduprogrammeur,leLookUpDispatchAction
neprésentepasunvéritableintérêtparrapportàl
’
utilisationduDispatchActionquiestbeaucoupplussouple.
LaclasseMappingDispatchActionestsansdoutelaplusutiliséeetlaplussouplepourleprogrammeur.Ladéclaration
des méthodes associées a la même signature que la méthode execute() et la déclaration dans le fichier struts
config.xml
permetdedéfinirplusieursactionsavecl
’
attributparameter.Lavaleurdeparametercorrespondalorsàla
méthoded
’
actionàutiliser.
NousallonsmontrerceprincipeenreprenantnotrepageJSPd
’
origine/pages/authentificationForm.jspetenréalisant
deuxactionsdistinctes.Lapremièreactionpermetderéaliseruneauthentification,lasecondepermetuneredirection
surlapagedebienvenue.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Authentification</title><html:base/></head>
<body>
<html:errors/>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
</html:form>
<html:form action="/PasAuthentification">
<html:submit value="Sans authentification"/>
</html:form>
</body>
</html:html>
Lefichierdemappingesttrèsintéressantdanscecas.C
’
estlamêmeclassequigèrecesdeuxactions.L
’
attribut
parameterpermetdedéclencherlebontraitementenfonctionduchoixdel
’
utilisateur.
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="verifierAuthentification">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
</action>
<action
path="/PasAuthentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="pasAuthentification">
<forward name="succes" path="/pages/succes.jsp"/>
</action>
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
MappingDispatchAction
- 6 - © ENI Editions - All rigths reserved
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.MappingDispatchAction;
import actionform.AuthentificationForm;
@SuppressWarnings("serial")
public class AuthentificationAction extends MappingDispatchAction {
public ActionForward verifierAuthentification(ActionMapping
mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm)
form;
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(authentificationform.getIdentifiant().equals("jlafoss") &&
authentificationform.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
public ActionForward pasAuthentification(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
}
Nousremarquonsqu
’
unclicsurleboutonEnvoyernécessiteuneauthentificationcorrecteavecledéclenchementdela
méthodeverifierAuthentification()alorsqu
’
unclicsurlebouton
Sansauthentification
permetdedéclencherlafonction
pasAuthentification()delamêmeclasse.
La classe d
’
action hérite de la classe MappingDispatchAction et implémente, comme la classe DispatchAction, des
méthodesrespectantlasignaturedelaméthodeexecute().Lorsdesdéveloppementsilfaututiliserdanslaplupart
descaslaclasseMappingDispatchActionavecunparamètrepourletraitementdel
’
actionàdéclencher.Ilyaurapar
exemple une fonction pour la liste des articles, une pour la consultation, une pour la modification, une pour la
validation de la modification, une pour la création, une pour la validation de la création et une dernière pour sa
suppression.Latotalitéducodeseraalorsregroupéeparthèmedanslamêmeclassed
’
action.
Ensuite,laclassesimpleActionserautiliséepourledéveloppementdeservicesspécifiquescommeparexemple,une
actiondetéléchargement,uneactiondecopiedefichier,uneactiondechargementouuploaddedonnées.
LaclasseActionDispatcherestidentiqueàlaclasseMappingDispatchActionmaishéritedelaclasseActionetpossède
unepropriétédetypeActionDispatcher.Cetteclasseestpeuutiliséecarpluslourdeàmettreenplacequelaclasse
MappingDispatchActionetnécessiteenplusladéclarationdelaméthodeexecute()pourlagestiondudispatcher.
LaclasseSwitchActionpermetd
’
utiliseruneactionprésentedansunautrefichierdeconfigurationstruts
config.xml
que
celuidumoduleprincipal.Eneffet,nouspouvonstrèsbiencréerunsecondmodule,enutilisantunautrefichierstruts
config.xml
quiseraappeléparexemplestruts
configadmin.xmlpourl
’
interfaced
’
administration.
Ladéclarationd
’
unnouveaufichierdeconfigurationesttrèssimple,elleestréaliséedanslefichierdeconfigurationde
l
’
applicationweb.xml
.
<servlet>
<servlet -name>action</servlet -name>
<servlet -class>org.apache.struts.action.ActionServlet
</servlet -class>
<init -param>
<param -name>config</param -name>
<param-value>/WEB -INF/struts -config.xml,/WEB -INF/struts -
ActionDispatcher
SwitchAction
- 7 -© ENI Editions - All rigths reserved
configadmin.xml</param -value>
</init -param>
<load -on-startup>1</load -on-startup>
</servlet>
<servlet -mapping>
<servlet -name>action</servlet -name>
<url -pattern>*.do</url -pattern>
</servlet -mapping>
Cette déclaration fonctionnelle autorise le développement d
’
actions et formulaires dans plusieurs fichiers de
configurationmaisnepermetpasdedistinguerlesformulairesdemanièreunique.Pourcela,nousdevonsdonnerun
nomauparamètredeconfigurationcommeavecladéclarationsuivante :
<servlet>
<servlet -name>action</servlet -name>
<servlet -class>org.apache.struts.action.ActionServlet</servlet -class>
<init -param>
<param -name>config</param -name>
<param -value>/WEB -INF/struts -config.xml</param -value>
</init -param>
<init -param>
<param -name>config/admin</param -name>
<param -value>/WEB -INF/struts -configadmin.xml</param -value>
</init -param>
<load -on-startup>2</load -on-startup>
</servlet>
Onutiliseralefichierstruts
config.xml
pourgérerlesdéclarationsdesformulaires,actionsetredirectionspourlapartie
enaccèspublicdusite(
frontoffice
)et le fichierstruts
configadmin.xmlpourgérerlapartieprivée(
backoffice
).Il faut
cependantprévoirlecasoùcertainsutilisateurs,avecdesdroitsd
’
accèsplusimportants,voudraientaccéderàdes
servicesdumoduled
’
administration.C
’
estlerôledelaclasseSwitchAction
.
Pourutiliserceservice,ilfautdéclareruneactiondetypeSwitchActiondanslemoduleprincipalstruts
config.xml
.
<action path="/switch" type="org.apache.struts.actions.SwitchAction"/>
Unefoiscettedéclarationréalisée,ilestnécessairedecréerlesactionsdumoduleprincipal.Nousallonsreprendrele
formulaired
’
authentificationauthentificationForm.jspetencasdesuccès,uneactiondumoduleadministrateursera
appelée.
Ladéclarationdanslefichierdeconfigurationprincipalestruts
config.xml
estlasuivante :
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="verifierAuthentification">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/switch.do?page=/
succesAdmin.do&prefix=/admin"/>
</action>
Nousremarquonsqu
’
encasdesuccèssuiteàuneauthentificationcorrecte,c
’
estlemoduleswitch.doquivaréaliserle
routageversl
’
action/succesAdmin.doprésentedansl
’
autremodule.Leparamètre
prefix
indiquelenomdumodule
déclarédanslefichierweb.xmlavecdansnotreexemple,letermeadminpourladéclaration :
<param-name>config/admin</param-name>
<param-value>/WEB-INF/struts-configadmin.xml</param-value>
Ilestimportantdebienutiliserlesymbole&;danslefichierdeconfigurationauformatXML.Lecaractère
&estréservélorsdel
’
utilisationdulangageXML.
Enfin,ilnousresteàdéclarerl
’
actionetlaclasseassociéedanslesecondmodulestruts
configadmin.xml
.
- 8 - © ENI Editions - All rigths reserved
<action-mappings>
<action
path="/succesAdmin"
type="action.SuccesAdminAction">
</action>
</action -mappings>
Pourterminer,laclasseaction.SuccesAdminActionpermetuniquementd
’
afficherunmessagedanslaconsoleEclipse
afindevaliderlefonctionnementdel
’
ensemble.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
@SuppressWarnings("serial")
public class SuccesAdminAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{
System.out.println("Dans l ’action du second module Struts");
return null;
}
}
5.LesméthodesHelper
LaclasseActionpossèdeunensembledeméthodesappelées
Helper
quipermettentdefournirdesinformationssur
l
’
actionoudetestercertainesopérationsdéclenchéesparl
’
utilisateurcommeunclicsurunboutonannuler,l
’
ajout
d
’
unmessaged
’
erreur,lamodificationdelalocalepourlalangue...
●
addErrors()
: cette méthode permet d
’
ajouter une collection de messages d
’
erreurs à partir de clés. Ces
messagessontensuiteaffichésparlabalise<html:errors/>
.
●addMessages() :cetteméthodepermetd
’
ajouterunecollectiondemessages(desuccèsengénéral)àpartirde
clés.Cesmessagessontensuiteaffichésparlabalise<html:messages/>
.
- 9 -© ENI Editions - All rigths reserved
●generateToken() :cetteméthodepermetdegénérerunjetondetransactionpourforcerlesrequêtesHTTP.
●
isTokenValid()
: cetteméthoderenvoietruesiunjetondetransactionexistedanslasessiondel
’
utilisateuret
quelavaleursoumiseaveclarequêteestidentique.
●
isCancelled()
:cetteméthoderenvoietruesileboutonCancelduformulaireestcliqué.
●
getLocale()
:cetteméthoderenvoieunobjetdetype
Locale
quireprésentelalangueutiliséeparl
’
utilisateur.
●setLocale() :cetteméthodedéfinitlalocalisationdel
’
utilisateur.Ellepermetdepouvoirchangerdelangue.
●saveToken() :cetteméthodeenregistreunnouveaujetondetransactiondanslasessiondel
’
utilisateur.
●
getDataSource()
:cetteméthoderenvoieunpooldeconnexiondel
’
application.
Nousallonsmettreenapplicationunedecesméthodes
Helper
aveclafonction
isCancelled()
.Cetteméthodepermetde
vérifiersiunboutondetype
Cancel
aétécliquédansunformulairepourl
’
initialiserparexemple.Sileboutonaété
cliqué,laméthode
isCancelled()
renvoietruesinonfalse
.
Pourmettreenapplicationcetexemple,nousallonsreprendreleformulaire/pages/authentificationForm.jspetinitialiser
leschampsduformulaireavecunutilisateurpublicquiapourcoordonnées,l
’
identifiant
public
etlemotdepasse
public
avecladéclarationsuivantedel
’
action :
<action
path="/Authentification"
type="action.AuthentificationAction"
name="authentificationForm"
scope="request"
input="/pages/authentificationForm.jsp"
validate="false"
parameter="verifierAuthentification">
<forward name="erreurs" path="/pages/erreurs.jsp"/>
<forward name="succes" path="/pages/succes.jsp"/>
<forward name="initialisation" path="/pages/authentificationForm.jsp"/>
</action>
Nous avons déclaré une nouvelle redirection possible nommée initialisation qui permet de retourner à la page
d
’
authentification.MaintenantdanslapageJSPnousallonsajouterunboutondetype
Cancel
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html:html>
<head><title>Authentification</title><html:base/></head>
<body>
<html:errors/>
<html:form action="/Authentification">
Identifiant : <html:text property="identifiant"/><br/>
Mot de passe <html:text property="motdepasse"/><br/>
<html:submit value="Envoyer"/>
<html:cancel></html:cancel>
</html:form>
</body>
</html:html>
Enfin, il nous reste à traiter cette méthode
Helper
dans l
’
action ActionAuthentificationAction de l
’
application. Nous
vérifionssil
’
utilisateuracliquésurleboutonCanceletdanscecas,nousinséronslesvaleurspardéfautpourune
visiteenmodepublic.
package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.MappingDispatchAction;
import actionform.AuthentificationForm;
- 10 - © ENI Editions - All rigths reserved
@SuppressWarnings("serial")
public class AuthentificationAction extends MappingDispatchAction {
public ActionForward verifierAuthentification(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm)
form;
//clique sur le bouton Cancel
if(isCancelled(request))
{
//vider le formulaire par sécurité
authentificationform.reset(mapping, request);
//renvoyer le formulaire dans la requete avec
les paramètres par défaut
authentificationform.setIdentifiant("public");
authentificationform.setMotdepasse("public");
//renvoyer le formulaire dans la requete
request.setAttribute("authentificationForm",authentificationform);
return mapping.findForward("initialisation");
}
//vérifier les saisies (identifiant=jlafoss et motdepasse=jerome)
if(authentificationform.getIdentifiant().equals("jlafoss") &&
authentificationform.getMotdepasse().equals("jerome"))
{
//redirection vers la page de succes
return mapping.findForward("succes");
}
//dans tous les autres cas, retour vers la page d ’erreurs
return mapping.findForward("erreurs");
}
}
Pourrésumer,voiciunschémaexplicatifdesdifférentesactionsenvisageablesdansuneapplicationdéveloppéeavec
leframeworkStruts.
Ilestégalementimportantdepréciserqu
’
ilexisteplusieursméthodestrèsprécieuseslorsdel
’
utilisationduframework
Struts.LapremièreestuneméthodestatiquedelaclasseBeanUtilsquipermetdecopierlatotalitéd
’
unJavaBeande
formulairereçu(detypeActionFormouDynaForms)dansunobjetJava.Nouspourronsainsiparlasuitedirectement
manipulernotreobjetpourlesérialiser,lerendrepersistantdansunebasededonnées...
Sinousmodifionsnotreméthodeprécédentepourtestercettefonction :
public ActionForward verifierAuthentification(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm) form;
//instancier un objet utilisateur
Utilisateur utilisateur=new Utilisateur();
//copier en une seule ligne l ’ensemble des saisies
du formulaire dans notre objet
BeanUtils.copyProperties(utilisateur,authentificationform);
- 11 -© ENI Editions - All rigths reserved
//Afficher les valeurs saisies
System.out.println("Identifiant : "+utilisateur.getIdentifiant());
System.out.println("Mot de passe : "+utilisateur.getMotdepasse());
return null;
}
La seconde méthode intéressante est request.setAttribute() qui permet de renvoyer l
’
objet formulaire directement
danslarequête.Ilseraainsipossibledemodifierlesvaleurssaisiesetrenvoyerl
’
ensemblesansperted
’
informations.
public ActionForward verifierAuthentification(ActionMapping mapping,
ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm) form;
//modification de la saisie, on vide tout
authentificationform.setIdentifiant("");
authentificationform.setMotdepasse("");
//retour sur la page de saisie
return mapping.findForward("initialisation");
}
Enfin,laméthoderequest.setAttribute()permetégalementderenvoyertouslesparamètresreçusenuneseuleligne :
public ActionForward verifierAuthentification(ActionMapping
mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{
//lire le JavaBean de la vue
AuthentificationForm authentificationform=(AuthentificationForm) form;
//renvoyer tous les paramètres reçus dans la requête
request.setAttribute(mapping.getAttribute(), authentificationform);
//retour sur la page de saisie
return mapping.findForward("initialisation");
}
- 12 - © ENI Editions - All rigths reserved
Développementdumoduled
’
administration
1.Présentation
AprèsavoirprésentéleframeworkJavaEEStruts,nousallonsdévelopperlapartieadministrationetgestionduchat
BetaBoutique.Cetteapplicationreprésentelapremièrebriquedenotreprécédentschémaexplicatif.
Cettenouvelleapplicationnomméechatbetaboutiqueseradéveloppéeenrespectantlachartegraphiquedusitemais
sansintroduirelapartieserviceduchataveclesactionsliéesàlacommunicationetauxdialogues.
Ceprojetpermetdegérerleserveur,lessalons,utilisateursetmessages.
2.Miseenplace
PourledéveloppementdecettepartieduprojetnousallonsutiliserlestechnologiesStrutslesplusadaptéesàla
réalisationrapideetefficaced
’
uneapplicationJavaEE.Bienentendu,ilexisteplusieursmanièresdedévelopperune
applicationsuivantlesalgorithmes,solutionstechniques,choixetcompétencesdechacun.Lasolutionproposéen
’
est
pasLAsolutionparfaitemaisunesolutionenvisageable,d
’
uneapplicationdegestiond
’
unchat.
Nousallonscommencerlamiseenplacedecetteapplicationencréantunnouveauprojetnomméchatbetaboutique
avec Eclipse à partir du projet betaboutique. Pour cela, le plus simple est de réaliser une copie du répertoire
betaboutiqueprésentdansnosprojetsEclipseetdelenommerchatbetaboutique.Ensuite,avecEclipse,nousréalisons
unenouvellecréationdeprojetàpartirdessources.
Leprojetestdésormaisopérationnel,nousallonssupprimerplusieursfichiersquinesontpasnécessairespourle
développement du service de gestion du chat. La page
cgv.jsp
, le répertoire /admin/vues/articles, et tous les
paquetagesduprojet(betaboutique.servlets.clients
...).
L
’
applicationseradirectementaccessibleparuneURLprotégéeetserainstalléeàlaracinedel
’
application.Leprojet
proposépossèdedonclastructuresuivanteaccessibleàcetteadresse :http://localhost:8080/chatbetaboutique/
Les répertoires /css
,
/img
et /javascript sont identiques à ceux de l
’
application betaboutique. Le fichier /WEB
INF/web.xmlesttrèssimpleetpossèdelastructuresuivante :
<?xml version="1.0" encoding="UTF -8"?>
<!DOCTYPE web -app SYSTEM "http://java.sun.com/dtd/web -app_2_3.dtd">
<web-app>
<!-- fichier de point de départ de l ’application -->
<!--
il interdit en plus l ’exploration de l ’arborescence -->
<!--
attention, chemin toujours relatif -->
<welcome-file-list>
<welcome -file>index.jsp</welcome -file>
</welcome -file-list>
</web-app>
Enfinlesfichiers
/index.jsp
,
/entete.jspf
,
/navigation.jspfet
/piedpage.jspf
sontprésentésci
dessous:
- 1 -© ENI Editions - All rigths reserved
<%@ include file="vues/outils/entete.jspf" %>
<%@ include file="vues/outils/piedpage.jspf" %>
<html>
<head>
<title>Administration - BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
<!-- feuilles de style -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
<link rel="stylesheet" type="text/css" href="css/styles_admin.css"
title="defaut" />
<!-- pour les bulles d ’aide -->
<link type="text/css" rel="stylesheet" media="all"
href="javascript/jtip/jtip.css"/>
<script type="text/javascript" src="javascript/jquery/jquery.js"></script>
<script type="text/javascript" src="javascript/jtip/jtip.js"></script>
</head>
<body>
<div id="global">
<div id="entete">
<table border="0" cellspacing="0" cellpadding="0"
width="100%" height="100%">
<tr>
<td align="left" valign="top"><a href="/"><img
src="img/bandeau_haut_gauche.jpg" border="0"/></a></td>
<td align="left" valign="top"><img
src="img/bandeau_haut_centre.jpg"/></td>
<td align="left" valign="top" background="img/
bandeau_haut_droite_admin.jpg" width="222"> </td>
</tr>
</table>
</div>
<div id="contenu">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td valign="top" width="200">
<! -- COLONNE GAUCHE -->
<%@ include file="navigation.jspf" %>
</td>
<! -- COLONNE CENTRE -->
<td valign="top" width="100%" style="padding -left:5px">
<table border="0" cellspacing="0" cellpadding="0"
name="menugauche" id="menugauche"
style="background: url( ’img/fondmenu.gif ’) repeat-x;" height="500">
<tr>
<td width="100%" valign="top"><br/><span style="margin -left:
30px;"><b>ADMINISTRATION</b></span>
<ul class="menucategorie">
<li><a href=""><img src="img/fleche.gif" border="0"
align="absmiddle"/> Gestion des clients</a></li>
<li><a href=""><img src="img/fleche.gif" border="0"
align="absmiddle"/> Gestion des articles</a></li>
<li><a href=""><img src="img/fleche.gif" border="0"
align="absmiddle"/> Gestion des commandes</a></li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div id="betaboutique">
BetaBoutique est une boutique de et par la société
BetaBoutique SARL au Capital 10 000Euros n° siret 111 222 333 444 555
- 2 - © ENI Editions - All rigths reserved
</div>
</div>
</body>
</html>
À cette étape du projet, le lancement de l
’
application permet d
’
afficher l
’
interface graphique du projet dans le
navigateurainsiquelemenudenavigationquiseramodifiéparlasuite.
3.InstallationdeStruts
L
’
applicationchatbetaboutiqueestmaintenantcorrectementdéployée.Nousallonsprocéderàl
’
installationdeStrutsen
copiantlesarchivesJava
.jar
etlesfichiersdeconfigurationassociés.Pourcela,nousallonscommencerparcopierles
archivessuivantesdanslerépertoire/WEB
INF/lib
del
’
application :
●antlr.jar
●commons
beanutils.jar
●commons
digester.jar
●commons
fileupload.jar
●commons
logging.jar
●commons
validator.jar
●jakarta
oro.jar
●struts.jar
Ensuite, nous allons inclure ces archives au classpath Java avec la commande suivante dans Eclipse Project
PropertiesJavaBuildPath
AddExternalJARs
etsélectiondetouteslesarchives.
L
’
installationestpresqueterminée,ilnousresteàinstallerparsimplecopier/collerlesfichiersdeconfigurationstruts
config.xml
,
validation.xml
et
validator
rules.xmldans lerépertoire/WEB
INFdel
’
applicationetàcréerun paquetage
nomméressourcesavecunfichierdepropriétésnommédansnotrecasressources.properties
.
Lagestiondecefichierestréaliséedanslefichierdeconfigurationstruts
config.xml
.
<!-- Message Resources Definitions -->
<message-resources parameter="ressources.ressources" />
Nous terminons l
’
installation du projet par la configuration de l
’
ActionServlet principale de l
’
application au sein du
fichier /WEB
INF/web.xml. Cette partie de code permet de gérer la totalité des actions avec Struts ainsi que la
déclarationdesfichiersdeconfiguration.
<?xml version="1.0" encoding="ISO -8859-1"?>
<web-app>
<display -name>Application chatbetaboutique</display -name>
<!-- Standard Action Servlet Configuration -->
<servlet>
- 3 -© ENI Editions - All rigths reserved
<servlet -name>action</servlet -name>
<servlet -class>org.apache.struts.action.ActionServlet</servlet -class>
<init -param>
<param -name>config</param -name>
<param -value>/WEB -INF/struts -config.xml</param -value>
</init -param>
<load -on-startup>2</load -on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet -mapping>
<servlet -name>action</servlet -name>
<url -pattern>*.do</url -pattern>
</servlet -mapping>
<!-- The Usual Welcome File List -->
<welcome -file-list>
<welcome -file>index.jsp</welcome -file>
</welcome -file-list>
</web-app>
Àcetteétapeduprojet,l
’
arborescencedel
’
applicationestlasuivante :
4.Installationdupooldeconnexionàlabasededonnées
Nousallonsterminerlapartieinstallationdel
’
applicationparlamiseenplacedelasourcedeconnexionàlabasede
données.Cetteétapeeststrictementidentiqueauprojetétudiédanslechapitreprécédentconsacréauxbasesde
données.Lefichierdeconfigurationdel
’
application/TOMCAT/conf/Catalina/localhost/chatbetaboutique.xmlestprésenté
- 4 - © ENI Editions - All rigths reserved
ci
après :
<Context path="/chatbetaboutique" reloadable="true"
docBase="E:\PROJETWEB\chatbetaboutique"
workDir="E:\PROJETWEB\chatbetaboutique\work">
<Resource name="jdbc_chatbetaboutiquemysql" auth="Container"
type="javax.sql.DataSource"
username="root" password="" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatbetaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"/>
</Context>
NouspassonsensuiteàladéfinitiondelaDataSourceauseindufichier/WEB
INF/web.xmldel
’
application.
<!-- datasource a la base de donnees -->
<resource -ref>
<description>DB Connection</description>
<res -ref-name>jdbc_chatbetaboutiquemysql</res -ref-name>
<res -type>javax.sql.DataSource</res -type>
<res -auth>Container</res -auth>
</resource -ref>
Pourcequiestdelacréationdelaconnexionaupool,nouspourrionsdéfiniruneclassechargéeaulancementde
l
’
applicationcommedanslechapitreprécédent.AvecleframeworkStruts,ilexistelapossibilitédecréerdesplug
ins
quiserontégalementchargésaudémarragedel
’
application.Ladéclarationd
’
unplug
inestréaliséeàlafindufichier
deconfigurationdel
’
application.
<!-- connexion a la source de données -->
<plug-in className="chatbetaboutique.PluginDataSource" />
Avec cette déclaration, nous précisons qu
’
un plug
in nommé PluginDataSource, présent dans le paquetage
chatbetaboutiqueserachargéaudémarragedel
’
application.
Parsouplesselorsdudéveloppement,nousallonscréerdeuxconstantesauseindufichierdeconfiguration/WEB
INF/web.xml
.
<?xml version="1.0" encoding="ISO -8859-1"?>
<web-app>
<display -name>Application chatbetaboutique</display -name>
<!-- paramètres globaux accessibles dans les JSP et Servlets -->
<!-- nom du connecteur jdbc -->
<context -param>
<param -name>connecteurjdbc</param -name>
<param -value>jdbc_chatbetaboutiquemysql</param -value>
</context -param>
<!-- url pour accéder à l ’application -->
<context-param>
<param -name>urlapplication</param -name>
<param -value>http://localhost:8080/chatbetaboutique</param -value>
</context -param>
...
</web-app>
Laclassechatbetaboutique.PluginDataSourcepossèdeunesyntaxedéjàabordéedanslechapitreconsacréauxbases
dedonnées.Ceplug
inpermet uniquement decharger le poolde connexion nommédatasourceaudémarrage de
l
’
applicationStruts.
package chatbetaboutique;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
public class PluginDataSource implements PlugIn
{
- 5 -© ENI Editions - All rigths reserved
//fonction appelée lors de la création du plugin
public void init(ActionServlet servlet,ModuleConfig
moduleConfig) throws ServletException
{
//récupérer les paramètres présents dans le
fichier de configuration web.xml
String nomprojet=(String)servlet.getServlet
Context().getInitParameter("urlapplication");
String connecteurjdbc=(String)servlet.getServlet
Context().getInitParameter("connecteurjdbc");
//initialiser le context
try
{
//initialiser le context
initCtx=new InitialContext();
if(initCtx==null)throw new Exception
("Il n’y a pas de contexte !");
else
{
System.out.println("Ok, contexte "+nomprojet+" charge !");
}
//se connecter au JNDI mysql
Context envCtx=(Context) initCtx.lookup("java:comp/env");
DataSource ds=(DataSource) envCtx.lookup(connecteurjdbc);
if(ds==null)throw new Exception ("Il n ’y a pas de DataSource !");
else
{
System.out.println("DataSource, "+nomprojet+" charge !");
}
//stocker la DataSource dans un attribut
du context de la servlet application
ServletContext servletContext=servlet.getServletContext();
servletContext.setAttribute("datasource",ds);
}
catch(Exception e)
{
throw new ServletException(e.getMessage());
}
finally
{
try
{
//fermer le context
if(initCtx!=null)initCtx.close();
System.out.println("initCtx correctement decharge !");
}
catch(Exception e)
{
System.out.println("Erreur lors de initCtx !");
}
}
}
//fonction appelée lors de la destruction du plugin
public void destroy(){}
//fin de la classe
}
Nouspouvonstesterlefonctionnementduplug
inendémarrantnotreapplicationdegestionduchat.
- 6 - © ENI Editions - All rigths reserved
5.Listedesutilisateurs
Nousallonscommencerlapremièrepartiedecetteapplicationdegestionduchatparleservicequipermetdelister
touslesutilisateursdel
’
application.Pourrappel,lesutilisateurssontdéfinisdanslatablechatbetaboutique.utilisateur
quipossèdelastructuresuivante :
PourlaréalisationdesservicesavecStrutsnousallonsprocéderméthodiquementparétape :
●DéfinitionduJavaBeanassociéàlaclasseàgérer.
●DéfinitionduformulairedeJavaBeandanslefichierdeconfigurationstruts
config.xml
.
●Définitionduroutageduserviceencoursdanslefichierdeconfigurationstruts
config.xml
.
●Définitiondesvalidationssinécessaire.
●Codagedelaclassedegestiondesactions.
●Codagedumodèlesinécessaire.
●CodagedelavueJSP.
Pour des raisons de confort de programmation et de maintenance future, nous allons développer la totalité de
l
’
applicationavecdesDynaFormsStrutsetlefichierdevalidationdesentrées
validation.xml
.
a.CréationduJavaBean
Nouscommençonsparcréerunnouveaupaquetagenomméboiteoutils.CepaquetageJavavacontenirparlasuite
lesclassesJavaBeanainsiquesibesoincertainesclassesstatiques.Auseindecepaquetage,unenouvelleclasse
JavaBeannomméeUtilisateurestcrééeencorrespondanceavecleschampsdudiagrammedeclassesUMLetdela
table.
package boiteoutils;
import java.io.Serializable;
public class Utilisateur implements Serializable
{
//java 5.0 pour avoir un identifiant unique de la sérialisation
private static final long serialVersionUID = 1L;
//variables de classe
private String id_utilisateur=null;
private String pseudonyme=null;
private String motdepasse=null;
- 7 -© ENI Editions - All rigths reserved
private String nom=null;
private String prenom=null;
private int autorisation=0;
private String image=null;
public Utilisateur() {
}
public int getAutorisation() {
return autorisation;
}
...
//fin de la classe
}
b.DéclarationduJavaBeandeformulaire
LeJavaBeanUtilisateurestcorrectementcodé,l
’
étapesuivanteconsisteàdéclarerceJavaBeandeformulaireausein
dufichierdeconfigurationdel
’
applicationStrutsstruts
config.xml
.
<form-beans>
<!-- ========== Formulaire de gestion des utilisateur ========== -->
<form -bean name="FormUtilisateur"
type="org.apache.struts.validator.DynaValidatorForm">
<form -property name="utilisateur" type="boiteoutils.Utilisateur" />
</form -bean>
</form-beans>
LeJavaBeandeformulaireestdéclarésousformed
’
unDynaFormsavecpournomFormUtilisateuretpourpropriété
unobjetutilisateurinstancedelaclasseboiteoutils.Utilisateur
.
c.Définitionduroutage
LeJavaBeanestcorrectementconfigurépourStruts,nouspouvonsdésormaispasseràladéclarationduroutagede
l
’
application.Cetteétapeestégalementréaliséedanslefichierdeconfigurationstruts
config.xml
parl
’
intermédiaire
delabalise
<action/>
.
<action-mappings>
<!-- ================= UTILISATEUR ===================== -->
<!-- ===action qui permet de lister les utilisateurs=== -->
<action
path="/listeutilisateur"
name="FormUtilisateur"
scope="request"
validate="false"
input="accueil"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="listeutilisateur"
>
<forward name="listeutilisateur"
path="/vues/utilisateur/listeutilisateur.jsp" redirect="false"/>
</action>
</action -mappings>
Cette déclaration d
’
action sera associée à une classe de type MappingDispatchAction. Le paramètrepath permet
d
’
indiquerquecetteactionseradéclenchéeavecunliennommélisteutilisateur.LeformulaireJavaBeanassociéà
cetteactionestFormUtilisateurdéclaréplushautparl
’
intermédiairedelabalise
<form
bean/>
.Leparamètrescope
indique que les paramètres auront une portée valable durant le temps de la requête utilisateur. L
’
attribut
validate=
’’
false
’’
indique qu
’
il n
’
y aura pas de validation des saisies du formulaire avec ce lien. Le paramètre
input=
’’
accueil
’’
seradéclenchéencasd
’
erreurdansl
’
actionoud
’
erreurdesaisie.Cetteactionnommée
accueil
est
uneactionglobaledéclaréecommececiauseindufichierstruts
config.xml
:
<!-- =========================================== Global Forward
Definitions -->
<global-forwards>
<forward name="accueil" path="/index.jsp" redirect="false"/>
</global -forwards>
- 8 - © ENI Editions - All rigths reserved
Cetteactionpourraainsiêtreréférencéedepuisn
’
importequelleactionStruts.Leparamètretypepermetd
’
indiquer
la classe d
’
action qui sera chargée de traiter et déclarer l
’
action associée au chemin /listeutilisateur. Enfin, le
paramètreparameterpermetaveclaclasseMappingDispatchActiondepréciserlaméthodedelaclassed
’
actionqui
seradéclenchée.Dansnotrecas,unliensur/listeutilisateurvadéclencherlaméthodelisteutilisateurdelaclasse
chatbetaboutique.GestionUtilisateurAction.Enfin,laredirectiondéclaréeaveclabalise
<forward/>
permetd
’
indiquerla
vuequiseraaffichéeenretourdutraitement.
d.Codagedelaclassedegestiondesactions
Ladéclarationdel
’
actionaétéeffectuéedansl
’
étapeprécédente.Nouspouvonsréaliserlecodagedelaclasse
d
’
actionnomméeGestionUtilisateurActionprésentedanslepaquetagechatbetaboutique
.
package chatbetaboutique;
import org.apache.struts.action.*;
import org.apache.struts.actions.MappingDispatchAction;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class GestionUtilisateurAction extends MappingDispatchAction{
//afficher la liste des utilisateurs
public ActionForward listeutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
System.out.println("Liste des utilisateurs");
return null;
}
//fin de la classe
}
Pourlemomentcetteactionpermetuniquementd
’
afficherunmessagedanslaconsoleJava.Nousallonsrecharger
le contexte de l
’
application et réaliser un lien vers cette action Struts pour tester dans un premier temps cette
première déclaration. Pour cela, nous devons réaliser le lien suivant et le déclencher dans la page
JSP/vues/outils/navigations.jspf
.
<table border="0" cellspacing="0" cellpadding="0"
name="menugauche" id="menugauche" style="background:
url(’img/fondmenu.gif ’) repeat-x;" height="500">
<tr>
<td width="100%" valign="top"><br/><span style="margin -left:30px;">
<b>ADMINISTRATION</b></span>
<ul class="menucategorie">
<li><a href="listeutilisateur.do"><img src="img/fleche.gif"
border="0" align="absmiddle"/> Gestion des utilisateurs</a></li>
</ul>
</td>
</tr>
</table>
Lefonctionnementdel
’
actionestopérationnel,nouspouvonspasseràsoncodagequidoitpermettredelireetde
retournerlalistedesutilisateursdel
’
applicationduchatdeBetaBoutique
.
Pourl
’
affichagedesinformations,nousneréaliseronspasdetri,rechercheetpaginationafindesimplifierle
codeetlesexplicationsassociées.
package chatbetaboutique;
import modele.UtilisateurModele;
import org.apache.struts.action.*;
import org.apache.struts.actions.MappingDispatchAction;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
- 9 -© ENI Editions - All rigths reserved
import javax.servlet.http.*;
import javax.sql.DataSource;
public class GestionUtilisateurAction extends MappingDispatchAction{
//variables de la classe
DataSource ds=null;
//afficher la liste des utilisateurs
public ActionForward listeutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute
("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//retourner la liste des utilisateur
ArrayList listeutilisateur=(ArrayList)
utilisateurmodele.getListeUtilisateur();
//retourner la liste des utilisateurs
request.setAttribute("listeutilisateur",listeutilisateur);
//vider par sécurité
listeutilisateur=null;
//retourner sur la page d ’affichage des utilisateurs
return mapping.findForward("listeutilisateur");
}
//fin de la classe
}
Lecodeesttrèssimpleetnousremarquonsalorstoutl
’
intérêtd
’
utiliserlemodèleMVCII.Nouscommençonspar
déclarer la connexion à la base de données (ds), ensuite nous instancions un objet qui représente le modèle
(
utilisateurmodele).NousdéclenchonslafonctiondumodèlequivaretournertouslesobjetsdetypeUtilisateursans
sesoucierdutraitement(getListeUtilisateur()),nouspostonscettecollectiondanslarequête(request.setAttribute()
)
etnousnousdirigeonsverslavuequivaafficherlesdonnées(
mapping.findForward()
).
e.Codagedumodèle
Letraitementdel
’
actionestdésormaisréalisé.Nouspouvonspasseraucodagedumodèle.Pourcela,nousallons
créerunnouveaupaquetagenommé
modele
etlaclasseUtilisateurModele.Cetteclassepossèdepourlemomentune
seuleméthodequipermetdelirelalistedesutilisateursdelabasededonnéesetdelesinsérerdansunecollection
detypeArrayList
.
package modele;
import boiteoutils.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.apache.commons.dbutils.BeanProcessor;
import java.util.ArrayList;
public class UtilisateurModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//liste des objets
ArrayList<Utilisateur> listeutilisateur=new ArrayList<Utilisateur>();
- 10 - © ENI Editions - All rigths reserved
/***********************************************************
* constructeur
***********************************************************/
public UtilisateurModele(DataSource ds)
{
//récupérer le DataSource de la servlet
this.ds=ds;
}
/***********************************************************
* liste des utilisateurs
***********************************************************/
public ArrayList getListeUtilisateur()
{
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM
utilisateur ORDER BY pseudonyme,id_utilisateur");
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//utilisation de la librairie DbUtils qui permet
de transformer un ResultSet en Objet Java
BeanProcessor bp=new BeanProcessor();
listeutilisateur =
(ArrayList<Utilisateur>)bp.toBeanList(rs,Utilisateur.class);
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe UtilisateurModele.java
fonction getListeUtilisateurModele");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getListeUtilisateurModele");
}
}
//retourner la liste des utilisateurs
return listeutilisateur;
}
//fin de la classe
}
La fonction getListeUtilisateur() utilise la bibliothèque DbUtils afin de transformer le ResultSet réponse en une
collectiond
’
objetsdelaclasseUtilisateur.Cettecollectionestensuiteretournéeàl
’
actionquivaelle
mêmeréaliserle
routageverslapageJSPd
’
affichagedesdonnées.
Nousutilisonségalementlaclassestatiqueboiteoutils.OutilsBaseDeDonneespourlagestionduSGBD.
f.CodagedelavueJSP
- 11 -© ENI Editions - All rigths reserved
LadernièreétapeconsisteàcoderlavueJSP.Pourcela,nousallonsutiliserlesbibliothèquesdetagslivréesen
standardavecStruts.L
’
inclusiondecesbibliothèquesestréaliséedanslefichier/vues/outils/entete.jspf
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html>
<head>
<title>Administration - BetaBoutique</title>
...
LavueJSP/vues/utilisateur/listeutilisateur.jspestcodéeenHTMLavecunepartiedecodeJavaquipermetd
’
afficher
lesdonnées.Nouspourrionstoutàfaitutiliserdesbalises100%HTMLenutilisantlestaglibsStruts.
<%@ page import="java.util.ArrayList" %>
<%@ page import="boiteoutils.Utilisateur" %>
<%
//liste des utilisateurs
ArrayList listeutilisateur=(ArrayList)request.getAttribute
("listeutilisateur");
%>
<!-- inclure l ’entete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="listeutilisateur.do"><img
src="img/utilisateur.jpg" border="0" align="absmiddle"/> Liste
des utilisateurs</a></div>
<div class="titreaction"><a href="creerutilisateur.do">Créer
un utilisateur</a></div>
<table border="0" id="tableaubordure" cellspacing="0" cellpadding="0">
<tr align="center" class="entetetableau">
<td>ID</td>
<td>Pseudonyme</td>
<td>Mot de passe</td>
<td>Nom</td>
<td>Prénom</td>
<td>Autorisation</td>
<td>Image</td>
<td colspan="3" width="130">Gestion</td>
</tr>
<%
for(int i=0;i<listeutilisateur.size();i++)
{
//récupérer l ’objet dans la liste
Utilisateur utilisateur=(Utilisateur)listeutilisateur.get(i);
if(i%2==0)out.println("<tr align=\"center\" class=\"ligneclaire\">");
else out.println("<tr align=\"center\" class=\"lignefoncee\">");
out.println("<td>"+utilisateur.getId_utilisateur()+"</td>");
out.println("<td>"+utilisateur.getPseudonyme()+"</td>");
out.println("<td>"+utilisateur.getMotdepasse()+"</td>");
out.println("<td>"+utilisateur.getNom()+"</td>");
out.println("<td>"+utilisateur.getPrenom()+"</td>");
if(utilisateur.getAutorisation()==1)
{
out.println("<td><img src=\"img/actif.gif\" alt=\"Actif\"
title=\"Actif\"/></td>");
}
else
{
out.println("<td><img src=\"img/inactif.gif\" alt=\"Inactif\"
title=\"Inactif\"/></td>");
}
out.println("<td>"+utilisateur.getImage()+"</td>");
out.println("<td><a href= ’consulterutilisateur.do?
id_utilisateur="+utilisateur.getId_utilisateur()+" ’><img
src=\"img/consulter.png\" border=\"0\" align=\"absmiddle\"
title=\"Consulter\"/></a></td>");
out.println("<td><a href= ’modifierutilisateur.do?
id_utilisateur="+utilisateur.getId_utilisateur()+" ’><img
- 12 - © ENI Editions - All rigths reserved
src=\"img/modifier.png\" border=\"0\" align=\"absmiddle\"
title=\"Modifier\"/></a></td>");
out.println("<td><a href= ’javascript:confirmerSuppressionUtilisateur
("+utilisateur.getId_utilisateur()+",\""+utilisateur.getPseudonyme()+"\"); ’>
<img src=\"img/supprimer.png\" border=\"0\" align=\"absmiddle\"
title=\"Supprimer\"/></a></td>");
out.println("</tr>");
}
%>
</table>
<%@ include file="../outils/piedpage.jspf" %>
NotrepremierservicedéveloppéentièrementenStrutsestterminé.Chaquepartieestcorrectementdécoupéeen
respectantlepatternMVCII.Nousallonsdanslasuitedecechapitreprésenterlatechniquepourconsulterlafiche
détaillée d
’
un utilisateur, le formulaire de création et de modification d
’
un utilisateur avec les validations XML et
l
’
actiondesuppression.Cestechniquesserontalorsutiliséesàl
’
identiquepourréaliserlapartiegestiondessalons
duchat.
6.Consultationdelaficheutilisateur
L
’
action de consultation est la plus simple à développer. Le JavaBean Utilisateur est déjà est place ainsi que sa
définition.Nouspouvonspasserdirectementàl
’
étapededéclarationdel
’
actiondanslefichierstruts
config.xml
.
<!-- ===action qui permet de consulter la fiche d ’un utilisateur=== -->
<action
path="/consulterutilisateur"
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="consulterutilisateur"
>
<forward name="consulterutilisateur" path="/vues/utilisateur/
consulterutilisateur.jsp" redirect="false"/>
</action>
Ladéclarationestpratiquementidentiqueàcelledelalistedesutilisateurs.Nousretrouvonsleparamètrepathqui
permet de définir le nom de l
’
actionqui va êtredéclenché : /consulterutilisateur.do.Le JavaBean deformulaire est
toujourslemême,iln
’
yapasdevalidationdesaisiesuruneconsultationetc
’
estlaméthodeconsulterutilisateur()de
laclassechatbetaboutique.GestionUtilisateurActionquiseradéclenchée.Enfin,uneseuledéfinitionderedirectionest
définie,ellecorrespondàlavueJSPpourl
’
affichage.
Lecodede l
’
actionde consultationest assezsimple. Leparamètre passédans lelien estrécupéré ettesté. Cet
attributcorrespondaunumérodel
’
utilisateuràconsulter.Ensuite,l
’
actiondéclenchelemodèleafinderécupérerla
totalitédel
’
objetassociéàcetidentifiant.LecodeestajoutéàlasuitedelaclasseGestionUtilisateurAction
.
//consulter un utilisateur
public ActionForward consulterutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
- 13 -© ENI Editions - All rigths reserved
//fermer la datasource
this.ds=null;
//récupérer l ’id de l ’utilisateur envoyé
String id_utilisateur=(String)request.getParameter("id_utilisateur");
//récupérer l ’objet dans la base de données
if(id_utilisateur!=null && !id_utilisateur.equals(""))
{
Utilisateur utilisateur=(Utilisateur)utilisateurmodele.getUtilisateur
(id_utilisateur);
request.setAttribute("utilisateur",utilisateur);
//redirection vers la page de consultation
return mapping.findForward("consulterutilisateur");
}
//en cas d ’erreur retourner sur la page d ’accueil
return mapping.findForward("accueil");
}
LaclassemodèleUtilisateurModelepossèdedésormaisunenouvelleméthodequipermetderécupéreruneimagede
l
’
objetutilisateursauvegardéenbase.
/***********************************************************
* récupérer l ’utilisateur indiqué
***********************************************************/
public Utilisateur getUtilisateur(String id_utilisateur)
{
//créer un objet utilisateur
Utilisateur utilisateur=new Utilisateur();
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM
utilisateur WHERE id_utilisateur=?");
requete.setString(1,(String)id_utilisateur);
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
if(rs.next())
{
//utilisation de la librairie DbUtils qui permet de
transformer un ResultSet en Objet Java
BeanProcessor bp=new BeanProcessor();
utilisateur = (Utilisateur)bp.toBean(rs, Utilisateur.class);
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getUtilisateur");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
- 14 - © ENI Editions - All rigths reserved
UtilisateurModele.java fonction getUtilisateur");
}
}
//retourner l ’objet utilisateur
return utilisateur;
}
L
’
action de consultation est quasiment terminée, il ne reste plus que le code de la
vue/vues/utilisateur/consulterutilisateur.jsp
.
<%@ page import="boiteoutils.Utilisateur" %>
<%
//objet utilisateur
Utilisateur utilisateur=(Utilisateur)request.getAttribute("utilisateur");
%>
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="listeutilisateur.do"><img
src="img/utilisateur.jpg" border="0" align="absmiddle"/> Liste
des utilisateurs</a></div>
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
<tr><td>Id</td><td><%= utilisateur.getId_utilisateur() %></td></tr>
<tr><td>Pseudonyme</td><td><%= utilisateur.getPseudonyme() %></td></tr>
<tr><td>Mot de passe</td><td><%= utilisateur.getMotdepasse() %></td></tr>
<tr><td>Nom</td><td><%= utilisateur.getNom() %></td></tr>
<tr><td>Prénom</td><td><%= utilisateur.getPrenom() %></td></tr>
<tr><td>Autorisation</td><td><%= utilisateur.getAutorisation() %></td></tr>
<tr><td>Image</td><td><%= utilisateur.getImage() %></td></tr>
<tr><td align="center" colspan="2"><html:submit value="Modifier"
styleClass="bouton"/></td></tr>
</table>
<!-- inclure le pied de page -->
<%@ include file="../outils/piedpage.jspf" %>
Leservicedeconsultationesttotalementopérationnel.NousallonscependantajouterunformulairedanslapageJSP
deconsultation afinde réaliser un lien versle formulairede modificationpar l
’
intermédiaire du bouton
Modifier
.La
baliseStruts
<html:form/>
permetd
’
ajouterunformulaireàlapage.Cependantl
’
ajoutdecettebaliseprovoqueune
erreur.Eneffet,l
’
action/modifierutilisateurn
’
estpasencoredéclaréedanslefichierdeconfigurationstruts
config.xml
.
<%@ page import="boiteoutils.Utilisateur" %>
<%
//objet utilisateur
- 15 -© ENI Editions - All rigths reserved
Utilisateur utilisateur=(Utilisateur)request.getAttribute("utilisateur");
%>
<!-- inclure l ’entete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="listeutilisateur.do"><img src="img/
utilisateur.jpg" border="0"
align="absmiddle"/> Liste des utilisateurs</a></div>
<html:form action="/modifierutilisateur" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
...
</table>
<html:hidden property="id_utilisateur" value="<%= utilisateur.
getId_utilisateur() %>"/>
</html:form>
<!-- inclure le pied de page -->
<%@ include file="../outils/piedpage.jspf" %>
7.Créationd
’
unnouvelutilisateur
Leformulairedecréationestaffichéparl
’
intermédiaired
’
unepageJSPsimple.Lespartiescréationetmodificationsont
réaliséesendeuxétapes.Lapremièreétapeconsisteàafficherleformulaireavantvalidation.Cetteactiondéclarée
dans le fichier de configuration struts
config.xml
permet de réaliser une redirection simple vers le formulaire de
création.
<!-- action qui permet d ’afficher le formulaire de
création d ’un utilisateur -->
<action
path="/creerutilisateur"
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="org.apache.struts.actions.ForwardAction"
parameter="/vues/utilisateur/creerutilisateur.jsp">
</action>
Leformulairedecréation/vues/utilisateur/creerutilisateur.jspreprendlesdifférentschampsdelatableUtilisateur
.
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="listeutilisateur.do"><img
src="img/utilisateur.jpg" border="0" align="absmiddle"/> Liste
des utilisateurs</a></div>
<html:form action="/validercreerutilisateur" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
<tr><td>Pseudonyme</td><td><html:text property="utilisateur.pseudonyme"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Mot de passe</td><td><html:text property="utilisateur.motdepasse"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Nom</td><td><html:text property="utilisateur.nom"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Prénom</td><td><html:text property="utilisateur.prenom"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Autorisation</td><td><html:checkbox
property="utilisateur.autorisation" styleClass="input"
errorStyleClass="inputerreur"/></td></tr>
<tr><td>Image</td><td><html:text property="utilisateur.image"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td align="center" colspan="2"><html:submit value="Envoyer"
styleClass="bouton"/></td></tr>
</table>
</html:form>
<!-- inclure le pied de page -->
<jsp:include page="../outils/piedpage.jspf" />
- 16 - © ENI Editions - All rigths reserved
AvecStruts,touteslesactionsetJavaBeandeformulairesontliés.Danslapageci
dessus,nousutilisonsletaglib
<html:form/>
avec le paramètre action qui déclenche l
’
action/validercreerutilisateur. Cette action doit être déclarée
danslefichierstruts
config.xml
afinquelapageJSP/vues/utilisateur/creerutilisateur.jsppuisseêtrecompilée.
<!-- action qui va valider la création d ’un utilisateur -->
<action
path="/validercreerutilisateur"
name="FormUtilisateur"
scope="request"
validate="true"
input="/creerutilisateur.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="validercreerutilisateur">
<forward name="listeutilisateur" path="/listeutilisateur.do"
redirect="false"/>
</action>
Afin d
’
afficher les messages d
’
erreur et de succès, nous allons ajouter quelques lignes à la fin du
fichier/vues/outils/entete.jspf
.
...
<!-- COLONNE CENTRE -->
<td valign="top" width="100%" style="padding -left:5px">
<! -- AFFICHER LES MESSAGES DE SUCCES ET ERREUR -->
<div id="message_erreur"><html:errors/></div>
<div id="message_information">
<html:messages id="message" message="true">
<ul><li><bean:write name="message"/></li></ul>
</html:messages>
</div>
Leformulairedecréationestopérationnel,nouspouvonspasseràlavalidationdessaisies.Dansladéclarationde
l
’
action/validercreerutilisateurquiestdéclenchéesuiteàlavalidationduformulairedesaisie,nousavonsdéclaréle
paramètre
validate
àtrue.Lescontrôlesdesaisiesserontdoncréalisésencorrespondanceaveclesdéclarationsdu
fichier
validation.xml
.Encasd
’
erreur,c
’
estl
’
actiondéclaréeavecleparamètreinputquiseraappelée.
Dansnotrecas,leformulairedecréationestrappelésansperdrelessaisies.Nousdéclaronsendébutdefichier/WEB
INF/validation.xmldeuxconstantesglobalesquicorrespondentàlasyntaxerespectivementdumotdepasseetdu
pseudonyme utilisateur. Ensuite, nous déclarons une référence au formulaire utilisateur
<form
name=
’’
FormUtilisateur
’’
/>
en correspondance avec le nom dans le fichier struts
config.xml <form
bean
name=
’’
FormUtilisateur
’’
/>
.
Lesvalidationsdesaisiessonttrèspointues,lessyntaxessontcontrôléesparrapportà
desexpressionsrégulières,desprésencesetdestailles.
<!DOCTYPE form -validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules
Configuration 1.1.3//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<form-validation>
<global>
<constant>
<constant -name>motdepasse</constant -name>
<constant -value>^[0 -9a-zA-Z]*$</constant -value>
</constant>
<constant>
<constant -name>pseudonyme</constant -name>
<constant -value>^[0 -9a-zA-Z\-]{4,20}$</constant -value>
</constant>
<constant>
<constant -name>autorisation</constant -name>
<constant -value>^[0|1]{1}$</constant -value>
</constant>
</global>
<formset>
<! -- ========== Formulaire authentification utilisateur ========== -->
<form name="FormUtilisateur">
<field property="utilisateur.pseudonyme"
depends="required,minlength,maxlength,mask">
<arg0 key="pseudonymeutilisateur"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
- 17 -© ENI Editions - All rigths reserved
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var -name>minlength</var -name>
<var -value>4</var -value>
</var>
<var>
<var -name>maxlength</var -name>
<var -value>20</var -value>
</var>
<var>
<var -name>mask</var -name>
<var -value>${pseudonyme}</var -value>
</var>
</field>
...
</form>
</formset>
</form-validation>
Leschamps peusdonyme
,
motdepasse etautorisation sontanalysésdefaçon précisealorsqueles champs nomet
prenomdoiventuniquementêtrerenseignés.Labalise
<arg0/>
permetdefairelelienaveclesnomsdanslefichier
ressources.properties.Commeindiquéprécédemment,lesvalidationsetmessagesd
’
erreurssontassociésaufichierde
propriétésquiestsituépournotreprojetdanslepaquetageressourcesetquiapournomressources.properties.Voici
soncontenu :
# -- standard errors --
errors.header=<UL>
errors.prefix=<LI>
errors.suffix=</LI>
errors.footer=</UL>
# -- messages --
errors.required=Attention, le champ [{0}] doit être renseigné !
errors.minlength=Attention, le champ [{0}] doit avoir au moins
{1} caractères !
errors.maxlength=Attention, le champ [{0}] ne peut avoir plus de
{1} caractères !
errors.invalid=Attention, le champ [{0}] est incorrect!
errors.date=Attention, le champ [{0}] n ’est pas une date valide !
errors.byte=Attention, le champ [{0}] doit être un octet !
errors.date=Attention, le champ [{0}] doit être une date !
errors.double=Attention, le champ [{0}] doit être un double !
errors.float=Attention, le champ [{0}] doit être un réel !
errors.integer=Attention, le champ [{0}] doit être un entier !
errors.long=Attention, le champ [{0}] doit être un entier long !
errors.short=Attention, le champ [{0}] doit être un entier court !
errors.range=Attention, le champ [{0}] doit être dans l ’intervalle
{1} et {2} !
errors.creditcard=Attention, le champ [{0}] n ’est pas un numéro
de carte valide !
errors.email=Attention, le champ [{0}] n ’est pas une adresse
électronique valide !
# -- gestion des utilisateurs --
pseudonymeutilisateur=Pseudonyme
motdepasseutilisateur=Mot de passe
nomutilisateur=Nom
prenomutilisateur=Prénom
autorisationutilisateur=Autorisation
imageutilisateur=Image
Nouspouvonsvérifierlessaisiesenréalisantdifférentstestsprécissurleformulairedecréation.
- 18 - © ENI Editions - All rigths reserved
Le service de création d
’
un nouvel utilisateur est presque terminé. Il nous reste à développer la méthode
validercreerutilisateur()delaclasseGestionUtilisateurActionainsiquelaméthodeassociéeaumodèle.
//valider la création d ’un utilisateur
public ActionForward validercreerutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute
("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le bean formulaire
DynaActionForm FormUtilisateur=(DynaActionForm)form;
//créer un objet utilisateur directement à partir des saisies
Utilisateur utilisateur=(Utilisateur)FormUtilisateur.get
("utilisateur");
//rendre l ’objet persistant dans la base de données
int resinstruction=utilisateurmodele.creerUtilisateur(utilisateur);
//en cas d ’erreur avec la base de données
if (resinstruction!=1)
{
//ajouter un message d ’erreur
ActionMessages erreurs=new ActionErrors();
erreurs.add("message", new ActionMessage
("erreurs.creationutilisateur"));
saveErrors(request,erreurs);
}
//tout est OK, enregistrer l ’utilisateur est créé
else
{
//toutes les vérifications sont correctes
ActionMessages messages=new ActionMessages();
messages.add("message", new ActionMessage
("succes.creationutilisateur"));
- 19 -© ENI Editions - All rigths reserved
saveMessages(request,messages);
//retourner sur la page de listing des utilisateurs
return mapping.findForward("listeutilisateur");
}
//en cas d ’erreur retour sur la page d ’accueil
return mapping.findForward("accueil");
}
L
’
actiondevalidationdelacréationpermetd
’
instancierunobjetutilisateuràl
’
imagedessaisiesdel
’
utilisateurenune
seuleligne.Ensuite,cetobjetestrendupersistantparlemodèle.Unmessagedynamiquedesuccès(oud
’
erreur)est
alorsinsérédanslarequêteaprèslecturedesvaleursdanslefichierdepropriétés.
/***********************************************************
* créer un utilisateur
***********************************************************/
public int creerUtilisateur(Utilisateur utilisateur)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("INSERT INTO
utilisateur(pseudonyme,motdepasse,nom,prenom,autorisation,image) VALUES
(?,?,?,?,?,?)");
requete.setString(1,(String)utilisateur.getPseudonyme());
requete.setString(2,(String)utilisateur.getMotdepasse());
requete.setString(3,(String)utilisateur.getNom());
requete.setString(4,(String)utilisateur.getPrenom());
requete.setInt(5,(Integer)utilisateur.getAutorisation());
requete.setString(6,(String)utilisateur.getImage());
resinstruction=requete.executeUpdate();
}
...
}
8.Modificationdelaficheutilisateur
Leformulairedemodificationesttoujoursl
’
élémentlepluscomplexeàdévelopperenprogrammationWeb.Certaines
informations doivent être testées, des messages d
’
erreurs sont affichés en conséquence, des redirections sont
réaliséesenfonctiondestraitementsetsurtoutlessaisiesdoiventêtreconservéesencasd
’
erreur.
Commedanslecasdelacréation,lavalidationestréaliséeavecdeuxétapes.Lapremièreconsisteàlirelesdonnées
delaficheàmodifieretlasecondepermetlamodificationdecelle
ci.Nouscommençonsparlaréalisationduroutage
avecladéfinitiondesdeuxactionsnécessaires.
<!-- ===action qui permet de modifier un utilisateur
en récupérant ses valeurs=== -->
<action
path="/modifierutilisateur"
- 20 - © ENI Editions - All rigths reserved
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="modifierutilisateur"
>
<forward name="modifierutilisateur"
path="/vues/utilisateur/modifierutilisateur.jsp" redirect="false"/>
</action>
<!-- ===action qui permet de valider la modification d ’un utilisateur=== -->
<action
path="/validermodifierutilisateur"
name="FormUtilisateur"
scope="request"
validate="true"
input="/modifierutilisateur.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="validermodifierutilisateur"
>
<forward name="modifierutilisateur"
path="/modifierutilisateur.do" redirect="false"/>
<forward name="listeutilisateur"
path="/listeutilisateur.do" redirect="false"/>
</action>
L
’
action Struts utilise la fonction getUtilisateur() du modèle précédemment développé et le JavaBean de formulaire
FormUtilisateur
.
//afficher le formulaire en modification pour l ’utilisateur indiqué
public ActionForward modifierutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer l ’id de l ’utilisateur
String id_utilisateur=(String)request.getParameter("id_utilisateur");
//le bean formulaire
DynaActionForm FormUtilisateur=(DynaActionForm)form;
//si validation du formulaire mais qu ’il y a
une erreur, récupérer les informations
if(request.getParameter("utilisateur.id_utilisateur")!=null)
{
id_utilisateur=request.getParameter("utilisateur.id_utilisateur");
}
if(id_utilisateur!=null && !id_utilisateur.equals(""))
{
Utilisateur utilisateur=(Utilisateur)utilisateurmodele.getUtilisateur
(id_utilisateur);
//copier l ’intégralité de l ’objet dans le JavaBean de formulaire
FormUtilisateur.set("utilisateur", utilisateur);
//redirection vers la page de modification
return mapping.findForward("modifierutilisateur");
}
//en cas d ’erreur retourner sur la page d ’accueil
return mapping.findForward("accueil");
}
Enfin,lavue JSP/vues/utilisateur/modifierutilisateur.jsp permet d
’
afficherlesdonnéesduJavaBeandeformulaireen
correspondanceavecl
’
attribut
id
del
’
utilisateur(
<html:hidden/>
).
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
- 21 -© ENI Editions - All rigths reserved
<br/><div class="titre"><a href="listeutilisateur.do"><img
src="img/utilisateur.jpg" border="0" align="absmiddle"/> Liste
des utilisateurs</a></div>
<html:form action="/validermodifierutilisateur" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
<tr><td>Pseudonyme</td><td><html:text property="utilisateur.pseudonyme"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Mot de passe</td><td><html:text property="utilisateur.motdepasse"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Nom</td><td><html:text property="utilisateur.nom"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Prénom</td><td><html:text property="utilisateur.prenom"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Autorisation</td><td><html:text property="utilisateur.autorisation"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Image</td><td><html:text property="utilisateur.image"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td align="center" colspan="2"><html:submit value="Envoyer"
styleClass="bouton"/></td></tr>
</table>
<html:hidden property="utilisateur.id_utilisateur"/>
</html:form>
<!-- inclure le pied de page -->
<jsp:include page="../outils/piedpage.jspf" />
Il ne reste plus qu
’
à développer l
’
action validermodifierutilisateur()de la classe GestionUtilisateurAction ainsi que la
fonctiondumodèle,afinderéaliserlamodificationenbase.
//valider la modification de l ’utilisateur
public ActionForward validermodifierutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le bean formulaire
DynaActionForm FormUtilisateur=(DynaActionForm)form;
//créer un objet utilisateur directement à partir des saisies
Utilisateur utilisateur=(Utilisateur)FormUtilisateur.get("utilisateur");
//modifier l ’utilisateur
int resinstruction=utilisateurmodele.modifierUtilisateur(utilisateur);
//en cas d ’erreur avec la base de données
if (resinstruction!=1)
{
//ajouter un message d ’erreur
ActionMessages erreurs=new ActionErrors();
erreurs.add("message", new ActionMessage
("erreurs.modificationutilisateur"));
saveErrors(request,erreurs);
}
//tout est OK
else
{
//toutes les vérifications syntaxiques sont correctes si arrivée ici
ActionMessages messages=new ActionMessages();
messages.add("message", new ActionMessage
("succes.modificationutilisateur"));
saveMessages(request,messages);
//retourner sur la page de listing des utilisateurs
return mapping.findForward("listeutilisateur");
- 22 - © ENI Editions - All rigths reserved
}
//en cas d ’erreur, retourner sur la page d ’accueil
return mapping.findForward("accueil");
}
/***********************************************************
* modifier l ’utilisateur
***********************************************************/
public int modifierUtilisateur(Utilisateur utilisateur)
{
int resinstruction=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("UPDATE utilisateur SET
pseudonyme=?,motdepasse=?,nom=?,prenom=?,autorisation=?,image=? WHERE
id_utilisateur=?");
requete.setString(1,(String)utilisateur.getPseudonyme());
requete.setString(2,(String)utilisateur.getMotdepasse());
requete.setString(3,(String)utilisateur.getNom());
requete.setString(4,(String)utilisateur.getPrenom());
requete.setInt(5,(Integer)utilisateur.getAutorisation());
requete.setString(6,(String)utilisateur.getImage());
requete.setString(7,(String)utilisateur.getId_utilisateur());
resinstruction=requete.executeUpdate();
}
...
}
Nousterminonsensuiteparladéfinitiondesdeuxnouveauxmessagesdanslefichierdepropriétés.
erreurs.modificationutilisateur=Erreur lors de la modification
de l’utilisateur
succes.modificationutilisateur=L ’utilisateur a été modifié avec succès
9.ActivationdesvérificationsJavaScript
NouspouvonsactiverlesvalidationsJavaScriptpourleformulairedecréationetdemodification.Pourcela,ilsuffitde
modifierlavueJSPavecdeuxlignesquipermettentdegénérerlecodeenrapportaveclesrèglesdevalidationdu
fichier
validation.xml
. Si nous prenons par exemple la page /vues/utilisateur/creerutilisateur.jsp, nous devons
simplementajouterleslignessuivantes :
...
<html:javascript formName="FormUtilisateur"/>
<html:form action="/validercreerutilisateur" method="POST" onsubmit="return
validateFormUtilisateur(this)">
...
- 23 -© ENI Editions - All rigths reserved
10.Suppressiond
’
unutilisateur
Lasuppressionestleserviceleplussimpleàdévelopperaveclaconsultation.Nousallonscependantajouterunfichier
JavaScript /javascript/boiteoutils.js qui permet d
’
afficher des messages de confirmation. Ce fichier contient le code
suivant :
//supprimer un utilisateur
function confirmerSuppressionUtilisateur(id_utilisateur,pseudonyme)
{
if(confirm("Voulez -vous supprimer cet utilisateur ?"))
{
chemin="supprimerutilisateur.do?id_utilisateur="+id_utilisateur
+"&pseudonyme="+pseudonyme;
document.location.href=chemin;
}
else
{
return;
}
}
Cescriptestinsérédanslapage/vues/outils/entete.jspfaveclaligneci
après :
<script type="text/javascript" src="javascript/boiteoutils.js"></script>
Nouspouvonsdésormaisréaliserlaconfigurationdel
’
actionparl
’
intermédiairedufichierstruts
config.xml
.
<!-- ===action qui permet de supprimer un utilisateur=== -->
<action
path="/supprimerutilisateur"
scope="request"
input="/listeutilisateur.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="supprimerutilisateur"
>
<forward name="listeutilisateur" path="/listeutilisateur.do"
redirect="false"/>
</action>
Enfin,ilresteàcoderl
’
actionsupprimerutilisateur()etdumodèleaveclesmessagesassociés.
//supprimer l ’utilisateur indiqué
public ActionForward supprimerutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plugin dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer l ’id de l ’utilisateur
String id_utilisateur=(String)request.getParameter("id_utilisateur");
//récupérer le pseudonyme de l ’utilisateur
String pseudonyme=(String)request.getParameter("pseudonyme");
//supprimer l ’administrateur
if(id_utilisateur!=null && !id_utilisateur.equals("") &&
pseudonyme!=null && !pseudonyme.equals(""))
{
int resinstruction=utilisateurmodele.supprimerUtilisateur
(id_utilisateur,pseudonyme);
//en cas d ’erreur avec la base de données
if (resinstruction!=1)
{
//ajouter un message d ’erreur
ActionMessages erreurs=new ActionErrors();
- 24 - © ENI Editions - All rigths reserved
erreurs.add("message", new ActionMessage
("erreurs.suppressionutilisateur"));
saveErrors(request,erreurs);
}
//tout est OK
else
{
ActionMessages messages=new ActionMessages();
messages.add("message", new ActionMessage
("succes.suppressionutilisateur"));
saveMessages(request,messages);
}
}
//retourner sur la page de listing des utilisateurs
return mapping.findForward("listeutilisateur");
}
/***********************************************************
* supprimer l ’utilisateur
***********************************************************/
public int supprimerUtilisateur(String id_utilisateur,String pseudonyme)
{
int resinstruction=0;
PreparedStatement requetea=null,requeteb=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//supprimer l ’utilisateur
requetea=connection.prepareStatement("DELETE FROM utilisateur WHERE
id_utilisateur=?");
requetea.setString(1,(String)id_utilisateur);
resinstruction=requetea.executeUpdate();
//supprimer ses inscriptions aux salons
requeteb=connection.prepareStatement("DELETE FROM utilisateursalon
WHERE pseudonyme=?");
requeteb.setString(1,(String)pseudonyme);
requeteb.executeUpdate();
}
...
}
- 25 -© ENI Editions - All rigths reserved
11.Gestiondel
’
étatduserveur
La gestion de l
’
état du serveur est réalisée avec la table serveur. Cette table possède un seul attribut nommé
etatserveurquipermetd
’
activeroudésactiverleserveur.
Pourcommencer,nousallonsdévelopperuneactionStrutsavecunparamètreenvoyéparunlienHTMLquipermetde
changer l
’
état du serveur. Pour cela il est nécessaire d
’
ajouter un nouveau lien dans le menu de la
page/vues/outils/navigation.jspf.Celienvadéclencherl
’
actionetatserveuravecleparamètreencours,représentantle
nouvelétat.Pourceservice,nouspouvonsutiliserla classe ActionsimpleproposéeparStruts.Demême,latable
serveurpossèdeunseulattribut,ilestdoncplussimpledenepas créer une classeJavaBeandeformulairemais
d
’
utiliseràlaplaceunparamètredetypechaînedecaractèresquireprésentel
’
étatduserveur.
NouscommençonsparajouterunlienHTMLpourdéclencherleservicedegestiondel
’
étatduserveur.
<table border="0" cellspacing="0" cellpadding="0"
name="menugauche" id="menugauche" style="background:
url(’img/fondmenu.gif ’) repeat-x;" height="500">
<tr>
<td width="100%" valign="top"><br/><span style="margin -left:
30px;"><b>ADMINISTRATION</b></span>
<ul class="menucategorie">
<li><a href="etatserveur.do"><img src="img/fleche.gif"
border="0" align="absmiddle"/> Gestion du serveur</a></li>
<li><a href="listeutilisateur.do"><img src="img/fleche.gif"
border="0" align="absmiddle"/> Gestion des utilisateurs</a></li>
</ul>
</td>
</tr>
</table>
Nouspassonsensuiteàladéfinitiondel
’
actionetatserveur.dodanslefichierdeconfigurationstruts
config.xml
.
<!-- ================= SERVEUR ===================== -->
<!-- ===action qui permet de gérer l ’état du serveur=== -->
<action
path="/etatserveur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionServeurAction"
>
<forward name="etatserveur" path="/vues/serveur/etatserveur.jsp"
redirect="false"/>
</action>
Lemappingdel
’
actionestréalisé,ilresteàcréerlecodedelaclassechatbetaboutique.GestionServeurActionainsique
lemodèleassocié.Nouspouvonségalementadapternotreactionafindevérifiersil
’
étatduserveurestpasséen
paramètreetsinousdevonsdanscecaschangersonétatavecuneméthodedumodèle.
package chatbetaboutique;
import modele.ServeurModele;
import org.apache.struts.action.*;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.sql.DataSource;
public class GestionServeurAction extends Action{
//variables de la classe
DataSource ds=null;
//gérer l ’état du serveur
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
- 26 - © ENI Editions - All rigths reserved
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
ServeurModele serveurmodele=new ServeurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le paramètre et changer l ’état du serveur si nécessaire
String etat=(String)request.getParameter("etat");
if(etat!=null && !etat.equals(""))
{
//modifier l ’état du serveur
int resinstruction=serveurmodele.setEtatServeur(etat);
//en cas d ’erreur avec la base de données
if (resinstruction!=1)
{
//ajouter un message d ’erreur
ActionMessages erreurs=new ActionErrors();
erreurs.add("message", new
ActionMessage("erreurs.etatserveur"));
saveErrors(request,erreurs);
}
//tout est OK
else
{
//toutes les vérifications sont correctes
ActionMessages messages=new ActionMessages();
messages.add("message", new
ActionMessage("succes.etatserveur"));
saveMessages(request,messages);
}
}
//retourner l ’état actuel du serveur
String etatserveur=(String)serveurmodele.getEtatServeur();
//retourner l ’état du serveur
request.setAttribute("etatserveur",etatserveur);
//retourner sur la page d ’affichage de l ’état du serveur
return mapping.findForward("etatserveur");
}
//fin de la classe
}
Lemodèlepermetdelirel
’
étatactuelduserveurdanslabasededonnées.
package modele;
import boiteoutils.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
public class ServeurModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
/***********************************************************
* constructeur
***********************************************************/
public ServeurModele(DataSource ds)
{
//récupérer le DataSource de la servlet
this.ds=ds;
- 27 -© ENI Editions - All rigths reserved
}
/***********************************************************
* etat du serveur
***********************************************************/
public String getEtatServeur()
{
String etatserveur="0";
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT
etatserveur FROM serveur");
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
if(rs.next())
{
etatserveur=rs.getString("etatserveur");
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ServeurModele.java fonction getEtatServeur");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ServeurModele.java fonction getEtatServeur");
}
}
//retourner l ’état du serveur
return etatserveur;
}
//fin de la classe
}
LapageJSPpermetd
’
afficherl
’
étatduserveuretdepasserunparamètredansunlienpourchangersavaleur.
<%
//récupérer l ’état du serveur
String etatserveur=(String)request.getAttribute("etatserveur");
%>
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="etatserveur.do"><img
src="img/serveur.jpg" border="0" align="absmiddle"/> Etat
du serveur</a></div>
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
- 28 - © ENI Editions - All rigths reserved
<tr><td>Etat</td><td>
<% if(etatserveur.equals("1"))
{
out.println("<a href=\"etatserveur.do?etat=0\">Serveur Actif
<img src=\"img/actif.gif\" border=\"0\" align=\"absmiddle\"/></a>");
}
else
{
out.println("<a href=\"etatserveur.do?etat=1\">Serveur Inactif
<img src=\"img/inactif.gif\" border=\"0\" align=\"absmiddle\"/></a>");
}
%></td></tr>
</table>
<!-- inclure le pied de page -->
<%@ include file="../outils/piedpage.jspf" %>
Lafonctiondumodèlequipermetdechangerl
’
étatduserveurestlasuivante :
/***********************************************************
* modifier l ’etat du serveur
***********************************************************/
public int setEtatServeur(String etat)
{
int resinstrution=0;
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
requete=connection.prepareStatement("UPDATE
serveur SET etatserveur=?");
requete.setString(1,(String)etat);
resinstrution=requete.executeUpdate();
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
ServeurModele.java fonction setEtatServeur");
}
finally
{
try
{
//fermer la connexion
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
ServeurModele.java fonction setEtatServeur");
}
}
//résultat de la modification
return resinstrution;
}
Enfin,lefichierdepropriétéspossèdelesdeuxnouveauxmessagessuivants :
#serveur
erreurs.etatserveur=Erreur lors de la modification de l ’état du serveur
succes.etatserveur=L ’état du serveur a été modifié avec succès
- 29 -© ENI Editions - All rigths reserved
12.Gestiondessalons
Nousallonsdévelopperunservicedegestiondessalonsaveclemêmeprincipequeceluiutilisépourlesutilisateurs
duchat.Lefonctionnementestidentique,ilconsisteàdévelopperleJavaBeandeformulaireSalonencorrespondance
aveclemodèledelatablesalondelabasededonnées.
package boiteoutils;
import java.io.Serializable;
public class Salon implements Serializable
{
//java 5.0 pour avoir un identifiant unique de la sérialisation
private static final long serialVersionUID = 1L;
//variables de classe
private String id_salon=null;
private String theme=null;
private String date=null;
private int actif=0;
public Salon(){
}
...
//fin de la classe
}
Nousréalisonsensuite,ladéfinitiondel
’
élément
<form
bean/>
etdumappingpourlesactionsliéesàlagestiondes
salonsduchat.
<!-- ========== Formulaire de gestion des salons ========== -->
<form-bean name="FormSalon"
type="org.apache.struts.validator.DynaValidatorForm">
<form -property name="salon" type="boiteoutils.Salon" />
</form-bean>
<!-- ================= SALON ===================== -->
<!-- ===action qui permet de lister les salons=== -->
<action
path="/listesalon"
name="FormSalon"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionSalonAction"
parameter="listesalon">
<forward name="listesalon" path="/vues/salon/listesalon.jsp"
redirect="false"/>
</action>
<!-- ===action qui permet de consulter la fiche d ’un salon=== -->
<action
path="/consultersalon"
name="FormSalon"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionSalonAction"
parameter="consultersalon">
<forward name="consultersalon" path="/vues/salon/consultersalon.jsp"
redirect="false"/>
</action>
<!-- action qui permet d ’afficher le formulaire de création d ’un salon -->
<action
path="/creersalon"
name="FormSalon"
scope="request"
validate="false"
input="/accueil.do"
type="org.apache.struts.actions.ForwardAction"
parameter="/vues/salon/creersalon.jsp">
</action>
- 30 - © ENI Editions - All rigths reserved
<!-- action qui va valider la création d ’un salon -->
<action
path="/validercreersalon"
name="FormSalon"
scope="request"
validate="true"
input="/creersalon.do"
type="chatbetaboutique.GestionSalonAction"
parameter="validercreersalon">
<forward name="listesalon" path="/listesalon.do" redirect="false"/>
</action>
<!-- ===action qui permet de modifier un salon en
récupérant ses valeurs=== -->
<action
path="/modifiersalon"
name="FormSalon"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionSalonAction"
parameter="modifiersalon">
<forward name="modifiersalon" path="/vues/salon/modifiersalon.jsp"
redirect="false"/>
</action>
<!-- ===action qui permet de valider la modification d ’un salon=== -->
<action
path="/validermodifiersalon"
name="FormSalon"
scope="request"
validate="true"
input="/modifiersalon.do"
type="chatbetaboutique.GestionSalonAction"
parameter="validermodifiersalon">
<forward name="modifiersalon" path="/modifiersalon.do" redirect="false"/>
<forward name="listesalon" path="/listesalon.do" redirect="false"/>
</action>
<!-- ===action qui permet de supprimer un salon=== -->
<action
path="/supprimersalon"
scope="request"
input="/listesalon.do"
type="chatbetaboutique.GestionSalonAction"
parameter="supprimersalon">
<forward name="listesalon" path="/listesalon.do" redirect="false"/>
</action>
Nous pouvons désormais passer au codage des actions associées, avec la classe de gestion nommée
chatbetaboutique.GestionSalonAction
.
package chatbetaboutique;
import modele.SalonModele;
import org.apache.struts.action.*;
import org.apache.struts.actions.MappingDispatchAction;
import boiteoutils.Salon;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.sql.DataSource;
public class GestionSalonAction extends MappingDispatchAction{
//variables de la classe
DataSource ds=null;
//afficher la liste des salons
public ActionForward listesalon(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws IOException,
ServletException
- 31 -© ENI Editions - All rigths reserved
{
...
}
//consulter un salon
public ActionForward consultersalon(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws IOException,
ServletException
{
...
}
//valider la création d ’un salon
public ActionForward validercreersalon(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
...
}
//afficher le formulaire en modification pour le salon indiqué
public ActionForward modifiersalon(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws IOException,
ServletException
{
...
}
//valider la modification du salon
public ActionForward validermodifiersalon(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
...
}
//supprimer le salon indiqué
public ActionForward supprimersalon(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws IOException,
ServletException
{
...
}
//fin de la classe
}
Laclassedegestiondumodèlemodele.SalonModeleestégalementtrèsprochedumodèledegestiondesutilisateurs.
package modele;
import boiteoutils.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.apache.commons.dbutils.BeanProcessor;
import java.util.ArrayList;
public class SalonModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
- 32 - © ENI Editions - All rigths reserved
ResultSet rs=null;
//liste des objets
ArrayList<Salon> listesalon=new ArrayList<Salon>();
/***********************************************************
* constructeur
***********************************************************/
public SalonModele(DataSource ds)
{
//récupérer le DataSource de la servlet
this.ds=ds;
}
/***********************************************************
* liste des salons
***********************************************************/
public ArrayList getListeSalon()
{
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM salon ORDER BY
theme,date");
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//utilisation de la librairie DbUtils qui permet de transformer
un ResultSet en Objet Java
BeanProcessor bp=new BeanProcessor();
listesalon = (ArrayList<Salon>)bp.toBeanList(rs,Salon.class);
}
}
...
}
/***********************************************************
* récupérer le salon indiqué
***********************************************************/
public Salon getSalon(String id_salon)
{
//créer un objet salon
Salon salon=new Salon();
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM salon WHERE
id_salon=?");
requete.setString(1,(String)id_salon);
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
if(rs.next())
{
//utilisation de la librairie DbUtils qui permet de transformer
un ResultSet en Objet Java
BeanProcessor bp=new BeanProcessor();
salon = (Salon)bp.toBean(rs, Salon.class);
}
}
- 33 -© ENI Editions - All rigths reserved
}
...
}
/***********************************************************
* créer un salon
***********************************************************/
public int creerSalon(Salon salon)
{
...
}
/***********************************************************
* modifier le salon
***********************************************************/
public int modifierSalon(Salon salon)
{
...
}
/***********************************************************
* supprimer le salon
***********************************************************/
public int supprimerSalon(String id_salon)
{
...
}
//fin de la classe
}
Latotalitéduservicedegestiondessalonsestpratiquementterminé,nousallonsajouterlesfonctionnalitésd
’
usage
pourl
’
ensemble.Nousinséronsd
’
abordunliendanslemenudenavigation/vues/outils/navigation.jspf
.
<table border="0" cellspacing="0" cellpadding="0" name="menugauche"
id="menugauche" style="background: url( ’img/fondmenu.gif ’) repeat-x;"
height="500">
<tr>
<td width="100%" valign="top"><br/><span style="margin -left:
30px;"><b>ADMINISTRATION</b></span>
<ul class="menucategorie">
<li><a href="etatserveur.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/><span>Cliquez ici
pour afficher le service de gestion du serveur</span> Gestion
du serveur</a></li>
<li><a href="listeutilisateur.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/><span>Cliquez ici
pour afficher le service de gestion des utilisateurs</span> Gestion
des utilisateurs</a></li>
<li><a href="listesalon.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/><span>Cliquez ici
pour afficher le service de gestion des salons</span> Gestion
des salons</a></li>
</ul>
</td>
</tr>
</table>
Ensuite,nouspassonsaucodagedesrèglesdevalidationpourleformulaire.Dansnotrecas,nousindiquonsune
règlesurleschampstheme,dateetactifdanslefichier/WEB
INF/validation.xml
.
...
<global>
...
<constant>
<constant -name>datesalon</constant -name>
<constant -value>^[0 -9]{4}-[0-9]{2}-[0-9]{2}\s[0 -9]{2}:[0 -9]{2}:[0 -9]{2}.{0,1}
[0-9]{0,1}$</constant -value>
</constant>
- 34 - © ENI Editions - All rigths reserved
</global>
...
<!-- ========== Formulaire salon ========== -->
<form name="FormSalon">
<field property="salon.theme" depends="required">
<arg0 key="themesalon"/>
</field>
<field property="salon.date" depends="required,mask">
<arg0 key="datesalon"/>
<var>
<var -name>mask</var -name>
<var -value>${datesalon}</var -value>
</var>
</field>
<field property="salon.actif" depends="required,mask">
<arg0 key="actifsalon"/>
<var>
<var -name>mask</var -name>
<var -value>${autorisation}</var -value>
</var>
</field>
</form>
...
Enfinnousterminonsparlefichierdepropriétéspourlesmessagesetlesnomsdeschamps.
#salon
erreurs.creationsalon=Erreur lors de la création du salon
succes.creationsalon=Le salon a été créé avec succès
erreurs.modificationsalon=Erreur lors de la modification du salon
succes.modificationsalon=Le salon a été modifié avec succès
erreurs.suppressionsalon=Erreur lors de la suppression du salon
succes.suppressionsalon=Le salon a été supprimé avec succès
# -- gestion des salons --
themesalon=Thème
datesalon=Date
nomutilisateur=Nom
actifsalon=Actif
Lorsdelasuppressiond
’
unsalon,unmessagedeconfirmationestaffiché.LafonctionconfirmerSuppressionSalondoit
doncêtrecodéedanslefichier/javascript/boiteoutils.js
.
//supprimer un salon
function confirmerSuppressionSalon(id_salon)
{
if(confirm("Voulez -vous supprimer ce salon ?"))
{
chemin="supprimersalon.do?id_salon="+id_salon;
document.location.href=chemin;
}
else
{
return;
}
}
Leserviceestopérationnel,nouspouvonstesterlessaisies,modificationsetmessages.
- 35 -© ENI Editions - All rigths reserved
13.Gestiondesinscriptionsetinteractivité
Pourledéveloppementduservicedegestiondesinscriptionsutilisateursauxsalons,ilexiste plusieursapproches
possibles :
●Développement d
’
un service spécifique qui permet d
’
associer des utilisateurs au salon indiqué à partir de
casesàcocher.
●Développementd
’
unserviceinteractifàbased
’
Ajaxetdesérialisationdesdonnées.
C
’
est la seconde solution que nous allons utiliser, avec un formulaire dynamique associé à chaque salon. Pour la
réalisationdecettepartiepluscomplexe,ilestnécessairededétaillerunscénarioexplicatif :
●Lapremièreétapeconsisteàcréerunebalise
<div/>
HTMLassociéeàchaquelignedelalistedessalons.Par
exemple,lesalonavec
id=6
auraunebalise
<div/>
cachéeavecunidentifiantuniqueetpourrarecevoirun
formulaireAjaxsuiteàunclicadministrateur.
●Suiteàceclic,unefonctionJavaScriptAjaxvarécupérertoutelalistedesutilisateursavecl
’
étatdel
’
inscription
associée(inscritoupasausalon)etafficherlaréponsedanslabalise
<div/>
cachée.
●Touslesutilisateursserontaffichésdansceformulaireetpourrontdoncêtreinscritsausalonsélectionnéen
uneseuleétape.Ilseraitpossible,parfacilité,deréaliseruneinscriptionenAjaxsurchaqueclicdelacaseà
cochermaisleserviceneseraitpastrèsergonomique.Nousallonsdoncutiliserunmécanismedesérialisation
pourenvoyertousleschoixdel
’
administrateuravecunseulclicdesouris.
Ledéveloppement dece servicenécessite l
’
utilisation de la librairie JavaScript JQuery (http://jquery.com/).
Cettelibrairieestréférencéedanslapageentete.jspf
.
Toutd
’
abordnousajoutonsunboutondegestiondanslavuequipermetd
’
afficherlalistedessalons.
<%
...
out.println("<td><a href= ’javascript:listeUtilisateurSalon
(\""+salon.getId_salon()+"\"); ’><img src=\"img/utilisateurchat.png\
" border=\"0\" align=\"absmiddle\" title=\"Utilisateur\"/></a><div
id=\"listeutilisateursalon_"+salon.getId_salon()+"\"\"
class=\"listeutilisateursalon\"> </div></td>");
...
%>
Ensuite,nouscodonslafonctionJavaScriptlisteUtilisateurSalon()présentedanslefichierboiteoutils.jsquipermetde
récupérertouslesutilisateursdelabasededonnéesainsiquelesétatsassociés,pourcesalon.
//fonction qui permet d ’afficher la liste des utilisateurs
inscrits ou pas au salon
function listeUtilisateurSalon(id_salon)
{
//fermer la boîte en cours
if($("#listeutilisateursalon_"+id_salon).css("display")=="block")
- 36 - © ENI Editions - All rigths reserved
{
$("#listeutilisateursalon_"+id_salon).slideUp("slow");
return;
}
//fermer toutes les boîtes qui ont la classe css
$(".listeutilisateursalon").hide();
//détruire leur formulaire
$(".listeutilisateursalon").html("");
//récupérer la liste des utilisateurs inscrits ou pas au salon indique
if(id_salon!=null)
{
//envoyer les donnees en POST
$.ajax(
{
type: "POST",
url: "listeutilisateursalon.do",
dataType: "html",
data: "id_salon="+id_salon,
timeout : 4000,
error: function(){
},
beforeSend : function()
{
},
success: function(html)
{
//mettre le résultat dans la balise <div/> concernee
$("#listeutilisateursalon_"+id_salon).html(html);
$("#listeutilisateursalon_"+id_salon).slideDown("slow");
}
});
}
}
L
’
instruction suivante $(".listeutilisateursalon").html(""); présente dans la fonction JavaScript
listeUtilisateurSalon()estimportante.Eneffet,ellepermetdedétruiretouslesformulairesquiontpourclasse
CSS.listeutilisateur.Eneffet,lafermeturedesblocs
<div/>
nepermetpaslasuppressiondescasesàcocherqui
sontalorsprésentesdanslapageHTMLmaisnonvisibles.Cetteinstructionpermetdedétruiretouslesformulaires
pourqu
’
iln
’
yaitpasdeconflitsdenoms.
Ensuite,nouscodonsl
’
actiondanslefichierdeconfigurationstruts
config.xml
.
<!-- ===action qui permet de retourner la liste
des utilisateurs pour un salon indiqué=== -->
<action
path="/listeutilisateursalon"
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="listeutilisateursalon"
>
<forward name="listeutilisateursalon"
path="/vues/utilisateur/listeutilisateursalon.jsp"
redirect="false"/>
</action>
Le code de l
’
action listeutilisateursalon est ajouté dans la classe GestionUtilisateur. Ce développement est assez
simple,ilpermetdedéclencherunenouvellefonctiondumodèlequiretournetouslesutilisateursetleurétatrespectif,
pourlesalonindiquéenparamètre.
//afficher la liste des utilisateurs pour le salon
public ActionForward listeutilisateursalon(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
- 37 -© ENI Editions - All rigths reserved
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le salon en paramètre
String id_salon=(String)request.getParameter("id_salon");
//récupérer la liste de tous les utilisateurs et l ’état pour ce salon
if(id_salon!=null && !id_salon.equals(""))
{
//retourner la liste des utilisateurs
ArrayList listeutilisateursalon=
(ArrayList)utilisateurmodele.getListeUtilisateurSalon(id_salon);
//retourner la liste des utilisateurs
request.setAttribute("listeutilisateursalon",listeutilisateursalon);
//retourner le numéro du salon
request.setAttribute("id_salon",id_salon);
//vider par sécurité
listeutilisateursalon=null;
//retourner sur la page d ’affichage des utilisateurs
return mapping.findForward("listeutilisateursalon");
}
//en cas d ’erreur
return mapping.findForward("accueil");
}
/***********************************************************
* liste des utilisateurs et informations pour le salon
***********************************************************/
public ArrayList getListeUtilisateurSalon(String id_salon)
{
// récupérer la liste de tous les utilisateurs
this.listeutilisateur=this.getListeUtilisateur();
//nouvelle liste des utilisateurs avec l ’état de l ’inscription
ArrayList<Utilisateur> listeutilisateursalon=new
ArrayList<Utilisateur>();
// parcours de chaque utilisateur
for(int i=0;i<listeutilisateur.size();i++)
{
Utilisateur utilisateur=(Utilisateur)listeutilisateur.get(i);
//récupérer son inscription pour le salon indiqué
int inscrit=this.getInscriptionUtilisateurSalon
(utilisateur.getPseudonyme(),id_salon);
//positionner cette valeur pour l ’utilisateur
utilisateur.setInscrit(inscrit);
//mettre l ’utilisateur dans la nouvelle liste
listeutilisateursalon.add(utilisateur);
}
return listeutilisateursalon;
}
/***********************************************************
* tester si l ’utilisateur concerné est inscrit au salon
***********************************************************/
public int getInscriptionUtilisateurSalon(String pseudonyme,String id_salon)
{
//inscription au salon
int inscrit=0;
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
- 38 - © ENI Editions - All rigths reserved
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM
utilisateursalon WHERE pseudonyme=? AND id_salon=?");
requete.setString(1,(String)pseudonyme);
requete.setString(2,(String)id_salon);
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//l ’utilisateur est inscrit à ce salon
if(rs.next())
{
inscrit=1;
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getInscriptionUtilisateurSalon");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getInscriptionUtilisateurSalon");
}
}
//retourner l ’état de la connexion
return inscrit;
}
LesaffichagessontréalisésaveclafeuilledestylepardéfautetlanouvelleclasseCSS.listeutilisateursalon
.
.listeutilisateursalon
{
display:none;
position:absolute;
height:5cm;
width:15cm;
overflow:auto;
z-index:1000;
margin-left:-500px;
border-style:solid;
border-top-width:1px;
border-right-width:2px;
border-bottom-width:2px;
border-left-width:1px;
border-color:#A8A6A7;
background -color:#fff;
}
Lemécanismepermetainsidefermertouteslesboîtesdéjàouvertes(etdelesdétruire)etderécupérerenarrière
plan, avec la technologie Ajax, la liste des utilisateurs et leur état respectif. Le codage de la
vue/vues/utilisateur/listeutilisateursalon.jspestassezprochedelavue/vues/utilisateur/listeutilisateur.jsp
.
<%@ page import="java.util.ArrayList" %>
<%@ page import="boiteoutils.Utilisateur" %>
<%
//liste des utilisateurs
- 39 -© ENI Editions - All rigths reserved
ArrayList listeutilisateursalon=
(ArrayList)request.getAttribute("listeutilisateursalon");
//numéro du salon
String id_salon=(String)request.getAttribute("id_salon");
%>
<form name="formulaire" id="formulaire">
<table border="0" id="tableaubordure" cellspacing="0" cellpadding="0">
<tr align="center" class="entetetableau">
<td>ID</td>
<td>Pseudonyme</td>
<td>Nom</td>
<td>Prénom</td>
<td>Inscription</td>
</tr>
<%
for(int i=0;i<listeutilisateursalon.size();i++)
{
//récupérer l ’objet dans la liste
Utilisateur utilisateur=(Utilisateur)listeutilisateursalon.get(i);
if(i%2==0)out.println("<tr align=\"center\" class=\"ligneclaire\">");
else out.println("<tr align=\"center\" class=\"lignefoncee\">");
out.println("<td>"+utilisateur.getId_utilisateur()+"</td>");
out.println("<td>"+utilisateur.getPseudonyme()+"</td>");
out.println("<td>"+utilisateur.getNom()+"</td>");
out.println("<td>"+utilisateur.getPrenom()+"</td>");
out.println("<td>");
//gestion de l ’inscription pour l ’utilisateur en cours
out.println("<input type=\"checkbox\"
name=\"inscriptionutilisateur_"+utilisateur.getPseudonyme()+"\"");
if(utilisateur.getInscrit()==1)
{
out.println(" checked=\"checked\"/>");
}
else
{
out.println(" />");
}
out.println("</tr>");
}
%>
<tr><td align="center" colspan="5"><input type="button"
onclick="javascript:validerInscriptionUtilisateurSalon(<%= id_salon%>)"
value="Valider" class="bouton"/></td></tr>
</table>
</form>
Le service d
’
affichage avec état est désormais opérationnel. Les utilisateurs inscrits au salon en cours sont bien
marquésetlacaseàcochercorrespondanteaffichel
’
état.Nouspouvonsmaintenantgérerlapartieinscriptiondes
utilisateursauxsalons.
Ilseraitpossibleàchaqueclicdesourissurlacaseàcocherdel
’
utilisateur,d
’
envoyerl
’
informationàuneactionetde
fermerlafenêtre.Cettetechniqueenvisageablen
’
estpastrèsergonomiquepourl
’
administrateurquidevraréaliser
plusieursopérationsàchaqueinscription. Uneinterfaceplusadaptéepermetde sélectionnerplusieursutilisateurs
(plusieurschoixd
’
inscription)etdevaliderl
’
ensembleenuneseuleétape.
Lescénariodeprogrammationestalorslesuivant :
- 40 - © ENI Editions - All rigths reserved
●L
’
administrateursélectionneplusieursinscriptionsàlafois.
●L
’
administrateur clique sur le bouton de validation. Ce bouton déclenche une fonction JavaScript qui va
récupérertouteslescasescochéesetlesiddesutilisateursassociés.Cettemêmefonctionréalisealorsune
sérialisationdesdonnéesetpostel
’
ensembledesinformationsàuneactionquipourraréaliserlatotalitédes
inscriptionsenuneseuleétape.
La sérialisation de données est très utilisée en programmation Web. Cette technique permet d
’
envoyer
plusieurs informations dans la requête HTTP GET ou POST avec un seul paramètre sous une syntaxe
spécifique. Pour notre exemple, nous allons utiliser une variable nommée inscription qui aura par exemple pour
valeur :inscription=2#4#6pourlesutilisateursd
’
id2,4et6.
NouscommençonslecodagedelafonctiondevalidationJavaScriptparleparcoursdesélémentsduformulairedetype
checkbox(caseàcocher).
//fonction qui permet d ’envoyer sous forme serialisee
les inscriptions utilisateur
function validerInscriptionUtilisateurSalon(id_salon)
{
//inscription des utilisateurs sous forme serialisee
var inscription="";
//recuperer toutes les cases a cocher
var elts=document.forms["formulaire"].elements;
//parcourir chaque element de la page
for(var i=0;i<elts.length;i++)
{
//pas une case a cocher
if(elts[i].type!="checkbox")
{
continue;
}
//case a cochee et validee
if(elts[i].checked)
{
//la case a cocher doit contenir le terme
inscriptionutilisateur_
var nom=elts[i].name;
var pseudonyme=nom.substring(nom.indexOf("_")+1,nom.length);
//l ’id de l ’utilisateur est correct on va le serialiser
if(pseudonyme!=null && pseudonyme!="")
{
inscription+="#"+pseudonyme;
}
}
}
alert(inscription);
}
Nousallonsmaintenantoptimisercetteméthodeafindevérifierquelescasesàcochersontbienenrapportavecles
utilisateursetsérialiserl
’
ensembleavantl
’
envoidesdonnéesenAjax.LanouvellefonctionJavaScriptestprésentée
ci
après :
//fonction qui permet d ’envoyer sous forme serialisee
- 41 -© ENI Editions - All rigths reserved
les inscriptions utilisateur
function validerInscriptionUtilisateurSalon(id_salon)
{
//inscription des utilisateurs sous forme serialisee
var inscription="";
//recuperer toutes les cases a cocher
var elts=document.forms["formulaire"].elements;
//parcourir chaque element de la page
for(var i=0;i<elts.length;i++)
{
//pas une case a cocher
if(elts[i].type!="checkbox")
{
continue;
}
//case a cochee et validee
if(elts[i].checked)
{
//la case a cocher doit contenir le terme inscriptionutilisateur_
var nom=elts[i].name;
var pseudonyme=nom.substring(nom.indexOf("_")+1,nom.length);
//l ’id de l’utilisateur est correct on va le serialiser
if(pseudonyme!=null && pseudonyme!="")
{
inscription+="#"+pseudonyme;
}
}
}
//envoyer les inscriptions en ajax
if(id_salon!=null)
{
//envoyer les donnees en POST
$.ajax(
{
type: "POST",
url: "inscriptionutilisateursalon.do",
dataType: "html",
data: "id_salon="+id_salon+"&inscription="+inscription,
timeout : 4000,
error: function(){
},
beforeSend : function()
{
},
success: function(html)
{
//reponse correcte
if(html==1)
{
//fermer la fenetre ouverte
$("#listeutilisateursalon_"+id_salon).slideUp("slow");
}
//erreur lors des inscriptions
else
{
alert("Erreur lors des inscriptions");
}
}
});
}
}
Le service d
’
inscription des utilisateurs aux salons est quasiment terminé, il ne reste plus qu
’
à coder l
’
action, la
fonctiondumodèleetlavueJSPpourl
’
appeldel
’
URLinscriptionutilisateursalon.do
.
<!-- ===action qui permet d ’inscrire des utilisateurs
a un salon indiqué=== -->
<action
- 42 - © ENI Editions - All rigths reserved
path="/inscriptionutilisateursalon"
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="inscriptionutilisateursalon"
>
<forward name="inscriptionutilisateursalon"
path="/vues/utilisateur/inscriptionutilisateursalon.jsp" redirect="false"/>
</action>
//inscrire les utilisateurs au salon précisé
public ActionForward inscriptionutilisateursalon(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le salon en paramètre
String id_salon=(String)request.getParameter("id_salon");
//récupérer les validations
String validation=(String)request.getParameter("inscription");
//decouper les validations
String[] tabutilisateur=validation.split("#");
//inscrire les utilisateurs
if(id_salon!=null && !id_salon.equals("") && tabutilisateur!=null)
{
int resinstruction=utilisateurmodele.inscrireUtilisateurSalon
(id_salon,tabutilisateur);
request.setAttribute("resinstruction",resinstruction);
return mapping.findForward("inscriptionutilisateursalon");
}
//en cas d ’erreur
return mapping.findForward("accueil");
}
/***********************************************************
* inscrire les utilisateurs au salon
***********************************************************/
public int inscrireUtilisateurSalon(String id_salon,String[] tabutilisateur)
{
int resinstruction=1;
PreparedStatement requetea=null,requeteb=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//toujours supprimer les inscriptions pour ce salon
requetea=connection.prepareStatement("DELETE FROM utilisateursalon WHERE
id_salon=?");
requetea.setString(1,(String)id_salon);
resinstruction=requetea.executeUpdate();
//inscrire les utilisateurs reçus, on utilise pas resinstruction car on
n’a pas forcement d ’utilisateur
for(int i=1; i<tabutilisateur.length;i++)
{
requeteb=connection.prepareStatement("INSERT INTO
utilisateursalon(pseudonyme,id_salon) VALUES (?,?)");
requeteb.setString(1,(String)tabutilisateur[i]);
requeteb.setString(2,(String)id_salon);
requeteb.executeUpdate();
}
- 43 -© ENI Editions - All rigths reserved
}
catch(Exception e)
{
System.out.println("Erreur dans la classe UtilisateurModele.java
fonction inscrireUtilisateurSalon");
}
finally
{
try
{
//fermer la connexion
if(requetea!=null)OutilsBaseDeDonnees.fermerConnexion(requetea);
if(requeteb!=null)OutilsBaseDeDonnees.fermerConnexion(requeteb);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe UtilisateurModele.java
fonction inscrireUtilisateurSalon");
}
}
//retourner le résultat de la suppression
return resinstruction;
}
<%
//resultat de l ’instruction
int resinstruction=(Integer)request.getAttribute("resinstruction");
%>
<%= resinstruction %>
Ilexiste dansles précédentes lignesde codedeux élémentsessentiels aubon fonctionnement del
’
ensemble.Le
premier, présent dans la fonction inscrireUtilisateurSalon(), permet de supprimer toutes les inscriptions au salon
indiqué avant de réaliser les nouvelles inscriptions. Comme cela, si l
’
administrateur décide de décocher toutes les
casesd
’
unsalonparexemple,lafonctiondesuppressionseraalorsdéclenchéemaislaboucledecréationneréalisera
aucuneopération,lasuppressiontotaledesinscriptionsseradoncbienréalisée.
Enfin la page JSP /vues/utilisateur/inscriptionutilisateursalon.jsp permet uniquement de retourner l
’
état de l
’
action
d
’
inscriptionquiseraludanslafonctionJavaScriptAjaxenretourpourl
’
affichage.
14.Gestiondesconnexionsutilisateuretinteractivité
Leserviced
’
administrationduchatestpresqueterminé,ilresteàdévelopperunserviced
’
affichagedesutilisateurs
connectésaudifférentssalons.Dansl
’
étapeprécédentenousavonsdéveloppéunserviceAjaxpourrécupérerlaliste
des utilisateurs inscrits à un salon donné. Nous pouvons développer sur des bases identiques des formulaires
dynamiquespourafficherlesutilisateursactuellementconnectés.
Ledéveloppementcommenceparlamodificationdelapage/vues/salon/listesalon.jspavecl
’
insertiondebalises
<div/>
cachéesetd
’
uneclasseCSSadaptée.
...
out.println("<td><a href= ’javascript:listeConnexionSalon
(\""+salon.getId_salon()+"\"); ’><img src=\"img/utilisateurconnecte.png\"
border=\"0\" align=\"absmiddle\" title=\"Connexion\"/></a>
<div id=\"listeconnexionsalon_"+salon.getId_salon()+"\"\"
class=\"listeconnexionsalon\"> </div></td>");
...
.listeutilisateursalon,.listeconnexionsalon
{
display:none;
position:absolute;
height:5cm;
width:15cm;
overflow:auto;
z-index:1000;
margin-left:-500px;
border-style:solid;
- 44 - © ENI Editions - All rigths reserved
border-top-width:1px;
border-right-width:2px;
border-bottom-width:2px;
border-left-width:1px;
border-color:#A8A6A7;
background -color:#fff;
}
.listeutilisateursalon,.listeconnexionsalon
{
display:none;
position:absolute;
height:5cm;
width:15cm;
overflow:auto;
z-index:1000;
margin-left:-500px;
border-style:solid;
border-top-width:1px;
border-right-width:2px;
border-bottom-width:2px;
border-left-width:1px;
border-color:#A8A6A7;
background -color:#fff;
}
NouspouvonsdésormaispasseraudéveloppementdelafonctionJavaScriptAjaxquipermetderetournerlalistedes
utilisateursconnectéspourlesalonindiquéetd
’
afficherlerésultatdanslabalise
<div/>
adaptée.
//fonction qui permet d ’afficher la liste des connexions au salon
function listeConnexionSalon(id_salon)
{
//fermer la boîte en cours
if($("#listeconnexionsalon_"+id_salon).css("display")=="block")
{
$("#listeconnexionsalon_"+id_salon).slideUp("slow");
return;
}
//fermer toutes les boîtes qui ont la classe css
$(".listeconnexionsalon").hide();
//détruire leur formulaire
$(".listeconnexionsalon").html("");
//récupérer la liste des connexions au salon indique
if(id_salon!=null)
{
//envoyer les donnees en POST
$.ajax(
{
type: "POST",
url: "listeconnexionsalon.do",
dataType: "html",
data: "id_salon="+id_salon,
timeout : 4000,
error: function(){
},
beforeSend : function()
{
},
success: function(html)
{
//mettre le résultat dans la balise <div/> concernee
$("#listeconnexionsalon_"+id_salon).html(html);
$("#listeconnexionsalon_"+id_salon).slideDown("slow");
}
});
}
}
- 45 -© ENI Editions - All rigths reserved
Ilneresteplusqu
’
àdévelopperl
’
actionavecsadéclaration,lemodèleetlavueJSP.
<!-- ===action qui permet de retourner la liste des connexions
pour un salon indiqué=== -->
<action
path="/listeconnexionsalon"
name="FormUtilisateur"
scope="request"
validate="false"
input="/accueil.do"
type="chatbetaboutique.GestionUtilisateurAction"
parameter="listeconnexionsalon"
>
<forward name="listeconnexionsalon"
path="/vues/utilisateur/listeconnexionsalon.jsp" redirect="false"/>
</action>
//afficher la liste des connexions pour le salon
public ActionForward listeconnexionsalon(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plug -in dans un attribut
présent dans le contexte de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//récupérer le salon en paramètre
String id_salon=(String)request.getParameter("id_salon");
//récupérer la liste de toutes les connexions pour ce salon
if(id_salon!=null && !id_salon.equals(""))
{
//retourner la liste des connexions
ArrayList listeconnexionsalon=
(ArrayList)utilisateurmodele.getListeConnexionSalon(id_salon);
//retourner la liste des connexions
request.setAttribute("listeconnexionsalon",listeconnexionsalon);
//retourner le numéro du salon
request.setAttribute("id_salon",id_salon);
//vider par sécurité
listeconnexionsalon=null;
//retourner sur la page d ’affichage des connexions
return mapping.findForward("listeconnexionsalon");
}
//en cas d ’erreur
return mapping.findForward("accueil");
}
/***********************************************************
* liste des connexions au salon
***********************************************************/
public ArrayList getListeConnexionSalon(String id_salon)
{
//statement
PreparedStatement requete=null;
try
{
//ouvrir une connexion
connection=ds.getConnection();
//enregistrements
requete=connection.prepareStatement("SELECT * FROM utilisateur,
utilisateursalon WHERE utilisateur.pseudonyme=utilisateursalon.pseudonyme
AND utilisateursalon.connecte=1 AND utilisateursalon.id_salon=? ORDER BY
utilisateur.pseudonyme,utilisateur.id_utilisateur");
- 46 - © ENI Editions - All rigths reserved
requete.setString(1,(String)id_salon);
rs=requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
//utilisation de la librairie qui permet de
transformer un ResultSet en Objet Java
BeanProcessor bp=new BeanProcessor();
listeutilisateur =
(ArrayList<Utilisateur>)bp.toBeanList(rs,Utilisateur.class);
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getListeConnexionSalon");
}
finally
{
try
{
//fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
UtilisateurModele.java fonction getListeConnexionSalon");
}
}
//retourner la liste des utilisateurs connectés
return listeutilisateur;
}
<%@ page import="java.util.ArrayList" %>
<%@ page import="boiteoutils.Utilisateur" %>
<%
//liste des connexions
ArrayList listeconnexionsalon=
(ArrayList)request.getAttribute("listeconnexionsalon");
//numéro du salon
String id_salon=(String)request.getAttribute("id_salon");
%>
<table border="0" id="tableaubordure" cellspacing="0" cellpadding="0">
<tr align="center" class="entetetableau">
<td>ID</td>
<td>Pseudonyme</td>
<td>Nom</td>
<td>Prénom</td>
</tr>
<%
for(int i=0;i<listeconnexionsalon.size();i++)
{
//récupérer l ’objet dans la liste
Utilisateur utilisateur=(Utilisateur)listeconnexionsalon.get(i);
if(i%2==0)out.println("<tr align=\"center\" class=\"ligneclaire\">");
else out.println("<tr align=\"center\" class=\"lignefoncee\">");
out.println("<td>"+utilisateur.getId_utilisateur()+"</td>");
out.println("<td>"+utilisateur.getPseudonyme()+"</td>");
out.println("<td>"+utilisateur.getNom()+"</td>");
out.println("<td>"+utilisateur.getPrenom()+"</td>");
out.println("</tr>");
}
%>
</table>
- 47 -© ENI Editions - All rigths reserved
Ceserviceestopérationnel,nousvoyonsparexemplequepourlesalond
’
identifiant5quiapourthèmeJava EE
,
l
’
utilisateurjlafossestactuellementconnecté.Cesystèmebienquetrèsutilen
’
estpasfonctionneletergonomique.En
effet,sil
’
utilisateursedéconnectedusalonetquel
’
administrateurn
’
actualisepassapageWeb,ilneverrapasle
départdel
’
utilisateur.Nousallonsdoncajouterdel
’
ergonomieàceserviceenutilisantunthreadJavaScriptquiesten
faitunobjettimer.Cetimervadéclencheràintervallesdetempsrégulierlafonctiondelistingpourprendreencompte
lesconnexionsetdéconnexionsutilisateurs.
//temps d ’attente entre deux rafraichissements
var tempsattente=1500;
//timer
var timer=null;
//fonction qui permet d ’afficher la liste des connexions au salon
function listeConnexionSalon(id_salon)
{
//détruire le timer
if(timer!=null)
{
clearTimeout(timer);
}
//fermer la boîte en cours
if($("#listeconnexionsalon_"+id_salon).css("display")=="block")
{
$("#listeconnexionsalon_"+id_salon).slideUp("slow");
return;
}
//fermer toutes les boîtes qui ont la classe css
$(".listeconnexionsalon").hide();
//détruire leur formulaire
$(".listeconnexionsalon").html("");
//declencher la fonction Ajax qui permet de retourner
la liste des connexions
getListeConnexionSalon(id_salon);
}
//recuperer la liste des connectes en Ajax
function getListeConnexionSalon(id_salon)
{
//récupérer la liste des connexions au salon indique
if(id_salon!=null)
{
//envoyer les donnees en POST
$.ajax(
{
type: "POST",
url: "listeconnexionsalon.do",
dataType: "html",
data: "id_salon="+id_salon,
timeout : 4000,
error: function(){
},
beforeSend : function()
{
},
success: function(html)
{
//mettre le résultat dans la balise <div/> concernee
$("#listeconnexionsalon_"+id_salon).html(html);
$("#listeconnexionsalon_"+id_salon).show();
}
});
}
//creer un Timer/thread qui va declencher la fonction par intervalle
- 48 - © ENI Editions - All rigths reserved
timer=setTimeout("getListeConnexionSalon("+id_salon+")",tempsattente);
}
LafonctionJavaScriptgetListeConnexionSalon()permetderécupéreravecl
’
objettimer,parintervallesréguliers,laliste
desutilisateursconnectésausalon.Cetobjetvadéclenchertoutesles1.5secondeslamêmefonctionpourrafraîchir
les connexions. Enfin, la fonction listeConnexionSalon() commence par détruire, s
’
il existe, l
’
objet timer lors des
fermeturesdesblocs
<div/>
.
Nouspouvonstesterceserviceenutilisantdeuxnavigateursetenaffichantunsalon.Danslepremiernavigateur,
nousvoyonslalistedesconnectésetdanslesecondnousréalisonsladéconnexionausalonavecPhpMyAdminpar
exemple,l
’
affichagedurésultatdoitêtrequasimentinstantané.
Le service d
’
administration de gestion du chat est maintenant terminé. Le paragraphe suivant de ce chapitre est
consacréauxtechnologiesWEB2.0etauxdifférentsservicesassociés.
- 49 -© ENI Editions - All rigths reserved
Web2.0
1.Présentation
Actuellement,lesprotagonistesdel
’
InternetparlentbeaucoupdetechnologiesWeb2.0.Derrièrecetermesecache
unensembledetechnologiesetd
’
outilsaxéssurl
’
ergonomiedesinterfaces.Letermeaétéinventéparunmembre
delasociétéO
’
Reillypourdésignerlesnouvellestechnologiesetlarenaissance du Web.Lebutestd
’
utiliserles
technologiesdel
’
Internettoutenserapprochantdesinterfaceshommemachineattractivesdeslogicielsinstalléssur
lesmachinespersonnelles.
LadéfinitionexacteduWeb2.0n
’
esttoujourspastrèsclaire,cependantilestadmisqu
’
unsiteWeb2.0possèdeles
caractéristiquessuivantes :
●Lasaisie,modificationetsuppressiondesinformationsdusitesontsimples.
●Lesiteesttotalementutilisableavecunnavigateurstandard.
●L
’
outilutilisedesstandardsdetechnologie.
Dupointdevuetechnologique,l
’
infrastructureWeb2.0estassezcomplexe,elleinclutàlafoisleoulesserveur(s),la
syndication (accessibilité partagée) de contenu, la messagerie et les applications. Un site Web 2.0 est désigné
commetels
’
ilutiliselestechnologiessuivantes :
●LesfeuillesdestyleCSS.
●LespagesXHTML.
●LasyndicationRSS(usagedesdonnéesdusitedansunautrecontexte).
●L
’
utilisationd
’
URLspécifiquespourleréférencement.
●UneApplicationInternetRiche(RichInternetApplication)utilisantlatechnologieAjax.
●L
’
étiquetage(métadonnées :utilisationdemots
clésounuagesdemots
cléspourlesrecherchesdansun
contenu,lebutétantd
’
interconnecterlesélémentsentreeux).
●Utilisationdel
’
approcheHTTPetSOAP.
Iln
’
existepasd
’
accordunanimesurladéfinitionetlesensduWeb2.0,letermepeutainsidésignerdesconcepts
radicalement différents suivant les personnes. Par exemple, certains associent le terme Web 2.0 pour des sites
XHTMLvalidesetbienformés.D
’
autresparlentdeWeb2.0avecl
’
utilisationabusived
’
Ajaxquipeutrendrelespages
Webparticulièrementlonguesauchargement.Pourrésumer,lestechnologiesquisecachentderrièrecetermesont
surtoutJavaScript,DHTMLetAjaxavecunrespectdesstandardsetducodevalidesyntaxiquement.
Dans cette partie du cours nous allons aborder quelques services Web 2.0 pour faire évoluer notre système de
gestionduchatBetaBoutique.
2.Tableauxredimensionnables
IlestparfoistrèsutiledebénéficierdetableauxredimensionnablesenDHTMLavecl
’
utilisationdelasouris.Nous
allons mettre en place ce service avec l
’
utilisation d
’
un plug
in pour la bibliothèque JQuery. Ce plug
in nommé
gridcolumnsizing,nécessitel
’
utilisationdelalibrairiedebaseJQueryetfonctionnesurlestableauxHTML.
Nous commençons notre mise en place en copiant les librairies du plug
in gridcolumnsizing dans le
répertoire
/javascript/jquery/plugin
denotreapplication.
Ensuite,nousdevonsinstallerceslibrairiesdansnotrepaged
’
en
têteafind
’
inclurelesfichiers.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
- 1 -© ENI Editions - All rigths reserved
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html>
<head>
<title>Administration - BetaBoutique</title>
<meta http -equiv="Content -Type" content="text/html; charset=iso -8859-1">
<meta name="description" content="BetaBoutique">
<!-- feuilles de style -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
<link rel="stylesheet" type="text/css" href="css/styles_admin.css"
title="defaut" />
<!-- librairie personnelle -->
<script type="text/javascript" src="javascript/boiteoutils.js"></script>
<!-- librairie JQuery -->
<script type="text/javascript" src="javascript/jquery/jquery.js"></script>
<!-- plug-in pour les redimensionnements de colonnes des tableaux -->
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.dimensions.pack.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.cookies.pack.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.iutil.pack.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.idrag.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.grid.columnSizing.pack.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
gridcolumnsizing/jquery.tabs.pack.js"></script>
</head>
<body>
LamiseenplacenécessiteladéclarationduserviceWeb2.0enJavaScriptdanslapaged
’
en
têteparexemple.
<!-- outil de redimensionnement -->
<script>
$(document).ready(function(){
//pour les en -tetes de colonnes des tableaux
$("#tableaubordure").columnSizing({
viewGhost : true,
viewResize : true,
opacity : 0.5,
dleft : 0,
dtop : 0,
title : "Redimensionner la colonne",
minWidth : 50,
tableWidthFixed : false,
cookies : false,
cssHandler :
{
position: "relative",
right:" -3px",
float:"right",
height:"20px",
cursor:"col -resize",
borderRight:"2px solid #fff",
marginRight:"2px",
borderLeft:"1px solid #555",
},
cssDragLine :
{
borderRight:"4px solid #777",
cursor:"col -resize"
},
cssDragArea :
{
border:"1px solid #777",
backgroundColor:"#eee",
cursor:"col -resize"
- 2 - © ENI Editions - All rigths reserved
}
});
});
</script>
Le script précédent est simple, il permet d
’
appliquer au chargement de l
’
application, à chaque balise HTML qui
possèdeunidentifiantnommétableaubordure,leservicederedimensionnementaveclesstylesCSS.Désormais,notre
tableau qui permet d
’
afficher la liste des utilisateurs est initialisé avec le redimensionnement automatique. Nous
pouvonstesternotreserviceendéplaçantlasourissurlesdifférentescolonnes.
...
<table border="0" id="tableaubordure" cellspacing="0" cellpadding="0">
...
3.Champsredimensionnables
DelamêmefaçonquenousavonsutiliséunservicederedimensionnementpourlestableauxHTML,nouspouvons
gérer les champs des formulaires. Pour cela nous utilisons le plug
in JQuery nomméresizehandle qui permet des
redimensionnementssurleschampsdetype
<textarea.../>
et
<input.../>
.
Nous commençons la mise en place de ce service avec la copie des librairies dans le
répertoire
/javascript/jquery/plugin/resizer
. Cette librairie contient deux scripts JavaScript (pour la gestion de
l
’
horizontaleetdelaverticale),deuximagesetunefeuilledestyleCSS.
Nousincluonscesfichiersdanslapage/vues/outils/entete.jspf
.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html>
<head>
...
<!-- pour les redimensionnements -->
<script type="text/javascript" src="javascript/jquery/plugin/
resizer/resizehandle.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
resizer/resizehandleH.js"></script>
</head>
<body>
...
L
’
installation est quasiment terminée, il ne reste plus qu
’
à déclarer les champs de la page qui seront
redimensionnablesetàinsérerlafeuilledestyleCSS.
<!-- feuilles de style pour resizer -->
<link rel="stylesheet" type="text/css"
href="javascript/jquery/plugin/resizer/resize.css" />
<!-- outil de redimensionnement -->
<script>
$(document).ready(function(){
...
//redimension forcee a l ’horizontale
$(".input").resizehandleH(98,350);
//textearea redimensionnable
$("textarea").resizehandle();
});
- 3 -© ENI Editions - All rigths reserved
</script>
Dans cette déclaration, tous les champs qui sont associés à la classe CSS input auront le service de
redimensionnementhorizontalavec98pixelsauminimumet350pixelsaumaximum.Enfin,chaquechampdetype
<textarea>
seraredimensionnableverticalement.
4.Bullesd
’
aide
Il est assez courant d
’
avoir besoin de créer des textes, images, boutons ou autre avec des bulles d
’
aide. Nous
pouvonscréerceciavecdesstylesCSSdefaçonsimple.
a.info
{
position:relative;
text-decoration:none
}
a.info:hover
{
color:#BEBCBC;
z-index:10;
cursor:help;
}
a.info span
{
display:none;
z-index:10;
}
a.info:hover span
{
display:block;
position:absolute;
z-index:10;
top:2em;
left:-130px;
width:220px;
border-style:solid;
border-left-width:1px;
border-top-width:1px;
border-right-width:2px;
border-bottom-width:2px;
border-top-color:#999999;
border-left-color:#999999;
border-right-color:#666666;
border-bottom-color:#666666;
padding-left:10px;
padding-right:10px;
padding-top:3px;
- 4 - © ENI Editions - All rigths reserved
padding-bottom:5px;
margin-left:10px;
background -color:#F6F6F6;
font-family:tahoma, verdana, arial, sans -serif;
font-size:11px;
color:#686667;
font-weight:normal;
text-align:left;
}
CettedéfinitiondestylespermetdedéclareruneclassenomméeinfoquiseraintégréedansunlienHTML.Chaque
balise
<ahref=
’’’’
...>
quicontientunélémentaveclaclasseinfopourrabénéficierdebullesd
’
aide.Nouspouvonspar
exempleajouterunebulled
’
aidesurchaqueliendumenudenavigation.
<table border="0" cellspacing="0" cellpadding="0" name="menugauche"
id="menugauche" style="background: url( ’img/fondmenu.gif ’)
repeat-x;" height="500">
<tr>
<td width="100%" valign="top"><br/><span style="margin -left:
30px;"><b>ADMINISTRATION</b></span>
<ul class="menucategorie">
<li><a href="etatserveur.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/><span>Cliquez ici
pour afficher le service de gestion du serveur</span> Gestion
du serveur</a></li>
<li><a href="listeutilisateur.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/><span>Cliquez ici
pour afficher le service de gestion des utilisateurs</span> Gestion
des utilisateurs</a></li>
</ul>
</td>
</tr>
</table>
Lelienpossèdelaclasseinfo,toutcequiserainclusàlasuitedecetteclassedansunebalise
<span>
seraalors
considérécommeletextedel
’
aideetseraalorsdéclenchéausurvoldelasourisutilisateur.
5.Menucontextuel
Ilestparfoisutiled
’
utiliserunmenucontextuelpourgérerlesclicsdroitssurdesimages(pourafficheruncopyright)
ouliens(pourafficherunmenud
’
actions).Leplug
incontextmenudeJQuerypermetdegérercesclicsetd
’
afficherun
contenu adapté en conséquence. Comme d
’
habitude nous copions d
’
abord les librairies dans le répertoire
adapté/javascript/jquery/plugin/contextmenu. Ensuite, nous insérons leslibrairies dansla paged
’
en
têtedenotre
projet.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
- 5 -© ENI Editions - All rigths reserved
<html>
<head>
<title>Administration - BetaBoutique</title>
...
<!-- pour les clics droits -->
<script type="text/javascript" src="javascript/jquery/plugin/
contextmenu/jquery.contextmenu.r2.packed.js"></script>
<script type="text/javascript" src="javascript/jquery/plugin/
contextmenu/contextmenu.js"></script>
</head>
<body>
...
Maintenant, nous pouvons coder la partie JavaScript pour la gestion du clic droit. Le
fichier/javascript/jquery/plugin/contextmenu/contextmenu.jspossèdelecodesuivant :
//au chargement de la page
$(document).ready(function(){
//declencher la fonction pour la gestion du clic droit
gestionClicDroit();
});
//pour le style du menu contextuel
$.contextMenu.defaults({
menuStyle : {
width : "110px",
},
shadow: true
});
//gerer les clics droits sur les liens de contexte
function gestionClicDroit()
{
$( ’.contextmenu ’).contextMenu( ’clicdroit ’, {
bindings: {
’consulter ’: function(t) {
document.location.href=
"consulterutilisateur.do?id_utilisateur="+t.id;
},
’modifier ’: function(t) {
document.location.href=
"modifierutilisateur.do?id_utilisateur="+t.id;
},
}
});
}
Cecodepermetdelancerlaméthode
gestionClicDroit()
aulancementdel
’
application.Cetteméthodepermetd
’
ajouter
unmenucontextuelsurchaquebaliseHTMLquiutiliselaclassecontextmenu.Cettefonctionestassociéeàunbloc
d
’
affichagenommé
clicdroit
.Ensuite,deuxactionssontdéfiniesconsulteretmodifier.Pouraméliorerl
’
ergonomie,nous
pouvons ajouter le style CSS suivant avec la classe contextmenu qui permet d
’
afficher l
’
icône d
’
aide (le point
d
’
interrogation)lorsdessurvolsdelasouris.
.contextmenu
{
cursor:help;
}
Lesstylessontinsérésainsiquelecodedegestion,ilrestemaintenantàcoderl
’
apparencedelaboîtedumenu
contextuel.Lapagelisteutilisateur.jspestdétailléeci
après :
- 6 - © ENI Editions - All rigths reserved
<%
...
out.println("<td><span class=\"contextmenu\"
id=\""+utilisateur.getId_utilisateur()+"\">"+utilisateur.getPseudonyme()
+"</span></td>");
...
%>
</table>
<%@ include file="../outils/piedpage.jspf" %>
<!-- gestion du clic droit -->
<div class="contextMenu" id="clicdroit" style="display:none">
<ul>
<li id="consulter"><img src="img/consulter.png"/> Consulter</li>
<li id="modifier"><img src="img/modifier.png"/> Modifier
</li>
</ul>
</div>
Nousremarquonsque labaliseHTML
<span>
associéeàlaclasse contextmenupermet degérerlemenuquiest
inséréetcachéenbasdepage.Leparamètre
id
delabalise
<span>
estliéauparamètre
t.id
dufichierJavaScriptafin
derécupérerl
’
identifiantàtraiter.
’consulter ’: function(t) {
document.location.href=
"consulterutilisateur.do?id_utilisateur="+t.id;
},
Leserviceestdésormaisdisponible.Nouspouvonstesterl
’
utilisationdesliensconsulteret
modifier
pourunutilisateur
donné,enréalisantunclicdroitsursonpseudonyme.
6.Lesarrondis
AveclamontéeenpuissancedestechnologiesWeb2.0,ilyadeplusenplusdesitesquiutilisentdesarrondis.Le
plug
incornerdelalibrairieJQuerypermetdegérerdesarrondissurdesblocsHTMLdetype
<div>
.L
’
utilisationde
cette librairie est identique à celle des étapes précédentes. Nous commençons par copier le répertoire
adapté
/javascript/jquery/plugin/corner
. Ensuite, nous déclarons le script JavaScript dans notre page d
’
en
têteafin
d
’
utiliserceservice.
<%@ taglib uri="http://struts.apache.org/tags -bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags -html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags -logic" prefix="logic" %>
<html>
<head>
<title>Administration - BetaBoutique</title>
...
<!-- pour les arrondis -->
<script type="text/javascript" src="javascript/jquery/plugin/
corner/jquery.corner.js"></script>
</head>
<body>
NouspouvonsensuiteajouterdesarrondissurdesblocsparsimpleJavaScript.Pourcela,nousallonsinsérerun
arrondisurletableauHTMLquiacommeidtableaubordure
.
<script>
- 7 -© ENI Editions - All rigths reserved
$(document).ready(function(){
...
//coins arrondis
$("#tableaubordure").corner();
});
</script>
7.Aidedynamiqueetremplissagesgénériques
Lorsdudéveloppementd
’
unsiteInternetmêmebasique,ilesttoujoursnécessaired
’
avoirdespagesd
’
aidepour
bénéficierd
’
informationstoutaulongdelanavigation.LaréalisationdespagesHTMLestsouventtrèslongueet
laborieuse.Demême,lesmisesàjourdetextesstatiquessonttrèsraresétantdonnéquelesutilisateursnesont
pas des développeurs HTML. Pour éviter cela, il est souvent très utile de développer un système modulable et
génériquepourlagestiondespagesd
’
aide.Demême,ilexistetoujoursdespagesstatiquesdansunsiteInternet
pourlesconditionsdevente,lesinformationsdiverses,lesfraisdeportoulesexplicationssurl
’
entreprise.Ilestalors
toutàfaitpossibledecréerunservicederemplissageavecducontenuXHTMLquipourraêtremisàjourparlasuite.
Nousallonscréerceserviced
’
aideetderemplissagegénériquesurlemêmeprincipequelagestiondesutilisateurs
oudessalons.NouscommençonsledéveloppementdeceserviceaveclecodagedelaclasseAideRemplissage
.
package boiteoutils;
import java.io.Serializable;
public class AideRemplissage implements Serializable
{
//java 5.0 pour avoir un identifiant unique de la sérialisation
private static final long serialVersionUID = 1L;
//variables de classe
private String aideremplissage=null;
private String contenu=null;
/***********************************************************
***********************************************************/
public AideRemplissage() {
}
public String getAideremplissage() {
return aideremplissage;
}
public void setAideremplissage(String aideremplissage) {
this.aideremplissage = aideremplissage;
}
public String getContenu() {
return contenu;
}
public void setContenu(String contenu) {
this.contenu = contenu;
}
//fin de la classe
}
Lastructuredelatableaideremplissageestlasuivante :lechampaideremplissageestutilisécommecléetlechamp
- 8 - © ENI Editions - All rigths reserved
contenuseracomposédetextebrutouXHTML.
Ledéveloppementn
’
estpasdétailléici,laconceptionesttoujourslamêmeavecladéclarationdesactionsdansle
fichier de configuration struts
config.xml
, le codage de la classe composée des actions
chatbetaboutique.GestionAideRemplissage, le codage du modèle chatbetaboutique.AideRemplissageModele et les
différentesvuesprésentesdanslerépertoire/vues/aideremplissage/
.
Lapartieadministrationdespagesd
’
aideetderemplissageestréalisée.Nouspouvonspasseraudéveloppement
d
’
unepageJSPnommée/vues/outils/aideremplissage.jspquipermetd
’
afficheruncontenudynamiqueàl
’
aidedela
fonctiongetContenuAideRemplissage()dumodèle.
<%@ page contentType="text/html;charset=UTF -8" %>
<%@ page import="javax.sql.DataSource" %>
<%@ page import="modele.AideRemplissageModele" %>
<%
//récupérer la datasource du plug -in dans un attribut
présent dans le context de la servlet
DataSource ds=(DataSource)this.getServletContext().getAttribute
("datasource");
//créer le modèle
AideRemplissageModele aideremplissagemodele=new AideRemplissageModele(ds);
//fermer la datasource
ds=null;
//récupérer la page d ’aide ou de remplissage de manière dynamique
StringBuffer
contenu=aideremplissagemodele.getContenuAideRemplissage
((String)request.getParameter("aideremplissage"));
%>
<html>
<head>
<title>Aide Remplissage</title>
</head>
<body>
<%= contenu %>
</body>
</html>
/***********************************************************
* récupérer une page d ’aide ou de remplissage
***********************************************************/
public StringBuffer getContenuAideRemplissage(String aideremplissage)
{
PreparedStatement requete=null;
//contenu de l ’aide ou du remplissage
StringBuffer contenu=new StringBuffer();
//récupérer la page d ’aide ou de remplissage indiquée
if(aideremplissage!=null && !aideremplissage.equalsIgnoreCase(""))
{
try
{
//ouvrir une connexion
connection=ds.getConnection();
- 9 -© ENI Editions - All rigths reserved
requete=connection.prepareStatement("SELECT contenu
FROM aideremplissage WHERE aideremplissage.aideremplissage=?");
requete.setString(1,(String)aideremplissage);
rs = requete.executeQuery();
//exécuter la requête
if(rs!=null)
{
if(rs.next())
{
//récupérer la aide
if(rs.getString("contenu")==null)contenu.append("");
else contenu.append(rs.getString("contenu"));
}
}
}
catch(Exception e)
{
System.out.println("Erreur dans la classe
AideRemplissageModele.java fonction getContenuAideRemplissage");
}
finally
{
try
{
fermer la connexion
if(rs!=null)OutilsBaseDeDonnees.fermerConnexion(rs);
if(requete!=null)OutilsBaseDeDonnees.fermerConnexion
(requete);
if(connection!=null)OutilsBaseDeDonnees.fermerConnexion
(connection);
}
catch(Exception ex)
{
System.out.println("Erreur dans la classe
AideRemplissageModele.java fonction getContenuAideRemplissage");
}
}
}
//retourner le contenu de l ’aide ou du remplissage
return contenu;
}
Le plug
in JTip de la librairie JQuery permet d
’
afficher un contenu dynamique à partir d
’
un système générique et
ergonomique.Lalibrairieestcopiéedanslerépertoire
/javascript/jquery/plugin/jtip
.L
’
inclusiondecettelibrairieest
réaliséedanslefichierd
’
en
tête/vues/outils/entete.jspf
.
<!-- pour l’aide et les remplissages -->
<script type="text/javascript"
src="javascript/jquery/plugin/jtip/jtip.js"></script>
<link type="text/css" rel="stylesheet" media="all"
href="javascript/jquery/plugin/jtip/jtip.css"/>
Leserviceestopérationnel,nouspouvonsmodifiernotrepagedenavigation/vues/outils/navigation.jspfafind
’
utiliser
uneaidedynamique.
...
<li><a href="listeutilisateur.do" class="info"><img
src="img/fleche.gif" border="0" align="absmiddle"/> Gestion
des utilisateurs</a><a href="vues/outils/aideremplissage.jsp?
aideremplissage=aideutilisateur&width=400" class="jTip"
id="aidedynamique" name="Aide et Remplissage"><img
src="img/aide.gif" align="absmiddle" border="0"
style="margin -left:20px;"/></a>
</li>
...
Laclasse
jTip
utiliséesurunlienpermetd
’
activerlemoduleavecenparamètres,lenomdel
’
aideouduremplissageà
afficher(aideutilisateur),latailledefenêtre(width=400
),l
’
iddelafenêtre(aidedynamique)etletitrequivaapparaître
- 10 - © ENI Editions - All rigths reserved
danslafenêtreavecleparamètrename
.
Nouspouvonsainsiajouteraussibienenpartiefront
officequ
’
enadministrationdesicônesd
’
aidedynamique,dontle
contenuestprésentdansunebasededonnées.
8.Éditeurdetexteévolué
LatechnologieDHTMLpermetactuellementd
’
utiliserdeséditeursdetexteriche,enanglaisRTE(RichTextEditor).Ces
objetsJavaScriptsontplusoumoinslourds,biendéveloppésetutiles.
LeRTETinyMCEestunéditeurdetypebarred
’
outils100%XHTML/XMLquipermetderéaliserlesdifférentesétapes
demiseenformedansdespagesHTML.http://tinymce.moxiecode.com/.
Nous pouvons installer cet éditeur pour gérer les contenus des pages d
’
aide ou de remplissage. Nous copions
l
’
archive tinymce dans le répertoire /javascript de l
’
application. Ensuite, nous éditons les fichiers de
création /vues/aideremplissage/creeraideremplissag.jsp et de
modification/vues/aideremplissage/modifieraideremplissage.jsppuisnousajoutonslecodeadaptépourl
’
inclusionde
l
’
éditeurDHTMLafindegérerlechampdetypetextareadelapageenmodeRTE.
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<!-- inclure l ’éditeur syntaxique -->
<script language="javascript" type="text/javascript"
src="javascript/tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
<script language="javascript" type="text/javascript">
tinyMCE.init({
mode : "exact",
elements : "aideremplissage.contenu",
theme : "advanced",
language : "fr",
plugins : "table,save,advhr,advimage,advlink,emotions,
iespell,insertdatetime,preview,zoom,flash,searchreplace,print,
contextmenu,paste,directionality,fullscreen",
_advanced_buttons1_add_before : "save,newdocument,separator",
theme_advanced_buttons2_add : "separator,insertdate,
inserttime,preview,zoom,separator,forecolor,backcolor",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
content_css : "css/styles_admin.css",
plugi2n_insertdate_dateFormat : "%d -%m-%Y",
plugi2n_insertdate_timeFormat : "%H:%M:%S",
theme_advanced_resizing : true,
theme_advanced_resize_horizontal : false,
paste_auto_cleanup_on_paste : true,
entities : "",
entity_encoding : "numeric"
});
</script>
<br/><div class="titre"><a href="listeaideremplissage.do"><img
src="img/aideremplissage.jpg" border="0" align="absmiddle"/> Liste
des aides et remplissages</a></div>
- 11 -© ENI Editions - All rigths reserved
<html:form action="/validercreeraideremplissage" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
<tr><td>Aideremplissage</td><td><html:text
property="aideremplissage.aideremplissage"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td valign="top">Contenu</td><td><html:textarea
property="aideremplissage.contenu"/></td></tr>
<tr><td align="center" colspan="2"><html:submit value="Envoyer"
styleClass="bouton"/></td></tr>
</table>
</html:form>
<!-- inclure le pied de page -->
<jsp:include page="../outils/piedpage.jsp" />
Lamiseenplaceestsimple,ilsuffitd
’
inclureleslibrairiesetdedonnerlemêmenomàl
’
éditeur(attributelements
)
qu
’
au champ de type
<textarea/>
(attribut name) qui va recevoir celui
ci. Il est aussi possible d
’
utiliser plusieurs
optionsdel
’
éditeurcommeletypedebarred
’
outils,lestyledel
’
éditeur,lesplug
insutilisés,l
’
encodagedesentités
XML,letyped
’
encodageetsurtout,lierl
’
éditeurànotrefeuilledestyleavecleparamètrecontent_css.Aveccette
optionnouspourronsainsiappliquernospropresstylesaucontenu.
Lalibrairiedel
’
éditeurn
’
estpasinclusedemanièreglobaledansl
’
en
têteJSPFdenotreprojetcommepour
lesautresJavaScriptcarcettebibliothèqueestlourde.Iln
’
estdoncpasnécessaired
’
inclurepartoutcette
bibliothèquealorsqu
’
elleserauniquementutiliséedansleformulairedecréationetdemodificationducontenu
d
’
uneaideoud
’
unremplissage.
Nouspouvonsmaintenantréaliseruneaidecomplexeavecdesstylesoudesimagesetdisposerainsid
’
uncontenu
richeinséréavecunéditeur100%XHTML.
- 12 - © ENI Editions - All rigths reserved
9.Dateetcalendrier
Lagestiondeschampsdetypedateestparfoiscontraignantedufaitdesdifférentessyntaxesutilisées.Eneffet,
certains utilisateurs vont saisir jj/mm/aaaa,d
’
autres
jj
mm
aaaa
ouencore
aaaa
mm
jj
. L
’
utilisationd
’
uncalendrier
DHTML est donc plus souple et ergonomique. La librairie calendar (http://www.dynarch.com/projects/calendar/)
permet d
’
utiliser plusieurs formulaires de type date au sein de la même page HTML. Nous allons procéder à
l
’
installationdelalibrairie
/javascript/calendar
ainsiqu
’
àl
’
inclusiondesfichiersdanslapage/vues/outils/entete.jspf
.
<!-- pour les calendriers -->
<link rel="stylesheet" type="text/css" media="all"
href="javascript/calendar/calendar -win2k-cold-1.css">
<script type="text/javascript"
src="javascript/calendar/calendar.js"></script>
<script type="text/javascript"
src="javascript/calendar/calendar -fr.js"></script>
<script type="text/javascript"
src="javascript/calendar/calendar -setup.js"></script>
Nous allons ensuite installer un calendrier dynamique sur le champ date du formulaire de
création/vues/salon/creersalon.jspetdemodificationd
’
unsalon/vues/salon/modifiersalon.jsp
.
<!-- inclure l ’en-tete de la page -->
<%@ include file="../outils/entete.jspf" %>
<br/><div class="titre"><a href="listesalon.do"><img
src="img/salon.jpg" border="0" align="absmiddle"/> Liste
des salons</a></div>
<html:form action="/validercreersalon" method="POST">
<table border="0" id="tableau" cellpadding="0" cellspacing="0" width="50%">
<tr><td>Thème</td><td><html:text property="salon.theme"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Date</td><td><html:text styleId="salon.date" property="salon.date"
styleClass="input" errorStyleClass="inputerreur"/></td></tr>
<tr><td>Actif</td><td><html:text property="salon.actif" styleClass="input"
errorStyleClass="inputerreur"/></td></tr>
<tr><td align="center" colspan="2"><html:submit value="Envoyer"
styleClass="bouton"/></td></tr>
</table>
</html:form>
<script type="text/javascript">
Calendar.setup({
inputField : "salon.date", // id of the input field
ifFormat : "%Y -%m-%d %H:%M:%S", //
format of the input field
showsTime : "true",
timeFormat : "24"
});
</script>
<!-- inclure le pied de page -->
<jsp:include page="../outils/piedpage.jsp" />
- 13 -© ENI Editions - All rigths reserved
L
’
identifiantduchampestpréciséavecl
’
attributstyleIDetlecodeJavaScriptassocié(inputField).Leformatdeladate
estpréciséavecleparamètreifFormat.Dansnotrecas,nousutilisonsleformatTimeStamppourêtreenaccordavec
notretypedanslabasededonnées.Pourutiliserplusieurscalendriersdanslamêmepage,ilsuffitderéaliserautant
deportionsdecodeJavaScriptquedecalendrier.
L
’
utilisationducalendriernécessitelaprésencedelabalise
id
danslechampHTML.Avecl
’
utilisationdela
taglibStruts,ilestdoncnécessairedepréciserceparamètreavecl
’
attributstyleID
.
10.Effetsd
’
attente
Ilestparfoisnécessairederéaliserdeseffetsd
’
attentesurdesformulairesAjaxafind
’
indiquerunchargement,une
modificationducontenuouautre.Nousallonsajouteruneffetd
’
attentesurleformulairedegestiondesconnexions
utilisateursauxsalons.Pourrappel,lafonctionJavaScriptlisteConnexionSalon(),présentedanslefichierboiteoutils.js
,
permetderécupérerenAjaxtouteslesXsecondeslalistedesutilisateursactuellementconnectés.
NousallonsmodifierlafonctionJavaScriptgetListeConnexionSalon()afinderéaliseruneanimationd
’
attente.Pourcela,
nousutilisonslafonction
beforeSend()
afindeplaceruneanimationd
’
attentedanslabalise
<div/>
concernéeavecun
fondnoir,uneffetdetransparenceetuneanimation.Ensuite,nousréalisonsl
’
opérationinversedanslafonction
success()quiestexécutéeàlafindel
’
opération.Nousaugmentons égalementletemps derafraîchissementà 4
secondes.
//recuperer la liste des connectes en Ajax
function getListeConnexionSalon(id_salon)
{
//récupérer la liste des connexions au salon indique
if(id_salon!=null)
{
//envoyer les donnees en POST
$.ajax(
{
type: "POST",
url: "listeconnexionsalon.do",
dataType: "html",
data: "id_salon="+id_salon,
timeout : 4000,
error: function(){
},
beforeSend : function()
{
//effet d ’ouverture noire
$("#listeconnexionsalon_"+id_salon).css("background -color","#000");
$("#listeconnexionsalon_"+id_salon).fadeTo("slow",0.33);
//animation d ’attente
$("#listeconnexionsalon_"+id_salon).html( ’<img
- 14 - © ENI Editions - All rigths reserved
src="img/ajax -loader.gif" style="margin -top:50px;"/> ’);
},
success: function(html)
{
//effet de fermeture noire
$("#listeconnexionsalon_"+id_salon).css("background -color",
"#E8E8E8");
$("#listeconnexionsalon_"+id_salon).fadeTo("slow",1);
//mettre le résultat dans la balise <div/> concernee
$("#listeconnexionsalon_"+id_salon).html(html);
$("#listeconnexionsalon_"+id_salon).show();
}
});
}
//creer un Timer/thread qui va declencher la fonction par intervalle
timer=setTimeout("getListeConnexionSalon("+id_salon+")",tempsattente);
}
Nousremarquonsl
’
affichaged
’
uneimageaniméeauformat.gifpendantlechargementdesdonnéesenAjax.
11.Feuillesdestyledynamiques
Parfois,ilestnécessairedeproposersurunsiteplusieurstaillesdepolices,différentescouleursouencoremieux,
deux compositions graphiques différentes. Actuellement avec le langage JavaScript, il est possible de changer de
feuilledestyleàlavolée.NousallonsutiliserpourcelalalibrairieJavaScript/javascript/styleswitcher
.
<!-- pour les feuilles de style -->
<script type="text/javascript" src="javascript/styleswitcher.js"></script>
Lorsdeladéclarationd
’
unefeuilledestyle,leparamètretitlepermetdedonnerunnomàlafeuilleetainsidela
référencer.
<!-- feuilles de style -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
<link rel="alternate stylesheet" type="text/css" href="css/tableau.css"
title="tableau" />
Ladéclarationci
dessuspermetd
’
insérerunefeuilledestylepardéfautnomméestyles.css.Parcontre,ilexisteune
secondefeuilledestylenomméetableauquiserachargéeencasdebesoin(
rel=
’’
alternatestylesheet
’’
).
Danscette
feuille de style, nous modifions uniquement la classe tableaubordure pour proposer une autre apparence. Nous
pourrions changer l
’
image de fond, la taille de toutes les polices, réaliser une mise en forme différente pour
l
’
impressionoumodifierlatotalitédesstyles.
#tableaubordure{
margin-top:2px;
border-style:solid;
border-width:1px;
border-color:#000;
- 15 -© ENI Editions - All rigths reserved
background -color:#fff;
color:#000;
font-family:tahoma, verdana, arial, sans -serif;
font-size:12px;
font-weight:bold;
width:98%;
}
#tableaubordure td{
padding-top:3px;
padding-right:3px;
padding-bottom:3px;
padding-left:3px;
}
Nousterminonsparl
’
ajoutdeliensdynamiquesdanslapaged
’
en
tête/vues/outils/entete.jspf
.
<ul>
<li><a href="JavaScript:chargerFeuilleStylesCss( ’defaut’);">Charger
la feuille de style par défaut</a></li>
<li><a href="JavaScript:chargerFeuilleStylesCss( ’tableau’);">Charger
la feuille de style pour les tableaux</a></li>
</ul>
Enfin,lecodedelafonctionJavaScriptchargerFeuilleStylesCss()présentedanslefichier/javascript/boiteoutils.jsest
trèssimple.
//changeant dynamiquement de feuille de style
function chargerFeuilleStylesCss(nom)
{
if(nom!=null && nom!="")
{
setActiveStyleSheet(nom);
}
}
IlexisteégalementunefeuilledestylequiestutiliséepardéfautpourlesimpressionsenCSS.Cettefeuilledestyle
estchargéelors desimpressionsou desaperçusavant impression.Cettefeuille estdistinguéepar leparamètre
media
quiapourvaleurprint
.
Nous allons réaliser une feuille de style pour l
’
impression (impressions.css) par simple copier
coller de la feuille
principalestyles.cssetenmodifiantl
’
apparencedublocen
tête.
<!-- feuilles de style -->
<link rel="stylesheet" type="text/css" href="css/styles.css"
title="defaut" />
<link rel="alternate stylesheet" type="text/css"
href="css/tableau.css" title="tableau" />
<link rel="stylesheet" type="text/css" href="css/impression.css"
media="print" />
#en-tete
{
height:150px;
border-style:solid;
border-left-width:0px;
- 16 - © ENI Editions - All rigths reserved
border-top-width:0px;
border-right-width:0px;
border-bottom-width:1px;
background -color:#FFF;
align:left;
display:none;
}
Cetexemplesimplepermetdecacherleblocd
’
en
têteàl
’
impressionavecladirectivedisplay:none.Ilestpossiblede
modifierlatailledespolices,lescouleurs,l
’
apparencedesblocs,leurspositions...Siunaperçuavantimpressionest
réalisé avec un navigateur, c
’
est la feuille de style impression.css qui va se charger automatiquement et qui va
enleverleblocd
’
en
tête,doncl
’
imageduhautdelapartieadministration.
CettetechniqueesttrèslargementutiliséesurInternetpourimprimerdesfichesarticles,desmodesd
’
emploi,des
conditionsdeventeouautre.
Lavued
’
écranprésentel
’
affichagedusiteaveclafeuilledestyleimpression.csslorsd
’
unaperçuavantimpression.
- 17 -© ENI Editions - All rigths reserved
Enrésumé
Cechapitre aprésentéle frameworkJava EEStruts et samise enplace danslecadre duprojet BetaBoutique.La
premièrepartieaprésentélesintérêtsdel
’
utilisation d
’
unframeworkdanslecadredudéveloppementd
’
unprojet
d
’
entreprise.
Dansundeuxièmetemps,leguideaprésentéendétailleframeworkStrutsavecsonmodèle,etsoninstallation.
La troisième partie a concerné le développement du système d
’
administration du chat BetaBoutique au travers
d
’
exemplesconcrets.
La quatrième partie a détaillé l
’
utilisation des formulaires en Struts avec les JavaBean de formulaires, le fichier de
configurationstruts
config.xml
etlesactionsassociées.
LeparagraphesuivantaintroduitlagestiondesvuesenStrutsaveclestaglibsspécifiques,l
’
accèsauxdonnéesetles
accèsmultilingues.
LasixièmepartieaconcernélavalidationdesdonnéesquiresteunélémentessentieldesdéveloppementsInternet.
Pourcela,Strutsfournitunensembledesolutionssouplesetsimplesàmettreenoeuvreaussibienducôtéserveur
queducôtéclient.
LapartiesuivanteaprésentéendétaillecontrôleurStruts,lesclassesActionsetlesméthodesassociées.
La septième partie a été consacrée au développement du module d
’
administration BetaBoutique. Tout le
développementfutdétaillédelamiseenplacedupooldeconnexionàlasourcededonnéesenpassantparlecodage
desactions,vuesetmodèles,jusqu
’
auxoptimisationsJavaScript.
LedernierparagrapheaétéconsacréauxtechniquesWeb2.0etàl
’
ergonomiequisontactuellementutiliséessur
Internet. Nous avons retrouvé ainsi les différentes techniques qui permettent de rendre un site plus agréable et
professionnel.
- 1 -© ENI Editions - All rigths reserved
Gestiondestracesetdeslogs
1.Présentation
Laconceptiondeprojetsnécessitelamiseenplacedetraceslorsdesdéveloppements,débogagesmaisaussien
phasedeproduction.LaplupartdesprojetsutilisentdesAPIdejournalisationréaliséeseninterneetplusoumoins
performantes.Lesprogrammeursplacentdestracesdanslecodeenutilisantlasortiestandardetlaclassestatique
Systemaumoyenducodesuivant :
System.out.println("Une trace dans la console Java");
Les outils de journalisation offrent de nombreux avantages aux programmeurs. Le service le plus utilisé est
évidemment la mise au point du code lors des développements. Ils permettent également d
’
enregistrer les
messages,d
’
envoyerdesemails,degérerdesniveauxdetracesouautre,maissurtoutparl
’
intermédiaired
’
unfichier
deconfiguration,degéreràtoutmomentlestraces.Eneffet,unoutildejournalisationpermetd
’
activeroudésactiver
àtoutmomentcertainsmessagesenfonctiondenosbesoins,sansêtreobligédereprendretoutlecode.
2.L
’
APILOG4J
LabibliothèqueLog4J esttrèsrépandue danslemonde Javaetnotamment JavaEE.Cette APIdejournalisation
permetdegérerlestracesutilisateursencombinaisonavecl
’
APIcommons
logging
.Nousallonscommencerlamiseen
placedelajournalisationpournotreprojetBetaBoutiqueeninstallantl
’
APILOG4J.Actuellementlaversion1.3estla
plusutiliséeenattendantlaversion2.0quienestaustadeexpérimental(http://logging.apache.org/log4j/).
La première étape consiste à télécharger et à installer les archives au format
.jar
dans notre répertoire de
librairies/WEB
INF/lib
.Nousutilisonsl
’
applicationchatbetaboutiqueaveclapartied
’
administrationdéveloppéeàl
’
aide
duframeworkStrutsdanslechapitreprécédent.
Leslibrairiessuivantessontalorscopiéesdanslerépertoire/WEB
INF/lib
:
●commons
logging.jar
●
log4j
1.3alpha
8.jar
3.MiseenplacedeL
’
APILOG4J
LalibrairieLog4Jmetàdispositionduprogrammeurtroiscomposants :
●LesLoggersquipermettentd
’
écrirelesmessages.
●Les
Appenders
quipermettentdesélectionnerladestinationdesmessages.
●LesLayoutsquipermettentdemettreenformelesmessages.
a.Logger
LeLoggerestl
’
entitédebasequiestutiliséepourlajournalisation,ilutiliselaclasse
org.apache.log4J.logger
.La
déclarationd
’
unLoggerestréaliséedanschaqueclassequidoitutiliserlesystèmedejournalisation.Nouspouvons
parexemplereprendrelaclasse
ServerModele
etajouterladéclarationduLoggerendébutdefichier.
package modele;
import boiteoutils.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
- 1 -© ENI Editions - All rigths reserved
public class ServeurModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//log4j
private final static Logger logger=Logger.getLogger("modele");
...
L
’
obtentiondel
’
instanceduLoggerestréaliséeenappelantlaméthodestatique
Logger.getLogger()
.Cetteméthode
prendenparamètreunnomdeLoggerdenotrechoixoularéférencedirecteàlaclasse.
private final static Logger logger2=Logger.getLogger(ServeurModele.class);
Ensuite, il est nécessaire de gérer le niveau de journalisation ou de priorité des messages. Ceci permet de
représenterl
’
importancedumessageàjournaliser.Laclasse
org.apache.log4j.Level
permetdegérercesniveauxde
messages.Unmessagen
’
estalorsjournaliséquesisaprioritéestsupérieureouégaleauLoggereffectuantla
journalisation.
L
’
APILog4Jdéfinitcinqniveauxdeloggingclassésparordred
’
importance :
●FATAL :ceniveauestutilisépouruneerreurgravepouvantprovoquerl
’
arrêtdel
’
application.
●ERROR :ce niveau estutilisé pour uneerreur qui empêcheun fonctionnement importantde l
’
application
(requêteSQL,copiedefichier...).
●WARN :ceniveauestutilisépourunavertissementouunetrace.
●INFO :ceniveauestutilisépourunmessageinformatif.
●DEBUG :ceniveautrèsverbeuxestutilisépourdesmessagesutilisésenphasededébogage.
La journalisation d
’
un message à un niveau donné se fait au moyen de la méthode
log(priorité,message)
. Par
exemple,nouspouvonsdéclencherunetraced
’
erreurdesdeuxmanièressuivantes :
logger.error("Erreur dans la classe ServeurModele.java
fonction ServeurModele");
logger.log(Level.FATAL,"Erreur dans la classe ServeurModele.java
fonction ServeurModele");
b.Appenders
UnAppenderreprésentela cibled
’
unmessage,c
’
est
à
dire l
’
endroitoùserastockéouaffichécemessage.Les
Appenders sont utilisés pour enregistrer les événements de journalisation. Ils sont représentés par l
’
interface
org.apache.log4j.Appender
etchaqueAppenderenregistred
’
unemanièrespécifiquelesévénements.
Il existe plusieurs types d
’
Appender afin de gérer les traces dans une base de données
(
org.apache.log4j.jdbc.JDBCAppender
), par mail (
org.apache.log4j.net.SMTPAppender
), pour la console
(
org.apache.log4j.ConsoleAppender
)ouencoredansunfichier(
org.apache.log4j.FileAppender
).Nousretrouvonsdonc
des Appenders pour la console, le système de fichiers, les sockets, le démon Unix syslog ou les composants
graphiques.
Nouspouvonsreprendrenotreclasseprécédenteafindetracerlemessaged
’
erreurdanslaconsole.
/***********************************************************
* constructeur
***********************************************************/
public ServeurModele(DataSource ds)
{
PatternLayout layout=new PatternLayout("%d % -5p %c - %F:%L - %m%n");
ConsoleAppender stdout=new ConsoleAppender(layout);
logger.addAppender(stdout);
logger.error("Erreur dans la classe ServeurModele.java
fonction ServeurModele");
- 2 - © ENI Editions - All rigths reserved
logger.log(Level.FATAL,"Erreur dans la classe
ServeurModele.java fonction ServeurModele");
//récupérer le DataSource de la servlet
this.ds=ds;
}
Leformatdéfiniaveclemodèle(PatternLayout)permetdeconserverl
’
heureetladate,leniveaud
’
erreur,lenomdu
fichieretlenumérodelalignedecodecorrespondanteaumessagelui
même.Désormaissinousdéclenchonsun
servicequiutilisecetteclassemodèle,lestracessontaffichéesdanslaconsolesystème.
LaclassePatternLayoutpermetdegérerlamiseenformedesmessagesensortie.Cetteclassepermetd
’
informer
les développeurs de données utiles mais demande parfois d
’
importantes ressources en fonction du détail des
informationstracées.
c.Layouts
LesLayoutssontutiliséspourlamiseenformedesévénementsdejournalisation.Ilssontutilisésenaccordavec
les Appenders afin d
’
associer la cible de l
’
enregistrement avec la manière de tracer les données. Log4J fournit
plusieursLayoutscomme :
●
org.apache.log4j.SimpleLayout
:quipermetdejournaliserdefaçonsimplelesévénements.
●org.apache.log4j.PatternLayout :quipermetdejournaliserlesmessagesenfonctiond
’
unmodèleoumotif.
●org.apache.log4j.HTMLLayout : qui permet de journaliser les événements au format HTML. Chaque
journalisationproduitundocumentHTMLcomplet.
●org.apache.log4j.XMLLayout :quipermetdejournaliserlesévénementsauformatXMLenconjugaisonavec
unFileAppenders.
●
org.apache.log4j.net.SMTPAppender
:quipermetdejournaliserlesévénementsenlesenvoyantparemail.
4.Configurationdynamique
LaconfigurationprécédenteaveclesAppendersdanslesfichierssourcesn
’
estpastrèspratiqueetmélangelecode
sourceavecdesélémentsdeconfigurationdelajournalisation.Demême,lorsdudéveloppement,touslesmessages
serontaffichésavecunniveauWARNparexempleetenproduction,seulslesmessagesdetypeERRORdevrontêtre
tracés.
Avecl
’
APILog4J,ilexistetroisméthodespourconfigurerlesLoggers.Lapremièreestlaconfigurationpardéfautque
nousvenonsd
’
aborderavecunegestiondanslessources.Lasecondeconfigurationestréaliséeàl
’
aided
’
unfichier
depropriétésavecleformathabituelclé=valeur.Enfinlederniertypedeconfigurationreposesurunfichierauformat
XML.
Nousallonsutiliserlasolutionintéressanteproposéeàpartird
’
unfichierdepropriétés.Dansuntelfichier,chaque
LoggerpeutêtreconfiguréetlesAppendersetlesLayoutspeuventyêtreparamétrés.Nouspourronsainsiaisément
modifierleformatagedesmessages.Danslefichierdeconfiguration,chaqueAppenderdoit avoirunnomafinde
pouvoiryfaireréférencelorsdelaconfigurationdesLoggers.LesAppenderssontpréfixéspar
log4j.appender
.
Ladéclarationd
’
unAppenderestréaliséesouslaformesuivante :
log4j.appender.NomAppender=ClasseAppender
Le paramètre NomAppender est le nom que nous souhaitons utiliser pour notre Appender et le paramètre
ClasseAppenderestlaclassed
’
implémentationdel
’
Appender.Voiciunexempledeconfigurationd
’
unAppendersimple
pourlaconsole.
#CONSOLE est l ’Appender de type ConsoleAppender
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.SimpleLayout
- 3 -© ENI Editions - All rigths reserved
L
’
Appender est correctement configuré, nous devons maintenant paramétrer les Loggers pour qu
’
ils utilisent les
Appenders. La configuration des Loggers est similaire à celle des Appenders, la forme est la suivante :
log4j.logger.nomdulogger=niveau,appender1,appender2
...
Le paramètre nomdulogger est le nom du Logger indiqué dans l
’
instruction
Logger.getLogger(nomdulogger)
et
représenteunestructurepointée,identiqueàlanotiondepaquetage.Leparamètreniveauestlenomduniveau
attribuéauLogger(ERROR,DEBUG...).S
’
il n
’
estpasprécisé,leniveauesthéritédunœudparentoupositionnéà
DEBUG.Leparamètre
appenderX
estlenomd
’
unAppenderdéclarédanslefichier.
Pourmettreenplacecemécanismenousallonscréerunfichiernommé
log4j.properties
danslepaquetageressources.
#définition du niveau et des Appender du rootLogger
(ordre : DEBUG - INFO - WARN - ERROR - FATAL)
log4j.rootLogger=ERROR, CONSOLE
#CONSOLE est l ’Appender de type console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#definition du format des messages 2005 -06-18 14:53:37 DEBUG
[Main] Hello World
log4j.appender.CONSOLE.layout.ConversionPattern=%d % -5p %c - %F:%L - %m%n
NousallonsdoncpouvoirintercepterlestracesdetypesERRORetdoncFATAL,carcelles
cisontplusbassesdansla
hiérarchie. Ce type de trace est associé à l
’
Appender nommé CONSOLE qui utilise la classe
org.apache.log4j.ConsoleAppender
etquivadoncafficherlestracesdanslaconsoleJavaavecleformatdéfiniparle
Layoutoumodèle.
public class ServeurModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//log4j
private final static Logger logger=Logger.getLogger("modele");
/***********************************************************
* constructeur
***********************************************************/
public ServeurModele(DataSource ds)
{
logger.error("Erreur dans la classe ServeurModele.java
fonction ServeurModele");
logger.log(Level.FATAL,"Erreur dans la classe
ServeurModele.java fonction ServeurModele");
//récupérer le DataSource de la servlet
this.ds=ds;
}
Maintenant,pourqueceservicesoitopérationneletquelesLoggersdéclarésdanslesfichierssourcesutilisentce
fichierdepropriétés,ilestnécessairedelemettreenplacedansuneclassechargéeaudémarrage.Pourcela,nous
ajoutonsunattributdanslefichierdeconfigurationdel
’
applicationweb.xml
.
<?xml version="1.0" encoding="ISO -8859-1"?>
<web-app>
<display -name>Application chatbetaboutique</display -name>
<!-- chemin pour acceder au fichier de proprietes log4J -->
<context -param>
<param -name>log4jfichier</param -name>
<param -value>WEB -INF/classes/ressources/log4j.properties</param -value>
</context -param>
...
Enfin,nousterminonsleparamétrageenprécisantlefichierdeconfigurationdanslaclassedegestiondesplug
ins
outouteautreclasselancéeaudémarragedel
’
application.
package chatbetaboutique;
import javax.naming.Context;
import javax.naming.InitialContext;
- 4 - © ENI Editions - All rigths reserved
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import org.apache.log4j.PropertyConfigurator;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
public class PluginDataSource implements PlugIn
{
//fonction appelée lors de la création du plug -in
public void init(ActionServlet servlet,ModuleConfig
moduleConfig) throws ServletException
{
//récupérer les paramètres présents dans le fichier
de configuration web.xml
String nomprojet=(String)servlet.getServletContext().getInitParameter
("urlapplication");
String connecteurjdbc=(String)servlet.getServletContext().getInitParameter
("connecteurjdbc");
//gestion de la journalisation
String prefix=servlet.getServletContext().getRealPath("/");
String log4jfichier=(String)servlet.getServletContext().getInitParameter
("log4jfichier");
PropertyConfigurator.configure(prefix+log4jfichier);
//initaliser le context
Context initCtx=null;
...
Désormais,sinousdéclenchonsunepagequiutiliselaclasseServeurModele,lestracesdeniveauERRORetFATAL
sontaffichéesdanslaconsoleJava.
Toutes les traces de types ERROR et FATAL sont affichées dans la console étant donné que notre Logger est
positionnéauniveaudelaracine(
log4j.rootLogger
).
Nouspouvonsvérifierceciendéclarantunetracedansl
’
actiondelaclasseGestionServeurAction
.
package chatbetaboutique;
import modele.ServeurModele;
import org.apache.log4j.Logger;
import org.apache.struts.action.*;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.sql.DataSource;
public class GestionServeurAction extends Action{
//variables de la classe
DataSource ds=null;
//log4j
private final static Logger logger=Logger.getLogger("action");
//gérer l ’état du serveur
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)throws
IOException, ServletException
{
logger.fatal("Fatale dans la classe
GestionServeurAction.java fonction execute");
//récupérer la datasource du plugin dans un attribut
présent dans le context de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
- 5 -© ENI Editions - All rigths reserved
ServeurModele serveurmodele=new ServeurModele(ds);
//fermer la datasource
this.ds=null;
...
Maintenant, nous pourrions souhaiter journaliser les traces du paquetage Log4J nommé
modele
différemment du
paquetageroot.Nousallonsparexemplechangerdeniveaudejournalisationpourlepaquetage
modele
afind
’
avoir
destracessimplesetstockerlesdonnéesdansunfichierdejournalisation.
#définition du niveau et des Appender du rootLogger
(ordre : DEBUG - INFO - WARN - ERROR - FATAL)
log4j.rootLogger=ERROR, CONSOLE
#CONSOLE est l ’Appender de type console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#definition du format des messages 2005 -06-18 14:53:37 DEBUG
[Main] Hello World
log4j.appender.CONSOLE.layout.ConversionPattern=%d % -5p %c - %F:%L - %m%n
#logger pour le paquet modele, dans le fichier tracer que les WARN,
ERROR et FATAL
log4j.logger.modele=WARN, fichiermodele
#fichier
log4j.appender.fichiermodele=org.apache.log4j.RollingFileAppender
log4j.appender.fichiermodele.File=E:\\PROJETWEB\\chatbetaboutique\\modele.log
log4j.appender.fichiermodele.MaxFileSize=200KB
log4j.appender.fichiermodele.MaxBackupIndex=2
log4j.appender.fichiermodele.layout=org.apache.log4j.PatternLayout
log4j.appender.fichiermodele.layout.ConversionPattern=%d % -5p %c -
%F:%L - %m%n
NousprécisonssurquelpaquetageLog4Jnoussouhaitonsréaliserlajournalisationparl
’
intermédiairedel
’
instruction
log4j.logger.modele
. Ensuite, nous détaillons la configuration de l
’
Appender avec le type
org.apache.log4j.RollingFileAppender
(journalisationparfichier),lefichierdestockagedestracesavecl
’
attribut
File
,la
taillemaximaledufichieravantrotation(larelationestutiliséepourconserverplusieursfichiersdejournalisation)
avecleparamètre
MaxFileSize
,lenombredefichiersconservésavecleparamètreMaxBackupIndex,letypedeLayout
utilisé(org.apache.log4j.PatternLayout)etenfinlemodèleduLayoutavecl
’
attributConversionPattern
.
package modele;
import boiteoutils.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
public class ServeurModele
{
//variables de classe
DataSource ds=null;
Connection connection=null;
ResultSet rs=null;
//log4j
private final static Logger logger=Logger.getLogger("modele");
/***********************************************************
* constructeur
***********************************************************/
public ServeurModele(DataSource ds)
{
logger.debug("Debug dans la classe ServeurModele.java
fonction ServeurModele");
logger.info("Info dans la classe ServeurModele.java
- 6 - © ENI Editions - All rigths reserved
fonction ServeurModele");
logger.warn("Warn dans la classe ServeurModele.java
fonction ServeurModele");
logger.error("Error dans la classe ServeurModele.java
fonction ServeurModele");
logger.fatal("Fatal dans la classe ServeurModele.java
fonction ServeurModele");
//récupérer le DataSource de la servlet
this.ds=ds;
}
Nousvoyons,aprèsavoirlancéunservicequiutilisecetteclasse,qu
’
unfichiernommé
modele.log
estcrééàlaracine
duprojetetpossèdelecontenusuivant :
2008-10-31 11:09:37,468 WARN modele - ServeurModele.java:31 -
Warn dans la classe ServeurModele.java fonction ServeurModele
2008-10-31 11:09:37,468 ERROR modele - ServeurModele.java:32 -
Error dans la classe ServeurModele.java fonction ServeurModele
2008-10-31 11:09:37,484 FATAL modele - ServeurModele.java:33 -
Fatal dans la classe ServeurModele.java fonction ServeurModele
LestracesdetypeWARN,ERRORetFATALsontbieninséréesdanslefichier
modele.log
sanslestracesde
typesDEBUGetINFOetsanslestracesdetyperootmalgréladéclarationdansl
’
actionGestionServeurAction
.
Maintenant,nouspouvonsencoreaméliorerlajournalisationenutilisantunetracedetypefichierHTML.
#définition du niveau et des Appender du rootLogger
(ordre : DEBUG - INFO - WARN - ERROR - FATAL)
log4j.rootLogger=ERROR, CONSOLE
#CONSOLE est l ’Appender de type console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#definition du format des messages 2005 -06-18 14:53:37 DEBUG
[Main] Hello World
log4j.appender.CONSOLE.layout.ConversionPattern=%d % -5p %c - %F:%L - %m%n
#logger pour le paquet MODELE, dans le fichier tracer que les WARN,
ERROR et FATAL
log4j.logger.modele=WARN, fichiermodele, htmlmodele
#fichier
log4j.appender.fichiermodele=org.apache.log4j.RollingFileAppender
log4j.appender.fichiermodele.File=E:\\PROJETWEB\\chatbetaboutique\\modele.log
log4j.appender.fichiermodele.MaxFileSize=200KB
log4j.appender.fichiermodele.MaxBackupIndex=2
log4j.appender.fichiermodele.layout=org.apache.log4j.PatternLayout
log4j.appender.fichiermodele.layout.ConversionPattern=%d % -5p %c - %F:%L
- %m%n
#html
log4j.appender.htmlmodele=org.apache.log4j.RollingFileAppender
log4j.appender.htmlmodele.File=E:\\PROJETWEB\\chatbetaboutique\\modele.html
log4j.appender.htmlmodele.MaxFileSize=300KB
log4j.appender.htmlmodele.MaxBackupIndex=2
log4j.appender.htmlmodele.layout=org.apache.log4j.HTMLLayout
log4j.appender.htmlmodele.layout.LocationInfo=true
log4j.appender.htmlmodele.layout.Title=Logs modele
log4j.appender.htmlmodele.layout.ConversionPattern=%d % -5p %c - %F:%-4L
- %m%n
La configuration est quasiment identique à celle d
’
un
RollingFileAppender
, c
’
est essentiellement la déclaration du
Layout qui change. Cette journalisation aura pour effet de générer des traces de journalisation au format HTML
directementconsultables(modele.html
).
- 7 -© ENI Editions - All rigths reserved
Afind
’
améliorerl
’
affichagedelapageHTML,nouspouvonstrèsbiendévelopperunefeuilledestyleCSSavecdes
couleurs,desimagesoucadrespourlesniveauxdemessagesetassociercettefeuilleauxpagesHTMLgénéréespar
Log4J.
Enfin,noustermineronslamiseenplacedeLog4Javecl
’
installationd
’
unsystèmedejournalisationparemail.Pour
cela, le paquetage Log4J SMTP est nécessaire, nous installons donc l
’
archive log4j
smtp
1.3alpha
8.jar dans le
répertoire /WEB
INF/lib
de l
’
application. La librairie Log4J utilise JavaMail pour le transfert des emails, il donc
nécessaired
’
installerégalementlepaquetage
mail.jar
.
#définition du niveau et des Appender du rootLogger
(ordre : DEBUG - INFO - WARN - ERROR - FATAL)
log4j.rootLogger=ERROR, CONSOLE
#CONSOLE est l ’Appender de type console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#definition du format des messages 2005 -06-18 14:53:37 DEBUG
[Main] Hello World
log4j.appender.CONSOLE.layout.ConversionPattern=%d % -5p %c - %F:%L - %m%n
#logger pour le paquet MODELE, dans le fichier tracer que les WARN,
ERROR et FATAL
log4j.logger.modele=WARN, fichiermodele, htmlmodele, emailmodele
#fichier
log4j.appender.fichiermodele=org.apache.log4j.RollingFileAppender
log4j.appender.fichiermodele.File=E:\\PROJETWEB\\chatbetaboutique\\modele.log
log4j.appender.fichiermodele.MaxFileSize=200KB
log4j.appender.fichiermodele.MaxBackupIndex=2
log4j.appender.fichiermodele.layout=org.apache.log4j.PatternLayout
log4j.appender.fichiermodele.layout.ConversionPattern=%d % -5p %c - %F:%L
- %m%n
#html
log4j.appender.htmlmodele=org.apache.log4j.RollingFileAppender
log4j.appender.htmlmodele.File=E:\\PROJETWEB\\chatbetaboutique\\modele.html
log4j.appender.htmlmodele.MaxFileSize=300KB
log4j.appender.htmlmodele.MaxBackupIndex=2
log4j.appender.htmlmodele.layout=org.apache.log4j.HTMLLayout
log4j.appender.htmlmodele.layout.LocationInfo=true
log4j.appender.htmlmodele.layout.Title=Logs modele
log4j.appender.htmlmodele.layout.ConversionPattern=%d % -5p %c - %F:%-4L
- %m%n
#email
log4j.appender.emailmodele=org.apache.log4j.net.SMTPAppender
log4j.appender.emailmodele.Threshold=INFO
log4j.appender.emailmodele.BufferSize=100
log4j.appender.emailmodele.To=jerome@nouvellepage.com
log4j.appender.emailmodele.From=info@documentheque.com
log4j.appender.emailmodele.SMTPHost=localhost
log4j.appender.emailmodele.Subject=Log4J Message en ligne classe : modele
log4j.appender.emailmodele.layout=org.apache.log4j.PatternLayout
log4j.appender.emailmodele.layout.ConversionPattern=%5p [%t] (%F:%L)
- %m%n
L
’
outilLog4Jpermet degérerla journalisationdefaçon précise etadaptée.Pour notreprojetBetaBoutique,nous
pouvonsutiliserparexemplelefichierprécédentainsiquetroisautrespaquetagesLog4Jpourlesactions,lesvueset
lesJavaBean.NousplaçonschaquedéfinitionavecleniveauWARN,nousréalisonsdestracesdedébogage,detests
ouautreavecleniveauWARNetleserreursgravesavecleniveauERROR.
- 8 - © ENI Editions - All rigths reserved
Dèslamiseenproduction,ilfaudrautilisersoitunsecondfichierdepropriétésavecleniveauERRORàlaplacede
WARN,soitmodifierleniveaudanslefichier
log4j.properties
afindetraceruniquementleserreursgraves.Leservice
degénérationdeserreursparemailouavecunfichierHTMLpermetalorsdeconsulterlestracesenligneetentemps
réel.
ParhabitudeavecdesdéveloppementsenMVCetJavaEE,ilfaututiliserquatredéfinitionsdepaquetagesLog4Jqui
sontrelativesauxmodèles,auxactions,auxJavaBeanetauxvuesafindetracertrèsprécisémentcequiestsouhaité
pendantlesphasesdedéveloppementetproduction.
- 9 -© ENI Editions - All rigths reserved
Ant:AnotherNeatTool
1.Présentation
AntestunprojetOpenSourceduconsortiumApache
Jakarta.C
’
estunoutilquipermetdecompilerdessourcesJava,
delesassemblerdanslesfichiers
.jar
etdelesexécuter.Ilestaussipossibledes
’
enservirpourautomatisercertaines
tâches.
AntestsouventcomparéaucélèbreoutilmakeUnix.ÉcritenJava,ilestdoncmultiplates
formesetreposesurun
fichierde configuration écriten XML quidéfinit les différentestâches qui devrontêtre exécutées parl
’
outil(fichier
build.xml
).Lefichierdeconfigurationcontientunensembledeciblesoutarget.Chaqueciblecontientuneouplusieurs
tâchesetchaqueciblepeutavoirunedépendanceenversuneouplusieursautrescibleslorsdel
’
exécution.
Antestlivréenstandardaveclaplupartdesoutilsdedéveloppement(EclipseouNetBeans).Ilestmaintenantutilisé
danslaplupartdesprojetspourconstruirelesexécutablesàpartirdesources.
AntreposesurJava,ilestdoncaisédedévelopperunplug
inpourunefonctionnalitéprécise.Ilpermeteneffetde
couvriràpeuprèstouslesbesoinsnécessairesaudéveloppementd
’
applicationsconséquentes :compilation,gestion
deversion,empaquetage,déploiementetarchivage.
2.Utilisation
LefichierdeconfigurationpourlesprojetsAntestnommé
build.xml
.Nousallonscréerunpremierfichierd
’
exemplequi
permetd
’
afficherunmessagesimpledanslaconsoleJava.SousEclipse,lacommandeFenêtreAfficherlavue
ANT
permetd
’
afficherl
’
éditeurXMLpourAnt.Ensuite,danslafenêtreaffichée(icônefourmi),ilestnécessairedecréerun
nouveaufichier
build.xml
àlaracineduprojet.
Lefichier
build.xml
contientladescriptionduprocessusdeconstructiondel
’
application.CommetoutdocumentXML,le
fichiercommenceparleprologue.
<?xml version="1.0" ?>
Le principal élément de l
’
arborescencedu documentXML est représenté par letag
<project/>
qui est la racine. À
l
’
intérieurdecetag,nousretrouvonsladéfinitiondesélémentsduprojet :
●Lesciblesoutargetsquisontlesétapesdeconstructionduprojet.
●Lespropriétésoupropertiesquisontlesvariablesquicontiennentlesdonnéesutilisablesparleséléments.
●Lestâchesoutasksquisontlestraitementsàréaliserdansunecibledonnée.
Cettebalisepermetdedéfinirlaracineduprojetdanslefichierdeconfiguration
build.xml
.Cetagpossèdeplusieurs
attributs :
●name :quipermetdepréciserlenomduprojet.
Leprojetproject
- 1 -© ENI Editions - All rigths reserved
●default :quipermetdepréciserlacibleàexécuterpardéfaut.
●
basedir
:quipermetdepréciserlerépertoirequiserviraderéférencepourlalocalisationd
’
autresréférences.
<project name="AFFICHAGE" default="run" basedir=".">
Labalise
<target/>
permetdedéfinirunecible.Unecibleestunensembledetâchesàréaliserdansunordrebien
précis.L
’
ordrecorrespondauxtâchesdéfiniesdanslacibleelle
même.
Cetagpossèdeplusieursattributs :
●name :quipermetdepréciserunnomàlacible.
●description :quicontientunebrèvedescriptiondelacible.
Une tâche est un traitement qui est réalisé par l
’
intermédiaire d
’
une classe Java qui implémente l
’
interface
org.apache.ant.Task.Unetâcheestobligatoirementinclusedansuneciblepourpouvoirêtreexécutée.
Antfournitunelistetrèscomplètedetâchespourlestraitementslorsdesdéveloppements :
●echo :afficherunmessagedanslaconsole.
●taskdef :définirunetâcheexterne.
●
available
:définirunepropriété.
●
java
:exécuteruneapplication.
●
javac
:compilerlessources.
●
javadoc
:générerladocumentation.
●signjar :signerunfichierjar.
●gunzip :décompresserunearchive.
●
jar
:créerunearchiveauformat.jar.
●tar :créeruneunearchiveauformat.tar.
●
zip
:créerunearchiveauformat.zip.
●war :créerunearchiveauformat.war.
●
exec
:exécuterunecommandeexterne.
●mail :envoyerunemail.
●chmod :modifierlesdroitssurunfichier.
●
copy
:copierunfichier.
●
delete
:supprimerunfichier.
Lescibles
target
Lestâches
task
- 2 - © ENI Editions - All rigths reserved
●mkdir :créerunrépertoire.
●ant :exécuterunautrefichierdebuild.
●
record
:enregistrerlestraitementsdel
’
exécutiondansunfichierjournal.
La balise
<property/>
permet de définir une propriété qui pourra être utilisée dans le projet. Les propriétés sont
souventutiliséespourpréciserunevariable,unrépertoiredesources,uneversion...
Ladéfinitionparl
’
intermédiairedepropriétéou variablepermetfacilementde changer lesvaleursdesparamètres
sansreprendrelatotalitédufichierdeconstruction
build.xml
.
Cetagpossèdeplusieursattributs :
●name :quipermetdedonnerunnomàlapropriété.
●value :quipermetdedonnerunevaleuràlapropriété.
●location :quipermetdedéfinirunfichieravecuncheminabsolu.
●
file
:quipermetdepréciserlenomd
’
unfichierquicontientladéfinitiond
’
unensembledepropriétés.
Nouspouvonséditerlefichier
build.xml
etinsérerlecodeci
dessousafind
’
afficherunmessagedanslaconsole.
<?xml version="1.0" ?>
<project name="affichage" default="run" basedir=".">
<target name="run">
<echo message="Premier projet ANT"/>
</target>
</project>
Ceprojetnommé
affichage
utiliseuneseuleciblenomméerunquiestlancéepardéfaut.Pourexécutercecode,ilest
nécessaired
’
ouvrirl
’
ongletAnt,d
’
ajouterlefichier
build.xml
etdelancersonexécutionavecl
’
icônerun
.
Antpeutégalements
’
utiliserenlignedecommandeaveclasyntaxesuivante :
ant options cible
Par défaut, Ant recherche un fichier nommé
build.xml
dans le répertoire courant. Ce fichier peut être précisé avec
l
’
option :
ant -buildfile monbuild.xml
Lefichier
build.xml
suivantpermetdecréerdeuxtâchesappeléesrunetclean.Latâcherun,exécutéepardéfaut,
permetdecréerunrépertoirenomméESSAI,d
’
ycopierlefichier
build.xml
etdevérifierquecelui
ciexiste.Latâche
cleanpermetdesupprimerlerépertoirecrééainsiquelesfichiersdecerépertoire.Unetâchequin
’
estpascellepar
défautestlancéeendonnantsonnomenparamètre.
Lespropriétés
property
- 3 -© ENI Editions - All rigths reserved
<?xml version="1.0" ?>
<project name="fichier" default="run" basedir=".">
<target name="run">
<mkdir dir="ESSAI"/>
<copy file="build.xml" todir="./ESSAI/"/>
<available file="./ESSAI/build.xml" type="file"
property="build.xml.found"/>
<echo message="./ESSAI/build.xml trouvé"/>
</target>
<target name="clean">
<delete includeEmptyDirs="true" quiet="true">
<fileset dir="ESSAI"/>
</delete>
</target>
</project>
EnlançantAntavecEclipse,c
’
estlatâchepardéfautquiestexécutée,soitrun.Cettetâchevabiencréerlerépertoire
ESSAIdanslerépertoirecourantainsiquelefichierassocié.
Maintenant,nouspouvonslancerlatâchecleanquipermetdesupprimerlerépertoire.Pourcela,avecEclipse,ilfaut
déplierlefichierAntetsélectionnerlasecondetâche.
Nous pouvons également définir une propriété pour le nom du fichier
build.xml
. Cette technique permet ainsi de
changertrèsfacilementunevaleursansêtreobligédetoutvérifierdanslecodesource.
<?xml version="1.0" ?>
<project name="fichier" default="run" basedir=".">
<property name="repertoiredestination" value="./ESSAICOPIE/"/>
<target name="run">
<mkdir dir="${repertoiredestination}"/>
<copy file="build.xml" todir="${repertoiredestination}"/>
<available file="${repertoiredestination}build.xml"
type="file" property="build.xml.found"/>
<echo message="${repertoiredestination}build.xml trouvé"/>
</target>
<target name="clean">
<delete includeEmptyDirs="true" quiet="true">
<fileset dir="${repertoiredestination}"/>
</delete>
</target>
</project>
3.Générationdel
’
archived
’
unprojetJavaEE
Nousallonsmaintenantutiliserl
’
outilAntpourdéployernotreprojetchatbetaboutiquedefaçonprofessionnelleàl
’
aide
d
’
une archive qui sera directement exploitable sur n
’
importe quel serveur compatible Java EE. Le projet sera
transforméenunearchive.war(WebARchive)quipourradirectementêtredéployéeàl
’
aided
’
unserveur.Pourcela,
- 4 - © ENI Editions - All rigths reserved
nousprocéderonsparétape,enaugmentantprogressivementlacapacitédesfonctionnalitésdenotrefichier
build.xml
.
Lapremièretâcheàréaliserlorsdesdéploiementsestlacompilationdesfichierssourcescontenusdanslesdifférents
répertoires.
Latâche
<javac/>
permetdecompilerlesfichierssources.Cettetâchepossèdelesprincipauxattributssuivants :
●srcdir :quipermetdepréciserlerépertoireracinedel
’
arborescencedesfichierssources.
●destdir :quipermetdepréciserlerépertoirededestinationdesrésultatscompilés.
●executable :quipermetdepréciserlecheminverslecompilateurJavautilisé.
●
fork
:quipermetdelancerlacompilationdansuneJVMdédiéeounon,parrapportàl
’
exécutiondeANT.
Nousallonscréerdespropriétésafind
’
améliorerlalisibilitéetlamaintenancedufichier
build.xml
.
<?xml version="1.0" ?>
<project name="FICHIER" default="run" basedir=".">
<! -- librairies utilisées -->
<property name="lib" value="./WEB -INF/lib"/>
<property name="libtomcat" value="E:\Tomcat 6.0\lib"/>
<! -- fichier sources -->
<property name="src" location="./WEB -INF/src"/>
<! -- fichier compilés -->
<property name="classes" location="./WEB -INF/classes"/>
<! -- Définition du classpath du projet -->
<path id="classpath">
<fileset dir="${lib}">
<include name="*.jar"/>
</fileset>
<fileset dir="${libtomcat}">
<include name="*.jar"/>
</fileset>
</path>
<! -- tache1 : afficher la date de la génération du build ANT
et creer un fichier informatif -->
<target name="init" description="Initialisation">
<tstamp/>
<buildnumber file="numerobuild.txt"/>
<echo message="Generation numero : ${build.number} du ${TODAY}"/>
</target>
<! -- tache2 : générer les fichiers .class -->
<target name="compil" depends="init" description="Compilation">
<echo message="Compilation des classes en Java"/>
<javac srcdir="${src}" destdir="${classes}"
executable="E:\jdk1.5\bin\javac" fork="yes">
<classpath refid="classpath"/>
</javac>
<echo message="Copie des ressources dans
le repertoire des classes"/>
<copy todir="${classes}/ressources">
<fileset dir="${src}/ressources"/>
</copy>
</target>
<! -- tache 0 : lancée par défaut -->
<target name="run" depends="init, compil"
description="Generation complete">
<echo message="Generation complete"/>
</target>
</project>
Nousdéfinissonsleslibrairiesnécessairesàlacompilationduprojet.Ensuite,nousprécisonslerépertoiredessources
Javaainsiquelerépertoirededestination.Enfin,nousprécisonsleclasspathduprojetenindiquantquetoutesnos
librairies.jarserontutiliséeslorsdelacompilation.
- 5 -© ENI Editions - All rigths reserved
Latâcherunestlancéeenpremieretvadéclencherdansl
’
ordrelatâcheinitsuiviedelatâche
compil
.Latâche
compil
permetd
’
afficherdanslaconsoleetdansunfichier,lenumérodeversiondelacompilation.Ensuite,touslesfichiers
sources
.java
sontcompilésenfichiers.class
.
Nousallons maintenant créerun répertoire nommé
/war
ainsi qu
’
unenouvelle tâche nomméeclean qui permet de
supprimerl
’
anciennearchiveWARduprojetsielleexiste.
<!-- tache3 pour nettoyer l ’ancienne archive war -->
<target name="clean" description="Suppression de l ’ancienne archive war">
<echo message="Suppression de l ’ancienne archive war"/>
<delete file="./war/chatbetaboutiquefin.war"/>
</target>
Ensuite,nouspassonsàlatâchequipermetdegénérerunearchiveWARJavaEEpourledéploiementduprojet.
Cette tâche va générer une archive nommée chatbetaboutiquefin.war à partir du fichier de configuration /WEB
INF/web.xml,sansinclurelefichierAnt
build.xml
,lesfichierssources(/WEB
INF/src)etlesJSPcompilées(répertoire
work).Eneffet,leprojetenphasedeproductionn
’
apasbesoindesfichierssourcesetlesJSPserontrecompiléesavec
lemoteurJSPduserveurenproduction.
L
’
archivefinaleestnomméechatbetaboutiquefin.warafindetesterledéploiementsurleserveuretdefairela
distinctionavecleprojetlui
mêmequiestnomméchatbetaboutique
.
Il est également possible d
’
utiliser une tâche de communication Ant pour envoyer l
’
application WAR en FTP sur le
serveurenproduction.
<ftp server="ftp.gdawj.com" userid="utilisateur" password="motdepasse">
<echo message="Envoyer l ’application en FTP"/>
<fileset dir="./war/chatbetaboutiquefin.war" />
</ftp>
L
’
utilisation de la tâche FTP nécessite la mise en place de plusieurs librairies Java dépendantes.
http://ant.apache.org/manual/OptionalTasks/ftp.html
Lefichierfinalestprésentéci
dessous :
<?xml version="1.0" ?>
<project name="FICHIER" default="run" basedir=".">
<! -- librairies utilisées -->
<property name="lib" value="./WEB -INF/lib"/>
<property name="libtomcat" value="E:\Tomcat 6.0\lib"/>
<! -- fichier sources -->
<property name="src" location="./WEB -INF/src"/>
<! -- fichier compilés -->
<property name="classes" location="./WEB -INF/classes"/>
<! -- Définition du classpath du projet -->
<path id="classpath">
<fileset dir="${lib}">
<include name="*.jar"/>
</fileset>
<fileset dir="${libtomcat}">
<include name="*.jar"/>
</fileset>
</path>
<!-- tache1 : afficher la date de la génération du build ANT
et creer un fichier informatif -->
<target name="init" description="Initialisation">
<tstamp/>
<buildnumber file="numerobuild.txt"/>
<echo message="Generation numero : ${build.number} du ${TODAY}"/>
</target>
<! -- tache2 : générer les fichiers .class -->
<target name="compil" depends="init" description="Compilation">
<echo message="Compilation des classes en Java"/>
<javac srcdir="${src}" destdir="${classes}"
executable="E:\jdk1.5\bin\javac" fork="yes">
<classpath refid="classpath"/>
</javac>
<echo message="Copie des ressources dans
le repertoire des classes"/>
<copy todir="${classes}/ressources">
- 6 - © ENI Editions - All rigths reserved
<fileset dir="${src}/ressources"/>
</copy>
</target>
<! -- tache3 pour nettoyer l ’ancienne archive war -->
<target name="clean" description="Suppression
de l’ancienne archive war">
<echo message="Suppression de l ’ancienne archive war"/>
<delete file="./war/chatbetaboutiquefin.war"/>
</target>
<! -- tache4 : génération de l ’archive WAR -->
<target name="war" depends="compil,clean" description="Generation
de l’archive war">
<echo message="Generation de l ’archive war"/>
<war destfile="./war/chatbetaboutiquefin.war" basedir="./."
webxml="./WEB -INF/web.xml" excludes="build.xml,WEB -INF/src/**,work/org/**">
<manifest>
<attribute name="Built -By" value="Jérôme Lafosse"/>
</manifest>
</war>
</target>
<! -- tache 0 : lancée par défaut -->
<target name="run" depends="init, compil, clean,
war" description="Generation complete">
<echo message="Generation complete"/>
</target>
- 7 -© ENI Editions - All rigths reserved
DéployerunprojetJavaEE
1.Présentation
Maintenant que le projet est correctement archivé et généré conformément au standard WAR, nous pouvons le
déployersurleserveurJavaEEenproduction.Pourcela,nousdevonsdéfiniruncontexte.AvecTomcat6,celapeut
êtreréaliséexplicitementdeplusieursmanières :
●Soitdanslefichier :Tomcat6/conf/server.xml
.
●SoitdansunfichierXMLspécifiquedeconfigurationducontextecommec
’
estlecasavecnosprojetsEclipse
définisparleplug
inSysdeo :Tomcat6/conf/Catalina/localhost/nomduprojet.xml
.
Lasyntaxeestlasuivante :Tomcat6/conf/nom_engin/nom_hôte/nom_application
.
●Soitdanslefichierdel
’
application :META
INF/context.xml
.
Pourdéployernotreprojetfinalchatbetaboutiquefin.warsurunserveurenproductionilexisteplusieurstechniques :
●Enréalisantuncopier
collerdenotreapplicationdanslerépertoire
/webapps
deTomcat.
●Endéployantleprojetavecl
’
outilmanagerdeTomcat.
●EnutilisantlescommandesHTTPdeTomcat.
●Enutilisantl
’
outilTomcatClientDeployer.
Nous pouvons tester le déploiement de notre projet en démarrant Tomcat et en copiant notre archive dans le
répertoire
/webapps
de Tomcat. Après quelques secondes d
’
attente, nous pouvons observer dans le fichier de
journalisationdeTomcat/logs/catalin.out,ledéploiementautomatiquedenotreapplication.
Nousvoyonsd
’
ailleurslacréationdesaréférencedanslemanagerdeTomcat.
Tomcatadécompressél
’
archivechatbetaboutique.warafindegénérerunrépertoire/webapps/chatbetaboutiqueavecla
totalité des fichiers. De même, les pages JSP et les fichiers temporaires sont présents dans le répertoire Tomcat
6/work/Catalina/localhost/chatbetaboutiquefin
.
L
’
archiveestcorrectementdéployéemaisleprojetn
’
estpasfonctionnel.Eneffetledéclenchementdel
’
URLsuivante
entraîneuneerreurSQL :http://localhost:8080/chatbetaboutiquefin/etatserveur.do.Ceciesttoutàfaitnormal,nous
avonsdéployéun projetmaiscelui
cinepossèdepasdedéclarationdansleserveurTomcat.Lesinformationsde
configuration de ce projet ne sont pas présentes ni dans le fichier /conf/server.xml, ni dans un fichier
spécifique/conf/Catalina/localhost/nomduprojet.xml,nidanslefichierMETA
INF/context.xmldel
’
application.
Lorsdudéveloppementnousavonsutilisélefichier/conf/Catalina/localhost/chatbetaboutique.xmlpourdéclarerleprojet
etlepooldeconnexionJDBCàlabasededonnées.Avecnotrearchivecesdonnéesnesontpasrenseignées,le
projetnesaitdoncpascommentseconnecteràlabasededonnées.Pourréalisercesétapes,nousallonsutiliserun
fichierMETA
INF/context.xmldansnotreapplicationavecladéclarationsuivante :
<Context path="/chatbetaboutiquefin" reloadable="true" docBase=".">
<Resource name="jdbc_chatbetaboutiquemysql" auth="Container"
type="javax.sql.DataSource"
username="root" password="" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatbetaboutique"
maxActive="20" maxIdle="10"
validationQuery="SELECT 1" defaultAutoCommit="true"
- 1 -© ENI Editions - All rigths reserved
characterEncoding="UTF -8"/>
</Context>
Noussupprimonsl
’
anciennearchivesurleserveurTomcatetnousutilisonsAntpourréaliserunenouvellearchivede
notreprojetquenousdéposonsànouveausurleserveurenproduction.Cetteopérationnécessiteunarrêtetun
démarragedeTomcat(ouredémarrage).
Le déploiement de l
’
archive par le serveur Tomcat aura pour effet de créer un
fichier/conf/Catalina/localhost/chatbetaboutiquefin.xmlquiseraunecopieducontenudufichierMETA
INF/context.xmlde
l
’
application. De même la suppression de l
’
application aura pour conséquence de détruire le fichier de
configuration/conf/Catalina/localhost/chatbetaboutiquefin.xml
.
2.Miseenproductiond
’
unprojetJavaEE
NousavonsregardécommentdéployercorrectementunprojetJavaEEàpartird
’
unearchiveWAR.Cependant,lorsde
lamiseenproduction,noussouhaitonsdéployernotreprojetàpartird
’
unnomdedomaine.
Pourceparagraphe,nousallonsutiliserlesdeuxnomsdedomainessuivants :www.gdawj.cometwww.gdawj.net
(
g
uidede
d
éveloppementd
’
a
pplicationsweben
j
ava).
PouraccéderàcesURLenlocal,nouspouvonsmodifierlefichierhostsdelamachine.ParexemplesousWindows :
C:\WINDOWS\system32\drivers\etc\hostsou/etc/host.confsousLinux.
# Copyright (c) 1993 -1999 Microsoft Corp.
#
# Ceci est un exemple de fichier HOSTS utilisé par Microsoft TCP/IP
# pour Windows.
#
# Ce fichier contient les correspondances des adresses IP aux noms d ’hôtes.
# Chaque entrée doit être sur une ligne propre. L ’adresse IP doit être placée
# dans la première colonne, suivie par le nom d ’hôte correspondant. L ’adresse
# IP et le nom d ’hôte doivent être séparés par au moins un espace.
#
# De plus, des commentaires (tels que celui -ci) peuvent être insérés sur des
# lignes propres ou après le nom d ’ordinateur. Ils sont indiqué par le
# symbole ’#’.
#
# Par exemple :
#
# 102.54.94.97 rhino.acme.com # serveur source
# 38.25.63.10 x.acme.com # hôte client x
127.0.0.1 localhost
127.0.0.1 www.gdawj.com
127.0.0.1 www.gdawj.net
Le fonctionnement peut être vérifié en saisissant l
’
adresse suivante dans un navigateur :
http://www.gdawj.com:8080/.Nousdevonsalorsobtenirlapaged
’
accueildenotreserveurTomcatétantdonnéque
nousutilisonslabonneURLetlebonport.
Maintenantsinousvoulonsaccéderànotreboutiquedirectementdepuiscetteadresse,nousdevonscréerunhôte
virtuelàlafindufichier/conf/server.xmldeTomcat.
<Host name="www.gdawj.com"
appBase="E:\Tomcat 6.0\webapps\chatbetaboutiquefin"
unpackWARs="true" autoDeploy="true" deployXML="false">
<Alias>www.gdawj.net</Alias>
<Context path="" reloadable="true" docBase=".">
<Resource name="jdbc_chatbetaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatbetaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"
defaultAutoCommit="true" characterEncoding="UTF -8"/>
</Context>
</Host>
Cettedéfinitionestutiliséedanslecasd
’
undéploiementsansfichierMETA
INF/context.xmlauseindel
’
application.
C
’
estlecaslorsquel
’
administrateurduserveurvadéployernotreprojetsurleserveurenproduction.Nousretrouvons
labalise
<Host/>
aveclenomdedomaineutilisé(name),lerépertoiredebaseoùsetrouvel
’
applicationdéployée
- 2 - © ENI Editions - All rigths reserved
(
appBase),ledéploiementautomatiquedel
’
application(autoDeploy)etledécompactagedesarchivesauformatWAR
(
unpackWARs
).L
’
attribut(
deployXML
)permetd
’
ignorerlesfichiersMETA
INF/context.xmletdepréciserquelecontexte
estdéclaréàceniveau.
Labalise
<Alias/>
permetde ne pascréerun hôte virtuelpourchaque nom dedomainemais d
’
assignerlamême
configuration à plusieurs domaines. Enfin, nous retrouvons la déclaration courante du contexte avec une petite
modificationauniveaudel
’
attributpathquiestvideétantdonnéquenousn
’
accédonsplusausiteparuneadressede
laformehttp://localhost:8080/nomdemonprojetmaisdirectementàpartirdunomdedomaine.
Nouspouvonsredémarrerleserveuretaccéderdirectementàl
’
applicationàpartirdesdeuxdomaines.
Nous pouvons désormais optimiser l
’
ensemble afin de préciser l
’
attribut workDir qui est facultatif mais qui permet
d
’
indiquerl
’
emplacementdanslequelTomcatdoitécrirelesfichierstemporaires,telsquelecodesourceetlesServlets
compiléesqu
’
ilgénèreàpartirdespagesJSP.Sicetemplacementn
’
estpaspréciséTomcatcréelesrépertoiresde
travaildanslerépertoireTomcat6.0/worketvérifiequechacunpossèdeunnomuniquepouréviterlesconflitsentre
lesdifférentesapplications.
<Host name="www.gdawj.com" appBase="E:\Tomcat 6.0\webapps\chatbetaboutiquefin"
unpackWARs="true" autoDeploy="true" deployXML="false"
workDir="E:\Tomcat 6.0\webapps\chatbetaboutiquefintemp">
<Alias>www.gdawj.net</Alias>
<Context path="" reloadable="true" docBase=".">
<Resource name="jdbc_chatbetaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatbetaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"
defaultAutoCommit="true" characterEncoding="UTF -8"/>
</Context>
</Host>
Dans ce cas, l
’
application sera déployée dans le répertoire chatbetaboutiquefin et les fichiers temporaires dans le
répertoirechatbetaboutiquefintemp
.
Lorsdesdéploiementsavecladéclarationd
’
unhôtevirtuel,parfoislepremierdémarragenefonctionnepaset
indique que le répertoire portant le nom de l
’
applicationn
’
est pas trouvé. Il existe deux méthodes pour
résoudreceproblème :soitréaliserundémarragequivacréerlerépertoireetensuiteunredémarrage,oualors
créerlerépertoireportantlenomdel
’
applicationàlamainavantlepremierdémarrage.
L
’
attributunpackWARspermetdeprécisersilesfichiersWARcontenusdanslerépertoireindiquéparappBasedoivent
êtredécompressésaudémarrageduserveur.S
’
ilpossèdelavaleurtrue
,l
’
hôtedécompresselesfichiersWARdansun
répertoire portant le même nom que le fichier WAR sans l
’
extension. Par exemple, chatbetaboutiquefin.war est
décompressé dans le répertoirechatbetaboutiquefin. Par contre, le serveur ne décompresse pas le fichier WAR s
’
il
existedéjàunrépertoirecorrespondant.SinoussouhaitonsredéployeruneapplicationWebàpartird
’
unfichierWAR,
nousdevonssupprimercerépertoire.
Enfait,lorsdelamiseenproductiond
’
unprojetJavaEEl
’
attributautoDeploydoitêtrepositionnéàfalsecarilindiqueà
- 3 -© ENI Editions - All rigths reserved
Tomcatdemettreàjourautomatiquementleserveurlorsquedenouveauxfichierssontprésentésdanslerépertoire
del
’
application.Lavaleurpardéfautesttrue,cequiesttrèsutileenphasededéveloppementpuisquenousvoulons
voirdirectementlesmodificationsapportéesàuneServletoupageJSP.Cesystèmed
’
écoutepermanentestlourden
terme de ressources. Pour un serveur en production, il est préférable de placer les fichiers et de recharger
manuellementl
’
applicationaveclemanager.Demêmel
’
attribut
liveDeploy
placéauniveaudeladéclarationdel
’
hôte
possèdeunfonctionnement similaireetdoitégalement êtrepositionnéà false sur unserveurenproduction. Pour
notreprojet,lefichierdeconfigurationenproductionseradonclesuivant :
<Host name="www.gdawj.com" appBase="E:\Tomcat 6.0\webapps\chatbetaboutiquefin"
unpackWARs="false" autoDeploy="false" liveDeploy="false" deployXML="false"
workDir="E:\Tomcat 6.0\webapps\chatbetaboutiquefintemp">
<Alias>www.gdawj.net</Alias>
<Context path="" reloadable="true" docBase=".">
<Resource name="jdbc_chatbetaboutiquemysql" auth="Container"
type="javax.sql.DataSource" username="root" password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatbetaboutique"
maxActive="20" maxIdle="10" validationQuery="SELECT 1"
defaultAutoCommit="true" characterEncoding="UTF -8"/>
</Context>
</Host>
3.Déploiementd
’
unprojetJavaEEàdistance
Nousavonsétudiéjusqu
’
àmaintenantplusieursmanièresdedéployerunprojetJavaEEàpartird
’
unearchiveWAR
présentesurleserveuroupostéeenFTP,avecSSHouautre.
Ilexistedespossibilitéspourdéployerlesprojetsàdistance.Unepossibilitéintéressanteestl
’
utilisationdumanager
Tomcat.LeformulaireHTMLpermetdeparcourirnotredisqueduràlarecherched
’
unearchiveWARàl
’
aidedubouton
Parcouriretdel
’
envoyeravecleboutonDeploy.Leprojetestalorsenvoyédanslerépertoire
/webapps
duserveur.
Ledéploiementestalorseffectuécommeexpliquéprécédemment.
Uneautresolutionconsisteàutiliserl
’
outilAnt.Lefichier
build.xml
étudiédansleparagrapheprécédentpermetde
générercorrectementune archiveWARduprojetchatbetaboutique. Une foisl
’
archivegénérée,ilestpossible dela
déployer automatiquement sur un serveur Tomcat en utilisant Ant et le manager. Pour cela, il est nécessaire de
déclarerunetâcheexterneaveclabalise
<taskdef/>
.Pourcommencer,ilfautquelefichier
Tomcat6/lib/catalina
ant.jar
soitaccessibleparAnt.Ilfaututiliserpourcelaunebalise<classpath/>
.
Nouscréonsunetâcheexternepourledéploiement :
<!-- tache5 : déployer l ’archive WAR -->
<property name="tomcat" location="E:/Tomcat 6.0"/>
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
<classpath>
<path location="${tomcat}/lib/catalina -ant.jar" />
</classpath>
</taskdef>
Ensuite,il est nécessaired
’
ajouterunecible pourdéployerl
’
application, cette cible utilise la tâche
deploy
déclarée
précédemment,elleseconnecteaumanagerdeTomcatavecuncomptecorrectetdéploieleprojetaveclesattributs
nécessaires.
<!-- tache5 : déployer l ’archive WAR -->
<property name="tomcat" location="E:/Tomcat 6.0"/>
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
<classpath>
<path location="${tomcat}/lib/catalina -ant.jar" />
</classpath>
</taskdef>
<target name="deployer" depends="war" description="Deployer l ’archive war">
<deploy url="http://localhost:8080/manager" username="admin"
password="admin"
path="/chatbetaboutiquefin" war="./war/chatbetaboutiquefin.war"/>
- 4 - © ENI Editions - All rigths reserved
</target>
Cettetâchepermet donc d
’
utiliser la librairiecatalina
ant.jar par l
’
intermédiaire de la balise <classpath/>. La balise
<deploy/>
permetdedéployerleprojetàdistance,nousutilisonsdansnotreexempleleserveurhttp://localhostmais
rienn
’
empêched
’
utiliserunserveurdistant.Nousprécisonsensuitelecomptepourseconnecteraumanagerainsi
quel
’
archiveWARàdéployeretlepathsouslequelelleseraaccessible.
Ilestpossibleaveccetteassociationdel
’
outilAntetdelalibrairiecatalina
ant.jar,d
’
utilisertouteslestâchesutilesàla
gestiondeprojetsJavaEE,àsavoir :
●
org.apache.catalina.ant.DeployTask
:permetdedéployerunprojet.
●
org.apache.catalina.ant.ReloadTask
:permetderechargerunprojet.
●org.apache.catalina.ant.ListTask :permetdelisterlesapplicationsdéployées.
●org.apache.catalina.ant.StartTask :permetdedémarreruneapplication.
●org.apache.catalina.ant.StopTask :permetd
’
arrêteruneapplication.
●org.apache.catalina.ant.UndeployTask :permetdesupprimeruneapplication.
●org.apache.catalina.ant.SessionsTask :permetd
’
obtenirdesinformationssurlasession.
●org.apache.catalina.ant.RolesTask :permetd
’
obtenirdesinformationssurlesrôles.
●org.apache.catalina.ant.ServerInfoTask :permetd
’
obtenirdesinformationssurleserveur.
●org.apache.catalina.ant.ResourcesTask:permetd
’
obtenirdesinformationssurlesressourcesJNDI.
Cetexemplemontrequ
’
ilestrelativementsimpled
’
automatiserlacréationetledéploiementd
’
uneapplicationàpartir
dessources.AntfaitdoncpartieintégrantedelaboîteàoutilsdesadministrateursetdéveloppeursdeprojetsJava
EE.
- 5 -© ENI Editions - All rigths reserved
Optimisationdelamémoire
1.Présentation
LesapplicationsJavaetplusparticulièrementleserveurTomcatsontgourmandsentermedemémoirevive(RAM).La
version Tomcat 6.X améliore considérablement la gestion de la mémoire et l
’
utilisation du Garbage Collector ou
ramasse
miettesafind
’
éviterlesfuitesdemémoireetlapersistanced
’
objetsnonutilisésdanslamémoire.
Avantd
’
allouerinutilementdelaressourcemémoireànotreserveur,ilestnécessairedetesterlamontéeencharge
decelui
ciavecdesoutilsdemonitoringcommeceuxprésentésdanslechapitreconsacréàTomcat.Iln
’
estpasrare
deconstaterquelamontéeenchargedelaRAMestliéeàunemauvaisefermeturedeconnexionauSGBD,àun
pointeurdelectured
’
unfichierouunaccèsàdesressourcesdiverses.
LechapitreconsacréàTomcatexpliqueendétailcommentalloueretgérerlamémoireduserveurJava.Pourrappel,la
pagestatus/étatduserveurdumanagerTomcatpermetd
’
afficherlesinformationssurl
’
utilisationdelamémoire.
SousWindows,ilestpossibled
’
augmenterlamémoireallouéeauserveurTomcatenutilisantl
’
outilApache Tomcat
PropertiesetlesoptionsJavaouaveclafenêtredepréférencesd
’
Eclipse.
Lamémoireinitialeréservéeauserveurestparamétréeavecl
’
option
Xmsetlamémoiremaximaleavecl
’
attribut
Xmx
.
SousLinuxlamémoireallouéeàTomcatestparamétréedanslefichiercatalina.sh.Lecodesuivantpermetd
’
allouer2
GodeRAMauserveur.
JAVA_OPTS="-Xms2048m -Xmx2048m"
Ce principe d
’
allocation mémoire associé à la configuration de la JVM est très utilisé mais il est également parfois
nécessairedegérerlamémoireendynamiquedupointdevuedelaprogrammation.Eneffet,lecodepeutparfoisêtre
gourmandentermedemémoirelorsd
’
instanciationsdenombreuxobjets,d
’
accèsàdesressourcesimportantes,de
transformationsd
’
imagesetd
’
utilisationdebibliothèquesgraphiques.
- 1 -© ENI Editions - All rigths reserved
2.Gestiondynamiquedelamémoire
LagestiondynamiquedelamémoireetduGarbageCollectorpeutêtreréaliséeenprogrammation.Dansnotreprojet
chatbetaboutique,nousallonsajouterunattributaufichierdeconfigurationdel
’
applicationweb.xml
.
<!-- charge mémoire utilisée pour la limite (ex :
Total-free=15-6=9Mo de limite maxi à atteindre) -->
<context -param>
<param -name>chargememoire</param -name>
<param -value>6</param -value>
</context -param>
D
’
après la configuration affichée avec le manager de Tomcat, nous disposons dans notre exemple de 16 Mo de
mémoireavecunmaximumautoriséde64Mo.Cettechargemémoirecorrespondàuncalculmathématiquequiestle
maximumdechargemémoireutiliséesurlamachine(Total
libre).Dansnotrecas,lachargemaximaleautoriséeest
de9ModeRAM.Sicettelimiteestatteinte,leGarbageCollectorseradéclenchédemanièreforcée.Surunserveuren
productionnoustrouvonsparexemplelecalculsuivant :Total
libre=1290
600=690Moquiindiquequepour1Gode
RAMautotal,leramasse
miettesseradéclenchélorsquelamémoireauraatteint690 Modecharge.
Nouspouvonsensuitecréeruneclassestatiquenommée
OutilsGarbageCollector
placéedanslepaquetageboiteoutils
etquipermetdedéclencherleGarbageCollectorenfonctiondelalimiteatteinte.
package boiteoutils;
public class OutilsGarbageCollector
{
//fixer la mémoire maxi à atteindre en méga octets
(500Mo soit une charge de Total -free=6-5=1Mo)
private static final int LIMITEMEMOIRE=5;
/****************************************************************************
* fonction qui permet de vider le garbage collector en fonction d ’une taille
****************************************************************************/
public static void verifierChargeGarbageCollector(double chargememoire)
{
//récupérer les informations du système
Runtime r=Runtime.getRuntime();
//mémoire
double memoirelibre=(r.freeMemory() / 1000000d);
//limite
double limitememoire;
//utiliser notre limite ou celle par défaut
if(chargememoire!=0) limitememoire=chargememoire;
else limitememoire=LIMITEMEMOIRE;
//si moins de mémoire libre que notre limite, alors vider la mémoire
if(memoirelibre<limitememoire)
{
try
{
//appeler les méthodes finalize() des objets
r.runFinalization();
//vider la mémoire
r.gc();
System.gc();
}
catch(Exception e)
{
//logger
System.out.println("Erreur lors du vidage de
la mémoire en mode automatique");
}
}
}
/****************************************************************************
* fonction qui permet de vider le garbage collector de manière forcée
****************************************************************************/
public static void viderGarbageCollector()
- 2 - © ENI Editions - All rigths reserved
{
//récupérer les informations du système
Runtime r=Runtime.getRuntime();
try
{
//appeler les méthodes finalize() des objets
try
{
//appeler les méthodes finalize() des objets
r.runFinalization();
//vider la mémoire
r.gc();
System.gc();
}
catch(Exception e)
{
//logger
System.out.println("Erreur lors du vidage de
la mémoire en mode forcé runFinalization");
}
}
catch(Exception e)
{
//logger
System.out.println("Erreur lors du vidage de
la mémoire en mode forcé viderGarbageCollector");
}
}
//fin de la classe
}
La première méthode permet de vider la RAM à l
’
aide du GarbageCollector à partir d
’
un maximum atteint
correspondantàlavaleurpréciséedanslefichierdeconfigurationweb.xml.Pourcelanousvérifionslatailleactuellede
la mémoire et en cas de besoin nous détruisons les objets non utilisés et nous lançons le Garbage Collector. La
secondeméthodepermetdetoujoursviderlaRAMdemanièreforcée.
MaintenantnouspourronsvérifiercettemémoireetlavideraubesoindansunepageJSPquiréaliseuntraitement
complexe,uneclassequimanipuled
’
importantesressourcesoualorsunfiltrequiseradéclenchésurtouteslesURL.
<%@ page import="boiteoutils.OutilsGarbageCollector" %>
<%
//charge maxi de la mémoire autorisée
String chargememoire=getServletContext().getInitParameter("chargememoire");
//gérer la mémoire
OutilsGarbageCollector.verifierChargeGarbageCollector
(Double.parseDouble(chargememoire));
%>
Cettetechniquebienqueradicaleesttrèsutiliséeenproductionafind
’
éviterlesralentissementsdestraitementsvoire
même des saturations de mémoire. Cette solution nous permet en effet de bénéficier d
’
uneplate
forme Internet
conséquente,quimanipuled
’
importantesressourcessansrisquerdesaturerleserveur.
package chatbetaboutique;
import modele.UtilisateurModele;
import org.apache.struts.action.*;
import org.apache.struts.actions.MappingDispatchAction;
import boiteoutils.OutilsGarbageCollector;
import boiteoutils.Utilisateur;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.sql.DataSource;
public class GestionUtilisateurAction extends MappingDispatchAction{
- 3 -© ENI Editions - All rigths reserved
//variables de la classe
DataSource ds=null;
//afficher la liste des utilisateurs
public ActionForward listeutilisateur(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse
response)throws IOException, ServletException
{
//récupérer la datasource du plugin dans un attribut
présent dans le context de la servlet
ds=(DataSource)servlet.getServletContext().getAttribute("datasource");
//créer le modèle
UtilisateurModele utilisateurmodele=new UtilisateurModele(ds);
//fermer la datasource
this.ds=null;
//retourner la liste des utilisateur
ArrayList listeutilisateur=
(ArrayList)utilisateurmodele.getListeUtilisateur();
//retourner la liste des utilisateurs
request.setAttribute("listeutilisateur",listeutilisateur);
//vider par sécurité
listeutilisateur=null;
//vider le garbage collector
OutilsGarbageCollector.viderGarbageCollector();
//retourner sur la page d ’affichage des utilisateurs
return mapping.findForward("listeutilisateur");
}
}
- 4 - © ENI Editions - All rigths reserved
Enrésumé
CechapitreaprésentélestechniquesavancéesenJavaEE.Lapremièrepartieaprésentélesintérêtsdel
’
utilisation
d
’
unoutildejournalisationperformant.Eneffetl
’
outilLog4Jpermetparl
’
intermédiairedesLogger,AppenderetLayout
deparamétrerdefaçonpréciselestracesd
’
unprojetJavaEE.
Dansundeuxièmetemps,l
’
outilAntaétéprésentépourlacompilation,l
’
empaquetageetledéploiementdesprojets.
Lesbalises
<projet/>
et
<target/>
ontétédétailléesainsiquelestâchesafindegénérerautomatiquementunearchive
JavaEEpouvantêtredirectementdéployée.
La troisième partie a concerné le déploiement d
’
un projet Java EE à partir d
’
une archive WAR sur un serveur en
production.Ledéploiementàpartird
’
unnomdedomaineaétéexplicitédanscetteétapeainsiquelesbalisesXML
nécessairesàlaconfiguration.Unélémentimportantaaussiétéévoqué :ledéploiementdeprojetJavaEEàdistance.
LedernierparagrapheadéveloppéunélémentessentieldelaprogrammationdeprojetJava,àsavoirlagestionet
l
’
optimisationdelamémoire.LaconfigurationdelamémoireduserveurJavaEEaétéprésentéeainsiquelagestion
dynamiquedelamémoireenprogrammationàpartirdufichierdeconfigurationweb.xmletdecodeJava.
- 1 -© ENI Editions - All rigths reserved