Skip to content

3. Introduzione ai servlet Java e alle pagine JSP

In questo capitolo si trovano vari esempi di servlet e pagine JSP. Sono stati testati con il server Tomcat, che opera sulla porta 8080. Seguendo i link presenti nella pagina iniziale, è possibile accedere agli esempi di servlet e pagine JSP. Gli esempi riportati di seguito sono per lo più tratti dagli esempi di Tomcat. Per testarli, è sufficiente avviare Tomcat, richiedere l’URL http://localhost:8080 con un browser e seguire il link dei servlet.

3.1. Servlet Java

3.1.1. Invio di un contenuto 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>");
    }
}

All'esecuzione di questo servlet, si ottiene la seguente visualizzazione:

Image

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 serie nel JDK. In tal caso, è possibile scaricarla direttamente dal sito di Sun.

  • Un servlet deriva dalla classe HttpServlet

public class HelloWorld extends HttpServlet {
  • Una richiesta GET inviata alla servlet viene elaborata dal metodo doGet

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • Allo stesso modo, una richiesta POST inviata al servlet viene elaborata dal metodo doPost

    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • L'oggetto HttpServletRequest request è l'oggetto che ci consente di accedere alla richiesta effettuata dal client Web. La risposta del servlet verrà inviata tramite l'oggetto HttpServletResponse response
  • L'oggetto response ci permette di impostare le intestazioni HTTP che verranno inviate al client. Ad esempio, l'intestazione Content-type: text/html viene qui impostata tramite:
        response.setContentType("text/html");
  • Per inviare la risposta al client, il servlet utilizza un flusso di output fornito dall’oggetto response:
        PrintWriter out = response.getWriter();
  • Una volta ottenuto questo flusso di output, il codice HTML viene scritto al suo interno 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. Recuperare i parametri inviati da un client web

L'esempio seguente mostra come un servlet possa recuperare i parametri inviati dal client web. Un modulo di inserimento dati:

Image

La risposta inviata dalla servlet:

Image

Il codice sorgente della 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 novità rispetto all'esempio precedente:

  • I parametri inviati dal browser vengono recuperati nel modo seguente:

        String firstName = request.getParameter("firstname");
        String lastName = request.getParameter("lastname");

Il metodo request.getParameter("nomParamètre") restituisce il puntatore null, se il parametro nomParamètre non fa parte dei parametri inviati dal client web.

  • Il modulo specifica che il browser deve inviare i parametri tramite il metodo POST
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
  • I parametri ricevuti saranno elaborati dal metodo doPost del servlet. In questo caso, tale metodo si limita a richiamare il metodo doGet. Pertanto, questa servlet elabora i valori del modulo indipendentemente dal fatto che siano stati inviati tramite un GET o un POST.

    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        doGet(request, response);
    }

3.1.3. Recuperare le intestazioni HTTP inviate da un client web

Il seguente servlet mostra come recuperare le intestazioni HTTP inviate dal client web:

Image

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);
        }
    }
}

Note:

  • Sono l'oggetto request e il relativo metodo getHeaderNames a consentirci di accedere alle intestazioni HTTP inviate dal browser sotto forma di elenco:
        Enumeration e = request.getHeaderNames();
  • Il metodo request.getHeader("intestazione") consente di ottenere una specifica intestazione HTTP. L'esempio sopra riportato ne illustra alcune. Va ricordato che le intestazioni qui presentate vengono inviate dal browser. Anche il server dispone delle proprie intestazioni HTTP, che talvolta riprendono quelle del browser. Le intestazioni HTTP inviate dal browser hanno lo scopo di informare il server sulle capacità del browser.
Header
Significato
User-Agent
identificativo del browser
Accept
i formati MIME supportati dal browser. Pertanto, image/gif indica che il browser è in grado di elaborare immagini nel formato GIF
Host
nel formato hote:port. Indica quale macchina e quale porta il browser intende contattare.
Accept-Encoding
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 uno in formato compresso gzip e il browser ha indicato di essere in grado di gestire il formato gzip, il server potrà inviare il documento in formato gzip per risparmiare larghezza di banda.
Accept-language
lingue supportate dal browser. Se un server dispone dello stesso documento in più lingue, ne invierà uno nella lingua supportata dal browser.
Referer
la richiesta URL inviata dal browser
Connection
la modalità di connessione richiesta dal browser. Keep-alive significa che il server non deve interrompere la connessione dopo aver fornito la pagina richiesta al browser. Se quest'ultimo rileva che la pagina ricevuta contiene, ad esempio, collegamenti a immagini, potrà inviare nuove richieste al server per scaricarle senza dover creare una nuova connessione. Sarà quindi il browser a chiudere la connessione una volta ricevuti tutti gli elementi della pagina.

3.1.4. Recupero delle informazioni sull'ambiente

Il servlet riportato di seguito mostra come accedere alle informazioni sull’ambiente di esecuzione del servlet. Alcune di queste vengono inviate sotto forma di intestazioni HTTP dal browser e sono quindi recuperabili tramite il metodo precedente.

Image

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 qui riportate sono state ottenute con diversi 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());

Di seguito è riportato un elenco di alcuni dei metodi disponibili e del loro significato:

metodo
significato
getServerName()
il nome del server Web
getServerPort()
porta operativa del server web
getMethod()
il metodo GET o POST utilizzato dal browser per effettuare la richiesta
getRemoteHost()
il nome del computer client da cui il browser ha effettuato la richiesta
getRemoteAddr()
l'indirizzo IP di quella stessa macchina
getContentType()
il tipo di contenuto inviato dal browser (intestazione HTTP Content-type)
getContentLength()
il numero di caratteri inviati dal browser (intestazione HTTP Content-length)
getProtocol()
la versione del protocollo HTTP richiesta dal browser
getRequestURI()
l'URI richiesto dal browser. Corrisponde alla parte dell'URL che segue l'identificativo hote:port nelhttp://hote:port/URI

3.1.5. Creare un servlet con JBuilder, distribuirlo con Tomcat

Descriviamo ora come creare ed eseguire una servlet Java. Utilizzeremo due strumenti: JBuilder per compilare la servlet e Tomcat per eseguirla. Tomcat potrebbe essere sufficiente da solo; tuttavia, offre funzionalità di debug limitate. Riprendiamo l’esempio sviluppato in precedenza che visualizza i parametri ricevuti dal server. La servlet invia innanzitutto il seguente modulo di immissione dati:

Image

La risposta inviata dalla servlet:

Image

Il codice sorgente della 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 myRequestParamExample con JBuilder e includervi il programma myRequestParamExample.java precedente.
  • Durante la compilazione, potrebbe verificarsi il seguente problema: il vostro JBuilder potrebbe non disporre della libreria javax.servlet necessaria per la compilazione dei servlet. In questo caso, è necessario configurare JBuilder affinché utilizzi librerie di classi aggiuntive. La procedura è descritta negli allegati del presente documento per JBuilder 7. La riportiamo qui di seguito in parte:
  • attivare l’opzione Tools/Configure JDKs oppure (Options/Configurare JDK)

Image

Nella sezione JDK Settings sopra riportata, normalmente nel campo Name è presente una versione 1.3.1 di JDK. Se si dispone di una versione più recente di JDK, utilizzare il pulsante Change per specificare la directory di installazione di quest'ultima. Nell’esempio sopra riportato, è stata specificata la cartella E:\Program Files\jdk14 in cui era installato un JDK 1.4. D’ora in poi, JBuilder utilizzerà questo JDK per le proprie compilazioni ed esecuzioni. Nella sezione (Class, Source, Documentation) è presente l’elenco di tutte le librerie di classi che saranno analizzate da JBuilder, in questo caso le classi di JDK 1.4. Le classi di quest’ultimo non sono sufficienti per lo sviluppo web in Java. Per aggiungere altre librerie di classi si utilizza il pulsante Add e si indicano i file .jar aggiuntivi che si desidera 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:

Image

Con il pulsante Add, aggiungeremo queste librerie, una alla volta, all’elenco delle librerie esplorate da JBuilder:

Image

Da questo momento in poi, è possibile compilare programmi Java conformi allo standard J2EE, in particolare i servlet Java. JBuilder serve solo per la compilazione, mentre l'esecuzione è successivamente gestita da Tomcat.

  • Ora è possibile compilare il programma myRequestParamExample.java e generare il servlet myRequestParamExample.class. Dove collocare questa servlet? Se la configurazione iniziale di Tomcat non è stata modificata, i file .class delle servlet devono essere collocati in <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
  • Verificate che Tomcat sia in esecuzione e, tramite un browser, richiedete l'URL http://localhost:8080/examples/servlet/myRequestParamExample:

Image

3.1.6. Esempi

Per gli esempi che seguono, abbiamo utilizzato il metodo descritto in precedenza:

  • compilazione del codice sorgente XX.java del servlet con JBuilder
  • distribuzione della servlet XX.class in <tomcat>\webapps\examples\WEB-INF\classes
  • Avviato Tomcat, accedere con un browser all'URL http://localhost:8080/examples/servlet/XX

3.1.6.1. Generazione dinamica di un modulo - 1

