Java EE Guide De Développement D'applications Web En

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 507

DownloadJava EE Guide De Développement D'applications Web En
Open PDF In BrowserView PDF
Java EE
Guide de développement d'applications web en Java

Jérôme LAFOSSE

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

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.

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

© ENI Editions - All rigths reserved

- 1-

Avant­propos
La réalisation de sites Web passe par différentes étapes : l’analyse, la modélisation, le codage, la mise en production,
les tests et la maintenance. Toutes ces phases de conception sont longues, complexes et doivent être maîtrisées en
détail pour mener à bien les projets Internet. Pour ce type de projet, différents langages de programmation sont
utilisés comme PHP, Ruby, Perl, .NET ou Java. Java est reconnu actuellement comme l’un des meilleurs langages de
programmation objet pour la réalisation de projets Internet complexes avec son API spécifique, Java EE.
Ce guide détaillé suit une démarche progressive et vous aidera à créer des applications Web complexes et
fonctionnelles. Tous les concepts de la création d’un projet professionnel sont abordés dans ce livre, de la prise en
main du langage, à l’installation de l’environnement, à la configuration d’un serveur Web Java jusqu’à la création et la
mise en production d’un projet. Cet ouvrage permet de percevoir le développement d’applications Web en Java dans
sa globalité.
Mon objectif est de fournir un guide complet de développement d’applications Web en Java sur les deux principaux
environnements de développement que sont Windows et Linux, sans faire l’impasse sur une partie du cycle de
développement. Il s’agit d’explications et de conseils concrets, illustrés par une étude de cas réaliste de boutique de
vente en ligne.
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éveloppement d’applications web,etc.

© ENI Editions - All rigths reserved

- 1-

Organisation du guide
Le guide est divisé en sept chapitres spécifiques et autonomes :
●

Le chapitre 1 (Objectifs et spécifications de Java EE) présente le langage Java, les règles de nommage ainsi
que l’installation de l’environnement de développement.

●

Le chapitre 2 (Le serveur d’applications Apache­Tomcat) est consacré à la mise en place du serveur Java de
référence, Tomcat.

●

Le chapitre 3 (Les JavaServer Page) aborde la programmation de Servlets avec les classes, objets et
méthodes.

●

Le chapitre 4 (Les Servlets) explore en détail le développement de pages Web au travers des pages JSP.

●

Le chapitre 5 (Java et les bases de données) présente les bases de données en Java ainsi que les solutions
techniques et les outils adaptés à la persistance des données.

●

Le chapitre 6 (Framework Java EE) est consacré à l’étude d’un framework de développement Java nommé
Struts.

●

Le chapitre 7 (Techniques avancées) est dédié aux techniques avancées en développement Java EE.

© ENI Editions - All rigths reserved

- 1-

À qui s’adresse ce guide ?
Que vous ayez peu de connaissances en développement Web ou que vous soyez un expert Java EE, ce guide a pour
objectif de vous présenter en détail et de façon exhaustive, toutes les étapes de réalisation d’applications Internet à
partir d’un projet concret mais facilement portable. Au cours de mes études et de mon parcours professionnel, j’ai
étudié les langages du Web, l’installation d’un serveur Java, la mise en œ uvre d’un framework, les bases de données
pour Internet, les Servlets/JSP et le développement de projets mais aucun ouvrage ne regroupait tous ces aspects de
conception et certaines parties importantes n’étaient pas détaillées, comme s’il manquait quelques clés essentielles qui
ne s’acquièrent qu’avec l’expérience. La démarche pédagogique de ce guide est de ne pas faire l’impasse sur des
points essentiels d’un projet Web comme le déploiement sur un serveur en production avec un nom de domaine, la
mise en place d’un pool de connexion sur une base de données quelconque ou encore la configuration complète d’un
serveur Java. L’ouvrage utilise pour cela une mise en page adaptée afin de mettre en valeur les concepts essentiels à
partir de schémas, graphiques, etc.

© ENI Editions - All rigths reserved

- 1-

Les conventions
1. Les conventions du guide, de Java, de codage et les règles de nommage
Dans le cycle de vie d’un produit logiciel, la phase de maintenance représente la majeure partie du temps (environ 80
%). De même, un logiciel est rarement développé par une seule et même personne. C’est une équipe entière qui
réalise le projet de développement avec tous les avantages mais aussi toutes les contraintes que cela implique.
La réussite d’un projet dépend beaucoup de l’homogénéité dans le codage. Cette étape essentielle passe par la mise
en œ uvre de conventions strictes respectées par toute l’équipe impliquée dans le projet. La plupart des outils de
développement (IDE) proposent des fonctionnalités pour permettre cette homogénéité mais il existe des règles que
seuls les développeurs doivent appliquer. Le fait de vouloir à tout prix avoir un code esthétique et de faible
complexité peut être un frein aux performances. À l’inverse, la course à la performance peut découler sur un code peu
lisible. Il revient donc aux développeurs de trouver le bon équilibre entre les conventions et les contraintes du projet
(projet axé sur la performance, projet OpenSource...).

2. Les conventions du guide
Tout au long de ce guide seront utilisées des conventions pour la rédaction des explications, les parties de code, les
rappels et les résumés. Les explications seront rédigées sous format textuel et graphique avec un maximum de
clarté. Les parties de code seront bien différenciées par la police de caractères et seront encadrées. Les schémas
viendront appuyer une explication et permettront d’expliquer sous forme graphique une convention, un choix, une
spécification ou un exemple.

3. Les conventions de codage
Des conventions de codage sont utilisées tout au long de ce document. Par défaut, les conventions de codage pour la
plupart des projets OpenSource suivent ces instructions. Par exemple, si la parenthèse { est après la condition if, le
code n’est pas correct.
Tous les blocs de code doivent commencer par une nouvelle ligne.
public class MaClasse
{
public void maMethode()
{
if (xxx)
{
}
}
}
Chaque conditionnelle contient les parenthèses ouvrantes et fermantes.
//Correct
if (expression)
{
//le code
}
//Incorrect
if (expression)
//le code
Une indentation se trouve après chaque instruction. Les noms des fonctions et des paramètres, ne doivent pas avoir
de préfixe, commencent par une minuscule et chaque partie de mot est en majuscule.
public class MaClasse
{
private String maChaine;
public void maMethode(String monParametre)
{
}

© ENI Editions - All rigths reserved

- 1-

}
La version de l’application (du code) est précisée dans chaque fichier d’extension .java.
@version 2.8
Le nom de l’auteur est précisé.
@author jeromelafosse
Chaque importation de paquetage (paquet) Java est pleinement qualifiée.
//Correct
import java.util.Date;
import java.net.HttpURLConnection;
//Incorrect
import java.util.*;
import java.net.*;

4. Les conventions Java
Pour utiliser efficacement le langage de programmation Java, il existe plusieurs conventions à connaître et à
appliquer. Les instructions Java se terminent par un point­virgule. Les instructions Java utilisent des accolades { }
pour indiquer le début et la fin du corps. Un corps peut contenir plusieurs instructions.
La présentation avec les accolades alignées sur la même colonne que le premier caractère de l’instruction Java sera
utilisée. Cette présentation ajoute des lignes de code, mais elle est plus facile à lire. Il est important de mettre en
retrait (espace ou tabulation) les instructions Java qui comportent un corps. Il est également nécessaire de toujours
utiliser la même mise en retrait du code.
Java recourt comme tout langage à plusieurs mots­clés, c’est­à­dire des mots réservés exclusivement au langage. Il
ne sera donc pas possible d’utiliser ces mots­clés comme noms ou valeurs de variables.
Voici une liste non exhaustive de ces mots­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...
Le code doit être succinct, cela facilite la maintenance et la lisibilité de l’ensemble. Il vaut mieux découper parfois des
méthodes et ajouter des commentaires. Les recommandations doivent être appliquées sur l’intégralité du projet et
non avec parcimonie. Si les règles sont plus ou moins appliquées, si le code change d’un fichier à l’autre, la
compréhension sera difficile. L’application uniforme des règles est un gage de maintenabilité.
Fichiers
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. Le fichier de construction du projet porte le nom build.xml (Ant) et le fichier de description du projet
est appelé README.
Un projet de développement utilise plusieurs répertoires. Il est généralement composé du répertoire /src qui contient
les sources, du répertoire /build qui contient les classes compilées, /docs qui contient la documentation du projet
et /log qui contient les traces d’exécutions et d’erreurs du projet.
Souvent, pour un projet minime, seuls les répertoires /src, /build et /docs sont utilisés.
Les classes doivent être regroupées en packages (paquetages ou paquets).
Sources
Il est recommandé de ne pas dépasser 2000 lignes de code par fichier. Si tel est le cas, il est important d’optimiser le
code, de vérifier s’il n’existe pas de redondance et de découper les fonctionnalités en plusieurs classes (boîte à outils
par exemple).
Formatage
Il faut configurer l’éditeur pour que la tabulation écrive huit caractères espace (configuration par défaut). L’entrée
dans un bloc impose l’ajout d’une indentation. Des blocs de même niveau doivent débuter sur la même colonne, c’est­
à­dire avoir la même indentation. Les lignes blanches doivent être utilisées pour séparer des portions de code et les
méthodes. Les espaces peuvent être utilisés en quantité mais sous certaines conditions. Le caractère espace est

- 2-

© ENI Editions - All rigths reserved

proscrit avant les points­virgules, avant les crochets des tableaux et entre une variable et les opérateurs de pré/post
incrément. Par contre, l’espace est autorisé après les virgules, avant et après les accolades, avant et après chaque
opérateur, après les mots réservés du langage et entre le nom d’une méthode et la parenthèse de ses paramè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);
Nommage
Les noms utilisés doivent être explicites, c’est­à­dire que le nom doit expliquer le contenu de l’objet, le rôle de la
méthode... Les acronymes qui apparaissent dans les noms doivent être passés en minuscules (sauf la première lettre
du premier mot). Il est conseillé de mettre les identifiants en langue anglaise pour des projets internationaux.
Package/Paquetage
Les noms des paquetages doivent être en minuscules. Ces noms doivent être pleinement qualifiés (comme une URL)
et reprennent le nom du projet, l’URL du site... En général, la technique consiste à retourner l’URL du projet.
Exemple : monprojet.com devient : com.monprojet.monpaquet
//Correct
package com.monprojet.monpaquet;
//Incorrect
package Com.MonProjet.MonPaquet;
Classes et interfaces
Les noms des classes doivent être en minuscules, hormis les initiales des mots qui les composent.
//Correct
class MaClasseFavorite;
//Incorrect
class maclassefavorite;
class maClassefavorite;
Méthodes
Les noms des méthodes doivent être en minuscules hormis les initiales des mots qui composent les mots (sauf la
première lettre).
//Correct
public void maMethodeFavorite() {
//Incorrect
public void mamethodefavorite() {
Les accesseurs directs (getters et setters) des attributs d’une classe doivent être préfixés d’un get pour la lecture de
l’attribut et d’un set pour l’écriture. Le préfixe is doit être utilisé pour les méthodes qui retournent un boolé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() {
Nous pouvons également utiliser d’autres mots pour les recherches, les suppressions, les ajouts, la fermeture de
connexions... (find, delete, add, close...).

© ENI Editions - All rigths reserved

- 3-

Attributs, variables et paramètres
Les attributs des classes , les variables ainsi que les paramètres des méthodes doivent être en minuscules hormis les
initiales des mots qui les composent (sauf le premier). Les variables de boucle doivent porter une seule lettre : i, j, k...
Les signes dollars ($) et soulignement (_) sont proscrits.
//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;
Les collections doivent être nommées au pluriel.
//Correct
Vector comptes;
Collection banques;
Object[] mesObjets;
//Incorrect
Vector compte;
Collection banque;
Object[] monObjet;
Constantes
Les noms des constantes doivent être écrits entièrement en majuscules. Le séparateur de mot est le caractère de
soulignement (underscore).
//Correct
static final
//Incorrect
static final
static final
static final

int LOG_CONSOLE = 1;
int LOGCONSOLE = 1;
int console_Log = 1;
int Console_LOG = 1;

Commentaires
Les commentaires sont essentiels dans un code source. Ils permettent de documenter le projet à l’intérieur même du
code source en vue de la génération de la documentation via l’outil JavaDoc. Il existe en Java deux types de
commentaires :
●

les commentaires mono­ligne qui permettent de désactiver tout ce qui apparaît sur la même ligne // ;

●

les commentaires multilignes qui permettent de désactiver tout le code qui se trouve entre les deux
délimiteurs /* */.

Il est important de réserver les commentaires multilignes aux blocs utiles à la JavaDoc et à l’inactivation de portions
de code. Les commentaires mono­ligne permettent de commenter le reste, à savoir, toute information de
documentation interne aux lignes de code.
/*
* La classe MaClasse permet telles fonctionnalités…
*/
public class MaClasse() {
// Recuperer un objet de la collection
monFichier = (Fichier)fichiers.get((int)item.getIdFichier());
Déclarations
Les variables doivent être déclarées ligne par ligne. L’initialisation doit se faire lors de la déclaration lorsque cela est
possible. Les variables doivent être déclarées au plus tôt dans un bloc de code. Les noms des méthodes sont accolés
à la parenthèse ouvrante listant leurs paramètres. Aucun espace ne doit y être inséré.
//Correct
int niveau = 10;
void maMethode() {

- 4-

© ENI Editions - All rigths reserved

//Incorrect
int niveau;
niveau = 10;
void maMethode () {
Ordre
L’ordre de déclaration des entités du code source doit être le suivant (qui est plus ou moins naturel) :
●

Les attributs de la classe (1­> statiques, 2­>publiques, 3­>protégés, 4­> privés).

●

Les méthodes de la classe (1­>statiques, 2­>publiques, 3­>protégées, 4­>privées).

Instructions
Une ligne de code ne peut contenir qu’une seule instruction.
//Correct
count++;
i--;
println("Bonjour");
//Incorrect
count++; i--; println("Bonjour");

© ENI Editions - All rigths reserved

- 5-

Définitions de J2EE/Java EE
De nombreuses possibilités existent pour réaliser des applications Internet depuis plusieurs années. Des langages ont
été créés, des architectures et des environnements de travail ont été conçus pour répondre aux besoins et faciliter la
tâche des développeurs. Sun (le concepteur de Java) a donc mis en place un ensemble de technologies pour réaliser
des applications Web. Ces technologies sont regroupées sous le nom J2EE (Java 2 Entreprise Edition), désormais Java
EE.
Depuis la version 5, le chiffre 2 a disparu pour faciliter la compréhension de la version et ne pas mélanger le
chiffre 2 avec le numéro de version.
La plate­forme Java EE s’appuie entièrement sur le langage Java. Java EE est donc une norme, qui permet à des
développeurs, entreprises et SSII de développer leur propre application qui implémente en totalité ou partiellement les
spécifications de SUN. En simplifiant, il est possible de représenter Java EE comme un ensemble de spécifications d’API,
une architecture, une méthode de packaging et de déploiement d’applications et la gestion d’applications déployées sur
un serveur compatible Java.
Une API est un ensemble de librairies ou bibliothèques de fonctions destinées à être utilisées par les
programmeurs dans leurs applications.
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).
Java Entreprise Edition est apparue à la fin des années 90. Cette évolution apporte au langage Java une plate­forme
logicielle robuste et complète pour le développement. La plate­forme Java EE a souvent été remise en cause, mal
utilisée et mal comprise. Des outils OpenSource sont venus la concurrencer. Ces remarques et la concurrence ont permis
à Sun d’améliorer son produit et d’éditer des versions de plus en plus abouties. Java EE ne remplace en aucun cas J2SE.
Au contraire, J2SE est la base de Java EE qui est plus complet et qui est axé sur le Web. La plate­forme J2SE offre des
outils de développement d’applications client/serveur, applications graphiques fenêtrées et Applets.
La plate­forme J2SE est composée des éléments suivants :
●

La machine virtuelle Java (JVM) : permet d’exécuter des applications Java. Elle constitue une passerelle et
permet une portabilité entre les architectures (Windows, Linux, Mac...).

●

La bibliothèque de classes Java : un ensemble de composants logiciels prêt à l’emploi.

●

Les outils de développement : le compilateur javac , un interpréteur Java nommé java, le générateur de
documentation javadoc, la console de supervisation Jconsole... La plate­forme Java EE est une extension de la
plate­forme J2SE. Elle permet un développement d’applications qui vont s’exécuter sur un serveur d’applications.
Les applications seront utilisées par des clients légers (comme des navigateurs Web) ou bien des applications
lourdes (IHM). La dernière version stable de Java EE est la version Java EE 5.0 et fonctionne avec le JDK 5.0 et
6.0.

1. Pourquoi choisir Java EE
Il existe actuellement beaucoup d’autres plates­formes de développement qui sont basées sur d’autres langages (C#,
PHP5, .NET...). Les principaux avantages d’utiliser Java EE (et donc Java) sont la portabilité, l’indépendance, la sécurité
et la multitude de librairies proposées.
Le développement d’applications d’entreprise nécessite la mise en œ uvre d’une infrastructure importante. Beaucoup
de fonctionnalités sont utilisées et développées, le but étant de produire des applications sûres, robustes et faciles à
maintenir. Certains services sont d’ailleurs récurrents comme : l’accès aux bases de données, l’envoi de mails, les
transactions, la gestion de fichiers, la gestion d’images, le téléchargement, le chargement ou upload, la supervision du
système...
C’est pour cela que l’architecture Java EE est intéressante car tous les éléments fondamentaux sont déjà en place.
Pas besoin de concevoir une architecture , des librairies et des outils spécialement adaptés. Cela nécessiterait un
temps et un investissement considé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émentation est gratuite et permet de bénéficier de la totalité de l’API sans investissement. La plate­forme Java EE
est la plus riche des plates­formes Java et offre un environnement standard de développement et d’exécution
d’applications d’entreprise multi­tiers.

© ENI Editions - All rigths reserved

- 1-

Le fait que Java EE soit standardisé a contribué à son adoption par de très nombreux éditeurs de logiciels/outils
informatique. Ces éditeurs associés à Sun Microsystems font partie du JCP (Java Community Process). Le Java
Community Process regroupe les entreprises suivantes : Sun, IBM, Oracle, Borland, Nokia, Sony, la fondation Apache,
ObjectWeb... L’objectif du JCP est de définir les spécifications des technologies basées sur Java.
Chaque demande de modification est appelée une JSR (Java Specification Request).

2. L’API Java EE (JDBC, Servlets, JSP)
La plate­forme Java EE est composée de plusieurs API (ensemble de libraires et services). Java EE fait intervenir trois
types de composants logiciels (Servlets, JSP, EJB).

a. Les Servlets
L’API Servlet fournit les éléments nécessaires à la conception de composants Web dynamiques avec le langage Java.
Les Servlets sont des composants logiciels entièrement écrits en Java. Les Servlets effectuent des traitements côté
serveur en réponse aux requêtes des clients distants. Une Servlet est chargée en mémoire lors de son premier
appel. De même, il n’existe qu’une seule instance d’une Servlet en mémoire, le serveur utilise alors un thread global
pour traiter les demandes émises par les clients.
Cycle de vie d’une Servlet
Une Servlet est une classe Java. Cette classe doit être chargée puis interprétée par une machine virtuelle Java (celle
du serveur d’applications). La Servlet est alors prête à recevoir des requêtes et à renvoyer des réponses. Lorsque
l’application ou le serveur s’arrête, la Servlet est détruite, puis son instance est nettoyée par le ramasse­miettes de
la machine virtuelle.

Les Servlets permettent de développer des pages dynamiques, dont le contenu est créé à la volée sur demande.
C’est le cas par exemple, lorsqu’un client souhaite obtenir la liste des articles d’une boutique pour une plage de prix.
Les pages HTML sont alors générées dynamiquement en fonction de critères spécifiques (prix, dates, recherches...).

Une Servlet est un composant Java qui implémente l’interface javax.servlet.Servlet. Cette interface permet de gérer
les requêtes du client, dirigées vers la Servlet en question. Le serveur reçoit une demande adressée à une Servlet
sous la forme d’une requête HTTP. Il transmet alors la requête à la Servlet concernée par le traitement puis renvoie
la réponse fournie par celle­ci au client. La Servlet est gérée par le conteneur de Servlets Java EE. Lorsque le serveur
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.Servlet afin de satisfaire la requête.

- 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ès aux bases de données et autres avec une excellente portabilité.
Exemple de Servlet simple :
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. Les JSP (Java Server Page)
L’API JSP permet de développer des pages Web dynamiques rapidement à partir d’un squelette HTML et de données
incluses directement dans chaque page. Les JSP permettent d’insérer des bouts de code Java (scriptlets)
directement dans le code HTML.
Du point de vue de la structure, une JSP est très proche d’une page PHP ou bien ASP. Une page JSP est un fichier qui
porte l’extension .jsp ou .jspf (pour les fragments de code).
Lors de la création de Servlets, le but est de construire des composants capables de produire un service
(essentiellement du code HTML). Toutefois, ce principe est parfois complexe pour des personnes qui ne sont pas
habituées à la programmation objet et au code 100% Java. C’est pour ces raisons que les développeurs de chez
SUN ont inventé JSP.
La page JSP est transformée en classe Java puis compilée en Servlet par le serveur d’applications. Ce traitement est
réalisé par le serveur d’applications au premier appel de la page et à chaque fois que cette page est modifiée par un
programmeur.
C’est cette étape qui nécessite un serveur d’applications Java EE, un compilateur Java et qui par conséquent
nécessite pour la majorité l’installation de Java avec un JDK plutôt qu’un JRE.
Cycle de vie d’une JSP

© ENI Editions - All rigths reserved

- 3-

Le résultat de la compilation (essentiellement du code HTML) est renvoyé au client. Cette technologie est simple,
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("

Ma première Servlet

"); %> Il est donc possible d’avoir des équipes de développement séparées avec une personne spécialiste de HTML/XHTML et du design et un programmeur Java qui réalise les scriptlets. Les JSP sont exécutées sous la forme de Servlets, elles disposent donc des mêmes fonctionnalités que celles­ci et peuvent ainsi manipuler les sessions, les bases de données, les mails... - 4- © ENI Editions - All rigths reserved c. LES EJB (Entreprise Java Bean) Les EJB sont des composants métier distribués, c’est­à­dire qu’ils sont invocables par le réseau. Un composant EJB est une classe qui possède des attributs et méthodes pour mettre en application la logique métier. L’API EJB fournit un ensemble de services (persistance, transaction...) de gestion de composants. Il existe plusieurs (trois) types d’EJB : les beans sessions, les beans entités et les beans contrôlés par message. ● EJB session : il permet de maintenir des informations sur les clients et les traitements qu’ils réalisent. ● EJB entité : c’est un composant persistant, son état est sauvegardé dans une base de données. ● EJB piloté par message : les EJB message sont semblables aux EJB session mais sont invoqués différemment (par le biais de Java Message Service). La mise en place d’EJB nécessite l’utilisation d’un serveur d’applications capable de gérer ces EJB. Actuellement, les serveurs GlassFish, JBoss et Jonas existent dans le domaine du libre. Le serveur Tomcat ne permet pas d’utiliser les EJB. Il existe ensuite au sein de Java EE, la plate­forme de Services avec JDBC, JNI, JavaMail, JTA, RMI, JAAS et XML. JDBC (Java DataBase Connectivity) L’API JDBC permet de faciliter l’obtention de connexions JDBC vers des sources de données (essentiellement des bases de données, mais également annuaire...). L’API fournit les librairies pour se connecter aux bases de données et pour la gestion des transactions. JNDI (Java Naming and Directory Interface) L’API JNDI permet d’accéder à des services de nommage ou d’annuaire (LDAP par exemple). Cette API est par exemple utilisée pour se connecter à une source de données pour des accès à la base de données ou la gestion des accès (associée aux Realms). JNDI permet d’implémenter un service de nommage. L’ensemble des ressources que le serveur d’applications met à disposition via ces API de services, doit être enregistré avec un nom logique unique, permettant aux applications de rechercher cette ressource dans le serveur. JavaMail L’API JavaMail fournit des fonctionnalités de gestion de courrier électronique (transfert, type de contenu, pièces jointes...). JavaMail permet la création et l’envoi de messages électroniques via Java. Cette API permet de manipuler les protocoles de messagerie Internet comme POP, IMAP, SMTP. JavaMail n’est pas un serveur de courrier mais plutôt un outil qui permet d’interagir avec ce type de serveur. JPA (Java Persistance API) Les entités Beans ont été développées pour le modèle de persistance en Java EE. Ce modèle de composants avait 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èle de persistance nommé JPA. JPA s’appuie sur JDBC pour communiquer avec la base de données mais permet d’éviter de manipuler directement les fonctionnalités de JDBC et le langage SQL. 3. Les autres API Parmi les autres API Java EE, il faut citer : JMS (Java Message Service) permet d’accéder à un service de messages pour une gestion asynchrone des composants. Le composant appelant poste un message (en arrière­plan) à destination d’une file d’attente de messages hébergés par le serveur d’applications puis continue son traitement sans attendre. RMI (Remote Method Invocation) permet de concevoir des applications distribuées en Java. RMI permet l’appel de fonctionnalités à distance par le biais de la communication réseau. JTA (Java Transaction API) permet de mettre en place une gestion des transactions dans des applications distribuées (commit, rollback...). Le principe des transactions est de considérer un ensemble d’opérations comme une seule. Ce type de service est obligatoire pour des traitements bancaire. Par exemple, une application bancaire qui permet de réaliser des virements entre deux comptes va d’abord débiter le premier compte et ensuite créditer le second compte. Si le débit puis le crédit aboutissent sans problème, alors la transaction est validée. JDBC permet de gérer les transactions sur une base de données locale mais si les données sont réparties, il faudra alors utiliser les transactions JTA. JTA permet en effet de gérer les transactions distribuées qui font intervenir différentes bases de données. © ENI Editions - All rigths reserved - 5- JCA (J2EE Connector Architecture) : ce connecteur permet à Java EE d’utiliser des gros systèmes tels que les mainframes. JAAS (Java Authentication and Autorisation Service) est un mécanisme de sécurité géré par le serveur d’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. De même, XML est la base d’un nouveau mode de communication entre les applications qui est appelé Web Service. Par exemple, JAXP (Java API for XML Parsing) analyse des fichiers ou données XML, JAX­RPC (Java API for XML based RPC) utilise des Web Services et JAXB (Java API for XML Binding) permet de générer des classes Java à partir de schémas XML ou inversement. Actuellement, la version de Java EE est 5.0 associée à l’API Servlet 2.5, et à l’API JSP 2.1 (et à Apache­Tomcat 5.X­6.X). - 6- © ENI Editions - All rigths reserved Encodage des applications Java 1. Présentation Les ordinateurs travaillent avec des bits ou suites d’octets (un octet=8 bits). Les chaînes de caractères, entiers, réels sont donc codées sous forme d’octets. Les ordinateurs utilisent donc un procédé qui consiste à transformer les chaînes de caractères en octets, associé à une technique afin de relire les chaînes d’origine. C’est ce procédé qui est appelé encodage. Il existe plusieurs encodages qui utilisent plus ou moins le même nombre d’octets, donc de caractères disponibles comme ISO­8859­1, ASCII, UTF­8... L’encodage UTF­8 est le plus pratique pour échanger des textes constitués de caractères UNICODE (standard du consortium Unicode). Ce consortium a pour but de répertorier tous les caractères utilisés dans les différentes langues et d’associer à chacun un code noté sous forme hexadécimal. L’encodage UTF­8 est compatible avec l’encodage ASCII ce qui est très pratique lors des développements informatiques. Lors des développements d’applications Java et/ou Java EE, il n’est pas rare de constater de nombreux problè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 application Java nécessite donc la gestion de plusieurs paramètres. La difficulté est qu’il ne faut pas en oublier un seul sous peine de constater l’affichage de ’’hiéroglyphes’’ à la place du caractère souhaité. 2. Les fichiers La première contrainte à vérifier est que tous les fichiers (HTML, JSP, JSPF, XML, XSLT...) de l’application développée soient dans l’encodage souhaité. Pour cela, la plupart des IDE peuvent se paramétrer afin de sélectionner l’encodage. Avec Eclipse, nous pouvons sélectionner l’encodage dans le menu Fenêtre ­ Préférences ­ Editeurs ­ Codage du fichier texte et Types de contenu. 3. Les pages JSP et JSPF Il est nécessaire de déclarer dans chaque page JSP ou JSPF d’en­tête l’encodage utilisé. Pour cela, il faut utiliser la directive JSP adaptée. ou © ENI Editions - All rigths reserved - 1- <%@ page contentType="text/html;charset=UTF-8" %> Il est possible également de centraliser l’encodage dans le fichier de configuration et de déploiement de l’application web.xml du serveur Tomcat. Config. de l’encodage des pages JSP *.jsp UTF-8 4. Les pages HTML/XHTML Il est également important de prévenir le navigateur client de l’encodage qu’il doit utiliser pour afficher la page HTML/XHTML. Cette directive est précisée avec la balise meta et le paramètre content. ... Il est également possible de préciser l’encodage d’une feuille de style externe à l’aide de la directive placée en tout début de fichier. @charset "UTF-8"; 5. Les feuilles de style XSL Si des transformations XSLT sont utilisées dans notre application, il est nécessaire de déclarer également explicitement l’encodage dans ces pages. 6. Code Java Du point de vue du code Java, il est possible d’utiliser un filtre qui va forcer le serveur d’applications Java à lire les paramètres de la requête dans l’encodage souhaité et qui va renvoyer les réponses avec le même encodage. 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() {} } Il est alors possible de déclarer ce filtre au sein du fichier de configuration de l’application web.xml. Les filtres étant exécutés dans l’ordre de déclaration, ce mapping doit être le premier déclaré dans le fichier de configuration. encodingfilter application.filters.EncodingFilter encoding UTF-8 encodingfilter /* L’encodage peut aussi être géré au sein d’une Servlet générique. Chaque Servlet du projet devra alors ensuite hériter de cette Servlet. 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. Encodage de la JVM Il est important d’exécuter la JVM dans l’encodage voulu. Le traitement des chaînes de caractères doit être le même que le reste de l’application. C’est au lancement de la JVM, donc au lancement du serveur Java EE, que l’encodage est spécifié à l’aide de l’argument : -Dfile.encoding=UTF-8 Avec Tomcat, cet argument est spécifié dans le fichier de lancement du serveur, catalina.sh. JAVA_OPTS="$JAVA_OPTS "-Dfile.encoding=utf-8" Il est parfois également nécessaire de vérifier l’encodage des URL de l’application. Avec Tomcat, cet encodage est déclaré explicitement via l’attribut URIEncoding sur le connecteur Coyote. Voici la ligne du fichier server.xml concerné : © ENI Editions - All rigths reserved - 3- Le code suivant est très utile car il permet la transformation d’une chaîne de caractères dans un encodage pré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. Gestion de l’encodage Il est tout à fait possible en Java de gérer l’encodage à utiliser. Pour les flots d’entrée par exemple, la connexion est réalisée à l’aide de la classe InputStreamReader. Le jeu de caractères à utiliser peut être alors précisé. Par défaut, le jeu de caractères est fonction du système d’exploitation utilisé et de la localisation (ex : fr_FR UTF­8). Avec un flot, il est possible de préciser l’encodage utilisé : InputStreamReader i=new InputStreamReader(is,"UTF-8"); Le nom du jeu de caractères utilisé par défaut est obtenu en programmation avec la méthode System.getProperty() et le paramètre file.encoding. package com.betaboutique.classes; public class Programmation { public static void main(String[] args) { System.out.println("Encodage : "+System.getProperty("file.encoding")); } } Lors des développements Web, il est assez courant que les paramètres reçus par les méthodes HTTP GET et POST ne soient pas dans un format correct. Les problèmes portent alors sur les accents, les caractères spéciaux... Pour cela, la transformation d’un encodage peut être forcé en utilisant les octets. Le code ci­dessous permet de transformer un paramètre reçu en caractères UTF­8. String parametre=(String)request.getParameter("parametre"); String parametreUTF8=new String(parametre.getBytes(),"UTF-8"); De même pour les envois d’informations en programmation Java à travers des flux, l’encodage et les transformations de jeux de caractères sont utilisé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"); } } } © ENI Editions - All rigths reserved - 5- Les architectures Web 1. Les types d’architectures Dans les applications Web, la communication entre le client et le serveur est réalisée selon le protocole TCP/IP qui est chargé du routage des données. Le transit des informations s’effectue selon le protocole HTTP pour le Web, les données sont alors transmises entre le client et le serveur via TCP/IP. On distingue alors deux types de clients : ● Le client léger : il est aussi appelé client Web car le module d’exécution est alors un navigateur. Les applications clientes sont composées de pages HTML/XHTML voire DHTML avec l’utilisation du langage client JavaScript . ● Le client lourd : il s’agit d’une application composée d’une interface graphique évoluée ou en mode console. Dans l’idéal, les clients lourds communiquants ne contiennent que la logique présentation (affichage des données). Tous les traitements sont délégués à des composants métier distants. Il existe actuellement un grand nombre d’architectures utilisées pour le Web. L’architecture 2­tiers est composée de deux éléments, un client et un serveur. Cette architecture physique simple peut être représentée de cette façon : Cette architecture peut aussi être représentée avec un serveur de base de données (SGBD), le schéma est alors le suivant : Dans ce type d’architecture, le client assume les tâches de présentation et communique uniquement avec le serveur d’applications. Le client est dit ’’lourd’’. Ce type d’architecture peut être développé très rapidement en fonction de la complexité du projet. Il existe un très grand nombre d’outils de développement et de langages pour les architectures 2­tiers. Du point de vue des inconvénients, le problème d’évolutivité, de maintenance et la mise en place lors de projets complexes peuvent être cités. Dans l’architecture 3­tiers, le client est constitué d’un simple navigateur Internet et communique avec le serveur. Cette architecture est composée de trois éléments ou trois couches. La couche présentation ou affichage est le client ’’léger’’ dans la mesure où il ne fait aucun traitement. La couche fonctionnelle ou métier est en général un serveur Web. Et enfin, la couche de données est liée au serveur de bases de données (SGBD). ● La couche présentation (de premier niveau) souvent appelée IHM (Interface Homme Machine) correspond à la partie visible et interactive. Cette partie est réalisée pour le Web en HTML en général avec JavaScript, Flash... © ENI Editions - All rigths reserved - 1- ● La couche métier (de second niveau) correspond à la partie fonctionnelle de l’application. Les opérations à réaliser, les fonctions d’accès aux données et les traitements sont mis à la disposition des utilisateurs et invoqués par leurs requêtes. Pour fournir ces services, elle s’appuie parfois sur la couche accès aux données et en retour renvoie à la couche présentation les résultats qu’elle a calculés. ● La dernière couche (de troisième niveau) gère l’accès aux données du système. Ces données peuvent être stockées sur le même système (fichiers, fichiers XML, base de données, images...) ou sur d’autres systèmes. L’accès aux données est transparent pour la couche métier et correspond uniquement à la préoccupation de la couche accès aux données. D’une manière générale cette abstraction améliore la maintenance du système. Parmi les avantages de cette architecture, la flexibilité de l’ensemble peut être citée. La partie client est composée uniquement d’affichage (pas de programmation, de requêtes SQL...). De fait, des modifications peuvent être réalisées au niveau du SGBD sans que cela apporte un impact sur la couche client. De même, par la suite toute nouvelle technologie peut être introduite sans tout remettre en question. Du point de vue développement, la séparation entre le client, le serveur et le SGBD permet une spécialisation des développeurs et une meilleure répartition des tâches et fonctions (développeur de modèle/designer, programmeur, administrateur de bases de données...). Le gros inconvénient de ce modèle (et le principal), est l’expertise qu’il est nécessaire d’avoir et qui est assez longue à obtenir pour bien maîtriser chaque tiers et interconnexions. Les coûts de développement d’une architecture 3­tiers sont plus élevés que pour du 2­tiers. L’architecture n­tiers a été pensée pour pallier les limitations des architectures 3­tiers et concevoir des applications puissantes et simples à maintenir. D’un point de vue théorique, cette architecture permet de solutionner les problèmes suivants : ● Elle permet l’utilisation de clients riches. ● Elle sépare nettement tous les niveaux de l’application. ● Elle facilite la gestion des sessions. ● Elle offre de grandes capacités d’extension. Une définition possible de ce type d’architecture est : une architecture 3­tiers dans laquelle le traitement des données (couche accès aux données ou middleware) contient lui­même plusieurs couches multipliant ainsi les tiers. Les types d’architectures en Java EE Dans ce cas, l’application cliente peut être développée avec des composants graphiques (Swing par exemple) et faire appel à des règles métier EJB qui accèdent à une base de données. Il est aussi possible de trouver des applications clientes avec JSP, EJB et base de données. Le client est un navigateur Web. Les pages JSP accèdent aux règles métier et construisent le contenu HTML fourni au navigateur. - 2- © ENI Editions - All rigths reserved Un dérivé de l’architecture précédente est une Applet cliente avec JSP et base de données. Dans ce cas, le client est 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ées nécessaires grâ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 pratiquement le même cas que l’architecture avec Applet si ce n’est que le client est une application téléchargeable et utilisable de manière autonome (sans navigateur) mais uniquement avec une connexion réseau. Le dernier type d’architecture repose sur l’intégration de services Web. Une application cliente accède aux données grâce à un service Web programmé en Java. © ENI Editions - All rigths reserved - 3- Les architectures proposées par Java EE reposent sur le découpage des applications en plusieurs tiers aux responsabilités clairement séparées. Les programmeurs vont développer des composants qui seront hébergés par un serveur d’application Java EE (Tomcat, JBoss...). Les applications distribuées (réalisées sous forme de composants distincts) permettent de diviser le logiciel en plusieurs couches appelées tiers (chaque couche représente un tiers). Le modèle le plus courant étant l’architecture 3­tiers/n­tiers. Cette division facilite la maintenance et l’adaptabilité du produit. 2. L’architecture MVC (Model View Controller) L’architecture MVC proposée par Sun est la solution de développement Web côté serveur qui permet de séparer la partie logique/métier de la partie présentation dans une application Web. C’est un point essentiel du développement de projets car cela permet à toute l’équipe de travailler séparément (chacun possède ses fichiers, ses logiciels de développement et ses composants). Cette architecture trouve son origine dans le langage SmallTalk au début des années 1980, ce n’est donc pas un modèle (design pattern ) nouveau uniquement lié à Java EE. L’objectif principal est de diviser l’application en trois parties distinctes : le modèle, la vue et le contrôleur. Dans l’architecture MVC, nous retrouvons : ● Le modèle qui est représenté par les EJB et/ou JavaBeans et/ou systèmes de persistances (Hibernate, objets sérialisés en XML, stockage de données par le biais de JDBC...). ● La vue qui est représentée par les JSP. ● Le contrôleur qui est représenté par les Servlets. Principe de fonctionnement de l’architecture MVC - 4- © ENI Editions - All rigths reserved ● 1. Le client envoie une requête HTTP au serveur. C’est en général une Servlet (ou un programme exécutable côté serveur) qui traite la demande. ● 2. La Servlet récupère les informations transmises par le client et délègue le traitement à un composant métier adapté. ● 3. Les composants du modèle manipulent ou non des données du système d’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. La Servlet stocke alors le résultat dans un contexte adapté (session, requête, réponse...). ● 5. La Servlet appelle la JSP adéquate qui peut accéder au résultat. ● 6. La JSP s’exécute, utilise les données transmises par la Servlet et génère la réponse au client. Les composants sont bien sûr plus nombreux mais également plus simples. Leurs spécificités font qu’ils pourront être développés par des spécialistes : les Servlets et EJB par des développeurs Java, les JSP par des développeurs et Webdesigner, les accès aux données par des spécialistes SQL... Ce découpage permet également une maintenance plus aisée du système. Ainsi, le changement de la charte graphique sera opéré facilement en utilisant les vues sans toucher au modèle et au contrô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...) de développement entièrement basés sur ce modèle ont été développés (Apache Struts/Spring). Le schéma complexe ci­dessous reprend un exemple de mise en place d’une telle architecture dans un projet d’entreprise. 3. Les différents modules Java EE Comme indiqué précédemment, les architectures Java EE offrent de multiples possibilités. Il est donc nécessaire d’organiser les différents éléments d’une application en fonction de leur rôle. Module Web Le module Web contient les éléments d’une application Java EE qui vont permettre l’utilisation de cette application au travers d’un navigateur Web et de toute application utilisant le protocole HTTP. Les éléments regroupés dans ce module Web sont les Servlets, les JSP et les ressources statiques de l’application Internet (images, JavaScripts, fichiers statiques...). Il y a également des bibliothèques dynamiques développées en Java fournies sous forme de fichiers .jar et qui sont utilisées par les Servlets et/ou JSP (manipulation d’images, de fichiers...). Les modules Web possèdent un descripteur de déploiement, le fichier web.xml. L’ensemble des fichiers est regroupé dans un fichier d’extension .war signifiant WebARchive. Module EJB/composants métier Les composants EJB sont constitués de fichiers de code Java. Il y a aussi dans ce module des bibliothèques au © ENI Editions - All rigths reserved - 5- format .jar. Les modules sont ensuite assemblés en archive d’extension .jar. Module Client Il est possible d’utiliser un client riche avec une interface graphique développée en utilisant les API de programmation Java comme Swing et/ou AWT. Un module client permet un assemblage en classes et fournit un descripteur de déploiement. Le module client est un fichier d’archive portant l’extension .jar. - 6- © ENI Editions - All rigths reserved Mise en place de l’environnement L’interface Java EE permet de créer des sites Web dynamiques avec une technologie Java. La mise en place d’un environnement Java EE nécessite l’utilisation d’un serveur d’applications capable d’exécuter le code et de répondre aux requêtes des clients. GlassFish, Jonas, JBoss, WebSphere et Apache­Tomcat font partie de ces serveurs d’applications Java. Il est également nécessaire d’utiliser un environnement de développement évolué. Il n’est pas possible de développer de manière confortable des centaines de fichiers sources, la documentation, les fichiers de configuration avec un simple éditeur de texte et le compilateur en ligne de commandes. Il existe plusieurs grands IDE Java : Eclipse et ses différentes versions, JBuilder, NetBean... Eclipse est très puissant, il dispose d’une grande panoplie de plug­ins pour l’interfacer avec Tomcat, pour manipuler Mysql, pour gérer les fichiers XML, les JSP, le code JavaScript... Enfin, point important, Eclipse est un projet OpenSource gratuit. Pour la réalisation des pages et des exemples, la version Lomboz d’Eclipse sera utilisée (Eclipse + ensemble de plug­ ins pour Java EE). Cette version complète d’Eclipse a été développée par le consortium ObjectWeb. L’installation sera expliquée pour un système Windows et pour un système Linux. Vous remarquez alors l’intérêt de 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’installation avec un petit projet plutôt que de rencontrer des problèmes ensuite lors de la réalisation d’exemples complexes. 1. Installation du JDK (Java Development Kit) Le Java Development Kit (couramment abrégé en JDK) est l’environnement dans lequel le code Java est compilé pour être transformé en bytecode afin que la JVM (machine virtuelle Java) puisse l’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éveloppement Java. En effet, avant l’installation du serveur Java EE, il est impératif que le JDK soit installé sur la machine où celui­ci sera installé. Le serveur d’applications fonctionne en Java et a donc besoin lui aussi du JDK pour travailler. Le JDK fournira un compilateur Java nécessaire pour le traitement des JSP. Avant d’installer une version de développement Java, il est nécessaire de vérifier si le système actuel ne possède pas déjà une version de Java. Pour vérifier si le système actuel possède une version de Java, il faut ouvrir une invite de commandes Ms­DOS et lancer la commande : java -version Nous pouvons voir que la version indiquée dans cet exemple est Java 1.6 (ou également appelée 6.0). Le compilateur utilisé est J2SE (il aurait été aussi possible d’utiliser Java EE). Si cette commande n’indique aucun résultat, c’est que le JDK n’est pas installé ou que la variable d’environnement PATH n’est pas correctement configurée sur le système. Les variables d’environnement sont des raccourcis utilisés par Windows et Linux pour désigner certains dossiers (exécutables) du disque dur (ex : pas besoin de saisir /usr/bin/java/javac, le raccourci javac suffit). Une fois les variables d’environnement correctement configurées, l’exécution de vos applications sera beaucoup plus simple. Nous pouvons également obtenir des informations sur l’API Java en utilisant sous Windows le panneau de configuration. L’icône Java ouvre une fenêtre avec les onglets suivants : Général ­ Mise à jour ­ Java ­ Sécurité et Avancé. a. Installation sous Windows Une fois ces vérifications effectuées, l’installation de Java EE s’effectue simplement en exécutant le fichier téléchargé sur le site de Sun Microsystems (http://java.sun.com/). Le programme d’installation démarre puis indique les étapes à suivre. © ENI Editions - All rigths reserved - 1- La page suivante permet de télécharger le JDK/Java EE/ Java SE : 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’installation s’effectue toute seule après avoir indiqué le répertoire d’installation des bibliothèques. 1 ­ Lancement de l’installation 2 ­ Acceptation des conditions - 2- © ENI Editions - All rigths reserved 3 ­ Choix du répertoire d’installation 4 ­ Fin de l’installation b. Installation sous Linux L’installation sous Linux nécessite le téléchargement de la librairie au format .bin. Toujours sur la même page, il est possible de télécharger la version du JDK pour Linux. La page suivante permet de télécharger le JDK/J2EE/JSE : https://sdlc5b.sun.com/ECom/EComActionServlet;jsessionid=BB61072B1EEB17E1232E99D84013FACB La version Linux Platform ­ Java EE SDK d’environ 150 Mo permet une installation sous Linux. L’installation sous © ENI Editions - All rigths reserved - 3- Linux est un peu différente de celle sous Windows. Après le téléchargement de la bibliothèque, il est nécessaire de copier celle­ci dans un répertoire de librairies Linux (en général /usr/local/src). Exemple avec le jdk­1.6.0 : #mv /home/jdk-1_6_0-linux-i586.bin /usr/local/src Puis, il faut rendre ce paquet exécutable. #chmod +x jdk-1_6_0-linux-i586.bin L’installation peut être ensuite lancée. #./jdk-1_6_0-linux-i586.bin Tout le répertoire est alors décompacté dans /usr/local/src. Il faut maintenant déplacer le répertoire du JDK dans un répertoire final d’installation (en général /usr/local). #mv /jdk.6.0_00 /usr/local Il faut ensuite se déplacer dans le répertoire /usr/local. #cd /usr/local Un lien (raccourci) appelé jdk peut être créé, il sera plus simple d’accès que le nom complet jdk.6.0_00. #ln -s jdk.6.0_00 jdk Il reste à placer les droits sur les fichiers de la librairie Java. #chmod 755 jdk.6.0_00 Java est désormais installé mais il faut encore paramétrer les variables d’environnement pour pouvoir lancer les commandes directement (sans indiquer le chemin complet, ex : /usr/local/jdk/java). Dans l’état actuel nous ne pouvons pas lancer Java directement : #java -version (not found...) Il existe plusieurs solutions pour cela : 1. Éditer le fichier /etc/profile et ajouter la ligne suivante dans le fichier. PATH=$PATH:/usr/local/jdk/bin 2. Éditer le fichier /root/.bashrc et ajouter la ligne suivante dans le fichier. export PATH=$PATH:/usr/local/jdk/bin 3. Éditer le fichier de l’utilisateur connecté et ajouter la ligne suivante dans le fichier. export PATH=$PATH:/usr/local/jdk/bin 4. Exporter la variable d’environnement PATH directement en ligne de commande dans un shell. export PATH=’’$PATH:/usr/local/jdk/bin’’ echo $PATH (pour vérifier) Pour que les modifications soient effectives, il faut fermer toutes les fenêtres et ouvrir un nouveau terminal. Attention, par la suite nous réaliserons un script de démarrage de Tomcat (serveur d’applications) qui exportera lui­même les variables d’environnement. Vous pouvez désormais vérifier que l’installation du JDK sous Linux est opérationnelle. - 4- © ENI Editions - All rigths reserved #java -version © ENI Editions - All rigths reserved - 5- Installation du serveur d’applications Java EE (Tomcat) Un serveur Java EE est aussi appelé serveur d’applications (applications signifiant applications Web). L’utilisation d’un serveur Java EE est obligatoire pour le développement de pages Web dynamiques en Java EE. Un serveur HTTP classique reçoit des requêtes HTTP et renvoie des réponses mais il ne connaît pas les Servlets, les JSP... Il est donc essentiel d’utiliser un programme appelé moteur de Servlets qui est contenu dans le serveur Java EE et qui permet de pallier ce manque. Dans la plupart des cas, le serveur Java EE contient également un serveur HTTP mais il n’est pas aussi puissant que les serveurs spécialisés du monde informatique pour les contenus statiques (Apache). Il existe un grand nombre de serveurs qui répondent à cette norme (Tomcat, WebSphere, JRun, JBoss, GlassFish...). Nous utiliserons le serveur Apache­Tomcat de la fondation Apache. Il est très important de comprendre que ces serveurs servent uniquement à fournir une plate­forme d’exploitation de l’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ée d’un point de vue rapidité, sécurité, facilité d’utilisation, montée en charge, gestion ou pas des EJB... Les versions majeures de Tomcat correspondent toutes à une implémentation de référence des technologies Servlet et JSP. Voici un bref rappel des relations entre les versions des technologies Java EE et les versions de Tomcat. Spécifications J2EE API Servlet API JSP Apache Tomcat J2EE 1.2 2.2 1.1 3.X J2EE 1.3 2.3 1.2 4.X J2EE 1.4 2.4 2.0 5.X Java EE 5.0 2.5 2.1 5.X ­ 6.X Comme indiqué précédemment, il est impératif que le JDK soit déjà installé avant l’installation du serveur d’applications. Le serveur Tomcat 6 est disponible en libre téléchargement. Les versions binaires de Tomcat sont en fait constituées de classes Java et sont donc portables entre les systèmes d’exploitation et les plates­formes matérielles. Il existe trois formats d’archives binaires : ● Les archives au format ZIP : une fois le répertoire décompressé, le serveur est directement opérationnel après configuration. Ce format est intéressant pour les administrateurs car il permet une mise à jour rapide en cas de changement de version du serveur. De plus, la configuration du système n’est pas modifiée, l’installation est transparente. ● Les archives au format TAR.GZ : c’est le format le plus commun sous les systèmes Linux. ● Les installeurs Windows : au format EXE permettent une installation à partir d’un assistant qui réalise également la configuration. C’est la méthode la plus simple pour installer Tomcat sur le système de Microsoft. 1. Quelle version choisir ? 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 requiert donc au minimum une machine virtuelle Java 1.5 pour fonctionner correctement. Pour le guide et le projet, la version de Tomcat utilisée est la version 6.0.16. Installation sous Windows L’installation de Tomcat version 6 suppose le téléchargement de la bibliothèque depuis le site de la fondation Apache à l’adresse suivante : http://tomcat.apache.org/ La section download permet de choisir la version adaptée à nos besoins. Les liens Core sont alors utilisés au format souhaité (Windows Service Installer par exemple) afin de télécharger Tomcat pour Windows (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’importance d’installer le JDK avant le serveur). © ENI Editions - All rigths reserved - 1- Tomcat 6 utilise un certain nombre de ports TCP/IP pour fonctionner. Il faut donc s’assurer que ces ports ne sont pas déjà utilisés : Port 8080 : port du connecteur HTTP Tomcat, Port 8005 : port d’arrêt du serveur, Port 8809 : port du connecteur JK. L’installation avec l’installeur Windows permet de créer les entrées dans le menu Démarrer de Windows ainsi qu’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 ­ Lancement de l’installation 2 ­ Acceptation des conditions - 2- © ENI Editions - All rigths reserved 3 ­ Création de l’utilisateur (User Name : admin, Password : admin) 4 ­ Suite de l’installation © ENI Editions - All rigths reserved - 3- 5 ­ Fin de l’installation Après l’installation, le fonctionnement du serveur peut être testé en lançant le moniteur Tomcat situé dans le menu démarrer. Le moniteur se lance dans le systray (barre des tâches à côté de l’horloge Windows) avec une icône rouge. Le service de monitoring est lancé mais le serveur pas encore. Pour réellement lancer le serveur Tomcat, il faut soit double cliquer sur l’icône soit faire un clic droit et start service. Dans cette fenêtre de monitoring, Tomcat peut être démarré en cliquant sur Start, stoppé avec le bouton Stop... Les autres onglets de la fenêtre permettent de gérer les logs, le JDK associé au serveur, le lancement au démarrage de la machine, les opérations d’arrêt du serveur... Vérifier que le serveur Java EE est actif Afin de vérifier que le serveur est correctement installé et opérationnel (capable d’exécuter des Servlets et pages - 4- © ENI Editions - All rigths reserved JSP), nous pouvons lancer un navigateur et saisir l’adresse suivante : http://localhost:8080/ Le nom localhost correspond à l’adresse locale de la machine (le serveur de la machine locale) et le port 8080 est le port HTTP pour Tomcat. Suivant le serveur utilisé (Tomcat, JRun...) le fonctionnement est identique, seul le port d’accès HTTP change (8080, 8200...). Le lien Tomcat Manager du menu permet de gérer les différentes applications déployées sur le serveur. Une authentification est nécessaire. Les coordonnées (identifiant et mot de passe) correspondent à celles saisies lors de l’installation du serveur (User Name : admin, Password : admin). Il peut être intéressant d’installer la partie administration de Tomcat. Pour cela, il faut télécharger le ZIP Administration Web Application (http://tomcat.apache.org) et décompresser ce fichier dans le répertoire d’installation de Tomcat. Il sera alors possible d’accéder à la partie administration du serveur à l’adresse suivante : http://localhost:8080/admin. Pour vérifier le fonctionnement du serveur, nous pouvons lancer les exemples présents, par défaut, sur le serveur avec des Servlets et des JSP. Pour l’installation sous Linux, voir le chapitre Le serveur d’applications Apache­Tomcat consacré à Tomcat. © ENI Editions - All rigths reserved - 5- Installation de l’environnement de développement (IDE) Eclipse 1. Présentation Eclipse est l’environnement de développement (spécialisé pour le langage Java) qui sera utilisé dans cet ouvrage. 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 (versions 2.1, 3.X). La version utilisée dans cet ouvrage correspond à Eclipse 3.3 Europa. 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/) est actuellement l’une des plus poussées et stables (parseur XML, syntaxe JSP, CSS, HTML, XHTML...). La version utilisée de Lomboz regroupe Eclipse et les plug­ins pour le développement Java EE. La version téléchargée est la suivante : http://lomboz.objectweb.org/downloads/drops/R­3.3­200710290621/ Il existe une version pour Windows et une autre pour Linux. Il est important de télécharger le projet complet (Lomboz Complete Installation) afin de disposer d’un système robuste et stable. Installation sous Windows L’installation ne pose pas de problème particulier. Le fichier téléchargé au format .zip doit être décompressé dans le répertoire où nous souhaitons installer Eclipse/Lomboz. Comme pour le serveur d’applications, il est important de bien installer avant l’IDE un JDK Java. Lors de l’installation d’Eclipse, il sera alors demandé de préciser le répertoire d’installation du JDK. Si au lancement de Windows nous obtenons une fenêtre d’erreur de ce type : JVM Terminated. Exit code= 1..., il faut alors supprimer le répertoire .metadata qui se trouve dans le répertoire des projets d’Eclipse (Workspace) et relancer l’IDE. Installation sous Linux Le fichier téléchargé au format .tar.gz doit être copié dans le répertoire des sources. #cp lomboz.tar.gz /usr/local/src Il faut ensuite dézipper et détarrer cette archive. #gunzip lomboz.tar.gz Un répertoire nommé lomboz est alors créé. Il faut déplacer ce répertoire dans /usr/local. #mv -r lomboz /usr/local/lomboz Il faut enfin positionner des droits corrects sur les fichiers. © ENI Editions - All rigths reserved - 1- #chown -R tomcat:tomcat /usr/local/lomboz Nous pouvons ensuite lancer l’IDE Eclipse. #/usr/local/lomboz/eclipse& Le lancement d’Eclipse est donc effectué en tapant la commande shell adaptée (depuis le répertoire d’installation d’Eclipse ou avec le chemin complet). #eclipse -vm /usr/local/jdk Cette commande permet de lancer Eclipse en précisant le répertoire d’installation du JDK. Par défaut, les projets sont stockés dans le répertoire workspace situé dans le répertoire d’Eclipse. Il est également possible de modifier ce paramètre avec l’option ­data. #eclipse -data ’/home/lafosse/mesprojets’ 3. Les plug­ins Eclipse Un plug­in est un composant logiciel additionnel qui permet à un logiciel principal d’apporter de nouvelles fonctionnalités. Il existe une multitude de plug­ins plus ou moins intéressants et fiables pour Eclipse qui sont classés par catégorie (J2EE, graphiques...) à cette adresse URL : http://www.eclipseplugincentral.com 4. Lancement d’Eclipse et paramétrage du serveur Java EE (Tomcat) Au lancement d’Eclipse, un splashscreen (écran d’attente) est affiché et permet de charger en arrière­plan les très nombreuses librairies de l’environnement. Il faut ensuite valider le workspace (répertoire de travail) et l’environnement s’ouvre. - 2- © ENI Editions - All rigths reserved Installation du plug­in de gestion du serveur Tomcat Eclipse possède un plug­in développé par la société SYSDEO qui permet de gérer le serveur d’applications Tomcat. 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 Il est important de choisir le plug­in qui correspond à la version d’Eclipse installée. Pour l’utilisation de ce guide et le développement du projet, le plug­in utilisé est tomcatPluginV321.zip. Une fois le téléchargement terminé, il faut décompresser ce fichier dans le répertoire des plug­ins d’Eclipse (/lomboz­ eclipe3.3/plugins/). Avant l’installation d’un nouveau plug­in Eclipse, il est important de fermer l’environnement de développement, d’installer le plug­in et de relancer l’environnement. Lors du premier lancement, il est important de paramétrer correctement l’IDE avec le serveur Java Tomcat et de vérifier le JDK utilisé. Pour contrôler Tomcat depuis Lomboz et éviter de toujours recharger les pages, démarrer/arrêter Tomcat avec le menu démarrer, il faut réaliser les opérations suivantes : Menu Windows ­ Preferences ­ Tomcat. Dans la fenêtre principale, il faut indiquer la version de Tomcat utilisée (Version 6.x), le répertoire d’installation de Tomcat et les déclarations de contextes. Les déclarations de contextes permettent de préciser si un seul fichier est utilisé pour déclarer les applications ou si au contraire, il y a un fichier par application. Il est préférable d’utiliser un fichier par application ou contexte pour des raisons pratiques de maintenabilité. © ENI Editions - All rigths reserved - 3- Dans la fenêtre Application Tomcat Manager, il faut vérifier que l’URL est bien http://localhost:8080/manager et indiquer l’identifiant et le mot de passe Tomcat (ceux utilisés lors de l’installation du serveur, par défaut : User Name : admin ­ Password : admin). Enfin, l’installation de l’environnement est terminée en vérifiant la compatibilité et la version du JDK utilisé par Eclipse : Windows ­ Preferences ­ Java ­ Compiler. - 4- © ENI Editions - All rigths reserved Dans la fenêtre, il est visible que le compilateur Java JDK utilisé est la version 6.0 (JDK 1.6). Nous pouvons désormais relancer Eclipse et observer la nouvelle barre d’outils proposée. 5. En ré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’encodage en Java. La plate­forme Java EE a été expliquée en détail ainsi que ses services associés tels que les Servlets, JSP, EJB, JDBC... Dans un troisième temps, les architectures Web ont été présentées afin de montrer la pertinence d’un choix physique avant le développement d’un projet. Enfin, l’environnement Java EE a été mis en place avec l’installation du JDK Java, du serveur d’applications, de l’environnement de développement et du paramétrage de l’ensemble. © ENI Editions - All rigths reserved - 5- Qu’est­ce que Tomcat ? 1. Présentation et définition Apache­Tomcat est le serveur d’applications Java du projet Jakarta de la fondation Apache. Ce serveur libre, sous licence Apache permet d’exécuter des applications Web développées avec les technologies Java (Servlets, JSP...). Apache­Tomcat trouve ses origines au tout début de l’apparition des technologies Servlets et JSP Java lorsque Sun Microsystems décide de donner le code de son serveur Java Web Server à la fondation Apache (1999). Aujourd’hui, Tomcat est pour Sun Microsystems, le serveur de référence pour les technologies Java EE Servlet et JSP. Tomcat est un moteur de Servlets fiable, évolutif et adapté à l’utilisation professionnelle. Il est actuellement utilisé dans le monde entier et mis en application au sein de domaines très variés. 2. La fondation Apache Le serveur Web Apache a été développé par Rob McCool en 1994. La première version de ce serveur Web est rendue disponible en Avril 1995 sous le nom d’Apache (A Patchy Server). Aujourd’hui, le serveur Web Apache est le serveur le plus utilisé de la planète. En 1999, les développeurs à l’origine d’Apache fondent l’Apache Software Foundation. Cette organisation à but non lucratif développe de nombreux projets et logiciels libres (le serveur Tomcat, des librairies pour le développement Internet, le serveur Web Apache, des bibliothèques de balises...). 3. Le projet Jakarta Jakarta est un des très nombreux projets de la fondation Apache. Jakarta divise ses projets en trois grandes catégories : ● les serveurs d’applications ; ● les bibliothèques, outils et API ; ● les frameworks. Le serveur d’applications Tomcat appartient à la première catégorie des projets Apache. Parmi les autres projets, il y a: ● JMeter : outil de mesure de performances des applications Web ; ● Log4J : bibliothèque de gestion des fichiers journaux (logs) et traces de programmation ; ● Struts : le framework de développement Web en Java le plus célèbre ; ● ANT : l’outil d’automatisation des applications Web ; ● Commons : un ensemble de bibliothèques de programmation Java. Actuellement, le projet Tomcat a pris une telle ampleur qu’il n’est plus considéré comme un sous­projet Jakarta (de la catégorie serveurs d’applications) mais comme un projet complet dénommé Apache­Tomcat. 4. Évolutions de Tomcat La première version de Tomcat est la version 3.X qui est l’implémentation des technologies Servlets 2.2 et JSP 1.1. Cette version a été conçue à partir du code source donné par Sun Microsystems à la fondation Apache. À partir de 2000, le serveur a été complètement modifié et donne alors naissance à la version 4.X. Le serveur possède alors un nouveau moteur de Servlets baptisé Catalina (Servlets 2.3 et JSP 1.2). Tomcat 5.X est apparu récemment et implémente les Servlets 2.4 et JSP 2.0. Cette version apporte des nouveautés au niveau du monitoring (intégration de JMX ­ Java Management Extension) ainsi que plusieurs optimisations (mémoire, © ENI Editions - All rigths reserved - 1- configuration du serveur...). Tomcat 5.X intègre le support de la version Java 5.0. La dernière version de Tomcat 6.X permet l’utilisation de Java 6.0. Cette version repose sur les Servlets 2.5 et JSP 2.1. Le serveur Jakarta Tomcat est développé depuis ses premières versions en Java. Les applications hébergées par Tomcat sont elles­mêmes écrites en Java, l’intégration est alors totale et robuste. Aujourd’hui, la version 6.X de Tomcat sait tirer profit des améliorations apportées à la plate­forme Java SE, notamment en terme de performance. - 2- © ENI Editions - All rigths reserved Installation de Tomcat 1. Quelle version choisir ? Actuellement, Tomcat propose une version 6.X stable qui est supportée par la majorité des environnements de développement. La version 6.X de Tomcat utilise la spécification Java EE 5 ainsi que l’API Servlet 2.5 et l’API JSP 2.1. C’est cette version de Tomcat qui sera utilisée tout au long de ce guide et qui est parfaitement gérée par Eclipse pour les opérations de démarrage, d’arrêt et de redémarrage du serveur. Le serveur Tomcat 6.X est disponible en libre téléchargement sur le site Internet d’Apache à cette adresse : http://tomcat.apache.org 2. Installation sous Windows Pour l’installation sous Windows, vous pouvez vous référer au premier chapitre de ce guide de développement d’applications Web en Java. La démarche à suivre est expliquée en détail avec une progression étape par étape. 3. Installation sous Linux Comme indiqué dans le premier chapitre de ce guide, le serveur d’applications a besoin d’un JDK Java pour fonctionner correctement. Il est donc nécessaire d’avoir installé un JDK 1.5 ou JDK 1.6 fonctionnel avant de procéder à l’installation du serveur Tomcat. La version de Tomcat utilisée dans ce guide est apache­tomcat­6.0.16 et peut être téléchargée à cette adresse : http://tomcat.apache.org/download­60.cgi. Lors de l’installation, nous allons préparer le serveur de façon à ce qu’il puisse dialoguer par la suite avec le serveur Web. Le système Linux utilisé tout au long de ce guide est une version Debian (Etch) stable. Vérifier les paquets installés Avant toute chose, il est nécessaire de mettre à jour les paquets installés. #apt-get update #apt-get upgrade Mise en place du serveur Web Apache Apache sera utilisé par la suite lors du déploiement d’un exemple complet sur un serveur en production. 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 #apt-get #apt-get #apt-get remove remove remove remove --purge --purge --purge --purge apache apache2 apache-perl apache-ssl Nous allons ensuite, détruire les répertoires des précédentes installations. #rm #rm #rm #rm -rf -rf -rf -rf /etc/apache /etc/apache2 /etc/apache-ssl /etc/apache-perl Nous procédons à l’installation du serveur Web Apache (1.3 ou 2.0 au choix). #apt-get install apache L’étape suivante consiste à installer le JDK 1.5 ou JDK 1.6 Java. Il existe de très fortes dépendances de la plate­forme Java EE 5 et Tomcat 6.X avec Java 5. Il est donc impératif d’installer un serveur Tomcat 6 sur un JDK 5.0 au minimum. Installation du JDK 1.5/1.6 Pour l’installation du jdk sous Linux, vous pouvez vous référer au chapitre Objectifs et spécifications de Java EE de ce guide de développement d’applications Web en Java. © ENI Editions - All rigths reserved - 1- Installation de Tomcat 6.0.X L’installation de Tomcat à partir d’une archive est assez simple. Il est nécessaire de télécharger sur le site d’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. Avec la version 6.0.X de Tomcat, la partie administration n’est pas incluse mais fait partie d’une option. Il est donc important de télécharger également la partie administration du serveur : apache­tomcat­6.0.16­ admin.tar.gz. Après le téléchargement, il faut copier l’archive dans le répertoire des sources Linux (/usr/local/src). #cp apache-tomcat-6.0.16.tar.gz /usr/local/src Il faut ensuite détarrer l’archive dans le répertoire /usr/local. #tar -xzf apache-tomcat-6.0.16.tar.gz -C /usr/local Il faut maintenant créer un lien pour référencer Tomcat directement. #cd /usr/local #ln -s apache-tomcat-6.0.16 ./tomcat Il sera donc possible de référencer Tomcat de cette manière sans se soucier de la version utilisée. #cd /usr/local/tomcat Désormais, il est nécessaire de créer un utilisateur système, dédié à Tomcat et d’indiquer que cet utilisateur est le propriétaire de Tomcat. #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 Le serveur de base est désormais opérationnel. Nous pouvons alors vérifier son fonctionnement en lançant le serveur et en ouvrant un navigateur à l’adresse suivante : http://localhost:8080/. #/usr/local/tomcat/bin/startup.sh Pour un fonctionnement correct et afin d’éviter de positionner les variables d’environnement à chaque démarrage ou dans les fichiers spécifiques, il est nécessaire de créer un script de démarrage et d’arrêt de Tomcat. #! /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 - 2- © ENI Editions - All rigths reserved ;; *) echo "Usage : /etc/init.d/tomcat {start|stop}" exit 1 ;; esac exit 0 Il est aussi possible de modifier légèrement le script afin de gérer la partie redémarrage du serveur et ainsi d’éviter de réaliser des arrêts/démarrages pour un simple redé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 Le propriétaire de ce script doit être le super utilisateur root car c’est le seul utilisateur qui a le droit de démarrer des services. #chown root:root /etc/init.d/tomcat Nous pouvons désormais tester le fonctionnement du serveur en utilisant les commandes suivantes : #/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’utiliser la commande : #chkconfig - -add /etc/init.d/tomcat À ce stade de l’installation le serveur Tomcat est opérationnel. Il est désormais possible de lancer un navigateur et de se connecter sur la page d’accueil du serveur à l’adresse suivante : http://localhost:8080/ Suivre les fichiers journaux Il est souvent très utile de suivre les traces lors d’un démarrage ou arrêt du serveur. De même, les fichiers journaux permettent de renseigner les administrateurs sur les problèmes de connexion au SGBD, les exceptions, les traces de programmation... Les fichiers journaux de Tomcat sont stockés dans le répertoire : /usr/local/tomcat/logs. Une bonne habitude est d’ouvrir en permanence une console avec la commande suivante qui trace en temps réel le fichier de logs du serveur. #tail -f /usr/local/tomcat/logs/catalina.out& © ENI Editions - All rigths reserved - 3- Le nom donné au service de Tomcat 6.0.X (moteur de Servlets) est Catalina. 4. Mise en place de la partie administration de Tomcat Par défaut, la partie administration du serveur n’est pas installée. Si nous cliquons sur le lien Tomcat Administration ou que nous accédons à l’adresse suivante : http://localhost:8080/admin/ le message suivant est affiché : Tomcat’s administration web application is no longer installed by default. Download and install the "admin" package to use it. Nous allons donc procéder à l’installation de la partie administration en utilisant la librairie apache­tomcat­6.0.16­ admin.tar.gz. Il est nécessaire de commencer l’installation en détarrant l’archive dans le répertoire des sources Linux. #tar -xzf apache-tomcat-6.0.16-admin.tar.gz /usr/local/src Le répertoire apache­tomcat­6.0.16 va être créé. Il faut alors copier tout le contenu de ce répertoire (qui possède plus de fichiers que la version d’origine) dans le répertoire d’installation de Tomcat. #cd /usr/local/src/apache-tomcat-6.0.16 #cp -r * /usr/local/tomcat/ Le serveur est presque opérationnel, il faut juste remettre les bons droits sur les fichiers et répertoires et redémarrer le serveur (arrêt puis dé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 Nous pouvons désormais nous connecter à l’adresse suivante http://localhost:8080/admin/ mais il est nécessaire de créer un utilisateur manager Tomcat. Pour cela, il est nécessaire d’éditer le fichier : /usr/local/tomcat/conf/tomcat­users.xml. #vi /usr/local/tomcat/conf/tomcat-users.xml La ligne suivante est ajoutée, elle permet de créer un utilisateur avec l’identifiant admin et le mot de passe admin puis on redémarre le serveur. #/etc/init.d/tomcat stop #/etc/init.d/tomcat start En fait, la partie administration de Tomcat est une application/webapp fournie sous forme de fichiers .tar ou .gz. La seule différence avec les autres webapps est que le projet n’est pas livré sous la forme d’une archive .war directement déployable. Il faut donc copier les fichiers de configuration au bon endroit. Pour éviter certains problèmes au démarrage du serveur, il est nécessaire d’éditer le fichier /usr/local/tomcat/conf/server.xml et de mettre en commentaire la ligne suivante qui est appelée APR pour Apache Portable Runtime et qui empêche parfois Tomcat de fonctionner correctement : 5. Augmenter la mémoire allouée à Tomcat Tomcat est développé en langage Java et comme tout programme qui utilise ce langage, il est assez gourmand en terme de mémoire. Il est donc parfois nécessaire d’augmenter la mémoire allouée à Tomcat afin qu’il puisse compiler plus rapidement les pages et répondre au plus vite aux requêtes clients (même en phase de développement un serveur rapide est plus souple). Sous Windows - 4- © ENI Editions - All rigths reserved Sous Windows, la mémoire allouée à Tomcat peut être paramétrée avec la console de gestion située dans le systray. Il faut ouvrir l’onglet Java et paramétrer les champs Initial memory pool et Maximum memory pool. Pour l’utilisation de ce guide et de ses exemples, 200 Mo seront alloués à Tomcat. Une autre solution pour la gestion de la mémoire allouée à Tomcat est d’utiliser Eclipse et l’onglet Windows ­ Preferences. Dans la partie réservée à Tomcat, il est possible de préciser la mémoire initiale (­Xms) et la mémoire maxi (­Xmx). Dans cet exemple, 200 Mo sont alloués à Tomcat. Il est important de ne pas allouer trop de mémoire par rapport aux possibilités de la machine afin d’éviter des blocages lors de déploiements/codages. Sous Linux 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.sh et d’ajouter la ligne suivante en début de fichier. Dans cet exemple, 2 Go de RAM sont alloués à Tomcat pour un serveur en production. JAVA_OPTS="-Xms2048m -Xmx2048m" © ENI Editions - All rigths reserved - 5- Coupler Tomcat et le serveur Web Apache 1. Présentation Si nous ne souhaitons pas installer un serveur en production, cette partie est alors facultative. En effet, elle ne permet pas d’améliorer l’environnement de développement mais elle est recommandée pour un déploiement sur un serveur en 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 comme Tomcat. L’utilisation d’un serveur Web en frontal est nécessaire pour des raisons de performance, de sécurité et de configurabilité. ● Performance : le moteur HTTP de Tomcat est beaucoup plus lent que le moteur HTTP d’un serveur Web dédié à cette tâche. Le serveur Web permet de délivrer les contenus statiques comme les pages HTML, le code JavaScript, les images du site, les feuilles de style... Tomcat sera utilisé alors pour servir uniquement les contenus dynamiques en Java. ● Sécurité : le serveur Web est utilisé en frontal et isole le conteneur Web d’Internet. Le conteneur est alors au plus près des données et il est moins sollicité pour des services simples. ● Configurabilité : un serveur Web comme Apache dispose d’une plus grande palette de services (point de vue HTP) que Tomcat (.htaccess, gestion des droits, urlrewriting, alias, annuaire...). 2. Un connecteur pour l’intégration du serveur Web L’intégration d’un serveur Tomcat avec un serveur Web se fait au travers d’un connecteur configuré au sein de Tomcat et d’une extension ajoutée au serveur Web. Un connecteur Tomcat est une classe Java qui supporte un protocole réseau spécifique et propriétaire. La librairie d’extension du serveur Web est chargée dynamiquement par le serveur Web lors de son démarrage et permet un dialogue entre les deux serveurs. Plusieurs connecteurs existent comme le module mod_jserv pour le serveur JServ et le module mod_webapp pour Tomcat 4.X. Ces modules sont désormais abandonnés au profit du connecteur JK. Le connecteur JK utilise le protocole AJP (Apache JServ Protocol) dans sa version 1.3 (AJP13). Ce connecteur est plus performant mais il offre également le support de plusieurs systèmes d’exploitation, de plusieurs serveurs Web (Apache, IIS, Lotus Domini) et du protocole HTTPS. Ce connecteur est aujourd’hui la référence pour le couplage d’un serveur d’applications avec un serveur Web. a. Fonctionnement Le connecteur JK utilise donc le protocole AJP13 et nécessite l’installation du module mod_jk pour fonctionner avec Apache. Le connecteur JK permet ainsi d’utiliser le serveur Web en frontal et de déléguer certaines tâches au serveur Tomcat. Les requêtes des clients sont envoyées au serveur Web Apache qui retourne alors directement les contenus statiques comme les images, JavaScript, pages HTML, ... Pour les requêtes avec du contenu dynamique, le module mod_jk du serveur Web est alors sollicité et délègue certaines tâches au serveur d’applications Tomcat. La configuration de Tomcat avec un serveur Web utilise la notion de worker ou travailleur. Un travailleur est lié à une instance de serveur Tomcat. Un travailleur est caractérisé par un nom d’hôte ou une adresse IP et un numéro de port (comme une socket/prise). Le travailleur AJP13 représente une instance de Tomcat en fonctionnement et il est utilisé comme plug­in pour le serveur Apache. Le module mod_jk agit alors comme un routeur de requêtes vers le serveur Tomcat. © ENI Editions - All rigths reserved - 1- b. Installation du module mod_jk L’extension d’Apache qui supporte le connecteur JK est le module mod_jk. Ce module est livré sous forme de binaires (ou code source). Le module mod_jk fonctionne sur le port 8009 de Tomcat. Par défaut, le fichier de configuration de Tomcat server.xml propose un connecteur AJP13 qui fonctionne sur le port 8009 : Avant de commencer l’installation, il faut mettre en place sous Linux Debian, le paquet apache­dev qui permet de compiler les modules d’Apache. #apt-get install apache-dev L’installation de Tomcat 6.X avec une intégration pour un serveur Web ne requiert aucune configuration particulière. Pour utiliser mod_jk, il faut un connecteur compatible configuré dans le fichier server.xml de l’instance de serveur Tomcat 6.X. Cette configuration existe désormais par défaut avec Tomcat 6.X et propose un connecteur qui fonctionne sur le port 8009. Ensuite, il est nécessaire de télécharger le connecteur (la dernière version) sur le site d’Apache­Tomcat (http://tomcat.apache.org). Le lien suivant est utilisé : The Apache Jakarta Tomcat Connector. Pour ce guide, c’est la version jakarta­tomcat­connectors­1.2.15­src.tar.gz du connecteur qui a été utilisée. L’installation commence en copiant la bibliothèque source dans le répertoire des sources Linux. #cp jakarta-tomcat-connectors-1.2.15-src.tar.gz /usr/local/src Ensuite, l’archive est dé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 modules d’Apache. Un fichier source compilé va alors être généré. Ce fichier source est alors copié dans le répertoire des librairies Apache. #cp apache-1.3/mod_jk.so.0.0.0 /usr/lib/apache/1.3/mod_jk.so Il faut alors positionner les droits corrects sur le module. #chown root:root /usr/lib/apache/1.3/mod_jk.so #chmod 644 /usr/lib/apache/1.3/mod_jk.so c. Configurer le module mod_jk Il nous reste à configurer le fichier de gestion d’Apache pour qu’il charge dynamiquement lors de son démarrage le module mod_jk. Pour cela, il faut éditer selon la version d’Apache le fichier httpd.conf ou modules.conf et ajouter les lignes suivantes : 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 La première opération permet de réaliser le chargement du module mod_jk dans le serveur Apache (LoadModule) . La directive JkWorkersFile permet de spécifier l’emplacement du fichier de configuration du module. La directive JkLogFile précise l’emplacement d’un fichier journal réservé au module et la directive JkLogLevel indique le type de messages enregistrés dans ce fichier. Il ne reste maintenant plus qu’à configurer le serveur Web Apache et à créer le fichier workers.properties. Il faut commencer par éditer le fichier de configuration d’Apache /etc/apache/httpd.conf puis ajouter les lignes suivantes pour notre hôte virtuel. - 2- © ENI Editions - All rigths reserved 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 JkMount worker1 JkMount worker1 #pages d’accueil autorisées DirectoryIndex index.html index.htm index.jsp Vous remarquez que le serveur sera capable de définir la page index.jsp comme page d’accueil de l’application. De même, tous les contenus autres que les Servlets (extension .do) et les JSP (extension .jsp) seront traités par le serveur Web qui est plus approprié. La directive JkMount est très importante car c’est elle qui permet au serveur Apache d’accéder aux applications Tomcat. Elle permet en effet, de spécifier un travailleur pour l’accès au contexte. Il y aura donc une redirection de requêtes utilisateur à destination du travailleur. La directive JkUnMount permet de réaliser l’inverse et donc de ne pas rediriger les requêtes utilisateurs à destination de ressources particulières. Exemple : JkUnMount /usr/local/tomcat/webapps/monapplication/mesimages/*.gif D’autres directives d’Apache sont utilisables avec le mod_jk. JkAutoAlias permet de réaliser un alias du répertoire de Tomcat sur le répertoire de données Apache, JkLogStampFormat permet de gérer le format de la date dans le fichier journal du module, JkExtractSSL permet de transmettre les informations SSL vers le serveur Tomcat (état on par défaut)... d. Créer le fichier de configuration du travailleur Une fois le fichier de configuration d’Apache modifié, il faut ensuite créer le fichier de configuration du travailleur indiqué par la directive JkWorkersFile dans le fichier d’Apache. Ce fichier permet de gérer la communication entre le serveur Web et le serveur Tomcat. Le fichier utilisé nommé workers.properties porte l’extension .properties qui est un format très utilisé avec les technologies Java. Ces fichiers sont composés d’un ensemble de paires clé/valeur. La syntaxe pour le fichier workers.properties est la suivante : worker..= La syntaxe de notre fichier est la suivante : 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 © ENI Editions - All rigths reserved - 3- #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 directive JkWorkersFile. Les paramètres de configuration du fichier workers.properties sont, entre autres : ● worker.list : permet de spécifier une liste de travailleurs séparés par des virgules. ● type : permet de spécifier le type de travailleur. ● host : permet de spécifier le nom d’hôte ou adresse IP du serveur Tomcat à contacter pour le travail. ● port : indique le numéro de port du connecteur JK. ● socket_timeout : indique le temps d’expiration de la communication entre Apache et Tomcat. Si le serveur Tomcat ne répond pas dans le délai indiqué, mod_jk génère une erreur et recommence. ● retries : positionne le nombre de tentatives de connexions vers Tomcat en cas de non réponse de ce dernier (3 par défaut). ● socket_keepalive : permet d’éviter que le firewall coupe les connexions inactives (défaut 0). ● recycle_timeout : indique le nombre de secondes au­delà duquel le serveur coupe une connexion AJP en cas d’inactivité (défaut 0, bonne moyenne 300). ● cachesize : précise le nombre de connexions AJP maintenues en cache. ● cache_timeout : permet de spécifier combien de temps une connexion doit être maintenue dans le cache avant d’être fermée par mod_jk afin de réduire le nombre de processeurs de requêtes actifs sur le serveur Tomcat (défaut 0). ● lbfactor : permet de gérer le facteur de charge d’un travailleur dans le cas où la répartition de la charge est mise en œ uvre par mod_jk. Cette valeur permet de préciser quel pourcentage de requêtes l’instance Tomcat sera amenée à traiter (défaut 1). Une fois la configuration du connecteur JK terminée, il faut redémarrer les deux serveurs et tester l’accès à une Servlet ou une JSP pour notre hôte précédemment défini (http://monserveur.com/index.jsp). Pour cela, sous Windows, il est possible d’éditer le fichier C:\WINDOWS\system32\drivers\etc\hosts et d’ajouter le nom de l’hôte virtuel. 127.0.0.1 127.0.0.1 localhost monserveur.com Sous Linux, nous pouvons réaliser la même opération en éditant le fichier /etc/hosts. 127.0.0.1 127.0.0.1 localhost monserveur.com Désormais, lorsque l’url suivante : http://monserveur.com sera appelée dans un navigateur, c’est la machine locale (127.0.0.1) qui répondra aux requêtes du client. #/etc/init.d/apache restart #/etc/init.d/tomcat stop #/etc/init.d/tomcat start Si la page .jsp est affichée, l’installation est correcte. En effet, c’est l’hôte virtuel du serveur Web Apache qui est précisé par le nom de domaine (http://monserveur.com) et le fichier .jsp est exécuté par Tomcat par le biais du connecteur. En cas de problème lors du test du connecteur, les fichiers journaux d’Apache et de mod_jk peuvent nous aider en indiquant les raisons des dysfonctionnements. - 4- © ENI Editions - All rigths reserved © ENI Editions - All rigths reserved - 5- Architecture et configuration de Tomcat Le serveur d’applications Tomcat utilise une architecture spécifique (spécifique aux projets Java EE) qu’il est nécessaire de bien maîtriser. Tomcat est livré pré­configuré, il est possible de l’utiliser comme cela sans avoir à modifier les fichiers de configuration, mais lors des développements et de la mise en production des applications, il sera nécessaire de bien contrôler l’administration du serveur. 1. Les composants de Tomcat Tomcat est constitué d’un ensemble de composants dédiés à l’exécution d’un service particulier et précis. Les composants de Tomcat sont appelés des conteneurs (parce qu’ils contiennent eux aussi des composants). Il existe actuellement cinq types de conteneurs : Server, Service, Engine, Host et Context. Tous ces conteneurs sont représentés dans le fichier de configuration du serveur server.xml qui est le principal fichier de configuration de Tomcat. Chaque conteneur est représenté par une balise XML en suivant une structure arborescente adaptée en conséquence. 2. Arborescence du serveur La structure des fichiers au sein du répertoire d’installation est présentée dans cette image. © ENI Editions - All rigths reserved - 1- Parmi les répertoires présents, certains sont dédiés à la configuration du serveur et sont donc difficilement modifiables, d’autres peuvent être modifiés suivant les développements. Le répertoire /bin contient tous les scripts et fichiers indispensables au bon fonctionnement de Tomcat. Ces exécutables contiennent des scripts shell et des fichiers batch qui permettent de démarrer et d’arrêter le serveur sur les différentes plates­formes prises en charge. Les fichiers d’extension .bat sont utilisés sous Windows et les fichiers .sh sont utilisés sous Linux. 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 .class et donc copiées dans le répertoire /lib/classes ou sous forme de fichiers .jar. Ce répertoire /lib sera très souvent utilisé pour placer le pilote JDBC de la base de données utilisée. Par exemple, lors du déploiement d’un projet, la librairie mysql­connector­java­3.1.11­bin.jar sera copiée dans le répertoire /lib et sera donc utilisable par toutes nos classes du projet (ce qui est valable pour les librairies mail.jar, xalan.jar...). Dans le cas où plusieurs applications Web ont toutes besoin d’une même bibliothèque, il peut être plus judicieux de copier cette bibliothèque dans ce répertoire plutôt que dans chacune des applications. Le répertoire /conf contient tous les fichiers de configuration de Tomcat avec les quatre fichiers importants que sont server.xml, tomcat­users.xml, web.xml et catalina.policy. Le répertoire /logs contient tous les fichiers journaux du serveur Tomcat. Il est important de préciser que les fichiers journaux sont automatiquement remplacés tous les jours à minuit, et contiennent dans leur intitulé la date au format anglais. Il existe trois principaux types de fichiers journaux : les fichiers relatifs au serveur, les fichiers relatifs aux applications et aux noms d’hôtes. Le répertoire /temp est un répertoire temporaire pour les applications non déployées. Le répertoire /webapps est le répertoire par défaut d’installation des applications Java EE. Il contient par défaut des applications d’exemples ainsi que l’application tomcat­docs qui fournit la documentation du serveur. Il est tout à fait possible de référencer des applications Web qui ne se trouvent pas dans le répertoire /webapps. Dans ce cas, il faut préciser le répertoire de l’application de façon précise dans le fichier de configuration server.xml. Le répertoire /work est utilisé pour le traitement des pages JSP et leur transformation en classes Java. Toutes les Servlets générées à partir de pages JSP seront stockées dans ce répertoire. Chaque application possède alors son propre sous­répertoire (ex : work/Catalina/localhost/monapplication) en suivant l’arborescence suivante : work///, et représentent le nom des conteneurs Engine, Host et Context dans lesquels cette application est installée. Ce répertoire sera d’une grande utilité lors du débuggage de pages JSP. Le code transformé sera en effet accessible. De même, il sera parfois nécessaire de supprimer le contenu de ce répertoire pour notre hôte lorsque l’on voudra regénérer toutes les pages JSP. - 2- © ENI Editions - All rigths reserved Rappels XML Les fichiers de configuration de Tomcat sont écrits avec le langage XML. Il est donc important de présenter la syntaxe et les balises de ce langage afin de configurer correctement le serveur d’applications. XML (eXtended Markup Language) dérive du langage SGML (Standard Generalized Markup Language) développé dans les années 80. SGML est un langage très complexe à apprendre et à utiliser. Une version plus simple de ce langage a été proposée pour la présentation de document Web : le HTML (HyperText Markup Language). HTML HTML est aujourd’hui le standard pour le développement Web. Il commence à être remplacé progressivement par le XHTML (eXtend Hypertext Markup Language) qui est assez similaire mais qui respecte les normes XML. HTML est ’’un langage’’ de description, il est lié à une DTD (Document Type Definition) qui permet de vérifier la syntaxe du langage. XML XML utilise la simplicité du HTML avec la souplesse de SGML. Le point commun le plus important entre SGML et XML est l’utilisation d’une DTD ou d’un schéma. Cette association n’est pas obligatoire et un fichier XML peut très bien se suffire à 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) sont séparées de l’apparence (le contenant). Il sera donc possible de fournir plusieurs types de sorties pour un même fichier de données (image, fichier HTML, fichier XML, fichier PDF...). Les langages SGML, HTML/XHTML et XML sont en fait composés de balises qui peuvent être comparées à des mots du langage français. Par contre, il y a des règles à respecter pour l’utilisation de ces mots dans un document, ces règles sont appelées : grammaires. XML permet de séparer le fond de la forme. Cela signifie qu’un document XML ne comporte que des données. Ainsi, pour produire un document HTML à partir d’un fichier XML, il est nécessaire de créer au moins deux fichiers, le premier pour les données et le second pour la mise en forme de ces données. Un troisième fichier peut parfois être utilisé, c’est une DTD ou un schéma permettant de définir les balises et la grammaire utilisées. Bien formé Un document XML bien formé est un document XML qui respecte certaines règles. ● Le document doit commencer par une déclaration XML (prologue) . ● Il ne doit exister qu’une seule balise racine. donnée1 donnée2 ... ● Les valeurs des attributs doivent être impérativement encadrées par des guillemets simples ou doubles. ● Toute balise ouverte doit être fermée. donnée ● Une balise vide doit être obligatoirement fermée. ● Les balises doivent être correctement imbriquées. donnée © ENI Editions - All rigths reserved - 1- ● Les noms des balises doivent commencer par une lettre ou ’’_’’, les autres caractères peuvent être des chiffres, des lettres, ’’_’’, ’’.’’ ou ’’­’’. <_balise attribut28=’valeur’/> ● Les noms des balises et des attributs doivent conserver une casse identique. ● Les noms des balises ne doivent pas commencer par xml. ● Le document doit contenir un ou plusieurs éléments. Si le document contient un seul élément, alors ce document sera composé du seul élément racine. ● Le caractère inférieur < est réservé à l’ouverture des balises. ● Les caractères inférieur <, esperluette &, supérieur >, quote ’, et double quotes ’’ doivent être remplacés par leurs entités HTML ou numériques (<; &; >;...). Valide Un document XML est dit valide lorsque celui­ci est bien formé et qu’il est conforme à une grammaire. Structure d’un document XML Un fichier XML est composé d’un prologue, d’un élément racine et d’un arbre. L’arbre est composé d’éléments imbriqués les uns dans les autres. Le prologue est constitué de la première ligne du document permettant d’indiquer que c’est un document XML et éventuellement de l’association à une DTD. Les infiltrés 17.99 125 Le cercle rouge 14 120 Psychose 22 155 Dans cet exemple, le prologue est indiqué par la première ligne du document , il précise que le document est un fichier XML utilisant l’encodage ISO­8859­1 (caractères standards du clavier). Dans le prologue, l’attribut version précise la version XML utilisée dans le document. L’attribut encoding permet de définir le jeu de caractères utilisé. Pour la France, le jeu de caractères standard est ISO­8859­1. Pour faire des pages en international (Japonais, Anglais, Français...), il faut utiliser l’encodage Unicode UTF­8. Un troisième attribut optionnel nommé standalone permet de préciser si oui ou non le fichier XML est lié à une DTD externe pour vérifier sa syntaxe. ● - 2- sans utilisation d’une DTD : © ENI Editions - All rigths reserved ● avec utilisation d’une DTD : L’élément est l’élément racine du document. Il est lui­même constitué de trois éléments . Dans chacun des éléments de cet exemple vous retrouvez un élément , et . L’élément possède un attribut nommé langue. Cette vue représente le code XML de la page précédente ouvert avec un navigateur Une DTD peut être associée à ce document et permet alors de définir la structure du document. Les infiltrés 17.99 125 Le cercle rouge 14 120 Psychose 22 155 © ENI Editions - All rigths reserved - 3- Lors de la déclaration d’une DTD, il faut préciser l’élément racine ( 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érifier la validité d’un document XML. Dans un document XML, les commentaires sont utilisés comme dans un document HTML avec la syntaxe suivante . Explications L’élément racine est la base du document XML. Il doit être unique et englobe tous les autres éléments. Il s’ouvre juste après le prologue et se ferme à la fin du document. Les autres éléments forment la structure du document. Ce sont donc les branches et les feuilles de l’arborescence. Les éléments contenants sont appelés élément parent et les autres éléments imbriqués élément enfant. Les éléments peuvent contenir un ou plusieurs attributs. Chaque élément ne peut contenir qu’une fois le même attribut. Un attribut est composé d’un nom et d’une valeur. Un attribut ne peut être présent que dans une balise ouvrante d’un élément (et pas dans la fermante). Certains caractères ont un sens particulier en XML, il est nécessaire de trouver un remplaçant quand il faut insérer ces caractères dans le document. Il faut alors avoir recours aux entités. Les caractères réservés en XML sont les suivants : Caractère Entité & & ; < < ; > > ; ’’ " ; ’ &aquot ; Pour les lettres accentuées, il faudra parfois utiliser les entités numériques du type &#numero; (où numero est une valeur décimale). Par exemple, le caractère codé é peut être remplacé par é;. Syntaxe d’une DTD Dans l’exemple précédent, le fichier est correctement affiché dans un navigateur. Le document est valide et bien formé, il peut désormais être utilisé. Les infiltrés 17.99 125 Le cercle rouge 14 120 Psychose 22 155 Rien ne nous empêche de ne plus avoir de prix et de durée pour chaque dvd. La structure du document est correcte du point de vue des balises mais la grammaire ne l’est pas. C’est là qu’intervient la DTD (fichier dvd.dtd ci­dessous) qui permet de définir la syntaxe que le document XML devra respecter. Exemple 1 : - 4- © ENI Editions - All rigths reserved Exemple 2 : Les infiltrés Le cercle rouge Psychose Le fichier XML est associé à cette DTD. Il devra donc respecter la grammaire suivante : la balise racine doit être , la balise racine est composée de balises et chaque balise contient une balise , et . La balise contient deux attributs optionnels type et langue. L’attribut type ne peut contenir que les valeurs ’thriller, policier, horreur et théâtre’. La balise langue contient par défaut la valeur ’fr’. Le mot clé SYSTEM dans le fichier XML indique que le fichier DTD se trouve sur l’ordinateur local et qu’il est disponible en accès privé uniquement. Le mot clé PUBLIC indique qu’une ressource est disponible pour tous (accès public) sur un serveur Web distant. Les éléments Une déclaration d’élément est de la forme : nom est le nom de l’élément et type_element est le type auquel l’élément est associé. Un élément peut être de type 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ément texte est précisé par #PCDATA. Un élément vide utilise le mot clé EMPTY. Dans le document XML, l’élément vide sera représenté par : . Un élément vide peut par contre posséder des attributs. Lors de la déclaration de séquence ou de choix d’éléments, une indication d’occurence (?, + ou *) peut être attribuée à chaque élément enfant. ● elt1 ne contient aucune indication d’occurrence, il doit donc apparaître une seule et unique fois dans l’élément elt0 (1 et 1 seul). ● elt2 a pour indication d’occurrence ?, l’élément doit apparaître au maximum une fois et il peut ne pas apparaître © ENI Editions - All rigths reserved - 5- du tout (0 ou 1). ● elt3 a pour indication d’occurrence +, l’élément doit apparaître au moins une fois et autant de fois que l’auteur le désire (1 ou plusieurs). ● elt4 a pour indication d’occurrence *, l’élément doit apparaître autant de fois que l’auteur le désire (il peut ne pas apparaître du tout) (0 ou plusieurs). Les séquences d’éléments Une séquence d’éléments est une liste ordonnée d’éléments devant apparaître comme éléments enfants de l’élément qui est en train d’être défini. Ce dernier ne pourra contenir aucun autre élément que ceux figurant dans la séquence. Cette liste est composée d’éléments séparés par des virgules et est placée entre parenthèses. Chaque élément doit être déclaré par ailleurs dans la DTD. Dans le fichier XML, les éléments doivent apparaître dans l’ordre de la séquence. Il est possible bien sûr d’utiliser les indicateurs d’occurrence. La déclaration d’attributs Des attributs peuvent être présents dans un document XML, la DTD permet donc de définir des contraintes sur ces attributs. Le mot clé de déclaration d’un attribut est ATTLIST. Chaque attribut peut être requis, optionnel ou fixe et avoir une valeur par défaut. Un attribut peut avoir une valeur par défaut. Un attribut peut être obligatoire. Un tel attribut est obligatoire. Son absence déclenche une erreur du vérificateur syntaxique sur le fichier XML. Un attribut peut être optionnel. Un attribut peut être fixe. L’attribut ne peut donc prendre qu’une seule valeur fixée. Exemples concrets La syntaxe XML a été présentée mais il est important de montrer son utilisation avec un simple fichier XHTML et avec un fichier de configuration d’une application Tomcat. Ci­dessous, un exemple d’un fichier simple en XHTML. Nous remarquons l’utilisation d’une DTD (grammaire) PUBLIC proposée par le serveur Web du W3C. Nous pouvons également noter que chaque balise est fermée, correctement imbriquée et que certains éléments possèdent des attributs (). Mon Titre

Ma page

- 6- © ENI Editions - All rigths reserved Une application déployée avec Tomcat est configurée par l’intermédiaire d’un fichier nommé web.xml. Une syntaxe possible de ce fichier est la suivante : Une application déployée avec Tomcat DisplaySource DisplaySource display source of sample jsp pages org.displaytag.sample.DisplaySourceServlet DisplaySource *.source css text/css index.jsp index.html 404 /404.jsp Nous remarquons que ce fichier de configuration d’une application Tomcat doit être conforme à la DTD PUBLIC de Sun Microsystems (). Dans un éditeur tel que Eclipse, la syntaxe du fichier de configuration sera donc vérifiée en temps réel grâce à une connexion Internet. Par exemple, il n’est pas possible de placer les balises après les balises ce qui est très utile et permet d’augmenter la fiabilité du projet. © ENI Editions - All rigths reserved - 7- Les fichiers de configuration Tomcat Le fichier server.xml Le principal fichier de configuration du serveur Tomcat s’appelle server.xml et se trouve dans le répertoire /conf du serveur. Il fournit à Tomcat tous les paramètres nécessaires pour le fonctionnement, les ports à écouter, les hôtes virtuels, les instances de processeur HTTP et les classes à utiliser pour gérer les connexions entrantes. Ce fichier est au format XML et possède une balise de déclaration XML (prologue) mais il n’est lié à aucun fichier de validation DTD ou schéma. Ce fichier est donc bien formé mais non valide. Comme tout fichier XML, le langage est sensible à la casse et le fichier server.xml doit utiliser des balises commençant par une majuscule suivie de lettres en minuscules. Lors du démarrage, Tomcat vérifie la syntaxe des éléments déclarés dans ce fichier. Tomcat comprend un processus principal, le serveur qui contient lui­même plusieurs sous­composants qui sont utilisés pour traiter les requêtes. L’élément est la racine du fichier server.xml. Il représente l’instance de serveur Tomcat. Cet élément utilise 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émentation par défaut. Les deux autres attributs sont port et shutdown. Ils contrôlent le service pour écouter les commandes d’arrêt du serveur. © ENI Editions - All rigths reserved - 1- L’élément regroupe les éléments qui permettent la connectivité au serveur ainsi que le moteur d’exécution de Tomcat. Le seul attribut obligatoire de cette balise est name. Cet attribut permet d’affecter un nom au service qui s’affiche dans les fichiers journaux. Le nom par défaut de cet attribut est Catalina. L’élément permet de gérer les threads (sous­processus) à l’intérieur de la machine virtuelle Java. 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 suppressions inutiles de threads, Tomcat 6.X utilise un mécanisme nommé pool de threads. Plutôt que de détruire un thread après traitement d’une requête client, celui­ci est recyclé. Le pool de threads est dimensionné avec une valeur initiale qui permet de définir combien de threads doivent être créés dedans et le nombre maximum de threads. Ces deux paramètres sont respectivement configurés à partir des attributs minSpareThreads et maxThreads de l’élément . L’élément : cet élément fils de la balise permet d’implémenter la couche transport des requêtes clients vers le moteur de Servlets Tomcat (Catalina). Depuis la version 5.0 de Tomcat, il existe deux types de connecteurs selon le protocole, à savoir HTTP et AJP. Le choix de transiter par l’un ou l’autre des protocoles se fait avec l’attribut protocol. L’attribut port permet d’indiquer le port à l’écoute du connecteur. Par défaut, Tomcat utilise le port 8080 pour le HTTP et le port 8009 pour le connecteur AJP. L’attribut address permet de spécifier une adresse IP particulière pour écouter les connexions entrantes. Enfin, l’attribut secure permet de définir un connecteur HTTPS. L’élément est chargé de répartir toutes les requêtes. C’est le moteur de Servlets Catalina de Tomcat. Une application est obligatoirement associée à un élément . Les deux attributs obligatoires de cet élément sont name et defaultHost. L’attribut name permet d’identifier le moteur de Servlets et l’attribut defaultHost permet de définir lequel des éléments de la configuration va recevoir les requêtes en cas de non correspondance de nom d’hôte (hôte par défaut). L’élément : le conteneur permet de configurer les attributs à associer à un hôte unique. La possibilité de configurer une seule machine pour servir plusieurs hôtes offre une souplesse considérable. L’hébergement virtuel est une technique utilisée par les serveurs HTTP, permettant d’héberger plusieurs sites distincts à une même adresse 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 Le système DNS va permettre au navigateur de trouver l’adresse IP du serveur Web à contacter et la requête sera envoyée au serveur indiqué. Un autre site http://www.monentreprise.com peut être hébergé sur la même machine et donc posséder la même adresse IP. C’est dans ce cas précis que l’élément est utilisé pour identifier de façon unique et précise le site. Donc autant d’hôtes que nécessaire peuvent être configurés dans un serveur Tomcat et c’est l’attribut name qui permet de préciser le nom d’hôte. Si un client accède au serveur directement par l’adresse IP, le serveur sera alors dans l’incapacité de résoudre le nom d’hôte. L’attribut de configuration defaultHost de l’élément permet donc de définir l’hôte qui sera contacté dans ce cas précis. Il peut être intéressant de mapper plusieurs noms de domaine sur un contenu unique. Par exemple, les sites www.monentreprise.com et monentreprise.com peuvent être utilisés avec le même hôte. Dans ce cas, les noms d’hôtes doivent retourner un contenu identique, il faut alors utiliser l’élément à l’intérieur du conteneur . ... monentreprise.com ... 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 autoDeploy permet d’indiquer à Tomcat si les applications déposées dans le répertoire des webapps (indiqué donc par appBase) doivent être automatiquement déployées sans redémarrage du serveur pour un hôte. Par défaut la valeur est autoDeploy=’’true’’ ce qui est très intéressant pour un serveur en développement, mais il oblige Tomcat à surveiller en 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é automatiquement au niveau du projet et pas d’un hôte. L’attribut deployOnStartup rend disponible toutes les applications au démarrage de Tomcat, ce qui est évidemment la valeur par défaut. - 2- © ENI Editions - All rigths reserved L’attribut unpackWARs permet de décompresser les fichiers d’archives WAR ce qui est le cas par défaut. L’attribut deployXML permet d’autoriser le déploiement des applications via les fichiers de contexte XML. Enfin, le répertoire workDir permet de spécifier un répertoire de travail pour les applications. Les classes des Servlets et des JSP seront générées dans ce répertoire. Chaque application possède son propre sous­répertoire. Par défaut, ce répertoire est placé de cette façon : /work/Catalina/nom_hôte. L’élément qui est une balise fille de l’élément permet de déployer une application Web dans Tomcat. Un contexte permet de relier une URL à une application Web. Cet élément est utilisé pour déclarer explicitement une application, il peut être utilisé dans le fichier server.xml ou bien dans les fichiers de contexte XML, c’est cette dernière méthode qui est préconisée avec Tomcat 6.X. La balise possède deux attributs obligatoires afin de préciser le répertoire qui contient l’application et l’URL pour accéder à cette application. L’attribut docBase permet de faire référence au répertoire des données de l’application ou bien directement au fichier WAR de l’application. L’attribut path permet d’indiquer le chemin de contexte de cette application Web. Ce chemin commence toujours par le caractère /, chaque application doit posséder une valeur unique de cet attribut. L’attribut facultatif reloadable permet d’activer une surveillance des répertoires /WEB­INF/lib et /WEB­INF/classes de l’application. Chaque modification apportée au contenu de ces répertoires sera automatiquement prise en compte par Tomcat qui rechargera alors automatiquement l’application. Il est préférable d’utiliser l’outil manager du serveur d’applications pour recharger les classes et ainsi éviter un gaspillage inutile des ressources. L’attribut facultatif workDir permet de spécifier le répertoire de travail pour l’application. Si cet attribut est spécifié dans l’hôte, il surcharge alors celui défini dans l’élément . L’attribut facultatif cookie permet d’indiquer si le serveur utilise les cookies pour gérer les sessions utilisateurs. La valeur par défaut est true. Tomcat utilise un contexte par défaut qui est mis en œ uvre dans le fichier /conf/context.xml. L’élément : la plate­forme Java EE définit un mécanisme standard basé sur la notion de rôles pour la gestion des authentifications dans les applications Web. Un peut être défini en tant qu’élément enfant des balises , ou . Suivant le placement, l’authentification sera appliquée à tout le moteur de Servlets, à un hôte particulier ou à une application. monsite.com mesites.com © ENI Editions - All rigths reserved - 3- L’élément définit un chargeur de classe Java. Le rôle de cet élément est de charger les classes Java d’une application Web. Le serveur charge les classes contenues dans le répertoire /WEB­INF/classes de l’application, les classes contenues dans les fichiers JAR présents dans le répertoire /WEB­INF/lib et les classes rendues accessibles aux applications par le moteur de Servlets. L’élément permet de configurer le gestionnaire de session pour une application Web spécifique. Par défaut, un conteneur Web Java EE stocke les informations de session utilisateur dans la Machine Virtuelle Java. En plus d’être stockées en mémoire, les sessions utilisateurs sont sauvegardées soit dans une base de données soit dans un fichier par la classe d’implémentation org.apache.catalina.session.PersistentManager. L’élément représente un composant sous forme d’une classe Java. Cet élément peut être considéré comme un filtre de requêtes. Voici les différentes valeurs possibles pour l’attribut className et le type de filtre associé : ● org.apache.catalina.valves.AccessLogValve : génère un fichier journal des accès au serveur. ● org.apache.catalina.valves.JDBCAccessLogValve : génère un fichier journal des accès à une base de données. ● org.apache.catalina.valves.RemoteAddrValve : applique une restriction d’accès en fonction des adresses IP des clients. ● org.apache.catalina.valves.RemoteHostValve : applique une restriction d’accès en fonction des noms de machines des clients. ● org.apache.catalina.valves.RequestDumperValve : génère un fichier journal des requêtes des clients. ● org.apache.catalina.authenticator.SingleSignOn : permet une authentification unique entre plusieurs applications. Il est important de noter que nous pouvons implémenter votre propre filtre en écrivant une classe Java sur le modèle de celles déjà fournies. Exemple : Avec JDBCAccessLogValve, le code suivant peut être inséré dans le fichier de configuration server.xml. ... .... L’attribut connectionURL permet de spécifier la base de données et les coordonnées de connexion à la base de données. L’attribut driverName permet de spécifier le pilote JDBC d’accès à la base de données. Celui­ci devra bien sûr être installé dans le répertoire /lib de Tomcat. L’attribut tableName permet de préciser le nom de la table qui va recevoir les données des journaux. L’attribut resolveHosts permet de remplacer l’adresse IP du client par le nom de machine. Enfin, l’attribut pattern permet de définir le format d’entrée des fichiers journaux. La base de données doit avoir la syntaxe suivante : 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 permet de définir un écouteur d’événements sur les éléments , ou . Cet élément ne dispose que d’un seul attribut obligatoire nommé className qui est la classe Java qui implémente l’écouteur. Tomcat possède par défaut deux écouteurs définis sur l’élément . Ces écouteurs permettent la supervision globale du serveur ainsi que des ressources JNDI. Les objets MBeans de supervision sont définis et utilisés par l’API Java JMX (Java Management Extension) que nous verrons dans ce chapitre. Parmi les autres fichiers de configuration de Tomcat, il existe les fichiers tomcat­users.xml, catalina.policy et web.xml. Ces fichiers sont présents dans le répertoire /conf de Tomcat. Les fichiers tomcat­users.xml et catalina.policy sont utilisés pour la sécurité du serveur et le fichier web.xml définit les applications Web déployées sur le serveur. © ENI Editions - All rigths reserved - 5- Le fichier de configuration des applications Le fichier web.xml 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 toutes les applications Web installées sauf, si les applications fournissent leur propre fichier de configuration web.xml. Par la suite, toutes les applications déployées utiliseront leur propre fichier web.xml. Ce fichier de configuration commence par la déclaration de Servlets qui sont spécifiques à Tomcat. La Servlet par défaut de Tomcat est définie avec la classe DefaultServlet et son attribut listings qui permet d’autoriser ou non l’indexation. La Servlet InvokerServlet permet de déclencher http://serveur/application/servlet/nomdelaclasseservlet. des Servlets directement avec des URL La troisième Servlet est JspServlet et permet de transformer des pages JSP en Servlet. Les sections suivantes de ce fichier de configuration concernent les paramètres Java EE : par exemple, le temps d’expiration des sessions, les types MIME des en­têtes HTTP, les pages d’accueil (ex: index.html, index.jsp...). © ENI Editions - All rigths reserved - 1- Le fichier de configuration des utilisateurs Le fichier tomcat­users.xml Ce fichier est utilisé pour les authentifications de Tomcat. Tomcat utilise le système d’authentification basé sur une connexion JNDI. Ce gestionnaire d’authentification est associé au fichier tomcat­users.xml contenant les associations identifiant, mot de passe et rôle. La partie manager et administration de Tomcat utilise ce fichier d’authentification. La déclaration de ce système d’authentification se retrouve dans le fichier de configuration du serveur /conf/server.xml. Voici le contenu de ce fichier qui se trouve dans le répertoire : /conf/tomcat­users.xml. Nous remarquons la définition des rôles et des utilisateurs avec leur mot de passe. L’application manager de Tomcat n’est accessible que par les utilisateurs qui possèdent le rôle manager, déclaré dans ce fichier. Il sera possible de modifier ce fichier pour réaliser une authentification qui repose sur une base de données ou un annuaire. © ENI Editions - All rigths reserved - 1- Le fichier de configuration de la sécurité Le fichier catalina.policy Le gestionnaire de sécurité de la machine virtuelle Java est appelé SecurityManager et permet d’assurer la sé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. Les applications déployées sur le serveur peuvent donc lire, écrire et supprimer des fichiers, 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). Dans ce cas précis, tout ce qui n’est pas explicitement précisé dans le fichier catalina.policy est interdit. © ENI Editions - All rigths reserved - 1- Arborescence d’un projet Tomcat Une application Web est un ensemble de ressources qui participent au fonctionnement de l’application. Une application est constituée de plusieurs éléments : ● Le composant serveur dynamique : Servlets et JSP. ● Les librairies de classes Java utilitaires. ● Les éléments Web statiques : pages HTML, images, feuilles de style, JavaScript... ● Les composants clients dynamiques : Applets, JWS, JavaBean. ● Un descripteur de déploiement et de configuration de l’application Web sous la forme d’un (ou plusieurs) fichier (s) de configuration au format XML : web.xml. Les spécifications de l’API Servlet indiquent l’organisation des dossiers et fichiers à respecter pour déployer une application Web. L’application est configurée par le fichier XML : /monapplicationweb/WEB­INF/web.xml. L’arborescence de l’application Web est la suivante : Le dossier nommé dans l’exemple monapplicationweb représente la racine de l’application Web. N’importe quel nom peut lui être donné, en général c’est le nom de l’application (ex : facturation, boutique...). Une fois en production ce dossier est public, nous retrouvons donc les fichiers HTML, JavaScript... Le dossier META­INF contient le fichier manifest.mf qui est généré par l’archiveur jar.exe lorsqu’une archive du projet est réalisée pour le déployer. Ce fichier permet de stocker des informations concernant l’archive comme sa version, le contenu, la version du compilateur... Exemple : Manifest-Version: 1.0 Created-By: 1.5.0_04 (Sun Microsystems Inc.) Le dossier WEB­INF représente la partie privée de l’application Web. Il contient le fichier web.xml qui représente le descripteur de déploiement. Le sous­dossier /classes du dossier WEB­INF contient les fichiers .class (compilés) de l’application (Servlets, JavaBean, classes utilitaires...). Le sous­dossier /src du dossier WEB­INF contient les fichiers .java (sources) de l’application (Servlets, JavaBean, classes utilitaires...). Le sous­dossier /lib du dossier WEB­INF peut contenir des librairies .jar utiles au fonctionnement de l’application Web. Par exemple, la librairie de gestion d’e­mails, les librairies de Struts, les librairies de manipulation de code XML, des fichiers .dtd et .tld... Le sous­dossier /ressources du dossier WEB­INF contient les fichiers de configuration pour les applications : par exemple les traductions, les fichiers de gestion de Log4J... 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 ressources seront placés plutôt dans le dossier /WEB­INF/src sachant qu’Eclipse recopie automatiquement les fichiers de ce répertoire vers le répertoire /WEB­INF/classes. En effet, lors du rafraîchissement et de la compilation complète des pages, Eclipse vide le répertoire /WEB­INF/classes ce qui est contraignant. Une application Web est accessible par défaut à l’adresse : http://nomhote:port/webapp/ © ENI Editions - All rigths reserved - 1- Ce second exemple présente une application Java EE plus complexe avec : ● le répertoire /admin pour l’administration, ● le répertoire /css pour les feuilles de style, ● le répertoire pour les /images et pour les /javascript, ● le répertoire /jsp qui contient les JSP non compilées, ● le répertoire /work qui contient les JSP compilées, ● le répertoire /logs qui permet de stocker les fichiers journaux Log4J. 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’arborescence pour développer une application Java EE. Des outils de développement évolués comme Eclipse génèrent automatiquement l’arborescence à la suite de la création d’un projet Tomcat/Java EE. 1. Le descripteur de déploiement web.xml Le fichier web.xml contient des informations qui permettent de définir l’environnement d’exécution de l’application Internet et de lier les composants entre eux. Le fichier permet de définir les relations entre les URL et les Servlets/JSP, la page d’accueil, la page d’erreur, les contraintes de sécurité, les authentifications, les ressources pour accéder aux données... Chaque application Web possède son propre fichier de configuration web.xml placé dans le répertoire /WEB­INF de l’application. Ce fichier est appelé le descripteur de déploiement de l’application ou webapp. Un des avantages de l’utilisation d’un fichier au format XML est le caractère autodescriptif de ce langage. Le fichier web.xml commence par déclarer l’encodage et le type de grammaire utilisée pour vérifier sa syntaxe (DOCTYPE ou schéma XML). Le fichier repose sur une DTD, il est donc important de respecter la syntaxe et l’ordre des balises. En réalité, une application simple n’a pas besoin de ce descripteur car le fichier web.xml du serveur peut suffire mais c’est une bonne habitude que de toujours définir un fichier de déploiement avec une application. Tomcat possède un descripteur de déploiement par défaut qui est utilisé pour les applications et qui est placé dans le répertoire /conf. Le descripteur de déploiement permet de décrire les éléments suivants : - 2- ● les paramètres d’initialisation des Servlets ; ● les variables globales ; ● la session de l’application ; ● les liens entre les URI (Uniform Resource Identifier) et les Servlets ; ● les classes de gestion des événements ; © ENI Editions - All rigths reserved ● les déclarations des filtres ; ● les liens entre les types MIME et les applications correspondantes ; ● les pages d’accueil par défaut ; ● les liens entre les codes d’erreur HTTP et les pages d’erreur ; ● les ressources telles que JDBC, JNDI... ● les ressources EJB. L’élément racine du descripteur de déploiement est la balise . Tous les autres éléments seront donc des enfants de cette balise. Voici ci­dessous la liste exhaustive ordonnée des balises du fichier de déploiement web.xml. L’élément permet de spécifier le chemin vers une icône qui permet de représenter l’application Web pour des outils de gestion d’applications. L’élément : ce nœ ud permet d’assigner un nom à l’application Web. Ce nom peut être utilisé par un outil de gestion d’applications. L’élément : ce nœ ud permet de décrire l’application de manière textuelle. L’élément est une balise vide qui permet de distribuer l’application sur plusieurs serveurs (exemple : cluster de serveurs Tomcat). L’élément : ce nœ ud permet de définir des paramètres qui seront valables pour l’application. Les informations seront passées sous forme d’une paire nom­valeur dans la ServletConfig et donc accessibles depuis des Servlets et pages JSP. Ce principe est très souvent utilisé pour définir des chemins, des fichiers de configuration, des identifiants... Un paramètre peut aussi être passé à une Servlet précise (et non à toutes comme avec ) en utilisant le nœ ud des Servlets. L’élément permet de définir une classe de filtre qui sera applicable avant l’exécution de la Servlet. Le principe de fonctionnement est similaire aux classes Tomcat Valve. Un filtre peut permettre par exemple d’écrire un fichier XSL pour répondre à un contenu XML si la requête se termine par .xml ou en HTML si elle se termine par .html. L’élément : ce nœ ud permet d’associer un filtre à une Servlet spécifique ou à une URL. Les filtres sont mappés en respectant l’ordre dans lequel ils apparaissent dans le descripteur de déploiement. L’élément permet de définir un écouteur qui sera appelé suite à un événement particulier. L’élément permet de définir une Servlet et les attributs. La Servlet sera définie par son nom et sa classe. L’élément : ce nœ ud permet de mapper un modèle d’URI vers un nom de Servlet défini dans un nœ ud . L’élément permet de définir la session de l’application Web avec entre autres le délai d’expiration en minutes des sessions de l’application. L’élément : ce nœ ud mappe une extension au type MIME. C’est une association entre les fichiers publics et des types MIME. L’élément : ce nœ ud permet de spécifier le ou les fichiers (par ordre de priorité) qui doivent être servis à partir d’un répertoire lorsque seul le nom du répertoire est spécifié (ex : index.html, index.jsp). L’élément permet de définir une page d’erreur qui servira en cas de renvoi d’un code erroné suite à une exception. L’élément permet de définir l’emplacement des bibliothèques de balises. Le nœ ud mappe un modèle d’URI vers un fichier de descripteur de bibliothèques de balises. L’élément : ce nœ ud configure une ressource externe qui peut être utilisée par la Servlet. L’élément : ce nœ ud configure également une ressource externe qui peut être utilisée par la Servlet. L’élément permet de configurer la méthode d’authentification et le nom du Realm à utiliser pour l’application. L’élément : ce nœ ud définit le nom d’une ressource accessible grâce à l’interface JNDI. L’élément permet de déclarer une référence distante à un Entreprise JavaBean. L’élément permet de déclarer une référence locale à un Entreprise JavaBean. © ENI Editions - All rigths reserved - 3- 2. Déployer un premier projet Afin de mettre en application les explications de ce guide, nous allons déployer une première application simple. La configuration des applications Web déployées au sein du serveur Tomcat se fait à l’aide du fichier /conf/server.xml ou d’un fichier spécifique par application présent dans /conf/Catalina/localhost/monapplication.xml. a. Projet simple manuellement Ces fichiers peuvent être créés à la main car la structure est assez simple mais nous allons dans un premier temps utiliser l’outil manager de Tomcat qui permet de déployer une application avec une interface Web. Le lien Tomcat Manager ouvre une fenêtre d’authentification. Les informations de connexion correspondent aux données insérées lors de l’installation du serveur. Nous obtenons une page qui liste toutes les applications actuellement déployées sur le serveur. Pour déployer une nouvelle application, il faut utiliser l’interface en précisant les informations pour le déploiement. La première partie permet de préciser le contexte (chemin d’accès à l’application par URL). Le second paramètre optionnel permet d’indiquer le fichier XML pour la configuration de l’application. Le dernier paramètre correspond au répertoire (ou au fichier .war) qui contient l’application. Si nous utilisons un répertoire, celui­ci doit d’abord être créé avant le déploiement de l’application et il doit respecter l’arborescence Java EE (répertoire WEB­INF...). Si nous ne précisons pas de fichier de configuration XML, le projet sera déployé dans le répertoire /webapps de Tomcat et sera donc directement accessible sans fichier de configuration. Nous utilisons l’application monapplication1 qui est un simple projet Java EE avec l’arborescence habituelle (/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’application en HTML et en Java.

monapplication1

<% out.println("mon application1 en Java"); %> - 4- © ENI Editions - All rigths reserved Nous cliquons sur Deploy. 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, il n’y a pas d’insertion de la configuration dans le fichier server.xml ni de création d’un fichier particulier pour le contexte (/conf/Catalina/localhost) car tous les fichiers déployés dans le répertoire des webapps du serveur sont automatiquement exploitables. Le projet a été copié dans le répertoire des /webapps ce qui signifie que désormais, les fichiers devront être modifiés dans ce répertoire /webapps/monapplication1. Notre application est alors accessible à l’adresse suivante : http://localhost:8080/monapplication1/ b. Projet simple avec Eclipse Pour créer un projet Tomcat avec Eclipse, nous allons utiliser la commande File ­ New ­ Project. Avant de pouvoir utiliser Tomcat avec Eclipse, il est nécessaire d’installer le plug­in Sysdeo comme précisé au chapitre Objectifs et spécifications de Java EE de ce guide. © ENI Editions - All rigths reserved - 5- L’item Projet Tomcat permet de créer un projet Tomcat sans avoir à écrire le déploiement dans le fichier de configuration de Tomcat (server.xml) ou un fichier particulier (/conf/Catalina/localhost/) et il permet également de créer automatiquement l’arborescence du projet. La seconde page nous demande de définir un nom pour le projet Eclipse. Nous précisons le répertoire sur le disque (ce répertoire vide doit être créé avant le déploiement) qui recevra le projet Eclipse et l’intégralité de ses fichiers. - 6- © ENI Editions - All rigths reserved Lors de cette dernière étape, nous http://localhost:8080/monapplication2. indiquons le nom du contexte qui sera accessible à l’adresse : Eclipse a alors automatiquement créé l’arborescence nécessaire au projet dans le répertoire sélectionné avec location. De même, un fichier nommé monapplication2.xml a été créé par Eclipse dans le répertoire /conf/Catalina/localhost. Nous retrouvons le paramétrage courant d’une application Web avec le chemin d’accès à la webapp (le contexte avec le paramètre path), l’attribut qui indique que l’application sera rechargée automatiquement (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’application monapplication2 est identique en terme de contenu à 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éployer une application nommée monapplication3 avec Eclipse mais en déclarant l’application directement dans le fichier de configuration du serveur (server.xml) et non dans un fichier spécifique au projet (/conf/Catalina/localhost/monapplication3.xml). Pour cela, dans le menu d’Eclipse, nous utilisons le menu Windows ­ Preferences ­ Tomcat et nous modifions la partie Déclaration des contextes en précisant désormais le fichier de configuration du serveur : server.xml. © ENI Editions - All rigths reserved - 7- Dans le menu d’Eclipse nous réalisons alors un nouveau projet : File ­ New ­ Project. Ensuite, nous indiquons le répertoire (qui est vide mais qui doit déjà exister sur le disque dur) de l’application. - 8- © ENI Editions - All rigths reserved Nous allons créer une simple page index.jsp dans le répertoire de monapplication3.

monapplication3

<% out.println("mon application3 en Java"); %> Si nous testons maintenant l’application monapplication3, nous remarquons que celle­ci n’est pas accessible à l’adresse suivante : http://localhost:8080/monapplication3/. En effet, le projet Eclipse monapplication3 existe et il a été inséré dans le fichier /conf/server.xml en fin de fichier. ...
© ENI Editions - All rigths reserved - 9-
Le problème, c’est que les applications déclarées dans ce fichier nécessitent un redémarrage du serveur. Après cette action, la connexion à l’adresse suivante : http://localhost:8080/monapplication3/ est opérationnelle. Nous avons vu plusieurs manières de déployer un projet Java EE avec Tomcat. Nous remarquons rapidement que l’utilisation d’un fichier spécifique par contexte (/conf/Catalina/localhost) est beaucoup plus souple que la déclaration dans le fichier de configuration du serveur : server.xml. En effet, nous n’avons pas besoin de relancer le serveur, de lire tout le fichier server.xml pour la gestion d’une application et nous obtenons un meilleur découpage... Pour la suite, nous utiliserons la déclaration des applications avec un fichier par application (/conf/Catalina/localhost). Supprimer un projet L’opération de suppression d’un projet avec Eclipse est très simple, il suffit de faire un clic droit sur le nom du projet et de valider l’action delete. Eclipse demande alors si la déclaration du projet doit être supprimée et également le répertoire des fichiers sources associés. 3. Déployer un projet Java EE avec un fichier war Lors de la mise en production d’un projet, un fichier d’empaquetage .war est utilisé pour le déploiement. Dans les gros projets, ce fichier est créé avec l’outil ANT que nous verrons dans un prochain chapitre. La totalité de l’application Web est alors contenue dans un fichier portant l’extension .war pour Web ARchive. Ce fichier d’empaquetage est destiné au déploiement de l’application Web au sein du serveur d’applications. Par la suite, c’est le conteneur du serveur d’applications qui s’occupe de la mise en œ uvre de l’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épertoire monapplication1 sous le nom monapplicationwar. Ensuite, nous allons juste changer le contenu de la page index.jsp en indiquant le nom du nouveau projet.

monapplicationwar

<% 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’organisation des fichiers au sein de l’archive. Ensuite, nous lançons la commande jar (à partir du chemin complet si la variable d’environnement n’a pas été précisée). - 10 - © ENI Editions - All rigths reserved Le paramètre cvf permet de créer le fichier MANIFEST.MF qui donnera des informations sur la version de l’application, la version de Java... Un fichier nommé monapplicationwar.war est alors créé. Pour déployer ce projet, il ne reste plus qu’à déposer ce fichier dans le répertoire /webapps de n’importe quel serveur compatible Java EE. Comme indiqué précédemment, toutes les applications du répertoire /webapps d’une application sont automatiquement déployées. Il faut remarquer l’intérêt de développer avec un langage comme Java qui est portable, plus besoin de se soucier de l’environnement d’exécution final si le standard WAR est respecté. Nous réalisons juste une copie de notre archive dans le répertoire /webapps de Tomcat. Nous patientons quelques instants et le serveur déploie automatiquement la nouvelle application en décompressant l’archive dans son répertoire /webapps. La trace du serveur dans ce cas précis est d’ailleurs observable : La nouvelle application est déployée, le résultat est visible avec l’interface manager de Tomcat. Il est alors possible d’accéder directement à l’application sans déclaration dans le serveur d’applications. © ENI Editions - All rigths reserved - 11 - Analyse, monitoring et supervision 1. Présentation Le système de journalisation de Tomcat et de Java est très complexe et évolué. Tomcat utilise la bibliothèque Jakarta 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èque Log4J et permet la journalisation tout comme l’API java.util.logging de Java. Le fichier de journalisation de Tomcat utilise le fichier de configuration /conf/logging.properties. C’est le principal fichier de configuration pour le serveur mais chaque application déployée peut fournir son propre fichier loggin.properties dans son répertoire /WEB­INF/classes. La machine virtuelle de Java est responsable de la réservation de l’espace mémoire nécessaire à l’application. Au démarrage de l’application, celle­ci réserve de la mémoire auprès du système d’exploitation. Si l’application né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. Cette erreur est très fréquente en Java et doit donc être évitée au maximum. Il est donc extrêmement important de mesurer et surveiller la consommation mémoire du serveur dans la machine virtuelle pour anticiper ce problème. La configuration de la machine virtuelle peut être visualisée par le biais du manager à cette adresse : http://localhost:8080/manager/status. On retrouve la quantité de mémoire disponible (Free Memory) dans la machine virtuelle, la mémoire utilisable (Total Memory) et la mémoire maximum allouable auprès du système (Max Memory). 2. Tester la montée en charge Nous réaliserons par la suite des tests de montée en charge avec l’outil JMeter. Il est également possible de visualiser le nombre de connexions JDBC disponibles à un instant précis, le nombre de threads occupés, la mémoire consommée, les classes chargées... Java Management Extensions JMX est une API Java conçue pour la supervision des applications. JMX est intégrée à partir de la plate­forme Java 1.5. Actuellement, JMX est le standard pour le développement des outils de supervision et d’administration dans les technologies Java. Dans une machine virtuelle Java, il est possible d’associer aux différents objets Java des composants qui permettent d’obtenir des informations sur l’exécution et le traitement des objets. Ces composants sont appelés des MBeans. Ces MBeans sont accessibles via l’élément central, le serveur MBeans qui est capable d’utiliser des protocoles variés pour la connexion d’outils de supervision. Donc les données des MBeans peuvent être accédées grâce à un client JMX en local ou bien à distance. Tomcat crée des MBeans dynamiquement pendant son exécution, lorsque des applications déployées fonctionnent sur le serveur. Les MBeans dynamiques de Tomcat sont créés grâce aux éléments de configuration appelés présents dans le fichier server.xml. Les informations JMX sont disponibles à partir du manager à l’URL http://localhost:8080/manager/jmxproxy/. Il est ainsi possible d’obtenir des informations sur les connecteurs, les threads, les classes... © ENI Editions - All rigths reserved - 1- Il est possible d’utiliser des filtres et par exemple d’afficher uniquement les informations sur les objets MBean de type ThreadPool http://localhost:8080/manager/jmxproxy/?qry=Catalina:type=ThreadPool,*. 3. JConsole et MC4J, des consoles JMX L’ergonomie offerte par le manager de Tomcat est limitée et il est assez difficile de comprendre les paramètres en temps réel. Il existe ainsi plusieurs outils évolués qui permettent d’afficher des rapports détaillés. a. JConsole Pour pouvoir obtenir des statistiques en temps réel, il faut utiliser un client lourd JMX. Le logiciel JConsole développé par Sun et livré en standard avec le JDK 1.6 permet d’utiliser JMX. Son utilisation est assez simple et permet la création de graphiques en temps réel. Si JConsole est lancé par défaut sans rien indiquer (/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’’ car elles ne concernent pas uniquement la machine virtuelle de Tomcat. Pour cela, il est nécessaire de configurer Tomcat avec un connecteur en lui passant des options au démarrage (port pour se connecter au serveur Mbean...). De même, il est important de sécuriser cette connexion par identifiant et mot de passe afin d’éviter à des - 2- © ENI Editions - All rigths reserved hôtes distants de se connecter à notre machine. Sous Windows Avant de pouvoir connecter Tomcat à JConsole, il faut configurer la machine virtuelle Java du serveur pour autoriser l’accès distant de son connecteur. Pour cela, il est nécessaire d’ajouter des options à la machine virtuelle via le script de démarrage de Tomcat. Pour cela, il est nécessaire de configurer la sécurité d’accès au connecteur. Sécurité La configuration de la sécurité démarre par la création des fichiers jmxremote.access et jmxremote.password. Il existe plusieurs possibilités pour la mise en place de ces fichiers. Ces fichiers peuvent être utilisés dans le répertoire /conf du serveur Tomcat ou dans le répertoire /jdk/jre/lib/management de l’installation du JDK Java (c’est cette seconde option qui est utilisée dans ce guide). Le fichier jmxremote.access permet d’indiquer les noms d’utilisateurs autorisés à utiliser le connecteur ainsi que les permissions sur le connecteur. Le fichier jmxremote.password contient les mots de passe associés aux utilisateurs. Le répertoire /jdk/jre/lib/management contient des fichiers d’exemples qui peuvent être renommés pour l’utilisateur. Nous allons copier et renommer le fichier jmxremote.password.template en jmxremote.password. Nous ajoutons ensuite notre utilisateur en fin de fichier. # 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 Nous pourrons donc utiliser cet utilisateur avec son mot de passe pour se connecter au serveur MBean mais pour le moment, nous ne pourrons rien faire car l’utilisateur ne possède aucun droit. Le fichier jmxremote.access permet de définir les droits en fonction des utilisateurs du fichier jmxremote.password. Nous éditons donc ce fichier et ajoutons la ligne suivante : # 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’option readwrite permet d’indiquer qu’il sera possible de réaliser des opérations de lecture sur les objets MBean mais aussi d’écriture ce qui est important par exemple pour vider à distance le garbage collector de la machine virtuelle. Il faut désormais démarrer la machine virtuelle de Tomcat avec un connecteur activé en passant des options au démarrage. Pour cela si le serveur Tomcat est installé avec une archive, le fichier /bin/catalina.bat peut être modifié. Par contre, si le serveur Tomcat a été installé en mode service, c’est­à­dire avec l’installation pour Windows, ce fichier n’est pas présent. Il faut dans ce cas ajouter des paramètres dans la console Tomcat ou dans Eclipse pour le démarrage de Tomcat. -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 © ENI Editions - All rigths reserved - 3- Si le serveur est lancé, alors l’erreur suivante est obtenue dans la console d’Eclipse. Cette erreur indique que le propriétaire du fichier doit être l’utilisateur qui démarre le serveur. Sous Windows XP, dans le menu Outils, il faut choisir Options des dossiers, puis dans la fenêtre cliquer sur l’onglet Affichage et décocher Utiliser le partage de fichiers simple. Cette action permet de gérer la sécurité sur les fichiers. Il faut ensuite réaliser un clic droit sur le fichier jmxremote.password puis sélectionner l’onglet Sécurité et cliquer sur le bouton Paramètres avancés. Il faut alors sélectionner l’onglet Autorisations et décocher la case Hérite de l’objet parent les entrées... Une boîte de dialogue apparaît alors, il faut répondre Copier à la question posée. Ensuite, dans la partie Autorisations, il faut supprimer tous les utilisateurs autorisés et sélectionner l’utilisateur Administrateur du système qui a les droits sur la machine virtuelle Java. - 4- © ENI Editions - All rigths reserved Nous ne devons nous retrouver qu’avec notre utilisateur autorisé. Le serveur Tomcat peut être lancé, le message d’erreur disparaît. Désormais si nous lançons l’outil JConsole, nous pourrons toujours nous connecter en local mais également à la partie des objets MBean qui contrôlent Tomcat. Pour cela, il faut utiliser nos informations de connexion. © ENI Editions - All rigths reserved - 5- Le champ Remote Process correspond à l’adresse locale de la machine et au port MBeans. Les champs Username et Password correspondent aux informations de connexion du fichier jmxremote.password. Dans ce cas de figure, les indications sont correctes et conformes à celles indiquées par le manager de Tomcat. Des informations précises sur les applications déployées par le serveur sont obtenues. Dans le premier onglet, il y a un résumé pratique avec les Threads exécutés, la consommation de la mémoire et les classes chargées. Le deuxiè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 Servlets consomment trop de mémoire... Les derniers onglets permettent d’avoir des informations sur les Threads, classes, objets MBean et la machine virtuelle. - 6- © ENI Editions - All rigths reserved Nous voyons ici un fonctionnement conforme de la mémoire avec les parties chargement et déchargement. Sous Linux Sous Linux l’installation est pratiquement identique. Nous éditons le fichier /usr/local/tomcat/bin/catalina.sh qui permet de démarrer Tomcat. Nous ajoutons alors uniquement les deux lignes suivantes en milieu de fichier vers la ligne 260 qui est relative à la variable JAVA_OPTS et qui correspond au démarrage. -Dcom.sun.management.jmxremote.port=8364 -Dcom.sun.management.jmxremote.ssl=false Nous procédons ensuite comme sous Windows et nous copions le fichier d’exemple jmxremote.password.template en jmxremote.password. #cp /usr/local/jdk/jre/lib/management/jmxremote.password.template /usr/local/jdk/jre/lib/management/jmxremote.password Nous modifions les droits sur ce fichier. #chmod 744 jmxremote.password Comme sous Windows, nous ajoutons notre utilisateur JMX au fichier jmxremote.password. # # # # # Following are two commented-out entries. The "measureRole" role has password "QED". The "controlRole" role has password "R&D". monitorRole controlRole QED R&D moniteurjava monmotdepasse Pour la gestion des droits sur le fichier, c’est plus simple que sous Windows, il suffit de positionner les droits suivants sur le fichier. #chmod 600 jmxremote.password © ENI Editions - All rigths reserved - 7- Ensuite, ce qui est important c’est que l’utilisateur qui lance la JVM soit le propriétaire de ce fichier. #chown tomcat:tomcat jmxremote.password Désormais, nous allons déclarer nos droits avec le fichier jmxremote.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 interface distante pour se connecter au serveur et obtenir des informations de supervision. b. MC4J MC4J est une application JMX encore plus puissante que JConsole. Ce logiciel libre simple d’utilisation est téléchargeable à l’adresse suivante : http://mc4j.org. Ce logiciel est orienté vers la supervision des serveurs d’applications Java EE. MC4J est par exemple compatible avec Tomcat, JBoss, Weblogic, WebSphere... Il est également disponible pour la majorité des systèmes d’exploitation avec une version pour Windows, Linux et Mac OS X. Après le téléchargement et l’installation du logiciel, il peut être lancé depuis la machine locale ou n’importe quelle machine distante capable d’accéder à la machine locale. Après le démarrage de MC4J, il faut configurer une connexion au serveur Tomcat en choisissant Create Server Connection dans le menu Management de l’interface graphique. En premier, le type de serveur auquel se connecter doit être indiqué. Il faut ensuite donner un nom à notre connexion et le numéro de port pour la connexion. Ensuite, si la sécurité est activée sur le serveur d’applications, il faut saisir les informations d’authentification. - 8- © ENI Editions - All rigths reserved Lors de la deuxième étape, l’assistant demande de préciser le répertoire d’installation de Tomcat. Dans le cas où 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 Tomcat pour la supervision. Une fois la connexion établie, l’écran principal de la console MC4J apparaît avec la liste des MBeans du serveur. Le temps de chargement peut parfois être assez long en fonction de la connexion réseau et de la taille du projet à superviser. Beaucoup d’informations utiles pour la supervision du projet sont alors affichées comme les classes chargées en mémoire, les connexions JDBC, les requêtes des clients. Avec un outil comme MC4J, il est désormais assez facile de suivre l’évolution en temps réel des ressources internes du serveur pendant un test de montée en charge ou le développement de celui­ci. Cet outil est également très pratique sur un serveur en production, il fait partie des logiciels indispensables d’un administrateur de serveur Java EE. © ENI Editions - All rigths reserved - 9- 4. JMeter et les tests de montée en charge Après avoir correctement configuré le serveur et les outils de supervision, il est essentiel d’installer un outil pour tester la montée en charge du serveur. Il est important d’utiliser ces outils lors de développement Java EE afin de simuler la - 10 - © ENI Editions - All rigths reserved charge et d’en mesurer l’impact et les conséquences afin de valider les choix de développement, les pages de code... Par exemple, lors du développement d’une application Java EE de gestion et de traitement d’images, il sera né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 conception sur un composant de programmation, l’outil de test pourra nous permettre de valider les librairies, logiciels ou architectures (ex : JDBC ou Hibernate). Il existe plusieurs outils sur le marché pour les tests de montée en charge, tels que l’outil LoadRunner de Mercury et le logiciel libre Apache JMeter. Apache JMeter JMeter est un outil écrit en Java et développé par la fondation Apache. JMeter peut générer des tests sur les serveurs Web HTTP et HTTPS, des bases de données avec JDBC, des annuaires et des serveurs FTP. JMeter fournit enfin un ensemble d’outils pour collecter et mesurer les résultats des tests. Du point de vue de la programmation, il pourra analyser plusieurs types de ressources comme des fichiers Java, des Servlets et pages JSP. Apache JMeter est 100 % Java, il requiert donc juste une machine virtuelle Java pour fonctionner et lancer les tests. JMeter peut être installé sur la même machine que le serveur d’applications, mais afin de ne pas fausser les tests, il vaut mieux éviter d’exécuter JMeter et Tomcat sur les mêmes machines. 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’avoir installé au préalable un JRE ou JDK correctement avec la variable d’environnement JAVA_HOME paramé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/jmeter sous Linux. Il est possible d’ajouter des librairies à JMeter pour tester des connexions JDBC par exemple. Pour cela, il suffit de copier la librairie dans le répertoire /lib et /lib/ext de JMeter. Si JMeter détecte une erreur, celle­ci sera écrite dans le fichier de log appelé /log/jmeter.log. Ce fichier est créé au lancement de l’application. Le fichier de configuration de JMeter pour SSL, proxy et utilisateurs est /bin/jmeter.properties. Utilisation Au démarrage de l’interface, le plan de tests (Test Plan) et le plan de travail (WorkBench) sont affichés. Un plan de test permet de décrire la série de tests à exécuter par JMeter. Pour ajouter ou supprimer un plan de tests, il faut faire un clic droit dans l’explorateur. Il est également possible de charger des éléments de tests présents dans un fichier. Un plan de tests est sauvegardé au format JMX. Un plan de travail contient les éléments qui ne sont pas utilisés par le plan de tests, il s’agit d’un espace de stockage temporaire. Avant de commencer l’écriture d’un plan de tests, il est nécessaire de configurer le serveur et les ressources dans des conditions qui soient le plus proche possible d’un environnement en production. Il faut donc paramétrer correctement la mémoire allouée, le serveur Tomcat, les connecteurs aux bases de données... Un plan de tests consiste à tester la montée en charge du serveur en simulant des accès distants. Il existe plusieurs étapes essentielles pour la construction d’un plan de test : ● Définir le groupe de threads qui simulent le nombre de requêtes en direction du serveur. Un thread est en fait un utilisateur potentiel qui navigue sur le site en production. ● Écrire la configuration du serveur en précisant le nom de la machine en production, le port à utiliser, le protocole... ● Écrire les requêtes HTTP à invoquer par les utilisateurs. ● Ajouter un ou plusieurs composants de mesure JMeter qui va (ou vont) analyser et traiter les réponses sur la charge. ● Enregistrer le plan de tests et le lancer. Créer un plan de tests La première étape consiste à créer les utilisateurs (Threads) qui vont envoyer des requêtes au serveur. Nous allons créer par exemple 5 utilisateurs qui vont envoyer des requêtes à 2 pages du site, 2 fois de suite. Il y aura donc au © ENI Editions - All rigths reserved - 11 - total : 5 utilisateurs * 2 pages * 2 appels = 20 requêtes. Utilisateur Il faut ajouter un groupe d’utilisateurs (ThreadGroup) à notre plan de tests. Nous faisons un clic droit sur le plan de tests et l’action Ajouter ­ Groupe de Thread. L’écran affiché permet de configurer le nombre de Threads ainsi que l’intervalle de temps pendant lequel ils seront 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éfinit le nombre de secondes entre le déclenchement de chaque utilisateur. En positionnant à 0, JMeter va démarrer tous les utilisateurs en même temps et donc simuler des accès simultanés au serveur d’applications. Enfin, la propriété Compteur de Boucle (Loop­Count) permet d’indiquer le nombre de boucles à effectuer sur le serveur. Nous positionnons cette propriété à 2 pour effectuer 2 répétitions. Il est aussi possible de rejouer cette séquence à l’infini en cochant la case appropriée. Configuration du serveur La seconde étape consiste à préciser le serveur d’applications qui sera utilisé pour les tests. Il faut d’abord sélectionner le groupe de Thread dans le plan de tests. Puis après un clic droit sur cet élément, les choix Ajouter (Add) ­ Elément de Configuration ­ Requête HTTP Request par défaut sont sélectionnés. - 12 - © ENI Editions - All rigths reserved Les valeurs à préciser sont le protocole utilisé, le nom de domaine du serveur ou son adresse IP et le numéro de port de celui­ci. Des paramètres aux requêtes peuvent être également ajoutés. Dans ce cas, ils seront transmis avec toutes les requêtes en direction du serveur. Écrire les requêtes utilisateurs Lors de cette troisième étape, il est temps d’écrire les requêtes HTTP, l’objectif étant de simuler la navigation d’un utilisateur sur le site en production. 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.jsp et la seconde vers une page d’aide http://127.0.0.1:8080/aide.jsp. JMeter va donc envoyer les requêtes dans l’ordre où elles apparaissent dans l’arbre. Pour ajouter une page, il faut faire un clic droit sur notre groupe de Threads dans notre plan de travail et ajouter une requête HTTP. © ENI Editions - All rigths reserved - 13 - La configuration du serveur a été réalisée précédemment, il n’y a donc que très peu d’éléments à renseigner. Les éléments indispensables sont la méthode HTTP à utiliser (GET ou POST ) ainsi que le nom et le chemin de la ressource. Nous donnons un nom explicite à notre requête (page d’accueil) et nous positionnons le chemin d’accès à la page (/ étant donné que c’est la racine du site). Nous réalisons ensuite la même opération pour notre seconde page/requête. Ajouter un écouteur/composant de mesure Lors de cette étape, il faut ajouter un ou plusieurs écouteurs pour analyser les tests. Pour ajouter un moniteur de résultats, nous réalisons un clic droit sur le groupe de Thread dans notre plan de tests et nous ajoutons par exemple un écouteur graphique. Nous précisons alors un fichier de sauvegarde pour les résultats en utilisant le bouton Parcourir. Le fichier de sortie est au format .jtl. - 14 - © ENI Editions - All rigths reserved Enregistrer et lancer le plan de tests L’enregistrement du plan de tests n’est pas obligatoire mais conseillé. Pour sauvegarder un plan de tests, il faut sélectionner l’item Enregistrer du menu et préciser un nom de fichier .jmx. Pour lancer l’exécution du plan de tests, il faut utiliser le menu Démarrer (Run). JMeter indique alors avec un petit carré vert lumineux en haut à droite de la fenêtre principale l’exécution des tests. Le rectangle repasse en gris quand les 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 possible d’utiliser des boucles infinies pour simuler des accès en temps réel sur une plus grande période. Les résultats d’analyse peuvent être de toute sorte comme des graphiques, des tableaux de données, des arbres... JMeter est un outil très puissant qui permet d’analyser en temps réel la montée en charge d’un serveur. Les résultats graphiques, textuels et sous forme d’arbres sont très précieux en phases de développement et de production. © ENI Editions - All rigths reserved - 15 - Apache­Tomcat et SSL/HTTPS 1. Présentation Le serveur Tomcat propose dans sa configuration par défaut un connecteur HTTPS préconfiguré. La configuration est en commentaire dans le fichier principal server.xml. Il est donc très simple d’activer ce connecteur en réactivant cette ligne et en utilisant des clés de cryptage conformes. Le connecteur HTTPS de Tomcat utilise l’élément de configuration avec le port SSL indiqué (8443). Cet élément possède les mêmes attributs que les autres connecteurs mais également les attributs keystoreFile (chemin de stockage des clés), keystorePass (mot de passe du fichier de stockage des clés), sslProtocol (le protocole utilisé par HTTPS), clientAuth (certificat client obligatoire ou non), truststoreFile (fichier de validation des certificats clients), truststorePass (le mot de passe du fichier des certificats clients). Voici la configuration que l’on peut utiliser avec un certificat auto­signé. Pour la mise en place de Tomcat en mode HTTPS, il est nécessaire de disposer de clés cryptées conformément aux spécifications en vigueur. Comme pour l’utilisation d’un serveur Tomcat HTTP en production, la configuration idéale en terme de performances et de fiabilité est l’utilisation du moteur HTTP d’un serveur Web à la place de celui de Tomcat. Pour cela, nous utiliserons sur un serveur de production sous Linux, le serveur Apache­SSL. La configuration d’Apache­SSL est semblable à celle d’Apache. Il est cependant nécessaire d’utiliser des clés correctes et une configuration adaptée. Voici ci­dessous un exemple de configuration sur un serveur Linux Debian. Fichier /etc/apache­ssl/httpd.conf NameVirtualHost 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/ AllowOverride None deny from all AllowOverride None deny from all JkMount worker1 JkMount worker1 DirectoryIndex index.html index.htm index.php index.jsp ... SSLEnable © ENI Editions - All rigths reserved - 1- ... SSLCertificateFile /etc/apache-ssl/cles/monprojet.cert SSLCertificateKeyFile /etc/apache-ssl/cles/monprojet.key 2. En résumé Ce chapitre a présenté le serveur d’applications Apache­Tomcat. La première partie a concerné l’installation de Tomcat, la réalisation d’un script de gestion du serveur et la présentation des fichiers journaux. Dans un deuxième temps, le guide a introduit la mise en place de la partie administration et la gestion de la mémoire allouée au serveur. La troisième partie plus technique, a précisé comment coupler Tomcat avec le serveur Web Apache. L’architecture de Tomcat avec l’arborescence et les fichiers de configuration au format XML ont été également détaillés. Puis un bref rappel XML a été évoqué afin de pouvoir manipuler les fichiers de configuration du serveur Java EE. Cette étape a été suivie d’une présentation de l’arborescence d’un projet/webapp Tomcat avec l’introduction du descripteur de déploiement web.xml et le déploiement d’un premier projet simple. L’avant­dernière partie a concerné la supervision, le monitoring du serveur et de ses applications déployées. L’outil JMX intégré à Tomcat et les outils de manipulation JConsole et MC4J ont été détaillés, suivis de la mise en œ uvre de JMeter pour les tests de montée en charge. Enfin la dernière partie a été axée sur la mise en œ uvre de Tomcat avec l’utilisation du protocole HTTPS. - 2- © ENI Editions - All rigths reserved Qu’est­ce qu’une JavaServer Page ? 1. Présentation Une page JSP est une page HTML qui peut contenir du code Java. D’un point de vue technique, il est possible de faire la même chose avec une page JSP qu’avec une Servlet. La différence est essentiellement au niveau de la structure. Une page JSP est un squelette de page HTML qui contient des morceaux de code Java permettant de réaliser des traitements dynamiques ou d’intégrer des données. Une Servlet est à l’inverse, plutôt composée de code Java et parfois de portions de code HTML pour la gestion de l’affichage. La technologie JSP permet l’utilisation et la création de étendre les possibilités de développement. Le mécanisme des données de la logique métier, contrôlant la valeur compilées par le moteur JASPER pour devenir des Servlets balises JSP (taglib) qui ajoutent des fonctionnalités pour JSP permet de séparer la logique contrôlant la présentation des données. D’un point de vue technique, les JSP sont Java. Il existe également des actions JSP (balises XML) qui appellent des fonctions afin de rendre un service spécifique : inclusion de page, redirection... Le principe de fonctionnement est très proche des langages PHP et ASP mais à l’inverse de ces mécanismes, JSP compile le code au lieu de l’interpréter à chaque fois, ce qui soulage la charge de traitement du serveur. 2. Introduction Les pages JSP sont exécutées par le serveur d’applications pour répondre aux requêtes des clients. Les JSP ont plusieurs fonctionnalités : ● la création de sites Web dynamiques (pages Web dynamiques) ; ● le travail avec des bases de données ; ● l’amélioration de la sécurité (le code est exécuté par le serveur et donc non accessible par les clients). ● l’utilisation des JavaBeans (simplicité du code, manipulation d’objets simples...) ; ● l’utilisation de balises personnalisées (taglib). Avec ce modèle de programmation, il est plus aisé de scinder le développement d’une application Web : le graphiste s’occupe de la partie présentation HTML et le développeur s’occupe de la partie logique avec les traitements et l’accès aux données. Côté serveur, une page JSP est interprétée une seule fois, soit lors du premier appel, soit lors du lancement du conteneur Web. En fait, le conteneur Web crée une Servlet à partir de la page JSP. La Servlet est ensuite compilée, chargée en mémoire (s’il n’y a pas d’erreur) et mise en service pour répondre aux requêtes clients. Il existe une Servlet système dans le conteneur Java EE qui est configurée pour traiter les ressources concernant les fichiers portant l’extension .jsp et .jspf (javax.servlet.jsp et javax.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ègre un moteur de Servlets appelé CATALINA. JASPER génère une Servlet source Java (fichier .java) qui est à son tour compilée par CATALINA pour devenir une Servlet exécutable (fichier .class). La syntaxe propre à Java est encadrée par des balises <% et %>, ce qui explique au compilateur JASPER que le reste du code doit être traité comme du langage HTML. 4. Cycle de vie d’une Servlet Une JSP est composée de texte brut contenant du code HTML et du code Java. Lors du premier appel de la page par un utilisateur distant, la page JSP est traduite en une Servlet Java par l’intermédiaire du parseur JSP contenu dans le serveur Java EE. © ENI Editions - All rigths reserved - 1- Le compilateur va ensuite traduire ce fichier Java généré, en un fichier .class, et ce nouveau fichier .class est alors exécuté par la machine virtuelle. La transformation de la page .jsp en Servlet .class n’est effectuée qu’une seule fois, lors du premier appel de la page. C’est pour cela, que le premier chargement de la page est beaucoup plus long que les suivants, qui ne nécessitent pas cette étape. Les utilisateurs suivants n’auront pas ce temps d’attente puisque la page est dé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. Le cycle d’une page JSP comporte quatre phases : ● Chargement et instanciation : le serveur localise la classe Java correspondant à la page JSP et la charge dans la machine virtuelle. ● Initialisation : la page JSP est initialisée selon le principe d’une Servlet. ● Traitement des requêtes clients : la page répond aux requêtes des clients. Aucun code Java n’est envoyé aux clients. ● Fin du cycle : toutes les requêtes sont traitées et terminées, les instances de la classe sont détruites. Lorsqu’un client envoie une requête à une page JSP, le serveur transmet celle­ci au conteneur de JSP et celui­ci détermine la classe d’implémentation qui devra la traiter. - 2- © ENI Editions - All rigths reserved Une page JSP est en fait une Servlet ce qui signifie que tout ce qui est utilisable pour une Servlet (cookies, sessions, context...) peut être également mis en œ uvre dans les pages JSP. Pour résumer, les développeurs et concepteurs graphique utilisent la technologie JSP pour développer rapidement des pages Web dynamiques au contenu riche. Les pages JSP peuvent être créées facilement et entretenues car elles sont basées sur du code HTML et XML. Toute la puissance de Java est alors cachée derrière les pages JSP. © ENI Editions - All rigths reserved - 3- Déclarations, commentaires et scriptlets 1. Présentation Une page JSP porte l’extension .jsp et doit être accessible avec un navigateur dans l’arborescence du serveur Java EE. 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/...). Les pages JSP sont conçues pour avoir un comportement dynamique et elles sont chargées de répondre aux requêtes envoyées par les clients. C’est le code Java inséré dans le code HTML qui permet ce comportement dynamique. 2. Les éléments JSP Nous avons besoin, comme pour d’autres langages de scripts (ex : PHP), d’indiquer au serveur où commence et s’arrête le code HTML et le code Java. Pour cela, la spécification JSP définit des balises qui peuvent être employées pour délimiter le code. Ces balises permettent de définir trois catégories d’éléments : ● Les directives : informations sur la page. ● Les scripts : placement du code dans la page. ● Les actions : inclusion de code basé sur des balises dans la page courante. 3. Les directives Les directives sont des éléments qui permettent de préciser des informations relatives à la page. Il existe les trois directives suivantes : page, include et taglib. Les directives JSP commencent par <%@ et se terminent par %>. Les directives sont parfois appelées directives de moteur JSP. Elles fournissent des instructions et des paramètres qui déterminent la façon dont une page JSP est traitée. Par contre, une directive n’affecte que la page JSP qui la contient. Chaque directive a des attributs auxquels peuvent être affectées des valeurs spécifiques. L’instruction de directive inclut le nom de la directive, suivi de l’attribut et des paires de valeurs dont l’utilisation est souhaitée. La directive page L’élément directive de page permet de définir les attributs de la page JSP, comme les importations de classes ou paquetages, la page invoquée en cas d’erreur, le type de contenu... La directive page peut être définie plusieurs fois dans un même document. Il est recommandé de toujours placer cette directive en tout début de document pour une question de logique et de lisibilité. Nous pouvons développer une page d’erreur générique pour le projet betaboutique. Cette page est placée dans un répertoire nommé /vues afin de bien séparer les éléments de traitements (Servlets) des éléments d’affichage (JSP). <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="java.util.ArrayList" %> ERREURS <% ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); %>

Les erreurs suivantes se sont produites

    <% for(int i=0;i"+(String)erreurs.get(i)+""); } © ENI Editions - All rigths reserved - 1- %>
Il faut remarquer la définition de la directive page qui permet à la première ligne, par l’intermédiaire de ses attributs, d’indiquer le langage utilisé, le type mime et l’encodage. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> L’attribut language permet de préciser le langage utilisé, Java dans tous les cas. L’attribut contentType indique le type MIME utilisé par la page JSP. L’attribut pageEncoding indique le type d’encodage de caractère utilisé dans la JSP pour la réponse HTTP. Il existe plusieurs autres attributs comme errorPage qui permet de spécifier la page JSP à afficher en cas d’erreur. L’attribut info permet de faire une description de la JSP et peut être récupéré par l’instruction getServletInfo(). L’attribut session permet par exemple d’indiquer si le contexte de session HTTP est accessible ou non dans la JSP. Il est possible de modifier la première ligne de cette page et l’attribut contentType pour afficher du texte brut. <%@ page language="java" contentType="text/plain; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Il y a également sur la seconde ligne du fichier un autre exemple d’utilisation de la directive page avec l’importation de la classe Java ArrayList pour la manipulation d’une collection d’objets. <%@ page import="java.util.ArrayList" %> De cette manière, les objets ArrayList, ainsi que toutes les fonctions de cette classe, pourront être manipulés et utilisés. La directive include Nous pouvons également modifier légèrement notre page d’erreur pour utiliser le second type de directive, à savoir la directive include. Dans notre exemple, une page HTML statique est utilisée pour inclure un message descriptif en début de la JSP. Une autre page JSP pourra bien entendu être incluse sans problème. Le projet betaboutique utilise une Servlet d’authentification nommée ServletAuthentification qui permet de vérifier la syntaxe de l’identifiant et du mot de passe. En cas d’erreur, les données sont transférées à la page JSP erreurs.jsp. Cette page est chargée d’afficher les raisons de l’échec de l’authentification. Voici le code de cette page avec l’inclusion d’une page d’en­tête au format HTML. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="java.util.ArrayList" %> <%@ include file="enteteerreurs.html" %> ERREURS <% ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); %>

Les erreurs suivantes se sont produites

    <% for(int i=0;i"+(String)erreurs.get(i)+""); } %>
Si aucune directive n’est utilisée dans une page JSP, le moteur JSP du serveur Web utilisera ses propres paramètres par défaut. Par exemple, si la directive page avec l’attribut contentType est omise, le moteur JSP - 2- © ENI Editions - All rigths reserved affichera automatiquement les informations comme une page WEB (contentType=’’text/html’’). Chaque page JSP compilée est transformée en une Servlet placée dans le répertoire /work/org/apache/jsp. Pour cette page nommée erreurs.jsp, la Servlet générée est présente sous le nom : /work/org/apache/jsp/erreurs_jsp.jsp. Son code compilé est présent dans le fichier : /work/org/apache/jsp/erreurs_jsp.class. Voici le code de la JSP transformée en Servlet par le moteur JSP : 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("

ERREURS SUR LA PAGE

"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\n"); out.write("\n"); out.write("\n"); out.write("\n"); out.write("ERREURS\n"); out.write("\n"); out.write("\r\n"); out.write("\r\n"); out.write(’\n’); ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); out.write("\r\n"); out.write("

Les erreurs suivantes se sont © ENI Editions - All rigths reserved - 3- produites

\r\n"); out.write("
    \r\n"); out.write("\t"); //commentaire monoligne /* commentaire multilignes */ for(int i=0;i"); out.print( this.miseEnFormeMessage((String)erreurs.get(i)) ); out.write("\r\n"); out.write("\t"); } out.write("\r\n"); out.write("
\n"); out.write("\n"); out.write("\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); } } } La directive taglib Le dernier type de directive JSP est l’utilisation de librairies externes personnalisées, par le biais de la directive taglib (<%@ taglib ...%>), que nous utiliserons dans le chapitre dédié à cette technologie. 4. Les scripts Les scripts sont des éléments qui permettent de placer le code Java dans les pages JSP. Il existe trois types de scripts : ● Les déclarations : pour déclarer et/ou initialiser une variable. ● Les scriptlets : pour contenir les instructions Java. ● Les expressions : pour renvoyer des informations aux utilisateurs. Il peut y avoir une partie de script, du code HTML, puis de nouveau du script dans une même page à condition que chaque morceau de script soit bien entouré par les balises Java. a. Les déclarations Une déclaration est employée pour introduire et éventuellement initialiser une variable ou une méthode Java, comme dans un programme. Une déclaration permet de définir un bloc pour les variables globales ainsi que pour des méthodes qui pourront ensuite être utilisées dans le reste du document JSP. Ce principe est très pratique pour l’utilisation de méthodes, car en Java tout est objet, et donc nécessite la déclaration de classes. Cependant, cette technique autorise la simple utilisation de méthodes sans classe et objet. Pour créer une déclaration, il faut placer le code entre le délimiteur d’ouverture <%! et le délimiteur de fermeture % >. Les déclarations peuvent être placées à n’importe quel endroit dans une page JSP, mais il est préférable comme pour les directives, de les placer en début de fichier. Nous allons modifier notre page d’erreur JSP afin d’ajouter une méthode qui permet d’insérer la balise HTML de couleur rouge pour les messages d’erreurs. - 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 ""+message+""; } %> ERREURS <% ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); %>

Les erreurs suivantes se sont produites

    <% for(int i=0;i"+this.miseEnFormeMessage((String) erreurs.get(i))+""); } %>
b. Les scriptlets Les scriptlets contiennent des instructions Java. Une scriptlet est un bloc de code incorporé dans une page. Le code écrit dans une scriptlet est en Java. De même, les scriptlets peuvent contenir n’importe quel code Java valide. Une page contenant une scriptlet est souvent appelée un modèle. Pour ajouter une scriptlet à une page, il faut placer le code entre le délimiteur d’ouverture <% et le délimiteur de fermeture %>. À l’intérieur d’une scriptlet l’objet out peut être utilisé avec la méthode print (ou println) pour générer du contenu texte à destination du client. Les visiteurs qui invoquent la page JSP ne pourront pas voir le code Java contenu dans cette page même s’ils affichent le code source de la page avec leur navigateur Web, ce dernier ne contenant que la sortie générée après traitement. La partie de code présente dans la page d’erreur précédente correspond à une scriptlet. <% for(int i=0;i"+this.miseEnFormeMessage((String) erreurs.get(i))+""); } %> Le code d’une scriptlet ressemble beaucoup à l’élément déclaration des scripts, cependant il existe plusieurs différences entre les deux syntaxes : ● Les scriptlets ne peuvent être employées pour définir des méthodes. Seules les déclarations permettent cela. ● Les variables déclarées dans une déclaration sont des variables d’instance (donc accessibles dans toutes les scriptlets de la page). © ENI Editions - All rigths reserved - 5- ● Les variables déclarées dans une scriptlet sont locales (donc visibles uniquement à l’intérieur du bloc dans lequel elles sont définies). c. Les expressions Les expressions sont utilisées pour renvoyer au client les valeurs d’expressions Java. Elles permettent en effet de générer une sortie sur une page JSP. Souvent, les expressions sont utilisées comme raccourcis pour simplifier le code. Par exemple, le code out.println(...) peut être remplacé par l’expression <%= %>. Le serveur Java EE traite le code contenu dans l’expression et convertit le résultat en une chaîne. Une expression ne peut pas s’achever par un point virgule. Si un point virgule est utilisé dans une expression, une erreur se produira. En effet, l’expression suivante <%= exp > est convertie par le compilateur en la scriptlet suivante <% out.println(exp); %>. Une expression peut contenir un appel de méthode, une instruction ou autre. Nous allons modifier notre page d’erreur du projet betaboutique afin d’utiliser une expression à la place de l’objet out. <%@ 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 ""+message+""; } %> ERREURS <% ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); %>

Les erreurs suivantes se sont produites

    <% for(int i=0;i
  • <%= this.miseEnFormeMessage((String)erreurs.get(i))%>
  • } %>
d. Les commentaires L’ajout de commentaires permet de clarifier le code HTML et JSP. Il est possible d’utiliser des commentaires HTML dans les pages JSP. Ces commentaires apparaissent dans la page renvoyée au navigateur client. Ils sont alors de la forme suivante : . Ce commentaire n’apparaît pas dans la page du client mais peut être visualisé dans le source HTML avec l’outil du navigateur. Il existe également la possibilité d’ajouter des commentaires dans le code JSP en utilisant les balises suivantes : <%­­ un commentaire JSP qui ne sera pas renvoyé au client ­­%>. L’ensemble des informations et du code placé entre les balises de commentaire JSP sera supprimé avant le traitement de la page JSP sur le serveur Web et ne sera pas renvoyé au client. Par contre, un commentaire JSP est de la même forme qu’un commentaire HTML, il ne doit pas se placer dans du code Java (une scriptlet). Les scriptlets sont codées en langage Java, les commentaires Java sont donc appliqués. On retrouve les types monoligne // et multilignes /* */. - 6- © ENI Editions - All rigths reserved <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> COMMENTAIRES <%-- un commentaire JSP qui ne sera pas renvoyé au client --%> <% //commentaire monoligne /* commentaire multilignes */ %> e. Les actions Les actions dans une page JSP sont écrites sous la forme d’éléments XML et permettent de condenser en une seule ligne un traitement qui serait plus long à écrire à l’aide d’un script Java. Il existe deux types d’actions : ● Les actions standards. ● Les actions personnalisées. Les actions standards sont définies par la spécification JSP (d’où le nom standard) et sont les actions de base, accessibles à toutes les pages JSP, car définies dans l’API Java EE. Les éléments XML correspondant, commencent tous par le préfixe jsp. Les actions personnalisées viennent enrichir les actions standards en offrant d’autres possibilités. Ces librairies sont déclarées par la directive taglib vue précédemment (<%@ taglib prefix=’’...’’ uri=’’...’’ % >). Pour le moment nous allons étudier les librairies standards avant d’aborder par la suite les librairies personnalisées. La spécification JSP définit les actions standards suivantes : ● , , , , Ces actions standards sont certainement les plus importantes. Elles permettent de créer ou d’importer un JavaBean © ENI Editions - All rigths reserved - 7- dans la page et de le manipuler. L’action possède plusieurs attributs mais trois sont essentiels (id, class et scope). La signification est la suivante : si l’objet monObjet existe dans la portée indiquée par la propriété scope alors il est 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 est en fait le nom utilisé pour accéder au JavaBean dans le reste de la page. Ce nom doit être unique, il s’agit d’un nom de variable référençant l’instance du JavaBean. Les valeurs possibles sont : ● request : pour les attributs de portée requête. ● session : pour les attributs de portée session. ● application : pour les attributs de portée application. ● page : pour les attributs de portée page. L’élément class est le nom pleinement qualifié de la classe du JavaBean. Il est indispensable de créer une référence à un JavaBean à l’aide de avant de pouvoir utiliser les actions standards et qui sont les accesseurs du JavaBean. Dans notre projet betaboutique, nous avons utilisé un objet appelé client1 de la classe JavaBean Client qui permet de stocker les informations du client en cas de succès. Nous allons modifier notre Servlet d’authentification afin de stocker cet objet dans la portée requête et récupérer ses informations pour l’affichage. package betaboutique.servlets.client; import import import import import import import import java.io.IOException; java.util.ArrayList; javax.servlet.ServletConfig; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; 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" %> BIENVENUE <% Client client=(Client)request.getAttribute("client"); if(client==null) { client.setIdentifiant(""); client.setMotdepasse(""); } %>

Bienvenue client : <%= client.getIdentifiant() %> <%= client.getMotdepasse() %>

Nous remarquons que la dernière partie du code, qui correspond à la page bienvenue.jsp, permet de récupérer l’objet client dans la portée request, de vérifier sa présence (client==null) et d’afficher ses informations. Ce code est un peu lourd à utiliser et nécessite beaucoup de tests. Comme l’objet client est un JavaBean, nous pouvons utiliser les actions standards JSP pour le manipuler. Nous allons modifier notre code afin d’utiliser ce principe. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client :

Nous remarquons que le code est beaucoup plus simple et ’’propre’’. Le JavaBean est utilisé dans sa portée et ses accesseurs pour accéder aux attributs. Pour résumer, le code suivant : est équivalent au code suivant : <% Client client=(Client)request.getAttribute("client"); if(client==null) { © ENI Editions - All rigths reserved - 9- client.setIdentifiant(""); client.setMotdepasse(""); } %> Voici d’ailleurs la portion de code de la Servlet générée (/work/org/apache/jsp/bienvenue_jsp.java) : ... out.write("\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("

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("

\n"); out.write("\n"); ... Comment cela fonctionne­t­il ? La classe Client, qui est située dans le paquetage betaboutique.javabean, est instanciée. Nous utilisons un objet JavaBean nommé client qui est stocké dans la portée request. Le test qui permet de savoir si l’objet n’est pas null, n’est plus obligatoire avec l’action . La Servlet réalise elle­même le test de façon transparente. Nous pouvons, dans notre exemple, accéder au JavaBean car dans notre Servlet l’objet a été passé avec une portée request. Le code est le suivant : ... //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); ... Maintenant modifions la portée de notre JavaBean de cette façon : <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client :

- 10 - © ENI Editions - All rigths reserved Notre code JavaBean client sera alors accessible dans les autres pages JSP de la session de cet utilisateur. Si la portée utilisée est de type application, l’objet sera alors visible par toutes les pages du site quel que soit l’utilisateur connecté. Enfin, si la portée est page, l’objet pourra être utilisé uniquement dans la page. Dès que l’utilisateur quitte la page courante, l’objet JavaBean est alors détruit. Nous pouvons modifier notre Servlet afin de manipuler un objet client dans la session de l’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’objet client est désormais stocké dans une portée session. Si nous utilisons le code du dernier exemple exécuté, voici le résultat : Le JavaBean a une portée request et ne peut donc pas lire un JavaBean de portée session. Il est possible de modifier la portée de notre JavaBean et d’actualiser notre page. ...

Bienvenue client :

... Le JavaBean est désormais accessible. Il possède une portée session ; il peut donc être accédé dans d’autres pages du site (ex : sessionjavabean.jsp). <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client avec la session :

© ENI Editions - All rigths reserved - 11 - Pour résumer, le code suivant : est équivalent au code suivant : <% Client client=(Client)session.getAttribute("client"); if(client==null) { client.setIdentifiant(""); client.setMotdepasse(""); } %> L’action permet de modifier la valeur d’une propriété du JavaBean. L’attribut name correspond à l’identifiant de l’objet JavaBean et l’attribut property est le nom de la propriété à modifier. La valeur de l’attribut property peut être * et permet dans ce cas de lier tous les paramètres de la requête au JavaBean. Voici un exemple de cette utilisation. Le formulaire HTML de la première page (authentification.html) appelle directement une page JSP qui renseigne les attributs correspondants et affiche le résultat à l’écran. Authentification BetaBoutique

Authentification - Client

Identifiant/Login :
Mot de passe :
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client :

- 12 - © ENI Editions - All rigths reserved Si un des attributs n’est pas renseigné dans la requête, l’action tente d’utiliser le paramètre de la requête portant le même nom que la propriété. Si l’attribut n’est pas présent, il prend simplement la valeur null. L’action permet de lire la valeur d’une propriété du JavaBean. L’attribut name correspond à l’identifiant de l’objet JavaBean et le paramètre property correspond à un des attributs de l’objet. Le code suivant :

Bienvenue client :

Est équivalent au code suivant :

Bienvenue client :<%= client.getIdentifiant() %>

Nous remarquons que cette technique très puissante permet à un intégrateur Web de réaliser du code et d’utiliser un langage objet dynamique simplement, sans être un développeur Java confirmé. Cette action est donc très intéressante à utiliser sauf dans le cas d’une portée page. Pour utiliser les actions et , l’attribut à manipuler doit être un JavaBean ce qui oblige a déclarer chaque accesseur (ce qui par contre, n’est pas obligatoire lorsque l’on utilise request.getParameter(...)). Son utilisation est facilement remplaçable par request.getParameter(...), elle est alors intéressante pour des questions de visibilité et pour renseigner une liste complète de paramètres en une seule étape . Cette action permet de réaliser une redirection et donc de transférer le contrôle à une autre page. La syntaxe est la suivante : Elle est équivalente au code suivant : <% RequestDispatcher disp=request.getRequestDispatcher("mapage.jsp"); disp.forward(request,response); %> Des paramètres peuvent être également passés à la page appelée en utilisant la balise . Cette action permet de donner le contrôle à la page JSP courante, puis lorsque cette page est terminée, à une autre page indiquée par cette balise. Cet élément permet d’inclure le contenu d’une ressource Web statique ou dynamique (dans ce cas après son traitement) dans le contenu de la réponse HTTP de la page JSP retournée. La syntaxe est la suivante : Elle est équivalente au code suivant : <% RequestDispatcher disp=request.getRequestDispatcher("mapage.jsp"); disp.include(request,response); %> Des paramètres peuvent être également passés à la page incluse en utilisant la balise . © ENI Editions - All rigths reserved - 13 - Il ne faut pas confondre la directive include (<%@ include file=’’mapage.jsp’’ %>) et l’action . La directive est exécutée au moment de la compilation de la page JSP, tandis que l’action est exécutée autant de fois que la page est appelée et peut donc produire un résultat différent à chaque appel (principe d’un paramètre de requête et d’une inclusion de contenu dynamique). - 14 - © ENI Editions - All rigths reserved Les objets implicites 1. Présentation Les objets implicites sont créés automatiquement lors du traitement de la page JSP par le serveur Java EE. Il existe neuf objets implicites. L’avantage des objets implicites est qu’ils permettent d’invoquer directement leurs méthodes sans avoir à les déclarer et initialiser. Par exemple, une page JSP peut accéder directement à la requête par l’intermédiaire d’un objet implicite nommé request. Les objets implicites utilisables en JSP sont les suivants : ● request : classe javax.servlet.ServletRequest. ● response : classe javax.servlet.ServletResponse. ● session : classe javax.servlet.http.HttpSession. ● pageContext : classe javax.servlet.jsp.PageContext. ● application : classe javax.servlet.ServletContext. ● config : classe javax.servlet.ServletConfig. ● exception : classe java.lang.Throwable. ● out : classe javax.servlet.jsp.JspWriter. ● page : classe java.lang.Object. 2. Utilisation L’objet request L’objet implicite request permet de référencer le contexte de la requête HTTP. Cet objet permet d’utiliser les méthodes relatives à la requête courante. Le plus souvent, cet objet est utilisé pour connaître les paramètres de la requête. Les paramètres envoyés avec les méthodes POST et GET sont donc manipulables. L’objet response L’objet implicite response permet de gérer la réponse renvoyée au client de l’application. Les informations générées par le serveur Web avant qu’elles ne soient envoyées au client sont stockées dans cet objet. L’objet session L’objet implicite session permet de référencer le contexte de session HTTP associé au client. Cet objet permet ainsi de récupérer et manipuler les objets des sessions utilisateurs. L’objet pageContext L’objet implicite pageContext permet de référencer le contexte de la page JSP. Cet objet implicite permet de centraliser les différents attributs de la page JSP dans un seul objet et fournit les fonctionnalités associées à ces attributs. Il est possible par exemple de récupérer un attribut dans la requête ou la session, modifier la valeur d’un attribut, récupérer un attribut du fichier de configuration de l’application (web.xml)... L’objet application L’objet implicite application permet de référencer le contexte de l’application Web. Le principal intérêt de cet objet implicite est de manipuler le fichier de configuration de l’application (web.xml) afin de lire et d’écrire des paramètres. Voici un exemple, qui permet d’afficher l’adresse email du Webmestre présente dans le fichier de configuration de l’application (web.xml). © ENI Editions - All rigths reserved - 1- <% out.println("Contactez le Webmestre : "+application.getInitParameter("emailAdministrateur")); %> L’objet config L’objet implicite config permet de référencer le contexte de l’application Web. Cet objet permet de manipuler les informations concernant la configuration de l’environnement dans lequel une page JSP est traitée sur un serveur Web. Il ne faut pas confondre une application et l’environnement de l’application. L’objet implicite application permet de manipuler les objets de l’application, donc déclarés dans le fichier de configuration de cette façon : emailAdministrateur admin@betaboutique.fr Par contre, si l’objet implicite config est utilisé pour accéder à ces attributs cela ne fonctionne pas. L’objet implicite config permet de manipuler les informations de l’environnement, soit la déclaration suivante : servletauthentification betaboutique.servlets.client.Servlet Authentification defautIdentifiant monidentifiant Voici un exemple de code de la page bienvenueformulaire.jsp du projet betaboutique afin de bien comprendre la distinction entre l’objet application et l’objet config. <% //afficher les informations avec les objets implicites out.println("Contactez le Webmestre : "+application.getInitParameter("emailAdministrateur")+"
"); out.println("Contactez le Webmestre : "+config.getInitParameter("emailAdministrateur")+"
"); out.println("Adresse postale du Webmestre : "+application.getInitParameter("adressewebmestre")+"
"); out.println("Adresse postale du Webmestre : "+config.getInitParameter("adressewebmestre")+"
"); %> ... configjsp /bienvenueformulaire.jsp adressewebmestre 2 rue du haut Dole configjsp /configjsp ... - 2- © ENI Editions - All rigths reserved Cet exemple montre clairement que l’objet implicite application correspond à l’application déployée et que l’objet config correspond à une partie spécifique de l’environnement de l’application. L’objet exception L’objet implicite exception est utilisé pour gérer les erreurs qui pourraient se produire lors du traitement de la page JSP. Cet objet est accessible dans une page d’erreur déclarée dans le fichier de configuration par les balises : . Voici un exemple d’appel d’une page JSP et son code. ... java.lang.Throwable /erreur.jsp ... ... <%@page isErrorPage="true" %> <%= exception.printStackTrace(); %> ... L’objet out L’objet implicite out permet de référencer le flux de sortie des données. Il permet d’envoyer du texte en direction de l’utilisateur Web. Contrairement aux Servlets, il est accessible directement et évite le bloc de code suivant : //flux de sortie PrintWriter out=response.getWriter(); L’objet page L’objet implicite page permet de référencer l’instance courante de la Servlet obtenue après compilation de la JSP. Cet objet est synonyme du mot­clé this et n’est pas très utilisé en programmation JSP. © ENI Editions - All rigths reserved - 3- Premières JSP simples 1. Présentation Nous allons intégrer des premières JSP simples pour la mise en page de la boutique BetaBoutique. Les pages JSP utilisées pour de la mise en page, sont appelées des fragments, en référence à des morceaux de carrelage dans le monde du bâtiment. Les fragments de pages en JSP possèdent l’extension .jspf. Pour le développement de ce projet, il est utile de créer une seconde application avec Eclipse (ex : betaboutiquefin) afin de pouvoir faire des essais (tester les exemples du guide) avec l’application betaboutique et d’utiliser une autre version finale de la boutique. L’application betaboutiquefin possède à cette étape du guide l’arborescence suivante : Le répertoire /vues contient les pages .jsp et .jspf qui seront utilisées pour l’affichage. Le répertoire /img contient toutes les images du projet. Le répertoire /css contient les feuilles de style du projet. Le répertoire /javascript contient les librairies JavaScript utilisées pour les contrôles, effets, auto­complétion... Le répertoire /admin contient les pages et répertoires pour la partie administration. Cette application contient le code définitif mais pas les exemples et tests que nous réalisons. Le fichier de configuration de l’application est le suivant : index.jsp Pour le moment le fichier de configuration de l’application (web.xml) est simple et indique la page d’accueil à utiliser lors de l’appel du site (projet). De cette façon, en utilisant l’URL suivante : http://localhost:8080/betaboutiquefin/, c’est la page index.jsp qui sera appelée. La page index.jsp est très simple, elle possède deux directives d’inclusion afin d’utiliser une page fragment pour l’en­ tête du site et une autre pour le pied de page. © ENI Editions - All rigths reserved - 1- <%@ 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 scripts JavaScript. En effet, dans notre exemple c’est la page index.jsp qui est appelée, notre chemin sera donc relatif à cette page. Dans ce cas, au sein de la page /vues/outils/entete.jspf, il faudra utiliser le répertoire /img directement sans remonter dans l’arborescence (ex : ../../img). Voici un exemple d’utilisation de la feuille de style et du chemin précisé : 2. Utilisation Nous utilisons donc trois pages JSP pour réaliser la mise en page du projet. La page /vues/outils/entete.jspf utilise elle­même la page /vues/outils/navigation.jspf. La page /vues/outils/piedpage.jspf est utilisée pour le bas de page. Le code de la page /vues/outils/entete.jspf est présenté ci­dessous. Ce code simple, est essentiellement composé de balises HTML. La seule partie utilisée (et qui nécessite l’utilisation d’une page JSP) en Java est la directive d’inclusion pour le menu de navigation : BetaBoutique - 2- © ENI Editions - All rigths reserved
RECHERCHE
MON COMPTE       MON PANIER
DECONNEXION 

<%@ include file="navigation.jspf" %>
La page de navigation /vues/outils/navigation.jspf est également très simple. Elle permet d’afficher la liste des catégories ainsi que le résumé du panier réalisé en JavaScript. Pour le moment, les catégories sont affichées en dur, mais il est évident que par la suite, ce menu sera généré de manière dynamique (lecture des catégories dans une source de données). Le projet BetaBoutique est codé en XHTML avec l’utilisation de tableaux, balises
, ... Il est évident que pour un développement professionnel, les techniques conformes au W3C seraient utilisées de préférence (pas de tableau pour la mise en page, utilisation massive de feuilles de style). Enfin, le code de la page /vues/outils/piedpage.jspf est présenté ci­dessous. Il permet de fermer la mise en page et d’afficher des informations sur la boutique.
BetaBoutique est une boutique de et par la société BetaBoutique SARL au Capital 10 000 Euros n° siret 111 222 333 444 555
Nous avons vu comment utiliser des pages JSP simples ainsi que les fragments .jspf. La page obtenue à cette étape du projet est présentée ci­dessous. Le code de la page index.jsp est le suivant : <%@ include file="vues/outils/entete.jspf" %> <%@ include file="vues/outils/piedpage.jspf" %> - 4- © ENI Editions - All rigths reserved Nous remarquons que chaque page du site devra utiliser l’inclusion de la page d’en­tête entete.jspf et de la page de pied de page piedpage.jspf. Cette technique est utilisée par tous les développeurs Web avec tous les langages de programmation. Par contre, Java EE offre une possibilité très souple qui permet de configurer un en­tête et un pied de page directement applicables à toutes les pages du site par l’intermédiaire du fichier de configuration de l’application (web.xml). Pour appliquer notre en­tête et notre pied de page à toutes les pages du site, nous allons ajouter les directives et . Cette technique nécessite le remplacement de la grammaire XML par un schéma pour le fichier web.xml. index.jsp *.jsp include-prelude>/vues/outils/entete.jspf /vues/outils/piedpage.jspf Nous allons désormais traiter lors de la prochaine partie les exceptions et erreurs en JSP. © ENI Editions - All rigths reserved - 5- Gérer les exceptions et erreurs en JSP 1. Présentation L’écriture du code dans un langage informatique doit également prévoir la gestion de diverses erreurs qui ne manqueront pas de survenir. Nous avons déjà abordé la gestion des erreurs et des exceptions lors des premiers exemples de ce chapitre. Nous avons déjà rencontré des bogues lors de nos développements. Parfois, les réponses à ces bogues sont affichées de manière peu sympathique sous la forme de trace ou stack trace. Les applications Web Java peuvent traiter les exceptions de différentes façons. La manière la plus courante de traiter les exceptions en Java est d’utiliser les blocs try...catch ou la directive throws. En Java, un programme déclenche (throws) une exception ou une erreur lorsqu’un dysfonctionnement détourne le programme de son exécution normale. La liste des exceptions est recensée dans la documentation javadoc de la classe Exception. http://java.sun.com/j2se/1.3/docs/api/java/lang/Exception.html Cependant, nous avons besoin d’un moyen permettant de traiter des exceptions imprévues. Nous disposons pour cela en JSP de deux solutions : ● La directive page. ● Le descripteur de déploiement de l’application (le fichier web.xml). 2. La directive page La directive page possède un attribut nommé errorPage. Si une exception se produit et qu’elle n’est pas interceptée par notre programme, c’est­à­dire non traitée dans notre code, la page indiquée est alors retournée par le serveur. 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 isErrorPage doit avoir la valeur true. Nous allons utiliser notre application betaboutique et modifier la page bienvenue.jsp afin de déclencher volontairement des exceptions. Nous allons appeler la page erreursexception.jsp qui permet d’afficher des informations sur l’exception qui vient de se déclencher. <%@ page isErrorPage="true" %> Erreurs

Exception


Description de l’exception : <%= exception.toString() %>


Message de l’exception : <%= exception.getMessage() %>


Pour déclencher une première exception nous allons tenter de convertir une chaîne de caractères en entier. Le code 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" %> BIENVENUE <% //declaration d’une chaine de caracteres © ENI Editions - All rigths reserved - 1- String chaine="machaine"; Integer chaineentier=new Integer(chaine); %> Le message nous indique qu’il s’agit d’une erreur interne à la Servlet. La cause est affichée avec la trace de pile (stack trace). Cet affichage de trace de pile est déclenché en réponse à la première exception trouvée et renvoyée par la méthode exception.printStackTrace(). Il faut bien faire la différence entre une erreur de compilation qui empêche la transformation par le moteur de JSP de la page en Servlet et une erreur d’exécution. Les erreurs d’exécution surviennent pendant l’accès à la page par le visiteur. Ces erreurs sont causées par un problème dans la page JSP (bien compilée) ou bien dans le code qui est appelé par la page, comme un JavaBean par exemple ou une inclusion. Lors du développement d’applications, il est important de suivre les conseils suivants pour corriger les problèmes : ● Il faut commencer par lire le message affiché sur la page d’erreur. ● Si le message affiché ne nous permet pas de corriger le problème, il faut analyser le fichier .java. ● Si vraiment dans les cas extrêmes, l’erreur n’est pas trouvée, il faut compiler la Servlet pour voir la trace affichée. La directive page est donc très utile pour désigner une page d’erreur à afficher en cas de problème. Cependant, l’inconvénient de cette méthode est que la même page d’erreur est renvoyée quelle que soit l’exception rencontrée. 3. Le descripteur de déploiement (web.xml) Le descripteur de déploiement permet de désigner des gestionnaires d’erreurs pour toute l’application. Il est ainsi possible d’avoir des pages d’erreur différentes, en fonction du type d’exception, mais aussi des erreurs relatives au serveur (ex : page non trouvée, problème du serveur...). Il est possible de définir des pages d’erreur pour les exceptions Java et pour les erreurs HTTP. La définition des pages d’erreur vient immédiatement après l’élément du fichier web.xml conformément à la DTD. Nous allons définir une page en cas d’erreur 404 (page non trouvée sur le serveur) dans le fichier web.xml. 404 /404.html La définition d’une page d’erreur pour une exception Java est identique à celle des erreurs HTTP. Le nom de la classe de l’exception Java et la page d’erreur associée sont indiquées. Dans notre cas, la page affichée en cas de problème de conversion sera /erreurconversion.html. Pour tester cette technique, nous allons modifier notre page bienvenue.jsp et enlever la directive de la page d’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 BIENVENUE <% //declaration d’une chaine de caracteres String chaine="machaine"; Integer chaineentier=new Integer(chaine); %> Nous allons déclencher cette page avant de paramétrer le fichier web.xml et donc de générer une erreur d’exécution. La trace affichée sans traitement est présentée ci­dessous : Une astuce de programmation consiste à déclencher volontairement l’erreur et à observer la trace. Dans notre exemple, il est visible que l’exception est de type : java.lang.NumberFormatException. Nous allons donc définir notre exception pour l’erreur de conversion en indiquant la classe précisée. java.lang.NumberFormatException /erreurconversion.html Nous avons modifié le fichier de configuration de l’application, il faut donc recharger l’application pour que nos modifications soient prises en compte. Nous déclenchons à nouveau la page bienvenue.jsp et nous observons que la page d’erreur adaptée est alors affichée. Cette technique est donc beaucoup plus puissante que l’utilisation de la directive page propre aux JSP. Une page d’erreur indiquée dans une page JSP a la priorité sur celles indiquées par le descripteur de déploiement. Nous pouvons donc, avec cette technique gérer plusieurs exceptions Java et erreurs HTTP. La priorité correspond à l’importance de la portée de l’exception, dans le descripteur de déploiement. Nous allons modifier le descripteur de déploiement de la façon suivante : java.lang.Throwable /erreurglobale.html java.lang.NumberFormatException © ENI Editions - All rigths reserved - 3- /erreurconversion.html 404 /404.html Nous avons ajouté le traitement de l’exception java.lang.Throwable qui correspond à toutes les exceptions générées en Java. Autrement dit, dès qu’une exception sera déclenchée (erreur de conversion, calcul, problème de fermeture d’une source de données ou autre) la page erreurglobale.html sera appelée. Nous pouvons recharger l’application et déclencher la page bienvenue.jsp pour voir le résultat. Nous remarquons que la page possède une erreur qui déclenche une exception de conversion mais que c’est la page d’erreur globale qui est affichée. En effet, les erreurs sont attrapées par ordre de priorité dans le fichier de définition de l’application (web.xml). Comme l’exception java.lang.Throwable est plus générale que l’exception java.lang.NumberFormatException c’est bien la page erreurglobale.html qui est affichée. Pour un serveur en production, il faudra juste gérer l’exception générale (java.lang.Throwable) avec une page d’erreur. L’idéal serait bien entendu une page d’erreur adaptée à la plupart des erreurs rencontrées. Par contre, lors de l’étape de développement il est important de ne pas mettre une page statique HTML associé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 contraignant pour les débogages. - 4- © ENI Editions - All rigths reserved Bibliothèque de tags JSTL 1. Présentation Il existe de nombreuses librairies de tags (utilisables à partir de balises XML) proposées pour la technologie JSP Java. Une bibliothèque de balises est composée de grammaires XML (fichier .tld) et de classes d’implémentation des fonctionnalités. Les responsables Java EE se sont aperçus que de nombreux développeurs dépensaient beaucoup d’énergie pour créer de nouvelles balises répondant souvent aux mêmes besoins. Ces actions avaient des syntaxes et des noms différents mais accomplissaient pratiquement la même chose. Le but de JSTL (Java server page Standard Tag Library) est de standardiser un certain nombre d’actions. JSTL est donc un ensemble de tags personnalisés développés sous la JSR 052 permettant de réaliser des opérations de structure (conditions, itérations...), gérer les langues, exécuter des requêtes SQL et utiliser le langage XML. JSTL est actuellement le standard pour l’utilisation de tags, mais il existe de nombreuses autres librairies : ● La librairie de tags Struts : manipulation de JavaBean, HTML, conditions... (http://struts.apache.org/1.x/struts­taglib/index.html) ● La librairie Displaytag : gestion de l’affichage de tableaux HTML, XML, Excel... (http://displaytag.sourceforge.net/11/) ● La librairie Image taglib : gestion des opérations sur des images. (http://jakarta.apache.org/taglibs/sandbox/doc/image­doc/index.html) ● La librairie Upload taglib : gestion du chargement ascendant de fichiers. (http://www.servletsuite.com/servlets/uptag.htm) ● La librairie Ajax Upload taglib : gestion du chargement ascendant de fichiers avec la technologie Ajax. (http://www.servletsuite.com/servlets/ajaxuploadtag.htm) 2. Utilisation La mise en place d’une librairie de tags JSP nécessite le chargement de l’archive d’implémentation au format .jar dans le projet et l’association URI/TLD dans le descripteur de déploiement web.xml. Pour utiliser une librairie de tag, il est nécessaire de procéder de la façon suivante : ● La première étape consiste à télécharger l’archive au format .jar qui contient l’implémentation des balises. Puis il faut copier cette archive dans le répertoire des librairies, à savoir /WEB­INF/lib. Il faut aussi copier tous les fichiers .tld (qui sont les grammaires XML des balises) dans le répertoire /WEB­INF/tld ou /WEB­INF/tlds (à créer). ● La deuxième étape consiste à créer l’association entre la librairie de tags et notre projet dans le descripteur de déploiement (web.xml), par le biais d’une déclaration et d’une URI. ● La dernière étape consiste à ajouter la directive <%@ taglib.../> dans chaque page JSP devant utiliser la librairie. Les fichiers .jar contiennent le code Java pour l’utilisation des balises. Par contre, les fichiers .tld contiennent la grammaire des balises utilisables avec les archives .jar. Ainsi, la déclaration de ces grammaires nous permet de bien utiliser les balises et de bénéficier de messages d’erreurs efficaces lors des utilisations (nombre de paramètres pour la balise, syntaxe des paramètres...). © ENI Editions - All rigths reserved - 1- Dans un premier temps nous allons utiliser la bibliothèque de tags standard Java EE JSTL. JSTL possède quatre bibliothèques de tags : ● Fonctions de base avec c.tld et l’URI http://java.sun.com/jstl/core ● Fonctions de traitement XML avec x.tld et l’URI http://java.sun/com/jstl/xml ● Fonctions d’internationalisation (langues) avec fmt.tld et l’URI http://java.sun.com/jstl/fmt ● Fonctions de traitement SQL avec sql.tld et l’URI http://java.sun.com/jstl/sql 3. Implémentation Pour utiliser JSTL, il faut copier les librairies jstl.jar et standard.jar dans notre répertoire de librairies /WEB­INF/lib. Ces archives sont disponibles sur Internet sur le site Java. Dans un second temps, nous allons copier les fichiers .tld (grammaires des balises) dans un répertoire nommé /WEB­INF/tld que nous allons créer. L’arborescence de notre projet doit avoir la structure suivante : Il reste alors une dernière étape après le chargement des librairies .jar et la mise en place des grammaires .tld, c’est la déclaration des bibliothèques à utiliser dans le descripteur de déploiement du projet (web.xml). Nous plaçons le code suivant en fin de fichier web.xml après les balises . ... /WEB-INF/tld/c.tld /WEB-INF/tld/c.tld /WEB-INF/tld/x.tld /WEB-INF/tld/x.tld /WEB-INF/tld/sql.tld /WEB-INF/tld/sql.tld ... La balise nous indique une URI pleinement qualifiée qui sera par la suite utilisée dans notre directive <% @taglib/> des pages JSP. Dans notre cas, la valeur utilisée est le chemin vers la librairie c.tld, mais nous pourrions utiliser n’importe quelle URI pleinement qualifiée. Voici un autre exemple d’URI : ... http://java.sun.com/jstl/core /WEB-INF/tld/c.tld http://java.sun.com/jstl/xml /WEB-INF/tld/x.tld http://java.sun.com/jstl/sql - 2- © ENI Editions - All rigths reserved /WEB-INF/tld/sql.tld ... La seconde balise permet de préciser le chemin vers la grammaire des librairies que nous venons d’installer. Dans la configuration précédente, nous avons donc paramétré les librairies c.tld, x.tld et sql.tld. Désormais il ne reste plus qu’à déclarer nos balises de tags dans chaque page JSP qui souhaite les utiliser. Cette opération simple est réalisée par l’intermédiaire de la directive <%@ taglib.../>. Il faut préciser le préfixe des balises et le chemin pleinement qualifié identique à celui renseigné dans le descripteur de déploiement : <%@ taglib uri="/WEB­ INF/tld/c.tld" prefix="c" %>. 4. Utilisation de bibliothèques a. La bibliothèque core Nous allons utiliser la bibliothèque core : c.tld. Cette bibliothèque regroupe les actions fondamentales. Pour cela, nous utilisons notre page bienvenue.jsp et nous allons définir les directives pour l’utilisation de nos bibliothè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" %> BIENVENUE Nos bibliothèques de balises sont désormais définies, nous pouvons utiliser de nouvelles balises XML très puissantes pour nos développements. ● Le tag set permet de stocker une variable dans une portée particulière (page, request, session ou application). ● Le tag out permet d’afficher la valeur d’une variable, ce tag est équivalent à <%= ...%>. ● Le tag remove permet de supprimer une variable. ● Le tag catch permet de gérer les exceptions. ● Le tag if est utilisé pour réaliser une condition. ● Le tag choose est utilisé pour des cas mutuellement exclusifs (équivalent du switch). ● Le tag foreach est utilisé pour réaliser des itérations. ● Le tag forTokens est utilisé pour découper une chaîne selon un ou plusieurs séparateurs. ● Le tag import permet d’accéder à une ressource via son URL pour l’inclure ou l’utiliser dans la page JSP. ● Le tag redirect permet de réaliser une redirection vers une nouvelle URL. Grâce à l’utilisation de grammaires XML (fichiers .tld pour les taglibs), Eclipse sait gérer l’auto­complétion des balises et les erreurs de déclaration. © ENI Editions - All rigths reserved - 3- Voici un exemple d’utilisation de la bibliothè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" %> BIENVENUE
Une conditionnelle opérationnelle Une conditionnelle non affichée


b. La bibliothèque XML Dans un second temps, nous allons utiliser la bibliothèque xml : x.tld. Pour cela, nous allons créer une nouvelle page xmltaglib.jsp et définir la directive pour utiliser nos nouvelles balises. Cette bibliothèque très puissante permet de manipuler des données en provenance d’un contenu XML (document ou 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" %> XML Taxi 4 taxi4.png Le choc choc.png Mort un dimanche de pluie muDd32D3.png - 4- © ENI Editions - All rigths reserved Cette page affiche le contenu complet XML sous la forme d’un flux textuel. En effet, la variable nommée xml stocke le contenu du code XML et la balise permet de l’afficher. Le tag parse permet d’analyser le document et de stocker le résultat dans une variable qui pourra être exploitée par la JSP. Le tag set est équivalent au tag set de la bibliothèque core. Il permet d’évaluer l’expression fournie dans l’attribut select et de stocker le résultat dans une variable. Le tag out est équivalent au tag out de la bibliothèque core. Il permet d’envoyer le résultat dans le flux de sortie. L’attribut select permet de préciser le chemin XPath de l’arbre XML. Le tag if est équivalent au tag if de la bibliothèque core à la différence qu’il évalue une expression XPath. Le tag choose est équivalent au tag choose de la bibliothèque core à la différence qu’il utilise une expression XPath. Le tag forEach est équivalent au tag forEach de la bibliothèque core. Il permet de réaliser des boucles sur des nœ uds. Le tag transform permet d’appliquer une transformation XSLT (eXtensible Stylesheet Language Transformations) à un document XML. L’attribut xsl permet de spécifier la feuille de styles XSL à utiliser. Le tag transform est introduit dans le cadre du projet betaboutique afin de réaliser une transformation XSLT sur le document XML qui contient la liste des DVD. Le code précédent permet d’afficher sous forme textuelle le contenu du document XML. Nous allons donc séparer les données de la mise en page en utilisant la technique suivante : Un premier document au format XML (ou contenu XML généré à la volée) contient uniquement des métadonnées et ne s’occupe en aucun cas de la présentation. Son contenu peut avoir la structure suivante : Taxi 4 taxi4.png Le choc choc.png Mort un dimanche de pluie muDd32D3.png Un second document (présent en dur sur le serveur) est composé de balises XML/XPath et HTML, il permet de lire les données du fichier XML et de gérer la mise en forme de ces données. Il s’occupe uniquement d’opérations simples (conditions, boucles) et de la mise en page. Ce document est une feuille de styles XSLT au format .xsl. Voici un exemple de son contenu pour l’affichage des DVD : © ENI Editions - All rigths reserved - 5- BETABOUTIQUE

Liste des DVD

#eaeaea
TITREIMAGEID
Nous avons donc désormais nos données XML, notre fichier de mise en forme .xsl, il ne nous reste plus qu’à utiliser notre page JSP qui permet par l’intermédiaire de la balise 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" %> XML Taxi 4 taxi4.png Le choc choc.png Mort un dimanche de pluie muDd32D3.png - 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 technique très puissante permet de complètement séparer la partie données de la partie présentation. Le document résultant dans notre cas est un fichier au format HTML, mais nous aurions pu générer des documents PDF, des images SVG, des pages 3D VRML... Bien entendu, par la suite, le contenu XML des données sera généré de façon dynamique avec une page JSP ou une Servlet. Les données seront lues dans une source de données de type fichier ou base de données, la feuille de style sera adaptée en conséquence pour l’affichage et la balise permettra de générer à la volée la page HTML (ou n’importe quel autre format compatible). c. La bibliothèque I18n Nous allons mettre en place la bibliothèque I18n : fmt.tld. Cette bibliothèque regroupe les actions pour la gestion de l’internationalisation. L’internationalisation en programmation et développement consiste à gérer les langues et donc à proposer des pages multilingues. Le terme I18n est standardisé pour l’internationalisation et correspond aux 18 caractères qui composent le mot internationalisation entre le i et le n. En programmation la langue est présentée avec une locale. Une locale est composée de deux paramètres : la première partie lang et la seconde partie country séparées par le caractère underscore. Par exemple : ● le français France est représenté par : fr_FR. ● le français canadien est représenté par : fr_CA ● l’anglais américain est représenté par : en_US Pour manipuler la bibliothèque de langue, nous ajoutons sa définition dans le fichier de configuration de l’application (web.xml). ... /WEB-INF/tld/fmt.tld /WEB-INF/tld/fmt.tld ... Nous ajoutons également la déclaration avec la directive adaptée dans notre page JSP, qui utilise cette bibliothèque de tags. <%@ taglib uri="/WEB-INF/tld/fmt.tld" prefix="fmt" %> Pour la mise en œ uvre de la localisation des messages en Java, il faut utiliser un ensemble de fichiers appelés bundle en anglais. Il faut définir un fichier par langue/locale. Chaque fichier possède un préfixe commun appelé basename et doit avoir l’extension .properties. Les fichiers pour des langues particulières doivent avoir le même préfixe suivi d’un caractère underscore et du code langue. Pour être accessibles, ces fichiers doivent être inclus dans le classpath, ils © ENI Editions - All rigths reserved - 7- doivent donc être placés dans le répertoire /WEB­INF/classes. Ce répertoire est fréquemment effacé par Eclipse lors du développement, lors des mises à jour du projet (compilation des fichiers...). Nos fichiers de langues seront donc effacés à chaque fois. Le plus simple est alors de déposer les fichiers de langues dans un répertoire nommé /WEB­INF/src/ressources car celui­ci sera copié automatiquement (et à chaque modification) dans le répertoire /WEB­INF/classes. Pour la mise en œ uvre de l’internationalisation, nous allons créer un répertoire ressources dans l’arborescence /WEB­ INF/src et deux fichiers. Le premier fichier nommé betaboutique.properties correspond aux données de la langue par défaut (français dans notre cas) et le fichier betaboutique_en.properties correspond aux données en anglais. Nous utilisons également un fichier pour le français nommé betaboutique_fr.properties. Voici l’arborescence au final : Un fichier de langue contient des paires de valeurs composées d’une clé unique et de sa valeur associée. Exemple : dvd.webmestre=Merci de contacter webmestre@betaboutique.com Voici les deux fichiers que nous allons définir. Cet exemple regroupe seulement deux traductions, mais nous pouvons indiquer autant de traductions que souhaité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 La technique d’internationalisation abordée dans ce chapitre est valable pour toute la programmation Java et sera utilisée de la même façon avec des Servlets, Struts, des applications JWS ou des Applets. - 8- ● Le tag setBundle permet de localiser/paramétrer le bundle utilisé par défaut. ● Le tag message permet de localiser un message en fonction de sa langue et de l’afficher. ● Le tag setLocale permet de positionner une locale (changer de langue). ● Le tag formatNumber permet de formater un nombre en fonction de la locale. ● Le tag parseNumber permet des conversions. ● Le tag formatDate permet de formater la date selon la locale. ● Le tag setTimeZone permet de stocker le fuseau horaire dans une variable. © ENI Editions - All rigths reserved ● Le tag timeZone permet de préciser le fuseau horaire à utiliser dans le corps de la page. Avant de tester ces fonctionnalités, nous allons mettre notre navigateur en français. Pour cela, avec Firefox nous utilisons le menu Outils ­ Options ­ Avancé ­ Langues ­ Choisir et nous laissons uniquement les langues Français[fr] et Français/France [fr­fr]. Pour la prise en compte de ces opérations, il faut fermer le navigateur et ouvrir une nouvelle fenêtre. Pour mettre en application l’internationalisation, nous allons créer une page JSP nommée i18n.jsp et utiliser le code 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" %> I18n

Si aucun couple de valeur n’est trouvé pour la clé fournie, le tag renvoie alors ???xxx??? où xxx représente le nom de la clé, comme pour le cas : dvd.retour. Notre page est correctement affichée en français (langue par défaut dans le navigateur). Nous allons paramétrer notre navigateur en anglais selon le même principe que précédemment et relancer le navigateur. © ENI Editions - All rigths reserved - 9- La page est automatiquement traduite dans la langue sélectionnée. Cette technique de programmation très utile permet de proposer des sites multilingues. Par la suite, nous pourrons proposer un service basé sur une Servlet qui permet de changer la locale à la volée avec des liens HTML. Nous pouvons également modifier une locale avec les taglibs JSP de cette façon (après avoir positionné le navigateur en 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" %> I18n - 10 - © ENI Editions - All rigths reserved

d. La bibliothèque DataBase Cette bibliothèque SQL facilite l’accès et la manipulation de bases de données. Elle permet entre autres de créer une connexion vers une base de données, de réaliser des requêtes de sélection, d’encapsuler plusieurs requêtes dans une transaction ou de réaliser des mises à jour. Ces balises sont très pratiques pour des sites qui nécessitent un développement rapide. Par contre, elles sont peu utilisées dans les projets qui séparent la partie Vue de la partie Modèle/Accès aux données. En utilisant de telles balises, en effet le code HTML, le code de traitement et l’accès aux données sont mélangés dans la même page JSP. L’exemple suivant nommé sql.jsp permet de se connecter à la base de données betaboutique et de lister la totalité des enregistrements de la table categorie afin de générer le menu principal de navigation. Le tag permet de créer un lien vers la base de données à partir d’une connexion simple ou d’un pool de connexion JDBC. Ce code utilise une connexion JDBC vers une base de données MySQL. Il est donc nécessaire de copier le pilote MySQL approprié dans le répertoire /WEB­INF/lib de l’application ou /common/lib du serveur Tomcat ou /lib du serveur Tomcat 6.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" %> SQL
Dans le cas d’un pool de connexion JDBC, c’est le tag qui change. Le nom de notre connexion JNDI est précisé en paramètre. Cette bibliothèque de tag permet également de gérer des paramètres dans les requêtes, les transactions, les rollbacks... © ENI Editions - All rigths reserved - 11 - Bibliothèque de balises personnalisées 1. Présentation Nous avons étudié les balises/taglibs proposées par Java, des communautés diverses et des développeurs. Parfois, pour les besoins d’un projet ou par habitude de développement nous avons besoin d’une bibliothèque de balises qui n’existe pas encore ou qui ne fournit par les services souhaités (balises pour la pagination, le cryptage de données, la transformation de chaînes de caractères). Nous pouvons alors développer notre propre bibliothèque de balises personnalisées en suivant le principe du standard JSTL et de ses taglibs. De cette façon, nos pages JSP ne contiendront aucun code Java. En fait, il ne s’agit pas vraiment de supprimer le code Java mais plutôt de le masquer de façon qu’il soit invisible pour les graphistes/concepteurs des pages. Nous avons utilisé plusieurs balises JSP standards comme , ... Ces balises répondent à des besoins courants et sont utilisées par tous les programmeurs JSP. Les bibliothèques de balises servent à répondre à des besoins particuliers pour nos applications. De même, lorsqu’un programmeur a conçu une bibliothè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. Ces balises dynamiques s’utilisent de la même manière que les balises HTML, le partage des tâches entre programmeurs et concepteurs de l’interface devient beaucoup plus clair. 2. Actions personnalisé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 : . Le préfixe permet d’éviter des conflits de noms entre les balises des différentes bibliothèques. Ce préfixe est choisi par le développeur. Le préfixe est suivi du nom de l’action, également choisi par le développeur de la bibliothèque. Les actions peuvent être vides (juste la présence de la balise) mais peuvent également posséder un corps. Enfin, les actions peuvent avoir des attributs qui spécifient les détails de leur comportement. 3. Mise en place La première étape dans la création d’une balise personnalisée consiste à créer le fichier de classe gestionnaire de balises. Ce gestionnaire de balises stocke les méthodes qui lancent des actions spécifiques lorsque les balises personnalisées sont traitées. Cette classe Java chargée d’implémenter le comportement de l’action doit respecter les spécifications d’un JavaBean et implémenter une des interfaces d’extension de balises. Il existe plusieurs interfaces pour la gestion des actions : ● Tag et BodyTag : pour les actions simples avec ou sans corps. ● IterationTag : pour gérer les itérations plus facilement. ● SimpleTag et JspFragmentTag : pour encapsuler le contenu du corps de l’action dans un objet. Les gestionnaires de balises simples L’interface SimpleTag et la classe SimpleTagSupport permettent d’implémenter tous les gestionnaires de balises JSP 2.0, avec ou sans itération et évaluation du corps. Pour utiliser une action personnalisée, il suffit de créer une classe qui étend la classe de base SimpleTagSupport et redéfinir les méthodes nécessaires pour produire le comportement souhaité. Dans la majorité des cas, la méthode doTag() est suffisante. Cette méthode gère l’intégralité du comportement de l’action (sans se soucier du début de la balise, du corps et de la fin). Un gestionnaire de balises doit importer les paquetages javax.servlet.jsp et javax.servlet.jsp.tagext. Un gestionnaire de balises simple doit avoir une méthode doStartTag() qui contient le code exécuté. Cette méthode doit être déclarée en accès public afin d’être accessible en dehors du gestionnaire de balises. La méthode doStartTag() doit retourner une valeur pour indiquer que la balise renvoie quelque chose ou la valeur SKIP_BODY pour sauter cette étape. Afin de mettre en place notre bibliothèque de balises, nous allons commencer par créer un nouveau paquetage nommé taglib et placer la classe suivante dans ce paquet. package taglib; © ENI Editions - All rigths reserved - 1- import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.*; public class SimpleTag extends TagSupport { public int doStartTag()throws JspException { return SKIP_BODY; } } La classe du gestionnaire de balises est créée, nous pouvons utiliser l’objet PageContext pour gérer le flux de sortie et envoyer des données à la page utilisatrice. La méthode getOut() de cet objet permet d’envoyer des informations au client. Cette méthode peut générer une exception qu’il est donc nécessaire de traiter. 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. Mise en place d’un fichier de description Les fichiers de définition des bibliothèques portent l’extension .tld comme par exemple c.tld, x.tld ou fmt.tld. Ces fichiers qui sont des grammaires XML, sont placés dans le répertoire /WEB­INF/tld de l’application courante. Une fois le gestionnaire créé avec une classe Java adaptée, nous devons créer le fichier de description au format XML. Le fichier commence par un en­tête XML qui contient des informations sur le fichier avec le prologue et la grammaire utilisée. Nous commençons par créer le fichier suivant : /WEB­INF/tld/betaboutique.tld. - 2- ● Les balises et sont utilisées pour délimiter le corps principal du fichier de description. ● La balise correspond au numéro de version de la bibliothèque du concepteur. ● La balise correspond à la version JSP supportée par la bibliothèque du développeur. ● La balise est un nom abrégé de la bibliothèque. ● La balise est un identificateur optionnel de ressources. ● La balise est une brève description de la bibliothèque. © ENI Editions - All rigths reserved Ensuite, vient la définition de chacune des balises. Chaque définition commence avec la balise qui accepte les sous­éléments ci­dessous : ● La balise est le nom officiel de la balise. Ce nom est le même que celui utilisé dans les pages JSP. ● La balise est la désignation de la classe Java supportant la balise. ● La balise est une description de la balise. ● La balise stipule le contenu de la balise. Voici notre fichier de description betaboutique.tld dans sa version minimale : 1.0 2.0 Une premiere balise pour le Webmestre webmestre taglib.SimpleTag 5. Configuration de la librairie dans le descripteur web.xml Nous avons créé le code du gestionnaire de balises ainsi que sa grammaire associée, nous devons maintenant réaliser la dernière étape qui consiste à paramétrer le descripteur de déploiement (web.xml) avec la balise . Cette opération est identique à l’utilisation des actions standards JSTL, elle est souvent nommée aiguillage. Il faut d’abord définir la balise pour préciser une adresse pleinement qualifiée. Puis il faut indiquer simplement la localisation de notre fichier de description des balises sur le serveur et recharger le contexte pour que ces modifications soient prises en compte. ... /WEB-INF/tld/betaboutique.tld /WEB-INF/tld/betaboutique.tld ... 6. Utilisation d’une librairie personnalisée Nous avons suivi les trois étapes nécessaires à la mise en place d’une bibliothèque de balises personnalisées, à savoir : ● la création de la classe Java pour le gestionnaire de balises ; ● le codage du fichier de description des balises au format XML ; ● la configuration du fichier web.xml. Nous pouvons maintenant utiliser notre bibliothèque avec la directive <%@ taglib .../> et l’URI correspondante dans une page JSP. Nous allons reprendre notre page bienvenue.jsp du projet betaboutique pour mettre en œ uvre la librairie. La directive taglib emploie l’attribut uri pour spécifier un identifiant relatif 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érencer la bibliothèque de balises qui contient l’information sur la balise personnalisée. Chaque bibliothèque de © ENI Editions - All rigths reserved - 3- balises a besoin d’un préfixe différent pour éviter des conflits d’actions. Pour utiliser une balise personnalisée dans une page JSP, nous devons saisir le préfixe et le nom de l’action que nous avons affectés dans le fichier de description, séparés par un symbole deux points. Les balises simples qui ne contiennent pas de corps ou d’information entre la balise de début et de fin peuvent être réduites à une seule balise. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/WEB-INF/tld/betaboutique.tld" prefix="bb" %> BIENVENUE OU 7. Gestionnaire de balises et gestion des attributs Une balise personnalisée peut contenir des attributs qui seront spécifiés au sein de la page JSP. Dans une balise personnalisée, un attribut est représenté par une variable au sein du fichier de classe du gestionnaire de balises. Cette variable peut avoir une valeur par défaut, qui sera utilisée par la balise si aucune valeur n’est précisée pour l’attribut lors de son utilisation. Lorsque, dans la page JSP, la balise est utilisée avec un attribut, la valeur donnée pour cet attribut est transmise au gestionnaire de balises. Il faut alors créer une méthode d’affectation (accesseur) pour cet attribut dans le gestionnaire de balises. La méthode doit être en accès public, il n’y a pas de type de retour et le nom de la méthode est le même que celui de l’attribut. Nous allons créer une seconde balise qui affiche l’adresse email du Webmestre avec un lien mailto ou pas suivant le choix du codeur. package taglib; import import import import import java.io.IOException; javax.servlet.jsp.JspException; javax.servlet.jsp.JspTagException; javax.servlet.jsp.JspWriter; 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("Webmestre : info@betaboutique.com"); } 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("
"); } catch(Exception e) { } return SKIP_BODY; } public boolean isMailto() { return mailto; } public void setMailto(boolean mailto) { this.mailto = mailto; } } Ensuite, nous devons modifier notre fichier de description pour gérer les attributs. La balise permet de préciser des détails sur un attribut et doit se situer à la suite de la balise dans le fichier de description. Le nom de l’attribut est précisé à l’aide de la balise . Le nom donné à cette balise est sensible à la casse et doit être le même que celui utilisé dans les pages JSP. La balise suivante indique si l’attribut est indispensable lors de son utilisation dans une page JSP. Si la valeur utilisée est false, cet attribut sera optionnel. Si sa valeur est true, l’attribut devra être obligatoirement utilisé, sinon la page JSP provoquera une erreur. Voici le nouveau contenu du fichier betaboutique.tld. 1.0 2.0 Une premiere balise pour le Webmestre webmestre taglib.SimpleTag mailto true Si désormais nous utilisons notre page bienvenue.jsp avec le code suivant, la page nous indique que d’après la grammaire de la bibliothèque l’attribut mailto est obligatoire. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/WEB-INF/tld/betaboutique.tld" prefix="bb" %> BIENVENUE © ENI Editions - All rigths reserved - 5- Nous devons donc préciser l’attribut mailto pour générer une page sans erreur. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/WEB-INF/tld/betaboutique.tld" prefix="bb" %> BIENVENUE OU La page est exécutée sans erreur et l’attribut mailto est bien pris en compte. Vous remarquez également l’utilité de la fonction doEndTag() qui permet d’afficher une barre de séparation HTML (
) après chaque utilisation de la balise. L’ajout de la balise true dans le fichier de description autorise la valeur de l’attribut à être affectée durant l’exécution du code JSP. 8. Gestionnaire de balises et gestion du corps des balises Le corps d’une balise est l’information comprise entre les balises de début et de fin. Le corps de la balise peut être constitué de texte ou de code JSP. Un gestionnaire de balises peut donc être créé pour utiliser l’information contenue dans le corps de la balise personnalisée. La méthode doStartTag() est toujours utilisée, mais pour une balise avec corps, cette méthode doit retourner la valeur EVAL_BODY_INCLUDE, pour que le serveur Web traite l’information contenue dans le corps de la balise. Le gestionnaire de balises doit également inclure une méthode doEndTag() lors du traitement du corps. Cette méthode contient pour rappel, le code à exécuter après le traitement du corps. La méthode doEndTag() doit retourner une valeur pour indiquer si le reste de la page JSP doit être traité ou non. Dans la plupart des cas, la valeur retournée est EVAL_PAGE pour indiquer que le reste de la page doit être traité par le serveur. Pour que le reste de la page ne soit pas traité, il faut utiliser la valeur SKIP_PAGE (pour les sessions par exemple). package taglib; import import import import import public java.io.IOException; javax.servlet.jsp.JspException; javax.servlet.jsp.JspTagException; javax.servlet.jsp.JspWriter; javax.servlet.jsp.tagext.*; 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(""); } 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(""); } out.print("
"); } catch(Exception e) { } return EVAL_PAGE; } public boolean isMailto() { return mailto; } public void setMailto(boolean mailto) { this.mailto = mailto; } } Après avoir créé un fichier gestionnaire de balises pour une balise personnalisée, il est conseillé d’inclure dans le fichier de description une note indiquant si la balise comprend un corps. La balise permet de signaler la présence éventuelle d’un corps. Le type de contenu de la balise personnalisée est alors précisé en insérant une valeur exemple entre les balises et . Si aucun type de contenu n’est spécifié, il prendra la valeur précisée par défaut. Une bonne habitude est d’utiliser le terme JSP pour préciser le type de contenu. Il est conseillé de toujours inclure la balise au sein du fichier de description. Dans le cas d’une balise vide il faudra utiliser empty. Si par contre, des instructions dynamiques autre que du code JSP sont utilisées, il faut insérer la définition suivante : tagdependent. Nous pouvons désormais utiliser notre balise avec un corps, avec le fichier de définition suivant : © ENI Editions - All rigths reserved - 7- 1.0 2.0 Une premiere balise pour le Webmestre webmestre taglib.SimpleTag JSP mailto true <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/WEB-INF/tld/betaboutique.tld" prefix="bb" %> BIENVENUE contact@betaboutique.com info@betaboutique.com 9. Gestionnaire de balises et gestion du contenu du corps Le principe de l’exemple précédent est très intéressant mais il nous empêche de manipuler le contenu du corps de la balise (adresse email dans notre cas). Pour manipuler le corps d’une balise, le gestionnaire de balises doit dériver de la classe BodyTagSupport. La classe BodyTagSupport dérive elle­même de la classe TagSupport et contient des méthodes permettant ce type de traitement. Il faut créer une méthode doAfterBody() pour traiter le corps d’une balise. Dans cette méthode, l’objet BodyContent permet de stocker des informations sur le corps reçu. La méthode getString() permet de récupérer le corps de la balise sous la forme d’une chaîne de caractères qui peut alors être manipulée. Enfin, la méthode getEnclosingWriter() de l’objet BodyContent permet de retourner le résultat à la page JSP. Le code suivant permet de traiter le corps passé avec la balise et de le mettre en majuscule. 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(""+bodytext.toUpperCase()+" - 8- © ENI Editions - All rigths reserved

"); } else { bodycontent.getEnclosingWriter().print("Webmestre : "+bodytext.toUpperCase()+"

"); } } 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’utilisation d’une balise personnalisée qui manipule le contenu d’un corps est identique à l’utilisation de toute autre balise contenant de l’information entre les indicateurs de début et de fin. La seule différence réside dans le fait que le gestionnaire de balises peut modifier cette information avant de la retourner à la page JSP. Si un problème surgit pendant le traitement du gestionnaire de balises, une erreur de serveur sera générée et le reste de la page JSP ne sera pas traité. Le corps de la balise peut contenir directement du texte ou être généré dynamiquement par d’autres méthodes, comme du code Java contenu dans une scriptlet ou une expression. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/WEB-INF/tld/betaboutique.tld" prefix="bb" %> BIENVENUE contact@betaboutique.com info@betaboutique.com <%= new java.util.Date() %> Cette technique très puissante est couramment utilisée pour accéder à une base de données, manipuler des images, réaliser du formatage (résumés, remplacement de caractères...) ou envoyer un email automatique. Une fois que la librairie est terminée, c’est­à­dire que l’implémentation du code est réalisée et que la grammaire .tld est déclarée, nous pouvons empaqueter la totalité de la librairie dans une archive au format .jar. Nous allons illustrer 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. urlApplication http://192.168.0.1:8080/betaboutique/ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> © ENI Editions - All rigths reserved - 9- TAGLIB <% String parametre=pageContext.getServletContext().getInitParameter ("urlApplication"); if(parametre!=null) { out.println(parametre); } %> 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ée Configuration dans le paquetage 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, nous procédons à la définition de la grammaire avec le fichier configuration.tld dans le répertoire /WEB­INF/tld. 1.0 2.0 Opérations sur les parametres config betaboutique.taglib.Configuration JSP - 10 - © ENI Editions - All rigths reserved attribut true Il ne reste maintenant plus que la dernière étape de la configuration, à savoir la déclaration de la librairie dans le fichier de gestion de l’application web.xml. urlApplication http://192.168.0.1:8080/betaboutique/ /WEB-INF/tld/configuration.tld /WEB-INF/tld/configuration.tld L’exemple précédent peut maintenant être remplacé par le code simple suivant : <%@ taglib uri="/WEB-INF/tld/configuration.tld" prefix="conf" %> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> TAGLIB © ENI Editions - All rigths reserved - 11 - Les JavaBeans ou Beans 1. Présentation Un composant JavaBean, également appelé composant logiciel, est une classe Java conçue pour être réutilisable lors des développements en Java. Un composant JavaBean définit : ● Des propriétés correspondant aux données d’un objet. ● Des événements permettant au JavaBean de communiquer avec d’autres classes. Une simple classe est un composant JavaBean si : ● Elle est en accès public. ● Elle possède un constructeur public sans paramètre (constructeur par défaut). ● Elle définit des méthodes préfixées par get et set appelées accesseurs, permettant d’interroger et de modifier des données de la classe. Les propriétés peuvent être éventuellement héritées d’une super­classe. Les composants JavaBeans permettent une séparation entre le code Java et la gestion de l’affichage des données. En renvoyant le code dans les Beans au lieu de le laisser accessible dans les scriptlets, nous allégeons le code source JSP. 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­formes que celle d’origine. Un composant JavaBean est un composant logiciel. Un composant logiciel est un bloc de programme élémentaire qui offre des accès via une interface, ce qui permet de masquer la complexité des détails du composant. Le but est de concevoir des applications complexes et volumineuses par combinaison de petits blocs. Le modèle JavaBean (JCA) propose un standard pour le développement de composants réutilisables et portables en langage Java. Les JavaBeans sont des composants fonctionnels capables de communiquer entre eux de façon standardisée. L’architecture JCA est en théorie apte à être déployée sous n’importe quel système d’exploitation et dans n’importe quel environnement applicatif. Les JavaBeans permettent de masquer leurs détails fonctionnels, cette approche est appelée sous le terme : encapsulation. Chaque objet possède une partie privée, inaccessible aux objets qui utilisent ses services et une partie publique, l’interface. Chaque objet est donc capable de travailler avec d’autres, chaque composant simple (article, utilisateur, produit...) est responsable d’une tâche bien précise qui participe à l’ensemble du projet. 2. Utilisation Techniquement, les JavaBeans permettent d’éviter que le code des pages JSP devienne trop long et difficile à manipuler. Les méthodes publiques qui conservent l’intégrité du principe d’encapsulation servent à lire et modifier les valeurs de certaines variables du JavaBean. Les méthodes qui servent à lire sont des lecteurs (accesseurs) et celles qui permettent de créer ou modifier des valeurs sont appelées des modificateurs (mutateurs). En pratique elles sont souvent appelées getter et setter. Les accesseurs et mutateurs ont des noms standardisés qui commencent par une minuscule et qui sont suivis par le nom de la propriété commençant par une majuscule. Exemple : un attribut prix possède deux méthodes, getPrix() et setPrix(param). Pour notre projet betaboutique, nous avons créé un premier JavaBean client avec les attributs associés. Par la suite, nous aurons une classe JavaBean pour les articles de la boutique, les commandes et les catégories. Le JavaBean de la classe Client possède la structure suivante : 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() { © ENI Editions - All rigths reserved - 1- return identifiant; } public void setIdentifiant(String identifiant) { this.identifiant = identifiant; } public String getMotdepasse() { return motdepasse; } public void setMotdepasse(String motdepasse) { this.motdepasse = motdepasse; } } Cette classe est bien un JavaBean car elle est sérialisable, elle possède un constructeur par défaut sans paramètre et tous ses attributs conservent l’encapsulation en proposant uniquement un accès par l’intermédiaire des méthodes. Si nous reprenons notre page d’authentification (authentification.html), la Servlet (ServletAuthentification) de vérification et la page de bienvenue (bienvenue.jsp), nous pouvons instancier un objet client en cas de succès et le lire dans la page JSP. Authentification BetaBoutique

Authentification - Client

Identifiant/Login :
Mot de passe :
... //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 ... La page de bienvenue accède au JavaBean client de façon simple et intuitive. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> BIENVENUE

Bienvenue client :

Pour rappel, la balise est obligatoire et permet par le biais de ses attributs de manipuler l’objet de portée indiquée par l’attribut scope. L’attribut id correspond au nom du JavaBean à créer ou à récupérer dans la portée (variable qui servira à manipuler l’objet), l’attribut class correspond à la classe du JavaBean et l’attribut scope correspond à la portée où le JavaBean va être créé ou lu. Si l’objet est présent dans la portée, il est lu, sinon un nouvel objet de nom indiqué par l’attribut id est créé. La portée scope peut avoir les valeurs suivantes : ● request : JavaBean valable uniquement dans la requête ; ● session : JavaBean valable tout au long de la session de l’utilisateur ; ● page : JavaBean valable uniquement pour la page en cours ; ● application : JavaBean partagé par l’ensemble des pages de l’application. Suite à cette déclaration, le moteur JSP sait que l’objet désigné est un composant JavaBean, ce qui permet d’exploiter les caractéristiques particulières à ce genre d’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 Servlets et pages JSP. Les EJB sont des composants spéciaux, fonctionnant sur serveur Java EE et utilisés pour construire la logique applicative et d’accès aux données. Les balises JavaBean les plus utilisées sont : qui permet d’assigner une valeur à une propriété (ou toutes les valeurs automatiquement avec le signe *) et qui permet de lire la valeur d’un attribut. La balise possède un attribut param qui permet de lire un attribut dans la requête avec le nom indiqué par le champ param et de l’affecter directement au JavaBean. La pseudo­valeur * force toutes les propriétés d’un composant JavaBean à prendre les valeurs qui ont été transmises au serveur dans le flux de la requête. Si cette technique est utilisée au retour d’une Servlet ou suite à une saisie dans un formulaire HTML, les noms des composants de saisie du formulaire doivent être les mêmes que les propriétés correspondantes dans le composant JavaBean. Les JavaBeans peuvent être créés dans une page JSP et être utilisés dans d’autres pages JSP de la même application par exemple, en fonction de la portée déclarée. Nous allons montrer ce principe en utilisant un lien sur la page de bienvenue bienvenue.jsp pour aller sur la page sessionjavabean.jsp et afficher les valeurs du JavaBean. ... //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é"); } © ENI Editions - All rigths reserved - 3- 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"%> BIENVENUE

Bienvenue client :

Lire le JavaBean dans la session <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client avec la session : -

- 4- © ENI Editions - All rigths reserved L’instruction permet de créer un JavaBean d’un type spécifié puis de le lier au nom fourni par l’attribut id. En fait, l’instruction se comporte ainsi seulement si aucun composant JavaBean de ce type et de ce nom n’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 existant qui sera employé. De plus, si le nom précisé par id est trouvé, l’action est réalisée avec succès, dans le cas contraire il en résulte une exception ClassCastException. Le code placé entre la balise d’ouverture et de fermeture ...
n’est exécuté que si le JavaBean n’existe pas. Si un JavaBean convient, le code est ignoré. Dans l’exemple suivant, si le JavaBean n’existe pas, il utilise d’autres valeurs pour l’identifiant et le mot de passe. Dans le cas contraire, ce code n’est pas exécuté. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> BIENVENUE

Bienvenue client :

Lire le JavaBean dans la session

<%= "Le JavaBean client2 n’est pas présent dans la requête, il a été créé et initialisé" %> 3. Librairies de gestion des JavaBeans © ENI Editions - All rigths reserved - 5- Les JavaBeans étant très utilisés dans le monde de la programmation Java, plusieurs sociétés et organismes ont développé des librairies spécifiques de leur gestion et manipulation. 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 nombreux outils compatibles Java EE comme le framework Struts. Le modèle (ou pattern en anglais) JavaBean est utilisé en standard, mais il manque parfois des méthodes aux librairies de base Java pour manipuler ces objets. Cette adresse fournit tous les composants nécessaires à l’utilisation de la librairie proposée par la communauté Apache : (http://commons.apache.org/downloads/download_beanutils.cgi). La documentation de l’API est présente à cette adresse : (http://commons.apache.org/beanutils/apidocs/org/apache/commons/beanutils/packagesummary.html#package_description) Après avoir téléchargé la librairie au format adapté, nous pouvons copier les archives .jar dans le répertoire /WEB­INF/lib de notre projet et renseigner le classpath Java. L’arborescence de notre projet est alors la suivante : Pour insérer les nouvelles librairies dans le projet/classpath, il faut utiliser le menu Project ­ Properties ­ Java Build Path ­ Add External JARs et ajouter les librairies. 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ême un 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; } } Nous allons réaliser une authentification par le biais de la Servlet adaptée qui va stocker un objet JavaBean client dans la session et nous rediriger sur la page de bienvenue. Sur cette page de retour, nous allons utiliser un lien HTML pour déclencher notre nouvelle Servlet de manipulation du JavaBean présent dans la session. ... //vérifier l’égalité des valeurs if( (identifiant!=null && identifiant.equals(ident)) && (motdepasse!=null && motdepasse.equals(mdp)) ) { if(urlBienvenue==null) © ENI Editions - All rigths reserved - 7- { 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" %> BIENVENUE

Bienvenue client :


Utiliser la librairie commonbean 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); } } La classe PropertyUtils a été utilisée car elle permet de manipuler des JavaBeans en programmation Java (donc dans une Servlet ou page JSP). Il est possible d’accéder à l’objet ainsi qu’à l’ensemble de ses propriétés. La classe PropertyUtils permet de manipuler un JavaBean existant (instancié à partir d’une classe JavaBean). La classe DynaBean permet de manipuler des JavaBeans créés à partir d’une classe ou des JavaBeans créés à la volée, sans avoir une 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 transformation d’un JavaBean vers un DynaBean. Nous allons modifier notre classe pour créer un objet DynaBean à partir d’un JavaBean et afficher ses valeurs. package betaboutique.servlets.client; import import import import import import import import import import java.io.IOException; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.http.HttpSession; org.apache.commons.beanutils.DynaBean; org.apache.commons.beanutils.WrapDynaBean; betaboutique.javabean.Adresse; 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")); © ENI Editions - All rigths reserved - 9- 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); } } La classe LazyDynaBean permet de créer directement des DynaBeans, afin de les manipuler par la suite dans nos pages et de bénéficier de la puissance de ces objets. package betaboutique.servlets.client; import import import import import import import java.io.IOException; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; org.apache.commons.beanutils.DynaBean; 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); } } Une autre classe très utile de manipulation des JavaBeans est la classe BeanUtils. Nous allons utiliser cette classe avec un formulaire HTML simple. Le formulaire nommé : article.html permet de saisir le nom de l’article, sa référence et son prix. Authentification BetaBoutique - 10 - © ENI Editions - All rigths reserved

Article

Nom :
Référence :
Prix :
Cette page va déclencher la Servlet ServletArticle qui permet de renseigner directement un JavaBean article et d’aller sur une page d’affichage de l’article (article.jsp). ... servletarticle betaboutique.servlets.client.ServletArticle ... servletarticle /article ... 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)); © ENI Editions - All rigths reserved - 11 - } 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" %> FICHE ARTICLE

ARTICLE


Nom :
Référence :
Prix :

Cette technique est très puissante. Elle permet de renseigner directement un objet à partir de données saisies. Cet objet pourra être ensuite manipulé avec toutes les librairies qui fonctionnent avec les JavaBeans et DynaBeans, comme la sérialisation d’objet, la transformation d’objets en données XML, PDF... En une seule ligne, grâce au code suivant : BeanUtils.populate(article, map); tout notre objet a été renseigné. En plus, si une propriété est envoyée et copiée dans notre JavaBean mais qu’elle n’appartient pas à notre JavaBean, cela ne gêne pas son fonctionnement. Par exemple, si nous insérons dans notre formulaire HTML un nouveau champ pour l’image de l’article mais que le JavaBean ne le possède pas, lors de la copie, cela ne provoquera pas d’erreur. L’attribut sera simplement ignoré. Nous allons prendre un dernier exemple plus complexe avec la classe Article qui possède un attribut de type collection (ensemble de valeurs) pour stocker les acteurs associés à un article/DVD. Le principe serait le même avec une propriété qui serait un objet d’une autre classe (ex : personne et l’instance de la classe Adresse). package betaboutique.javabean; import java.util.ArrayList; - 12 - © ENI Editions - All rigths reserved public class Article implements java.io.Serializable { private private private private String nomarticle=null; String refarticle=null; float prixarticle=0; ArrayList acteurs=new ArrayList(); //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 getActeurs() { return acteurs; } public void setActeurs(ArrayList acteurs) { this.acteurs = acteurs; } } Le formulaire HTML permet de saisir les articles avec des champs . Authentification BetaBoutique

Article

© ENI Editions - All rigths reserved - 13 -
Nom :
Référence :
Prix :
Acteur 1:
Acteur 2:
La Servlet est un peu plus complexe et permet de recomposer la collection des acteurs et de la stocker dans le JavaBean. La page d’affichage permet de parcourir la collection contenue dans le Bean. package betaboutique.servlets.client; import import import import import import import import import import java.io.IOException; java.util.ArrayList; java.util.Enumeration; java.util.HashMap; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; org.apache.commons.beanutils.BeanUtils; 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 acteurs=new ArrayList(); //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" %> FICHE ARTICLE

ARTICLE


Nom :
Référence :
Prix :
Acteur :
Acteur :

4. Sérialiser des JavaBeans Les JavaBeans sont des standards et peuvent donc être sérialisés. La sérialisation (sérialization en anglais) permet d’encoder un objet (et son état) sous la forme d’une suite d’octets. Cette suite d’octets peut ensuite être utilisée pour sauvegarder l’objet (persistance) sur le disque, dans une base de données ou permettre son transport sur le réseau. Il existe ensuite un processus symétrique qui permet de décoder la suite d’octets pour créer une copie conforme de l’objet d’origine. Cette activité est appelée désérialisation. Dans de nombreux cas, il est nécessaire de sauvegarder l’état complet d’un objet. La solution qui vient immédiatement à l’esprit est de sauvegarder sous forme de chaînes de caractères chacun des attributs. Cette solution, s’avère rapidement très complexe pour les attributs de type tableau, collection ou même objet. La sauvegarde d’éléments de type int ou String par exemple ne pose pas de problème, mais par contre, la sauvegarde sous forme de chaînes de caractères d’éléments issus d’autres classes est complexe. De plus, la lecture par la suite de l’objet sérialisé en vue de la sauvegarde de l’objet est © ENI Editions - All rigths reserved - 15 - très complexe à gérer. La solution offerte par Java est la sérialisation d’objets. Ensuite, il sera possible de lire l’état complet de cet objet depuis un fichier binaire et de ré­instancier un objet dont l’état sera le même que celui de l’objet précédemment sauvegardé. Pour rendre une classe sérialisable, c’est­à­dire pour que les objets de cette classe soient transformables en flux d’octets, il suffit de réaliser une implémentation de l’interface Serializable lors de la définition de cette classe. L’interface Serializable ne comprend aucune méthode, elle ne sert qu’à déclarer la capacité de sérialisation des objets issus de la classe. Un objet est sérialisable à condition que tous ses composants soient eux­mêmes sérialisables. Les méta­éléments ainsi que les chaînes de caractères sont des éléments sérialisables. Par contre, les éléments qui sont déjà des flux d’octets ne sont pas sérialisables (images, threads, descripteurs de flux...). Si une classe sérialisable possède un attribut non sérialisable (une image par exemple), il faut alors déclarer cet attribut comme transient. La classe sera alors opérationnelle mais cet attribut ne sera pas pris en compte lors de la sérialisation. a. Sérialiser un objet Nous avons des classes sérialisables (JavaBean) dans notre projet, nous pouvons donc exploiter des flux d’objets sérialisés. Nous allons donc modifier notre Servlet de gestion d’articles afin d’enregistrer notre objet article dans un fichier binaire. La classe ObjectOutputStream permet la sérialisation d’objets vers un flux. L’écriture d’un objet est réalisée avec la méthode writeObject(...) et la purge du buffer avec la méthode flush(). La Servlet ServletArticle permet de stocker sous la forme d’un flux binaire l’objet article dans un fichier nommé article.dat présent à la racine du disque C:\. Le nom du fichier binaire ainsi que son extension n’ont pas d’importance. Après exécution de la Servlet, nous pouvons observer la création d’un fichier C:\article.dat avec un flux d’octets. package betaboutique.servlets.client; import import import import import import import import import import import import java.io.FileOutputStream; java.io.IOException; java.io.ObjectOutputStream; java.util.ArrayList; java.util.Enumeration; java.util.HashMap; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; org.apache.commons.beanutils.BeanUtils; 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 acteurs=new ArrayList(); //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érialiser un objet La classe ObjectInputStream permet de lire un flux sérialisé en entrée et donc de désérialiser un objet depuis un flux. La désérialisation permet de lire un objet (ou plusieurs) et de créer de nouvelles instances de cet objet à partir de l’état de celui sauvegardé. La méthode readObject() retourne une référence de type Object. Il y aura donc juste à réaliser un transtypage (cast) de l’objet pour l’utiliser. Nous allons créer une page JSP nommée deserialisationarticle.jsp qui permet de lire le JavaBean et de l’afficher en direct. Cette page sera appelée directement et sera fonctionnelle tant que l’objet sérialisé article.dat sera présent sur le disque de la machine. <%@ 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" %> FICHE ARTICLE <% //lecture de l’objet sérialisé ObjectInputStream f = new ObjectInputStream(new FileInputStream("C:\\article.dat")); © ENI Editions - All rigths reserved - 17 - Article articleserialise = (Article) f.readObject(); %>

ARTICLE


Nom :
Référence :
Prix :
Acteur :
Acteur :

Cette technique très intéressante permet des manipulations d’objets. Nous retrouvons bien notre objet d’origine avec ses attributs simples (chaînes de caractères) et complexes (collection). Cette méthode est un moyen efficace de réaliser de la persistance d’objet, mais du fait de sa lenteur lors de nombreux accès, ne permet pas de gérer la persistance complète d’une application. c. Sérialisation et désérialisation en XML La sérialisation précédente sous forme de flux d’octets est intéressante mais empêche la lecture du fichier avec d’autres langages que Java. Il sera donc obligatoire de désérialiser chaque objet pour pouvoir le relire. Pour répondre à ce problème, Java propose des classes qui permettent de réaliser la sérialisation et la désérialisation à partir de données XML. Nous allons reprendre notre exemple précédent (Servlet et page JSP) pour réaliser la même étape avec un flux XML. ... 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 est désormais sérialisé sous la forme d’un fichier XML nommé article.xml qui possède la structure suivante : - 18 - © ENI Editions - All rigths reserved Du fait de cette sérialisation avec le standard XML, il sera possible d’exploiter l’objet avec n’importe quelle technologie compatible comme par exemple PHP, ASP, Flash ou JavaScript. La page JSP suivante permet alors d’afficher l’objet sérialisé à partir du fichier XML. <%@ 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" %> FICHE ARTICLE <% //lecture de l’objet sérialisé XMLDecoder e = new XMLDecoder(new BufferedInputStream(new FileInputStream("C:\\article.xml"))); Article articleserialise = (Article) e.readObject(); %>

ARTICLE


Nom :
Référence :
Prix :
© ENI Editions - All rigths reserved - 19 - Acteur :
Acteur :

La technique précédente bien que très facile d’utilisation nécessite le passage par un fichier ’’temporaire’’. De plus, cette API est très lente avec de nombreuses connexions et n’est donc pas envisageable pour la persistance massive de données dans un environnement en production. L’API XStream (http://xstream.codehaus.org/) simple d’utilisation, permet de sérialiser des objets Java, mais surtout, elle est très rapide et ne consomme pas beaucoup de mémoire. L’API XStream est disponible sous la forme d’une librairie xstream­1.2.2.jar que nous devons inclure à notre projet (/WEB­INF/lib et classpath). Un des avantages de cette librairie est qu’elle permet de travailler avec des objets sérialisables, c’est­à­dire qui implémentent l’interface Serializable mais également avec des objets non sérialisables. Nous allons modifier notre Servlet ServletArticle afin d’afficher l’article dans la console Java sous la forme d’un flux XML. ... 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"); } } ... Le résultat de cette exécution produit le contenu XML ci­dessous. Nous remarquons deux éléments très important : la librairie a traité elle­même les caractères spéciaux en entité XML (les apostrophes du titre) et la structure XML est la même que celle de la classe. Les balises ont les mêmes noms et sont facilement manipulables. - 20 - © ENI Editions - All rigths reserved Nous pouvons reprendre notre exemple précédent et sérialiser l’objet article dans un fichier et le lire en le désérialisant avec la page deserialiserarticle.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" %> FICHE ARTICLE <% //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) { } %>

ARTICLE


Nom :
Référence :
Prix :
Acteur :
Acteur :

Nous allons modifier notre application pour mettre en œ uvre un modèle à plusieurs étapes et facilement maintenable. Un formulaire de saisie permet d’insérer un nouvel article. Cette saisie est transformée en JavaBean. L’objet JavaBean est retourné à la page JSP qui permet de transformer simplement le JavaBean en flux XML. Ce principe pourrait être également envisagé pour une lecture d’informations dans une base de données. La page JSP affiche alors les données au format XML. ... 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"); } ... <%@ 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)); } %> La saisie du formulaire pour un article entraîne automatiquement une réponse au format XML. Cette technique peut être particulièrement utilisée pour afficher des listings d’articles, le panier en cours... Il faut remarquer que la page JSP commence par le prologue XML et que le contenu de la page (attribut contentType) est en XML. Désormais cette page peut être parsée avec le langage Flash ou JavaScript. Nous allons afficher notre page avec une feuille XSLT pour présenter nos résultats dans un tableau. Voici le contenu de la nouvelle page JSP qui permet de stocker le flux XML dans une variable et de le transformer en HTML pour l’affichage par l’intermédiaire de la feuille XSLT articlexsl.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); } %> <%= buffer %> Cette première partie de code permet de stocker le flux XML dans une variable et de l’afficher. Dans une application professionnelle, nous pourrions utiliser un paramètre pour savoir si nous voulons en retour la transformation ou le contenu XML d’origine. Cette étape serait également nécessaire pour le développement de la feuille XSLT afin de bien analyser la structure générée par XStream pour les données (nom de la racine, nom des nœ uds, nom des attributs...). Le résultat présenté dans le navigateur est le suivant : Maintenant, nous allons déclencher la transformation de ce flux XML en HTML grâce à notre feuille de style XSLT. BETABOUTIQUE

FICHE ARTICLE

Titre :
Référence :
Prix :
Acteur(s) : tr> xsl:for-each select="/betaboutique.javabean.Article/ acteurs/string"> #eaeaea
Nom
© ENI Editions - All rigths reserved - 23 -
La page JSP de transformation du contenu XML en contenu HTML est la suivante : <%@ 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); } %> <%= buffer %> Les données sont correctement affichées avec les bonnes entités HTML, la structure conservée et les éléments composés (comme les acteurs) sont également bien traités. Grâce à cette technique, nous avons totalement séparé la partie Données de la partie Mise en page. Ce type de programmation permet une meilleure maintenance et surtout de manipuler les données avec différents langages selon les besoins du commanditaire (Java, PHP, Flash ou JavaScript). Pour résumer, la technologie JavaBean est un standard de programmation, elle permet donc d’utiliser des librairies très puissantes de manipulation comme la sérialisation, la transformation d’objets en XML, les transformations en PDF, en VRML... Cette technologie permet de générer des pages 100% compatibles XML afin de les parser avec une (ou plusieurs) feuille(s) XSLT, du code JavaScript ou une application graphique Java évoluée. - 24 - © ENI Editions - All rigths reserved Transfert de contrôle 1. Présentation Il est très fréquent lors de développements d’applications, d’échanger des informations entre une Servlet et une page JSP puis de transférer le contrôle de la JSP vers une Servlet. Nous allons introduire un exemple de chaque contrôle afin de réaliser des redirections, des appels de pages... Cette technique a été présentée tout au long des exemples, mais il est important de bien comprendre son fonctionnement. Nous allons utiliser l’exemple de l’authentification avec la Servlet ServletAuthentification pour présenter les différents types de redirections. 2. Utilisation a. Transfert du contrôle d’une Servlet à une page JSP Dans un premier temps, nous réalisons une simple redirection entre une page HTML ou JSP et une Servlet. L’utilisateur réalise une authentification (authentification.html), la Servlet (ServletAuthentification) vérifie l’intégrité des données et appelle une page de bienvenue (bienvenue.jsp). Authentification BetaBoutique

Authentification - Client

Identifiant/Login :
Mot de passe :
... //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 { © ENI Editions - All rigths reserved - 1- //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"%> BIENVENUE

Bienvenue client :

Dans cet exemple, la page bienvenue.jsp ne peut pas afficher le JavaBean client. En effet, l’action response.sendRedirect(’’bienvenue.jsp’’) réalise une redirection forcée et perd les données présentes dans la requête. Cette technique permet de vider, la totalité de la requête et donc d’éviter par exemple de réaliser plusieurs insertions d’un même article dans un panier en cas de rafraîchissement de la page par l’utilisateur. Avec cette méthode, il est quand même possible de forcer le passage de paramètre uniquement en GET avec les appels HTTP. Voici un second exemple avec le passage d’un message dans la redirection forcé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" %> - 2- © ENI Editions - All rigths reserved BIENVENUE

Bienvenue client :

<% out.println("Message : "+request.getParameter("message")) ; %> Pour réaliser des transferts de contrôle entre une Servlet et une page JSP sans perdre les données, nous devons utiliser le RequestDispatcher. Cette classe permet de positionner des attributs dans la requête (session, context...) et de les lire dans une page JSP appelée. La Servlet précédente est modifiée avec l’utilisation de la classe RequestDispatcher. ... //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); } } ... Il est possible d’améliorer l’exemple précédent en passant le paramètre message dans la requête et non dans le nom de la page. Par contre, dans ce cas, il faut modifier la page bienvenue.jsp afin d’utiliser la mé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"); © ENI Editions - All rigths reserved - 3- //redirection simple getServletContext().getRequestDispatcher ("/bienvenue.jsp").forward(request, response); } } ... b. Transfert du contrôle d’une page JSP à une Servlet Lors du développement d’une application, il est très fréquent qu’une page JSP transfère le contrôle à une Servlet avec des informations associées. Pour réaliser cette opération, il faut utiliser la directive . Cette directive permet également de passer des paramètres à la Servlet avec la directive . Par exemple, dans une page JSP qui permet de faire un calcul ou de charger un fichier, après le bloc de traitement, l’utilisateur est automatiquement redirigé vers une Servlet d’insertion dans une base de données avec le code suivant : <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> Bien que cette technique soit très simple d’utilisation, elle n’est pas très souvent utilisée étant donné que dans la plupart des cas c’est l’utilisateur lui­même qui déclenche, par l’intermédiaire d’un lien ou d’un formulaire HTML, la Servlet appelée par la page JSP. c. Transfert du contrôle d’une page JSP à une autre page JSP Une page JSP peut donner le contrôle à une autre page JSP en utilisant deux méthodes : ● En utilisant la directive de la première page vers la seconde. ● En utilisant le bouton d’un formulaire avec les données associées, ou un lien avec paramètres. Pour illustrer ces techniques, appeler directement la page paramètre en POST et un lien différents accès en fonction de en GET). nous allons de nouveau utiliser le formulaire d’authentification qui cette fois­ci va JSP bienvenueformulaire.jsp. Nous utilisons donc un formulaire avec passage de avec passage de paramètre en GET. Nous donnons en même temps, l’utilisation des la méthode HTTP utilisée (request.getAttribute(...) en POST et request.getParameter(...) Authentification BetaBoutique

Authentification - Client

- 4- © ENI Editions - All rigths reserved
Identifiant/Login :
Mot de passe :

Passage du controle à une autre page JSP avec paramètres
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="betaboutique.javabean.Client" %> BIENVENUE

Bienvenue client :

<% out.println("Identifiant : "+request.getParameter("identifiant")+"
"); out.println("Mot de passe : "+request.getParameter("motdepasse")+"
"); %> © ENI Editions - All rigths reserved - 5- Travailler avec des fichiers et répertoires 1. Présentation L’API Java est extrêmement complète en ce qui concerne la manipulation de répertoires et fichiers. Les pages JSP 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... Nous allons étudier les différentes techniques, classes et méthodes proposées par Java pour gérer les fichiers et répertoires. 2. Travailler avec les répertoires Nous allons créer une page JSP nommée repertoire.jsp avec un ensemble de commandes pour gérer les répertoires en Java. Il est nécessaire de vérifier l’existence d’un répertoire avant toute opération le concernant. Pour vérifier l’existence d’un répertoire, nous utilisons un objet issu de la classe File située dans le paquetage 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" %> REPERTOIRE <% //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
"); } else { out.println("Le fichier "+nomFichier+" n’existe pas sur le système
");} //gestion des droits if(file.canRead()) { out.println("Le fichier "+nomFichier+" est accessible en lecture
"); } if(file.canWrite()) { out.println("Le fichier "+nomFichier+" est accessible en écriture
"); } //propriétés out.println("Le chemin absolu est : "+file.getAbsoluteFile()+"
"); out.println("Le chemin absolu est : "+file.getAbsolutePath()+"
"); out.println("Le nom du fichier est : "+file.getName()+"
"); out.println("Sa date de dernière modification est : "+file.lastModified()+"
"); out.println("Sa taille est : "+file.length()+" octets
"); %> © ENI Editions - All rigths reserved - 1- 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 sous Linux. Voici un deuxième exemple qui permet d’afficher l’arborescence des racines de fichiers et le parcours d’un répertoire. <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="java.io.File" %> REPERTOIRE <% //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("
    "); for(int i=0;i"+listing[i]+""); } out.println("
"); //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("
    "); for(int i=0;i"+listeRacine[i]+""); } out.println("
"); %> 3. Travailler avec les fichiers Nous allons créer une page JSP nommée fichier.jsp avec un ensemble de commandes pour gérer les fichiers en Java. Les opérations de base seront utilisées, avec l’écriture de données et la lecture d’informations. La première étape consiste à créer un objet de type File, qui servira à spécifier le chemin et le nom du fichier que nous souhaitons lire. Ensuite, un objet de type FileReader utilisant l’objet de type File, doit être créé. La lecture des informations en provenance d’un fichier sera meilleure si les informations sont stockées au fur et à mesure dans un tampon. La méthode readLine() de l’objet BufferedReader permet de lire une ligne dans un fichier avec le caractère de rupture indiquant une nouvelle ligne. <%@ 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" %> FICHIER <% //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+"
"); } %> L’API Java possède de très nombreuses classes et méthodes pour la manipulation des fichiers, avec toujours le souci de respecter la portabilité. Il sera facile par exemple de connaître la liste des racines de l’arborescence, parcourir des répertoires, supprimer un fichier ou répertoire, renommer ou déplacer un fichier, créer un fichier temporaire... 4. En résumé Ce chapitre a présenté le mécanisme de JSP en Java. La première partie a introduit le principe des JSP et le cycle de vie d’une page d’exemple. Dans un second temps, les déclarations, commentaires et scriptlets ont été expliqués à partir des pages du projet BetaBoutique. Le chapitre suivant a présenté les objets implicites JSP qui sont utilisables directement sans instanciation ni importation. La partie associée a permis de mettre en œ uvre une première JSP simple et de présenter la notion de fragment JSPF. Ensuite, la gestion des exceptions et des erreurs en JSP a été largement détaillée dans un chapitre dédié. Le chapitre suivant a présenté la notion de bibliothèques de tags ou taglibs Java. Ces bibliothèques permettent de réaliser des opérations de programmation, de manipuler des objets simples, de gérer des données XML, de manipuler les langues... Ces bibliothèques très utiles, sont parfois limitées et nécessitent donc le développement de balises personnalisées, comme cela a été expliqué au chapitre suivant. Ces balises personnalisées sont basées sur des classes Java et des fichiers de descriptions au format XML. Les JavaBeans ont été ensuite présentés avec des exemples du projet BetaBoutique. Des manipulations simples ont été d’abord réalisées et ont été agrémentées de manipulations plus complexes à partir de flux d’octets et XML. Les techniques de sérialisation et désérialisation sous formes de données brutes ou XML ont aussi été détaillées à partir d’exemples du projet. L’avant­dernier chapitre a permis de lister et détailler le transfert de contrôle en Java EE, qui est un élément important de la programmation MVC. Enfin, le dernier chapitre a été une introduction à la manipulation de fichiers et répertoires en JSP/Java. © ENI Editions - All rigths reserved - 3- Qu’est­ce qu’une Servlet ? 1. Présentation Les Servlets sont la base de la programmation Java EE. La conception d’un site Web dynamique en Java repose sur ces éléments. Une Servlet est un composant Web conçu à partir d’une classe Java qui est déployée au sein d’une application. Les Servlets sont chronologiquement le deuxième élément de Java EE après JDBC. Une Servlet interagit avec un client Web par l’intermédiaire du protocole HTTP via le mécanisme de requête/réponse. Bien que la totalité du traitement puisse être effectué dans la Servlet, celle­ci fait souvent appel à des classes utilitaires pour la logique métier. Elles apportent un contenu dynamique en réponse à des requêtes client. En général, une Servlet n’est pas appelée directement, elle ne l’est qu’à travers une URL. Par exemple, la Servlet ServletMonApplication.java (compilée en ServletMonApplication.class) est appelée lorsque la page monapplication.htm est invoquée. La page monapplication.htm n’existe pas sur le serveur elle ne sert qu’à faire le lien avec la Servlet. Les Servlets ont de nombreux avantages : ● Elles sont portables et évolutives. ● Elles sont performantes et rapides car chargées en mémoire dès le premier appel. ● Elles sont disponibles car elles s’exécutent dans un contexte multitâche (une seule instance créée). Bien que les Servlets aient été conçues pour travailler avec tous les types de serveurs (HTTP, FTP, SMTP...) elles ne sont employées en pratique qu’avec des serveurs Web HTTP. L’API Servlet contient pour cela une classe nommée HttpServlet afin de gérer le protocole HTTP et les méthodes GET et POST . 2. Requêtes HTTP Pour comprendre le fonctionnement de l’exécution de Servlets, il est nécessaire d’expliquer au préalable le protocole HTTP. Sous Windows ou Linux, un client Telnet peut être lancé avec la commande suivante : Ensuite, une commande HTTP peut être lancée pour récupérer le contenu de la page d’accueil. GET /index.html HTTP/1.0 La réponse est alors la suivante : © ENI Editions - All rigths reserved - 1- La ligne de commande Telnet comporte le nom du programme Telnet suivi du nom de l’ordinateur hôte et le port. La requête est composée de son type (GET) suivi de l’URI relative à la ressource concernée puis de l’identificateur HTTP et du numéro de version supporté par le programme Telnet. Une première pression sur la touche [Entrée] termine la requête et une seconde indique au serveur que l’en­tête est terminé. La première ligne de la réponse correspond à la ligne d’état. Elle indique la version HTTP utilisée, le code de la réponse et le message. Ensuite viennent les en­têtes de réponse : chemin, utilisation du cache, le cookie utilisé pour la session, le type de contenu, la longueur de la réponse, la date... Lorsqu’un logiciel/programme Web est utilisé comme un navigateur, c’est le navigateur lui­même qui génère ces requêtes quand des pages Internet sont demandées. L’utilisation du protocole HTTP est alors transparente pour le client. Dans notre exemple, nous remarquons que la page : http://www.google.fr/index.html n’existe pas et que le serveur renvoie une page d’erreur adaptée en conséquence. Lorsqu’une requête est envoyée en utilisant la méthode POST, elle peut comporter un corps que le client va transmettre à la ressource demandée. Le plus souvent les requêtes POST sont envoyées par des formulaires affichés dans les navigateurs Web. La méthode GET peut envoyer des données dans l’URL alors que la méthode POST utilise le corps de la requête pour placer les données. Voici un exemple de méthode POST avec deux paramètres (identifiant et mot de passe) : POST /servlet/personnes HTTP/1.0 Content-type: application/x-www-form-urlencoding Content-length : 39 identifiant=monidentifiant&motdepasse=monmotdepasse a. Comment le serveur va répondre aux requêtes des clients ? Avec une requête et la méthode GET, le serveur localise la ressource correspondante à l’URI indiquée et retourne cette ressource dans le corps du message HTTP envoyé au navigateur client. Le navigateur du client analyse celui­ci et affiche la page. La ressource demandée peut être un programme. Dans ce cas, le serveur doit décoder l’URI et appeler le programme spécifique. Le programme peut être écrit en C, Perl... Ce type de programme appelé CGI est exécuté dans un processus différent de celui du serveur et nécessite la création d’un nouveau programme en cours d’exécution (thread/processus) à chaque appel. Les Servlets Java offrent de nombreux avantages sur ces programmes CGI. Elles peuvent être exécutées dans le même processus que le serveur ce qui économise énormément de ressources, elles sont donc beaucoup plus rapides et sont portables. En effet, les programmes écrits en C doivent être recompilés pour chaque système d’exploitation utilisé. - 2- © ENI Editions - All rigths reserved Le projet BetaBoutique 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 application les parties théoriques et les exemples. Le développement d’une boutique permet : ● de gérer des utilisateurs/personnes pour les clients, les administrateurs (création, modification, suppression, authentification) ; ● de gérer des articles/produits (création, modification, suppression, tri) ; ● de gérer un panier dynamique (création, modification, suppression, session, listes...) ; ● de gérer des commandes et réservations (transactions, mails, tri...) ; ● de gérer des langues (articles en plusieurs langues) ; ● de manipuler le stock d’articles/produits avec un client lourd (application JWS). Dans un premier temps, le diagramme des cas d’utilisation de la boutique sera présenté et nous utiliserons ensuite le service Personne/Clients pour développer les premières Servlets. L’entreprise fictive BetaBoutique vend des articles en rapport avec le cinéma, principalement des DVD. Elle exerce son métier avec des fiches papier, des commandes par fax et par chèque bancaire envoyé par courrier postal puis envoie la commande au client. Dès que le chèque est encaissé, elle utilise les services de La Poste pour expédier les colis. La société BetaBoutique n’arrive plus à gérer manuellement son expansion et souhaite utiliser un système d’Information pour lui permettre de répondre à cette croissance. Elle attend plusieurs services du Système d’Information comme la vente en ligne, la gestion du catalogue d’articles (essentiellement des DVD) et la base de données des clients. 2. Expression des besoins Pour modéliser l’expression des besoins de la société BetaBoutique, nous allons utiliser UML (Unified Modeling Language). Dans un premier temps, le diagramme des cas d’utilisation qui permet de représenter les fonctionnalités du système du point de vue utilisateur sera présenté. Le diagramme des cas d’utilisation se compose : ● d’acteurs (entités externes humaines ou robot/matériel qui utilisent le système) ; ● de cas d’utilisation (fonctionnalités proposées par le système). Les acteurs utilisant le système sont : ● Employé : les employés mettent à jour le catalogue d’articles et vérifient les commandes et la liste des clients. ● Internaute : les personnes visitant le site peuvent consulter le catalogue. ● Client : les clients peuvent visualiser le catalogue, gérer leurs coordonnées et acheter des articles en ligne. Il est également possible de mentionner comme systèmes externes La Poste qui permet de gérer les livraisons et le système bancaire pour l’encaissement des achats par carte. Dans un diagramme des cas d’utilisation, le rectangle qui englobe les cas représente le système étudié. Les acteurs sont représentés par une icône appelée stick man et les cas d’utilisation sont représentés par une forme ovale. Les cas d’utilisation concernent les besoins des utilisateurs. Les cas d’utilisation sont donc très souvent réalisés sur la base d’interviews et d’entretiens avec le personnel. © ENI Editions - All rigths reserved - 1- Les acteurs "La Poste" et "Banque" représentent respectivement le service de livraison par la poste et le paiement en ligne, associés au bon de commande (ex : n° de colissimo inséré). Le diagramme des cas d’utilisation peut être lu comme ceci : Un employé peut gérer les articles du catalogue, administrer les clients, visualiser les commandes. Un internaute peut créer un compte et consulter le catalogue. Un client peut s’authentifier pour accéder à son compte. Il peut gérer son compte et acheter des articles après avoir complété son panier pour créer un bon de commande. Les cas présentés dans le diagramme peuvent être accompagnés d’un texte explicatif avec le nom du cas d’utilisation, un résumé du service, les acteurs impliqués... 3. Maquettes de la plate­forme Les maquettes d’écran facilitent la compréhension des cas d’utilisation. Les utilisateurs se repèrent facilement grâce à ces schémas visuels et peuvent ainsi valider les choix d’analyse. Les Internautes et les clients visualisent le contenu du catalogue à partir d’un navigateur. Sur la colonne de gauche qui représente le menu, sont affichées les catégories d’articles vendus par la société BetaBoutique. En cliquant sur une catégorie, le visiteur est redirigé vers une page qui affiche tous les produits de la catégorie sélectionnée. a. Découpage utilisé La boutique BetaBoutique sera découpée selon le modèle suivant avec un en­tête, un menu de navigation et un pied de page. Les fichiers .jspf sont des pages JSP qui sont utilisées pour définir des fragments de pages et qui sont incluses dans d’autres pages (comme pour les menus, en­têtes...). Les fichiers .jsp sont utilisés pour des fichiers sources ’’complets’’ et les fichiers .jspf sont utilisés pour des fragments ou segments de fichiers sources (menu, formulaire de recherche...). - 2- © ENI Editions - All rigths reserved b. Catalogue des articles La page de catalogue des articles, permet d’afficher la liste paginée des produits par catégorie. Un résultat paginé est un découpage des réponses par page (exemple 5 résultats par page et 3 pages pour afficher 15 articles). © ENI Editions - All rigths reserved - 3- c. Fiche article La fiche article permet d’afficher le détail du produit avec le nom, la note, la description, le prix et un bouton pour ajouter cet article au panier. d. Rechercher un article La zone de saisie présente en haut à droite du site permet de rechercher un article à partir de son nom ou de sa description. Les visiteurs peuvent ainsi rechercher des articles à partir d’une chaîne de caractères. La recherche ne tient pas compte de la casse (Majuscules/Minuscules). Si aucun article ne correspond à la recherche, un message informatif est affiché en conséquence. - 4- © ENI Editions - All rigths reserved e. Authentification Cette page permet au client de se connecter et se déconnecter du système. Le client doit avoir créé un compte auparavant. Il saisit alors son identifiant et son mot de passe. Si l’authentification est correcte, l’utilisateur est redirigé sur la page d’accueil et un message d’invitation est alors affiché sur la totalité des pages du site jusqu’à la prochaine déconnexion. Ce message permet d’afficher l’identifiant de l’utilisateur actuellement connecté. Lorsque le client se déconnecte, il redevient Internaute/visiteur simple. © ENI Editions - All rigths reserved - 5- f. Créer un compte Cette page permet au visiteur de se créer un compte dans le système et de devenir ainsi un client. Pour créer un compte, l’Internaute doit saisir un identifiant et un mot de passe avec une confirmation. L’identifiant doit être unique dans le système. Si ce n’est pas le cas, l’Internaute doit être averti et doit recommencer sa saisie avec un autre identifiant. Les deux mots de passe (mot de passe et sa confirmation) doivent être identiques. Si les informations sont correctes, le visiteur est alors invité à compléter ses informations personnelles (nom, prénom, email, adresse...). g. Gérer le compte client Chaque client peut consulter et mettre à jour ses informations personnelles au sein du système. Pour cela, le client doit se connecter au système et cliquer sur le lien MON COMPTE (après authentification). La page affiche en consultation ses données. - 6- © ENI Editions - All rigths reserved Le client peut passer en mode édition en cliquant sur Modifier. h. Acheter des articles Les clients (visiteurs authentifiés) peuvent acheter des articles dans le système. Le client visualise le catalogue par catégorie d’articles ou réalise une recherche. Lorsqu’il est intéressé par un article, il clique sur le lien adapté pour l’ajouter à son panier. Le client a ensuite la possibilité de modifier la quantité désirée pour chaque article ou supprimer un ou plusieurs articles. Le client peut visualiser à tout moment le contenu de son panier pendant toute la © ENI Editions - All rigths reserved - 7- durée de sa session. Lorsque le caddie est vide, un message informatif est affiché. Le panier affiche la liste des articles avec le nom, la photo, la quantité, le prix unitaire et le sous­total (prix*quantité). Le montant total du panier est également affiché en bas du panier. Lorsque le client est satisfait et qu’il souhaite valider sa commande, il peut saisir les informations de sa carte bancaire ainsi que son adresse de livraison. Une fois toutes les données validées, un bon de commande est créé et le panier électronique est automatiquement vidé. i. Gérer les commandes Les employés de la société BetaBoutique peuvent visualiser et supprimer les commandes dans le système. Pour chaque commande, les employés peuvent visualiser le contenu et les articles associés. j. Gérer les articles Les employés de la société BetaBoutique peuvent visualiser et supprimer les articles dans le système. - 8- © ENI Editions - All rigths reserved © ENI Editions - All rigths reserved - 9- Première Servlet 1. Cycle de vie d’une Servlet Chaque Servlet déployée sur un serveur d’applications possède un cycle de vie bien précis. ● La classe Java représentant la Servlet est déployée au sein du conteneur Web. ● Le conteneur Web va ensuite créer une instance de la classe de la Servlet et la charger en mé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’initialisation définis dans le descripteur de déploiement web.xml de l’application Web. ● La Servlet passe ensuite dans un état en service ou de manière plus précise, en attente de réception de requêtes clients. Le conteneur gère ensuite une file d’attente de Threads. Chaque Thread invoque la méthode service(...) de la Servlet qui exécutera la méthode HTTP adaptée (doPost(...), doGet(...)...). ● L’instance de la Servlet reste chargée en mémoire tant que le conteneur Web s’exécute. Lors de l’arrêt du conteneur Web, la méthode destroy() est invoquée sur l’instance de la Servlet pour indiquer qu’elle n’est plus dans l’état en service. ● Une Servlet est un objet Java. Une instance non utilisée est donc supprimée de la mémoire par le Garbage Collector Java (déclenchement automatique de la méthode finalize()). 2. Fonctionnement d’une Servlet (la classe HttpServlet) Lorsqu’un client (généralement par l’intermédiaire d’un navigateur mais pas nécessairement) envoie une requête HTTP 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 traitements sur la requête émise par le client et sur la réponse qui sera renvoyée. Un objet de type javax.servlet.ServletRequest fournit des méthodes permettant de récupérer ou d’ajouter des données dans la requête ainsi que des méthodes pour les métadonnées de la requête (infos clients, taille, type de contenu, protocole...). Un objet de type javax.servlet.ServletResponse fournit des méthodes pour insérer des données dans la réponse renvoyée au client. L’interface javax.servlet.http.HttpServletRequest hérite de l’interface © ENI Editions - All rigths reserved javax.servlet.ServletRequest et l’interface - 1- javax.servlet.http.HttpServletResponse hérite de l’interface javax.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émenter les traitements au sein de la méthode doXXX(...). Lorsque le conteneur Web reçoit une requête, il analyse l’URI, les en­têtes, le corps et stocke toutes les données dans un objet implémentant l’interface javax.servlet.ServletRequest. Il crée ensuite une instance d’un objet implémentant l’interface javax.servlet.ServletResponse. Cet objet encapsule la réponse qui sera envoyée au client. Le conteneur appelle ensuite une méthode de la classe de la Servlet, en lui passant les objets pour la requête et pour la réponse. La Servlet traite alors la requête et renvoie la réponse au client par l’intermédiaire du serveur. a. La méthode service() L’interface Servlet définit un nombre limité de méthodes. Les méthodes init() et destroy() ne gèrent pas les requêtes. La seule méthode concernée par cette tâche est service(). Les HttpServlet sont conçues pour répondre aux requêtes HTTP. Elles doivent donc traiter des requêtes GET, POST, HEAD... La méthode doGet(...) traite les requêtes de type GET et la méthode doPost(...) les requêtes de type POST. Il existe autant de méthodes doXXX(...) qu’il existe de type de requêtes HTTP. Le rôle du programmeur est donc d’écrire une implémentation de la classe HttpServlet en redéfinissant les méthodes dont il a besoin. Dans la majorité des cas il s’agira des méthodes doGet(...) et doPost(...). Pour résumer, une Servlet étend la classe HttpServlet et redéfinit la méthode service() pour traiter les requêtes HTTP. Toutefois, la classe HttpServlet implémente déjà la méthode service(). Il est donc préférable de ne pas le faire et de redéfinir (définir) uniquement doPost(...) et doGet(...). Pour rappel, lorsque le conteneur de Servlets reçoit des requêtes HTTP, il associe chaque requête à une Servlet. Il appelle ensuite automatiquement la méthode service() de celle­ci. La méthode service() détermine le type de requête HTTP et appelle la méthode doXXX(...) adaptée. Si par contre, la Servlet redéfinit la méthode service(), c’est cette méthode qui sera exécutée. Il peut parfois être utile de redéfinir la méthode service() de la classe HttpServlet dans nos Servlets si nous voulons effectuer un même traitement pour des requêtes émises par différentes méthodes HTTP (GET, POST...). b. La méthode init() L’interface javax.servlet.Servlet fournit des méthodes qui correspondent au cycle de vie de la Servlet : initialisation, en service, destruction. Lorsque la Servlet est chargée en mémoire suite à une instanciation par le conteneur Web, la méthode init(...) de la Servlet est exécutée. Par défaut, cette méthode ne fait rien du tout. Il est cependant conseillé de redéfinir cette méthode dans le code des Servlets pour charger des ressources utilisées dans le reste de la Servlet (connexion JDBC, ouverture d’un fichier...), ou bien pour récupérer des paramètres d’initialisation renseignés dans le descripteur de déploiement (fichier web.xml de l’application) par l’intermédiaire de l’objet ServletConfig passé en paramètre de la méthode. c. La méthode destroy() Lors de l’arrêt du conteneur Web, les instances de Servlets chargées en mémoire sont détruites (par le Garbage Collector) mais avant la réalisation de cette étape, le conteneur exécute la méthode destroy() de chaque Servlet chargée. Par défaut, cette méthode ne fait rien du tout. Il est cependant conseillé de redéfinir dans le code de Servlets cette méthode pour fermer proprement les ressources précédemment ouvertes (connexions JDBC, fichiers...). 3. Invocation d’une Servlet La manière la plus naturelle de communiquer avec une Servlet est d’utiliser un client Web et le protocole HTTP. La méthode doGet(...) d’une Servlet est invoquée principalement lorsque son URL est saisie directement dans la barre d’adresse du navigateur ou lorsqu’il y a un clic sur un lien hypertexte qui pointe sur l’URL de la Servlet. - 2- © ENI Editions - All rigths reserved Chaque méthode doGet(...) et doPost(...) prend deux paramètres. L’objet HttpServletRequest encapsule la requête envoyée au serveur et contient toutes les données de la requête. L’objet HttpServletResponse encapsule la réponse à retourner au client. Nous allons créer nos premières Servlets afin de montrer le fonctionnement des méthodes évoquées dans ce guide. Pour cela, sous Eclipse, nous allons créer un nouveau projet Tomcat sous le nom : betaboutique. Attention, avant de créer le projet betaboutique, un répertoire de ce nom doit être créé sur le disque dur du système. Nous allons créer au sein de ce projet un paquetage nommé : betaboutique.servlets.exemples. © ENI Editions - All rigths reserved - 3- Dans ce paquetage, nous ajoutons une nouvelle classe Java nommé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 enregistrant le fichier MaServlet1.java dans le répertoire WEB­INF/src/ du projet. Le contenu du fichier MaServlet1.java est le suivant : 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(""); out.println("

MaServlet1 en GET

"); out.println(""); out.flush(); out.close(); } } Cette Servlet génère un warning The serializable class MaServlet1 does not declare a static final serialVersionUID field of © ENI Editions - All rigths reserved - 5- type long. En effet, une classe sérialisable (comme les JavaBeans) utilise un attribut serialVersionUID qui permet d’affecter un numéro de version à la classe. Ce numéro doit être changé et mis à jour lorsque un champ sérialisable (non transient) est ajouté ou supprimé de la classe. C’est donc au développeur de gérer ce numéro de version, mais si ce numéro est absent, le compilateur génèrera un numéro automatiquement. Le champ serialVersionUID est utilisé lors de la désérialisation, afin de vérifier que les versions des classes Java sont correctes. Il est donc conseillé de gérer le champ serialVersionUID pour les classes sérialisables et de changer cette valeur lors d’un changement sur les champs de la classe. Pour définir l’attribut serialVersionUID, après la définition de la classe, le code suivant est utilisé : 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’a pas d’importance du moment qu’elle est mise à jour lors de changements sur les champs sérialisables. Java 5.0/6.0 a introduit de nombreux warnings qui sont des avertissements mais qui n’empêchent pas l’exécution du code. Il est possible d’utiliser la notation suivante @SuppressWarning afin de supprimer un warning spécifique. Ainsi les warnings de version peuvent être supprimés avec cette syntaxe (attention, il n’y a pas de point virgule en fin d’instruction). @SuppressWarnings("serial") public class ServletAuthentification extends HttpServlet...} Il est également possible de cacher un warning sur une méthode avant la déclaration de celle­ci, mais il est important de limiter au maximum la portée de l’annotation @SuppressWarnings. En effet, devant une méthode, la portée concerne la méthode elle­même mais devant une classe, elle concerne la totalité de cette dernière. Cette première Servlet est compilée automatiquement par Eclipse (Projet ­ Build Automatically). Elle est déployée dans le répertoire : /WEB­INF/classes/betaboutique/servlets/exemples/MaServlet1.class. Nous pouvons créer une page HTML simple (nommée maservlet1.html) avec un lien hypertexte pour déclencher cette Servlet. Le code suivant est placé à la racine de l’application (dans le répertoire du projet betaboutique). MaServlet1 Invoquer la Servlet MaServlet1 a. Comment effectuer le lien entre la Servlet et l’URL? Le fichier web.xml permet de paramétrer les Servlets de l’application. Chaque Servlet de l’application doit être décrite dans le fichier web.xml et le lien de la Servlet avec l’URL y figure également. La déclaration d’une Servlet se fait dans l’élément XML : ● est le nom interne de la Servlet, il l’identifie de façon unique. ● est la classe Java associée à la Servlet (notre classe Java). Le lien entre l’URL/URI et la Servlet s’effectue grâce à l’élément : ● est le nom interne donné à la Servlet qui doit être identique à celui indiqué dans l’élément . ● est l’URL permettant de faire le lien avec la Servlet. servletmaservlet1 betaboutique.servlets.exemples.MaServlet1 - 6- © ENI Editions - All rigths reserved servletmaservlet1 /maservlet1 À chaque Servlet doit correspondre un élément dans le fichier de configuration web.xml sinon, la Servlet n’existera pas pour le serveur d’applications. Le fichier /WEB­INF/web.xml n’est pas créé par défaut par Eclipse. Nous pouvons le créer à partir d’un ancien projet qui fonctionne en réalisant un simple copier/coller ou avec les commandes suivantes d’Eclipse : clic droit sur le répertoire /WEB­INF, New ­ Other ­ XML. 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 Servlet par l’intermédiaire d’un lien. Nous pouvons également déclencher directement la Servlet à cette adresse : 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. Comment cela fonctionne­t­il ? Ce fonctionnement est très puissant et permet ainsi de déclencher des Servlets suivant des modèles d’URL. Dans l’exemple précédant l’appel à une URL du type : http://localhost:8080/betaboutique/maservlet1 déclenche la Servlet de nom servletmaservlet1 qui est liée au fichier MaServlet1.class. Il est possible, par exemple, de modifier ce fonctionnement en déclenchant la Servlet suite à l’appel d’une ’’fausse’’ page nommée maservlet1html.html. ... servletmaservlet1 /maservlet1html.html ... Attention, il est important de bien recharger le serveur après chaque modification du fichier de configuration web.xml avec le manager de Tomcat par exemple : 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écisant que toutes les URL qui se terminent par exemple par .spsf déclencheront notre Servlet. Ceci pourra être utilisé pour un service particulier de l’application (télécharger le catalogue, une fiche article en PDF...). ... servletmaservlet1 *.spsf ... © ENI Editions - All rigths reserved - 7- c. L’objet response Dans l’exemple de la Servlet précédente, l’objet response permet d’envoyer des données au navigateur client. Cet objet permet d’envoyer la réponse au format TEXT, HTML, XML, sous forme de tableau d’octets... L’exemple précédent utilise un en­tête de réponse au format HTML response.setContentType(’’text/html’’). Voici ci­après quatre exemples différents. Le premier permet de renvoyer au navigateur un contenu au format HTML. 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(""); out.println("

MaServlet1 en GET

"); out.println(""); out.flush(); out.close(); } } Le second exemple permet de renvoyer un contenu au format XML. 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(""); out.println(""); out.println("Garde à vue"); out.println(""); - 8- © ENI Editions - All rigths reserved out.flush(); out.close(); } } Le troisième permet de renvoyer un contenu dynamique sous la forme d’un flux d’octets (une image dans ce cas). 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"); } } } © ENI Editions - All rigths reserved - 9- Enfin, le dernier exemple permet de forcer un téléchargement et d’envoyer un fichier du serveur vers le client. package betaboutique.servlets.exemples; import import import import import import import import import java.io.BufferedInputStream; java.io.File; java.io.FileInputStream; java.io.IOException; javax.servlet.ServletException; javax.servlet.ServletOutputStream; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; 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’objet request L’objet request est également très puissant et permet de gérer les données envoyées avec la requête à la Servlet. Par exemple les paramètres utilisateur (getParameter(...)) et les paramètres à valeurs multiples comme les champs Mot de passe : Désormais, il faut réaliser le mapping dans le fichier de configuration action=’’authentificationclient’’...> déclenche notre Servlet : ServletAuthentification. pour que l’appel
servletmaservlet1 betaboutique.servlets.exemples.MaServlet1 servletauthentification betaboutique.servlets.client.Servlet Authentification servletmaservlet1 /maservlet1 servletauthentification /authentificationclient Après le rechargement de Tomcat, l’appel de l’URL authentificationclient, déclenchera l’action servletauthentification qui est associée à la Servlet betaboutique.servlets.client.ServletAuthentification. Il ne nous reste donc plus qu’à créer le code de la Servlet elle­même. package betaboutique.servlets.client; import import import import import import java.io.IOException; java.io.PrintWriter; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; 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 © ENI Editions - All rigths reserved - 3- 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. Comment cela fonctionne­t­il ? Lorsqu’une Servlet est appelée pour la première fois, c’est sa méthode init() qui est déclenchée. C’est le seul cas où elle est appelée. Si la Servlet a été appelée par la méthode HTTP GET, la méthode doGet(...) de la Servlet traite la requête du client. Si la Servlet a été appelée par la méthode HTTP POST, la méthode doPost(...) de la Servlet traite la requête du client. La Servlet est dans ce cas la classe betaboutique.servlets.client.ServletAuthentification. Si elle n’est pas déjà chargée, elle le sera. Elle restera alors en mémoire pour les futures requêtes. Dans notre Servlet (et dans la plupart des cas), la méthode doPost(...) renvoie à la méthode doGet(...) le contenu entier (requête et réponse). Le client pourra donc envoyer indifféremment ses paramètres par un POST ou un GET. 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éthode getParameter(param) de l’objet request sert à récupérer dans la requête du client la valeur du paramètre de nom param. Le contenu des champs est vérifié. S’ils ne sont pas à l’état null, l’authentification est vérifiée par rapport aux données initialisées dans la Servlet. Dans cet exemple, c’est une page statique (HTML) qui déclenche une page dynamique (Java). Il n’existe donc aucun moyen d’indiquer à la page HTML de manière dynamique le résultat de l’exécution. En effet, il ne sera possible à aucun moment d’afficher le message d’erreur, de succès ou même les saisies de l’utilisateur dans la page HTML. Pour éviter cela, toute la page HTML peut être codée dans la Servlet. 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( ""+ ""+ ""+ "Authentification BetaBoutique"+ ""+ ""+ "

Authentification - Client

"+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ ""+ © ENI Editions - All rigths reserved - 5- "
Identifiant/Login :
Mot de passe :
"+ "
"+ ""+ "" ); } 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 (échec ou succès) et les saisies de l’utilisateur sont conservées dans les champs du formulaire. Toutefois, nous remarquons immédiatement que la Servlet est mal adaptée pour générer du code HTML. Le fonctionnement était beaucoup plus simple et clair avec une page HTML pour le déclenchement et une Servlet pour la logique applicative. Par la suite, c’est là qu’interviendront les pages JSP. - 6- ● Les pages d’appels et de réponses seront des documents dynamiques JSP. ● La logique de traitement des requêtes sera assurée par des Servlets. © ENI Editions - All rigths reserved Interface ServletConfig 1. Présentation L’objet de type javax.servlet.ServletConfig représente les informations de configuration d’une Servlet au sein d’une application Web. Le conteneur Web va créer un objet de type javax.servlet.ServletConfig pour chaque élément déclaré dans le fichier de configuration de l’application web.xml. Pour déclarer une Servlet dans le fichier de configuration web.xml, nous retrouvons le nom de la Servlet, la classe Java associée ainsi qu’une éventuelle/optionnelle liste de paramètres de la forme nom/valeur. Ces informations de configuration peuvent ensuite être récupérées par la Servlet de préférence dans la méthode init(...). Le fichier de configuration peut avoir zéro ou plusieurs éléments par Servlet. Chaque élément correspond à un paramètre représenté par une paire nom/valeur avec et . Nous allons modifier notre précédente Servlet pour définir l’identifiant et le mot de passe dans le fichier de configuration plutôt que dans la Servlet elle­même. servletauthentification betaboutique.servlets.client.Servlet Authentification defautIdentifiant monidentifiant defautMotDePasse monmotdepasse servletauthentification /authentificationclient 2. Initialisation d’une Servlet Après son chargement en mémoire, le conteneur Web passe en phase d’initialisation de la Servlet et la méthode init (...) est invoquée. La méthode init(...) est très souvent surchargée pour récupérer des informations de configuration, charger des ressources utiles à la Servlet... L’interface ServletConfig permet ensuite de manipuler les paramètres du fichier de configuration de l’application web.xml. La méthode getInitParameter(...) permet de récupérer la chaîne de caractères associée à un paramètre dans le fichier de configuration (ou la valeur null si le paramètre n’existe pas). La méthode getInitParameterNames() permet de récupérer sous la forme d’une énumération l’ensemble des paramètres déclarés dans le fichier de configuration. 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’exemple suivant est une amélioration de la Servlet d’authentification afin de lire l’identifiant et le mot de passe dans le fichier de configuration pour perfectionner la maintenabilité de l’ensemble. package betaboutique.servlets.client; import java.io.IOException; import java.io.PrintWriter; © ENI Editions - All rigths reserved - 1- import import import import import javax.servlet.ServletConfig; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; 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 Interface ServletContext 1. Présentation La précédente interface ServletConfig est très intéressante pour gérer des paramètres qui sont propres à une Servlet comme un mot de passe, une clé de cryptage ou un chemin vers un fichier du disque dur. À l’inverse de l’objet javax.servlet.ServletConfig qui représente les informations de configuration d’une Servlet au sein d’une application Web, un objet de type javax.servlet.ServletContext représente les informations de configuration d’une application Web totale. Chaque Servlet de la même application Web aura donc accès à ces paramètres. Une autre propriété très importante de l’objet javax.servlet.ServletContext est qu’il peut également interagir avec le conteneur Web de l’application. Il y aura donc un accès en lecture mais aussi un autre accès en écriture dans le fichier de configuration. Le but étant alors de créer, lire et supprimer des attributs de façon dynamique, ce qui permet alors le partage de ressources entre Servlets d’une même application Web. 2. Utilisation Un objet de type javax.servlet.ServletContext est obtenu en invoquant directement la méthode getServletContext() dans les méthodes des Servlets (objet servlet). De la même manière que la déclaration spécifique de paramètre par Servlet, il est possible de déclarer des paramètres globaux pour toute l’application. Par exemple, il est possible de déclarer l’adresse email du Webmestre, de l’administrateur, une URL complète, une adresse IP fixe, un répertoire, une base de données, le pilote JDBC... Des objets pour des pools de connexions et des objets partagés peuvent être aussi déclarés. L’élément , racine du fichier de configuration web.xml, peut contenir zéro ou plusieurs éléments qui permettent de déclarer des paramètres de l’application Web. Chaque élément correspond à un paramètre représenté par une paire nom/valeur avec les éléments et . Pour notre projet Betaboutique, nous allons ajouter l’email de l’administrateur à contacter lors de l’authentification. emailAdministrateur admin@betaboutique.fr servletauthentification betaboutique.servlets.client.Servlet Authentification defautIdentifiant monidentifiant defautMotDePasse monmotdepasse servletauthentification /authentificationclient 3. Récupérer des paramètres Pour récupérer les paramètres présents dans le fichier de configuration, il existe plusieurs méthodes. La première méthode getInitParameter(param) permet de récupérer une chaîne de caractères contenant la valeur d’un paramètre © ENI Editions - All rigths reserved - 1- ou null si le paramètre n’existe pas. La seconde méthode getInitParameterNames() permet de retourner l’énumération de l’ensemble des paramètres déclarés dans le fichier de configuration. La Servlet d’authentification est modifiée afin d’afficher l’email de l’administrateur du site en cas d’erreur. Il faut remarquer l’utilisation de l’objet de type javax.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. Ajouter des paramètres L’avantage de l’utilisation de l’interface ServletContext est la possibilité d’ajouter des attributs à la volée de manière logicielle (en programmation). Le contexte est accessible par l’ensemble des Servlets/JSP de l’application Web et peut donc stocker, récupérer et détruire des attributs. Contrairement à l’interface ServletConfig, des objets divers peuvent être gérés et pas seulement des chaînes de caractères. Ce procédé très utile permet par exemple de déclarer au lancement de l’application un objet pour la connexion à la base de données et de le stocker à la volée dans le fichier de configuration comme variable globale. La connexion sera alors disponible pour l’ensemble des classes Java EE (Servlets et JSP). Ces attributs sont des paires clé/valeur, la clé étant une chaîne de caractères et la valeur étant un objet de n’importe quel type. Les attributs d’une application Web sont donc des variables globales appelées également variables d’application des éléments participants à son fonctionnement et qui peuvent être partagées simultanément par plusieurs Servlets et pages JSP. La méthode setAttribute(nom, objet) est utilisée pour positionner un attribut qui sera visible dans toute l’application (portée application). Si le nom de l’attribut existe déjà, la valeur existante est remplacée par la nouvelle. La méthode getAttribute(nom) est utilisée pour récupérer la valeur d’un attribut de type quelconque dans l’application ou la valeur null si l’attribut n’existe pas. La méthode getAttributesNames() permet de récupérer le nom de tous les attributs actuellement stockés dans le contexte de l’application Web. Enfin, la méthode removeAttribute(nom) permet la suppression de l’attribut indiqué dans le contexte de l’application. 5. Mise en application Afin de mettre en application l’interface ServletContext, nous allons créer une classe JavaBean Client afin de gérer un objet client suite à une authentification correcte. L’objet client sera alors enregistré dans le contexte de l’application et lu par une seconde Servlet (ServletLectureAuthentification). Pour cela nous allons créer un nouveau paquetage appelé betaboutique.javabean. Ce paquetage permet de stocker toutes les classes POJO (Plain Old Java Object, utilisé pour faire référence à des classes simples composées d’accesseurs). L’acronyme POJO est utilisé pour faire référence à la simplicité d’utilisation d’un objet Java en comparaison avec la lourdeur d’utilisation d’un composant EJB (Entreprise Java Bean). Les JavaBeans (à ne pas confondre avec les EJB) sont des composants logiciels simples réutilisables et manipulables. Pour être une classe JavaBean, celle­ci devra respecter certaines conventions pour son utilisation, sa réutilisation et sa connexion JavaBean : la classe doit être sérialisable (pour les sauvegardes et lectures), la classe doit avoir un constructeur par défaut (sans argument), les propriétés des méthodes doivent être accessibles via des méthodes (accesseurs). La seule différence réelle entre un POJO et un JavaBean est la possibilité de gérer des événements pour les JavaBeans. 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; } © ENI Editions - All rigths reserved - 3- 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 import import import import import import import import java.io.IOException; java.io.PrintWriter; javax.servlet.ServletConfig; javax.servlet.ServletContext; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; 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 http://localhost:8080/betaboutique/authentificationclient, nous pouvons respectant le code et la configuration du fichier web.xml. par le déclencher la biais Servlet de l’URL ci­dessous en Nous remarquons alors que notre objet client1 stocké dans la variable globale est accessible pour toute l’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. emailAdministrateur admin@betaboutique.fr servletauthentification betaboutique.servlets.client.Servlet Authentification defautIdentifiant monidentifiant defautMotDePasse monmotdepasse servletlectureauthentification betaboutique.servlets.client.ServletLecture Authentification servletauthentification /authentificationclient servletlectureauthentification /lectureauthentificationclient package betaboutique.servlets.client; import import import import import import import import java.io.IOException; java.io.PrintWriter; javax.servlet.ServletContext; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; 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) { © ENI Editions - All rigths reserved - 5- 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 Traitement des requêtes 1. Présentation Les méthodes doGet(...) et doPost(...) possèdent en premier paramètre un objet de type javax.servlet.http.HttpServletRequest. Cet objet est créé par le conteneur Web juste avant le déclenchement d’une des méthodes et correspond à la requête du client. Il existe de nombreuses méthodes dédiées au traitement des requêtes utilisateurs comme la récupération de paramètres, la gestion des flux... a. Récupérer des paramètres transmis par le client Les paramètres permettent à la Servlet de réaliser les traitements adaptés. La méthode getParameter(nom) permet de récupérer sous la forme d’une chaîne de caractères la valeur d’un paramètre nommé passé en argument. Si le paramètre n’existe pas, la valeur null est retourné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"); ... La méthode getParameterNames() permet de récupérer sous la forme d’une énumération l’ensemble des noms des paramètres contenus dans la requê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); } ... La méthode getParameterValues(nom) permet de récupérer sous la forme d’un tableau de chaînes de caractères un ensemble de valeurs ou la valeur null si le paramètre n’existe pas. Cette méthode est utilisée pour les listes

<% //afficher la recherche si présente if(recherche!=null && !recherche.equalsIgnoreCase("")) { out.println("Votre recherche : "+recherche+"

"); } %> ... package betaboutique.servlets; import import import import import import import import java.io.IOException; java.util.ArrayList; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.sql.DataSource; 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(); ... La recherche est désormais fonctionnelle. Nous pouvons essayer avec la chaîne ’’balance’’ qui dans ce cas va porter sur le nom de l’article ou avec la chaîne ’’nathalie baye’’ qui va porter sur la description (acteurs). Cette requête est opérationnelle mais très lente lors de l’utilisation de nombreux enregistrements et surtout sur des champs de grande taille comme la description. Nous pouvons optimiser ce système avec l’utilisation d’expressions régulières en 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+")"); © ENI Editions - All rigths reserved - 13 - 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ère des moteurs de recherches. Cette instruction SQL nommée MATCH permet de faire des pondérations (ajout d’opérateurs). Par contre, il est parfois nécessaire de réaliser un index FULLTEXT sur les champs de recherche pour améliorer le temps d’exécution. Dans notre exemple, nous ne modifions à chaque fois que la classe modèle (d’où l’intérêt du MVC). Nous pouvons ainsi apporter des améliorations au site sans coder à nouveau l’ensemble d’une fonctionnalité. Voici le code modifié de la requête avec la clause 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 indiquer que le mot à la suite du signe doit être présent dans une ligne des enregistrements. L’opérateur ­ indique que le mot à la suite du signe ne doit pas être présent dans une ligne des enregistrements. L’opérateur * permet de faire des recherches sur des portions de mots. L’opérateur ’’ (guillemets) permet de faire des recherches exactes dans un champ (ex : citation entre guillemets). Nous pouvons tester ces opérateurs avec les recherches suivantes : ● nathalie baye : tous les articles avec l’actrice Nathalie Baye dans le titre ou la description seront retrouvés. ● baye : tous les articles avec l’actrice Baye dans le titre ou la description seront retrouvés. ● baye ­léotard : les articles contenant Nathalie Baye mais pas Philippe Léotard seront retrouvés. ● pier* : les articles contenant le terme pier (Pierre Palmade, Pierre Desproges et Jean­Pierre Melville) seront retrouvés. Ces exemples permettent de mettre en évidence l’utilisation d’une classe modèle et de requêtes SQL évoluées par rapport à un système de persistance (Hibernate ou autre). En effet, le code est parfois plus long à écrire mais les requêtes sont optimisées et utilisent TOUTES les instructions et la puissance du langage SQL. - 14 - © ENI Editions - All rigths reserved Classe modèle 1. Présentation L’affichage des articles et la recherche sont désormais opérationnels. Nous allons maintenant développer une classe modèle complète qui permet de gérer toutes les opérations d’administration pour un service (gestion des articles) et qui servira d’exemple pour la construction des autres services de la boutique. La classe modèle aura donc une fonction pour lister tous les articles avec des recherches, une fonction pour récupérer toutes les informations d’un article précis, une fonction pour modifier un article, une fonction pour la création et enfin, une fonction pour la suppression. Nous allons également gérer la pagination dans les listes et les recherches avec des contraintes sur les types (ex : recherche sur le nom ou sur la description). 2. Mise en place Nous commençons par créer une nouvelle Servlet nommée ServletGestionArticles. Cette Servlet permettra de réaliser toutes les opérations sur les articles en administration. Sa définition dans le fichier web.xml est présentée ci­dessous. ... servletgestionarticles betaboutique.servlets.ServletGestion Articles servletgestionarticles /admin/gestionarticles/* ... Nous retrouvons la déclaration de la Servlet de gestion des articles en administration (/admin/gestionarticles/*). Ensuite, il faut réaliser le code du contrôleur (Servlet) afin de préparer la gestion des articles. package betaboutique.servlets; import import import import import import import import java.io.IOException; java.util.ArrayList; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.sql.DataSource; 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"); © ENI Editions - All rigths reserved - 1- //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ètres qui correspondent à la pagination, aux recherches et aux calculs d’affichage. Ci­dessous, la fonction de liste de la classe modè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) © ENI Editions - All rigths reserved - 3- { //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 est composée de plusieurs pages JSPF pour la mise en page 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"; %> <%@ include file="../outils/entete.jspf" %> <%@ include file="../outils/pagination.jspf" %>
Rechercher
select name="typerecherche" id="typerecherche">

<% //entete pagination out.println(pagination); %> <% for(int i=0;i"); else out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if(article.getEtatarticle().equals("1")) { out.println(""); } else { out.println(""); } out.println(""); out.println(""); out.println(""); out.println(""); } %> <% - 6- © ENI Editions - All rigths reserved //piedpage pagination out.println(pagination); %>
Id<% if(nomtri.equals("id_article") && tri.equals("ASC")) {out.println("");} if(nomtri.equals("id_article") && tri.equals("DESC")) {out.println("");}%> Nom<% if(nomtri.equals("nomarticle") && tri.equals("ASC")) {out.println("");} if(nomtri.equals("nomarticle") && tri.equals("DESC")) {out.println("");}%> Prix<% if(nomtri.equals("prixarticle") && tri.equals("ASC")) {out.println("");} if(nomtri.equals("prixarticle") && tri.equals("DESC")) {out.println("");}%> Date<% if(nomtri.equals("datearticle") && tri.equals("ASC")) {out.println("");} if(nomtri.equals("datearticle") && tri.equals("DESC")) {out.println("");}%> Vignette Catégorie Etat<% if(nomtri.equals("etatarticle") && tri.equals("ASC")) {out.println("");} if(nomtri.equals("etatarticle") && tri.equals("DESC")) {out.println("");}%> Gestion
"+article.getId_article()+""+article.getNomarticle()+""+article.getPrixarticle()+" Eur"+article.getDatearticle()+""+article.getNomcategoriearticle()+"\"Actif\"\"Inactif\"
<%@ include file="../outils/piedpage.jspf" %> Le code pour la pagination est ci­après. Ce code générique est commun à plusieurs pages, il est donc plus intéressant d’utiliser une page fragment (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("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(" "); pagination.append(" "); } //afficher les boutons suivants que si nécessaire if(pagesuivante"); pagination.append(" "); © ENI Editions - All rigths reserved - 7- } pagination.append(""); %> Deux variables sont utilisées pour les liens et la maintenabilité. La variable action permet de réaliser les liens vers la Servlet adaptée et la variable urlapplication permet de réaliser les liens, insérer les images... Enfin, dans le fichier web.xml la déclaration de l’URL de la Servlet est faite avec le modèle * afin de pouvoir passer des paramètres : /admin/gestionarticles/*. (ex : /admin/gestionarticles? action=modifier&id_article=4). 3. Optimisation avec JavaScript La partie administration commence à être fournie en terme de fonctionnalités. Nous allons utiliser à cette étape du développement le langage JavaScript pour augmenter l’ergonomie de l’ensemble. Pour commencer, nous allons ajouter un fichier JavaScript qui servira de boîte à outils tout au long du projet (découpage de chaînes, messages d’alertes...). Pour cela, nous utilisons le code suivant à enregistrer sous le nom : /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; } } Le code contient pour le moment uniquement une fonction pour valider les confirmations de suppression. Il reste à inclure ce fichier dans l’en­tête JSPF avec la ligne suivante : Administration - BetaBoutique ... Désormais si nous cliquons sur l’image de suppression d’un article, une boîte de confirmation est affichée. - 8- © ENI Editions - All rigths reserved Ce fichier boiteoutils.js sera incrémenté de fonctions au fur et à mesure du développement de l’application. Toutefois, il existe actuellement trois grandes librairies JavaScript qui permettent de réaliser des services intéressants. Les trois principales librairies actuelles en matière de JavaScript sont : ● JQuery (http://jquery.com/). C’est la librairie la plus légère et la plus simple à utiliser. Elle possède un fichier très léger pour les principales fonctionnalités et des fichiers plug­in pour des services adaptés. ● ScriptAculous (http://script.aculo.us/). Cette librairie est très complète mais assez lourde. Son utilisation est 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’est actuellement la librairie la plus puissante. Son extrême lourdeur (400 Ko de librairies) et sa complexité sont ses principaux défauts. Les fonctionnalités proposées et son design Web 2.0 sont ses avantages. Pour notre projet, nous allons utiliser la librairie JQuery de base ainsi que certains plug­ins au besoin. Pour installer la librairie, il suffit de la télécharger, de l’installer dans le répertoire /javascript et de réaliser les inclusions nécessaires dans le fichier d’en­tête JSPF. Nous allons également utiliser le plug­in autocomplete pour gérer les "auto­complétions". Pour cela, il est nécessaire de copier la librairie dans le répertoire /javascript/jquery/plugin/autocomplete. Il faut aussi créer un fichier nommé recherche.js dans le répertoire /javascript/jquery/plugin/recherche. Les inclusions sont réalisées avec les instructions suivantes : Dans le fichier recherche.js, nous allons pour le moment juste gérer l’affichage du formulaire de recherche. Sur l’image de la loupe, nous ajoutons le code qui permet de déclencher la fonction JavaScript recherche().
Rechercher
Nous allons également modifier notre feuille de style sur l’objet formulairerecherche (
) afin de ne pas afficher le formulaire de recherche par défaut. #formulairerecherche © ENI Editions - All rigths reserved - 9- { display:none; } Nous pouvons écrire le code de fichier /javascript/jquery/plugin/recherche/recherche.js. la fonction recherche() présente dans le //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); } } Le code est très simple et utilise les fonctionnalités de la bibliothèque JQuery. Si le style de la feuille CSS est caché (display:none) alors il est montré avec la fonction slideDown(), sinon il est caché avec la fonction slideUp(). Nous avons donc une boîte de recherche qui s’ouvre de manière dynamique et ergonomique. 4. Optimisation avec Ajax Asynchronous JavaScript and XML (XML et JavaScript asynchrones) est une solution libre pour le développement de fonctionnalités Web. Ajax est un framework (ensemble de technologies et librairies) qui regroupe HTML/XHTML, XML, CSS, JavaScript et l’objet XMLHttpRequest pour l’échange de données en asynchrone. Cette technologie très utilisé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 XMLHttpRequest est très lourd à manipuler, c’est pourquoi, beaucoup de concepteurs et sociétés ont développé des librairies plus simples pour utiliser Ajax telles que ScriptAculous, Advajax ou JQuery. En effet, la librairie JQuery de base possède toutes les fonctionnalités pour utiliser la technologie Ajax. Nous allons ainsi mettre en œ uvre la technologie Ajax pour l’auto­complétion des formulaires de recherche. Ce service d’auto­ complétion sera générique à toutes nos pages, c’est­à­dire qu’il sera opérationnel sans modification du code, sans développement d’une Servlet spécifique à chaque fois... Pour cela, le nom de la table et le nom du champ sur lequel réaliser la recherche seront utilisés en paramètre. Voici les étapes à suivre pour installer l’auto­complétion : 1 ­ Utilisation de la librairie Ajax (JQuery). 2 ­ Téléchargement de plug­in autocomplete. 3 ­ Création d’un fichier recherche.js avec notre code personnel. 4 ­ Insertion des librairies dans notre application (
 
<%@ include file="navigation.jspf" %>
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
À cette étape du projet, le lancement de l’application permet d’afficher l’interface graphique du projet dans le navigateur ainsi que le menu de navigation qui sera modifié par la suite. 3. Installation de Struts L’application chatbetaboutique est maintenant correctement déployée. Nous allons procéder à l’installation de Struts en copiant les archives Java .jar et les fichiers de configuration associés. Pour cela, nous allons commencer par copier les archives suivantes dans le répertoire /WEB­INF/lib de l’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 ­ Properties ­ Java Build Path ­ Add External JARs et sélection de toutes les archives. L’installation est presque terminée, il nous reste à installer par simple copier/coller les fichiers de configuration struts­ config.xml, validation.xml et validator­rules.xml dans le répertoire /WEB­INF de l’application et à créer un paquetage nommé ressources avec un fichier de propriétés nommé dans notre cas ressources.properties. La gestion de ce fichier est réalisée dans le fichier de configuration struts­config.xml. 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éclaration des fichiers de configuration. Application chatbetaboutique © ENI Editions - All rigths reserved - 3- action org.apache.struts.action.ActionServlet config /WEB-INF/struts-config.xml 2 action *.do index.jsp À cette étape du projet, l’arborescence de l’application est la suivante : 4. Installation du pool de connexion à la base de données Nous allons terminer la partie installation de l’application par la mise en place de la source de connexion à la base de données. Cette étape est strictement identique au projet étudié dans le chapitre précédent consacré aux bases de données. Le fichier de configuration de l’application /TOMCAT/conf/Catalina/localhost/chatbetaboutique.xml est présenté - 4- © ENI Editions - All rigths reserved ci­après : Nous passons ensuite à la définition de la DataSource au sein du fichier /WEB­INF/web.xml de l’application. DB Connection jdbc_chatbetaboutiquemysql javax.sql.DataSource Container Pour ce qui est de la création de la connexion au pool, nous pourrions définir une classe chargée au lancement de l’application comme dans le chapitre précédent. Avec le framework Struts, il existe la possibilité de créer des plug­ins qui seront également chargés au démarrage de l’application. La déclaration d’un plug­in est réalisée à la fin du fichier de configuration de l’application. Avec cette déclaration, nous précisons qu’un plug­in nommé PluginDataSource, présent dans le paquetage chatbetaboutique sera chargé au démarrage de l’application. Par souplesse lors du développement, nous allons créer deux constantes au sein du fichier de configuration /WEB­ INF/web.xml. Application chatbetaboutique connecteurjdbc jdbc_chatbetaboutiquemysql urlapplication http://localhost:8080/chatbetaboutique ... La classe chatbetaboutique.PluginDataSource possède une syntaxe déjà abordée dans le chapitre consacré aux bases de données. Ce plug­in permet uniquement de charger le pool de connexion nommé datasource au démarrage de l’application Struts. package chatbetaboutique; import import import import import import import import javax.naming.Context; javax.naming.InitialContext; javax.servlet.ServletContext; javax.servlet.ServletException; javax.sql.DataSource; org.apache.struts.action.ActionServlet; org.apache.struts.action.PlugIn; org.apache.struts.config.ModuleConfig; public class PluginDataSource implements PlugIn { © ENI Editions - All rigths reserved - 5- //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 } Nous pouvons tester le fonctionnement du plug­in en démarrant notre application de gestion du chat. - 6- © ENI Editions - All rigths reserved 5. Liste des utilisateurs Nous allons commencer la première partie de cette application de gestion du chat par le service qui permet de lister tous les utilisateurs de l’application. Pour rappel, les utilisateurs sont définis dans la table chatbetaboutique.utilisateur qui possède la structure suivante : Pour la réalisation des services avec Struts nous allons procéder méthodiquement par étape : ● Définition du JavaBean associé à la classe à gérer. ● Définition du formulaire de JavaBean dans le fichier de configuration struts­config.xml. ● Définition du routage du service en cours dans le fichier de configuration struts­config.xml. ● Définition des validations si nécessaire. ● Codage de la classe de gestion des actions. ● Codage du modèle si nécessaire. ● Codage de la vue JSP. Pour des raisons de confort de programmation et de maintenance future, nous allons développer la totalité de l’application avec des DynaForms Struts et le fichier de validation des entrées validation.xml. a. Création du JavaBean Nous commençons par créer un nouveau paquetage nommé boiteoutils. Ce paquetage Java va contenir par la suite les classes JavaBean ainsi que si besoin certaines classes statiques. Au sein de ce paquetage, une nouvelle classe JavaBean nommée Utilisateur est créée en correspondance avec les champs du diagramme de classes UML et de la 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 private String private String private String classe id_utilisateur=null; pseudonyme=null; motdepasse=null; © ENI Editions - All rigths reserved - 7- private private private private String nom=null; String prenom=null; int autorisation=0; String image=null; public Utilisateur() { } public int getAutorisation() { return autorisation; } ... //fin de la classe } b. Déclaration du JavaBean de formulaire Le JavaBean Utilisateur est correctement codé, l’étape suivante consiste à déclarer ce JavaBean de formulaire au sein du fichier de configuration de l’application Struts struts­config.xml. Le JavaBean de formulaire est déclaré sous forme d’un DynaForms avec pour nom FormUtilisateur et pour propriété un objet utilisateur instance de la classe boiteoutils.Utilisateur. c. Définition du routage Le JavaBean est correctement configuré pour Struts, nous pouvons désormais passer à la déclaration du routage de l’application. Cette étape est également réalisée dans le fichier de configuration struts­config.xml par l’intermédiaire de la balise . Cette déclaration d’action sera associée à une classe de type MappingDispatchAction. Le paramètre path permet d’indiquer que cette action sera déclenchée avec un lien nommé listeutilisateur. Le formulaire JavaBean associé à cette action est FormUtilisateur déclaré plus haut par l’intermédiaire de la balise . Le paramètre scope 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’’ sera déclenché en cas d’erreur dans l’action ou d’erreur de saisie. Cette action nommée accueil est une action globale déclarée comme ceci au sein du fichier struts­config.xml : - 8- © ENI Editions - All rigths reserved Cette action pourra ainsi être référencée depuis n’importe quelle action Struts. Le paramètre type permet d’indiquer la classe d’action qui sera chargée de traiter et déclarer l’action associée au chemin /listeutilisateur. Enfin, le paramètre parameter permet avec la classe MappingDispatchAction de préciser la méthode de la classe d’action qui sera déclenchée. Dans notre cas, un lien sur /listeutilisateur va déclencher la méthode listeutilisateur de la classe chatbetaboutique.GestionUtilisateurAction. Enfin, la redirection déclarée avec la balise permet d’indiquer la vue qui sera affichée en retour du traitement. d. Codage de la classe de gestion des actions La déclaration de l’action a été effectuée dans l’étape précédente. Nous pouvons réaliser le codage de la classe d’action nommée GestionUtilisateurAction présente dans le paquetage chatbetaboutique. package chatbetaboutique; import import import import import org.apache.struts.action.*; org.apache.struts.actions.MappingDispatchAction; java.io.IOException; javax.servlet.ServletException; 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 } Pour le moment cette action permet uniquement d’afficher un message dans la console Java. Nous allons recharger 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. Le fonctionnement de l’action est opérationnel, nous pouvons passer à son codage qui doit permettre de lire et de retourner la liste des utilisateurs de l’application du chat de BetaBoutique. Pour l’affichage des informations, nous ne réaliserons pas de tri, recherche et pagination afin de simplifier le code et les explications associées. package chatbetaboutique; import import import import import import modele.UtilisateurModele; org.apache.struts.action.*; org.apache.struts.actions.MappingDispatchAction; java.io.IOException; java.util.ArrayList; javax.servlet.ServletException; © ENI Editions - All rigths reserved - 9- 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 } Le code est très simple et nous remarquons alors tout l’intérêt d’utiliser le modèle MVC II. Nous commençons par déclarer la connexion à la base de données (ds), ensuite nous instancions un objet qui représente le modèle (utilisateurmodele). Nous déclenchons la fonction du modèle qui va retourner tous les objets de type Utilisateur sans se soucier du traitement (getListeUtilisateur()), nous postons cette collection dans la requête (request.setAttribute()) et nous nous dirigeons vers la vue qui va afficher les données (mapping.findForward()). e. Codage du modèle Le traitement de l’action est désormais réalisé. Nous pouvons passer au codage du modèle. Pour cela, nous allons créer un nouveau paquetage nommé modele et la classe UtilisateurModele. Cette classe possède pour le moment une seule méthode qui permet de lire la liste des utilisateurs de la base de données et de les insérer dans une collection de type ArrayList. package modele; import import import import import import import boiteoutils.*; java.sql.Connection; java.sql.PreparedStatement; java.sql.ResultSet; javax.sql.DataSource; org.apache.commons.dbutils.BeanProcessor; java.util.ArrayList; public class UtilisateurModele { //variables de classe DataSource ds=null; Connection connection=null; ResultSet rs=null; //liste des objets ArrayList listeutilisateur=new ArrayList(); - 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)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 collection d’objets de la classe Utilisateur. Cette collection est ensuite retournée à l’action qui va elle­même réaliser le routage vers la page JSP d’affichage des données. Nous utilisons également la classe statique boiteoutils.OutilsBaseDeDonnees pour la gestion du SGBD. f. Codage de la vue JSP © ENI Editions - All rigths reserved - 11 - La dernière étape consiste à coder la vue JSP. Pour cela, nous allons utiliser les bibliothèques de tags livrées en standard avec Struts. L’inclusion de ces bibliothèques est réalisée dans le fichier /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" %> Administration - BetaBoutique ... La vue JSP /vues/utilisateur/listeutilisateur.jsp est codée en HTML avec une partie de code Java qui permet d’afficher les données. Nous pourrions tout à fait utiliser des balises 100% HTML en utilisant les taglibs Struts. <%@ page import="java.util.ArrayList" %> <%@ page import="boiteoutils.Utilisateur" %> <% //liste des utilisateurs ArrayList listeutilisateur=(ArrayList)request.getAttribute ("listeutilisateur"); %> <%@ include file="../outils/entete.jspf" %>
<% for(int i=0;i"); else out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if(utilisateur.getAutorisation()==1) { out.println(""); } else { out.println(""); } out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } %>
ID Pseudonyme Mot de passe Nom Prénom Autorisation Image Gestion
"+utilisateur.getId_utilisateur()+""+utilisateur.getPseudonyme()+""+utilisateur.getMotdepasse()+""+utilisateur.getNom()+""+utilisateur.getPrenom()+"\"Actif\"\"Inactif\""+utilisateur.getImage()+"
<%@ include file="../outils/piedpage.jspf" %> Notre premier service développé entièrement en Struts est terminé. Chaque partie est correctement découpée en respectant le pattern MVC II. Nous allons dans la suite de ce chapitre présenter la technique pour consulter la fiche détaillée d’un utilisateur, le formulaire de création et de modification d’un utilisateur avec les validations XML et l’action de suppression. Ces techniques seront alors utilisées à l’identique pour réaliser la partie gestion des salons du chat. 6. Consultation de la fiche utilisateur L’action de consultation est la plus simple à développer. Le JavaBean Utilisateur est déjà est place ainsi que sa définition. Nous pouvons passer directement à l’étape de déclaration de l’action dans le fichier struts­config.xml. La déclaration est pratiquement identique à celle de la liste des utilisateurs. Nous retrouvons le paramètre path qui permet de définir le nom de l’action qui va être déclenché : /consulterutilisateur.do. Le JavaBean de formulaire est toujours le même, il n’y a pas de validation de saisie sur une consultation et c’est la méthode consulterutilisateur() de la classe chatbetaboutique.GestionUtilisateurAction qui sera déclenchée. Enfin, une seule définition de redirection est définie, elle correspond à la vue JSP pour l’affichage. Le code de l’action de consultation est assez simple. Le paramètre passé dans le lien est récupéré et testé. Cet attribut correspond au numéro de l’utilisateur à consulter. Ensuite, l’action déclenche le modèle afin de récupérer la totalité de l’objet associé à cet identifiant. Le code est ajouté à la suite de la classe GestionUtilisateurAction. //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); © ENI Editions - All rigths reserved - 13 - //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"); } La classe modèle UtilisateurModele possède désormais une nouvelle méthode qui permet de récupérer une image de l’objet utilisateur sauvegardé en base. /*********************************************************** * 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 vue /vues/utilisateur/consulterutilisateur.jsp. terminée, il ne reste plus que le code de la <%@ page import="boiteoutils.Utilisateur" %> <% //objet utilisateur Utilisateur utilisateur=(Utilisateur)request.getAttribute("utilisateur"); %> <%@ include file="../outils/entete.jspf" %>
Id<%= utilisateur.getId_utilisateur() %>
Pseudonyme<%= utilisateur.getPseudonyme() %>
Mot de passe<%= utilisateur.getMotdepasse() %>
Nom<%= utilisateur.getNom() %>
Prénom<%= utilisateur.getPrenom() %>
Autorisation<%= utilisateur.getAutorisation() %>
Image<%= utilisateur.getImage() %>
<%@ include file="../outils/piedpage.jspf" %> Le service de consultation est totalement opérationnel. Nous allons cependant ajouter un formulaire dans la page JSP de consultation afin de réaliser un lien vers le formulaire de modification par l’intermédiaire du bouton Modifier. La balise Struts permet d’ajouter un formulaire à la page. Cependant l’ajout de cette balise provoque une erreur. En effet, l’action /modifierutilisateur n’est pas encore déclarée dans le fichier de configuration struts­config.xml. <%@ page import="boiteoutils.Utilisateur" %> <% //objet utilisateur © ENI Editions - All rigths reserved - 15 - Utilisateur utilisateur=(Utilisateur)request.getAttribute("utilisateur"); %> <%@ include file="../outils/entete.jspf" %>
...
<%@ include file="../outils/piedpage.jspf" %> 7. Création d’un nouvel utilisateur Le formulaire de création est affiché par l’intermédiaire d’une page JSP simple. Les parties création et modification sont réalisées en deux étapes. La première étape consiste à afficher le formulaire avant validation. Cette action déclarée dans le fichier de configuration struts­config.xml permet de réaliser une redirection simple vers le formulaire de création. Le formulaire de création /vues/utilisateur/creerutilisateur.jsp reprend les différents champs de la table Utilisateur. <%@ include file="../outils/entete.jspf" %>
Pseudonyme
Mot de passe
Nom
Prénom
Autorisation
Image
- 16 - © ENI Editions - All rigths reserved Avec Struts, toutes les actions et JavaBean de formulaire sont liés. Dans la page ci­dessus, nous utilisons le taglib avec le paramètre action qui déclenche l’action /validercreerutilisateur. Cette action doit être déclarée dans le fichier struts­config.xml afin que la page JSP /vues/utilisateur/creerutilisateur.jsp puisse être compilée. Afin d’afficher les messages fichier /vues/outils/entete.jspf. d’erreur et de succès, nous allons ajouter quelques lignes à la fin du ...
Le formulaire de création est opérationnel, nous pouvons passer à la validation des saisies. Dans la déclaration de l’action /validercreerutilisateur qui est déclenchée suite à la validation du formulaire de saisie, nous avons déclaré le paramètre validate à true. Les contrôles de saisies seront donc réalisés en correspondance avec les déclarations du fichier validation.xml. En cas d’erreur, c’est l’action déclarée avec le paramètre input qui sera appelée. Dans notre cas, le formulaire de création est rappelé sans perdre les saisies. Nous déclarons en début de fichier /WEB­ INF/validation.xml deux constantes globales qui correspondent à la syntaxe respectivement du mot de passe et du pseudonyme utilisateur. Ensuite, nous déclarons une référence au formulaire utilisateur en correspondance avec le nom dans le fichier struts­config.xml . Les validations de saisies sont très pointues, les syntaxes sont contrôlées par rapport à des expressions régulières, des présences et des tailles. motdepasse ^[0-9a-zA-Z]*$ pseudonyme ^[0-9a-zA-Z\-]{4,20}$ autorisation ^[0|1]{1}$ © ENI Editions - All rigths reserved - 17 - minlength 4 maxlength 20 mask ${pseudonyme} ... Les champs peusdonyme, motdepasse et autorisation sont analysés de façon précise alors que les champs nom et prenom doivent uniquement être renseignés. La balise permet de faire le lien avec les noms dans le fichier ressources.properties. Comme indiqué précédemment, les validations et messages d’erreurs sont associés au fichier de propriétés qui est situé pour notre projet dans le paquetage ressources et qui a pour nom ressources.properties. Voici son contenu : # -- standard errors -errors.header=
    errors.prefix=
  • errors.suffix=
  • errors.footer=
# -- 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 Nous pouvons vérifier les saisies en réalisant différents tests précis sur le formulaire de cré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() de la classe GestionUtilisateurAction ainsi que la méthode associée au modè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")); © ENI Editions - All rigths reserved - 19 - 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’action de validation de la création permet d’instancier un objet utilisateur à l’image des saisies de l’utilisateur en une seule ligne. Ensuite, cet objet est rendu persistant par le modèle. Un message dynamique de succès (ou d’erreur) est alors inséré dans la requête après lecture des valeurs dans le fichier de proprié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. Modification de la fiche utilisateur Le formulaire de modification est toujours l’élément le plus complexe à développer en programmation Web. Certaines informations doivent être testées, des messages d’erreurs sont affichés en conséquence, des redirections sont réalisées en fonction des traitements et surtout les saisies doivent être conservées en cas d’erreur. Comme dans le cas de la création, la validation est réalisée avec deux étapes. La première consiste à lire les données de la fiche à modifier et la seconde permet la modification de celle­ci. Nous commençons par la réalisation du routage avec la définition des deux actions nécessaires. 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, la vue JSP /vues/utilisateur/modifierutilisateur.jsp permet d’afficher les données du JavaBean de formulaire en correspondance avec l’attribut id de l’utilisateur (). <%@ include file="../outils/entete.jspf" %> © ENI Editions - All rigths reserved - 21 -
Pseudonyme
Mot de passe
Nom
Prénom
Autorisation
Image
Il ne reste plus qu’à développer l’action validermodifierutilisateur() de la classe GestionUtilisateurAction ainsi que la fonction du modèle, afin de réaliser la modification en base. //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(); } ... } Nous terminons ensuite par la définition des deux nouveaux messages dans le fichier de propriétés. erreurs.modificationutilisateur=Erreur lors de la modification de l’utilisateur succes.modificationutilisateur=L’utilisateur a été modifié avec succès 9. Activation des vérifications JavaScript Nous pouvons activer les validations JavaScript pour le formulaire de création et de modification. Pour cela, il suffit de modifier la vue JSP avec deux lignes qui permettent de générer le code en rapport avec les règles de validation du fichier validation.xml. Si nous prenons par exemple la page /vues/utilisateur/creerutilisateur.jsp, nous devons simplement ajouter les lignes suivantes : ... ... © ENI Editions - All rigths reserved - 23 - 10. Suppression d’un utilisateur La suppression est le service le plus simple à développer avec la consultation. Nous allons cependant ajouter un fichier 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; } } Ce script est inséré dans la page /vues/outils/entete.jspf avec la ligne ci­après : Nous pouvons désormais réaliser la configuration de l’action par l’intermédiaire du fichier struts­config.xml. Enfin, il reste à coder l’action supprimerutilisateur() et du modèle avec les messages associé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(); } ... } © ENI Editions - All rigths reserved - 25 - 11. Gestion de l’état du serveur La gestion de l’état du serveur est réalisée avec la table serveur. Cette table possède un seul attribut nommé etatserveur qui permet d’activer ou désactiver le serveur. Pour commencer, nous allons développer une action Struts avec un paramètre envoyé par un lien HTML qui permet de 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. Ce lien va déclencher l’action etatserveur avec le paramètre en cours, représentant le nouvel état. Pour ce service, nous pouvons utiliser la classe Action simple proposée par Struts. De même, la table serveur possède un seul attribut, il est donc plus simple de ne pas créer une classe JavaBean de formulaire mais d’utiliser à la place un paramètre de type chaîne de caractères qui représente l’état du serveur. Nous commençons par ajouter un lien HTML pour déclencher le service de gestion de l’état du serveur. Nous passons ensuite à la définition de l’action etatserveur.do dans le fichier de configuration struts­config.xml. Le mapping de l’action est réalisé, il reste à créer le code de la classe chatbetaboutique.GestionServeurAction ainsi que le modèle associé. Nous pouvons également adapter notre action afin de vérifier si l’état du serveur est passé en paramètre et si nous devons dans ce cas changer son état avec une méthode du modèle. package chatbetaboutique; import import import import import import modele.ServeurModele; org.apache.struts.action.*; java.io.IOException; javax.servlet.ServletException; javax.servlet.http.*; 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 } Le modèle permet de lire l’état actuel du serveur dans la base de données. package modele; import import import import import boiteoutils.*; java.sql.Connection; java.sql.PreparedStatement; java.sql.ResultSet; 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; © ENI Editions - All rigths reserved - 27 - } /*********************************************************** * 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 } La page JSP permet d’afficher l’état du serveur et de passer un paramètre dans un lien pour changer sa valeur. <% //récupérer l’état du serveur String etatserveur=(String)request.getAttribute("etatserveur"); %> <%@ include file="../outils/entete.jspf" %>
- 28 - © ENI Editions - All rigths reserved
Etat <% if(etatserveur.equals("1")) { out.println("Serveur Actif "); } else { out.println("Serveur Inactif "); } %>
<%@ include file="../outils/piedpage.jspf" %> La fonction du modèle qui permet de changer l’état du serveur est la suivante : /*********************************************************** * 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, le fichier de propriétés possède les deux nouveaux messages suivants : #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 © ENI Editions - All rigths reserved - 29 - 12. Gestion des salons Nous allons développer un service de gestion des salons avec le même principe que celui utilisé pour les utilisateurs du chat. Le fonctionnement est identique, il consiste à développer le JavaBean de formulaire Salon en correspondance avec le modèle de la table salon de la base de donné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 } Nous réalisons ensuite, la définition de l’élément et du mapping pour les actions liées à la gestion des salons du chat. - 30 - © ENI Editions - All rigths reserved Nous pouvons désormais passer chatbetaboutique.GestionSalonAction. au codage des actions associées, avec la classe de gestion nommée package chatbetaboutique; import import import import import import import import import modele.SalonModele; org.apache.struts.action.*; org.apache.struts.actions.MappingDispatchAction; boiteoutils.Salon; java.io.IOException; java.util.ArrayList; javax.servlet.ServletException; javax.servlet.http.*; 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 © ENI Editions - All rigths reserved - 31 - { ... } //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 } La classe de gestion du modèle modele.SalonModele est également très proche du modèle de gestion des utilisateurs. package modele; import import import import import import import boiteoutils.*; java.sql.Connection; java.sql.PreparedStatement; java.sql.ResultSet; javax.sql.DataSource; org.apache.commons.dbutils.BeanProcessor; 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 listesalon=new ArrayList(); /*********************************************************** * 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)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); } } © ENI Editions - All rigths reserved - 33 - } ... } /*********************************************************** * 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 } La totalité du service de gestion des salons est pratiquement terminé, nous allons ajouter les fonctionnalités d’usage pour l’ensemble. Nous insérons d’abord un lien dans le menu de navigation /vues/outils/navigation.jspf. Ensuite, nous passons au codage des règles de validation pour le formulaire. Dans notre cas, nous indiquons une règle sur les champs theme, date et actif dans le fichier /WEB­INF/validation.xml. ... ... datesalon ^[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}$ - 34 - © ENI Editions - All rigths reserved ...
mask ${datesalon} mask ${autorisation}
... Enfin nous terminons par le fichier de propriétés pour les messages et les noms des champs. #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 Lors de la suppression d’un salon, un message de confirmation est affiché. La fonction confirmerSuppressionSalon doit donc être codée dans le fichier /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; } } Le service est opérationnel, nous pouvons tester les saisies, modifications et messages. © ENI Editions - All rigths reserved - 35 - 13. Gestion des inscriptions et interactivité Pour le développement du service de gestion des inscriptions utilisateurs aux salons, il existe plusieurs approches possibles : ● Développement d’un service spécifique qui permet d’associer des utilisateurs au salon indiqué à partir de cases à cocher. ● Développement d’un service interactif à base d’Ajax et de sérialisation des données. C’est la seconde solution que nous allons utiliser, avec un formulaire dynamique associé à chaque salon. Pour la réalisation de cette partie plus complexe, il est nécessaire de détailler un scénario explicatif : ● La première étape consiste à créer une balise
HTML associée à chaque ligne de la liste des salons. Par exemple, le salon avec id=6 aura une balise
cachée avec un identifiant unique et pourra recevoir un formulaire Ajax suite à un clic administrateur. ● Suite à ce clic, une fonction JavaScript Ajax va récupérer toute la liste des utilisateurs avec l’état de l’inscription associée (inscrit ou pas au salon) et afficher la réponse dans la balise
cachée. ● Tous les utilisateurs seront affichés dans ce formulaire et pourront donc être inscrits au salon sélectionné en une seule étape. Il serait possible, par facilité, de réaliser une inscription en Ajax sur chaque clic de la case à cocher mais le service ne serait pas très ergonomique. Nous allons donc utiliser un mécanisme de sérialisation pour envoyer tous les choix de l’administrateur avec un seul clic de souris. Le développement de ce service nécessite l’utilisation de la librairie JavaScript JQuery (http://jquery.com/). Cette librairie est référencée dans la page entete.jspf. Tout d’abord nous ajoutons un bouton de gestion dans la vue qui permet d’afficher la liste des salons. <% ... out.println("
 
"); ... %> Ensuite, nous codons la fonction JavaScript listeUtilisateurSalon() présente dans le fichier boiteoutils.js qui permet de récupérer tous les utilisateurs de la base de données ainsi que les états associés, pour ce salon. //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
concernee $("#listeutilisateursalon_"+id_salon).html(html); $("#listeutilisateursalon_"+id_salon).slideDown("slow"); } }); } } L’instruction suivante $(".listeutilisateursalon").html(""); présente dans la fonction JavaScript listeUtilisateurSalon() est importante. En effet, elle permet de détruire tous les formulaires qui ont pour classe CSS .listeutilisateur. En effet, la fermeture des blocs
ne permet pas la suppression des cases à cocher qui sont alors présentes dans la page HTML mais non visibles. Cette instruction permet de détruire tous les formulaires pour qu’il n’y ait pas de conflits de noms. Ensuite, nous codons l’action dans le fichier de configuration struts­config.xml. Le code de l’action listeutilisateursalon est ajouté dans la classe GestionUtilisateur. Ce développement est assez simple, il permet de déclencher une nouvelle fonction du modèle qui retourne tous les utilisateurs et leur état respectif, pour le salon indiqué en paramè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 © ENI Editions - All rigths reserved - 37 - 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 listeutilisateursalon=new ArrayList(); // parcours de chaque utilisateur for(int i=0;i <%@ page import="boiteoutils.Utilisateur" %> <% //liste des utilisateurs © ENI Editions - All rigths reserved - 39 - ArrayList listeutilisateursalon= (ArrayList)request.getAttribute("listeutilisateursalon"); //numéro du salon String id_salon=(String)request.getAttribute("id_salon"); %>
<% for(int i=0;i"); else out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } %>
ID Pseudonyme Nom Prénom Inscription
"+utilisateur.getId_utilisateur()+""+utilisateur.getPseudonyme()+""+utilisateur.getNom()+""+utilisateur.getPrenom()+""); //gestion de l’inscription pour l’utilisateur en cours out.println(""); } else { out.println(" />"); } out.println("
Le service d’affichage avec état est désormais opérationnel. Les utilisateurs inscrits au salon en cours sont bien marqués et la case à cocher correspondante affiche l’état. Nous pouvons maintenant gérer la partie inscription des utilisateurs aux salons. Il serait possible à chaque clic de souris sur la case à cocher de l’utilisateur, d’envoyer l’information à une action et de fermer la fenêtre. Cette technique envisageable n’est pas très ergonomique pour l’administrateur qui devra réaliser plusieurs opérations à chaque inscription. Une interface plus adaptée permet de sélectionner plusieurs utilisateurs (plusieurs choix d’inscription) et de valider l’ensemble en une seule étape. Le scénario de programmation est alors le suivant : - 40 - © ENI Editions - All rigths reserved ● L’administrateur sélectionne plusieurs inscriptions à la fois. ● L’administrateur clique sur le bouton de validation. Ce bouton déclenche une fonction JavaScript qui va récupérer toutes les cases cochées et les id des utilisateurs associés. Cette même fonction réalise alors une sérialisation des données et poste l’ensemble des informations à une action qui pourra réaliser la totalité des inscriptions en une seule é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#6 pour les utilisateurs d’id 2, 4 et 6. Nous commençons le codage de la fonction de validation JavaScript par le parcours des éléments du formulaire de type 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 //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 <%= resinstruction %> Il existe dans les précédentes lignes de code deux éléments essentiels au bon fonctionnement de l’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 cases d’un salon par exemple, la fonction de suppression sera alors déclenchée mais la boucle de création ne réalisera aucune opération, la suppression totale des inscriptions sera donc bien réalisée. Enfin la page JSP /vues/utilisateur/inscriptionutilisateursalon.jsp permet uniquement de retourner l’état de l’action d’inscription qui sera lu dans la fonction JavaScript Ajax en retour pour l’affichage. 14. Gestion des connexions utilisateur et interactivité Le service d’administration du chat est presque terminé, il reste à développer un service d’affichage des utilisateurs connectés au différents salons. Dans l’étape précédente nous avons développé un service Ajax pour récupérer la liste des utilisateurs inscrits à un salon donné. Nous pouvons développer sur des bases identiques des formulaires dynamiques pour afficher les utilisateurs actuellement connectés. Le développement commence par la modification de la page /vues/salon/listesalon.jsp avec l’insertion de balises
cachées et d’une classe CSS adaptée. ... out.println("
 
"); ... .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; } Nous pouvons désormais passer au développement de la fonction JavaScript Ajax qui permet de retourner la liste des utilisateurs connectés pour le salon indiqué et d’afficher le résultat dans la balise
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
concernee $("#listeconnexionsalon_"+id_salon).html(html); $("#listeconnexionsalon_"+id_salon).slideDown("slow"); } }); } } © ENI Editions - All rigths reserved - 45 - Il ne reste plus qu’à développer l’action avec sa déclaration, le modèle et la vue JSP. //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)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"); %> <% for(int i=0;i"); else out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); } %>
ID Pseudonyme Nom Prénom
"+utilisateur.getId_utilisateur()+""+utilisateur.getPseudonyme()+""+utilisateur.getNom()+""+utilisateur.getPrenom()+"
© ENI Editions - All rigths reserved - 47 - Ce service est opérationnel, nous voyons par exemple que pour le salon d’identifiant 5 qui a pour thème Java EE, l’utilisateur jlafoss est actuellement connecté. Ce système bien que très utile n’est pas fonctionnel et ergonomique. En effet, si l’utilisateur se déconnecte du salon et que l’administrateur n’actualise pas sa page Web, il ne verra pas le départ de l’utilisateur. Nous allons donc ajouter de l’ergonomie à ce service en utilisant un thread JavaScript qui est en fait un objet timer. Ce timer va déclencher à intervalles de temps régulier la fonction de listing pour prendre en compte les connexions et déconnexions utilisateurs. //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
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); } La fonction JavaScript getListeConnexionSalon() permet de récupérer avec l’objet timer, par intervalles réguliers, la liste des utilisateurs connectés au salon. Cet objet va déclencher toutes les 1.5 secondes la même fonction pour rafraîchir les connexions. Enfin, la fonction listeConnexionSalon() commence par détruire, s’il existe, l’objet timer lors des fermetures des blocs
. Nous pouvons tester ce service en utilisant deux navigateurs et en affichant un salon. Dans le premier navigateur, nous voyons la liste des connectés et dans le second nous réalisons la déconnexion au salon avec PhpMyAdmin par exemple, l’affichage du résultat doit être quasiment instantané. Le service d’administration de gestion du chat est maintenant terminé. Le paragraphe suivant de ce chapitre est consacré aux technologies WEB 2.0 et aux différents services associés. © ENI Editions - All rigths reserved - 49 - Web 2.0 1. Présentation Actuellement, les protagonistes de l’Internet parlent beaucoup de technologies Web 2.0. Derrière ce terme se cache un ensemble de technologies et d’outils axés sur l’ergonomie des interfaces. Le terme a été inventé par un membre de la société O’Reilly pour désigner les nouvelles technologies et la renaissance du Web. Le but est d’utiliser les technologies de l’Internet tout en se rapprochant des interfaces homme machine attractives des logiciels installés sur les machines personnelles. La définition exacte du Web 2.0 n’est toujours pas très claire, cependant il est admis qu’un site Web 2.0 possède les caractéristiques suivantes : ● La saisie, modification et suppression des informations du site sont simples. ● Le site est totalement utilisable avec un navigateur standard. ● L’outil utilise des standards de technologie. Du point de vue technologique, l’infrastructure Web 2.0 est assez complexe, elle inclut à la fois le ou les serveur(s), la syndication (accessibilité partagée) de contenu, la messagerie et les applications. Un site Web 2.0 est désigné comme tel s’il utilise les technologies suivantes : ● Les feuilles de style CSS. ● Les pages XHTML. ● La syndication RSS (usage des données du site dans un autre contexte). ● L’utilisation d’URL spécifiques pour le référencement. ● Une Application Internet Riche (Rich Internet Application) utilisant la technologie Ajax. ● L’étiquetage (métadonnées : utilisation de mots­clés ou nuages de mots­clés pour les recherches dans un contenu, le but étant d’interconnecter les éléments entre eux). ● Utilisation de l’approche HTTP et SOAP. Il n’existe pas d’accord unanime sur la définition et le sens du Web 2.0, le terme peut ainsi désigner des concepts radicalement différents suivant les personnes. Par exemple, certains associent le terme Web 2.0 pour des sites XHTML valides et bien formés. D’autres parlent de Web 2.0 avec l’utilisation abusive d’Ajax qui peut rendre les pages Web particulièrement longues au chargement. Pour résumer, les technologies qui se cachent derrière ce terme sont surtout JavaScript, DHTML et Ajax avec un respect des standards et du code valide syntaxiquement. Dans cette partie du cours nous allons aborder quelques services Web 2.0 pour faire évoluer notre système de gestion du chat BetaBoutique. 2. Tableaux redimensionnables Il est parfois très utile de bénéficier de tableaux redimensionnables en DHTML avec l’utilisation de la souris. 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écessite l’utilisation de la librairie de base JQuery et fonctionne sur les tableaux HTML. Nous commençons notre mise en place en copiant répertoire /javascript/jquery/plugin de notre application. les librairies du plug­in gridcolumnsizing dans le Ensuite, nous devons installer ces librairies dans notre page d’en­tête afin d’inclure les fichiers. <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> © ENI Editions - All rigths reserved - 1- <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %> Administration - BetaBoutique La mise en place nécessite la déclaration du service Web 2.0 en JavaScript dans la page d’en­tête par exemple. Le script précédent est simple, il permet d’appliquer au chargement de l’application, à chaque balise HTML qui possède un identifiant nommé tableaubordure, le service de redimensionnement avec les styles CSS. Désormais, notre tableau qui permet d’afficher la liste des utilisateurs est initialisé avec le redimensionnement automatique. Nous pouvons tester notre service en déplaçant la souris sur les différentes colonnes. ... ... 3. Champs redimensionnables De la même façon que nous avons utilisé un service de redimensionnement pour les tableaux HTML, nous pouvons gérer les champs des formulaires. Pour cela nous utilisons le plug­in JQuery nommé resizehandle qui permet des redimensionnements sur les champs de type et . 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’horizontale et de la verticale), deux images et une feuille de style CSS. Nous incluons ces fichiers dans la page /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" %> ... ... L’installation est quasiment terminée, il ne reste plus qu’à déclarer les champs de la page qui seront redimensionnables et à insérer la feuille de style CSS. Dans cette déclaration, tous les champs qui sont associés à la classe CSS input auront le service de redimensionnement horizontal avec 98 pixels au minimum et 350 pixels au maximum. Enfin, chaque champ de type