Les servlets et le moteur JServ
Une servlet est une partie de code Java, exécutant un traitement à la
demande d'un serveur Web, et fournissant à ce dernier une page html.
La suite de ce document est centrée sur le lien
avec le serveur Web, et n'aborde pas du tout l'utilisation, assez
fréquente, des servlets, pour accéder à une base de données, car cela
relève d'une programmation Java standard. Dans le même souci de simplifier
la présentation, on
n'envisage que l'utilisation des requêtes GET et POST
du protocole Http.
Le lien avec le serveur Web est mis en place par un moteur de
servlet; nous présentons l'exemple de JServ, moteur lié à
Apache.
I. Présentation des servlets
Les servlets sont un des outils liés à une évolution de la technologie
des serveurs Web, depuis les pages statiques vers les pages dynamiques.
Voici un liste d'outils, présentée dans l'ordre chronologique, à partir
des plus anciens;
- Javascript, Vbscript sur le serveur: la réponse est écrite sous la
forme d'une page Html enrichie par des balises définissant du code
interprété par le serveur.
- passerelle CGI: le serveur Web ne traite pas la requête; il fournit
les informations liées à la requête à un processus qui élabore la
réponse au client. Ce processus est conçu à partir de n'importe quel
langage de programmation. Deux langages interprétés, Perl et Php sont
très répandus.
- Asp (active server page) permet d'avoir un document analogue à une
page Html, enrichi par des balises spéciales, interprétées par le
serveur; ce langage est lié au serveur IIS.
- Servlet est une application écrite en Java qui fournit une réponse à
un client Web
- JSP (java server page) est analogue à Asp, (en version Java) créé par Sun
Les classes utilisées font partie de la bibliothèque jsdk.jar.
II. Premier exemple: création d'une page
Cette servlet montre comment est constituée la page envoyée au client.
Tout d'abord on utilise les paquetages javax.servlet et
javax.servlet.http pour créer une classe Coucou qui dérive de la
classe HttpServlet, dans laquelle on définit en particulier
la méthode doGet().
Cette méthode est activée quand la servlet est directement référencée
dans l'Url d'un lien hypertexte, ou dans l'attribut action d'un
formulaire. Par contre, si l'attribut method a la valeur
POST, alors la méthode doPost() est appelée; ces deux méthodes
ont les mêmes paramètres.
Ces méthodes reçoivent en paramètres:
- un objet HttpServletRequest qui fournit les informations
provenant de la requête
- un objet HttpServletResponse qui permet de communiquer la
réponse au navigateur, via le serveur Web.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class Coucou extends HttpServlet {
//Initialiser les variables globales
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
//Traiter la requête HTTP Get
public void doGet(HttpServletRequest requete,
HttpServletResponse reponse)
throws ServletException, IOException {
reponse.setContentType("text/html");
PrintWriter page = new PrintWriter (reponse.getOutputStream());
page.println("");
page.println("coucou");
page.println("Coucou !!!
");
page.println("");
page.close();
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information Coucou"; }
}
La méthode getServletInfo() est utilisée par le moteur de
servlet quand il veut publier des informations sur la servlet.
III. Cycle de vie d'une servlet
Une servlet est chargée à la première demande d'un client par une Url
apparaissant dans un lien ou par la méthode action d'un formulaire.
Le moteur charge la servlet en lui fournissant un certain nombres
d'arguments (cf. fichier zone.properties), puis :
- invoque la méthode init() qui doit préparer le fonctionnement de
la servlet; en particulier on appellera la méthode init de la classe
HttpServlet par un appel à son constructeur:
- pour chaque appel d'un client, une des méthodes doGet() ou
doPost() est exécutée. Il n'y a qu'une version présente dans
le moteur; ainsi remarquez que la méthode init() n'est pas appelée
lors d'une deuxième utilisation de la même servlet.
De plus il faut être attentif au cas ou un champ de la classe est modifié,
car tous les clients se partagent le même champ. Donc toute modification de
cette nature doit se faire dans un bloc synchronized.
Pour gérer des informations spécifiques à un client, on utilise un objet
HttpSession.
- avant que le moteur arrête la servlet, il appelle la méthode
destroy dans laquelle on pourra placer les sauvegardes
souhaitées,ou les fermetures de connexion à une base par exemple.
La servlet reste chargée tant que le moteur de servlet fonctionne.
IV. Liaison avec un formulaire
Cet exemple montre comment récupérer des informations, transmises depuis
un formulaire. Ce formulaire est un mini-calculateur qui permet la saisie de
2 valeurs numériques, et effectue une opération (choix addition/soustraction)
entre ces valeurs.
La servlet est appellée par l'attribut action de la balise FORM;
les informations lui sont envoyées par la méthode POST.
Le formulaire HTML
.
La servlet
Voici la définition de la classe ServCalc qui effectue le
traitement lié à ce questionnaire.
Dans la programmation la génération de la
page envoyée au client est placée dans un sous-programme (méthode
remplirPage).
Reparquez que les informations sont d'abord récupérées sous
forme chaîne (type String), avant d'être converties en valeur numérique pour
efffectuer un calcul. De façon analogue le résultat, est converti de sa
représentation numérique à la forme chaîne avant d'être envoyé au client.
/** ServCalc.java : micro calculateur */
import javax.servlet.*; // dans jsdk.jar
import javax.servlet.http.*; // dans jsdk.jar
import java.io.*;
public class ServCalc extends HttpServlet {
//Traiter la requête HTTP Post
public void doPost( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// Récupérer, sous forme int, les opérandes
String ch_a=req.getParameter("a");
int a=Integer.parseInt(ch_a);
String valsPar[] = req.getParameterValues("b");
int b=Integer.parseInt(valsPar[0]);
// Récupérer l'opération
String ch_op = req.getParameter("op");
// Calculer le résultat, le mettre sous forme chaîne
String ch_res;
if( ch_op.equals("+") ) ch_res= Integer.toString(a+b);
else if (ch_op.equals("-")) ch_res=Integer.toString(a-b);
else ch_res="erreur";
// Fournir le résultat, via le serveur Http
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, ch_res);
p.close();
}
void remplirPage(PrintWriter p, String res) {
p.println("");
p.println("Résultat");
p.println("");
p.println("Résultat: " + res);
p.println("
");
p.println("");
}
//Initialiser les variables de l'objet ServCalc
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information ServCalc"; }
} // public class ServCalc extends HttpServlet
Les valeurs des champs du formulaire sont récupérées à partir du nom
du champ définissant l'objet du formulaire (attribut name des balises)
par une des méthodes:
- req.getParameter("a") qui fournit une chaîne de
caractères (elle est ensuite convertie en un entier).
- req.getParameterValues("b") qui founit un tableau de chaînes;
ici on est assuré qu'au nom du champ "b" ne correspond qu'une seule
valeur donc l'utilisation de getParameterValues("b") n'est là que comme
l'exemple.
Par contre, dans le cas de plusieurs cases à cocher ayant le même nom,
l'utilisation d'un tableau de chaînes est indispensable et permet de
récupérer toutes les cases cochées.
V. Architecture
1. Les modules exécutables
Le tableau ci-dessous présente les divers modules logiciels du serveur
Web et du moteur de servlets. Les modules liés au navigateur, qui
communiquent avec le serveur, et des modules éventuels utilisés par les
servlets (on peut penser à un serveur de BD) ne sont pas représentés
.
L'exemple de moteur de servlets est Apache JServ; un certain
nombre d'informations reste valable pour tout moteur. On réalise que le
serveur http et le moteur de servlets sont bien séparés; le module
mod_jserv (librairie dynamique, sous-forme dll pour Windows) gère
l'échange d'informations pour le moteur Apache.
|
|
|
|
|
|
|
|
|
Serveur Http
|
|
Moteur de servlets
|
|
<-- -->
|
Apache
|
mod_jserv
|
<-- -->
|
Java
|
bib. jserv
|
servlets
|
<-- -->
|
|
|
auxilliaire du serveur Web, gérant le lien avec
le moteur de servlets
|
|
machine virtuelle
|
A.P.I servlets
(bib. de classes)
|
zone(s) des classes
|
|
|
httpd.conf
|
jserv.conf
|
|
jserv.properties
|
|
zone.properties
|
|
Dans l'architecture Apache, le serveur Web et le moteur de servlets
communiquent par sockets; on pourra donc avoir l'un et l'autre sur des
machines différentes. De plus Apache peut communiquer avec
plusieurs moteurs distincts; il est donc possible que les
servlets soient exécutées par des machines fonctionnant avec des systèmes
d'exploitation différents (solaris, Windows, linux ...).
2. Démarrage
Les fichiers de configurations, qui sont des fichiers 'texte', ne sont
lus qu'une fois, au démarrage des exécutables. Quand on modifie un fichier,
pour que la modification soit prise en compte, il faudra arrêter et relancer
l'exécutable concerné afin de voir la modification prise en compte.
- Au démarrage d'Apache le fichier httpd.conf est utilisé pour
la partie Serveur Web, puis jserv.conf est lu à son tour pour
configurer la liaison avec le moteur (ports, noms de zones ...).
- Au démarrage du moteur de servlets le fichier jserv.properties
permet de définir le port utilisé et la machine autorisée, relativement
à la communication, ainsi que les zones de servlets communiquées à
Apache.
Pour chaque zone un fichier zone.properties précise les
répertoires de la zone, et les arguments à fournir au chargement des
servlets.
3. Emplacement des fichiers
Dans la configuration standard, les fichiers sont rangés dans une
structure arborescente; voici l'emplacement des fichiers de
configurations et des fichiers 'journaux', d'extension *.log:
- Supposons le serveur Web installé dans le
répertoire E:/Apache Group/apache/; alors
le sous-répertoire conf/ contient httpd.conf
(configuration du serveur Web) et jserv.conf (configuration de
la liaison serveur/moteur)
le sous-répertoire logs/ contient access.log et
error.log
- Supposons le moteur de servlets installé dans le répertoire
E:/Apache Group/jserv/
le sous-répertoire conf/ contient:
jserv.properties pour la configuration du moteur
le sous-répertoire logs/ contient les fichiers
'journaux' mod_jserv.log et jserv.log
- Chaque zone de servlets contient un fichier zone.properties pour
la configuration de la zone.
4. Lien URL / servlet
Les termes placés dans une Url ne désignent pas l'emplacement d'un fichier
sur la machine serveur. En fait le serveur et le moteur réalisent des
conversions; ce mécanisme permet de réorganiser les emplacements des
fichiers, si nécessaire, tout en gardant la même Url pour les atteindre.
Soit une servlet de nom 'Ser'; un lien hypertexte tel que:
<A href="http://machine/ref/Ser">
va provoquer l'appel de la méthode doGet() de la servlet Ser; mais pour trouver l'emplacement du fichier Ser.class des conversions sont réalisés.
- le serveur Web situé sur 'machine' reconnait que 'ref' correspond à
une zone de Servlets, de nom 'zoneref' par exemple; il passe le nom de la
zone au moteur de servlets,
- qui convertit 'zoneref' en un ou plusieurs répertoires, dans lesquels
il cherche 'Ser.class'. La classe est alors chargée, sa méthode
init() est exécutée, puis sa méthde doGet().
Si la classe était déjà chargée dans le moteur, sa méthode
doGet(),seule, est appelée.
Depuis un formulaire, suivant la méthode indiquée, doGet() ou doPost()
est appelée; par exemple, si la balise est écrite:
<form method="POST" action="http://machine/ref/Ser">
alors, quand ce formulaire est soumis,
- Le serveur Web situé sur 'machine' reconnait que 'ref' correspond à une
zone 'zoneref' et sollicite le moteur de servlets
- qui fait comme précédemment, à partir de 'zoneref' pour localiser le
fichier Ser.class, puis active la méthode doPost()
de cette classe.
VI. Partage d'informations entre internautes
L'exemple suivant montre que l'information mémorisée dans une servlet est
conservée d'un appel à l'autre, ces appels pouvant provenir de plusieurs
internautes.
Modifions le calculateur pour qu'il puisse cumuler des valeurs.
Avec cette version appelée Cadeau, l'internaute fait appel à la servlet Cadeau,
et obtient une page; il tape alors un nombre et la servlet met à jour un
totalisateur avec la nouvelle valeur fournie.
Cette servlet peut être utilisée par un groupe d'internautes qui veut
faire un cadeau commun; chacun appelle la servlet en indiquant la somme
qu'il verse pour le cadeau; à chaque versement, la somme récoltée est
affichée.
Dans cet exemple la méthode doGet() est utilisée pour le premier
affichage de la page; dans cette page le formulaire sollicite la même servlet,
mais avec la méthode doPost(); Cette méthode va renvoyer la même page,
après exécution des calculs,
est affichée.
/** Cadeau.java servlet 'partagée'
Les méthodes doGet() et doPost() affichent une page.
*/
import javax.servlet.*; // dans jsdk.jar
import javax.servlet.http.*; // dans jsdk.jar
import java.io.*;
public class Cadeau extends HttpServlet {
int total, nval; // informations partagées
final String cf_val="val"; // nom du champ valeur à ajouter
//Initialiser les variables de l'objet Cadeau
public void init(ServletConfig config) throws ServletException {
super.init(config);
total=0; nval=0;
}
//Traiter la requête HTTP Post
public void doPost( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// Récupérer, sous forme int, la valeur à ajouter
String ch_val = req.getParameter(cf_val);
int val=Integer.parseInt(ch_val);
// Mise à jour
total += val; nval+=1;
String ch_total=Integer.toString(total);
String ch_nval=Integer.toString(nval);
// Fournir la page
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, ch_nval, ch_total, ch_val);
p.close();
}
public void doGet( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// Fournir le résultat, via le serveur Http
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, Integer.toString(nval),Integer.toString(total),"");
p.close();
}
// Génère un formulaire, méthode POST; fournit la valeur du champs val
void remplirPage(PrintWriter p, String nval, String tot, String der) {
p.println("Le cadeau");
p.println(" ");
p.println(""
);
p.println(" ");
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information Cadeau"; }
} // public class Cadeau extends HttpServlet
VII. Informations associées à une session
Nous allons convertir la servlet précédente afin que chaque internaute
dispose d'un totalisateur. Pour cela, associons une session à un internaute;
elle va contenir les informations que l'on veut conserver pour identifier
l'internaute.
1. Un exemple
La classe
Compte représente les informations à conserver sur un compte, ici
le nombre de valeurs ajoutées et la somme totale.
La méthode doGet() affiche simplement la page, alors que la méthode
doPost() récupére les informations (nouvelle valeur à ajouter), les
traite, et re-affiche la page.
A chaque appel, la servlet identifie la session ou crée un session et
si celle-ci est nouvelle, un nouveau compte est mémorisé:
HttpSession session = req.getSession(true);
if(session.isNew()) session.setAttribute("compte", new Compte())
Puis, dans tous les cas, le compte de la session est récupéré par
Compte cpt= (Compte) session.getAttribute("compte");
et les deux champs sont mis à jour:
cpt.total += val; cpt.nval+=1;
Voici le source complet.
/** Cumul.java servlet utilisant une session */
import javax.servlet.*; // dans jsdk.jar
import javax.servlet.http.*; // dans jsdk.jar
import java.io.*;
public class Cumul extends HttpServlet {
final String cf_val="val"; // nom du cahmp valeur à ajouter
//Initialiser les variables de l'objet Cumul
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
//Traiter la requête HTTP Post
public void doPost( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// AVANT d'utiliser la sortie reponse
// Créer une session si nécessaire
HttpSession session = req.getSession(true);
if( session.isNew() ) session.setAttribute("compte", new Compte());
// Récupérer, sous forme int, la valeur à ajouter
String ch_val = req.getParameter(cf_val);
int val=Integer.parseInt(ch_val);
// Récupérer le compte de cette session et le mettre à jour
Compte cpt= (Compte) session.getAttribute("compte");
cpt.total += val; cpt.nval+=1;
// Fournir la page
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, ch_val, cpt);
p.close();
}
public void doGet( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// Fournir le résultat, via le serveur Http
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, "",null);
p.close();
}
// Génère un formulaire, méthode POST, qui affiche l'état du compte et
// la dernière valeur ajoutée; puis une zone pour saisir la nouvelle valeur
// est créée.
void remplirPage(PrintWriter p, String der, Compte cpt) {
String tot=new String(), nval=new String();
if( cpt != null ) {
tot=Integer.toString(cpt.total);
nval=Integer.toString(cpt.nval);
}
p.println("calcul de cumul");
p.println("");
p.println(""
);
p.println("");
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information Cumul"; }
class Compte {
int total,nval;
Compte() { total=0; nval=0;; }
}
} // public class Cumul extends HttpServlet
2. Une session
On peut définir une session comme un ensemble d'interactions entre un client
unique et un serveur Web. C'est une notion qui n'est pas naturelle
avec Http, car c'estun protocole 'déconnecté'; c'est à dire qu'après
chaque réponse le serveur coupe la communication avec le client.
Pour établir une session, il faut que le serveur identifie le (même) client;
pour cela:
- le serveur envoie des données spécifiques au client
- le client les renvoie à chaque requête.
Plusieurs techniques sont utilisées:
- réécriture d'Url
- champ de formulaire caché
- par des cookies
-
- dans l'en-tête d'une réponse ou d'une requête
- délai d'expiration
- stocké sur la machine client
- Jsdk et l'objet HttpSession
-
- Object getAttribute(String p0);
- String[] getAttributeNames();
- long getCreationTime();
- long getLastAccessedTime();
- int getMaxInactiveInterval();
- String getId();
- void invalidate();
- boolean isNew();
- void setAttribute(String p0, Object p1);
- void setMaxInactiveInterval(int p0);
VIII. Deux servlets communiquent
Deux servlets sont exécutées:
- la servlet SaisieNumero affiche un formulaire qui déclenche l'appel
de la servlet CumulCompte (méthode doPost())
- la servlet CumulCompte affiche aussi un formulaire qui peut
déclencher:
- l'appel de CumulCompte (méthode doPost()), pour traiter une valeur tapée
- l'appel de SaisieNumero (méthode doGet()), qui termine la gestion du compte
La servlet SaisieNumero
Elle permet, par la méthode doGet(), de saisir un numéro de compte, et appelle
indirectement CumulCompte. Par sa méthode doPost(), elle enregistre
la valeur du compte, par un appel à la méthode gereCompte(), qui est ici
réduite à sa plus simple expression.
/** SaisieNumero.java servlet */
import javax.servlet.*; // dans jsdk.jar
import javax.servlet.http.*; // dans jsdk.jar
import java.io.*;
public class SaisieNumero extends HttpServlet {
final String cf_numcpt="numcpt"; // nom du champ numéro de compte
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
// Affiche le formulaire de saisie de numéro de compte.
public void doGet( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
String numcpt=null; int [] cpt=null;
HttpSession session = req.getSession(false);
if( session != null ) session.invalidate();
// Fournir la page
reponse.setContentType("text/html");
// reponse.setHeader("pragma","no-cache");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, "");
p.close();
}
//Traiter la requête Post utilisée par CumulCompte pour actualiser
public void doPost( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
String numcpt=null, msg=""; int [] cpt=null;
HttpSession session = req.getSession(false);
if( session != null ) {
numcpt = (String)session.getAttribute(cf_numcpt);
cpt = (int [])session.getAttribute("compte");
if( numcpt != null ) msg += gereCompte(numcpt, cpt);
session.invalidate();
}
// Fournir la page
reponse.setContentType("text/html");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, msg);
p.close();
}
void remplirPage(PrintWriter p, String msg) {
String tot=new String(), nval=new String();
p.println("intro de compte");
p.println("");
p.println(""+msg+"
\n
");
p.println("Gestion de compte
");
p.println(" \n"
);
p.println("");
}
String gereCompte( String numcpt, int cpt[]) {
return "Le compte "+numcpt+" (total "+cpt[0]+") est mis à jour";
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information SaisieNumero"; }
} // public class SaisieNumero extends HttpServlet
La servlet CumulCompte
Elle est sollicitée par un formulaire(POST) qui fournit un numéro de compte.
Elle affiche alors une page permettant de saisir une somme à accumuler sur
ce compte; cette page présente:
- un bouton pour inscrire la somme saisie sur le compte
- un autre bouton permettant de clore la mise à jour du compte, et
revenir à la page affichée par SaisieNumero.
Voici le texte de la servlet CumulCompte.
/** CumulCompte.java servlet 'partagée'*/
import javax.servlet.*; // dans jsdk.jar
import javax.servlet.http.*; // dans jsdk.jar
import java.io.*;
public class CumulCompte extends HttpServlet {
final int I_TOTAL=0, I_NVAL=1;
final String cf_val="val"; // nom du champ valeur à ajouter
final String cf_numcpt="numcpt"; // nom du champ numéro de compte
String numcpt;
//Initialiser les variables de l'objet CumulCompte
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
//Traiter la requête HTTP Post
public void doPost( HttpServletRequest req,
HttpServletResponse reponse)
throws ServletException, IOException {
// Créer une session si nécessaire
String ch_val = ""; int cpt[]=null; String msg="";
HttpSession session = req.getSession(true);
// Si on dispose d'un champ numero de compte, cela correspond
// au premier 'appel': on crée un session et on récupère l'état
// du compte (ici on part de zéro)
String ch = req.getParameter(cf_numcpt);
if( ch != null) {
numcpt=ch; cpt = new int[]{0,0};
session.setAttribute(cf_numcpt, ch);
session.setAttribute("compte", cpt);
msg=" ch != null " + ch;
}
else {
// Récupérer, sous forme int, la valeur à ajouter
int val;
ch_val = req.getParameter(cf_val);
try { val=Integer.parseInt(ch_val);}
catch (NumberFormatException e) { val=0; }
// Mise à jour
cpt = (int [])session.getAttribute("compte");
if(cpt!=null) { cpt[I_TOTAL] += val; cpt[I_NVAL] += 1;
msg=" ch == null cpt[0]=" + cpt[0]; }
else msg="cpt==null";
}
// Fournir la page
reponse.setContentType("text/html");
// reponse.setHeader("pragma","no-cache");
PrintWriter p = new PrintWriter( reponse.getOutputStream());
remplirPage(p, numcpt, ch_val, cpt);
p.close();
}
// Génère un formulaire, méthode POST qui fournit la valeur du champs val
void remplirPage(PrintWriter p, String numcpt, String der, int cpt[]) {
String tot=new String(), nval=new String();
if( cpt == null ) {tot=""; nval=""; }
else {
tot=Integer.toString(cpt[I_TOTAL]);
nval=Integer.toString(cpt[I_NVAL]);
}
p.println("calcul de cumul");
p.println("");
p.println("Compte numéro "+numcpt+"
");
p.println(" \n"
);
p.println(""
);
p.println("");
}
//Obtenir les informations de servlet
public String getServletInfo() { return "Information CumulCompte"; }
} // public class CumulCompte extends HttpServlet
Références
Un livre
Andrew Patner, Programmation Java, coté Serveur, Eyrolles
Quelques adresses sur le Web: