3. Introduzione ai servlet Java e alle pagine JSP
Questo capitolo contiene vari esempi di servlet e pagine JSP. Sono stati testati con il server Tomcat, che gira sulla porta 8080. Seguendo i link nella home page, puoi accedere agli esempi di servlet e pagine JSP. La maggior parte degli esempi riportati di seguito è tratta dagli esempi di Tomcat. Per provarli, è sufficiente avviare Tomcat, digitare l'URL http://localhost:8080 in un browser e seguire il link del servlet.
![]() | ![]() |
3.1. Servlet Java
3.1.1. Invio di contenuti HTML a un client Web
Esaminiamo l'esempio Hello World riportato sopra. Il servlet è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
Quando questo servlet viene eseguito, viene visualizzato quanto segue:

Si notino i seguenti punti:
- È necessario importare classi speciali per i servlet:
import javax.servlet.*;
import javax.servlet.http.*;
La libreria javax.servlet non è sempre inclusa di default nel JDK. In questo caso, è possibile scaricarla direttamente dal sito web di Sun.
- Un servlet estende la classe HttpServlet
public class HelloWorld extends HttpServlet {
- Una richiesta GET inviata al servlet viene gestita dal metodo doGet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Allo stesso modo, una richiesta POST inviata al servlet viene gestita dal metodo doPost
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- L'oggetto richiesta HttpServletRequest è l'oggetto che ci dà accesso alla richiesta effettuata dal client web. La risposta del servlet verrà inviata tramite l'oggetto risposta HttpServletResponse
- L'oggetto response ci permette di impostare le intestazioni HTTP che verranno inviate al client. Ad esempio, l'intestazione Content-Type: text/html viene impostata qui tramite:
- Per inviare la risposta al client, il servlet utilizza un flusso di output fornito dall'oggetto response:
- Una volta ottenuto questo flusso di output, il codice HTML viene scritto su di esso e quindi inviato al client:
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
3.1.2. Recupero dei parametri inviati da un client web
L'esempio seguente mostra come un servlet possa recuperare i parametri inviati dal client web. Un modulo di input:

La risposta inviata dal servlet:

Il codice sorgente del servlet è il seguente:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
Si notino le seguenti modifiche rispetto all'esempio precedente:
- I parametri inviati dal browser vengono recuperati come segue:
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
Il metodo request.getParameter("parameterName") restituisce un puntatore nullo se il parametro "parameterName" non è presente tra i parametri inviati dal client web.
- Il modulo specifica che il browser deve inviare i parametri utilizzando il metodo POST
- I parametri ricevuti saranno elaborati dal metodo doPost del servlet. In questo caso, tale metodo richiama semplicemente il metodo doGet. Pertanto, questo servlet elabora i valori del modulo indipendentemente dal fatto che siano stati inviati tramite GET o POST.
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
3.1.3. Recupero delle intestazioni HTTP inviate da un client web
Il seguente servlet mostra come recuperare le intestazioni HTTP inviate dal client web:

Il codice sorgente del servlet è il seguente:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestHeaderExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println(name + " = " + value);
}
}
}
Punti da tenere presenti:
- Sono l'oggetto `request` e il suo metodo `getHeaderNames` a fornirci l'accesso alle intestazioni HTTP inviate dal browser sotto forma di enumerazione:
- Il metodo request.getHeader("header") consente di recuperare una specifica intestazione HTTP. L'esempio sopra ne fornisce alcune. Tieni presente che le intestazioni qui mostrate sono inviate dal browser. Anche il server ha le proprie intestazioni HTTP, che a volte rispecchiano quelle del browser. Le intestazioni HTTP inviate dal browser hanno lo scopo di informare il server sulle capacità del browser.
Intestazione | Significato |
Identità del browser | |
Tipi MIME accettati dal browser. Ad esempio, image/gif significa che il browser è in grado di gestire immagini in formato GIF | |
nel formato host:porta. Indica quale macchina e quale porta il browser vuole contattare. | |
formato di codifica accettato dal browser per i documenti inviati dal server. Pertanto, se un server dispone di un documento in formato normale, non compresso, e di un altro in formato gzip, e il browser ha indicato di poter gestire il formato gzip, allora il server può inviare il documento in formato gzip per risparmiare larghezza di banda. | |
Lingue accettate dal browser. Se un server dispone dello stesso documento in più lingue, invierà la versione nella lingua accettata dal browser. | |
L'URL richiesto dal browser | |
La modalità di connessione richiesta dal browser. Keep-alive significa che il server non deve chiudere la connessione dopo aver fornito la pagina richiesta al browser. Se il browser rileva che la pagina ricevuta contiene, ad esempio, dei link a delle immagini, può inviare nuove richieste al server per recuperarle senza dover stabilire una nuova connessione. Il browser provvederà quindi a chiudere la connessione una volta ricevuti tutti gli elementi della pagina. |
3.1.4. Recupero delle informazioni sull'ambiente
Il seguente servlet mostra come accedere alle informazioni sull'ambiente di esecuzione del servlet. Alcune di queste informazioni vengono inviate come intestazioni HTTP dal browser e possono quindi essere recuperate utilizzando il metodo precedente.

Il codice del servlet è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestInfo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Request Information Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Request Information Example</h3>");
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
Le informazioni vengono ottenute qui utilizzando vari metodi:
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
Ecco un elenco di alcuni dei metodi disponibili e dei loro significati:
metodo | significato |
il nome del server web | |
la porta operativa del server web | |
il metodo GET o POST utilizzato dal browser per effettuare la richiesta | |
il nome del computer client da cui il browser ha effettuato la richiesta | |
l'indirizzo IP di quella stessa macchina | |
il tipo di contenuto inviato dal browser (intestazione HTTP Content-Type) | |
il numero di caratteri inviati dal browser (intestazione HTTP Content-Length) | |
la versione del protocollo HTTP richiesta dal browser | |
L'URI richiesto dal browser. Corrisponde alla parte dell'URL che segue l'identificatore host:port in http://hote:port/URI |
3.1.5. Creare un servlet con JBuilder, distribuirlo con Tomcat
Descriveremo ora come creare ed eseguire un servlet Java. Utilizzeremo due strumenti: JBuilder per compilare il servlet e Tomcat per eseguirlo. Tomcat da solo potrebbe essere sufficiente. Tuttavia, offre funzionalità di debug limitate. Riprenderemo l'esempio sviluppato in precedenza che visualizza i parametri ricevuti dal server. Il servlet invia innanzitutto il seguente modulo di input:

La risposta inviata dal servlet:

Il codice sorgente del servlet è il seguente:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
- Creare un progetto denominato myRequestParamExample utilizzando JBuilder e includervi il programma myRequestParamExample.java precedente.
- Durante la compilazione, potresti riscontrare il seguente problema: JBuilder potrebbe non disporre della libreria javax.servlet necessaria per compilare i servlet. In questo caso, devi configurare JBuilder per utilizzare librerie di classi aggiuntive. La procedura è descritta nelle appendici di questo documento per JBuilder 7. L'abbiamo riprodotta parzialmente qui:
- Abilitare l'opzione Strumenti/Configura JDK

Nella sezione "Impostazioni JDK" qui sopra, il campo "Nome" visualizza normalmente "JDK 1.3.1". Se si dispone di un JDK più recente, utilizzare il pulsante "Modifica" per specificarne la directory di installazione. In questo caso, abbiamo specificato la directory E:\Program Files\jdk14, dove è stato installato un JDK 1.4. D'ora in poi, JBuilder utilizzerà questo JDK per le compilazioni e le esecuzioni. Nella sezione (Class, Source, Documentation), vedrai un elenco di tutte le librerie di classi che JBuilder analizzerà: in questo caso, le classi del JDK 1.4. Le classi incluse in questo JDK non sono sufficienti per lo sviluppo web Java. Per aggiungere ulteriori librerie di classi, usa il pulsante Add e seleziona i file .jar aggiuntivi che desideri utilizzare. I file .jar sono librerie di classi. Tomcat 4.x include tutte le librerie di classi necessarie per lo sviluppo web. Si trovano in <tomcat>\common\lib, dove <tomcat> è la directory di installazione di Tomcat:

Utilizzando il pulsante Aggiungi, aggiungeremo queste librerie, una per una, all'elenco delle librerie analizzate da JBuilder:

D'ora in poi, è possibile compilare programmi Java conformi allo standard J2EE, inclusi i servlet Java. JBuilder viene utilizzato solo per la compilazione; l'esecuzione viene successivamente gestita da Tomcat.
- Ora è possibile compilare il programma myRequestParamExample.java e generare il servlet myRequestParamExample.class. Dove deve essere collocato questo servlet? Se la configurazione iniziale di Tomcat non è stata modificata, i file .class del servlet devono essere collocati in <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
- Verifica che Tomcat sia in esecuzione e utilizza un browser per richiedere l'URL http://localhost:8080/examples/servlet/myRequestParamExample:

3.1.6. Esempi
Per gli esempi seguenti, abbiamo utilizzato il metodo descritto sopra:
- compilazione del file sorgente XX.java del servlet con JBuilder
- distribuzione del servlet XX.class in <tomcat>\webapps\examples\WEB-INF\classes
- Con Tomcat in esecuzione, inserire l'URL http://localhost:8080/examples/servlet/XX in un browser
3.1.6.1. Generazione dinamica di moduli - 1
Useremo come esempio la generazione di un modulo con un solo controllo: un elenco. Il contenuto di questo elenco viene costruito dinamicamente utilizzando valori prelevati da un array. In realtà, questi valori vengono spesso recuperati da un database. Il modulo è il seguente:

Se si fa clic su Invia nell'esempio sopra riportato, si ottiene la seguente risposta:

Si noti che l'URL che restituisce la risposta è lo stesso di quello che visualizza il modulo. In questo caso, abbiamo un servlet che elabora la risposta al modulo che ha inviato. Si tratta di uno scenario comune. Il codice HTML del modulo è il seguente:
<html>
<head><title>Génération de formulaire</title></head>
<body>
<h3>Choississez un nombre</h3><hr>
<form method="POST">
<select name="cmbValeurs" size="1">
<option>zéro</option>
<option>un</option>
<option>deux</option>
<option>trois</option>
<option>quatre</option>
<option>cinq</option>
<option>six</option>
<option>sept</option>
<option>huit</option>
<option>neuf</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Si noti che i valori inviati dal modulo vengono inviati utilizzando il metodo POST. Il codice HTML della risposta:
<html>
<head><title>Voici ma réponse</title></head>
<body>
Vous avez choisi le nombre<h2>neuf</h2>
</body>
</html>
Il codice del servlet che genera questo modulo e questa risposta è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener1 extends HttpServlet{
// variables d'instance
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choississez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\">";
private final String HTML3="</form>\n</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
for (int i=0;i<valeurs.length;i++){
out.println("<option>"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// fin formulaire
out.println(HTML2+HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on récupère le choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) doGet(request,response);
// on prépare la réponse
String réponse="<html><head><title>Voici ma réponse</title></head>";
réponse+="<body>Vous avez choisi le nombre <h2>"+choix+"</h2></body></html>";
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
out.println(réponse);
}//POST
}//classe
Il metodo doGet viene utilizzato per generare il modulo. Esiste una parte dinamica, ovvero il contenuto dell'elenco, che in questo caso proviene da un array. Il metodo doPost viene utilizzato per generare la risposta. In questo caso, l'unica parte dinamica è il valore della selezione effettuata dall'utente nell'elenco del modulo. Questo valore viene ottenuto tramite request.getParameter("cmbValeurs"), dove cmbValeurs è il nome HTML dell'elenco:
Infine, si notino i seguenti punti:
- Il browser invia i valori del modulo al servlet che lo ha generato poiché il tag <form> non ha l'attributo <action>. In questo caso, il browser invia i dati inseriti nel modulo all'URL che lo ha fornito.
- Il tag <form> specifica che i dati del modulo devono essere inviati utilizzando il metodo POST. Questo è il motivo per cui questi valori vengono recuperati dal metodo doPost del servlet.
3.1.6.2. Generazione dinamica di moduli - 2
Riprendiamo l'esempio precedente e lo modifichiamo come segue. Il modulo rimane lo stesso:

Ma la risposta è diversa:

Nella risposta viene restituito il modulo, con il numero scelto dall'utente indicato sotto di esso. Inoltre, questo numero è quello che appare come selezionato quando viene visualizzato l'elenco. L'utente può quindi scegliere un altro numero:

quindi fare clic su Invia. Riceve la seguente risposta:

Il codice per il servlet denominato gener2.java è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener2 extends HttpServlet{
// variables d'instance
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// suite formulaire
out.println(HTML2);
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// fin du formulaire
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on renvoie sur GET
doGet(request,response);
}//POST
}//classe
Il metodo doGet fa tutto: crea il modulo che invia al client ed elabora i valori che il client restituisce. Vale la pena notare i seguenti punti:
- Verifichiamo se il parametro cmbValeurs ha un valore.
- In tal caso, durante la creazione del contenuto dell'elenco, ogni elemento viene confrontato con la selezione dell'utente per impostare l'attributo selected sull'elemento scelto dall'utente: <option selected>elemento</option>. Inoltre, il valore selezionato viene visualizzato sotto il modulo.
3.1.6.3. Generazione dinamica di moduli - 3
Stiamo affrontando lo stesso problema di prima, ma questa volta i valori vengono recuperati da un database. Nel nostro esempio, si tratta di un database MySQL:
- il database si chiama dbValues
- il suo proprietario è admDbValeurs con la password mdpDbValeurs
- il database ha una singola tabella denominata tvaleurs
- questa tabella ha un solo campo di tipo intero chiamato value
E:\Program Files\EasyPHP\mysql\bin>mysql --database=dbValeurs --user=admDbValeurs --password=mdpDbVa
leurs
mysql> show tables;
+---------------------+
| Tables_in_dbValeurs |
+---------------------+
| tvaleurs |
+---------------------+
1 row in set (0.00 sec)
mysql> describe tvaleurs;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| valeur | int(11) | | | 0 | |
+--------+---------+------+-----+---------+-------+
mysql> select * from tvaleurs;
+--------+
| valeur |
+--------+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 6 |
| 5 |
| 7 |
| 8 |
| 9 |
+--------+
10 rows in set (0.00 sec)
Il database MySQL dbValeurs è stato reso accessibile tramite un driver ODBC per MySQL. Il suo DSN (Data Source Name) è odbc-valeurs. Il codice del servlet è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener3 extends HttpServlet{
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// valeurs de liste
private String[] valeurs=null;
// msg d'erreur
private String msgErreur=null;
// code HTML
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on indique au client le type de document envoyé
response.setContentType("text/html");
// flux de sortie
PrintWriter out=response.getWriter();
// l'initialisation de la servlet s'est-elle bien passée ?
if (msgErreur!=null){
// il y a eu une erreur - on génère une page d'erreur
out.println("<html><head><title>"+title+"</title></head>");
out.println("<body><h3>Application indisponible ("+msgErreur+
")</h3></body></html>");
return;
}//if
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// on envoie le formulaire
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// suite formulaire
out.println(HTML2);
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// fin du formulaire
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on renvoie sur GET
doGet(request,response);
}//POST
// initialisation de la servlet
public void init(){
// remplit le tableau des valeurs à partir d'une base de données ODBC
// de nom DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connexion à la base ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// objet Statement
st=connexion.createStatement();
// exécution requête select pour récupérer les valeurs
rs=st.executeQuery("select valeur from Tvaleurs");
// les valeurs sont récupérées et mises dans un tableau dynamique
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// on enregistre la valeur dans la liste
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation liste --> tableau
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problème
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
}//classe
I punti chiave da tenere presenti sono i seguenti:
- Un servlet può essere inizializzato tramite un metodo la cui firma deve essere public void init(). Questo metodo viene eseguito solo quando il servlet viene caricato inizialmente
- Una volta caricato, un servlet rimane sempre in memoria. Ciò significa che dopo aver servito un client, non viene scaricato. Risponde quindi più rapidamente alle richieste dei client.
- Nel nostro servlet, è necessario recuperare un elenco di valori da un database. Poiché questo elenco non cambia nel tempo, il metodo init è il momento ideale per recuperarlo. Il servlet accede quindi al database una sola volta, al momento del caricamento iniziale, anziché ad ogni richiesta del client.
- L'accesso al database potrebbe fallire. Il metodo init del nostro servlet imposta un messaggio di errore, msgErreur, in caso di fallimento. Questo messaggio viene controllato nel metodo doGet e, se si è verificato un errore, doGet genera una pagina che lo segnala.
- L'implementazione del metodo init utilizza l'accesso standard al database con driver ODBC-JDBC. Se necessario, si invita il lettore a consultare i metodi per l'accesso ai database JDBC.
Quando il servlet viene eseguito e il server MySQL non è stato avviato, viene visualizzata la seguente pagina di errore:
![]()
Se ora si avvia il server MySQL, verrà visualizzata la pagina:

Se selezioni il numero 6 e clicchi su "Invia":

3.1.6.4. Recupero dei valori da un modulo
Riprenderemo un esempio che abbiamo già visto, quello del seguente modulo web:

Il codice HTML del modulo balises2.htm è il seguente:
<html>
<head>
<title>balises</title>
<script language="JavaScript">
function effacer(){
alert("Vous avez cliqué sur le bouton Effacer");
}//effacer
</script>
</head>
<body background="/images/standard.jpg">
...
<form method="POST" action="http://localhost:8080/examples/servlet/parameters">
<table border="0">
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" value="Oui" name="R1">Oui
<input type="radio" name="R1" value="non" checked>Non
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="C1" value="un">1
<input type="checkbox" name="C2" value="deux" checked>2
<input type="checkbox" name="C3" value="trois">3
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="txtSaisie" size="20" value="qqs mots">
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="txtMdp" size="20" value="unMotDePasse">
</td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td>
<textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
</td>
</tr>
<tr>
<td>combo</td>
<td>
<select size="1" name="cmbValeurs">
<option>choix1</option>
<option selected>choix2</option>
<option>choix3</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix simple</td>
<td>
<select size="3" name="lst1">
<option selected>liste1</option>
<option>liste2</option>
<option>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix multiple</td>
<td>
<select size="3" name="lst2" multiple>
<option selected>liste1</option>
<option>liste2</option>
<option selected>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>bouton</td>
<td>
<input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
</td>
</tr>
<tr>
<td>envoyer</td>
<td>
<input type="submit" value="Envoyer" name="cmdRenvoyer">
</td>
</tr>
<tr>
<td>rétablir</td>
<td>
<input type="reset" value="Rétablir" name="cmdRétablir">
</td>
</tr>
</table>
<input type="hidden" name="secret" value="uneValeur">
</form>
</body>
</html>
Il tag <form> nel modulo è stato definito come segue:
Il browser invierà i valori del modulo all'URL http://localhost:8080/examples/servlet/parameters, che è l'URL di un servlet gestito da Tomcat e che visualizza i valori del modulo precedente. Se chiamiamo direttamente il servlet dei parametri, otteniamo i seguenti risultati:

Se il modulo balises2.htm inserito è questo:

e si fa clic sul pulsante Invia, questa volta il servlet viene chiamato con i parametri. Restituisce quindi la seguente risposta:

Questa risposta contiene chiaramente i valori inseriti nel modulo. Il codice del servlet è il seguente:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class parameters extends HttpServlet{
// variables d'instance
String title="Récupération des paramètres d'un formulaire";
private String getParameter(HttpServletRequest request, String contrôle){
// rend la valeur request.getParameter(contrôle) ou "" si elle n'existe pas
String valeur=request.getParameter(contrôle);
if(valeur==null) return ""; else return valeur;
}//getParameter
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// on commence par récupérer les paramètres du formulaire
String R1=getParameter(request,"R1");
String C1=getParameter(request,"C1");
String C2=getParameter(request,"C2");
String C3=getParameter(request,"C3");
String txtSaisie=getParameter(request,"txtSaisie");
String txtMdp=getParameter(request,"txtMdp");
String areaSaisie=getParameter(request,"areaSaisie");
String[] lignes=areaSaisie.split("\\r\\n");
String cmbValeurs=getParameter(request,"cmbValeurs");
String lst1=getParameter(request,"lst1");
String[] lst2=request.getParameterValues("lst2");
String secret=getParameter(request,"secret");
// on indique le contenu du document
response.setContentType("text/html");
// on envoie le document
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
out.println("<hr>");
out.println("<table border=\"1\">");
out.println("<tr><td>R1</td><td>"+R1+"</td></tr>");
out.println("<tr><td>C1</td><td>"+C1+"</td></tr>");
out.println("<tr><td>C2</td><td>"+C2+"</td></tr>");
out.println("<tr><td>C3</td><td>"+C3+"</td></tr>");
out.println("<tr><td>txtSaisie</td><td>"+txtSaisie+"</td></tr>");
out.println("<tr><td>txtMdp</td><td>"+txtMdp+"</td></tr>");
for(int i=0;i<lignes.length;i++)
out.println("<tr><td>areaSaisie["+i+"]</td><td>"+lignes[i]+"</td></tr>");
out.println("<tr><td>cmbValeurs</td><td>"+cmbValeurs+"</td></tr>");
out.println("<tr><td>lst1</td><td>"+lst1+"</td></tr>");
if(lst2==null)
out.println("<tr><td>lst2</td><td></td></tr>");
else
for(int i=0;i<lst2.length;i++)
out.println("<tr><td>lst2</td><td>"+lst2[i]+"</td></tr>");
out.println("<tr><td>secret</td><td>"+secret+"</td></tr>");
out.println("</body>");
out.println("</html>");
}
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// renvoie sur GET
doGet(request,response);
}
}
Questo codice utilizza le tecniche presentate in precedenza in un altro esempio. Si notino i seguenti due punti:
- Il controllo lst2 è un elenco a selezione multipla, quindi è possibile selezionare più voci. È proprio quello che succede nel nostro esempio, dove sono state selezionate le voci list1 e list3. I valori di lst2 sono stati inviati dal browser al server nella forma lst2=liste1&lst2=liste3. Il servlet Java può recuperare questi valori in un array utilizzando il metodo getParameterValues: in questo caso, request.getParameterValues("lst2") restituisce un array di due stringhe ["liste1", "liste3"].
- Il controllo areaSaisie è un campo di immissione multiriga. request.getParameter("areaSaisie") restituisce il contenuto del campo come singola stringa. Se si desidera estrarre le singole righe al suo interno, è possibile utilizzare il metodo split della classe String. Il codice seguente
recupera le righe dal campo di immissione. Queste righe sono terminate dai caratteri \r\n (0D0A).
Per eseguire i test, abbiamo:
- creato e compilato i parametri del servlet utilizzando JBuilder, come spiegato in precedenza
- posizionato la classe generata in <tomcat>\webapps\examples\WEB-INF\classes, dove <tomcat> è la directory di installazione di Tomcat.
- richiesto l'URL http://localhost:81/html/balises2.htm, il cui codice è stato presentato sopra
- compilato il modulo e cliccato sul pulsante Invia.
3.1.6.5. Recupero delle intestazioni HTTP da un client web
Useremo lo stesso esempio di prima, ma in risposta al client web che ha inviato i valori del modulo, rispediremo le intestazioni HTTP che ha inviato contemporaneamente. Apporteremo una sola modifica al nostro modulo:
I valori del modulo verranno inviati utilizzando il metodo GET a un servlet Java denominato headers situato in <tomcat>\webapps\examples\WEB-INF\classes. Il servlet headers è stato creato e compilato con JBuilder:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class headers extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
// determine the nature of the document
response.setContentType("text/html");
// we obtain a writing flow
PrintWriter out = response.getWriter();
// display header list HTTP
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println("<b>"+name + "</b> = " + value + "<br>");
}
}//GET
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
//GET
doGet(request,response);
}//POST
}
Richiediamo l'URL http://localhost:81/html/balises2.htm e clicchiamo su Invia senza modificare il modulo. Otteniamo la seguente risposta:

Si noti l'URL parametrizzato nella barra degli indirizzi del browser, che mostra il metodo (GET) utilizzato per trasmettere i parametri. Useremo lo stesso esempio, ma cambieremo il metodo di invio dei parametri (POST):
Otteniamo la seguente nuova risposta:

Si notino le intestazioni HTTP content-type e content-length, caratteristiche di una richiesta POST. Inoltre, si noti che i valori del modulo non compaiono più nel campo Indirizzo del browser.
3.2. Pagine JSP
Le JSP (Java Server Pages) sono un altro modo per scrivere applicazioni per server web. In realtà, queste pagine JSP vengono tradotte in servlet prima di essere eseguite, quindi si tratta essenzialmente di tecnologia servlet. Le pagine JSP consentono di evidenziare in modo più chiaro la struttura delle pagine HTML generate. Di seguito sono riportati alcuni esempi, alcuni dei quali sono accessibili seguendo il link JSP sulla homepage di Tomcat:
![]() | ![]() |
3.2.1. Recupero delle informazioni sull'ambiente
Qui riprendiamo un esempio già trattato in precedenza con un servlet: la visualizzazione delle variabili d'ambiente di un servlet. Si tratta dell'esempio "snoop" tratto dagli esempi JSP:

Il codice sorgente della pagina JSP si trova in <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) o <tomcat>\examples\jsp\snp\snoop.jsp (Tomcat 4.x)
<html>
<!--
Copyright (c) 1999 The Apache Software Foundation. All rights
reserved.
-->
<body bgcolor="white">
<h1> Request Information </h1>
<font size="4">
JSP Request Method: <%= request.getMethod() %>
<br>
Request URI: <%= request.getRequestURI() %>
<br>
Request Protocol: <%= request.getProtocol() %>
<br>
Servlet path: <%= request.getServletPath() %>
<br>
Path info: <%= request.getPathInfo() %>
<br>
Path translated: <%= request.getPathTranslated() %>
<br>
Query string: <%= request.getQueryString() %>
<br>
Content length: <%= request.getContentLength() %>
<br>
Content type: <%= request.getContentType() %>
<br>
Server name: <%= request.getServerName() %>
<br>
Server port: <%= request.getServerPort() %>
<br>
Remote user: <%= request.getRemoteUser() %>
<br>
Remote address: <%= request.getRemoteAddr() %>
<br>
Remote host: <%= request.getRemoteHost() %>
<br>
Authorization scheme: <%= request.getAuthType() %>
<hr>
The browser you are using is <%= request.getHeader("User-Agent") %>
<hr>
</font>
</body>
</html>
Si prega di tenere presente quanto segue:
- Questo codice è molto simile all'HTML. Tuttavia, contiene i tag <%= espressione %> che sono specifici del linguaggio JSP. Il compilatore JSP sostituisce l'intero tag nel testo HTML con il valore di espressione.
- Questo esempio utilizza i metodi dell'oggetto Java `request`, che è l'oggetto `request` già incontrato nello studio dei servlet. Si tratta quindi di un oggetto `HttpServletRequest`. Pertanto, il tag `<%= request.getRemoteHost() %>` verrà sostituito nel codice HTML dal nome del computer client web che ha effettuato la richiesta.
- È possibile ottenere lo stesso risultato con un servlet, ma in questo caso la struttura della pagina web è più evidente.
3.2.2. Recupero dei parametri inviati dal client web
Qui riprendiamo l'esempio che abbiamo studiato in precedenza con un servlet. Al browser viene presentato un modulo:

In risposta alla richiesta sopra riportata, il browser riceve la seguente pagina:

Il codice della pagina JSP è il seguente:
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- code HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
- Sebbene vediamo nuovamente il tag <%= expression %>, come nell'esempio precedente, compare un nuovo tag: <% Java instructions; %>. Il tag <% introduce il codice Java. Questo codice termina quando incontra il tag di chiusura %>.
- L'intero codice precedente (HTML + JSP) verrà convertito in un servlet Java. Sarà racchiuso in un unico metodo, noto come metodo principale della pagina JSP. Questo è il motivo per cui le variabili Java dichiarate all'inizio della pagina JSP sono accessibili nelle altre parti del codice JSP sparse nell'HTML: queste variabili e questi segmenti di codice faranno parte dello stesso metodo Java. Tuttavia, se il nostro codice JSP contenesse dei metodi, le variabili `title`, `firstname` e `lastname` non sarebbero accessibili al loro interno a causa dell'ambito dei metodi. Dovremmo renderle variabili globali o passarle come parametri ai metodi. Torneremo su questo argomento più avanti.
- Per includere parti dinamiche nel codice HTML, sono possibili due metodi: <%= espressione %> o out.println(espressione). L'oggetto out è un flusso di output simile a quello con lo stesso nome incontrato negli esempi di servlet, ma non dello stesso tipo: è un oggetto JspWriter, non un PrintWriter. Consente di scrivere nel flusso HTML utilizzando i metodi print e println.
- La pagina JSP riflette meglio la struttura della pagina HTML generata rispetto al servlet equivalente.
3.2.3. Tag JSP
Ecco un elenco dei tag che potresti incontrare in una pagina JSP e il loro significato.
tag | Significato |
Commento HTML. Inviato al client. | |
Commento JSP. Non inviato al client. | |
Dichiara variabili e metodi globali. Le variabili saranno disponibili in tutti i metodi | |
Il valore dell'espressione verrà inserito nella pagina HTML al posto del tag | |
contiene codice Java che farà parte del metodo main della pagina JSP | |
imposta gli attributi per la pagina JSP. Ad esempio: import="java.util.*,java.sql.*" per specificare le librerie richieste dalla pagina JSP extends="aParentClass" per far sì che la pagina JSP erediti da un'altra classe |
3.2.4. Oggetti impliciti JSP
Negli esempi precedenti abbiamo incontrato due oggetti non dichiarati: request e out. Si tratta di due degli oggetti definiti automaticamente nel servlet in cui viene convertita la pagina JSP. Sono chiamati oggetti impliciti o predefiniti. Ne esistono altri, ma questi sono i più comunemente utilizzati insieme all'oggetto response:
oggetto | significato |
l'oggetto attraverso il quale è possibile accedere alla richiesta del client web (getParameter, getParameterNames, getParameterValues) | |
L'oggetto utilizzato per costruire la risposta del server web al client. Consente di impostare le intestazioni HTTP da inviare al client web. | |
il flusso di output che ci permette di inviare codice HTML al client (print, println) |
3.2.5. Convertire una pagina JSP in un servlet
Rivediamo il codice JSP di myRequestParamExample.jsp:
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- code HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
Quando il browser richiede questa pagina JSP dal server Tomcat, il server la convertirà in un servlet. Se l'URL richiesto è
http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp, Tomcat 4.x inserirà il servlet generato nella directory <tomcat>\work\localhost\examples\jsp\perso\intro:

Questo nome riflette l'URL della pagina JSP, http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp. Come mostrato sopra, possiamo accedere al codice Java del servlet generato per la pagina JSP. Nel nostro esempio, è il seguente:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class myRequestParamExample$jsp extends HttpJspBase {
static {
}
public myRequestParamExample$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
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;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// variables local to the main procedure
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
out.write("\r\n\r\n<!-- code HTML -->\r\n<html>\r\n <head>\r\n <title>");
out.print( title );
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
out.print( title );
out.write("</h3>\r\n ");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.write("\r\n <P>\r\n <form method=\"POST\">\r\n firstname= <input type=\"text\" size=\"20\" name=\"firstname\">\r\n <br>\r\n lastname= <input type=\"text\" size=\"20\" name=\"lastname\">\r\n <br>\r\n <input type=\"submit\">\r\n </form>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
Il codice generato è piuttosto complesso. Ci concentreremo solo sui seguenti punti:
- Il metodo main del servlet è il seguente:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
Questo metodo viene chiamato all'avvio del servlet. Come si può notare, accetta due parametri: la richiesta del client e un oggetto di risposta per generare la risposta al client web.
- Nel metodo main, viene dichiarato e quindi inizializzato un oggetto JspWriter denominato out. Questo oggetto viene utilizzato per inviare codice HTML al client tramite l'istruzione out.print("codice HTML").
- Il codice Java
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
è stato incluso per intero nel metodo principale _jspService del servlet. Lo stesso vale per qualsiasi codice situato all'interno dei tag <%… %>
- Il codice HTML della pagina JSP viene generato utilizzando le istruzioni out.print("codeHTML") o out.write(...). Ad esempio
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
- In questo esempio, non ci sono metodi oltre al metodo principale _jspService.
3.2.6. Metodi e variabili globali di una pagina JSP
Si consideri la seguente pagina JSP:
<%!
// la balise précédente démarre la partie variables et méthodes globales
// cette partie sera reprise sans modification dans la servlet
// une variabe globale
String prenom="inconnu";
// une méthode
private String sonChien(){
return "milou";
}//sonChien
// une autre méthode
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// fin de la partie globale de la servlet
%>
<%
// la balise précédente indique que le code qui suit sera enregistré
// dans la méthode principale de la servlet
// variable locale à la méthode principale
String nom="tintin";
%>
<%-- code HTML --%>
<html>
<head>
<title>Page JSP</title>
</head>
<body>
<center>
<h2>Page JSP</h2>
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// le nom de son ami
afficheAmi(out);
%>
</center>
</body>
</html>
Questa pagina JSP genera la seguente pagina web:

Diamo un'occhiata a come vengono generate le quattro righe sopra riportate:
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// le nom de son ami
afficheAmi(out);
%>
Le righe sopra riportate si trovano all'interno di un tag <%..%> e faranno quindi parte del metodo principale _jspService del servlet che verrà generato. In che modo accedono alle variabili lastName, firstName e ai metodi hisDog e displayFriend?
è una variabile locale del metodo main della pagina JSP ed è quindi nota al suo interno | |
è una variabile globale della pagina JSP ed è quindi nota all'interno del metodo main | |
è un metodo pubblico della pagina JSP ed è quindi accessibile dal metodo main | |
è un metodo pubblico della pagina JSP ed è quindi accessibile dal metodo main. Si noti che l'oggetto out viene passato come parametro al metodo. In questo caso è obbligatorio. Infatti, l'oggetto out è dichiarato e inizializzato nel metodo main del servlet e non è una variabile globale. |
Esaminiamo ora il codice del servlet Java generato da questa pagina JSP, una volta rimosso il codice superfluo:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class tintin$jsp extends HttpJspBase {
// the preceding tag starts the global variables and methods section
// this part will be adopted unchanged in the servlet
// global variability
String prenom="inconnu";
// a method
private String sonChien(){
return "milou";
}//sonChien
// another method
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// end of the global part of the servlet
static {
}
public tintin$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
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;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
out.write(" \r\n\r\n");
// the preceding tag indicates that the following code will be saved
// in the servlet's main method
// variable local to the main method
String nom="tintin";
out.write("\r\n\r\n\r\n");
out.write("\r\n<html>\r\n <head>\r\n <title>Page JSP</title>\r\n </head>\r\n <body>\r\n <center>\r\n <h2>Page JSP</h2>\r\n <p>Son nom est ");
out.print( nom );
out.write("</p>\r\n <p>Son prénom est ");
out.print( prenom );
out.write("</p>\r\n <p>Son chien s'appelle ");
out.print( sonChien() );
out.write("</p>\r\n ");
// his friend's name
afficheAmi(out);
out.write("\r\n </center>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
Come mostrato sopra, il codice Java che si trovava tra i tag JSP <%! .. %> è stato incluso nella sua interezza e non fa parte del metodo principale del servlet, _jspService. Le variabili dichiarate in questa sezione sono variabili di istanza e sono quindi globali per i metodi; qui è anche possibile definire metodi diversi da _jspService.
// this part will be adopted unchanged in the servlet
// global variability
String prenom="inconnu";
// a method
private String sonChien(){
return "milou";
}//sonChien
// another method
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// end of the global part of the servlet
3.2.7. Distribuzione e debug delle pagine JSP sul server Tomcat
Quando si desidera creare una pagina JSP e utilizzarla con il server Tomcat, si pone la questione di dove collocare la pagina nella struttura delle directory del server. Esistono diversi modi per farlo, sui quali torneremo più avanti. Per ora, l'approccio più semplice consiste nel collocare la pagina JSP in una cartella all'interno dell'albero di directory <tomcat>\webapps\examples\jsp (Tomcat 4.x), dove <tomcat> è la directory di installazione di Tomcat. Pertanto, l'URL nell'esempio precedente era http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Ciò significa che la pagina tintin.jsp si trovava nella cartella <tomcat>\webapps\examples\jsp\perso\tintin.
Una pagina JSP viene tradotta in un file sorgente Java, che viene poi compilato da Tomcat quando l'URL della pagina JSP viene richiesto da un browser. Possono verificarsi errori di compilazione. Tomcat 4.x li segnala nella sua risposta al browser. Indica specificamente quali righe del file .java contengono errori. Gli errori possono avere varie cause:
- il codice JSP nella pagina è errato (ad es. errori nei tag JSP utilizzati)
- il codice Java incluso nella pagina JSP è errato
La prima causa può essere eliminata controllando il codice JSP della pagina. La seconda può essere eliminata controllando il codice Java. Ciò può essere fatto compilando direttamente il file .java generato per la pagina JSP utilizzando uno strumento come JBuilder, che offre funzionalità di debug più avanzate rispetto a Tomcat.
3.2.8. Esempi
Riprendiamo l'esempio trattato in precedenza, in cui un utente seleziona un numero da un elenco e il server gli comunica quale numero ha selezionato, restituendo lo stesso elenco con l'elemento scelto dall'utente evidenziato:

Per creare questa pagina, abbiamo recuperato il codice del servlet e lo abbiamo modificato come segue:
- abbiamo mantenuto invariato il codice Java che non generava codice HTML
- Il codice Java che generava codice HTML è stato convertito in un mix di codice HTML e JSP
Il risultato è la seguente pagina JSP:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// variables globales de l'application
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// valeurs de liste
private String[] valeurs=null;
// msg d'erreur
private String msgErreur=null;
// initialisation de la page JSP - n'est exécutée qu'une seule fois
public void jspInit(){
// remplit le tableau des valeurs à partir d'une base de données ODBC
// de nom DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connexion à la base ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// objet Statement
st=connexion.createStatement();
// exécution requête select pour récupérer les valeurs
rs=st.executeQuery("select valeur from Tvaleurs");
// les valeurs sont récupérées et mises dans un tableau dynamique
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// on enregistre la valeur dans la liste
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation liste --> tableau
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problème
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
%>
<%
// code de _jspService exécuté à chaque requête cliente
// y-at-il eu une erreur lors de l'initialisation de la page JSP ?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %></h3>
</body>
</html>
<%
// fin de jspService
return;
}//if
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
%>
<%-- pas d'erreur - code HTML de la page normale --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST">
<select name="cmbValeurs">
<%
// affichage dynamique des valeurs
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// y-avait-il une valeur choisie ?
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Si prega di notare i seguenti punti:
- le istruzioni di importazione del servlet sono racchiuse in una direttiva <% page import="..." %>
- Il tag <%! ... %> racchiude le variabili globali dell'applicazione e i metodi Java
- Il metodo init del servlet, che viene eseguito una sola volta al caricamento del servlet, è denominato jspInit per una pagina JSP. Questi due metodi hanno lo stesso scopo. Pertanto, il codice del metodo init del servlet è stato incluso qui nella sua interezza.
- Le variabili di istanza del servlet, ovvero quelle che devono essere accessibili da più metodi, sono state incluse alla lettera. Si tratta principalmente delle variabili title, values e msgError, che vengono successivamente utilizzate nel codice JSP.
- I tag <% ... %> racchiudono il codice Java che verrà incluso nel metodo _jspService eseguito quando un client effettua una richiesta.
- Come per il servlet, il metodo _jspService verificherà innanzitutto il valore della variabile msgError per determinare se è necessario generare una pagina di errore. Se si verifica un errore, genera la pagina di errore e termina (return).
- Se non c'è alcun errore, genera il modulo con l'elenco dei valori
- Una volta fatto ciò, verifica se l'utente ha selezionato un numero; in tal caso, visualizza quel numero sulla pagina generata
Quali sono i vantaggi rispetto a un servlet? Indubbiamente, una visione più chiara del codice HTML generato. Tuttavia, c'è ancora molto codice Java che "ingombra" questa visualizzazione. Più avanti esamineremo un altro metodo chiamato delega, in cui possiamo inserire la maggior parte del codice Java in un servlet, mentre la pagina JSP conserva solo il codice HTML e JSP. Questo separa chiaramente la parte di elaborazione da quella di presentazione.
3.3. Distribuzione di un'applicazione Web sul server Tomcat
Spiegheremo ora come distribuire applicazioni Web Java utilizzando il server Tomcat. Sebbene le seguenti istruzioni siano specifiche per questo server, la distribuzione di un'applicazione Web Java all'interno di un altro contenitore J2EE comporterà passaggi simili a quelli qui descritti.
3.3.1. I file di configurazione server.xml e web.xml
Finora, per testare i nostri servlet e le nostre pagine JSP, abbiamo inserito
- i servlet nella cartella <tomcat>\webapps\examples\WEB-INF\classes. Erano quindi accessibili tramite l'URL http://localhost:8080/examples/servlet/nomServlet
- le pagine JSP nella struttura di directory <tomcat>\webapps\examples\jsp. Erano quindi accessibili tramite l'URL http://localhost:8080/examples/jsp/nomPageJSP
Non abbiamo mai spiegato perché fosse così. Il server Tomcat è configurato in un file di testo chiamato server.xml, situato nella directory <tomcat>\conf:

Questo file di testo è in realtà un file XML (eXtended Markup Language). Un documento XML è un documento di testo contenente tag, proprio come un documento HTML. Tuttavia, mentre i tag HTML sono ben definiti, quelli XML non lo sono. Pertanto, il seguente documento è un documento XML:
Un documento XML è semplicemente un documento "taggato" che segue determinate regole di tagging:
- un testo con tag nella forma <xx att1="val1" att2="val2" ....>testo</xx>
- un tag può essere autonomo e assumere la forma <xx att1="val1" att2="val2" ..../>
I campi "att" sono chiamati attributi del tag "xx", mentre i campi "val" sono i valori associati a tali attributi. Alcuni documenti HTML non sono documenti XML validi. Ad esempio, il tag HTML <br> non è un tag XML valido. Per essere valido, dovrebbe essere scritto come <br/>, in modo da rispettare la regola secondo cui tutti i tag XML devono essere chiusi. È stata creata una variante dell'HTML chiamata XHTML per garantire che ogni documento XHTML sia un documento XML valido. Alcuni browser moderni sono in grado di visualizzare i file XML. Quindi, se chiamiamo il documento XML mostrato nell'esempio sopra "person.xml" e lo visualizziamo con IE6, otteniamo la seguente visualizzazione:

IE6 riconosce i tag e li evidenzia. Riconosce inoltre la struttura del documento in base ai tag. Quindi, se chiamiamo il seguente documento “person2.xml”:
e lo visualizziamo con IE6, otteniamo la stessa visualizzazione:

IE6 ha riconosciuto correttamente la struttura e il contenuto del documento. Il punto centrale di un documento XML risiede proprio in questa proprietà: è facile recuperare la struttura e il contenuto di un documento XML. Ciò avviene utilizzando un programma chiamato parser XML. I documenti XML stanno diventando lo standard per lo scambio di documenti sul web. Consideriamo la macchina A, che deve inviare un documento DOC alla macchina B. Il documento DOC è costruito a partire dalle informazioni contenute nel database DB-A. La macchina B deve memorizzare il documento DOC nel database DB-B. Lo scambio può avvenire come segue:
- La macchina A recupera i dati dal database DB-A e li incapsula in un documento di testo XML
- il documento XML viene inviato al computer B attraverso la rete
- La macchina B analizza il documento ricevuto con un parser XML ed estrae sia la struttura che i dati (come ha fatto IE6 nel nostro esempio). Può quindi memorizzare i dati ricevuti nel database DB-B
Non diremo altro sul linguaggio XML, che meriterebbe un libro a sé stante.
Qui, quindi, Tomcat è configurato dal file XML server.xml. Se visualizziamo questo file con IE6, otteniamo un documento complesso. Ci concentreremo semplicemente sulle seguenti righe:

È il tag <Context ...> che ci interessa in questo caso. Viene utilizzato per definire le applicazioni web. Due dei suoi attributi meritano di essere segnalati:
- path: è il nome dell'applicazione web
- docBase: è la cartella in cui si trova. Qui è un nome relativo: examples. Relativo a quale cartella? La risposta si trova anch'essa nel file server.xml nella riga seguente:

La riga sopra definisce il server web:
- name: nome del server web
- appBase: radice dell'albero di documenti che serve. Anche in questo caso, abbiamo un nome relativo: webapps. È relativo alla directory di installazione del server Tomcat <tomcat>. Pertanto, si riferisce alla cartella <tomcat>\webapps.
L'applicazione web di esempio memorizza i propri file nella cartella examples (vedere docBase sopra). Questo nome è relativo alla radice dell'albero delle directory web del server, ovvero <tomcat>\webapps. Si tratta quindi della cartella <tomcat>\webapps\examples. Diamo un'occhiata più da vicino a questa cartella:

Qui troviamo la cartella WEB-INF\classes dove abbiamo salvato i nostri servlet per il test. La cartella WEB-INF contiene un file chiamato web.xml:

Questo file viene utilizzato per configurare l'applicazione web di esempio. Non entreremo nei dettagli di questo file, che per ora è troppo complesso. Ci concentreremo semplicemente sulle seguenti righe:
<servlet>
<servlet-name>
servletToJsp
</servlet-name>
<servlet-class>
servletToJsp
</servlet-class>
</servlet>
Il tag <servlet> viene utilizzato per definire un servlet all'interno di un'applicazione web. Si noti che l'applicazione web in questione è examples. Il tag servlet contiene altri due tag:
- <servlet-name>servletToJsp</servlet-name>: definisce il nome del servlet
- <servlet-name>servletToJsp</servlet-name>: definisce il nome della classe da eseguire quando viene richiesto il servlet. In questo esempio, il servlet e la sua classe hanno lo stesso nome. Ciò non è obbligatorio.
In che modo il servlet servletToJsp viene richiesto da un browser al server Tomcat?
- Il browser richiede l'URL http://localhost:8080/examples/servlet/servletToJsp
- Tomcat analizza il percorso del servlet /examples/servlet/servletToJsp. Interpreta la prima parte del percorso /examples come il nome di un'applicazione web e cerca nel suo file di configurazione server.xml per vedere dove sono memorizzati i documenti per questa applicazione. Come abbiamo visto in precedenza, questi si trovano nella cartella <tomcat>\webapps\examples.
- Tomcat utilizza il resto del percorso del servlet per individuarlo all'interno dell'applicazione web examples. Questo percorso /servlet/servletToJsp indica che deve eseguire il servlet denominato servletToJsp. Tomcat leggerà quindi il file di configurazione web.xml dell'applicazione examples, che troverà in <tomcat>\webapps\examples\WEB-INF. In questo file troverà che il servlet servletToJsp è collegato alla classe Java servletToJsp (vedi il file web.xml sopra). Cercherà quindi questa classe nella cartella WEB-INF\classes dell'applicazione web examples, ovvero in <tomcat>\webapps\examples\WEB-INF\classes, e la eseguirà.

3.3.2. Esempio: distribuzione dell'applicazione web "list"
Riprenderemo in esame un servlet che abbiamo già studiato, il quale presentava all'utente un elenco di numeri tra cui selezionarne uno. Il servlet confermava quindi il numero scelto:

Come mostrato nella barra degli indirizzi del browser qui sopra, il file di classe del servlet è stato denominato gener3. In base alle spiegazioni fornite in precedenza:
- l'URL /examples/servlet/gener3 indica che si tratta di un servlet denominato gener3 dall'applicazione web di esempio
- nel file web.xml dell'applicazione examples non c'è nulla che menzioni un servlet denominato gener3. Come ha fatto Tomcat a trovarlo, allora? Dopo aver esaminato l'intero file web.xml, non posso rispondere con certezza... La domanda rimane...
Abbiamo scelto di distribuire il servlet gener3.class con il nome lstValeurs in un'applicazione web chiamata liste situata nella cartella E:\data\serge\Servlets\lstValeurs:

Inseriamo il file gener3.class nella cartella WEB-INF\classes menzionata sopra:

Configuriamo l'applicazione web liste aggiungendo le seguenti righe al file server.xml, sopra quelle che definiscono l'applicazione web manager:
<!-- Perso: lstValeurs -->
<Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />
<!-- Tomcat Manager Context -->
<Context path="/manager" docBase="manager" debug="0" privileged="true" />
<!-- Tomcat Examples Context -->
<Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........
La riga che definisce l'applicazione lstValeurs indica che essa si trova nella cartella e:/data/serge/servlets/lstValeurs. Dobbiamo ora definire il file web.xml per questa applicazione. Questo file definirà l'unico servlet dell'applicazione:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
</web-app>
Il file sopra riportato indica che il servlet denominato lstValeurs è associato al file di classe gener3.class. Questo file web.xml deve essere creato e salvato nella cartella WEB-INF dell'applicazione liste:

Lo screenshot qui sopra mostra una cartella src in cui è stato inserito il file sorgente gener3.java. Questa cartella potrebbe non esistere. Non ha alcuna utilità in questa dimostrazione. Siamo pronti per eseguire i test:
- Arrestare e riavviare Tomcat in modo che rilegga il file di configurazione server.xml. Qui stiamo utilizzando Windows. Su Unix, è possibile forzare Tomcat a rileggere il file di configurazione senza arrestarlo.
- Utilizzando un browser, richiedere l'URL http://localhost:8080/liste/servlet/lstValeurs

Possiamo notare che l'URL precedente contiene la parola chiave *servlet*, proprio come tutti gli URL dei servlet utilizzati finora. Possiamo eliminarla associando il servlet lstValeurs a un modello di URL (*url-pattern*) nel file web.xml dell'applicazione:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
Nel tag <servlet-mapping>, associamo il percorso /values al servlet lstValeurs definito nelle righe precedenti. Salviamo il nuovo file web.xml e richiediamo l'URL http://localhost:8080/liste/valeurs:

3.3.3. Distribuzione delle pagine pubbliche di un'applicazione web
Abbiamo appena visto come distribuire un'applicazione web costituita da un singolo servlet. Un'applicazione web può avere molti componenti: servlet, pagine JSP, file HTML, applet Java, ecc. Dove collochiamo questi elementi dell'applicazione? Se <application> è la directory dell'applicazione web definita dall'attributo docBase dell'applicazione nel file server.xml della configurazione di Tomcat ( ), abbiamo visto che i servlet vengono collocati in <application>\WEB-INF\classes. Gli altri componenti dell'applicazione possono essere collocati ovunque all'interno dell'albero di directory <application>, eccetto nella directory WEB-INF. Consideriamo l'applicazione JSP listvaleurs.jsp che abbiamo studiato in precedenza:

Questa pagina JSP era memorizzata nella cartella <tomcat>\webapps\examples\jsp\perso\listvaleurs. Questa pagina potrebbe essere un componente dell'applicazione list precedentemente distribuita. Collochiamo il file listvaleurs.jsp direttamente nella cartella di questa applicazione:

Ricordiamo la configurazione dell'applicazione liste nel file server.xml:
Qualsiasi URL che inizi con il percorso /liste viene considerato parte dell'applicazione liste e verrà cercato nella cartella specificata. Proviamo a richiamare l'URL http://localhost:8080/liste/listvaleurs.jsp utilizzando un browser:

Abbiamo effettivamente ottenuto la pagina JSP prevista.
3.3.4. Parametri di inizializzazione del servlet
Abbiamo visto che un servlet è configurato dal file <application>\WEB-INF\web.xml, dove <application> è la cartella dell'applicazione web a cui appartiene. È possibile includere i parametri di inizializzazione del servlet in questo file. Torniamo al nostro servlet lstValeurs dell'applicazione web liste, il cui file di configurazione era il seguente:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
La classe associata al servlet è la classe gener3. Il codice sorgente di questa classe contiene la definizione di diverse costanti:
public class gener3 extends HttpServlet{
// page title
private final String title="Génération d'un formulaire";
// the list values database
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
Rivediamo il significato delle quattro costanti definite sopra:
titolo del documento HTML generato dal servlet | |
nome DSN del database ODBC da cui il servlet recupera i dati | |
nome utente di un utente con accesso in lettura al database sopra menzionato | |
la relativa password |
Se l'amministratore del database DSNValeurs modifica la password dell'utente admDbValeurs, il codice sorgente del servlet deve essere modificato e ricompilato. Ciò non è molto pratico. Il file di configurazione web.xml del servlet offre un'alternativa consentendo la definizione dei parametri di inizializzazione del servlet tramite il tag <init-param>:
consente di definire il nome del parametro | |
definisce il valore associato al parametro precedente |
Il servlet può accedere ai propri parametri di inizializzazione utilizzando i seguenti metodi:
Un metodo della classe Servlet, da cui deriva la classe HttpServlet utilizzata per la programmazione web. Restituisce un oggetto ServletConfig che fornisce l'accesso ai parametri di configurazione del servlet. | |
Un metodo della classe ServletConfig che restituisce il valore del parametro di inizializzazione "parameter" |
Configuriamo l'applicazione "liste" con il seguente nuovo file web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs2</servlet-name>
<servlet-class>gener5</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</init-param>
<init-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</init-param>
<init-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs2</servlet-name>
<url-pattern>/valeurs2</url-pattern>
</servlet-mapping>
</web-app>
Nell'applicazione list definiamo un secondo servlet chiamato lstValeurs2 collegato al file di classe gener5. Questo file è stato inserito in <application>\WEB-INF\classes:

Il servlet lstValeurs2 dispone di quattro parametri di inizializzazione: title, DSNValeurs, admDbValeurs, mdpDbValeurs. Inoltre, è stato definito l'alias /values2 per il servlet tramite il tag <servlet-mapping>. Pertanto, il servlet lstValeurs2 nell'applicazione list sarà accessibile tramite l'URL http://localhost:8080/liste/valeurs2.
Il codice sorgente del servlet è stato modificato come segue per recuperare i parametri di inizializzazione del servlet:
public class gener5 extends HttpServlet{
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
...............
// servlet initialization
public void init(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
DSNValeurs=config.getInitParameter("DSNValeurs");
admDbValeurs=config.getInitParameter("admDbValeurs");
mdpDbValeurs=config.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// table of values is filled from a ODBC database
// name DSN : DSNvaleurs
...............
Per testare il servlet, è necessario riavviare Tomcat in modo che possa caricare il nuovo file di configurazione web.xml per l'applicazione "liste". Utilizzando un browser, richiedere l'URL del servlet: http://localhost:8080/liste/valeurs2:

Se nel file web.xml manca uno qualsiasi dei parametri di inizializzazione richiesti dal servlet, viene visualizzata la seguente pagina:

3.3.5. Parametri di inizializzazione per un'applicazione web
Nell'esempio precedente, solo il servlet lstValeurs2 ha accesso ai parametri title, DSNValeurs, admDbValeurs e mdpDbValeurs. È possibile che un altro servlet nella stessa applicazione liste abbia bisogno dei dati provenienti dallo stesso database utilizzato dal servlet lstValeurs2. In tal caso, i parametri DSNValeurs, admDbValeurs e mdpDbValeurs dovrebbero essere ridefiniti nella sezione di configurazione del file web.xml del nuovo servlet. Un'altra soluzione consiste nel definire i parametri comuni a più servlet a livello di applicazione anziché a livello di servlet. Il nuovo file web.xml per l'applicazione diventa il seguente:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs3</servlet-name>
<url-pattern>/valeurs3</url-pattern>
</servlet-mapping>
</web-app>
Il nuovo servlet si chiama lstValeurs3, è collegato al file di classe gener6 ed è stato associato all'alias /valeurs3 (mappatura del servlet). Il parametro title è l'unico parametro che è stato mantenuto nella definizione del servlet. Gli altri sono stati inseriti nella configurazione dell'applicazione all'interno dei tag <context-param>. Questo tag viene utilizzato per definire informazioni specifiche dell'applicazione, non di un particolare servlet o pagina JSP. In che modo il servlet Java accede a questi parametri, spesso chiamati parametri di contesto? I metodi disponibili per ottenere informazioni di contesto sono molto simili a quelli utilizzati per ottenere i parametri di inizializzazione specifici del servlet:
un metodo della classe Servlet da cui deriva la classe HttpServlet utilizzata per la programmazione web. Restituisce un oggetto ServletContext che fornisce l'accesso ai parametri di configurazione dell'applicazione | |
metodo della classe ServletContext che restituisce il valore del parametro di inizializzazione "parameter" |
La classe gener6.java apporta solo le seguenti modifiche al codice Java della classe gener5.java utilizzata in precedenza:
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// table of values is filled from a ODBC database
// name DSN : DSNvaleurs
...............
Il parametro title specifico del servlet viene ottenuto tramite un oggetto ServletConfig. Gli altri tre parametri definiti a livello di applicazione vengono ottenuti tramite un oggetto ServletContext. Compiliamo questa classe e la collochiamo, come le altre, in <application>\WEB-INF\classes:

Riavviamo Tomcat in modo che tenga conto del nuovo file web.xml dell'applicazione e richiediamo l'URL http://localhost:8080/liste/valeurs3:

3.3.6. Parametri di inizializzazione per una pagina JSP
Abbiamo visto come definire i parametri di inizializzazione per un servlet o un'applicazione web. Possiamo fare lo stesso per una pagina JSP? Torniamo all'inizio del codice della pagina listvaleurs.jsp che abbiamo già studiato:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// variables globales de l'application
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
.........
Troviamo le quattro costanti title, DSNValues, admDbValues e mdpDbValues definite nel file web.xml dell'applicazione. Le costanti DSNValues, admDbValues e mdpDbValues sono state definite a livello di applicazione, quindi possiamo supporre che una pagina JSP appartenente a questa applicazione avrà accesso ad esse. Questo è effettivamente il caso. Sappiamo che la pagina JSP verrà tradotta in un servlet. Il servlet avrà accesso al contesto tramite il metodo getServletContext(). Il caso della costante title è più delicato. Infatti, l'abbiamo definita a livello di servlet piuttosto che a livello di applicazione come segue:
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
Per la pagina JSP, la sintassi precedente non è più appropriata perché il concetto di file di classe non è più applicabile. Tuttavia, la sintassi di configurazione per una pagina JSP è molto simile a quella di un servlet. È la seguente:
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
...
</servlet>
Infatti, una pagina JSP viene trattata come un servlet a cui viene assegnato un nome (servlet-name). Invece di associare un file di classe a questo servlet, associamo il file sorgente della pagina JSP da eseguire (jsp-file). Pertanto, le righe precedenti definiscono un servlet denominato JSPlstValues associato alla pagina JSP /listvalues2.jsp. Il percorso /listvalues2.jsp è relativo alla radice dell'applicazione. Pertanto, nel caso della nostra applicazione list, il file listvalues2.jsp si troverebbe nella cartella docBase (vedere server.xml) dell'applicazione list:

La configurazione della pagina JSP sarà la seguente nel file web.xml dell'applicazione:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
.......
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
<init-param>
<param-name>JSPtitle</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
..........
<servlet-mapping>
<servlet-name>JSPlstValeurs</servlet-name>
<url-pattern>/jspvaleurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
........
</web-app>
La pagina JSP listvaleurs2.jsp si trova nella directory principale dell'applicazione liste ed è associata al nome del servlet JSP JSPlstValeurs (servlet-name), a sua volta associato all'alias /jspvaleurs (servlet-mapping). Pertanto, la nostra pagina JSP sarà accessibile tramite l'URL http://localhost:8080/liste/jspvaleurs.
La pagina JSP originale listvaleurs.jsp viene rinominata listvaleurs2.jsp e recupera i suoi quattro parametri di inizializzazione nel metodo jspInit():
<%!
// application global variables
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// list values
private String[] valeurs=null;
// error msg
private String msgErreur=null;
// initialization of page JSP - executed only once
public void jspInit(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("JSPtitle");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// fills the table of values from a ODBC database
// name DSN : DSNvaleurs
..............
La pagina JSP recupera i propri parametri di inizializzazione allo stesso modo dei servlet. Il file precedente viene salvato nella directory principale dell'applicazione web "liste":

Il server Tomcat viene riavviato per costringerlo a ricaricare il nuovo file di configurazione web.xml dell'applicazione. È quindi possibile richiedere l'URL http://localhost:8080/liste/jspvaleurs:

3.3.7. Collaborazione tra servlet e JSP all'interno di un'applicazione web
Quando un client effettua una richiesta a un server web, la risposta può essere generata da più servlet e pagine JSP. Fino ad ora, la risposta era stata generata da un singolo servlet o da una singola pagina JSP. Abbiamo visto che la pagina JSP offre una migliore leggibilità della struttura del documento HTML generato. Tuttavia, in genere contiene anche molto codice Java. Possiamo migliorare questo aspetto inserendo
- il codice Java che non genera il codice HTML della risposta in uno o più servlet
- nelle pagine JSP, il codice per la generazione dei vari documenti HTML inviati in risposta al client
Questo dovrebbe aiutare a migliorare la separazione tra codice Java e codice HTML. Applicheremo questa nuova struttura alla nostra applicazione dell’elenco: un servlet Java denominato lstValeurs4 sarà responsabile della lettura dei valori dal database all’avvio e successivamente dell’elaborazione delle richieste del client all’indirizzo . A seconda del risultato di questa analisi, la richiesta del client verrà indirizzata a una pagina di errore (erreur.jsp) o alla pagina che visualizza l’elenco dei numeri (liste.jsp). L'applicazione dell'elenco sarà quindi costituita da un servlet e da due pagine JSP.
In che modo un servlet può trasmettere la richiesta ricevuta da un client a un altro servlet o a una pagina JSP? Utilizzeremo i seguenti metodi:
della classe ServletContext che restituisce un oggetto RequestDispatcher. Il parametro url è il nome dell'URL a cui vogliamo inoltrare la richiesta del client. Questo inoltro della richiesta può avvenire solo all'interno della stessa applicazione. Pertanto, il parametro url è un percorso relativo alla struttura delle directory web di quell'applicazione. | |
Un metodo dell'interfaccia RequestDispatcher che inoltra la richiesta del client e l'oggetto risposta — che deve essere utilizzato per costruire la risposta — all'URL precedente. | |
Quando un servlet o una pagina JSP inoltra una richiesta a un altro servlet o a un'altra pagina JSP, in genere deve trasmettere anche informazioni diverse dalla semplice richiesta del client, ovvero informazioni derivanti dalla propria elaborazione della richiesta. Il metodo setAttribute della classe ServletRequest consente di aggiungere attributi all'oggetto richiesta del client in un formato simile a un dizionario di coppie (attributo, valore), dove attributo è il nome dell'attributo e valore è qualsiasi oggetto che ne rappresenti il valore. | |
consente di recuperare i valori degli attributi di una richiesta. Questo metodo verrà utilizzato dal servlet o dalla pagina JSP a cui è stata inoltrata una richiesta per ottenere le informazioni ad essa aggiunte. |
Il servlet responsabile dell'elaborazione del modulo sarà configurato come segue nel file web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
............
<servlet>
<servlet-name>lstValeurs4</servlet-name>
<servlet-class>gener7</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>JSPerreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
<init-param>
<param-name>JSPliste</param-name>
<param-value>/liste.jsp</param-value>
</init-param>
<init-param>
<param-name>URLservlet</param-name>
<param-value>/liste/valeurs4</param-value>
</init-param>
</servlet>
...........
<servlet-mapping>
<servlet-name>lstValeurs4</servlet-name>
<url-pattern>/valeurs4</url-pattern>
</servlet-mapping>
.......
</web-app>
Il servlet lstValeurs4 avrà quattro parametri di inizializzazione specifici:
titolo del documento HTML da generare | |
l'URL della pagina JSP di errore | |
L'URL della pagina JSP che visualizza l'elenco dei numeri | |
L'URL associato all'attributo action del modulo visualizzato dalla pagina JSPlist. Questo URL sarà quello del servlet lstValeurs4 |
Il servlet avrà l'alias /valeurs4 (servlet-mapping) e sarà quindi accessibile tramite l'URL http://localhost:8080/liste/valeurs4. È collegato al file di classe gener7.java, il cui codice sorgente completo è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener7 extends HttpServlet{
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// JSP display pages
private String JSPerreur=null;
private String JSPliste=null;
// the URL of the servlet
private String URLservlet=null;
// list values
private String[] valeurs=null;
// error msg
private String msgErreur=null;
// -----------------------------------------------------------------
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// put msgErreur,title in the query attributes
request.setAttribute("msgErreur",msgErreur);
request.setAttribute("title",title);
request.setAttribute("URLservlet",URLservlet);
// was there an error loading the servlet?
if(msgErreur!=null){
// we hand over to a JSP error page
getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
// end
return;
}
// there was no error
// put the list of values in the query attributes
request.setAttribute("valeurs",valeurs);
// we retrieve the user's possible choice
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
request.setAttribute("choix",choix);
// hand over to the JSP list presentation page
getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
// end
return;
}//GET
// -----------------------------------------------------------------
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// returns to GET
doGet(request,response);
}//POST
// -----------------------------------------------------------------
// servlet initialization
public void init(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
JSPerreur=config.getInitParameter("JSPerreur");
JSPliste=config.getInitParameter("JSPliste");
URLservlet=config.getInitParameter("URLservlet");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
msgErreur="Configuration incorrecte";
return;
}
// fills the table of values from a ODBC database
// name DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connection to the ODBC database
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// statement object
st=connexion.createStatement();
// execute select query to retrieve values
rs=st.executeQuery("select valeur from Tvaleurs");
// values are retrieved and put into a dynamic table
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// the value is saved in the
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation list --> table
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problem
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
}//class
La novità di questa classe è che inoltra la richiesta del client alla pagina JSPerreur in caso di errore, e alla pagina JSPliste in caso contrario. La classe non genera la risposta da sola. Se ne occupano le pagine JSP JSPerreur e JSPliste. In precedenza, il servlet aggiungeva degli attributi (setAttribute) alla richiesta del client:
- un messaggio di errore msgError in caso di errore per la pagina JSPerreur
- i valori (values) da visualizzare, il valore selezionato (choice) dall'utente, il titolo del modulo (title) e l'URL (URLservlet) dell'attributo action del modulo per la pagina JSPliste
Questa classe viene compilata e inserita nelle classi dell'applicazione:

La pagina JSP che visualizza un messaggio di errore è configurata come segue:
<servlet>
<servlet-name>JSPerreur</servlet-name>
<jsp-file>/erreur.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
</servlet>
.........
<servlet-mapping>
<servlet-name>JSPerreur</servlet-name>
<url-pattern>/JSPerreur</url-pattern>
</servlet-mapping>
Il file JSP associato alla pagina di errore si chiama error.jsp e si trova nella radice dell'applicazione:

Ha l'alias /JSPerreur, che lo rende accessibile tramite l'URL http://localhost:8080/liste/JSPerreur. Ha un parametro di inizializzazione chiamato mainServlet, il cui valore è l'alias del servlet principale descritto sopra. Si noti che questo alias è relativo alla radice dell'applicazione liste; altrimenti, sarebbe /liste/valeurs4. Il codice della pagina erreur.jsp è il seguente:
<%
// code de _jspService
// on récupère le paramètre d'initialisation mainServlet
String servletListValeurs=config.getInitParameter("mainServlet");
// on récupère l'attribut msgErreur
String msgErreur=(String)request.getAttribute("msgErreur");
// attribut valide ?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %>)</h3>
</body>
</html>
<%
} else { // attribut msgErreur invalide - retour à la servlet principale
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}
%>
Questa pagina dovrebbe normalmente essere richiamata dal servlet precedente, che dovrebbe passarle l'attributo msgError. Tuttavia, nulla vi impedisce di richiamarla direttamente se ne conoscete l'URL. Inoltre, se vi accorgete che l'attributo msgError manca, passate la richiesta al servlet principale. Qui utilizziamo un tag specifico per le pagine JSP, la cui sintassi è:
dove URL è l'URL del servlet a cui viene inoltrata la richiesta del client. Se l'attributo msgError è presente, viene visualizzata la pagina di errore.
La pagina JSP che visualizza l'elenco dei numeri è configurata come segue:
<servlet>
<servlet-name>JSPliste</servlet-name>
<jsp-file>/liste.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
.........
<servlet-mapping>
<servlet-name>JSPliste</servlet-name>
<url-pattern>/JSPliste</url-pattern>
</servlet-mapping>
Il file JSP associato alla pagina di errore si chiama liste.jsp e si trova nella directory principale dell'applicazione:

Il servlet ha l'alias /JSPliste, che lo rende accessibile tramite l'URL http://localhost:8080/liste/JSPliste. Ha un parametro di inizializzazione chiamato mainServlet, il cui valore è l'alias del servlet principale. Il codice della pagina liste.jsp è il seguente:
<%-- page d'affichage de la liste des valeurs --%>
<%
// code de jspService
// on récupère le paramètre d'initialisation
String servletListValeurs=config.getInitParameter("mainServlet");
// on récupère les attributs de la requête venant de la servlet principale
String title=(String) request.getAttribute("title");
String[] valeurs=(String[]) request.getAttribute("valeurs");
String choix=(String) request.getAttribute("choix");
String URLservlet=(String) request.getAttribute("URLservlet");
// attributs valides ?
if(title==null || valeurs==null || choix==null){
// il y a un attribut invalide - on passe la main à la servlet
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}//if
%>
<%-- code HTML --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="<%= URLservlet %>">
<select name="cmbValeurs">
<%
// affichage dynamique des valeurs
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// y-avait-il une valeur choisie ?
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Questa pagina si comporta come la pagina error.jsp. Normalmente dovrebbe essere richiamata dal servlet /list/values4 e ricevere gli attributi title, values e choice. Se uno qualsiasi di questi parametri manca, il controllo viene passato al servlet URLservlet (/liste/valeurs4). Se tutti i parametri sono presenti, viene visualizzato l'elenco dei numeri insieme al numero scelto dall'utente, se presente.
Se si richiede l'URL del servlet principale, si ottiene il seguente risultato:

con il seguente codice sorgente (Visualizza/Sorgente):
<html>
<head>
<title>Génération d'un formulaire</title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="/liste/valeurs4">
<select name="cmbValeurs">
<option >0</option>
<option >1</option>
<option >2</option>
<option >3</option>
<option >4</option>
<option >6</option>
<option >5</option>
<option >7</option>
<option >8</option>
<option >9</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Questo documento HTML è stato generato dalla pagina JSP liste.jsp. Possiamo vedere che gli attributi title, values e URLservlet sono stati recuperati correttamente.
Per concludere sulla collaborazione tra servlet e pagine JSP, osserviamo che le pagine JSP qui sono molto brevi e prive di codice Java che non contribuisca direttamente alla creazione della risposta HTML. La struttura dei documenti generati è quindi più visibile.
3.4. Ciclo di vita dei servlet e delle pagine JSP
3.4.1. Il ciclo di vita
Qui ci concentriamo sul ciclo di vita dei servlet. Il ciclo di vita delle pagine JSP ne deriva. Consideriamo un servlet chiamato per la prima volta. Viene quindi creata un'istanza di classe dal server web e caricata in memoria. Essa servirà quindi la richiesta. Una volta fatto ciò, il servlet non viene scaricato dalla memoria. Rimane lì per servire altre richieste al fine di ottimizzare i tempi di risposta del server. Verrà scaricato quando sarà trascorso un periodo di tempo sufficientemente lungo senza che abbia servito nuove richieste. Questo tempo è generalmente configurabile all'interno del server web.
Mentre si trova in memoria, il servlet può gestire più richieste contemporaneamente. Il server web crea un thread per ogni richiesta, che utilizzano tutti la stessa istanza del servlet:
![]() |
Tutti i thread sopra indicati condividono le variabili dell'istanza del servlet. Potrebbe essere necessario sincronizzare i thread per evitare il danneggiamento dei dati del servlet. Torneremo su questo argomento più avanti.
Quando viene caricato un servlet, viene eseguito un metodo specifico del servlet:
Per una pagina JSP, si tratta del metodo
public void jspInit(){
}
. Ecco un esempio di pagina JSP che utilizza il metodo jspInit:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variable d'instance
int compteur;
// méthode pour incrémenter le compteur
public int getCompteur(){
// on incrémente le compteur
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// on le rend
return compteur;
}
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
La pagina JSP precedente inizializza un contatore a 100 in jspInit. Qualsiasi richiesta successiva al servlet incrementa e quindi visualizza il valore di questo contatore:
La prima volta:

La seconda volta:

Come si può vedere sopra, tra le due richieste, il servlet non è stato scaricato; altrimenti, il contatore sarebbe stato 101 durante la seconda richiesta. Quando il servlet viene scaricato, il metodo
viene eseguito, se esiste. Per le pagine JSP, è il metodo
public void jspDestroy(){
}
In questi metodi è possibile, ad esempio, chiudere le connessioni al database aperte nei corrispondenti metodi init.
3.4.2. Sincronizzazione delle richieste a un servlet
Torniamo alla precedente pagina JSP che incrementa un contatore e lo restituisce al client web. Supponiamo che ci siano due richieste simultanee. Vengono quindi creati due thread per eseguirle; questi thread useranno la stessa istanza di servlet e, quindi, lo stesso contatore. Ricordiamo il codice che incrementa il contatore:
public int getCompteur(){
// on incrémente le compteur
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// on le rend
return compteur;
}
L'incremento del contatore è stato scritto intenzionalmente in modo approssimativo. Supponiamo che l'esecuzione dei due thread proceda come segue:
![]() |
- Al tempo T1, viene eseguito il thread TH1. Legge il contatore (=145) da myCounter, poi viene interrotto e perde il processore. Non ha quindi avuto il tempo di incrementare myCounter e copiare il nuovo valore in counter.
- Al tempo T2, viene eseguito il thread TH2. Legge il contatore (=145) da myCounter, poi viene interrotto e perde il processore. Si noti che i due thread hanno variabili myCounter diverse. Condividono solo le variabili di istanza, quelle che sono globali per i metodi.
- Al tempo T3, il thread TH1 riprende il controllo e termina. Restituisce quindi 146 al suo client.
- Al tempo T4, il thread TH2 riprende il controllo e termina. Restituisce anch'esso 146 al proprio client, quando invece avrebbe dovuto restituire 147.
Si tratta di un problema di sincronizzazione tra thread. Quando TH1 vuole incrementare il contatore, deve impedire che qualsiasi altro thread faccia lo stesso. Per evidenziare questo problema, riscriviamo la pagina JSP come segue:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variable d'instance
int compteur;
// méthode pour incrémenter le compteur
public int getCompteur(){
// on lit le compteur
int myCompteur=compteur;
// on s'arrête 10 secondes
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// on incrémente le compteur
compteur=myCompteur+1;
// on le rend
return compteur;
}
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
Qui abbiamo forzato il thread a fermarsi 10 secondi dopo aver letto il contatore. Dovrebbe quindi liberare la CPU, consentendo a un altro thread di leggere un contatore che non è stato incrementato. Quando effettuiamo richieste con un browser, non notiamo alcuna differenza se non l'attesa di 10 secondi prima di ottenere il risultato.

Ora, se apriamo due finestre del browser ed effettuiamo due richieste a distanza di tempo sufficientemente ravvicinata:


Otteniamo lo stesso valore del contatore. Possiamo evidenziare meglio il problema con un client programmato piuttosto che con uno manuale come il browser. Ecco un client Perl che viene chiamato come segue:
*URL del programma N*
dove
URL è l'URL del servlet di conteggio
N è il numero di richieste da effettuare a questo servlet
Ecco i risultati ottenuti per 5 richieste, che dimostrano chiaramente il problema della scarsa sincronizzazione dei thread: tutte restituiscono lo stesso valore del contatore.
DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Il codice client Java è il seguente.
import java.net.*;
import java.util.regex.*;
import java.io.*;
public class clientCompteurJSP {
public static void main(String[] params){
// data
String syntaxe="Syntaxe : pg URL nbAppels";
// parameter verification
if(params.length!=2){
System.err.println(syntaxe);
System.exit(1);
}//if
// URL
URL urlCompteur=null;
try{
urlCompteur=new URL(params[0]);
String query=urlCompteur.getQuery();
if(query!=null) throw new Exception();
}catch (Exception ex){
System.err.println(syntaxe);
System.err.println("URL ["+params[0]+" incorrecte");
System.exit(2);
}//try-catch
// number of calls
int nbAppels=0;
try{
nbAppels=Integer.parseInt(params[1]);
if(nbAppels<=0) throw new Exception();
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("Nombre d'appels ["+params[1]+" incorrect");
System.exit(3);
}//try-catch
// parameters are correct - connections can be made to the URL
try{
getCompteurs(urlCompteur,nbAppels);
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
System.exit(4);
}//try-catch
}//hand
private static void getCompteurs (URL urlCompteur, int nbAppels)
throws Exception {
// does nbAppels at URL urlCompteur
// displays the counter value returned by the web server each time
// remove from urlCompteur the info needed to connect to the tax server
String path=urlCompteur.getPath();
if(path.equals("")) path="/";
String host=urlCompteur.getHost();
int port=urlCompteur.getPort();
if(port==-1) port=urlCompteur.getDefaultPort();
// calls are made to the URL
Socket[] clients=new Socket[nbAppels];
for(int i=0;i<nbAppels;i++){
// connect to the server
clients[i]=new Socket(host,port);
// create a write stream to the server
PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
// request URL - send HTTP headers
OUT.println("GET " + path + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println("");
}//for
// local data
String réponse=null; // server response
// the model searched for in the HTML server response
Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
// the model for a correct answer
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
// the result of the model comparison
Matcher résultat=null;
for(int i=0;i<nbAppels;i++){
// each client reads the response sent by the server
// create customer input/output flows TCP
BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));
// read the 1st line of the answer
réponse=IN.readLine();
// compare the HTTP line with the model of the correct answer
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// we have a URL problem
throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
}//if
// the response is read through to the end of the headers
while((réponse=IN.readLine())!=null && ! réponse.equals("")){
}//while
// that's it for HTTP headers - move on to HTML code
// to retrieve the counter value
boolean compteurTrouvé=false;
while((réponse=IN.readLine())!=null){
// compare the line with the counter model
if(! compteurTrouvé){
résultat=modèleCompteur.matcher(réponse);
if(résultat.find()){
// meter found
System.out.println("Compteur="+résultat.group(1));
compteurTrouvé=true;
}//if
}//if
}//while
// it's over
clients[i].close();
}//for
}//getCompteurs
}//class
Spieghiamo il codice precedente:
- Il programma accetta due parametri:
- l'URL della pagina JSP per il contatore
- il numero di client da creare per questo URL
- Il programma inizia quindi verificando la validità dei parametri: che ce ne siano effettivamente due, che il primo assomigli sintatticamente a un URL e che il secondo sia un numero intero maggiore di 0. Per verificare che l'URL sia sintatticamente corretto, utilizziamo la classe URL e il suo costruttore URL(String), che crea un oggetto URL da una stringa come http://istia.univ-angers.fr. Se la stringa non è un URL sintatticamente valido, viene generata un'eccezione. Questo ci permette di verificare la validità del primo parametro.
- Una volta verificati i parametri, il controllo passa alla procedura getCompteurs. Questa procedura creerà nbAppels client, che si connetteranno tutti contemporaneamente (o quasi) all'URL urlCompteur.
- La porta e la macchina a cui i client devono connettersi sono ricavate dall'URL urlCompteur: [URL].getHost() restituisce il nome della macchina e [URL].getPort() restituisce la porta.
- Un primo ciclo permette a ciascun client:
- connettersi al server web
- richiedere l'URL urlCompteur
In questo ciclo, il client non attende la risposta del server. Questo perché vogliamo che il server riceva richieste quasi simultanee.
- Un secondo ciclo permette a ciascun client di ricevere ed elaborare la risposta inviata dal server. L'elaborazione consiste nel trovare la riga nella risposta che contiene il valore del contatore e visualizzarlo.
Per risolvere il problema evidenziato in precedenza (lo stesso contatore inviato a tutti e cinque i client), dobbiamo sincronizzare i thread del servizio di conteggio su un unico oggetto prima di entrare nella sezione critica per la lettura e l'aggiornamento del contatore. La nuova pagina JSP è la seguente:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variables d'instance
int compteur;
Object verrou=new Object();
// méthode pour incrémenter le compteur
public int getCompteur(){
// on syncronise la section critique
synchronized(verrou){
// on lit le compteur
int myCompteur=compteur;
// on s'arrête 10 secondes
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// on incrémente le compteur
compteur=myCompteur+1;
}//synchronized
// on le rend
return compteur;
}//getCompteur
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
Una volta eseguito, si ottengono i seguenti risultati:
dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/compteur/compteur3.jsp 5
Compteur= 104
Compteur= 106
Compteur= 105
Compteur= 107
Compteur= 108
La documentazione indica che il server web a volte può creare più istanze dello stesso servlet. In questo caso, la sincronizzazione precedente non funziona più perché la variabile di blocco è locale a una singola istanza e quindi non è nota alle altre istanze. Lo stesso vale per la variabile contatore. Per renderle globali a tutte le istanze, scriviamo:
// variable de classe
static int compteur;
static Object verrou=new Object();
Il resto del codice rimane invariato.





