Skip to content

3. Einführung in Java-Servlets und JSP-Seiten

Dieses Kapitel enthält verschiedene Beispiele für Servlets und JSP-Seiten. Sie wurden mit dem Tomcat-Server getestet, der auf Port 8080 läuft. Über die Links auf der Startseite können Sie auf Beispiele für Servlets und JSP-Seiten zugreifen. Die meisten der folgenden Beispiele stammen aus den Beispielen von Tomcat. Um sie zu testen, starten Sie einfach Tomcat, geben Sie die URL http://localhost:8080 in einen Browser ein und folgen Sie dem Servlet-Link.

3.1. Java-Servlets

3.1.1. Senden von HTML-Inhalten an einen Web-Client

Betrachten wir das obige „Hello World“-Beispiel. Das Servlet sieht wie folgt aus:


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

Wenn dieses Servlet ausgeführt wird, wird Folgendes angezeigt:

Image

Beachten Sie folgende Punkte:

  • Sie müssen spezielle Klassen für Servlets importieren:

import javax.servlet.*;
import javax.servlet.http.*;

Die javax.servlet-Bibliothek ist nicht immer standardmäßig im JDK enthalten. In diesem Fall können Sie sie direkt von der Sun-Website herunterladen.

  • Ein Servlet erweitert die Klasse HttpServlet

public class HelloWorld extends HttpServlet {
  • Eine an das Servlet gerichtete GET-Anfrage wird von der Methode doGet verarbeitet

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException

  • Ebenso wird eine an das Servlet gerichtete POST-Anfrage von der Methode doPost verarbeitet

    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • Das HttpServletRequest-Objekt ist das Objekt, das uns Zugriff auf die vom Web-Client gestellte Anfrage gewährt. Die Antwort des Servlets wird über das HttpServletResponse-Objekt gesendet
  • Das Response-Objekt ermöglicht es uns, die HTTP-Header festzulegen, die an den Client gesendet werden. Beispielsweise wird der Header „Content-Type: text/html“ hier wie folgt gesetzt:
        response.setContentType("text/html");
  • Um die Antwort an den Client zu senden, verwendet das Servlet einen vom Response-Objekt bereitgestellten Ausgabestrom:
        PrintWriter out = response.getWriter();
  • Sobald dieser Ausgabestrom bereitgestellt ist, wird der HTML-Code in ihn geschrieben und somit an den Client gesendet:
        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. Abrufen von Parametern, die von einem Web-Client gesendet wurden

Das folgende Beispiel zeigt, wie ein Servlet vom Web-Client gesendete Parameter abrufen kann. Ein Eingabeformular:

Image

Die vom Servlet gesendete Antwort:

Image

Der Quellcode des Servlets lautet wie folgt:


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

Beachten Sie die folgenden Änderungen im Vergleich zum vorherigen Beispiel:

  • Die vom Browser gesendeten Parameter werden wie folgt abgerufen:

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

Die Methode request.getParameter("parameterName") gibt einen Null-Zeiger zurück, wenn der Parameter „parameterName“ nicht zu den vom Web-Client gesendeten Parametern gehört.

  • Das Formular legt fest, dass der Browser die Parameter mit der POST-Methode senden muss
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
  • Die empfangenen Parameter werden von der doPost-Methode des Servlets verarbeitet. Hier ruft diese Methode einfach die doGet-Methode auf. Somit verarbeitet dieses Servlet die Formularwerte unabhängig davon, ob sie per GET oder POST gesendet werden.

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

3.1.3. Abrufen von HTTP-Headern, die von einem Web-Client gesendet wurden

Das folgende Servlet zeigt, wie die vom Web-Client gesendeten HTTP-Header abgerufen werden:

Image

Der Quellcode für das Servlet lautet wie folgt:


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

Zu beachten:

  • Es sind das `request`-Objekt und dessen `getHeaderNames`-Methode, die uns Zugriff auf die vom Browser gesendeten HTTP-Header in Form einer Aufzählung gewähren:
        Enumeration e = request.getHeaderNames();
  • Mit der Methode `request.getHeader("header")` können Sie einen bestimmten HTTP-Header abrufen. Das obige Beispiel zeigt einige davon. Beachten Sie, dass die hier gezeigten Header vom Browser gesendet werden. Der Server verfügt ebenfalls über eigene HTTP-Header, die manchmal denen des Browsers entsprechen. Die vom Browser gesendeten HTTP-Header dienen dazu, den Server über die Fähigkeiten des Browsers zu informieren.
Header
Bedeutung
User-Agent
Browser-Identität
Accept
Vom Browser akzeptierte MIME-Typen. Beispielsweise bedeutet „image/gif“, dass der Browser Bilder im GIF-Format verarbeiten kann
Host
im Format host:port. Gibt an, welchen Rechner und welchen Port der Browser kontaktieren möchte.
Accept-Encoding
Vom Browser akzeptiertes Kodierungsformat für vom Server gesendete Dokumente. Wenn ein Server also ein Dokument in normaler, unkomprimierter Form und ein weiteres im gzip-Format bereitstellt und der Browser angegeben hat, dass er das gzip-Format verarbeiten kann, kann der Server das Dokument im gzip-Format senden, um Bandbreite zu sparen.
Accept-language
Vom Browser akzeptierte Sprachen. Wenn ein Server dasselbe Dokument in mehreren Sprachen bereitstellt, sendet er die Version in einer vom Browser akzeptierten Sprache.
Referer
Die vom Browser angeforderte URL
Verbindung
Der vom Browser angeforderte Verbindungsmodus. „Keep-alive“ bedeutet, dass der Server die Verbindung nicht schließen soll, nachdem er die angeforderte Seite an den Browser gesendet hat. Stellt der Browser beispielsweise fest, dass die empfangene Seite Links zu Bildern enthält, kann er neue Anfragen an den Server senden, um diese abzurufen, ohne eine neue Verbindung herstellen zu müssen. Der Browser schließt die Verbindung dann von sich aus, sobald er alle Elemente der Seite empfangen hat.

3.1.4. Abrufen von Umgebungsinformationen

Das folgende Servlet zeigt, wie auf die Laufzeitumgebungsinformationen des Servlets zugegriffen werden kann. Einige dieser Informationen werden vom Browser als HTTP-Header gesendet und können daher mit der zuvor beschriebenen Methode abgerufen werden.

Image

Der Servlet-Code lautet wie folgt:


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

Die Informationen werden hier mithilfe verschiedener Methoden abgerufen:

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

Hier ist eine Liste einiger der verfügbaren Methoden und ihrer Bedeutungen:

Methode
Bedeutung
getServerName()
der Name des Webservers
getServerPort()
der Arbeitsport des Webservers
getMethod()
die vom Browser für die Anfrage verwendete GET- oder POST-Methode
getRemoteHost()
der Name des Client-Rechners, von dem aus der Browser seine Anfrage gestellt hat
getRemoteAddr()
die IP-Adresse desselben Rechners
getContentType()
der vom Browser gesendete Inhaltstyp (HTTP-Content-Type-Header)
getContentLength()
die Anzahl der vom Browser gesendeten Zeichen (HTTP-Content-Length-Header)
getProtocol()
die vom Browser angeforderte Version des HTTP-Protokolls
getRequestURI()
Die vom Browser angeforderte URI. Entspricht dem Teil der URL, der auf die Host:Port-Kennung in http://hote:port/URI folgt

3.1.5. Erstellen Sie ein Servlet mit JBuilder und stellen Sie es mit Tomcat bereit

Wir werden nun beschreiben, wie man ein Java-Servlet erstellt und ausführt. Wir verwenden zwei Tools: JBuilder zum Kompilieren des Servlets und Tomcat zum Ausführen. Tomcat allein würde vielleicht ausreichen. Es bietet jedoch nur begrenzte Debugging-Funktionen. Wir greifen das zuvor entwickelte Beispiel wieder auf, das die vom Server empfangenen Parameter anzeigt. Das Servlet sendet zunächst das folgende Eingabeformular:

Image

Die vom Servlet gesendete Antwort:

Image

Der Quellcode für das Servlet lautet wie folgt:


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);
    }
 
}
  • Erstellen Sie mit JBuilder ein Projekt namens „myRequestParamExample“ und fügen Sie das vorstehende Programm „myRequestParamExample.java“ darin ein.
  • Während der Kompilierung kann folgendes Problem auftreten: Möglicherweise verfügt Ihr JBuilder nicht über die javax.servlet-Bibliothek, die zum Kompilieren von Servlets erforderlich ist. In diesem Fall müssen Sie JBuilder so konfigurieren, dass zusätzliche Klassenbibliotheken verwendet werden. Die Vorgehensweise ist in den Anhängen dieses Dokuments für JBuilder 7 beschrieben. Wir haben sie hier teilweise wiedergegeben:
  • Aktivieren Sie die Option „Tools/Configure JDKs“

Image

Im obigen Abschnitt „JDK-Einstellungen“ wird im Feld „Name“ normalerweise „JDK 1.3.1“ angezeigt. Wenn Sie über ein neueres JDK verfügen, geben Sie über die Schaltfläche „Ändern“ dessen Installationsverzeichnis an. Oben haben wir das Verzeichnis „E:\Program Files\jdk14“ angegeben, in dem ein JDK 1.4 installiert wurde. Von nun an verwendet JBuilder dieses JDK für seine Kompilierungen und Ausführungen. Im Abschnitt (Klasse, Quelle, Dokumentation) sehen Sie eine Liste aller Klassenbibliotheken, die JBuilder durchsuchen wird – in diesem Fall die Klassen aus JDK 1.4. Die in diesem JDK enthaltenen Klassen reichen für die Java-Webentwicklung nicht aus. Um weitere Klassenbibliotheken hinzuzufügen, verwenden Sie die Schaltfläche „Hinzufügen“ und wählen Sie die zusätzlichen .jar-Dateien aus, die Sie verwenden möchten. .jar-Dateien sind Klassenbibliotheken. Tomcat 4.x enthält alle für die Webentwicklung erforderlichen Klassenbibliotheken. Sie befinden sich unter <tomcat>\common\lib, wobei <tomcat> das Tomcat-Installationsverzeichnis ist:

Image

Über die Schaltfläche „Hinzufügen“ fügen wir diese Bibliotheken nacheinander zur Liste der von JBuilder gescannten Bibliotheken hinzu:

Image

Ab sofort können Sie Java-Programme kompilieren, die dem J2EE-Standard entsprechen, einschließlich Java-Servlets. JBuilder wird nur für die Kompilierung verwendet; die Ausführung übernimmt anschließend Tomcat.

  • Nun können Sie das Programm „myRequestParamExample.java“ kompilieren und das Servlet „myRequestParamExample.class“ generieren. Wo soll dieses Servlet abgelegt werden? Wenn die ursprüngliche Tomcat-Konfiguration nicht geändert wurde, müssen die Servlet-Class-Dateien in <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x) abgelegt werden.
  • Vergewissern Sie sich, dass Tomcat läuft, und rufen Sie mit einem Browser die URL http://localhost:8080/examples/servlet/myRequestParamExample auf:

Image

3.1.6. Beispiele

Für die folgenden Beispiele haben wir die oben beschriebene Methode verwendet:

  • Kompilieren der Servlet-Quelldatei XX.java mit JBuilder
  • Bereitstellen der Servlet-Datei XX.class unter <tomcat>\webapps\examples\WEB-INF\classes
  • Geben Sie bei laufendem Tomcat die URL http://localhost:8080/examples/servlet/XX in einen Browser ein

3.1.6.1. Dynamische Formularerstellung – 1

Als Beispiel verwenden wir die Erstellung eines Formulars mit nur einem Steuerelement: einer Liste. Der Inhalt dieser Liste wird dynamisch aus Werten eines Arrays aufgebaut. In der Praxis werden diese Werte häufig aus einer Datenbank abgerufen. Das Formular sieht wie folgt aus:

Image

Wenn Sie im obigen Beispiel auf „Absenden“ klicken, erhalten Sie folgende Antwort:

Image

Beachten Sie, dass die URL, die die Antwort zurückgibt, dieselbe ist wie die, die das Formular anzeigt. Hier haben wir ein Servlet, das die Antwort auf das von ihm gesendete Formular verarbeitet. Dies ist ein gängiges Szenario. Der HTML-Code für das Formular lautet wie folgt:

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

Beachten Sie, dass die vom Formular gesendeten Werte mit der POST-Methode übermittelt werden. Der HTML-Code der Antwort:

<html>
    <head><title>Voici ma réponse</title></head>
  <body>
      Vous avez choisi le nombre<h2>neuf</h2>
  </body>
</html>

Der Servlet-Code, der dieses Formular und diese Antwort generiert, lautet wie folgt:

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