Prendiamo come esempio la generazione di un modulo con un solo controllo: un elenco. Il contenuto di questo elenco viene costruito dinamicamente con valori prelevati da un array. Nella realtà, questi valori vengono spesso prelevati da un database. Il modulo è il seguente:

Image

Se nell’esempio sopra riportato si esegue Envoyer, si ottiene la seguente risposta:

Image

Si noti che il codice URL che costituisce la risposta è lo stesso di quello che visualizza il modulo. In questo caso abbiamo un servlet che elabora autonomamente la risposta al modulo che ha inviato. Si tratta di un caso 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 trasmessi tramite 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{
    // variabili di istanza
    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{

      // si indica al cliente il tipo di documento inviato
      response.setContentType("text/html");
       // si invia il modulo
      PrintWriter out=response.getWriter();
            // inizio
      out.println(HTML1);
            // menu a tendina
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            for (int i=0;i<valeurs.length;i++){
                out.println("<option>"+valeurs[i]+"</option>");
            }//per
            out.println("</select>");
            // fine del modulo
            out.println(HTML2+HTML3);
    }//GET

         // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException{

             // si recupera la scelta dell'utente
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) doGet(request,response);

             // si prepara la risposta
            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>";
             // si indica al cliente il tipo di documento inviato
            response.setContentType("text/html");
             // si invia il modulo
            PrintWriter out=response.getWriter();
            out.println(réponse);
    }//POST
    }//classificazione

Il metodo doGet serve a generare il modulo. Esiste una parte dinamica che corrisponde al contenuto dell'elenco, proveniente in questo caso da una tabella. Il metodo doPost serve a generare la risposta. In questo caso l'unica parte dinamica è il valore della scelta effettuata dall'utente nell'elenco del modulo. Questo valore viene ottenuto tramite request.getParameter("cmbValeurs"), dove cmbValeurs è il nome HTML dell’elenco:

      <select name="cmbValeurs" size="1">

Si notino infine i seguenti punti:

  • il browser invia i valori del modulo alla servlet che lo ha generato, poiché il tag <form> non presenta l’attributo <action>. In questo caso, il browser invia i dati inseriti nel modulo alla servlet URL che lo ha fornito.
  • Il tag <form> specifica che i dati del modulo devono essere inviati tramite il metodo POST. Per questo motivo tali valori vengono recuperati dal metodo doPost del servlet.

3.1.6.2. Generazione dinamica di moduli - 2

Riprendiamo l’esempio precedente modificandolo come segue. Il modulo proposto è sempre lo stesso:

Image

La risposta è diversa:

Image

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:

Image

e poi eseguire Envoyer. Ottiene la seguente risposta:

Image

Il codice del servlet denominato gener2.java è il seguente:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class gener2 extends HttpServlet{
        // variabili di istanza
        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{

            // si recupera l'eventuale scelta dell'utente
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // si indica al cliente il tipo di documento inviato
            response.setContentType("text/html");
             // si invia il modulo
            PrintWriter out=response.getWriter();
            // inizio
            out.println(HTML1);
            // menu a tendina
            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>");
            }//per
            out.println("</select>");
            // continua modulo
            out.println(HTML2);
            if(! choix.equals("")){
                // viene visualizzata la scelta dell'utente
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//if
             // fine del modulo
            out.println(HTML3);
        }//GET

        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // si reindirizza a GET
            doGet(request,response);
        }//POST
    }//classe

Il metodo doGet si occupa di tutto: genera il modulo che invia al client ed elabora i valori che quest'ultimo restituisce. I punti da tenere presenti sono i seguenti:

  • si verifica se il parametro cmbValeurs ha un valore.
  • In tal caso, durante la creazione del contenuto dell’elenco, si confronta ogni elemento di quest’ultimo con la scelta dell’utente per assegnare l’attributo selected all’elemento scelto dall’utente: <option selected>elemento</option>. Inoltre, sotto il modulo viene visualizzato il valore della scelta.

3.1.6.3. Generazione dinamica di moduli - 3

Riprendiamo lo stesso problema di prima, ma questa volta i valori vengono prelevati da un database. Nel nostro esempio si tratta del database MySQL:

  • il database si chiama dbValeurs
  • il proprietario è admDbValeurs con password mdpDbValeurs
  • il database contiene un'unica tabella denominata tvaleurs
  • questa tabella ha un solo campo di tipo intero denominato «valore»
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 da un driver ODBC per MySQL. Il suo nome 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{
        // il titolo della pagina
        private final String title="Génération d'un formulaire";
         // database dei valori dell'elenco
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // valori dell'elenco
        private String[] valeurs=null;
         // messaggio di errore
        private String msgErreur=null;
        // codice 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{

            // al cliente viene indicato il tipo di documento inviato
            response.setContentType("text/html");
             // flusso di uscita
            PrintWriter out=response.getWriter();

            // L'inizializzazione del servlet è avvenuta correttamente?
            if (msgErreur!=null){
                 // si è verificato un errore - viene generata una pagina di errore
                out.println("<html><head><title>"+title+"</title></head>");
                out.println("<body><h3>Application indisponible ("+msgErreur+
                                        ")</h3></body></html>");
                return;
            }//if

             // si recupera l'eventuale scelta dell'utente
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // si invia il modulo
             // inizio
            out.println(HTML1);
            // menu a tendina
            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>");
            // continua il modulo
            out.println(HTML2);
            if(! choix.equals("")){
                // viene visualizzata la scelta dell'utente
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//se
             // fine del modulo
            out.println(HTML3);
        }//GET

        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // si reindirizza a GET
            doGet(request,response);
        }//POST

        // inizializzazione del servlet
        public void init(){
             // compila l'array di valori da un database ODBC
             // di nome DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // connessione al database ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // oggetto Statement
                st=connexion.createStatement();
                 // esecuzione della query SELECT per recuperare i valori
                rs=st.executeQuery("select valeur from Tvaleurs");
                // i valori vengono recuperati e inseriti in un array dinamico
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // il valore viene registrato nell'elenco
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // trasformazione da elenco a tabella
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                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 importanti da tenere presenti sono i seguenti:

  1. Un servlet può essere inizializzato tramite un metodo la cui firma deve essere public void init(). Questo metodo viene eseguito solo al caricamento iniziale del servlet
  2. Una volta caricata, una servlet rimane in memoria per tutto il tempo. Ciò significa che, dopo aver servito un client, non viene scaricata. In questo modo risponde più rapidamente alle richieste dei client.
  3. Nel nostro servlet, è necessario recuperare un elenco di valori da un database. Poiché tale elenco non cambia nel tempo, il metodo init rappresenta il momento ideale per recuperarlo. In questo modo, il servlet accede al database una sola volta, al momento del suo caricamento iniziale, e non ad ogni richiesta di un client.
  4. L’accesso a un database può fallire. Il metodo init del nostro servlet genera un messaggio di errore msgErreur in caso di errore. Questo messaggio viene verificato nel metodo doGet e, in caso di errore, doGet genera una pagina che lo segnala.
  5. Il codice del metodo init utilizza un accesso classico a un database con i driver Odbc-Jdbc. Se necessario, si invita il lettore a consultare i metodi di accesso ai database JDBC.

Quando si esegue il servlet e il server MySQL non è stato avviato, viene visualizzata la seguente pagina di errore:

Image

Se ora si avvia il server MySQL, si ottiene la pagina:

Image

Se si seleziona il numero 6 e si clicca su «Invia»:

Image

3.1.6.4. Recuperare i valori di un modulo

Riprendiamo un esempio già visto, quello del seguente modulo web:

Image

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");
      }//cancellare
        </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> del modulo è stato definito come segue:

    <form method="POST" action="http://localhost:8080/examples/servlet/parameters">

Il browser "invierà" i valori del modulo a URL http://localhost:8080/examples/servlet/parameters, che è il URL di un servlet gestito da Tomcat e che visualizza i valori del modulo precedente. Se si chiama direttamente la servlet parameters, si ottengono i seguenti risultati:

Image

Se il modulo balises2.htm compilato è il seguente:

Image

e si preme il pulsante Invia (di tipo submit), questa volta viene richiamata la servlet parameters con dei parametri. Essa restituisce quindi la seguente risposta:

Image