public class gener1 extends HttpServlet{
    // variables d'instance
    private String title="Génération d'un formulaire";
    private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
      "sept","huit","neuf"};
    private final String HTML1=
                "<html>" +
                  "<head>" +
                    "<title>Génération de formulaire</title>"+
                    "</head>" +
                  "<body>" +
                     "<h3>Choississez un nombre</h3>"+
                     "<hr>" +
                     "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\">";
        private final String HTML3="</form>\n</body>\n</html>";

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

      // on indique au client le type de document envoyé
      response.setContentType("text/html");
      // on envoie le formulaire
      PrintWriter out=response.getWriter();
            // début
      out.println(HTML1);
            // combo
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            for (int i=0;i<valeurs.length;i++){
                out.println("<option>"+valeurs[i]+"</option>");
            }//for
            out.println("</select>");
            // fin formulaire
            out.println(HTML2+HTML3);
    }//GET

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

            // on récupère le choix de l'utilisateur
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) doGet(request,response);

            // on prépare la réponse
            String réponse="<html><head><title>Voici ma réponse</title></head>";
            réponse+="<body>Vous avez choisi le nombre <h2>"+choix+"</h2></body></html>";
            // on indique au client le type de document envoyé
            response.setContentType("text/html");
            // on envoie le formulaire
            PrintWriter out=response.getWriter();
            out.println(réponse);
    }//POST
    }//classe

Die Methode doGet wird verwendet, um das Formular zu generieren. Es gibt einen dynamischen Teil, nämlich den Inhalt der Liste, der hier aus einem Array stammt. Die Methode doPost wird verwendet, um die Antwort zu generieren. Hier ist der einzige dynamische Teil der Wert der Auswahl des Benutzers in der Liste des Formulars. Dieser Wert wird über request.getParameter("cmbValeurs") abgerufen, wobei cmbValeurs der HTML-Name der Liste ist:

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

Beachten Sie abschließend folgende Punkte:

  • Der Browser sendet die Formularwerte an das Servlet, das das Formular generiert hat, da das <form>-Tag kein <action>-Attribut hat. In diesem Fall sendet der Browser die im Formular eingegebenen Daten an die URL, die das Formular bereitgestellt hat.
  • Das <form>-Tag legt fest, dass die Formulardaten mit der POST-Methode gesendet werden müssen. Aus diesem Grund werden diese Werte von der doPost-Methode des Servlets abgerufen.

3.1.6.2. Dynamische Formularerstellung – 2

Wir greifen das vorherige Beispiel wieder auf und ändern es wie folgt. Das Formular bleibt unverändert:

Image

Die Antwort ist jedoch anders:

Image

In der Antwort wird das Formular zurückgegeben, wobei die vom Benutzer gewählte Zahl darunter angegeben ist. Außerdem ist dies die Zahl, die bei der Anzeige der Liste als ausgewählt erscheint. Der Benutzer kann dann eine andere Zahl auswählen:

Image

und dann auf „Absenden“ klicken. Er erhält die folgende Antwort:

Image

Der Code für das Servlet mit dem Namen gener2.java lautet wie folgt:

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

public class gener2 extends HttpServlet{
        // variables d'instance
        private String title="Génération d'un formulaire";
        private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
            "sept","huit","neuf"};
        private final String HTML1=
                "<html>" +
                    "<head>" +
                        "<title>Génération de formulaire</title>"+
                    "</head>" +
                    "<body>" +
                         "<h3>Choisissez un nombre</h3>"+
                         "<hr>" +
                         "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
        private final String HTML3="</body>\n</html>";

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

            // on récupère l'éventuel choix de l'utilisateur
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

            // on indique au client le type de document envoyé
            response.setContentType("text/html");
            // on envoie le formulaire
            PrintWriter out=response.getWriter();
            // début
            out.println(HTML1);
            // combo
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            String selected="";
            for (int i=0;i<valeurs.length;i++){
                if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                out.println("<option "+selected+">"+valeurs[i]+"</option>");
            }//for
            out.println("</select>");
            // suite formulaire
            out.println(HTML2);
            if(! choix.equals("")){
                // on affiche le choix de l'utilisateur
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//if
            // fin du formulaire
            out.println(HTML3);
        }//GET

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

            // on renvoie sur GET
            doGet(request,response);
        }//POST
    }//classe

Die doGet-Methode erledigt alles: Sie erstellt das Formular, das an den Client gesendet wird, und verarbeitet die vom Client zurückgegebenen Werte. Folgende Punkte sind dabei zu beachten:

  • Wir prüfen, ob der Parameter cmbValeurs einen Wert hat.
  • Ist dies der Fall, wird beim Erstellen des Listeninhalts jedes Element mit der Auswahl des Benutzers verglichen, um das Attribut „selected“ für das vom Benutzer gewählte Element zu setzen: <option selected>item</option>. Zusätzlich wird der ausgewählte Wert unterhalb des Formulars angezeigt.

3.1.6.3. Dynamische Formularerstellung – 3

Wir befassen uns mit demselben Problem wie zuvor, doch diesmal werden die Werte aus einer Datenbank abgerufen. In unserem Beispiel handelt es sich um eine MySQL-Datenbank:

  • Die Datenbank heißt dbValues
  • ihr Eigentümer ist admDbValeurs mit dem Passwort mdpDbValeurs
  • Die Datenbank enthält eine einzige Tabelle namens tvaleurs
  • Diese Tabelle enthält nur ein Ganzzahlfeld namens „value“
E:\Program Files\EasyPHP\mysql\bin>mysql --database=dbValeurs --user=admDbValeurs --password=mdpDbVa
leurs

mysql> show tables;
+---------------------+
| Tables_in_dbValeurs |
+---------------------+
| tvaleurs            |
+---------------------+
1 row in set (0.00 sec)

mysql> describe tvaleurs;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| valeur | int(11) |      |     | 0       |       |
+--------+---------+------+-----+---------+-------+

mysql> select * from tvaleurs;
+--------+
| valeur |
+--------+
|      0 |
|      1 |
|      2 |
|      3 |
|      4 |
|      6 |
|      5 |
|      7 |
|      8 |
|      9 |
+--------+
10 rows in set (0.00 sec)

Die MySQL-Datenbank dbValeurs wurde über einen ODBC-Treiber für MySQL zugänglich gemacht. Ihr DSN (Data Source Name) lautet odbc-valeurs. Der Servlet-Code lautet wie folgt:

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

public class gener3 extends HttpServlet{
        // le titre de la page
        private final String title="Génération d'un formulaire";
        // la base de données des valeurs de liste
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
        // valeurs de liste
        private String[] valeurs=null;
        // msg d'erreur
        private String msgErreur=null;
        // code HTML
        private final String HTML1=
                "<html>" +
                    "<head>" +
                        "<title>Génération de formulaire</title>"+
                    "</head>" +
                    "<body>" +
                         "<h3>Choisissez un nombre</h3>"+
                         "<hr>" +
                         "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
        private final String HTML3="</body>\n</html>";

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

            // on indique au client le type de document envoyé
            response.setContentType("text/html");
            // flux de sortie
            PrintWriter out=response.getWriter();

            // l'initialisation de la servlet s'est-elle bien passée ?
            if (msgErreur!=null){
                // il y a eu une erreur - on génère une page d'erreur
                out.println("<html><head><title>"+title+"</title></head>");
                out.println("<body><h3>Application indisponible ("+msgErreur+
                                        ")</h3></body></html>");
                return;
            }//if

            // on récupère l'éventuel choix de l'utilisateur
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

            // on envoie le formulaire
            // début
            out.println(HTML1);
            // combo
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            String selected="";
            for (int i=0;i<valeurs.length;i++){
                if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                out.println("<option "+selected+">"+valeurs[i]+"</option>");
            }//for
            out.println("</select>");
            // suite formulaire
            out.println(HTML2);
            if(! choix.equals("")){
                // on affiche le choix de l'utilisateur
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//if
            // fin du formulaire
            out.println(HTML3);
        }//GET

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

            // on renvoie sur GET
            doGet(request,response);
        }//POST

        // initialisation de la servlet
        public void init(){
            // remplit le tableau des valeurs à partir d'une base de données ODBC
            // de nom DSN : DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                // connexion à la base ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                // objet Statement
                st=connexion.createStatement();
                // exécution requête select pour récupérer les valeurs
                rs=st.executeQuery("select valeur from Tvaleurs");
                // les valeurs sont récupérées et mises dans un tableau dynamique
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // on enregistre la valeur dans la liste
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                // transformation liste --> tableau
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                // problème
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//init
    }//classe

Die wichtigsten Punkte sind folgende:

  1. Ein Servlet kann durch eine Methode initialisiert werden, deren Signatur public void init() lauten muss. Diese Methode wird nur beim ersten Laden des Servlets ausgeführt
  2. Einmal geladen, verbleibt ein Servlet jederzeit im Speicher. Das bedeutet, dass es nach der Bedienung eines Clients nicht entladen wird. Es reagiert somit schneller auf Client-Anfragen.
  3. In unserem Servlet muss eine Liste von Werten aus einer Datenbank abgerufen werden. Da sich diese Liste im Laufe der Zeit nicht ändert, ist die init-Methode der ideale Zeitpunkt, um sie abzurufen. Auf die Datenbank wird somit nur einmal vom Servlet zugegriffen, nämlich beim ersten Laden, und nicht bei jeder Client-Anfrage.
  4. Der Datenbankzugriff kann fehlschlagen. Die init-Methode unseres Servlets setzt im Falle eines Fehlers eine Fehlermeldung, msgErreur. Diese Meldung wird in der doGet-Methode überprüft, und wenn ein Fehler aufgetreten ist, generiert doGet eine Seite, die darauf hinweist.
  5. Die Implementierung der init-Methode nutzt den Standard-Datenbankzugriff mit ODBC-JDBC-Treibern. Bei Bedarf wird dem Leser empfohlen, sich mit den Methoden für den Zugriff auf JDBC-Datenbanken vertraut zu machen.

Wenn das Servlet ausgeführt wird und der MySQL-Server nicht gestartet wurde, erscheint die folgende Fehlerseite:

Image

Wenn Sie nun den MySQL-Server starten, wird folgende Seite angezeigt:

Image

Wenn Sie die Zahl 6 auswählen und auf „Submit“ klicken:

Image

3.1.6.4. Werte aus einem Formular abrufen

Wir greifen ein bereits bekanntes Beispiel wieder auf, nämlich das folgende Webformular:

Image

Der HTML-Code für das Formular balises2.htm lautet wie folgt:

<html>

  <head>
      <title>balises</title>
    <script language="JavaScript">
        function effacer(){
          alert("Vous avez cliqué sur le bouton Effacer");
      }//effacer
        </script>
  </head>

  <body background="/images/standard.jpg">
...

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

      <table border="0">
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
              <input type="radio" value="Oui" name="R1">Oui
              <input type="radio" name="R1" value="non" checked>Non
          </td>
        </tr>
        <tr>
          <td>Cases à cocher</td>
          <td>
              <input type="checkbox" name="C1" value="un">1
              <input type="checkbox" name="C2" value="deux" checked>2
              <input type="checkbox" name="C3" value="trois">3
          </td>
        </tr>
        <tr>
          <td>Champ de saisie</td>
          <td>
              <input type="text" name="txtSaisie" size="20" value="qqs mots">
          </td>
        </tr>
        <tr>
          <td>Mot de passe</td>
          <td>
              <input type="password" name="txtMdp" size="20" value="unMotDePasse">
          </td>
        </tr>
        <tr>
          <td>Boîte de saisie</td>
          <td>
               <textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
          </td>
        </tr>
        <tr>
          <td>combo</td>
          <td>
              <select size="1" name="cmbValeurs">
                <option>choix1</option>
                <option selected>choix2</option>
                <option>choix3</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>liste à choix simple</td>
          <td>
              <select size="3" name="lst1">
                <option selected>liste1</option>
                <option>liste2</option>
                <option>liste3</option>
                <option>liste4</option>
                <option>liste5</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>liste à choix multiple</td>
          <td>
              <select size="3" name="lst2" multiple>
                <option selected>liste1</option>
                <option>liste2</option>
                <option selected>liste3</option>
                <option>liste4</option>
                <option>liste5</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>bouton</td>
          <td>
              <input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
          </td>
        </tr>
        <tr>
          <td>envoyer</td>
          <td>
              <input type="submit" value="Envoyer" name="cmdRenvoyer">
          </td>
        </tr>
        <tr>
          <td>rétablir</td>
          <td>
              <input type="reset" value="Rétablir" name="cmdRétablir">
          </td>
        </tr>
      </table>
      <input type="hidden" name="secret" value="uneValeur">
    </form>
  </body>
</html>

Das <form>-Tag im Formular wurde wie folgt definiert:

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

Der Browser sendet die Formularwerte an die URL http://localhost:8080/examples/servlet/parameters, die die URL eines von Tomcat verwalteten Servlets ist und die Werte aus dem vorherigen Formular anzeigt. Wenn wir das Parameter-Servlet direkt aufrufen, erhalten wir folgende Ergebnisse:

Image

Wenn das Formular balises2.htm wie folgt lautet:

Image

und Sie auf die Schaltfläche „Absenden“ klicken, wird das Servlet „parameters“ diesmal mit den Parametern aufgerufen. Es gibt dann die folgende Antwort zurück:

Image

Diese Antwort enthält eindeutig die im Formular eingegebenen Werte. Der Servlet-Code lautet wie folgt:

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

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

    private String getParameter(HttpServletRequest request, String contrôle){
      // rend la valeur request.getParameter(contrôle) ou "" si elle n'existe pas
      String valeur=request.getParameter(contrôle);
      if(valeur==null) return ""; else return valeur;
    }//getParameter

    // GET
    public void doGet(HttpServletRequest request,HttpServletResponse response)
        throws IOException, ServletException
    {
      // on commence par récupérer les paramètres du formulaire
      String R1=getParameter(request,"R1");
      String C1=getParameter(request,"C1");
      String C2=getParameter(request,"C2");
      String C3=getParameter(request,"C3");
      String txtSaisie=getParameter(request,"txtSaisie");
      String txtMdp=getParameter(request,"txtMdp");
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");
      String cmbValeurs=getParameter(request,"cmbValeurs");
      String lst1=getParameter(request,"lst1");
      String[] lst2=request.getParameterValues("lst2");
      String secret=getParameter(request,"secret");

      // on indique le contenu du document
        response.setContentType("text/html");
      // on envoie le document
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>" + title + "</title>");
        out.println("</head>");
        out.println("<body bgcolor=\"white\">");
        out.println("<h3>" + title + "</h3>");
        out.println("<hr>");
        out.println("<table border=\"1\">");
        out.println("<tr><td>R1</td><td>"+R1+"</td></tr>");
        out.println("<tr><td>C1</td><td>"+C1+"</td></tr>");
        out.println("<tr><td>C2</td><td>"+C2+"</td></tr>");
        out.println("<tr><td>C3</td><td>"+C3+"</td></tr>");
        out.println("<tr><td>txtSaisie</td><td>"+txtSaisie+"</td></tr>");
        out.println("<tr><td>txtMdp</td><td>"+txtMdp+"</td></tr>");
        for(int i=0;i<lignes.length;i++)
          out.println("<tr><td>areaSaisie["+i+"]</td><td>"+lignes[i]+"</td></tr>");
        out.println("<tr><td>cmbValeurs</td><td>"+cmbValeurs+"</td></tr>");
        out.println("<tr><td>lst1</td><td>"+lst1+"</td></tr>");
        if(lst2==null)
          out.println("<tr><td>lst2</td><td></td></tr>");
        else
          for(int i=0;i<lst2.length;i++)
            out.println("<tr><td>lst2</td><td>"+lst2[i]+"</td></tr>");
        out.println("<tr><td>secret</td><td>"+secret+"</td></tr>");
        out.println("</body>");
        out.println("</html>");
    }

    // POST
    public void doPost(HttpServletRequest request,HttpServletResponse response)
        throws IOException, ServletException
    {
      // renvoie sur GET
      doGet(request,response);
    }
}

Dieser Code verwendet die zuvor in einem anderen Beispiel vorgestellten Techniken. Beachten Sie die folgenden zwei Punkte:

  1. Das Steuerelement „lst2“ ist eine Mehrfachauswahlliste, sodass mehrere Elemente ausgewählt werden können. Dies ist in unserem Beispiel der Fall, in dem die Elemente „list1“ und „list3“ ausgewählt wurden. Die Werte von lst2 wurden vom Browser in der Form lst2=liste1&lst2=liste3 an den Server gesendet. Das Java-Servlet kann diese Werte mithilfe der Methode getParameterValues in einem Array abrufen: Hier gibt request.getParameterValues("lst2") ein Array mit zwei Zeichenketten ["liste1", "liste3"] zurück.
  2. Das Steuerelement „areaSaisie“ ist ein mehrzeiliges Eingabefeld. request.getParameter("areaSaisie") gibt den Inhalt des Feldes als einzelne Zeichenkette zurück. Wenn Sie die einzelnen Zeilen daraus extrahieren möchten, können Sie die Methode split der Klasse String verwenden. Der folgende Code
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");

ruft die Zeilen aus dem Eingabefeld ab. Diese Zeilen werden durch die Zeichen \r\n (0D0A) abgeschlossen.

Um die Tests durchzuführen, haben wir:

  • die Servlet-Parameter mit JBuilder erstellt und kompiliert, wie zuvor erläutert
  • die generierte Klasse in <tomcat>\webapps\examples\WEB-INF\classes abgelegt, wobei <tomcat> das Tomcat-Installationsverzeichnis ist.
  • die URL http://localhost:81/html/balises2.htm aufgerufen, deren Code oben dargestellt wurde
  • das Formular ausgefüllt und auf die Schaltfläche „Submit“ geklickt.

3.1.6.5. Abrufen von HTTP-Headern von einem Web-Client

Wir verwenden dasselbe Beispiel wie zuvor, aber als Antwort an den Web-Client, der die Formularwerte gesendet hat, senden wir die HTTP-Header zurück, die er gleichzeitig gesendet hat. Wir nehmen eine einzige Änderung an unserem Formular vor:

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

Die Formularwerte werden mit der GET-Methode an ein Java-Servlet namens „headers“ gesendet, das sich in <tomcat>\webapps\examples\WEB-INF\classes befindet. Das „headers“-Servlet wurde mit JBuilder erstellt und kompiliert:

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

public class headers extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
       // determine the nature of the document
        response.setContentType("text/html");
       // we obtain a writing flow
        PrintWriter out = response.getWriter();
      // display header list HTTP
        Enumeration e = request.getHeaderNames();
        while (e.hasMoreElements()) {
            String name = (String)e.nextElement();
            String value = request.getHeader(name);
            out.println("<b>"+name + "</b> = " + value + "<br>");
        }
    }//GET

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

Wir rufen die URL http://localhost:81/html/balises2.htm auf und klicken auf „Absenden“, ohne das Formular zu ändern. Wir erhalten folgende Antwort:

Image

Beachten Sie die parametrisierte URL in der Adressleiste des Browsers, die die zur Übertragung der Parameter verwendete Methode (GET) anzeigt. Wir verwenden dasselbe Beispiel, ändern jedoch die Methode zum Senden der Parameter (POST):

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

Wir erhalten die folgende neue Antwort:

Image

Beachten Sie die HTTP-Header content-type und content-length, die für eine POST-Anfrage charakteristisch sind. Beachten Sie außerdem, dass die Formularwerte nicht mehr im Adressfeld des Browsers angezeigt werden.

3.2. JSP-Seiten

JSP (Java Server Pages) sind eine weitere Möglichkeit, Webserver-Anwendungen zu schreiben. Tatsächlich werden diese JSP-Seiten vor ihrer Ausführung in Servlets übersetzt, sodass es sich im Wesentlichen um Servlet-Technologie handelt. JSP-Seiten ermöglichen eine klarere Hervorhebung der Struktur der generierten HTML-Seiten. Nachfolgend finden Sie einige Beispiele, von denen einige über den JSP-Link auf der Tomcat-Homepage aufgerufen werden können:

3.2.1. Abrufen von Umgebungsinformationen

Hier greifen wir ein Beispiel wieder auf, das wir bereits im Zusammenhang mit einem Servlet behandelt haben: die Anzeige der Umgebungsvariablen eines Servlets. Es handelt sich um das „snoop“-Beispiel aus den JSP-Beispielen:

Image

Der Quellcode für die JSP-Seite befindet sich in <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) oder <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>

Beachten Sie bitte folgende Punkte:

  • Dieser Code ähnelt stark HTML. Er enthält jedoch <%= Ausdruck %>-Tags, die spezifisch für die JSP-Sprache sind. Der JSP-Compiler ersetzt das gesamte Tag im HTML-Text durch den Wert von Ausdruck.
  • Dieses Beispiel verwendet Methoden des Java-Objekts `request`, bei dem es sich um das `request`-Objekt handelt, das bereits bei der Behandlung von Servlets vorgestellt wurde. Es handelt sich also um ein `HttpServletRequest`-Objekt. Somit wird das Tag `<%= request.getRemoteHost() %>` im HTML-Code durch den Namen des Web-Client-Rechners ersetzt, der die Anfrage gestellt hat.
  • Das gleiche Ergebnis lässt sich auch mit einem Servlet erzielen, doch hier ist die Struktur der Webseite übersichtlicher.

3.2.2. Abrufen von Parametern, die vom Web-Client gesendet wurden

Hier greifen wir das Beispiel wieder auf, das wir zuvor mit einem Servlet behandelt haben. Dem Browser wird ein Formular angezeigt:

Image

Als Antwort auf die obige Anfrage erhält der Browser die folgende Seite:

Image

Der Code für die JSP-Seite lautet wie folgt:


<%
  // variables locales à la procédure principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>
 
<!-- code HTML -->
<html>
  <head>
    <title><%= title %></title>
  </head>
  <body bgcolor="white">
    <h3><%= title %></h3>
    <%
      if (firstName != null || lastName != null) {
        out.println("firstname= " + firstName + "<br>");
        out.println("lastname= " + lastName);
      } else {
        out.println("pas de paramètres");
      }
    %>
    <P>
    <form method="POST">
      firstname= <input type="text" size="20" name="firstname">
      <br>
      lastname= <input type="text" size="20" name="lastname">
      <br>
      <input type="submit">
    </form>
  </body>
</html>
  • Während wir wie im vorherigen Beispiel erneut das Tag <%= expression %> sehen, taucht ein neues Tag auf: <% Java-Anweisungen; %>. Das <%-Tag leitet Java-Code ein. Dieser Code endet, sobald er auf das schließende Tag %> trifft.
  • Der gesamte vorangehende Code (HTML + JSP) wird in ein Java-Servlet umgewandelt. Er wird in einer einzigen Methode eingeschlossen, die als Hauptmethode der JSP-Seite bezeichnet wird. Aus diesem Grund sind die am Anfang der JSP-Seite deklarierten Java-Variablen in den anderen Teilen des JSP-Codes, die im HTML-Code verstreut sind, zugänglich: Diese Variablen und Codeabschnitte werden Teil derselben Java-Methode sein. Wenn unser JSP-Code jedoch Methoden enthalten würde, wären die Variablen `title`, `firstname` und `lastname` aufgrund des Methodengültigkeitsbereichs innerhalb dieser Methoden nicht zugänglich. Wir müssten sie zu globalen Variablen machen oder sie als Parameter an die Methoden übergeben. Wir werden später darauf zurückkommen.
  • Um dynamische Teile in den HTML-Code einzubinden, stehen zwei Methoden zur Verfügung: <%= Ausdruck %> oder out.println(Ausdruck). Das out-Objekt ist ein Ausgabestrom, der dem gleichnamigen Objekt aus den Servlet-Beispielen ähnelt, jedoch nicht vom gleichen Typ ist: Es handelt sich um ein JspWriter-Objekt, nicht um ein PrintWriter-Objekt. Es ermöglicht das Schreiben in den HTML-Strom mithilfe der Methoden print und println.
  • Die JSP-Seite spiegelt die Struktur der generierten HTML-Seite besser wider als das entsprechende Servlet.

3.2.3. JSP-Tags

Hier finden Sie eine Liste der Tags, die in einer JSP-Seite vorkommen können, sowie deren Bedeutung.

Tag
Bedeutung
<!-- Kommentar -->
HTML-Kommentar. An den Client gesendet.
<%-- Kommentar --%>
JSP-Kommentar. Wird nicht an den Client gesendet.
<%! Deklarationen, Methoden %>
Deklariert globale Variablen und Methoden. Die Variablen stehen in allen Methoden zur Verfügung
<%= Ausdruck %>
Der Wert des Ausdrucks wird anstelle des Tags in die HTML-Seite eingefügt
<% Java-Code %>
Enthält Java-Code, der Teil der main-Methode der JSP-Seite sein wird
<%@ page attribute1=value1
attribut2=wert2 … %>
legt Attribute für die JSP-Seite fest. Beispiel:
import="java.util.*,java.sql.*", um die von der JSP-Seite benötigten Bibliotheken anzugeben
extends="aParentClass", damit die JSP-Seite von einer anderen Klasse erbt

3.2.4. Implizite JSP-Objekte

In den vorherigen Beispielen sind wir auf zwei nicht deklarierte Objekte gestoßen: request und out. Dies sind zwei der Objekte, die automatisch in dem Servlet definiert werden, in das die JSP-Seite konvertiert wird. Sie werden als implizite oder vordefinierte Objekte bezeichnet. Es gibt noch weitere, aber diese werden zusammen mit dem response-Objekt am häufigsten verwendet:

Objekt
Bedeutung
HttpServletRequest request
das Objekt, über das Sie auf die Anfrage des Web-Clients zugreifen können (getParameter, getParameterNames, getParameterValues)
HttpServletResponse response
Das Objekt, das zum Erstellen der Antwort des Webservers an den Client verwendet wird. Ermöglicht es Ihnen, die an den Web-Client zu sendenden HTTP-Header festzulegen.
JspWriter out
der Ausgabestrom, über den wir HTML-Code an den Client senden können (print, println)

3.2.5. Umwandlung einer JSP-Seite in ein Servlet

Schauen wir uns noch einmal den JSP-Code aus myRequestParamExample.jsp an:


<%
  // variables locales à la procédure principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>
 
<!-- code HTML -->
<html>
  <head>
    <title><%= title %></title>
  </head>
  <body bgcolor="white">
    <h3><%= title %></h3>
    <%
      if (firstName != null || lastName != null) {
        out.println("firstname= " + firstName + "<br>");
        out.println("lastname= " + lastName);
      } else {
        out.println("pas de paramètres");
      }
    %>
    <P>
    <form method="POST">
      firstname= <input type="text" size="20" name="firstname">
      <br>
      lastname= <input type="text" size="20" name="lastname">
      <br>
      <input type="submit">
    </form>
  </body>