In questa risposta si ritrovano effettivamente i valori inseriti nel modulo. Il codice della servlet è il seguente:

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class parameters extends HttpServlet{
    // variabili di istanza
    String title="Récupération des paramètres d'un formulaire";

    private String getParameter(HttpServletRequest request, String contrôle){
      // restituisce il valore request.getParameter (controllo) o "" se non esiste
      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
    {
      // si inizia recuperando i parametri del modulo
      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");

      // si indica il contenuto del documento
        response.setContentType("text/html");
       // si invia il documento
        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
    {
       // reindirizza a GET
      doGet(request,response);
    }
}

In questo codice si ritrovano le tecniche presentate in precedenza in un altro esempio. Si notino due punti:

  1. il controllo lst2 è un elenco a selezione multipla e quindi è possibile selezionare più elementi. È il caso del nostro esempio, in cui sono stati selezionati gli elementi liste1 e liste3. I valori di lst2 sono stati trasmessi dal browser al server nella forma lst2=liste1&lst2=liste3. Il servlet Java può recuperare questi valori in un array con il metodo getParameterValues: in questo caso request.getParameterValues("lst2") restituisce un array di 2 stringhe ["liste1","liste3"].
  2. Il controllo areaSaisie è un campo di immissione multilinea. request.getParameter("areaSaisie") restituisce il contenuto del campo sotto forma di un'unica stringa di caratteri. Se in quest’ultima si desidera recuperare le diverse righe che la compongono, è possibile utilizzare la funzione split della classe String. Il codice seguente
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");

recupera le righe del campo di immissione. Queste righe terminano con i caratteri \r\n (0D0A).

Per eseguire i test abbiamo:

  • costruito e compilato il servlet parameters con 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'http://localhost:81/html/balises2.htme URL, il cui codice è stato presentato in precedenza
  • compilato il modulo e premuto il pulsante Envoyer.

3.1.6.5. Recuperare le intestazioni HTTP da un client web

Riprendiamo lo stesso esempio di prima, ma in risposta al client web che ha inviato i valori del modulo, gli inviamo le intestazioni HTTP che ha inviato contemporaneamente. Introduciamo una sola modifica nel nostro modulo:

    <form method="GET" action="http://localhost:8080/examples/servlet/headers">

I valori del modulo saranno inviati tramite il metodo GET a un servlet Java denominato headers, situato in <tomcat>\webapps\examples\WEB-INF\classes. Il servlet headers è stato creato e compilato insieme a 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
    {
       // si definisce la tipologia del documento
        response.setContentType("text/html");
       // si ottiene un flusso di scrittura
        PrintWriter out = response.getWriter();
      // visualizzazione dell'elenco delle intestazioni 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
}

Si richiede il codice URL http://localhost:81/html/balises2.htm e si ottiene Envoyer senza modificare il modulo. Si ottiene la seguente risposta:

Image

Si noti il valore URL presente nel campo Address del browser, che mostra il metodo (GET) utilizzato per trasmettere i parametri. Riprendiamo lo stesso esempio, ma modificando il modo in cui vengono inviati i parametri (POST):

    <form method="POST" action="http://localhost:8080/examples/servlet/headers">

Si ottiene la seguente nuova risposta:

Image

Si notino le intestazioni HTTP, content-type e content-length, caratteristiche di un invio tramite POST. Inoltre, si noti che nel campo Address del browser i valori del modulo non compaiono più.

3.2. Pagine JSP

Le pagine JSP (Java Server Pages) rappresentano un altro modo di scrivere applicazioni per server web. Infatti, queste pagine JSP vengono tradotte in servlet prima di essere eseguite, ricorrendo così alla tecnologia dei servlet. Le pagine JSP consentono di evidenziare meglio la struttura delle pagine HTML generate. Di seguito presentiamo alcuni esempi, alcuni dei quali sono accessibili seguendo il link JSP dalla pagina iniziale di Tomcat:

3.2.1. Recuperare informazioni sull’ambiente

Riprendiamo qui un esempio già trattato con una servlet: visualizzare le variabili d’ambiente di una servlet. Si tratta dell’esempio snoop degli esempi JSP:

Image

Il codice sorgente della pagina JSP si trova in <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) oppure in <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 notano i seguenti punti:

  • si tratta di un codice che assomiglia molto a HTML. Vi si trovano tuttavia i tag <%= espressione %>, che sono propri del linguaggio JSP. Il compilatore JSP sostituisce nel testo HTML l'intero tag con il valore di expression.
  • 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 con il nome del computer del client web che ha effettuato la richiesta.
  • È possibile ottenere lo stesso risultato con un servlet, ma in questo caso la struttura della pagina web risulta più chiara.

3.2.2. Recuperare i parametri inviati dal client web

Riprendiamo qui l’esempio già esaminato con una servlet. Al browser viene presentato un modulo:

Image

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

Image

Il codice della pagina JSP è il seguente:


<%
   // variabili locali della procedura principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- codice 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>
  • Se ritroviamo il tag <%= espressione %>, già presente nell'esempio precedente, compare un nuovo tag <% istruzioni Java; %>. Il tag <% introduce del codice Java. Questo codice termina quando si incontra il tag di chiusura del codice %>.
  • L'intero codice precedente (HTML + JSP) sarà oggetto di una conversione in servlet Java. Sarà racchiuso in un unico metodo, denominato metodo principale della pagina JSP. Ecco perché le variabili Java dichiarate all’inizio della pagina JSP sono note nelle altre porzioni di codice JSP che costellano il codice HTML: queste variabili e porzioni di codice faranno parte dello stesso metodo Java. Ma se il nostro codice JSP dovesse contenere dei metodi, le variabili title, firstname e lastname non sarebbero riconosciute al suo interno a causa dell’isolamento tra i metodi. Sarebbe necessario renderle variabili globali o passarle come parametri ai metodi. Torneremo su questo punto.
  • Per includere parti dinamiche nel codice HTML sono possibili due metodi: <%= espressione %> o out.println(espressione). L’oggetto out è un flusso di output analogo a quello omonimo presente negli esempi di servlet, ma non dello stesso tipo: si tratta di un oggetto JspWriter e non di un PrintWriter. Consente di scrivere nel flusso HTML utilizzando i metodi print e println.
  • La pagina JSP rispecchia meglio la struttura della pagina HTML generata rispetto al servlet equivalente.

3.2.3. I tag JSP

Di seguito è riportato un elenco dei tag che si possono trovare in una pagina JSP e il loro significato.

tag
significato
<!-- commento -->
commento HTML. Viene inviato al client.
<%-- commento --%>
commento JSP. Non viene inviato al cliente.
<%! déclarations, méthodes %>
Dichiara variabili globali e metodi. Le variabili saranno visibili in tutti i metodi
<%= expression %>
Il valore dell'espressione verrà inserito nella pagina HTML al posto del tag
<% code Java %>
contiene codice Java che farà parte del metodo principale della pagina JSP
<%@ page attribut1=valeur1
attribut2=valeur2 … %>
imposta gli attributi per la pagina JSP. Ad esempio:
import="java.util.*,java.sql.*" per specificare le librerie necessarie alla pagina JSP
extends="unaClasseParent" per far derivare la pagina JSP da un'altra classe

3.2.4. Gli oggetti impliciti JSP

Negli esempi precedenti abbiamo incontrato due oggetti non dichiarati: request e out. Si tratta di due degli oggetti che vengono definiti automaticamente nel servlet in cui viene convertita la pagina JSP. Vengono chiamati oggetti impliciti o predefiniti. Ne esistono altri, ma questi sono i più utilizzati insieme all’oggetto response:

oggetto
significato
HttpServletRequest request
l’oggetto tramite il quale si accede alla richiesta del client Web (getParameter, getParameterNames, getParameterValues)
HttpServletResponse response
l'oggetto con cui è possibile costruire la risposta del server Web al proprio client. Consente di impostare le intestazioni HTTP da inviare al client Web.
JspWriter out
il flusso di output che ci permette di inviare il codice HTML al client (print, println)

3.2.5. La trasformazione di una pagina JSP in un servlet

Riprendiamo il codice JSP da myRequestParamExample.jsp:


<%
   // variabili locali della procedura principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- codice 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 al server Tomcat, quest'ultimo la trasformerà in un servlet. Se la pagina URL richiesta è

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:

Image

In questo nome si ritrova l'http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jspe URL della pagina JSP. Come si vede sopra, è possibile 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();


                   // variabili locali della procedura principale
                  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 limiteremo a sottolineare i seguenti punti:

  • Il metodo principale del servlet è il seguente:
    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

È questo il metodo che viene eseguito all'avvio del servlet. Si nota che riceve due parametri: la richiesta request proveniente dal client e un oggetto response per generare la risposta al client web.

  • Nel metodo principale viene dichiarato e poi inizializzato un oggetto JspWriter denominato «out». È proprio questo oggetto che consentirà di inviare il codice HTML al client tramite le istruzioni out.print("codeHTML").
        JspWriter out = null;
...
            out = pageContext.getOut();
  • Il codice Java

<%
   // variabili locali della procedura principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

è stato ripreso integralmente nel metodo principale _jspService del servlet. Lo stesso vale per tutto il codice contenuto tra i tag <%… %>

  • Il codice HTML della pagina JSP è oggetto delle 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 altri metodi oltre al metodo principale _jspService.

3.2.6. Metodi e variabili globali di una pagina JSP

Consideriamo la seguente pagina JSP:


<%!
   // il tag precedente dà inizio alla sezione delle variabili e dei metodi globali
   // questa sezione verrà ripresa senza modifiche nel servlet
  
   // una variabile globale
  String prenom="inconnu";

   // un metodo  
  private String sonChien(){
    return "milou";
  }//sonChien

   // un altro metodo
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   // fine della parte globale del servlet
%>  

<%
   // il tag precedente indica che il codice che segue verrà salvato
   // nel metodo principale del servlet
  
   // variabile locale del metodo principale
  String nom="tintin";
%>


<%-- codice 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>
      <%
         // il nome del suo amico
        afficheAmi(out);
      %>
    </center>
  </body>
</html>

Questa pagina JSP genera la seguente pagina web:

Image

Vediamo 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>
      <%
         // il nome del suo amico
        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 hanno accesso alle variabili nom, prenom e ai metodi sonChien e afficheAmi?

nom (tintin)
è una variabile locale del metodo principale della pagina JSP e quindi nota al suo interno
prenom (inconnu)
è una variabile globale della pagina JSP e quindi è nota nel metodo principale
sonChien (milou)
è un metodo pubblico della pagina JSP ed è quindi accessibile dal metodo principale
afficheAmi (Haddock)
è un metodo pubblico della pagina JSP e quindi accessibile dal metodo principale. 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 principale del servlet e non è una variabile globale.

Vediamo ora il codice del servlet Java generato a partire da questa pagina JSP, una volta eliminato 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 {

          // il tag precedente dà inizio alla sezione delle variabili e dei metodi globali
           // questa sezione verrà ripresa senza modifiche nel servlet

           // una variabile globale
          String prenom="inconnu";

           // un metodo  
          private String sonChien(){
            return "milou";
          }//sonChien

           // un altro metodo
          private void afficheAmi(JspWriter out) throws Exception{
            out.println("<p>Son ami s'appelle Haddock</p>");
          }//afficheAmi

           // fine della parte globale del 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");
                  // il tag precedente indica che il codice che segue verrà salvato
                   // nel metodo principale del servlet

                   // variabile locale del metodo principale
                  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      ");

                        // il nome del suo amico
                        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 si può vedere sopra, il codice Java che si trovava tra i tag JSP <%! .. %> è stato ripreso integralmente e non fa parte del metodo principale _jspService del servlet. Le variabili dichiarate in questa sezione sono quindi variabili di istanza, ovvero globali per i metodi, ed è proprio qui che è possibile definire metodi diversi da _jspService.


  // questa parte verrà ripresa senza modifiche nel servlet

   // una variabile globale
  String prenom="inconnu";

   // un metodo
  private String sonChien(){
    return "milou";
  }//sonChien

   // un altro metodo
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   // fine della parte globale del servlet

3.2.7. Implementazione 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 ad albero del server. Esistono diversi modi per farlo, sui quali torneremo in seguito. Per il momento, il modo più semplice è collocare la pagina JSP in una cartella della struttura <tomcat>\webapps\examples\jsp (Tomcat 4.x), dove <tomcat> è la directory di installazione di Tomcat. Pertanto, la pagina URL dell’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 la pagina JSP viene richiesta da un browser. Possono verificarsi errori di compilazione. Tomcat 4.x li segnala nella sua risposta al browser. In particolare, indica le righe del file .java che contengono errori. Gli errori possono avere diverse cause:

  1. il codice JSP della pagina è errato (ad esempio, errori nei tag JSP utilizzati)
  2. il codice Java incluso nella pagina JSP è errato

La prima causa può essere eliminata verificando il codice JSP della pagina. La seconda può essere eliminata verificando il codice Java. Ciò può essere fatto compilando direttamente il file .java generato per la pagina JSP con uno strumento come JBuilder, che offre funzionalità di debug più avanzate rispetto a quelle di Tomcat.

3.2.8. Esempi

Riprendiamo l’esempio già trattato con un servlet in cui un utente sceglie un numero da un elenco e il server gli comunica quale numero ha scelto, restituendogli al contempo lo stesso elenco con l’elemento selezionato dall’utente:

Image

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 il codice HTML
  • il codice Java che generava il codice HTML è stato trasformato in un mix di codice HTML e codice JSP

Si ottiene quindi la seguente pagina JSP:



<%@ page import="java.sql.*, java.util.*" %>

<%!

         // variabili globali dell'applicazione
         // il titolo della pagina
        private final String title="Génération d'un formulaire";
         // il database dei valori dell'elenco
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // valori dell'elenco
        private String[] valeurs=null;
         // messaggio di errore
        private String msgErreur=null;
    
         // inizializzazione della pagina JSP - viene eseguita una sola volta
        public void jspInit(){
             // compila la tabella dei valori da un database ODBC
             // denominato DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // connessione al database ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // oggetto Statement
                st=connexion.createStatement();
                 // esecuzione della query SELECT per recuperare i valori
                rs=st.executeQuery("select valeur from Tvaleurs");
                // i valori vengono recuperati e inseriti in un array dinamico
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // il valore viene registrato nell'elenco
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // trasformazione da elenco a tabella
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//inizializzazione
   
%>

<%
     // codice di _jspService eseguito ad ogni richiesta del client
   // si è verificato un errore durante l'inizializzazione della pagina JSP?
  if(msgErreur!=null){
%>
       <!-- codice HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %></h3>
      </body>
    </html>
<%
       // fine di jspService
    return;
  }//if
  
     // si recupera l'eventuale scelta dell'utente
    String choix=request.getParameter("cmbValeurs");
    if(choix==null) choix="";
%>
  
   <%-- nessun errore - codice HTML della pagina normale --%>
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST">
            <select name="cmbValeurs">
              <%
                 // visualizzazione dinamica dei valori
                          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>");
                          }//per
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
<%
         // è stato selezionato un valore?
                if(! choix.equals("")){
        // viene visualizzata la scelta dell'utente
%>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%        
                }//if
%>        
      </body>
    </html>

Si notino i seguenti punti:

  • le istruzioni import del servlet sono state inserite all'interno di una direttiva <% page import="..." %>
  • il tag <%! ... %> racchiude le variabili globali e i metodi Java dell’applicazione
  • il metodo init del servlet, che viene eseguito una sola volta al momento del caricamento del servlet, viene chiamato per una pagina JSP: jspInit. Questi due metodi hanno la stessa funzione. Abbiamo quindi riportato qui integralmente il codice del metodo init del servlet.
  • Le variabili di istanza della servlet, ovvero quelle che devono essere note in più metodi, sono state riprese identiche. Si tratta essenzialmente delle variabili title, valeurs e msgErreur, che vengono poi utilizzate nel codice JSP.
  • I tag <% ... %> racchiudono il codice Java che verrà incluso nel metodo _jspService eseguito al momento di una richiesta da parte di un client.
  • Come per il servlet, il metodo _jspService inizierà verificando il valore della variabile msgErreur per determinare se deve generare una pagina di errore. In caso di errore, genera la pagina di errore e si interrompe (return).
  • Se non si verifica alcun errore, genera il modulo con l’elenco dei valori
  • fatto ciò, verifica se l’utente ha selezionato un numero; in tal caso, visualizza tale numero nella pagina generata

Cosa abbiamo guadagnato rispetto al servlet? Senza dubbio una migliore leggibilità del codice HTML generato. Tuttavia, rimane ancora molto codice Java che “inquinano” questa visualizzazione. Vedremo in seguito un altro metodo chiamato “delega”, in cui potremo collocare la maggior parte del codice Java in una servlet, mentre la pagina JSP manterrà solo il codice HTML e JSP. In questo modo si separa nettamente la parte di elaborazione da quella di presentazione.

3.3. Distribuzione di un'applicazione web sul server Tomcat

Presentiamo ora come distribuire applicazioni web Java con il server Tomcat. Sebbene quanto verrà illustrato sia specifico per questo server, la distribuzione di un’applicazione web Java all’interno di un altro contenitore J2EE presenterà caratteristiche simili a quelle che verranno descritte di seguito.

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 <tomcat>\webapps\examples\jsp. All'epoca erano accessibili tramite l'URL http://localhost:8080/examples/jsp/nomPageJSP

Non abbiamo mai spiegato il motivo di questa scelta. La configurazione del server Tomcat è definita in un file di testo denominato server.xml, che si trova nella cartella <tomcat>\conf:

Image

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 del linguaggio HTML sono ben definiti, quelli del linguaggio XML non lo sono. Pertanto, il documento seguente è un documento XML:

<personne>
    <prenom>Pierre</prenom>
  <nom>Lucas</nom>
  <age>28</age>
</personne>

Un documento XML è semplicemente un documento "con tag" che segue determinate regole di marcatura:

  • un testo con tag nella forma <xx att1="val1" att2="val2" ....>testo</xx>
  • un tag può essere singolo e avere la forma <xx att1="val1" att2="val2" ..../>

I campi "atti" sono chiamati attributi del tag xx e i campi "vali" 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 esserlo, dovrebbe essere scritto come <br/>, in modo da rispettare la regola secondo cui ogni tag XML deve essere chiuso. È stata creata una variante di HTML denominata XHTML al fine di rendere ogni documento XHTML un documento XML valido. Alcuni browser recenti sono in grado di visualizzare i file XML. Pertanto, se chiamiamo personne.xml il documento XML presentato nell’esempio sopra riportato e lo visualizziamo con IE6, otteniamo la seguente visualizzazione:

Image

IE6 riconosce i tag e li evidenzia a colori. Riconosce inoltre la struttura del documento grazie ai tag. Pertanto, se chiamiamo personne2.xml il seguente documento:

<personne><prenom>Pierre</prenom><nom>Lucas</nom><age>28</age></personne>

e lo visualizziamo con IE6, otteniamo la stessa visualizzazione:

Image

IE6 ha riconosciuto correttamente la struttura e il contenuto del documento. Il vero vantaggio del documento XML risiede proprio in questa proprietà: è facile recuperare la struttura e il contenuto di un documento XML. Ciò avviene tramite un programma chiamato parser XML. I documenti XML stanno diventando lo standard negli scambi di documenti sul web. Prendiamo ad esempio un computer A che deve inviare un documento DOC a un computer B. Il documento DOC viene generato a partire dalle informazioni contenute in un database DB-A. Il computer B, dal canto suo, deve archiviare il documento DOC in un database DB-B. Lo scambio potrà avvenire nel modo seguente:

  • il computer A recupera i dati dal database DB-A e li incapsula in un documento di testo XML
  • il documento XML viene inviato alla macchina B attraverso la rete
  • il computer B analizza il documento ricevuto con un parser XML e ne 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 ci soffermeremo oltre sul linguaggio XML, che meriterebbe un libro a sé stante.

In questo caso, quindi, Tomcat è configurato dal file XML server.xml. Se lo visualizziamo con IE6, otteniamo un documento complesso. Ci soffermeremo semplicemente sulle seguenti righe:

Image

È il tag <Context ...> che ci interessa in questo caso. Serve a 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. In questo caso si tratta di un nome relativo: examples. Relativo a quale cartella? La risposta si trova anch’essa nel file server.xml nella riga seguente:

Image

La riga sopra riportata definisce il server web:

  • name: nome del server web
  • appBase: radice dell’albero dei documenti che distribuisce. Anche in questo caso abbiamo un nome relativo: webapps. È relativo alla directory di installazione del server Tomcat <tomcat>. Si tratta quindi della cartella <tomcat>\webapps.

L'applicazione web examples contiene i propri documenti nella cartella examples (cfr. docBase sopra). Questo nome è relativo alla radice dell’albero web del server, c.a.d. <tomcat>\webapps. Si tratta quindi della cartella <tomcat>\webapps\examples. Diamo un’occhiata più da vicino a questa cartella:

Image

Qui troviamo la cartella WEB-INF\classes in cui abbiamo salvato i nostri servlet per testarli. La cartella WEB-INF contiene un file denominato web.xml:

Image

Questo file serve a configurare l’applicazione web examples. Per il momento non entreremo nei dettagli di questo file, che risulta troppo complesso. Ci soffermeremo semplicemente sulle seguenti righe:

    <servlet>
      <servlet-name>
          servletToJsp
      </servlet-name>
      <servlet-class>
          servletToJsp
      </servlet-class>
    </servlet>

Il tag <servlet> serve a definire un servlet all'interno di un'applicazione web. Ricordiamo che l'applicazione web in questione è examples. Il tag servlet contiene qui 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 richiesta la servlet. In questo esempio, la servlet e la sua classe hanno lo stesso nome. Ciò non è obbligatorio.

In che modo il browser richiede la servlet servletToJsp al server Tomcat?

  • Il browser richiede la servlet URL all'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 proprio file di configurazione server.xml dove sono stati archiviati i documenti di tale applicazione. Come abbiamo visto in precedenza, 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 è associato alla classe Java servletToJsp (cfr. il file web.xml sopra). Cercherà quindi questa classe nella cartella WEB-INF\classes dell’applicazione web examples, c.a.d. in <tomcat>\webapps\examples\WEB-INF\classes e la eseguirà.

Image

3.3.2. Esempio: distribuzione dell’applicazione web “lista”

Riprendiamo una servlet già esaminata in precedenza, che presentava all’utente un elenco di numeri tra i quali ne sceglieva uno. La servlet gli confermava quindi il numero che aveva scelto:

Image

Come mostra il campo Address del browser sopra riportato, il file di classe della servlet si chiamava gener3. In base alle spiegazioni fornite in precedenza:

  • URL /examples/servlet/gener3 indica che si tratta di una servlet denominata gener3 dell’applicazione web examples
  • nel file web.xml dell’applicazione examples, non si trova nulla che faccia riferimento a una servlet gener3. Come ha fatto allora Tomcat a trovarla? Avendo esaminato l’intero file web.xml, non posso rispondere con certezza... La domanda rimane aperta...

Abbiamo deciso di distribuire la servlet gener3.class con il nome lstValeurs in un’applicazione web denominata «liste», situata nella cartella E:\data\serge\Servlets\lstValeurs:

Image

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

Image

Configuriamo l'applicazione web «liste» aggiungendo nel file server.xml le seguenti righe sopra quelle che definiscono l'applicazione web manager:

                 <!-- Utente: lstValeurs -->
                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

                <!-- Contesto Tomcat Manager -->
                <Context path="/manager" docBase="manager" debug="0" privileged="true" />
                <!-- Contesto esempi Tomcat -->
                <Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........

La riga che definisce l'applicazione indica che essa si trova nella cartella e:/data/serge/servlets/lstValeurs. Ora dobbiamo definire il file web.xml di 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:

Image

La schermata sopra riportata mostra una cartella src in cui è stato inserito il file sorgente gener3.java. Questa cartella potrebbe non esistere. Non ha alcuna utilità nella presente dimostrazione. Siamo pronti per eseguire i test:

  • arrestate e riavviate Tomcat affinché rilegga il proprio file di configurazione server.xml. Qui stiamo utilizzando Windows. Su Unix, è possibile forzare Tomcat a rileggere il proprio file di configurazione senza arrestarlo.
  • Con un browser, richiedete l'URL http://localhost:8080/liste/servlet/lstValeurs

Image

Si nota che il precedente URL contiene la parola chiave servlet, come tutti i URL dei servlet utilizzati finora. È possibile farne a meno associando, nel file web.xml dell’applicazione «liste», il servlet lstValeurs a un modello di URL (url-pattern):

<?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 /valori al servlet lstValeurs definito nelle righe precedenti. Salviamo il nuovo file web.xml e richiediamo il file URL http://localhost:8080/liste/valeurs:

Image

3.3.3. Distribuzione delle pagine pubbliche di un’applicazione web

Abbiamo appena visto l'implementazione di un'applicazione web costituita da un unico servlet. Un'applicazione web può avere numerosi componenti: servlet, pagine JSP, file HTML, applet Java, ... Dove si collocano questi elementi dell'applicazione? Se <application> è la cartella dell’applicazione web definita dall’attributo docBase dell’applicazione nel file di configurazione di Tomcat server.xml, abbiamo visto che i servlet venivano collocati in <application>\WEB-INF\classes. Gli altri elementi dell’applicazione possono essere collocati ovunque nella struttura della cartella <application>, tranne che nella cartella WEB-INF. Consideriamo l’applicazione JSP listvaleurs.jsp già esaminata:

Image

Questa pagina JSP era stata salvata nella cartella <tomcat>\webapps\examples\jsp\perso\listvaleurs. Questa pagina potrebbe essere una componente dell’applicazione «liste» distribuita in precedenza. Collochiamo il file listvaleurs.jsp direttamente nella cartella di questa applicazione:

Image

Ricordiamo la configurazione dell’applicazione liste nel file server.xml:

                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

Ogni URL che inizia con il percorso /liste è considerato parte dell'applicazione liste e verrà cercato nella cartella indicata. Proviamo a richiamare il file URL http://localhost:8080/liste/listvaleurs.jsp con un browser:

Image

Abbiamo ottenuto la pagina JSP che ci aspettavamo.

3.3.4. Parametri di inizializzazione di un servlet

Abbiamo visto che un servlet viene configurato tramite il file <application>\WEB-INF\web.xml, dove <application> è la cartella dell’applicazione web a cui appartiene. È possibile includere in questo file i parametri di inizializzazione della servlet. Torniamo alla nostra 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. Nel codice sorgente di quest'ultima si trovano le definizioni di alcune costanti:

public class gener3 extends HttpServlet{
        // il titolo della pagina
        private final String title="Génération d'un formulaire";
         // il database dei valori dell'elenco
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";

Ricordiamo il significato delle quattro costanti definite sopra:

title
titolo del documento HTML generato dal servlet
DSNValeurs
nome DSN del database ODBC da cui il servlet preleva i dati
admDbValeurs
nome di un utente con diritti di lettura sul database precedente
mdpDbvaleurs
la sua 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 del servlet web.xml offre un’alternativa, consentendo di definire i parametri di inizializzazione del servlet tramite il tag <init-param>:

    <init-param>
        <param-name>...</param-name>
        <param-value>...</param-value>
    </init-param>
<param-name>
consente di definire il nome del parametro
<param-value>
definisce il valore associato al parametro precedente

Il servlet può accedere ai propri parametri di inizializzazione tramite i seguenti metodi:

[Servlet].getServletConfig()
metodo della classe Servlet da cui deriva la classe HttpServlet utilizzata per la programmazione web. Restituisce un oggetto ServletConfig che consente di accedere ai parametri di configurazione del servlet.
[ServletConfig].getInitParameter("paramètre")
Metodo della classe ServletConfig che restituisce il valore del parametro di inizializzazione "parametro"

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 liste, definiamo un secondo servlet denominato lstValeurs2 collegato al file di classe gener5. Quest'ultimo è stato inserito in <application>\WEB-INF\classes:

Image

Il servlet lstValeurs2 ha quattro parametri di inizializzazione: title, DSNValeurs, admDbValeurs, mdpDbValeurs. Inoltre, per la servlet è stato definito l’alias /valeurs2 tramite il tag <servlet-mapping>. Pertanto, la servlet lstValeurs2 dell'applicazione liste sarà accessibile tramite URL e 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{
    // titolo della pagina
    private String title=null;
    // il database dei valori dell'elenco
    private String DSNValeurs=null;
    private String admDbValeurs=null;
    private String mdpDbValeurs=null;
...............

         // inizializzazione del servlet
        public void init(){
             // si recuperano i parametri di inizializzazione del servlet
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            DSNValeurs=config.getInitParameter("DSNValeurs");
            admDbValeurs=config.getInitParameter("admDbValeurs");
            mdpDbValeurs=config.getInitParameter("mdpDbValeurs");

             //sono stati recuperati tutti i parametri?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // si compila l'array dei valori da un database ODBC
             // denominato DSN: DSNvaleurs
...............

Per testare il servlet, è necessario riavviare Tomcat affinché riconosca il nuovo file di configurazione web.xml dell'applicazione liste. Da un browser, si richiede l'URL della servlet http://localhost:8080/liste/valeurs2:

Image

Se uno dei parametri di inizializzazione necessari al servlet è assente dal file web.xml, si ottiene la seguente pagina:

Image

3.3.5. Parametri di inizializzazione di un'applicazione web

Nell'esempio precedente, solo il servlet lstValeurs2 ha accesso ai parametri title, DSNValeurs, admDbValeurs, mdpDbValeurs. Si potrebbe ipotizzare che un’altra servlet della stessa applicazione, liste, abbia bisogno dei dati presenti nello stesso database utilizzato dalla servlet lstValeurs2. In tal caso, sarebbe necessario ridefinire i parametri DSNValeurs, admDbValeurs, mdpDbValeurs nella sezione di configurazione del file web.xml della nuova servlet. Un’altra soluzione consiste nel definire i parametri comuni a più servlet a livello di applicazione e non più a livello dei singoli servlet. Il nuovo file web.xml dell’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 (servlet-mapping). 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 serve a definire informazioni specifiche dell’applicazione e non di una particolare servlet o pagina JSP. In che modo la servlet Java ha accesso a questi parametri, spesso denominati parametri di contesto? I metodi disponibili per ottenere le informazioni di contesto sono molto simili a quelli utilizzati per ottenere i parametri di inizializzazione specifici di una servlet:

[Servlet].getServletContext()
metodo della classe Servlet da cui deriva la classe HttpServlet utilizzata per la programmazione web. Restituisce un oggetto ServletContext che consente di accedere ai parametri di configurazione dell’applicazione
[ServletContext].getInitParameter("paramètre")
metodo della classe ServletContext che restituisce il valore del parametro di inizializzazione "parametro"

La classe gener6.java apporta le sole seguenti modifiche al codice Java di gener5.java utilizzato in precedenza:

             // si recuperano i parametri di inizializzazione del servlet
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");

            ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //: sono stati recuperati tutti i parametri?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // si popola l'array dei valori da un database ODBC
             // denominato DSN: DSNvaleurs
...............

Il parametro title specifico del servlet viene ottenuto tramite un oggetto ServletConfig. Gli altri tre parametri definiti a livello di applicazione vengono invece ottenuti tramite un oggetto ServletContext. Compiliamo questa classe e la inseriamo, come le altre, in <application>\WEB-INF\classes:

Image

Riavviamo Tomcat affinché riconosca il nuovo file web.xml dell’applicazione e richiediamo URL e http://localhost:8080/liste/valeurs3:

Image

3.3.6. Parametri di inizializzazione di una pagina JSP

Abbiamo visto come definire i parametri di inizializzazione per un servlet o un'applicazione web. È possibile fare lo stesso per una pagina JSP? Torniamo all'inizio del codice della pagina listvaleurs.jsp già esaminata:

<%@ page import="java.sql.*, java.util.*" %>

<%!
         // variabili globali dell'applicazione
         // il titolo della pagina
        private final String title="Génération d'un formulaire";
         // database dei valori dell'elenco
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
.........

Le quattro costanti title, DSNValeurs, admDbValeurs e mdpDbValeurs sono definite nel file web.xml dell'applicazione. Le costanti DSNValeurs, admDbValeurs e mdpDbValeurs sono state ora definite a livello di applicazione e si può quindi supporre che una pagina JSP facente parte di questa applicazione vi avrà accesso. È proprio così. Sappiamo che la pagina JSP verrà tradotta in un servlet. Quest’ultimo avrà accesso al contesto tramite il metodo getServletContext(). Più delicato è il caso della costante title. Infatti l’abbiamo definita a livello di servlet e non a livello di applicazione nel modo seguente:

    <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ù adeguata, poiché non esiste più il concetto di file di classe. La sintassi di configurazione di una pagina JSP è tuttavia molto simile a quella di un servlet. È la seguente:

    <servlet>
      <servlet-name>JSPlstValeurs</servlet-name>
    <jsp-file>/listvaleurs2.jsp</jsp-file>
...
  </servlet>

In pratica, una pagina JSP è equiparata a un servlet a cui viene assegnato un nome (servlet-name). Anziché associare un file di classe a questo servlet, si associa il file sorgente della pagina JSP da eseguire (jsp-file). Pertanto, le poche righe precedenti definiscono una servlet denominata JSPlstvaleurs associata alla pagina JSP /listvaleurs2.jsp. Il percorso /listvaleurs2.jsp è misurato rispetto alla radice dell’applicazione. Pertanto, nel caso della nostra applicazione liste, il file listvaleurs2.jsp si troverebbe nella cartella docBase (cfr. server.xml) dell’applicazione liste:

Image

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 JSPlstValeurs (servlet-name), a sua volta associato all’alias /jspvaleurs (servlet-mapping). Pertanto, la nostra pagina JSP sarà accessibile tramite URL http://localhost:8080/liste/jspvaleurs.

La pagina iniziale JSP, originariamente denominata listvaleurs.jsp, viene modificata in listvaleurs2.jsp e recupera i suoi quattro parametri di inizializzazione nel metodo jspInit():

<%!
         // variabili globali dell'applicazione
         // titolo della pagina
        private String title=null;
        // il database dei valori dell'elenco
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // valori dell'elenco
        private String[] valeurs=null;
         // messaggio di errore
        private String msgErreur=null;

         // inizializzazione della pagina JSP - viene eseguita una sola volta
        public void jspInit(){

             // vengono recuperati i parametri di inizializzazione del servlet
      ServletConfig config=getServletConfig();
            title=config.getInitParameter("JSPtitle");
      ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //: sono stati recuperati tutti i parametri?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // compila l'array dei valori da un database ODBC
             // denominato 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:

Image

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

Image

3.3.7. Integrazione tra servlet e pagine JSP all’interno di un’applicazione web

Quando un client invia una richiesta a un server web, la risposta può essere elaborata da più servlet e pagine JSP. Finora, la risposta veniva elaborata da un unico servlet o da un'unica pagina JSP. Abbiamo visto che la pagina JSP garantiva una migliore leggibilità della struttura del documento HTML generato. Tuttavia, essa contiene in genere anche molto codice Java. È possibile migliorare la situazione inserendo

  • in uno o più servlet il codice Java che non genera il codice HTML della risposta
  • nelle pagine JSP, il codice di generazione dei vari documenti HTML inviati in risposta al client

In questo modo si spera di migliorare la separazione tra codice Java e codice HTML. Applicheremo questa nuova struttura alla nostra applicazione liste: un servlet Java lstValeurs4 avrà il compito di leggere all’avvio i valori presenti nel database e successivamente di analizzare le richieste dei clienti. A seconda del risultato di tale analisi, la richiesta del cliente verrà indirizzata verso una pagina di errore erreur.jsp o verso la pagina di visualizzazione dell’elenco di numeri liste.jsp. L’applicazione liste sarà quindi costituita da un servlet e da due pagine JSP.

In che modo una servlet può inoltrare la richiesta ricevuta da un client a un’altra servlet o a una pagina JSP? Utilizzeremo i seguenti metodi:

[ServletContext].getRequestDispatcher(
String url)
metodo della classe ServletContext che restituisce un oggetto RequestDispatcher. Il parametro url è il nome della servlet URL a cui si desidera trasmettere 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 web di tale applicazione.
[RequestDispatcher].forward
(ServletRequest request,
 ServletResponse response)
Metodo dell'interfaccia RequestDispatcher che trasmette al precedente URL la richiesta request del cliente e l'oggetto response che deve essere utilizzato per elaborare la risposta.
[ServletRequest].setAttribute(String nom, Object obj)
quando un servlet o una pagina JSP inoltra una richiesta a un altro servlet o a un'altra pagina JSP, in genere deve trasmettere a quest'ultimo altre informazioni oltre alla semplice richiesta del cliente, informazioni derivanti dal proprio lavoro sulla richiesta. Il metodo setAttribute della classe ServletRequest consente di aggiungere attributi all’oggetto request del client in una forma simile a un dizionario di coppie (attributo, valore), dove attribut è il nome dell’attributo e valeur è un oggetto qualsiasi che ne rappresenta il valore.
[ServletRequest].getAttribute(
String attribut)
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 che vi sono state aggiunte.

Il servlet incaricato di elaborare il 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:

title
il titolo del documento HTML da generare
JSPerreur
il codice di errore della pagina
JSPliste
il codice URL della pagina JSP contenente l'elenco di numeri
URLservlet
URL associata all'attributo action del modulo presentato dalla pagina JSPliste. Questa URL sarà quella del servlet lstValeurs4

La servlet avrà l'alias /valeurs4 (servlet-mapping) e sarà quindi accessibile tramite URL http://localhost:8080/liste/valeurs4. È collegata 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{
        // il titolo della pagina
        private String title=null;
        // il database dei valori dellelenco
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // le pagine di visualizzazione JSP
        private String JSPerreur=null;
        private String JSPliste=null;
        // il URL del servlet
        private String URLservlet=null;
        // valori dell'elenco
        private String[] valeurs=null;
         // messaggio di errore
        private String msgErreur=null;

        // -----------------------------------------------------------------
        // GET
        public void doGet(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // si inserisce msgErreur,title negli attributi della richiesta
            request.setAttribute("msgErreur",msgErreur);
            request.setAttribute("title",title);
            request.setAttribute("URLservlet",URLservlet);

             // Si è verificato un errore durante il caricamento del servlet?
            if(msgErreur!=null){
                 // si reindirizza a una pagina di errore JSP
                getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
                 // fine
                return;
            }

             // Non si è verificato alcun errore
             // si inserisce l'elenco dei valori negli attributi della richiesta
            request.setAttribute("valeurs",valeurs);

             // si recupera l'eventuale scelta dell'utente
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";
            request.setAttribute("choix",choix);

             // si passa il controllo alla pagina JSP di presentazione dell'elenco
            getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
            // fine
            return;
        }//GET

        // -----------------------------------------------------------------
        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

             // si rimanda a GET
            doGet(request,response);
        }//POST

        // -----------------------------------------------------------------
         // inizializzazione del servlet
        public void init(){

             // si recuperano i parametri di inizializzazione del servlet
            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");


             //sono stati recuperati tutti i parametri?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // compilazione dell'array di valori da un database ODBC
             // denominato DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // connessione al database ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // oggetto Statement
                st=connexion.createStatement();
                 // esecuzione della query SELECT per recuperare i valori
                rs=st.executeQuery("select valeur from Tvaleurs");
                // i valori vengono recuperati e inseriti in un array dinamico
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // il valore viene registrato nell'elenco
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // trasformazione da elenco a tabella
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                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

La novità di questa classe consiste nell'inoltro della richiesta del client alla pagina JSPerreur in caso di errore e alla pagina JSPliste in caso contrario. La classe non elabora autonomamente la risposta. Di questo si occupano le pagine JSP, JSPerreur e JSPliste. In precedenza, il servlet aggiungeva degli attributi (setAttribute) alla richiesta del client:

  • un messaggio di errore msgErreur in caso di errore nella pagina JSPerreur
  • i valori (valeurs) da visualizzare, il valore selezionato (choix) dall'utente, il titolo (title) del modulo, l'URL (URLservlet) dell’attributo action del modulo per la pagina JSPliste

Questa classe viene compilata e inserita nelle classi dell’applicazione:

Image

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 erreur.jsp e si trova nella directory principale dell'applicazione:

Image

Ha come alias /JSPerreur, il che lo rende accessibile tramite l'http://localhost:8080/liste/JSPerreur URL. Ha un parametro di inizializzazione denominato mainServlet, il cui valore è l’alias del servlet principale descritto in precedenza. Si noti che questo alias è relativo alla radice dell’applicazione liste,; altrimenti si avrebbe /liste/valeurs4.. Il codice della pagina erreur.jsp è il seguente:

<%
     // codice di _jspService
   // si recupera il parametro di inizializzazione mainServlet
  String servletListValeurs=config.getInitParameter("mainServlet");
   // si recupera l'attributo msgErreur
  String msgErreur=(String)request.getAttribute("msgErreur");
   // attributo valido?
  if(msgErreur!=null){
%>
       <!-- codice HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %>)</h3>
      </body>
    </html>
<%
    } else { // attributo msgErreur non valido - ritorno al servlet principale
%>
    <jsp:forward page="<%= servletListValeurs %>" />  
<%    
  }
%>  

Questa pagina dovrebbe normalmente essere richiamata dal servlet precedente, che deve passarle l'attributo msgErreur. Tuttavia, nulla impedisce di richiamarla direttamente se se ne conosce il URL. Inoltre, se si rileva che l’attributo msgErreur è assente, la richiesta viene inoltrata al servlet principale. In questo caso, si utilizza un tag specifico per le pagine JSP, la cui sintassi è:

<jsp:forward page="URL" />

dove URL è l'URL del servlet a cui viene trasferita la richiesta del client. Se l'attributo msgErreur è 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:

Image

Il servlet ha come alias /JSPliste, il che lo rende accessibile tramite l'http://localhost:8080/liste/JSPliste URL. Ha un parametro di inizializzazione denominato mainServlet, il cui valore è l'alias del servlet principale. Il codice della pagina liste.jsp è il seguente:

   <%-- pagina di visualizzazione dell'elenco dei valori --%>
  <%
       // codice di jspService
       // si recupera il parametro di inizializzazione
      String servletListValeurs=config.getInitParameter("mainServlet");

     // si recuperano gli attributi della richiesta proveniente dal 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");

     // attributi validi?
    if(title==null || valeurs==null || choix==null){
         // c'è un attributo non valido - si passa il controllo al servlet
   %>
   <jsp:forward page="<%= servletListValeurs %>" />
    <%
    }//if
  %>

  <%-- codice HTML --%>  
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST" action="<%= URLservlet %>">
            <select name="cmbValeurs">
              <%
                 // visualizzazione dinamica dei valori
                          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>
                <%
             // è stato selezionato un valore?
                    if(! choix.equals("")){
                // viene visualizzata la scelta dell'utente
                %>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
                <%        
                    }//if
                %>        
      </body>
    </html>

Questa pagina funziona come la pagina erreur.jsp. Normalmente deve essere richiamata dal servlet /liste/valeurs4 e ricevere gli attributi title, valeurs e choix. Se uno di questi parametri manca, il controllo passa alla servlet URLservlet (/liste/valeurs4). Se tutti i parametri sono presenti, viene visualizzato l’elenco dei numeri insieme al numero scelto dall’utente, qualora ne avesse selezionato uno.

Se si richiama il URL dal servlet principale, si ottiene il seguente risultato:

Image

con il seguente codice sorgente (View/Source):

<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. Si nota che gli attributi title, valeurs, URLservlet sono stati correttamente recuperati.

Per concludere in merito all’interazione tra servlet e pagine JSP, si osserva che le pagine JSP sono in questo caso molto brevi e prive del codice Java che non contribuisce direttamente alla creazione della risposta HTML. La struttura dei documenti generati risulta così più chiara.

3.4. Ciclo di vita dei servlet e delle pagine JSP

3.4.1. Il ciclo di vita

Qui ci interessiamo al ciclo di vita dei servlet. Quello delle pagine JSP ne deriva. Consideriamo un servlet chiamato per la prima volta. Il server Web crea quindi un’istanza della classe e la carica in memoria. A quel punto, il servlet elaborerà la richiesta. Una volta fatto ciò, il servlet non viene scaricato dalla memoria. Rimane in memoria per gestire altre richieste, al fine di ottimizzare i tempi di risposta del server. Verrà scaricata quando sarà trascorso un periodo di tempo sufficientemente lungo senza che abbia gestito nuove richieste. Questo tempo è generalmente configurabile all’interno del server web.

Quando si trova in memoria, la servlet può gestire più richieste contemporaneamente. Il server Web crea un thread per ogni richiesta, che utilizzano tutti la stessa istanza della servlet:

Tutti i thread sopra indicati condividono le variabili dell’istanza del servlet. Potrebbe essere necessario sincronizzare i thread per evitare la corruzione dei dati del servlet. Torneremo su questo argomento.

Al caricamento di una servlet, viene eseguito un metodo specifico della servlet:

public void init() throws ServletException{
}

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>

<%!
   // variabili e metodi globali della pagina JSP

   // variabile di istanza
  int compteur;

   // metodo per incrementare il contatore  
  public int getCompteur(){
     // si incrementa il contatore
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     // si riporta
    return compteur;
  }

   // il metodo eseguito al caricamento iniziale della pagina
  public void jspInit(){
     // inizializzazione del contatore
    compteur=100;
  }
%>

La pagina precedente JSP inizializza a 100 un contatore in jspInit. Ogni richiesta successiva al servlet incrementa e quindi visualizza il valore di questo contatore:

La prima volta:

Image

La seconda volta:

Image

Come si può vedere chiaramente sopra, tra le due richieste il servlet non è stato scaricato, altrimenti il contatore sarebbe stato a 101 alla seconda richiesta. Quando il servlet viene scaricato, il metodo

public void destroy(){
}

viene eseguito, se esiste. Per le pagine JSP viene eseguito il metodo


  public void jspDestroy(){
  }

In questi metodi sarà possibile, ad esempio, chiudere le connessioni ai database, connessioni che saranno state aperte nei corrispondenti metodi init.

3.4.2. Sincronizzazione delle richieste a un servlet

Torniamo alla pagina JSP precedente, che incrementa un contatore e lo restituisce al client web. Supponiamo che vi siano 2 richieste simultanee. Vengono quindi creati due thread per eseguirle, thread che utilizzeranno la stessa istanza di servlet, quindi in questo caso lo stesso contatore. Ricordiamo il codice che incrementa il contatore:


  public int getCompteur(){
     // si incrementa il contatore
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     // lo si restituisce
    return compteur;
  }

L'incremento del contatore è stato scritto volutamente in modo approssimativo. Supponiamo che l'esecuzione dei due thread avvenga nel modo seguente:

 
  1. al tempo T1, viene eseguito il thread TH1. Esso legge il contatore (=145) in myCompteur, poi viene interrotto e perde il controllo del processore. Non ha quindi avuto il tempo di incrementare myCompteur e di copiare il nuovo valore in compteur.
  2. Al momento di T2, viene eseguito il thread TH2. Legge il contatore (=145) in myCompteur, poi viene interrotto e perde il controllo del processore. Si noti che i due thread hanno variabili myCompteur diverse. Condividono solo le variabili di istanza, ovvero quelle globali ai metodi.
  3. Al tempo T3, il thread TH1 riprende il controllo e termina. Restituisce quindi 146 al proprio cliente.
  4. Al tempo T4, il thread TH2 riprende il controllo e termina. Anch’esso restituisce 146 al proprio cliente, mentre avrebbe dovuto restituire 147.

Si tratta di un problema di sincronizzazione dei thread. Quando TH1 vuole incrementare il contatore, occorre impedire che qualsiasi altro thread lo faccia contemporaneamente. Per evidenziare questo problema, riscriviamo la pagina JSP nel modo seguente:

<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // variabili e metodi globali della pagina JSP

   // variabile di istanza
  int compteur;

   // metodo per incrementare il contatore  
  public int getCompteur(){
     // si legge il contatore
    int myCompteur=compteur;
     // si attende 10 secondi
    try{
      Thread.sleep(10000);
    }catch (Exception ignored){}
     // si incrementa il contatore
    compteur=myCompteur+1;
     // si riporta
    return compteur;
  }

   // il metodo eseguito al caricamento iniziale della pagina
  public void jspInit(){
     // inizializza il contatore
    compteur=100;
  }
%>

Qui abbiamo forzato il thread ad arrestarsi 10 secondi dopo aver letto il contatore. Dovrebbe quindi cedere il processore e un altro thread dovrebbe poter leggere a sua volta 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.

Image

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

Image

Image

Otteniamo lo stesso valore del contatore. Possiamo evidenziare meglio il problema con un client programmato piuttosto che con uno manuale come il browser. Di seguito è riportato un client Perl che viene chiamato nel modo seguente:

programma URL N

dove

URL è l'ID del servlet di conteggio

N il numero di richieste da effettuare a questa servlet

Ecco i risultati ottenuti per 5 richieste, che evidenziano chiaramente il problema della mancata sincronizzazione dei thread: tutte ottengono 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 del client Java è il seguente.

import java.net.*;
import java.util.regex.*;
import java.io.*;

public class clientCompteurJSP {

    public static void main(String[] params){

         // dati
        String syntaxe="Syntaxe : pg URL nbAppels";

         // verifica dei parametri
        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
         // numero di chiamate
        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

         // i parametri sono corretti - è possibile effettuare le connessioni a 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
    }//main

    private static void getCompteurs (URL urlCompteur, int nbAppels)
            throws Exception {
         // esegue nbAppels su URL urlCompteur
         // visualizza ogni volta il valore del contatore restituito dal server web


         // da urlCompteur si estraggono le informazioni necessarie per la connessione al server fiscale
        String path=urlCompteur.getPath();
        if(path.equals("")) path="/";
        String host=urlCompteur.getHost();
        int port=urlCompteur.getPort();
        if(port==-1) port=urlCompteur.getDefaultPort();

         // si effettuano le chiamate a URL
        Socket[] clients=new Socket[nbAppels];
        for(int i=0;i<nbAppels;i++){
             // ci si connette al server
            clients[i]=new Socket(host,port);
             // si crea un flusso di scrittura verso il server
            PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
             // si richiede l'URL - invio delle intestazioni HTTP
            OUT.println("GET " + path + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            OUT.println("Connection: close");
            OUT.println("");
        }//per

         // dati locali
        String réponse=null;                        // risposta del server
         // il modello ricercato nella risposta HTML del server
        Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
         // il modello di una risposta corretta
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
         // il risultato del confronto con il modello
        Matcher résultat=null;

        for(int i=0;i<nbAppels;i++){
             // ogni client legge la risposta che gli invia il server

             // si creano i flussi di ingresso-uscita del client TCP
            BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));

             // si legge la prima riga della risposta
            réponse=IN.readLine();
             // si confronta la riga HTTP con il modello della risposta corretta
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                 // si riscontra un problema con URL
                throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
            }//se

             // si legge la risposta fino alla fine delle intestazioni
            while((réponse=IN.readLine())!=null && ! réponse.equals("")){
            }//mentre

             // le intestazioni sono terminate HTTP - si passa al codice HTML
             // per recuperare il valore del contatore
            boolean compteurTrouvé=false;
            while((réponse=IN.readLine())!=null){
                 // si confronta la riga con il modello del contatore
                if(! compteurTrouvé){
                    résultat=modèleCompteur.matcher(réponse);
                    if(résultat.find()){
                         // contatore trovato
                        System.out.println("Compteur="+résultat.group(1));
                        compteurTrouvé=true;
                    }//if
                }//if
            }//while

             // finito
            clients[i].close();
        }//for
    }//getCompteurs

}//classe

Spieghiamo il codice precedente:

  • il programma accetta due parametri:
    • il codice URL della pagina JSP del contatore
    • il numero di clienti da creare per questo URL
  • il programma inizia quindi verificando la validità dei parametri: che ce ne siano effettivamente due, che il primo sia sintatticamente simile a un URL e che il secondo sia un numero intero >0. Per verificare che il URL sia sintatticamente corretto, si utilizza la classe URL e il suo costruttore URL (String), che crea un oggetto URL a partire da una stringa di caratteri come http://istia.univ-angers.fr. Viene generata un'eccezione se la stringa non è un URL sintatticamente valido. Ciò ci permette di verificare la validità del primo parametro.
  • Una volta verificati i parametri, il controllo passa alla procedura getCompteurs. Quest’ultima creerà nbAppels client che si connetteranno tutti contemporaneamente (o quasi) a URL urlCompteur.
  • La porta e il computer a cui i client devono connettersi vengono ricavati da URL e urlCompteur: [URL].getHost() consente di ottenere il nome del computer, mentre [URL].getPort() consente di ottenere la porta.
  • Un primo ciclo consente a ciascun client:
    • connettersi al server web
    • di richiederne l'URL urlCompteur

In questo ciclo, il client non attende la risposta del server. Infatti, l’obiettivo è far sì che il server riceva richieste quasi simultanee.

  • Un secondo ciclo consente a ciascun client di ricevere ed elaborare la risposta inviata dal server. L’elaborazione consiste nell’individuare nella risposta la riga che contiene il valore del contatore e nel visualizzarla.

Per risolvere il problema evidenziato in precedenza (stesso contatore inviato a tutti e cinque i client), dobbiamo sincronizzare i thread del servizio di conteggio su uno stesso oggetto prima di entrare nella sezione critica di lettura e aggiornamento del contatore. La nuova pagina JSP è la seguente:


<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // variabili e metodi globali della pagina JSP
  
   // variabili di istanza
  int compteur;
  Object verrou=new Object();
  
   // metodo per incrementare il contatore  
  public int getCompteur(){
  
     // si sincronizza la sezione critica
    synchronized(verrou){
       // si legge il contatore
      int myCompteur=compteur;
       // si attende 10 secondi
      try{
        Thread.sleep(10000);
      }catch (Exception ignored){}
       // si incrementa il contatore
      compteur=myCompteur+1;
    }//sincronizzato
     // lo si riporta
    return compteur;
  }//getCompteur

   // il metodo eseguito al caricamento iniziale della pagina
  public void jspInit(){
     // inizializzazione del contatore
    compteur=100;
  }
%>

All'esecuzione, si ottengono quindi i seguenti risultati:

dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/contatore/compteur3.jsp 5
    Compteur= 104
    Compteur= 106
    Compteur= 105
    Compteur= 107
    Compteur= 108

La documentazione indica che il server Web può talvolta creare più istanze di uno stesso servlet. In questo caso, la sincronizzazione precedente non funziona più poiché la variabile verrou è locale a un'istanza e non è quindi nota alle altre istanze. Lo stesso vale per la variabile compteur. Per renderle globali a tutte le istanze, si scriverà:


// variabile di classe
  static int compteur;
  static Object verrou=new Object();

Il resto del codice rimane invariato.