</html>

Wenn der Browser diese JSP-Seite vom Tomcat-Server anfordert, wandelt der Server sie in ein Servlet um. Wenn die angeforderte URL

http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp lautet, legt Tomcat 4.x das generierte Servlet im Verzeichnis <tomcat>\work\localhost\examples\jsp\perso\intro ab:

Image

Dieser Name spiegelt die URL der JSP-Seite wider: http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp. Wie oben gezeigt, können wir auf den Java-Code des für die JSP-Seite generierten Servlets zugreifen. In unserem Beispiel sieht dieser wie folgt aus:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;


public class myRequestParamExample$jsp extends HttpJspBase {


    static {
    }
    public myRequestParamExample$jsp( ) {
    }

    private static boolean _jspx_inited = false;

    public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

        JspFactory _jspxFactory = null;
        PageContext pageContext = null;
        HttpSession session = null;
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        String  _value = null;
        try {

            if (_jspx_inited == false) {
                synchronized (this) {
                    if (_jspx_inited == false) {
                        _jspx_init();
                        _jspx_inited = true;
                    }
                }
            }
            _jspxFactory = JspFactory.getDefaultFactory();
            response.setContentType("text/html;charset=ISO-8859-1");
            pageContext = _jspxFactory.getPageContext(this, request, response,
                        "", true, 8192, true);

            application = pageContext.getServletContext();
            config = pageContext.getServletConfig();
            session = pageContext.getSession();
            out = pageContext.getOut();


                   // variables local to the main procedure
                  String title="Récupération des paramètres d'un formulaire";
                  String firstName = request.getParameter("firstname");
                  String lastName = request.getParameter("lastname");

                out.write("\r\n\r\n<!-- code HTML -->\r\n<html>\r\n  <head>\r\n    <title>");
                out.print( title );
                out.write("</title>\r\n  </head>\r\n  <body bgcolor=\"white\">\r\n    <h3>");
                out.print( title );
                out.write("</h3>\r\n    ");

                      if (firstName != null || lastName != null) {
                        out.println("firstname= " + firstName + "<br>");
                        out.println("lastname= " + lastName);
                      } else {
                        out.println("pas de paramètres");
                      }

                out.write("\r\n    <P>\r\n    <form method=\"POST\">\r\n      firstname= <input type=\"text\" size=\"20\" name=\"firstname\">\r\n      <br>\r\n      lastname= <input type=\"text\" size=\"20\" name=\"lastname\">\r\n      <br>\r\n      <input type=\"submit\">\r\n    </form>\r\n  </body>\r\n</html>\r\n");

        } catch (Throwable t) {
            if (out != null && out.getBufferSize() != 0)
                out.clearBuffer();
            if (pageContext != null) pageContext.handlePageException(t);
        } finally {
            if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
        }
    }
}

Der generierte Code ist recht komplex. Wir werden uns nur auf die folgenden Punkte konzentrieren:

  • Die main-Methode des Servlets sieht wie folgt aus:
    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

Diese Methode wird beim Start des Servlets aufgerufen. Wie man sieht, benötigt sie zwei Parameter: die Anfrage des Clients und ein Antwortobjekt, um die Antwort an den Web-Client zu generieren.

  • In der main-Methode wird ein JspWriter-Objekt namens out deklariert und anschließend initialisiert. Dieses Objekt wird verwendet, um HTML-Code über die Anweisung out.print("HTML-Code") an den Client zu senden.
        JspWriter out = null;
...
            out = pageContext.getOut();
  • Der Java-Code


<%
  // variables locales à la procédure principale
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

wurde vollständig in die Hauptmethode _jspService des Servlets aufgenommen. Dasselbe gilt für jeden Code, der sich innerhalb der Tags <%… %> befindet

  • Der HTML-Code der JSP-Seite wird mithilfe der Anweisungen out.print("codeHTML") oder out.write(...) ausgegeben. Zum Beispiel

                out.write("</title>\r\n  </head>\r\n  <body bgcolor=\"white\">\r\n    <h3>");
  • In diesem Beispiel gibt es keine anderen Methoden als die Hauptmethode _jspService.

3.2.6. Methoden und globale Variablen einer JSP-Seite

Betrachten Sie die folgende JSP-Seite:


<%!
  // la balise précédente démarre la partie variables et méthodes globales
  // cette partie sera reprise sans modification dans la servlet
 
  // une variabe globale
  String prenom="inconnu";
 
  // une méthode  
  private String sonChien(){
    return "milou";
  }//sonChien
 
  // une autre méthode
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi
 
  // fin de la partie globale de la servlet
%>  
 
<%
  // la balise précédente indique que le code qui suit sera enregistré
  // dans la méthode principale de la servlet
 
  // variable locale à la méthode principale
  String nom="tintin";
%>
 
 
<%-- code HTML --%>
<html>
  <head>
    <title>Page JSP</title>
  </head>
  <body>
    <center>
      <h2>Page JSP</h2>
      <p>Son nom est <%= nom %></p>
      <p>Son prénom est <%= prenom %></p>
      <p>Son chien s'appelle <%= sonChien() %></p>
      <%
        // le nom de son ami
        afficheAmi(out);
      %>
    </center>
  </body>
</html>

Diese JSP-Seite generiert die folgende Webseite:

Image

Schauen wir uns einmal an, wie die vier Zeilen oben generiert werden:


      <p>Son nom est <%= nom %></p>
      <p>Son prénom est <%= prenom %></p>
      <p>Son chien s'appelle <%= sonChien() %></p>
      <%
        // le nom de son ami
        afficheAmi(out);
      %>

Die obigen Zeilen befinden sich innerhalb eines <%..%>-Tags und sind daher Teil der Hauptmethode _jspService des Servlets, das generiert wird. Wie greifen sie auf die Variablen lastName, firstName und die Methoden hisDog und displayFriend zu?

name (tintin)
ist eine lokale Variable der Hauptmethode der JSP-Seite und ist daher innerhalb dieser bekannt
firstName (unbekannt)
ist eine globale Variable der JSP-Seite und daher innerhalb der Hauptmethode bekannt
sonChien (milou)
ist eine öffentliche Methode der JSP-Seite und daher von der Hauptmethode aus zugänglich
displayFriend (Haddock)
ist eine öffentliche Methode der JSP-Seite und daher von der Hauptmethode aus zugänglich. Beachten Sie, dass das out-Objekt als Parameter an die Methode übergeben wird. Dies ist hier obligatorisch. Tatsächlich wird das out-Objekt in der Hauptmethode des Servlets deklariert und initialisiert und ist keine globale Variable.

Sehen wir uns nun den Code für das Java-Servlet an, das aus dieser JSP-Seite generiert wurde, nachdem der überflüssige Code entfernt wurde:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;


public class tintin$jsp extends HttpJspBase {

          // the preceding tag starts the global variables and methods section
           // this part will be adopted unchanged in the servlet

           // global variability
          String prenom="inconnu";

           // a method  
          private String sonChien(){
            return "milou";
          }//sonChien

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

           // end of the global part of the servlet

    static {
    }
    public tintin$jsp( ) {
    }

    private static boolean _jspx_inited = false;

    public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

        JspFactory _jspxFactory = null;
        PageContext pageContext = null;
        HttpSession session = null;
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        String  _value = null;
        try {

            if (_jspx_inited == false) {
                synchronized (this) {
                    if (_jspx_inited == false) {
                        _jspx_init();
                        _jspx_inited = true;
                    }
                }
            }
            _jspxFactory = JspFactory.getDefaultFactory();
            response.setContentType("text/html;charset=ISO-8859-1");
            pageContext = _jspxFactory.getPageContext(this, request, response,
                        "", true, 8192, true);

            application = pageContext.getServletContext();
            config = pageContext.getServletConfig();
            session = pageContext.getSession();
            out = pageContext.getOut();

                out.write("  \r\n\r\n");
                  // the preceding tag indicates that the following code will be saved
                   // in the servlet's main method

                   // variable local to the main method
                  String nom="tintin";

                out.write("\r\n\r\n\r\n");
                out.write("\r\n<html>\r\n  <head>\r\n    <title>Page JSP</title>\r\n  </head>\r\n  <body>\r\n    <center>\r\n      <h2>Page JSP</h2>\r\n      <p>Son nom est ");
                out.print( nom );
                out.write("</p>\r\n      <p>Son prénom est ");
                out.print( prenom );
                out.write("</p>\r\n      <p>Son chien s'appelle ");
                out.print( sonChien() );
                out.write("</p>\r\n      ");

                        // his friend's name
                        afficheAmi(out);

                out.write("\r\n    </center>\r\n  </body>\r\n</html>\r\n");
        } catch (Throwable t) {
            if (out != null && out.getBufferSize() != 0)
                out.clearBuffer();
            if (pageContext != null) pageContext.handlePageException(t);
        } finally {
            if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
        }
    }
}

Wie oben gezeigt, wurde der Java-Code, der sich zwischen den JSP-Tags <%! .. %> befand, vollständig übernommen und ist nicht Teil der Hauptmethode des Servlets, _jspService. Die in diesem Abschnitt deklarierten Variablen sind Instanzvariablen und daher für die Methoden global gültig; hier können auch andere Methoden als _jspService definiert werden.


  // this part will be adopted unchanged in the servlet
 
   // global variability
  String prenom="inconnu";
 
   // a method
  private String sonChien(){
    return "milou";
  }//sonChien
 
   // another method
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi
 
   // end of the global part of the servlet

3.2.7. Bereitstellen und Debuggen von JSP-Seiten auf dem Tomcat-Server

Wenn Sie eine JSP-Seite erstellen und mit dem Tomcat-Server verwenden möchten, stellt sich die Frage, wo die Seite in der Verzeichnisstruktur des Servers abgelegt werden soll. Es gibt verschiedene Möglichkeiten, darauf werden wir später noch zurückkommen. Derzeit ist der einfachste Ansatz, die JSP-Seite in einem Ordner innerhalb der Verzeichnisstruktur <tomcat>\webapps\examples\jsp (Tomcat 4.x) abzulegen, wobei <tomcat> das Tomcat-Installationsverzeichnis ist. Die URL im vorherigen Beispiel lautete somit http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Das bedeutet, dass sich die Seite tintin.jsp im Ordner <tomcat>\webapps\examples\jsp\perso\tintin befand.

Eine JSP-Seite wird in eine Java-Quelldatei übersetzt, die dann von Tomcat kompiliert wird, wenn die URL der JSP-Seite von einem Browser angefordert wird. Dabei können Kompilierungsfehler auftreten. Tomcat 4.x meldet diese in seiner Antwort an den Browser. Dabei wird konkret angegeben, welche Zeilen der .java-Datei Fehler enthalten. Fehler können verschiedene Ursachen haben:

  1. Der JSP-Code auf der Seite ist fehlerhaft (z. B. Fehler in den verwendeten JSP-Tags)
  2. der in der JSP-Seite eingebundene Java-Code ist fehlerhaft

Die erste Ursache lässt sich durch Überprüfen des JSP-Codes der Seite beheben. Die zweite Ursache lässt sich durch Überprüfen des Java-Codes beheben. Dies kann durch direktes Kompilieren der für die JSP-Seite generierten .java-Datei mit einem Tool wie JBuilder erfolgen, das erweiterte Debugging-Funktionen bietet, die über die von Tomcat hinausgehen.

3.2.8. Beispiele

Wir greifen das zuvor behandelte Beispiel eines Servlets wieder auf, bei dem ein Benutzer eine Zahl aus einer Liste auswählt und der Server dem Benutzer mitteilt, welche Zahl er ausgewählt hat, während er dieselbe Liste zurückgibt, in der das vom Benutzer gewählte Element hervorgehoben ist:

Image

Um diese Seite zu erstellen, haben wir den Servlet-Code abgerufen und wie folgt geändert:

  • Wir haben den Java-Code, der keinen HTML-Code generierte, unverändert belassen
  • Der Java-Code, der HTML-Code generierte, wurde in eine Mischung aus HTML- und JSP-Code umgewandelt

Dies führt zu der folgenden JSP-Seite:


 
<%@ page import="java.sql.*, java.util.*" %>
 
<%!
 
        // variables globales de l'application
        // le titre de la page
        private final String title="Génération d'un formulaire";
        // la base de données des valeurs de liste
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
        // valeurs de liste
        private String[] valeurs=null;
        // msg d'erreur
        private String msgErreur=null;
 
        // initialisation de la page JSP - n'est exécutée qu'une seule fois
        public void jspInit(){
            // remplit le tableau des valeurs à partir d'une base de données ODBC
            // de nom DSN : DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                // connexion à la base ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                // objet Statement
                st=connexion.createStatement();
                // exécution requête select pour récupérer les valeurs
                rs=st.executeQuery("select valeur from Tvaleurs");
                // les valeurs sont récupérées et mises dans un tableau dynamique
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // on enregistre la valeur dans la liste
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                // transformation liste --> tableau
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                // problème
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//init
 
%>
 
<%
    // code de _jspService exécuté à chaque requête cliente
  // y-at-il eu une erreur lors de l'initialisation de la page JSP ?
  if(msgErreur!=null){
%>
       <!-- code HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %></h3>
      </body>
    </html>
<%
      // fin de jspService
    return;
  }//if
 
    // on récupère l'éventuel choix de l'utilisateur
    String choix=request.getParameter("cmbValeurs");
    if(choix==null) choix="";
%>
 
  <%-- pas d'erreur - code HTML de la page normale --%>
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST">
            <select name="cmbValeurs">
              <%
                // affichage dynamique des valeurs
                          String selected="";
                          for (int i=0;i<valeurs.length;i++){
                              if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                              out.println("<option "+selected+">"+valeurs[i]+"</option>");
                          }//for
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
<%
        // y-avait-il une valeur choisie ?
                if(! choix.equals("")){
        // on affiche le choix de l'utilisateur
%>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%        
                }//if
%>        
      </body>
    </html>

Beachten Sie folgende Punkte:

  • Die Import-Anweisungen des Servlets sind in eine <% page import="..." %>-Direktive eingeschlossen
  • Das Tag <%! ... %> umschließt die globalen Variablen und Java-Methoden der Anwendung
  • Die init-Methode des Servlets, die nur einmal beim Laden des Servlets ausgeführt wird, heißt bei einer JSP-Seite jspInit. Diese beiden Methoden dienen demselben Zweck. Daher wurde der Code für die init-Methode des Servlets hier vollständig aufgenommen.
  • Die Instanzvariablen des Servlets – also diejenigen, auf die über mehrere Methoden hinweg zugegriffen werden muss – wurden wörtlich übernommen. Dabei handelt es sich in erster Linie um die Variablen title, values und msgError, die anschließend im JSP-Code verwendet werden.
  • Die Tags <% ... %> umschließen Java-Code, der in die Methode _jspService aufgenommen wird, die ausgeführt wird, wenn ein Client eine Anfrage stellt.
  • Wie beim Servlet prüft die Methode _jspService zunächst den Wert der Variablen msgError, um festzustellen, ob eine Fehlerseite generiert werden muss. Liegt ein Fehler vor, generiert sie die Fehlerseite und beendet die Ausführung (return).
  • Liegt kein Fehler vor, generiert sie das Formular mit der Liste der Werte
  • Sobald dies geschehen ist, prüft sie, ob der Benutzer eine Zahl ausgewählt hat; wenn ja, zeigt sie diese Zahl auf der generierten Seite an

Was sind die Vorteile gegenüber einem Servlet? Zweifellos eine übersichtlichere Darstellung des generierten HTML-Codes. Es gibt jedoch immer noch viel Java-Code, der diese Ansicht „überfrachtet“. Wir werden uns später eine weitere Methode namens Delegation ansehen, bei der wir den Großteil des Java-Codes in ein Servlet auslagern können, während die JSP-Seite nur den HTML- und JSP-Code enthält. Dadurch wird der Verarbeitungsteil klar vom Präsentationsteil getrennt.

3.3. Bereitstellung einer Webanwendung auf dem Tomcat-Server

Wir werden nun erläutern, wie man Java-Webanwendungen mit dem Tomcat-Server bereitstellt. Die folgenden Anweisungen beziehen sich zwar speziell auf diesen Server, doch die Bereitstellung einer Java-Webanwendung in einem anderen J2EE-Container umfasst ähnliche Schritte wie die hier beschriebenen.

3.3.1. Die Konfigurationsdateien server.xml und web.xml

Bisher haben wir zum Testen unserer Servlets und JSP-Seiten

  • die Servlets im Ordner <tomcat>\webapps\examples\WEB-INF\classes abgelegt. Sie waren dann über die URL http://localhost:8080/examples/servlet/nomServlet
  • die JSP-Seiten im Verzeichnisbaum <tomcat>\webapps\examples\jsp abgelegt. Sie waren dann über die URL http://localhost:8080/examples/jsp/nomPageJSP

Wir haben nie erklärt, warum das so war. Der Tomcat-Server wird in einer Textdatei namens server.xml konfiguriert, die sich im Verzeichnis <tomcat>\conf befindet:

Image

Diese Textdatei ist eigentlich eine XML-Datei (eXtended Markup Language). Ein XML-Dokument ist ein Textdokument, das Tags enthält, genau wie ein HTML-Dokument. Während HTML-Tags jedoch genau definiert sind, ist dies bei XML-Tags nicht der Fall. Somit ist das folgende Dokument ein XML-Dokument:

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

Ein XML-Dokument ist einfach ein „getaggtes“ Dokument, das bestimmten Tagging-Regeln folgt:

  • ein getaggter Text in der Form <xx att1="val1" att2="val2" ....>text</xx>
  • Ein Tag kann für sich allein stehen und die Form <xx att1="val1" att2="val2" ..../> annehmen

Die „att“-Felder werden als Attribute des „xx“-Tags bezeichnet, und die „val“-Felder sind die mit diesen Attributen verbundenen Werte. Einige HTML-Dokumente sind keine gültigen XML-Dokumente. Zum Beispiel ist das HTML-Tag <br> kein gültiges XML-Tag. Es müsste als <br/> geschrieben werden, um gültig zu sein, damit die Regel eingehalten wird, dass alle XML-Tags geschlossen werden müssen. Eine Variante von HTML namens XHTML wurde entwickelt, um sicherzustellen, dass jedes XHTML-Dokument ein gültiges XML-Dokument ist. Einige moderne Browser sind in der Lage, XML-Dateien anzuzeigen. Wenn wir also das im obigen Beispiel gezeigte XML-Dokument „person.xml“ nennen und es mit IE6 anzeigen, erhalten wir folgende Darstellung:

Image

IE6 erkennt die Tags und hebt sie hervor. Er erkennt auch die Struktur des Dokuments anhand der Tags. Wenn wir also das folgende Dokument „person2.xml“ nennen:

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

und es mit IE6 anzeigen, erhalten wir dieselbe Darstellung:

Image

IE6 hat die Struktur und den Inhalt des Dokuments korrekt erkannt. Der Sinn eines XML-Dokuments liegt genau in dieser Eigenschaft: Es ist einfach, die Struktur und den Inhalt eines XML-Dokuments abzurufen. Dies geschieht mithilfe eines Programms, das als XML-Parser bezeichnet wird. XML-Dokumente entwickeln sich zum Standard für den Dokumentenaustausch im Web. Betrachten wir Rechner A, der ein DOC-Dokument an Rechner B senden muss. Das DOC-Dokument wird aus Informationen erstellt, die in der Datenbank DB-A enthalten sind. Rechner B muss das DOC-Dokument in der Datenbank DB-B speichern. Der Austausch kann wie folgt ablaufen:

  • Rechner A ruft die Daten aus der Datenbank DB-A ab und verpackt sie in ein XML-Textdokument
  • Das XML-Dokument wird über das Netzwerk an Rechner B gesendet
  • Rechner B analysiert das empfangene Dokument mit einem XML-Parser und extrahiert sowohl die Struktur als auch die Daten (wie es IE6 in unserem Beispiel getan hat). Anschließend kann er die empfangenen Daten in der Datenbank DB-B speichern

Wir werden nicht weiter auf die Sprache XML eingehen, die ein ganzes Buch für sich verdient.

Hier wird Tomcat also durch die XML-Datei server.xml konfiguriert. Wenn wir diese Datei mit IE6 anzeigen, erhalten wir ein komplexes Dokument. Wir konzentrieren uns einfach auf die folgenden Zeilen:

Image

Es ist das <Context ...>-Tag, das uns hier interessiert. Es dient zur Definition von Webanwendungen. Zwei seiner Attribute sind besonders erwähnenswert:

  • path: Dies ist der Name der Webanwendung
  • docBase: Dies ist der Ordner, in dem sie sich befindet. Hier handelt es sich um einen relativen Namen: examples. Relativ zu welchem Ordner? Die Antwort findet sich ebenfalls in der Datei server.xml in der folgenden Zeile:

Image

Die obige Zeile definiert den Webserver:

  • name: Name des Webservers
  • appBase: Stammverzeichnis des Dokumentbaums, den er bedient. Auch hier haben wir einen relativen Namen: webapps. Er ist relativ zum Installationsverzeichnis des Tomcat-Servers <tomcat>. Somit bezieht sich dies auf den Ordner <tomcat>\webapps.

Die Beispiel-Webanwendung speichert ihre Dateien im Ordner „examples“ (siehe „docBase“ oben). Dieser Name bezieht sich relativ auf das Stammverzeichnis des Webverzeichnisbaums des Servers, d. h. <tomcat>\webapps. Es handelt sich also um den Ordner <tomcat>\webapps\examples. Sehen wir uns diesen Ordner einmal genauer an:

Image

Dort finden wir den Ordner WEB-INF\classes, in dem wir unsere Servlets zum Testen gespeichert haben. Der Ordner WEB-INF enthält eine Datei namens web.xml:

Image

Diese Datei dient zur Konfiguration der Beispiel-Webanwendung. Wir werden nicht näher auf diese Datei eingehen, da sie für den Moment zu komplex ist. Wir konzentrieren uns einfach auf die folgenden Zeilen:

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

Das <servlet>-Tag wird verwendet, um ein Servlet innerhalb einer Webanwendung zu definieren. Beachten Sie, dass es sich bei der betreffenden Webanwendung um „examples“ handelt. Das servlet-Tag enthält zwei weitere Tags:

  • <servlet-name>servletToJsp</servlet-name>: definiert den Namen des Servlets
  • <servlet-name>servletToJsp</servlet-name>: definiert den Namen der Klasse, die ausgeführt werden soll, wenn das Servlet angefordert wird. In diesem Beispiel haben das Servlet und seine Klasse denselben Namen. Dies ist nicht zwingend erforderlich.

Wie wird das Servlet servletToJsp von einem Browser vom Tomcat-Server angefordert?

  • Der Browser fordert die URL http://localhost:8080/examples/servlet/servletToJsp an
  • Tomcat analysiert den Servlet-Pfad /examples/servlet/servletToJsp. Es interpretiert den ersten Teil des Pfads /examples als Namen einer Webanwendung und sucht in seiner Konfigurationsdatei server.xml nach dem Speicherort der Dokumente für diese Anwendung. Wie wir zuvor gesehen haben, befindet sich dieser im Ordner <tomcat>\webapps\examples.
  • Tomcat verwendet den Rest des Servlet-Pfads, um das Servlet innerhalb der Webanwendung „examples“ zu finden. Dieser Pfad /servlet/servletToJsp gibt an, dass das Servlet mit dem Namen servletToJsp ausgeführt werden muss. Tomcat liest daraufhin die Konfigurationsdatei web.xml für die Anwendung „examples“, die sich unter <tomcat>\webapps\examples\WEB-INF befindet. In dieser Datei findet er, dass das Servlet servletToJsp mit der Java-Klasse servletToJsp verknüpft ist (siehe die Datei web.xml oben). Anschließend sucht er nach dieser Klasse im Ordner WEB-INF\classes der Webanwendung „examples“, d. h. unter <tomcat>\webapps\examples\WEB-INF\classes, und führt sie aus.

Image

3.3.2. Beispiel: Bereitstellung der Webanwendung „list“

Wir werden uns noch einmal ein Servlet ansehen, das wir bereits behandelt haben und das dem Benutzer eine Liste von Zahlen präsentierte, aus der er eine auswählen konnte. Das Servlet bestätigte dann die von ihm gewählte Zahl:

Image

Wie oben im Adressfeld des Browsers zu sehen ist, lautete der Name der Servlet-Klassendatei „gener3“. Ausgehend von den zuvor gegebenen Erläuterungen:

  • weist die URL /examples/servlet/gener3 darauf hin, dass es sich um ein Servlet namens gener3 aus der Beispiel-Webanwendung handelt
  • In der Datei web.xml der Beispielanwendung findet sich kein Hinweis auf ein Servlet namens gener3. Wie hat Tomcat es dann gefunden? Nachdem ich die gesamte Datei web.xml durchgesehen habe, kann ich diese Frage nicht mit Sicherheit beantworten... Die Frage bleibt offen...

Wir haben uns entschieden, das Servlet gener3.class unter dem Namen lstValeurs in einer Webanwendung namens liste im Ordner E:\data\serge\Servlets\lstValeurs bereitzustellen:

Image

Wir legen die Datei gener3.class im oben genannten Ordner WEB-INF\classes ab:

Image

Wir konfigurieren die Webanwendung „liste“, indem wir die folgenden Zeilen in die Datei „server.xml“ oberhalb der Zeilen einfügen, die die Webanwendung „manager“ definieren:

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

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

Die Zeile, die die Anwendung lstValeurs definiert, gibt an, dass sie sich im Ordner e:/data/serge/servlets/lstValeurs befindet. Wir müssen nun die Datei web.xml für diese Anwendung definieren. Diese Datei definiert das einzige Servlet der Anwendung:

<?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>

Die obige Datei gibt an, dass das Servlet mit dem Namen lstValeurs der Klassendatei gener3.class zugeordnet ist. Diese web.xml-Datei muss erstellt und im Ordner WEB-INF der Anwendung liste gespeichert werden:

Image

Der obige Screenshot zeigt einen Ordner „src“, in dem die Quelldatei „gener3.java“ abgelegt wurde. Dieser Ordner muss nicht unbedingt vorhanden sein. Er spielt in dieser Demonstration keine Rolle. Wir sind nun bereit, die Tests auszuführen:

  • Beenden Sie Tomcat und starten Sie es neu, damit es seine Konfigurationsdatei server.xml erneut liest. Wir verwenden hier Windows. Unter Unix können Sie Tomcat zwingen, seine Konfigurationsdatei erneut zu lesen, ohne es anzuhalten.
  • Rufen Sie über einen Browser die URL http://localhost:8080/liste/servlet/lstValeurs auf

Image

Wir sehen, dass die vorherige URL das Schlüsselwort *servlet* enthält, genau wie alle bisher verwendeten Servlet-URLs. Wir können dies beseitigen, indem wir das lstValeurs-Servlet in der Datei web.xml der Anwendung einem URL-Muster (*url-pattern*) zuordnen:

<?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>

Im <servlet-mapping>-Tag ordnen wir den Pfad /values dem in den vorangegangenen Zeilen definierten Servlet lstValeurs zu. Wir speichern die neue Datei web.xml und rufen die URL http://localhost:8080/liste/valeurs auf:

Image

3.3.3. Bereitstellung der öffentlichen Seiten einer Webanwendung

Wir haben gerade gesehen, wie man eine Webanwendung bereitstellt, die aus einem einzigen Servlet besteht. Eine Webanwendung kann viele Komponenten haben: Servlets, JSP-Seiten, HTML-Dateien, Java-Applets usw. Wo platzieren wir diese Anwendungselemente? Wenn <application> das Webanwendungsverzeichnis ist, das durch das docBase-Attribut der Anwendung in der Datei server.xml der Tomcat-Konfiguration ( ) definiert wird, haben wir gesehen, dass Servlets in <application>\WEB-INF\classes abgelegt werden. Die anderen Anwendungskomponenten können an beliebiger Stelle innerhalb der Verzeichnisstruktur von <application> abgelegt werden, außer im Verzeichnis WEB-INF. Betrachten wir die JSP-Anwendung listvaleurs.jsp, die wir zuvor untersucht haben:

Image

Diese JSP-Seite wurde im Ordner <tomcat>\webapps\examples\jsp\perso\listvaleurs gespeichert. Diese Seite könnte eine Komponente der zuvor bereitgestellten list-Anwendung sein. Platzieren wir die Datei listvaleurs.jsp direkt im Ordner dieser Anwendung:

Image

Erinnern Sie sich an die Konfiguration der liste-Anwendung in der Datei server.xml:

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

Jede URL, die mit dem Pfad /liste beginnt, wird als Teil der Anwendung „liste“ betrachtet und im angegebenen Ordner gesucht. Rufen wir die URL http://localhost:8080/liste/listvaleurs.jsp über einen Browser auf:

Image

Wir haben tatsächlich die erwartete JSP-Seite erhalten.

3.3.4. Servlet-Initialisierungsparameter

Wir haben gesehen, dass ein Servlet über die Datei <application>\WEB-INF\web.xml konfiguriert wird, wobei <application> der Ordner der Webanwendung ist, zu der es gehört. Es ist möglich, Servlet-Initialisierungsparameter in diese Datei aufzunehmen. Kehren wir zu unserem Servlet lstValeurs aus der Webanwendung liste zurück, dessen Konfigurationsdatei wie folgt aussah:

<?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>

Die mit dem Servlet verknüpfte Klasse ist die Klasse „gener3“. Der Quellcode dieser Klasse enthält die Definition mehrerer Konstanten:

public class gener3 extends HttpServlet{
        // page title
        private final String title="Génération d'un formulaire";
         // the list values database
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";

Sehen wir uns die Bedeutung der vier oben definierten Konstanten an:

Titel
Titel des vom Servlet generierten HTML-Dokuments
DSNValues
DSN-Name der ODBC-Datenbank, aus der das Servlet Daten abruft
admDbValues
Benutzername eines Benutzers mit Lesezugriff auf die oben genannte Datenbank
dbPasswordValues
dessen Passwort

Wenn der Administrator der Datenbank DSNValeurs das Passwort für den Benutzer admDbValeurs ändert, muss der Quellcode des Servlets geändert und neu kompiliert werden. Dies ist nicht sehr praktisch. Die Konfigurationsdatei web.xml des Servlets bietet eine Alternative, indem sie die Definition von Servlet-Initialisierungsparametern mithilfe des Tags <init-param> ermöglicht:

    <init-param>
        <param-name>...</param-name>
        <param-value>...</param-value>
    </init-param>
<param-name>
ermöglicht es Ihnen, den Parameternamen zu definieren
<param-value>
definiert den Wert, der dem vorherigen Parameter zugeordnet ist

Das Servlet kann mit den folgenden Methoden auf seine Initialisierungsparameter zugreifen:

[Servlet].getServletConfig()
Eine Methode der Servlet-Klasse, von der die für die Webprogrammierung verwendete HttpServlet-Klasse abgeleitet ist. Gibt ein ServletConfig-Objekt zurück, das Zugriff auf die Konfigurationsparameter des Servlets bietet.
[ServletConfig].getInitParameter("parameter")
Eine Methode der Klasse `ServletConfig`, die den Wert des Initialisierungsparameters „parameter“ zurückgibt.

Wir konfigurieren die Anwendung „liste“ mit der folgenden neuen web.xml-Datei:

<?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>

In der Listenanwendung definieren wir ein zweites Servlet namens lstValeurs2, das mit der Klassendatei gener5 verknüpft ist. Diese Datei wurde im Verzeichnis <application>\WEB-INF\classes abgelegt:

Image

Das Servlet „lstValeurs2“ verfügt über vier Initialisierungsparameter: title, DSNValeurs, admDbValeurs und mdpDbValeurs. Zusätzlich wurde für das Servlet mithilfe des Tags <servlet-mapping> der Alias „/values2“ definiert. Somit ist das Servlet „lstValeurs2“ in der Listenanwendung über die URL http://localhost:8080/liste/valeurs2 erreichbar.

Der Quellcode des Servlets wurde wie folgt geändert, um die Initialisierungsparameter des Servlets abzurufen:

public class gener5 extends HttpServlet{
    // page title
    private String title=null;
    // the list values database
    private String DSNValeurs=null;
    private String admDbValeurs=null;
    private String mdpDbValeurs=null;
...............

         // servlet initialization
        public void init(){
             // retrieve servlet initialization parameters
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            DSNValeurs=config.getInitParameter("DSNValeurs");
            admDbValeurs=config.getInitParameter("admDbValeurs");
            mdpDbValeurs=config.getInitParameter("mdpDbValeurs");

             //have all the parameters been recovered?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // table of values is filled from a ODBC database
             // name DSN : DSNvaleurs
...............

Um das Servlet zu testen, müssen Sie Tomcat neu starten, damit es die neue Konfigurationsdatei web.xml für die Anwendung „liste“ laden kann. Rufen Sie die URL des Servlets über einen Browser auf: http://localhost:8080/liste/valeurs2:

Image

Falls einer der vom Servlet benötigten Initialisierungsparameter in der Datei web.xml fehlt, wird die folgende Seite angezeigt:

Image

3.3.5. Initialisierungsparameter für eine Webanwendung

Im vorangegangenen Beispiel hat nur das Servlet „lstValeurs2“ Zugriff auf die Parameter „title“, „DSNValeurs“, „admDbValeurs“ und „mdpDbValeurs“. Es ist möglich, dass ein anderes Servlet in derselben „liste“-Anwendung Daten aus derselben Datenbank benötigt, die auch vom Servlet „lstValeurs2“ verwendet wird. In diesem Fall müssten die Parameter DSNValeurs, admDbValeurs und mdpDbValeurs im Konfigurationsabschnitt der web.xml-Datei des neuen Servlets neu definiert werden. Eine andere Lösung besteht darin, Parameter, die mehreren Servlets gemeinsam sind, auf Anwendungsebene statt auf Servlet-Ebene zu definieren. Die neue web.xml-Datei für die Anwendung sieht dann wie folgt aus:

<?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>

Das neue Servlet heißt „lstValeurs3“, ist mit der Klassendatei „gener6“ verknüpft und wurde dem Alias „/valeurs3“ zugeordnet (Servlet-Zuordnung). Der Parameter „title“ ist der einzige Parameter, der in der Servlet-Definition beibehalten wurde. Die anderen wurden in der Anwendungskonfiguration innerhalb von <context-param>-Tags platziert. Dieses Tag wird verwendet, um anwendungsspezifische Informationen zu definieren, nicht für ein bestimmtes Servlet oder eine bestimmte JSP-Seite. Wie greift das Java-Servlet auf diese Parameter zu, die oft als Kontextparameter bezeichnet werden? Die Methoden zum Abrufen von Kontextinformationen sind denen sehr ähnlich, die zum Abrufen von servletspezifischen Initialisierungsparametern verwendet werden:

[Servlet].getServletContext()
eine Methode der Servlet-Klasse, von der die für die Webprogrammierung verwendete HttpServlet-Klasse abgeleitet ist. Gibt ein ServletContext-Objekt zurück, das Zugriff auf die Konfigurationsparameter der Anwendung bietet
[ServletContext].getInitParameter("parameter")
Methode der ServletContext-Klasse, die den Wert des Initialisierungsparameters „parameter“ zurückgibt

Die Klasse gener6.java nimmt gegenüber dem zuvor verwendeten Java-Code aus gener5.java lediglich die folgenden Änderungen vor:

             // retrieve servlet initialization parameters
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");

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

             //have all the parameters been recovered?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // table of values is filled from a ODBC database
             // name DSN : DSNvaleurs
...............

Der für das Servlet spezifische Titelparameter wird über ein ServletConfig-Objekt abgerufen. Die anderen drei auf Anwendungsebene definierten Parameter werden über ein ServletContext-Objekt abgerufen. Wir kompilieren diese Klasse und legen sie wie die anderen in <application>\WEB-INF\classes ab:

Image

Wir starten Tomcat neu, damit die neue web.xml-Datei der Anwendung berücksichtigt wird, und rufen die URL http://localhost:8080/liste/valeurs3 auf:

Image

3.3.6. Initialisierungsparameter für eine JSP-Seite

Wir haben gesehen, wie man Initialisierungsparameter für ein Servlet oder eine Webanwendung definiert. Können wir das Gleiche für eine JSP-Seite tun? Kehren wir zum Anfang des Codes für die Seite listvaleurs.jsp zurück, die wir bereits behandelt haben:

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

<%!
        // variables globales de l'application
        // le titre de la page
        private final String title="Génération d'un formulaire";
        // la base de données des valeurs de liste
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
.........

Wir finden die vier Konstanten title, DSNValues, admDbValues und mdpDbValues, die in der Datei web.xml der Anwendung definiert sind. Die Konstanten DSNValues, admDbValues und mdpDbValues wurden nun auf Anwendungsebene definiert, sodass wir davon ausgehen können, dass eine zu dieser Anwendung gehörende JSP-Seite Zugriff auf sie hat. Dies ist tatsächlich der Fall. Wir wissen, dass die JSP-Seite in ein Servlet übersetzt wird. Das Servlet hat über die Methode getServletContext() Zugriff auf den Kontext. Der Fall der Konstante „title“ ist heikler. Tatsächlich haben wir sie nicht auf Anwendungsebene, sondern auf Servlet-Ebene wie folgt definiert:

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

Für die JSP-Seite ist die vorherige Syntax nicht mehr geeignet, da das Konzept einer Klassendatei hier nicht mehr gilt. Die Konfigurationssyntax für eine JSP-Seite ist jedoch der eines Servlets sehr ähnlich. Sie lautet wie folgt:

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

Tatsächlich wird eine JSP-Seite als Servlet behandelt, dem ein Name zugewiesen wird (servlet-name). Anstatt diesem Servlet eine Klassendatei zuzuordnen, ordnen wir ihm die Quelldatei der auszuführenden JSP-Seite zu (jsp-file). Die vorangehenden Zeilen definieren somit ein Servlet namens JSPlstValues, das der JSP-Seite /listvalues2.jsp zugeordnet ist. Der Pfad /listvalues2.jsp ist relativ zum Anwendungsstammverzeichnis. Im Fall unserer Listenanwendung würde sich die Datei listvalues2.jsp also im Ordner docBase (siehe server.xml) der Listenanwendung befinden:

Image

Die Konfiguration der JSP-Seite sieht in der Datei web.xml der Anwendung wie folgt aus:

<?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>

Die JSP-Seite listvaleurs2.jsp befindet sich im Stammverzeichnis der Anwendung liste und ist dem JSP-Servlet-Namen JSPlstValeurs (servlet-name) zugeordnet, der wiederum dem Alias /jspvaleurs (servlet-mapping) zugeordnet ist. Somit ist unsere JSP-Seite über die URL http://localhost:8080/liste/jspvaleurs erreichbar.

Die ursprüngliche JSP-Seite listvaleurs.jsp wird in listvaleurs2.jsp umbenannt und ruft ihre vier Initialisierungsparameter in der Methode jspInit() ab:

<%!
         // application global variables
         // page title
        private String title=null;
        // the list values database
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // list values
        private String[] valeurs=null;
         // error msg
        private String msgErreur=null;

         // initialization of page JSP - executed only once
        public void jspInit(){

             // retrieve servlet initialization parameters
      ServletConfig config=getServletConfig();
            title=config.getInitParameter("JSPtitle");
      ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //have all the parameters been recovered?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // fills the table of values from a ODBC database
             // name DSN : DSNvaleurs
..............

Die JSP-Seite ruft ihre Initialisierungsparameter auf dieselbe Weise ab wie Servlets. Die vorstehende Datei wird im Stammverzeichnis der Webanwendung „liste“ gespeichert:

Image

Der Tomcat-Server wird neu gestartet, um das Neuladen der neuen Konfigurationsdatei web.xml der Anwendung zu erzwingen. Anschließend können Sie die URL http://localhost:8080/liste/jspvaleurs aufrufen:

Image

3.3.7. Zusammenarbeit von Servlets und JSPs innerhalb einer Webanwendung

Wenn ein Client eine Anfrage an einen Webserver sendet, kann die Antwort von mehreren Servlets und JSP-Seiten generiert werden. Bislang wurde die Antwort von einem einzigen Servlet oder einer einzigen JSP-Seite generiert. Wir haben gesehen, dass die JSP-Seite eine bessere Lesbarkeit der generierten HTML-Dokumentstruktur bietet. Allerdings enthält sie in der Regel auch viel Java-Code. Wir können dies verbessern, indem wir

  • den Java-Code, der nicht den HTML-Code der Antwort generiert, in ein oder mehrere Servlets
  • in JSP-Seiten, den Code zur Generierung der verschiedenen HTML-Dokumente, die als Antwort an den Client gesendet werden

Dies sollte dazu beitragen, die Trennung von Java-Code und HTML-Code zu verbessern. Wir werden diese neue Struktur auf unsere Listenanwendung anwenden: Ein Java-Servlet namens lstValeurs4 wird dafür zuständig sein, beim Start die Werte aus der Datenbank zu lesen und anschließend Client-Anfragen unter zu verarbeiten. Je nach Ergebnis dieser Analyse wird die Anfrage des Clients entweder auf eine Fehlerseite (erreur.jsp) oder auf die Seite weitergeleitet, die die Liste der Zahlen anzeigt (liste.jsp). Die Listenanwendung wird somit aus einem Servlet und zwei JSP-Seiten bestehen.

Wie kann ein Servlet die von einem Client empfangene Anfrage an ein anderes Servlet oder an eine JSP-Seite weiterleiten? Wir werden die folgenden Methoden verwenden:

[ServletContext].getRequestDispatcher(
String url)
Methode der ServletContext-Klasse, die ein RequestDispatcher-Objekt zurückgibt. Der Parameter url ist der Name der URL, an die wir die Anfrage des Clients weiterleiten möchten. Diese Weiterleitung der Anfrage kann nur innerhalb derselben Anwendung erfolgen. Daher ist der Parameter url ein Pfad, der relativ zur Webverzeichnisstruktur dieser Anwendung ist.
[RequestDispatcher].forward
(ServletRequest request,
 ServletResponse response)
Eine Methode der RequestDispatcher-Schnittstelle, die die Anfrage des Clients und das Antwortobjekt – das zum Erstellen der Antwort verwendet werden muss – an die vorherige URL weiterleitet.
[ServletRequest].setAttribute(String name, Object obj)
Wenn ein Servlet oder eine JSP-Seite eine Anfrage an ein anderes Servlet oder eine andere JSP-Seite weiterleitet, muss es in der Regel nicht nur die Anfrage des Clients, sondern auch Informationen weitergeben, die sich aus der eigenen Verarbeitung der Anfrage ergeben. Die Methode `setAttribute` der Klasse `ServletRequest` ermöglicht es Ihnen, dem Anfrageobjekt des Clients Attribute in einem Format hinzuzufügen, das einem Wörterbuch aus (Attribut, Wert)-Paaren ähnelt, wobei „Attribut“ der Name des Attributs und „Wert“ ein beliebiges Objekt ist, das dessen Wert darstellt.
[ServletRequest].getAttribute(
String attribute)
ermöglicht es Ihnen, die Werte der Attribute einer Anfrage abzurufen. Diese Methode wird von dem Servlet oder der JSP-Seite verwendet, an die eine Anfrage weitergeleitet wurde, um die ihr hinzugefügten Informationen abzurufen.

Das für die Verarbeitung des Formulars zuständige Servlet wird in der Datei web.xml wie folgt konfiguriert:

<?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>

Das Servlet lstValeurs4 verfügt über vier spezifische Initialisierungsparameter:

Titel
Der Titel des zu erzeugenden HTML-Dokuments
JSPerreur
die URL der JSP-Fehlerseite
JSPlist
Die URL der JSP-Seite, auf der die Zahlenliste angezeigt wird
Servlet-URL
Die URL, die mit dem action-Attribut des Formulars verknüpft ist, das von der JSPlist-Seite angezeigt wird. Diese URL ist die des Servlets lstValeurs4

Das Servlet hat den Alias /valeurs4 (Servlet-Mapping) und ist daher über die URL http://localhost:8080/liste/valeurs4 erreichbar. Es ist mit der Klassendatei gener7.java verknüpft, deren vollständiger Quellcode wie folgt lautet:

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

public class gener7 extends HttpServlet{
        // page title
        private String title=null;
        // the list values database
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // JSP display pages
        private String JSPerreur=null;
        private String JSPliste=null;
        // the URL of the servlet
        private String URLservlet=null;
        // list values
        private String[] valeurs=null;
         // error msg
        private String msgErreur=null;

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

            // put msgErreur,title in the query attributes
            request.setAttribute("msgErreur",msgErreur);
            request.setAttribute("title",title);
            request.setAttribute("URLservlet",URLservlet);

             // was there an error loading the servlet?
            if(msgErreur!=null){
                 // we hand over to a JSP error page
                getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
                 // end
                return;
            }

             // there was no error
             // put the list of values in the query attributes
            request.setAttribute("valeurs",valeurs);

             // we retrieve the user's possible choice
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";
            request.setAttribute("choix",choix);

             // hand over to the JSP list presentation page
            getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
            // end
            return;
        }//GET

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

             // returns to GET
            doGet(request,response);
        }//POST

        // -----------------------------------------------------------------
         // servlet initialization
        public void init(){

             // retrieve servlet initialization parameters
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            JSPerreur=config.getInitParameter("JSPerreur");
            JSPliste=config.getInitParameter("JSPliste");
            URLservlet=config.getInitParameter("URLservlet");

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


             //have all the parameters been recovered?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // fills the table of values from a ODBC database
             // name DSN : DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // connection to the ODBC database
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // statement object
                st=connexion.createStatement();
                 // execute select query to retrieve values
                rs=st.executeQuery("select valeur from Tvaleurs");
                // values are retrieved and put into a dynamic table
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // the value is saved in the
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // transformation list --> table
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problem
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//init
    }//class

Die neue Funktion dieser Klasse besteht darin, dass sie die Client-Anfrage im Fehlerfall an die Seite „JSPerreur“ weiterleitet und andernfalls an die Seite „JSPliste“. Die Klasse generiert die Antwort nicht selbst. Dies übernehmen die JSP-Seiten „JSPerreur“ und „JSPliste“. Zuvor fügte das Servlet der Client-Anfrage Attribute hinzu (setAttribute):

  • eine Fehlermeldung msgError im Fehlerfall für die Seite JSPerreur
  • die anzuzeigenden Werte (values), den vom Benutzer ausgewählten Wert (choice), den Formulartitel (title) und die URL (URLservlet) des action-Attributs des Formulars für die Seite JSPliste

Diese Klasse wird kompiliert und in den Klassen der Anwendung abgelegt:

Image

Die JSP-Seite, die eine Fehlermeldung anzeigt, ist wie folgt konfiguriert:

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

Die mit der Fehlerseite verknüpfte JSP-Datei heißt error.jsp und befindet sich im Anwendungsstammverzeichnis:

Image

Sie hat den Alias /JSPerreur, wodurch sie über die URL http://localhost:8080/liste/JSPerreur erreichbar ist. Sie verfügt über einen Initialisierungsparameter namens mainServlet, dessen Wert der Alias des oben beschriebenen Haupt-Servlets ist. Beachten Sie, dass dieser Alias relativ zum Stammverzeichnis der Anwendung liste ist; andernfalls wäre er /liste/valeurs4. Der Code für die Seite erreur.jsp lautet wie folgt:

<%
    // code de _jspService
  // on récupère le paramètre d'initialisation mainServlet
  String servletListValeurs=config.getInitParameter("mainServlet");
  // on récupère l'attribut msgErreur
  String msgErreur=(String)request.getAttribute("msgErreur");
  // attribut valide ?
  if(msgErreur!=null){
%>
       <!-- code HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %>)</h3>
      </body>
    </html>
<%
    } else { // attribut msgErreur invalide - retour à la servlet principale
%>
    <jsp:forward page="<%= servletListValeurs %>" />  
<%    
  }
%>  

Diese Seite sollte normalerweise vom vorherigen Servlet aufgerufen werden, das ihr das Attribut msgError übergeben sollte. Nichts hindert Sie jedoch daran, sie direkt aufzurufen, wenn Sie ihre URL kennen. Falls Sie feststellen, dass das Attribut msgError fehlt, leiten Sie die Anfrage an das Hauptservlet weiter. Hier verwenden wir ein für JSP-Seiten spezifisches Tag, dessen Syntax wie folgt lautet:

<jsp:forward page="URL" />

wobei URL die URL des Servlets ist, an das die Anfrage des Clients weitergeleitet wird. Ist das msgError-Attribut vorhanden, wird die Fehlerseite angezeigt.

Die JSP-Seite, die die Liste der Zahlen anzeigt, ist wie folgt konfiguriert:

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

Die mit der Fehlerseite verknüpfte JSP-Datei heißt liste.jsp und befindet sich im Anwendungsstammverzeichnis:

Image

Das Servlet hat den Alias /JSPliste, wodurch es über die URL http://localhost:8080/liste/JSPliste erreichbar ist. Es verfügt über einen Initialisierungsparameter namens mainServlet, dessen Wert der Alias des Haupt-Servlets ist. Der Code für die Seite liste.jsp lautet wie folgt:

  <%-- page d'affichage de la liste des valeurs --%>
  <%
      // code de jspService
      // on récupère le paramètre d'initialisation
      String servletListValeurs=config.getInitParameter("mainServlet");

    // on récupère les attributs de la requête venant de la servlet principale
    String title=(String) request.getAttribute("title");
    String[] valeurs=(String[]) request.getAttribute("valeurs");
    String choix=(String) request.getAttribute("choix");
    String URLservlet=(String) request.getAttribute("URLservlet");

    // attributs valides ?
    if(title==null || valeurs==null || choix==null){
        // il y a un attribut invalide - on passe la main à la servlet
   %>
   <jsp:forward page="<%= servletListValeurs %>" />
    <%
    }//if
  %>

  <%-- code HTML --%>  
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST" action="<%= URLservlet %>">
            <select name="cmbValeurs">
              <%
                // affichage dynamique des valeurs
                          String selected="";
                          for (int i=0;i<valeurs.length;i++){
                              if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                              out.println("<option "+selected+">"+valeurs[i]+"</option>");
                          }//for
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
                <%
            // y-avait-il une valeur choisie ?
                    if(! choix.equals("")){
                // on affiche le choix de l'utilisateur
                %>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
                <%        
                    }//if
                %>        
      </body>
    </html>

Diese Seite verhält sich wie die Seite error.jsp. Sie sollte normalerweise vom Servlet /list/values4 aufgerufen werden und die Attribute title, values und choice erhalten. Fehlt einer dieser Parameter, wird die Steuerung an das URLservlet-Servlet (/liste/valeurs4) übergeben. Sind alle Parameter vorhanden, wird die Liste der Zahlen zusammen mit der vom Benutzer ausgewählten Zahl angezeigt, falls vorhanden.

Wenn Sie die URL des Haupt-Servlets aufrufen, erhalten Sie das folgende Ergebnis:

Image

mit folgendem Quellcode (Ansicht/Quelltext):

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

Dieses HTML-Dokument wurde von der JSP-Seite liste.jsp generiert. Wir können sehen, dass die Attribute title, values und URLservlet erfolgreich abgerufen wurden.

Zum Abschluss der Betrachtung der Zusammenarbeit zwischen Servlets und JSP-Seiten stellen wir fest, dass die JSP-Seiten hier sehr kurz sind und keinen Java-Code enthalten, der nicht direkt zur Erstellung der HTML-Antwort beiträgt. Die Struktur der generierten Dokumente ist somit besser erkennbar.

3.4. Lebenszyklus von Servlets und JSP-Seiten

3.4.1. Der Lebenszyklus

Hier konzentrieren wir uns auf den Lebenszyklus von Servlets. Der Lebenszyklus von JSP-Seiten ergibt sich daraus. Betrachten wir ein Servlet, das zum ersten Mal aufgerufen wird. Der Webserver erstellt dann eine Klasseninstanz und lädt sie in den Arbeitsspeicher. Diese bearbeitet anschließend die Anfrage. Ist dies geschehen, wird das Servlet nicht aus dem Arbeitsspeicher entladen. Es verbleibt dort, um weitere Anfragen zu bearbeiten und so die Antwortzeiten des Servers zu optimieren. Es wird erst entladen, wenn eine ausreichend lange Zeitspanne verstrichen ist, ohne dass es neue Anfragen bearbeitet hat. Diese Zeitspanne ist in der Regel im Webserver konfigurierbar.

Solange es sich im Speicher befindet, kann das Servlet mehrere Anfragen gleichzeitig bearbeiten. Der Webserver erstellt pro Anfrage einen Thread, die alle dieselbe Servlet-Instanz verwenden:

Alle oben genannten Threads teilen sich die Variablen der Servlet-Instanz. Es kann erforderlich sein, die Threads zu synchronisieren, um eine Beschädigung der Servlet-Daten zu verhindern. Wir werden später darauf zurückkommen.

Wenn ein Servlet geladen wird, wird eine bestimmte Methode des Servlets ausgeführt:

public void init() throws ServletException{
}

Bei einer JSP-Seite ist dies die Methode


  public void jspInit(){
  }

, die ausgeführt wird. Hier ist ein Beispiel für eine JSP-Seite, die die Methode jspInit verwendet:

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

<%!
  // variables et méthodes globales de la page JSP

  // variable d'instance
  int compteur;

  // méthode pour incrémenter le compteur  
  public int getCompteur(){
    // on incrémente le compteur
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
    // on le rend
    return compteur;
  }

  // la méthode exécutée au chargement initial de la page
  public void jspInit(){
    // init compteur
    compteur=100;
  }
%>

Die vorherige JSP-Seite initialisiert in jspInit einen Zähler auf 100. Jede nachfolgende Anfrage an das Servlet erhöht den Wert dieses Zählers und zeigt ihn anschließend an:

Beim ersten Mal:

Image

Beim zweiten Mal:

Image

Wie oben zu sehen ist, wurde das Servlet zwischen den beiden Anfragen nicht entladen; andernfalls hätte der Zähler bei der zweiten Anfrage den Wert 101 angezeigt. Wenn das Servlet entladen wird, wird die Methode

public void destroy(){
}

ausgeführt, sofern sie vorhanden ist. Bei JSP-Seiten ist dies die Methode


  public void jspDestroy(){
  }

In diesen Methoden können Sie beispielsweise Datenbankverbindungen schließen, die in den entsprechenden init-Methoden geöffnet wurden.

3.4.2. Synchronisierung von Anfragen an ein Servlet

Kehren wir zu der vorherigen JSP-Seite zurück, die einen Zähler inkrementiert und ihn an den Web-Client zurückgibt. Angenommen, es gibt zwei gleichzeitige Anfragen. Dann werden zwei Threads erstellt, um diese auszuführen; diese Threads verwenden dieselbe Servlet-Instanz und somit denselben Zähler. Erinnern Sie sich an den Code, der den Zähler inkrementiert:


  public int getCompteur(){
    // on incrémente le compteur
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
    // on le rend
    return compteur;
  }

Die Inkrementierung des Zählers wurde absichtlich umständlich geschrieben. Angenommen, die Ausführung der beiden Threads verläuft wie folgt:

 
  1. Zum Zeitpunkt T1 wird Thread TH1 ausgeführt. Er liest den Zählerwert (=145) aus myCounter, wird dann unterbrochen und verliert die Prozessorsteuerung. Er hatte daher keine Zeit, myCounter zu inkrementieren und den neuen Wert in counter zu kopieren.
  2. Zum Zeitpunkt T2 wird Thread TH2 ausgeführt. Er liest den Zählerwert (=145) aus myCounter, wird dann unterbrochen und verliert die Prozessorsteuerung. Beachten Sie, dass die beiden Threads unterschiedliche myCounter-Variablen haben. Sie teilen sich nur Instanzvariablen, also solche, die für die Methoden global sind.
  3. Zum Zeitpunkt T3 erhält Thread TH1 die Kontrolle zurück und wird beendet. Er gibt daher 146 an seinen Client zurück.
  4. Zum Zeitpunkt T4 erlangt Thread TH2 die Kontrolle zurück und wird beendet. Er gibt ebenfalls 146 an seinen Client zurück, obwohl er eigentlich 147 hätte zurückgeben sollen.

Hier liegt ein Problem mit der Thread-Synchronisation vor. Wenn TH1 den Zähler erhöhen will, muss er verhindern, dass andere Threads dies ebenfalls tun. Um dieses Problem zu verdeutlichen, schreiben wir die JSP-Seite wie folgt um:

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

<%!
  // variables et méthodes globales de la page JSP

  // variable d'instance
  int compteur;

  // méthode pour incrémenter le compteur  
  public int getCompteur(){
    // on lit le compteur
    int myCompteur=compteur;
    // on s'arrête 10 secondes
    try{
      Thread.sleep(10000);
    }catch (Exception ignored){}
    // on incrémente le compteur
    compteur=myCompteur+1;
    // on le rend
    return compteur;
  }

  // la méthode exécutée au chargement initial de la page
  public void jspInit(){
    // init compteur
    compteur=100;
  }
%>

Hier haben wir den Thread dazu gezwungen, 10 Sekunden nach dem Auslesen des Zählers anzuhalten. Er sollte daher die CPU freigeben, sodass ein anderer Thread einen Zähler auslesen kann, der noch nicht erhöht wurde. Wenn wir Anfragen mit einem Browser stellen, bemerken wir keinen Unterschied außer der 10-sekündigen Wartezeit, bevor wir das Ergebnis erhalten.

Image

Wenn wir nun zwei Browserfenster öffnen und zwei Anfragen in zeitlich dichtem Abstand stellen:

Image

Image

erhalten wir denselben Zählerwert. Wir können das Problem mit einem programmierten Client besser verdeutlichen als mit einem manuellen wie dem Browser. Hier ist ein Perl-Client, der wie folgt aufgerufen wird:

*Programm-URL N*

wobei

URL die URL des Zähl-Servlets ist

N die Anzahl der an dieses Servlet zu sendenden Anfragen ist

Hier sind die Ergebnisse für 5 Anfragen, die das Problem der mangelhaften Thread-Synchronisation deutlich machen: Sie geben alle denselben Zählerwert zurück.


DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121

Der Java-Client-Code lautet wie folgt.

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

public class clientCompteurJSP {

    public static void main(String[] params){

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

         // parameter verification
        if(params.length!=2){
            System.err.println(syntaxe);
            System.exit(1);
        }//if
         // URL
        URL urlCompteur=null;
        try{
            urlCompteur=new URL(params[0]);
            String query=urlCompteur.getQuery();
            if(query!=null) throw new Exception();
        }catch (Exception ex){
            System.err.println(syntaxe);
            System.err.println("URL ["+params[0]+" incorrecte");
            System.exit(2);
        }//try-catch
         // number of calls
        int nbAppels=0;
        try{
            nbAppels=Integer.parseInt(params[1]);
            if(nbAppels<=0) throw new Exception();
        }catch(Exception ex){
            System.err.println(syntaxe);
            System.err.println("Nombre d'appels ["+params[1]+" incorrect");
            System.exit(3);
        }//try-catch

         // parameters are correct - connections can be made to the URL
        try{
            getCompteurs(urlCompteur,nbAppels);
        }catch(Exception ex){
            System.err.println(syntaxe);
            System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
            System.exit(4);
        }//try-catch
    }//hand

    private static void getCompteurs (URL urlCompteur, int nbAppels)
            throws Exception {
         // does nbAppels at URL urlCompteur
         // displays the counter value returned by the web server each time


         // remove from urlCompteur the info needed to connect to the tax server
        String path=urlCompteur.getPath();
        if(path.equals("")) path="/";
        String host=urlCompteur.getHost();
        int port=urlCompteur.getPort();
        if(port==-1) port=urlCompteur.getDefaultPort();

         // calls are made to the URL
        Socket[] clients=new Socket[nbAppels];
        for(int i=0;i<nbAppels;i++){
             // connect to the server
            clients[i]=new Socket(host,port);
             // create a write stream to the server
            PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
             // request URL - send HTTP headers
            OUT.println("GET " + path + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            OUT.println("Connection: close");
            OUT.println("");
        }//for

         // local data
        String réponse=null;                        // server response
         // the model searched for in the HTML server response
        Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
         // the model for a correct answer
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
         // the result of the model comparison
        Matcher résultat=null;

        for(int i=0;i<nbAppels;i++){
             // each client reads the response sent by the server

             // create customer input/output flows TCP
            BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));

             // read the 1st line of the answer
            réponse=IN.readLine();
             // compare the HTTP line with the model of the correct answer
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                 // we have a URL problem
                throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
            }//if

             // the response is read through to the end of the headers
            while((réponse=IN.readLine())!=null && ! réponse.equals("")){
            }//while

             // that's it for HTTP headers - move on to HTML code
             // to retrieve the counter value
            boolean compteurTrouvé=false;
            while((réponse=IN.readLine())!=null){
                 // compare the line with the counter model
                if(! compteurTrouvé){
                    résultat=modèleCompteur.matcher(réponse);
                    if(résultat.find()){
                         // meter found
                        System.out.println("Compteur="+résultat.group(1));
                        compteurTrouvé=true;
                    }//if
                }//if
            }//while

             // it's over
            clients[i].close();
        }//for
    }//getCompteurs

}//class

Lassen Sie uns den vorherigen Code erklären:

  • Das Programm akzeptiert zwei Parameter:
    • die URL der JSP-Seite für den Zähler
    • die Anzahl der Clients, die für diese URL erstellt werden sollen
  • Das Programm beginnt daher damit, die Gültigkeit der Parameter zu überprüfen: dass es tatsächlich zwei sind, dass der erste syntaktisch einer URL ähnelt und dass der zweite eine ganze Zahl größer als 0 ist. Um zu überprüfen, ob die URL syntaktisch korrekt ist, verwenden wir die URL-Klasse und ihren Konstruktor URL(String), der aus einer Zeichenkette wie http://istia.univ-angers.fr ein URL-Objekt erstellt. Wenn die Zeichenkette keine syntaktisch gültige URL ist, wird eine Ausnahme ausgelöst. So können wir die Gültigkeit des ersten Parameters überprüfen.
  • Sobald die Parameter überprüft wurden, wird die Kontrolle an die Prozedur getCompteurs übergeben. Diese Prozedur erstellt nbAppels-Clients, die sich alle gleichzeitig (oder fast gleichzeitig) mit der URL urlCompteur verbinden.
  • Der Port und der Rechner, mit dem sich die Clients verbinden müssen, werden aus der URL urlCompteur abgeleitet: [URL].getHost() gibt den Rechnernamen zurück, und [URL].getPort() gibt den Port zurück.
  • Eine erste Schleife ermöglicht es jedem Client:
    • eine Verbindung zum Webserver herzustellen
    • die URL urlCompteur abzufragen

In dieser Schleife wartet der Client nicht auf die Antwort des Servers. Der Grund dafür ist, dass der Server nahezu zeitgleiche Anfragen erhalten soll.

  • Eine zweite Schleife ermöglicht es jedem Client, die vom Server gesendete Antwort zu empfangen und zu verarbeiten. Die Verarbeitung umfasst das Auffinden der Zeile in der Antwort, die den Zählerstand enthält, und deren Anzeige.

Um das zuvor beschriebene Problem zu beheben (dass derselbe Zähler an alle fünf Clients gesendet wird), müssen wir die Threads des Zählerservices auf ein einziges Objekt synchronisieren, bevor wir den kritischen Abschnitt zum Lesen und Aktualisieren des Zählers betreten. Die neue JSP-Seite sieht wie folgt aus:


<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>
 
<%!
  // variables et méthodes globales de la page JSP
 
  // variables d'instance
  int compteur;
  Object verrou=new Object();
 
  // méthode pour incrémenter le compteur  
  public int getCompteur(){
 
    // on syncronise la section critique
    synchronized(verrou){
      // on lit le compteur
      int myCompteur=compteur;
      // on s'arrête 10 secondes
      try{
        Thread.sleep(10000);
      }catch (Exception ignored){}
      // on incrémente le compteur
      compteur=myCompteur+1;
    }//synchronized
    // on le rend
    return compteur;
  }//getCompteur
 
  // la méthode exécutée au chargement initial de la page
  public void jspInit(){
    // init compteur
    compteur=100;
  }
%>

Bei der Ausführung werden folgende Ergebnisse erhalten:

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

In der Dokumentation wird darauf hingewiesen, dass der Webserver manchmal mehrere Instanzen desselben Servlets erstellen kann. In diesem Fall funktioniert die vorherige Synchronisation nicht mehr, da die Sperrvariable lokal für eine einzelne Instanz ist und den anderen Instanzen daher nicht bekannt ist. Dasselbe gilt für die Zählervariable. Um sie für alle Instanzen global zu machen, schreiben wir:


// variable de classe
  static int compteur;
  static Object verrou=new Object();

Der Rest des Codes bleibt unverändert.