3. Inleiding tot Java-servlets en JSP-pagina’s
In dit hoofdstuk vindt u diverse voorbeelden van servlets en JSP-pagina’s. Deze zijn getest met de Tomcat-server. Deze draait op poort 8080. Via de links op de startpagina krijgt u toegang tot voorbeelden van servlets en JSP-pagina’s. De onderstaande voorbeelden zijn grotendeels ontleend aan de voorbeelden van Tomcat. Om ze te testen, hoeft u alleen maar Tomcat te starten, de URL http://localhost:8080 op te roepen met een browser en de link naar de servlets te volgen.
![]() | ![]() |
3.1. Java-servlets
3.1.1. Inhoud HTML naar een webclient verzenden
We bekijken het bovenstaande Hello World-voorbeeld. De servlet is als volgt:
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>");
}
}
Bij het uitvoeren van deze servlet verschijnt het volgende scherm:

Let op de volgende punten:
- er moeten speciale klassen voor de servlets worden geïmporteerd:
import javax.servlet.*;
import javax.servlet.http.*;
De bibliotheek javax.servlet wordt niet altijd standaard meegeleverd met de JDK. In dat geval kunt u deze rechtstreeks downloaden van de website van Sun.
- Een servlet is afgeleid van de klasse HttpServlet
public class HelloWorld extends HttpServlet {
- Een verzoek GET aan de servlet wordt verwerkt door de methode doGet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Evenzo wordt een verzoek POST aan de servlet verwerkt door de methode doPost
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Het object HttpServletRequest request is het object dat ons toegang geeft tot het verzoek van de webclient. Het antwoord van de servlet wordt verzonden via het object HttpServletResponse response
- Met het object response kunnen we de HTTP-headers instellen die naar de client worden verzonden. De header Content-type: text/html wordt hier bijvoorbeeld ingesteld met:
- Om het antwoord naar de client te verzenden, gebruikt de servlet een uitvoerstroom die door het object response wordt geleverd:
- Zodra deze uitvoerstroom is verkregen, wordt de code HTML erin geschreven en dus naar de client verzonden:
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. De door een webclient verzonden parameters ophalen
Het volgende voorbeeld laat zien hoe een servlet parameters kan ophalen die door de webclient zijn verzonden. Een invoerformulier:

Het door de servlet verzonden antwoord:

De broncode van de servlet is als volgt:
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);
}
}
De volgende verschillen ten opzichte van het vorige voorbeeld zijn op te merken:
- De door de browser verzonden parameters worden als volgt opgehaald:
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
De methode request.getParameter("nomParamètre") retourneert de pointer null, indien de parameter nomParamètre niet voorkomt in de door de webclient verzonden parameters.
- Het formulier geeft aan dat de browser de parameters via de methode POST moet verzenden
- De ontvangen parameters worden verwerkt door de methode doPost van de servlet. Deze methode roept hier alleen de methode doGet aan. Zo verwerkt deze servlet de waarden van het formulier, ongeacht of deze via een GET of een POST zijn verzonden.
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
3.1.3. De door een webclient verzonden HTTP-headers ophalen
De volgende servlet laat zien hoe de door de webclient verzonden HTTP-headers kunnen worden opgehaald:

De broncode van de servlet is als volgt:
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);
}
}
}
Opmerkingen:
- Het zijn het object request en de bijbehorende methode getHeaderNames die ons toegang geven tot de HTTP-headers die door de browser worden verzonden in de vorm van een lijst:
- Met de methode request.getHeader("header") kan een specifieke HTTP-header worden opgehaald. Het bovenstaande voorbeeld toont er enkele. Houd er rekening mee dat de hier getoonde headers door de browser worden verzonden. De server heeft ook zijn eigen HTTP-headers, die soms overeenkomen met die van de browser. De door de browser verzonden HTTP-headers zijn bedoeld om de server te informeren over de mogelijkheden van de browser.
Header | Betekenis |
identiteit van de browser | |
de door de browser ondersteunde MIME-formaten. image/gif betekent dus dat de browser afbeeldingen in het formaat GIF kan verwerken | |
in het formaat hote:port. Geeft aan met welke machine en welke poort de browser verbinding wil maken. | |
door de browser geaccepteerd coderingsformaat voor documenten die door de server worden verzonden. Als een server dus een document in een normale, niet-gecomprimeerde vorm heeft en een in het gecomprimeerde gzip-formaat, en de browser heeft aangegeven dat hij het gzip-formaat kan verwerken, dan kan de server het document in het gzip-formaat verzenden om bandbreedte te besparen. | |
door de browser geaccepteerde talen. Als een server hetzelfde document in meerdere talen heeft, zal hij een versie verzenden waarvan de taal door de browser wordt geaccepteerd. | |
de URL die door de browser is opgevraagd | |
de door de browser gevraagde verbindingsmodus. Keep-alive betekent dat de server de verbinding niet mag verbreken nadat de gevraagde pagina aan de browser is geleverd. Als de browser ontdekt dat de ontvangen pagina bijvoorbeeld links naar afbeeldingen bevat, kan hij nieuwe verzoeken naar de server sturen om deze op te vragen zonder dat er een nieuwe verbinding hoeft te worden gemaakt. De browser neemt dan het initiatief om de verbinding te verbreken zodra hij alle elementen van de pagina heeft ontvangen. |
3.1.4. Omgevingsinformatie ophalen
De volgende servlet laat zien hoe je toegang kunt krijgen tot informatie over de uitvoeringsomgeving van de servlet. Sommige van deze gegevens worden door de browser als HTTP-headers verzonden en kunnen dus met de vorige methode worden opgehaald.

De code van de servlet is als volgt:
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);
}
}
De informatie wordt hier via verschillende methoden verkregen:
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());
Hieronder volgt een lijst met enkele van de beschikbare methoden en hun betekenis:
methode | betekenis |
de naam van de webserver | |
de werkpoort van de webserver | |
de methode GET of POST die door de browser wordt gebruikt om het verzoek te verzenden | |
de naam van de clientcomputer van waaruit de browser zijn verzoek heeft verzonden | |
het adres IP van diezelfde computer | |
het type inhoud dat door de browser is verzonden (HTTP-header Content-type) | |
het aantal tekens dat door de browser is verzonden (HTTP-header Content-length) | |
de versie van het http-protocol die door de browser wordt aangevraagd | |
de door de browser aangevraagde URI. Komt overeen met het deel van de URL dat na de identificatie hote:port in http://hote:port/URI staat |
3.1.5. Een servlet maken met JBuilder, deze implementeren met Tomcat
We beschrijven nu hoe je een Java-servlet kunt maken en uitvoeren. We gebruiken twee tools: JBuilder om de servlet te compileren en Tomcat om deze uit te voeren. Tomcat alleen zou voldoende kunnen zijn. Het biedt echter beperkte mogelijkheden voor foutopsporing. We nemen het eerder ontwikkelde voorbeeld weer op, dat de door de server ontvangen parameters weergeeft. De servlet verstuurt eerst het volgende invoerformulier:

Het antwoord dat door de servlet wordt verzonden:

De broncode van de servlet is als volgt:
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);
}
}
- Maak een project myRequestParamExample aan met JBuilder en voeg daarin het vorige programma myRequestParamExample.java op.
- Tijdens het compileren kan het volgende probleem optreden: uw JBuilder beschikt niet noodzakelijkerwijs over de bibliotheek javax.servlet die nodig is voor het compileren van de servlets. In dat geval moet u JBuilder zo configureren dat het gebruikmaakt van aanvullende klassenbibliotheken. De werkwijze wordt beschreven in de bijlagen van dit document voor JBuilder 7. We geven hier een gedeeltelijke samenvatting:
- schakel de optie Tools/Configure JDKs of (Options/Configure JDK) in

In het gedeelte JDK Settings hierboven staat normaal gesproken in het veld Name een JDK 1.3.1. Als u een nieuwere versie van JDK hebt, gebruik dan de knop Change om de installatiemap van deze versie aan te geven. Hierboven is de map E:\Program Files\jdk14 aangewezen, waarin een JDK 1.4 was geïnstalleerd. Voortaan zal JBuilder deze JDK gebruiken voor zijn compilaties en uitvoeringen. In het gedeelte (Class, Source, Documentation) staat de lijst met alle klassenbibliotheken die door JBuilder zullen worden doorzocht, in dit geval de klassen van JDK 1.4. De klassen hiervan zijn niet voldoende om webontwikkeling in Java te doen. Om andere klassenbibliotheken toe te voegen, gebruik je de knop Add en selecteer je de extra .jar-bestanden die je wilt gebruiken. De .jar-bestanden zijn klassenbibliotheken. Tomcat 4.x bevat alle klassenbibliotheken die nodig zijn voor webontwikkeling. Deze bevinden zich in <tomcat>\common\lib, waarbij <tomcat> de installatiemap van Tomcat is:

Met de knop Add voegen we deze bibliotheken één voor één toe aan de lijst met bibliotheken die door JBuilder worden doorzocht:

Vanaf nu kunnen we Java-programma's compileren die voldoen aan de norm J2EE, met name Java-servlets. JBuilder dient alleen voor het compileren; de uitvoering wordt later door Tomcat verzorgd.
- Nu kunt u het programma myRequestParamExample.java compileren en de servlet myRequestParamExample.class genereren. Waar moet u deze servlet plaatsen? Als de standaardconfiguratie van Tomcat niet is gewijzigd, moeten de .class-bestanden van de servlets worden geplaatst in <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
- Controleer of Tomcat is gestart en roep met een browser de pagina http://localhost:8080/examples/servlet/myRequestParamExample op:

3.1.6. Voorbeelden
Voor de volgende voorbeelden hebben we de eerder beschreven methode gebruikt:
- compileren van de broncode XX.java van de servlet met JBuilder
- de servlet XX.class implementeren in <tomcat>\webapps\examples\WEB-INF\classes
- Tomcat gestart, vraag met een browser de pagina URL http://localhost:8080/examples/servlet/XX op
3.1.6.1. Dynamische formuliergeneratie - 1
We nemen als voorbeeld het genereren van een formulier met slechts één besturingselement: een lijst. De inhoud van deze lijst wordt dynamisch samengesteld met waarden uit een array. In de praktijk worden deze waarden vaak uit een database gehaald. Het formulier ziet er als volgt uit:

Als we in het bovenstaande voorbeeld Envoyer invoeren, krijgen we het volgende antwoord:

Merk op dat de URL die het antwoord vormt, dezelfde is als die welke het formulier weergeeft. Hier hebben we een servlet die zelf het antwoord op het formulier verwerkt dat zij heeft verzonden. Dit is een veelvoorkomend geval. De code HTML van het formulier is als volgt:
<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>
Merk op dat de waarden die via het formulier worden verzonden, worden verzonden via de methode POST. De code HTML van het antwoord:
<html>
<head><title>Voici ma réponse</title></head>
<body>
Vous avez choisi le nombre<h2>neuf</h2>
</body>
</html>
De code van de servlet die dit formulier en dit antwoord genereert, is als volgt:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener1 extends HttpServlet{
// instantievariabelen
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{
// de klant wordt geïnformeerd over het type verzonden document
response.setContentType("text/html");
// het formulier wordt verzonden
PrintWriter out=response.getWriter();
// begin
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>");
}//voor
out.println("</select>");
// einde formulier
out.println(HTML2+HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// de keuze van de gebruiker wordt opgehaald
String choix=request.getParameter("cmbValeurs");
if(choix==null) doGet(request,response);
// het antwoord wordt voorbereid
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>";
// we geven de klant aan welk type document is verzonden
response.setContentType("text/html");
// het formulier wordt verzonden
PrintWriter out=response.getWriter();
out.println(réponse);
}//POST
}//classificeren
De methode doGet wordt gebruikt om het formulier te genereren. Er is een dynamisch gedeelte, namelijk de inhoud van de lijst, die hier afkomstig is uit een tabel. De methode doPost wordt gebruikt om het antwoord te genereren. Hier is het enige dynamische gedeelte de waarde van de keuze die de gebruiker in de lijst van het formulier heeft gemaakt. Deze waarde wordt verkregen via request.getParameter("cmbValeurs"), waarbij cmbValeurs de naam is van de lijst:
Tot slot zijn de volgende punten van belang:
- de browser stuurt de waarden van het formulier naar de servlet die het formulier heeft gegenereerd, omdat de tag <form> geen attribuut <action> heeft. In dit geval stuurt de browser de in het formulier ingevoerde gegevens naar de URL die het formulier heeft geleverd.
- De tag <form> geeft aan dat de gegevens van het formulier via de methode POST moeten worden verzonden. Daarom worden deze waarden opgehaald door de methode doPost van de servlet.
3.1.6.2. Dynamische formuliergeneratie - 2
We nemen het vorige voorbeeld weer op en passen het als volgt aan. Het voorgestelde formulier is nog steeds hetzelfde:

Is het antwoord anders:

In het antwoord wordt het formulier teruggestuurd, met daaronder het door de gebruiker gekozen getal. Dit getal is overigens het getal dat als geselecteerd wordt weergegeven wanneer de lijst wordt getoond. De gebruiker kan vervolgens een ander getal kiezen:

en vervolgens Envoyer uitvoeren. Hij krijgt dan het volgende antwoord:

De code van de servlet met de naam gener2.java is als volgt:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener2 extends HttpServlet{
// instantievariabelen
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{
// de eventuele keuze van de gebruiker wordt opgehaald
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// het type verzonden document wordt aan de klant aangegeven
response.setContentType("text/html");
// het formulier wordt verzonden
PrintWriter out=response.getWriter();
// begin
out.println(HTML1);
// keuzelijst
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>");
}//voor
out.println("</select>");
// vervolg formulier
out.println(HTML2);
if(! choix.equals("")){
// de keuze van de gebruiker wordt weergegeven
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// einde van het formulier
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// doorverwijzing naar GET
doGet(request,response);
}//POST
}//klasse
De methode doGet doet alles: ze stelt het formulier op dat naar de client wordt verzonden en verwerkt de waarden die de client terugstuurt. Let op het volgende:
- er wordt gecontroleerd of de parameter cmbValeurs een waarde heeft.
- Als dat het geval is, wordt bij het samenstellen van de inhoud van de lijst elk element ervan vergeleken met de keuze van de gebruiker, om het attribuut selected toe te kennen aan het door de gebruiker gekozen element: <option selected>element</option>. Bovendien wordt onder het formulier de waarde van de keuze weergegeven.
3.1.6.3. Dynamische formuliergeneratie - 3
We nemen hetzelfde probleem als eerder, maar deze keer worden de waarden uit een database gehaald. In ons voorbeeld is dit de database MySQL:
- de database heet dbValeurs
- de eigenaar is admDbValeurs met het wachtwoord mdpDbValeurs
- de database bevat één enkele tabel met de naam tvaleurs
- deze tabel heeft slechts één geheelgetalveld met de naam ‘valeur’
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)
De database MySQL dbValeurs is toegankelijk gemaakt door een driver ODBC voor MySQL. De naam DSN (Data Source Name) is odbc-valeurs. De code van de servlet is als volgt:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener3 extends HttpServlet{
// de titel van de pagina
private final String title="Génération d'un formulaire";
// de database met lijstwaarden
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// lijstwaarden
private String[] valeurs=null;
// foutmelding
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{
// de klant wordt geïnformeerd over het type verzonden document
response.setContentType("text/html");
// uitvoerstroom
PrintWriter out=response.getWriter();
// Is de initialisatie van de servlet goed verlopen?
if (msgErreur!=null){
// er is een fout opgetreden – er wordt een foutpagina gegenereerd
out.println("<html><head><title>"+title+"</title></head>");
out.println("<body><h3>Application indisponible ("+msgErreur+
")</h3></body></html>");
return;
}//if
// de eventuele keuze van de gebruiker wordt opgehaald
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// het formulier wordt verzonden
// begin
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>");
}//voor
out.println("</select>");
// vervolg formulier
out.println(HTML2);
if(! choix.equals("")){
// de keuze van de gebruiker wordt weergegeven
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// einde van het formulier
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// doorverwijzing naar GET
doGet(request,response);
}//POST
// initialisatie van de servlet
public void init(){
// vult de waarden in de tabel aan vanuit een database ODBC
// met de naam DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// verbinding met de database ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// Statement-object
st=connexion.createStatement();
// uitvoering van een SELECT-query om de waarden op te halen
rs=st.executeQuery("select valeur from Tvaleurs");
// de waarden worden opgehaald en in een dynamische array geplaatst
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// de waarde wordt in de lijst opgeslagen
lstValeurs.add(rs.getString("valeur"));
}//while
// omzetting van lijst naar array
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// probleem
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//initialiseren
}//klasse
De volgende belangrijke punten moeten in acht worden genomen:
- Een servlet kan worden geïnitialiseerd door een methode met de handtekening public void init(). Deze methode wordt alleen uitgevoerd bij het eerste laden van de servlet
- Zodra een servlet is geladen, blijft deze continu in het geheugen aanwezig. Dit betekent dat wanneer de servlet een client heeft bediend, deze niet uit het geheugen wordt verwijderd. Hierdoor reageert de servlet sneller op verzoeken van clients.
- In onze servlet moet een lijst met waarden uit een database worden opgehaald. Aangezien deze lijst in de loop van de tijd niet verandert, is de methode `init` het ideale moment om deze op te halen. De database wordt zo slechts één keer door de servlet geraadpleegd, namelijk bij het eerste laden ervan, en niet bij elk verzoek van een klant.
- Toegang tot een database kan mislukken. De methode init van onze servlet genereert een foutmelding msgErreur in geval van een mislukking. Dit bericht wordt gecontroleerd in de methode doGet en als er een fout is opgetreden, genereert doGet een pagina die dit meldt.
- De implementatie van de methode `init` maakt gebruik van klassieke databasetoegang met de ODBC-JDBC-drivers. Indien nodig kan de lezer de methoden voor databasetoegang JDBC raadplegen.
Wanneer de servlet wordt uitgevoerd en de server MySQL niet is gestart, verschijnt de volgende foutpagina:
![]()
Als je nu de server MySQL start, krijg je de volgende pagina te zien:

Als we het getal 6 kiezen en op ‘Verzenden’ klikken:

3.1.6.4. Waarden uit een formulier ophalen
We nemen een voorbeeld dat we al eerder hebben gezien, namelijk het volgende webformulier:

De code HTML van het formulier balises2.htm is als volgt:
<html>
<head>
<title>balises</title>
<script language="JavaScript">
function effacer(){
alert("Vous avez cliqué sur le bouton Effacer");
}//verwijderen
</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>
De tag <form> van het formulier is als volgt gedefinieerd:
De browser zal de waarden van het formulier 'verzenden' naar de URL http://localhost:8080/examples/servlet/parameters, wat de URL is van een door Tomcat beheerde servlet die de waarden van het vorige formulier weergeeft. Als de servlet parameters rechtstreeks wordt aangeroepen, zijn de resultaten als volgt:

Als het ingevulde formulier balises2.htm het volgende is:

en je op de knop Verzenden (van het type submit) drukt, wordt de servlet parameters deze keer met parameters aangeroepen. Deze stuurt dan het volgende antwoord terug:

In dit antwoord zijn de waarden die in het formulier zijn ingevoerd duidelijk terug te vinden. De code van de servlet is als volgt:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class parameters extends HttpServlet{
// instantievariabelen
String title="Récupération des paramètres d'un formulaire";
private String getParameter(HttpServletRequest request, String contrôle){
// geeft de waarde request.getParameter (controle) of "" terug als deze niet bestaat
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
{
// we beginnen met het ophalen van de formulierparameters
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");
// we geven de inhoud van het document op
response.setContentType("text/html");
// we verzenden het 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
{
// verwijst door naar GET
doGet(request,response);
}
}
In deze code komen de technieken terug die eerder in een ander voorbeeld zijn behandeld. Er zijn twee punten die de aandacht verdienen:
- het besturingselement lst2 is een lijst met meervoudige selectie, waardoor meerdere elementen kunnen worden geselecteerd. Dit is het geval in ons voorbeeld, waarin de elementen liste1 en liste3 zijn geselecteerd. De waarden van lst2 zijn door de browser naar de server verzonden in de vorm lst2=liste1&lst2=liste3. De Java-servlet kan deze waarden in een array ophalen met de methode getParameterValues: hier levert request.getParameterValues("lst2") een array op met 2 tekenreeksen ["liste1","liste3"].
- Het besturingselement areaSaisie is een invoerveld voor meerdere regels. request.getParameter("areaSaisie") geeft de inhoud van het veld weer in de vorm van één enkele tekenreeks. Als men daaruit de verschillende regels wil ophalen waaruit het bestaat, kan men de functie split van de klasse String gebruiken. De volgende code
haalt de regels uit het invoerveld op. Deze regels eindigen met de tekens \r\n (0D0A).
Om de tests uit te voeren hebben we:
- de servlet parameters gebouwd en gecompileerd met JBuilder, zoals eerder uitgelegd
- de gegenereerde klasse geplaatst in <tomcat>\webapps\examples\WEB-INF\classes, waarbij <tomcat> de installatiemap van Tomcat is.
- de URL-http://localhost:81/html/balises2.htm, waarvan de code hierboven is weergegeven, opgevraagd
- het formulier ingevuld en op de knop Envoyer geklikt.
3.1.6.5. De headers HTTP van een webclient ophalen
We nemen hetzelfde voorbeeld als eerder, maar als antwoord op de webclient die de formulierwaarden heeft verzonden, sturen we hem de HTTP-headers terug die hij tegelijkertijd heeft verzonden. We brengen slechts één wijziging aan in ons formulier:
De formulierwaarden worden via de methode GET verzonden naar een Java-servlet met de naam headers, die zich bevindt in <tomcat>\webapps\examples\WEB-INF\classes. De servlet headers is samen met JBuilder gebouwd en gecompileerd:
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
{
// de aard van het document wordt vastgelegd
response.setContentType("text/html");
// er wordt een schrijfstroom verkregen
PrintWriter out = response.getWriter();
// weergave van de lijst met kopteksten 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
}
We vragen de URL http://localhost:81/html/balises2.htm en voeren Envoyer uit zonder het formulier te wijzigen. We krijgen het volgende antwoord:

Let op de ingestelde parameter URL in het veld Address van de browser, die laat zien op welke manier (GET) de parameters worden verzonden. We nemen hetzelfde voorbeeld, maar wijzigen de manier waarop de parameters worden verzonden (POST):
We krijgen dan het volgende nieuwe antwoord:

Let op de kopteksten HTTP, content-type en content-length, die kenmerkend zijn voor een verzending via POST. Bovendien valt op dat in het veld Address van de browser de waarden van het formulier niet meer worden weergegeven.
3.2. JSP-pagina’s
De JSP-pagina’s (Java Server Pages) vormen een andere manier om webservertoepassingen te schrijven. In feite worden deze JSP-pagina’s omgezet in servlets voordat ze worden uitgevoerd, waardoor de servlet-technologie weer aan bod komt. Met JSP-pagina’s kan de structuur van de gegenereerde HTML-pagina’s beter worden benadrukt. Hieronder presenteren we enkele voorbeelden, waarvan sommige toegankelijk zijn via de link JSP op de startpagina van Tomcat:
![]() | ![]() |
3.2.1. Omgevingsinformatie ophalen
We nemen hier een voorbeeld dat al met een servlet is behandeld: het weergeven van de omgevingsvariabelen van een servlet. Dit is het voorbeeld snoop uit de voorbeelden JSP:

De broncode van de pagina JSP bevindt zich in <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) of <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>
We merken het volgende op:
- deze code lijkt sterk op HTML. Er staan echter <%= expression %>-tags in die specifiek zijn voor de taal JSP. De compiler JSP vervangt in de tekst HTML de volledige tag door de waarde van expression.
- In dit voorbeeld worden de methoden van het Java-object request gebruikt; dit is het object request dat we al zijn tegengekomen bij de behandeling van servlets. Het is dus een object van het type HttpServletRequest. Zo wordt de tag <%= request.getRemoteHost() %> in de code HTML vervangen door de naam van de computer van de webclient die het verzoek heeft gedaan.
- We kunnen hetzelfde resultaat bereiken met een servlet, maar hier is de structuur van de webpagina duidelijker.
3.2.2. De door de webclient verzonden parameters ophalen
We nemen hier het voorbeeld dat we al met een servlet hebben bekeken. Er wordt een formulier aan de browser getoond:

Als reactie op het bovenstaande verzoek ontvangt de browser de volgende pagina:

De code van de pagina JSP is als volgt:
<%
// lokale variabelen in de hoofdprocedure
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>
- Naast de tag <%= uitdrukking %>, die we al in het vorige voorbeeld zagen, verschijnt er een nieuwe tag: <% Java-instructies; %>. De tag <% introduceert Java-code. Deze code eindigt bij de afsluitende codetag %>.
- De volledige voorgaande code (HTML + JSP) wordt omgezet in een Java-servlet. Deze wordt ondergebracht in één enkele methode, de zogenaamde hoofdmethode van de pagina JSP. Daarom zijn de Java-variabelen die aan het begin van de pagina JSP zijn gedeclareerd, bekend in de andere codegedeelten JSP die verspreid zijn over de code HTML: deze variabelen en codegedeelten zullen deel uitmaken van dezelfde Java-methode. Maar als onze code JSP methoden zou bevatten, zouden de variabelen title, firstname en lastname daar niet bekend zijn vanwege de afscherming tussen methoden. We zouden er globale variabelen van moeten maken of ze als parameters aan de methoden moeten doorgeven. We komen hier later op terug.
- Om dynamische onderdelen in de code HTML op te nemen, zijn er twee methoden mogelijk: <%= uitdrukking %> of out.println(uitdrukking). Het object out is een uitvoerstroom die vergelijkbaar is met die met dezelfde naam in de servletvoorbeelden, maar niet van hetzelfde type: het is een JspWriter-object en geen PrintWriter. Hiermee kan in de stroom HTML worden geschreven met de methoden print en println.
- De pagina JSP geeft de structuur van de gegenereerde pagina HTML beter weer dan de bijbehorende servlet.
3.2.3. De tags van JSP
Hieronder volgt een lijst met tags die in een JSP-pagina kunnen voorkomen, met de bijbehorende betekenis.
tag | betekenis |
opmerking HTML. Wordt naar de client verzonden. | |
opmerking JSP. Wordt niet naar de klant verzonden. | |
definieert globale variabelen en methoden. De variabelen zijn in alle methoden beschikbaar | |
De waarde van de uitdrukking wordt in de pagina HTML opgenomen in plaats van de tag | |
bevat Java-code die deel zal uitmaken van de hoofdmethode van de pagina JSP | |
stelt attributen in voor de pagina JSP. Bijvoorbeeld: import="java.util.*,java.sql.*" om de bibliotheken aan te geven die nodig zijn voor de pagina JSP extends="eenOuderklasse" om de JSP-pagina te laten afleiden van een andere klasse |
3.2.4. De impliciete objecten JSP
In de voorgaande voorbeelden zijn we twee niet-gedeclareerde objecten tegengekomen: request en out. Dit zijn twee van de objecten die automatisch worden gedefinieerd in de servlet waarin de pagina JSP wordt geconverteerd. Deze worden impliciete of vooraf gedefinieerde objecten genoemd. Er zijn er nog meer, maar deze worden het meest gebruikt, samen met het object response:
object | betekenis |
het object waarmee toegang wordt verkregen tot het verzoek van de webclient (getParameter, getParameterNames, getParameterValues) | |
het object waarmee het antwoord van de webserver aan de client kan worden opgebouwd. Hiermee kunnen de HTTP-headers worden ingesteld die naar de webclient moeten worden verzonden. | |
de uitvoerstroom waarmee we HTML-code naar de client kunnen verzenden (print, println) |
3.2.5. De omzetting van een pagina JSP naar een servlet
Laten we de code JSP uit myRequestParamExample.jsp nog eens bekijken:
<%
// lokale variabelen in de hoofdprocedure
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>
Wanneer de browser deze pagina JSP opvraagt bij de Tomcat-server, zet deze de pagina om in een servlet. Als de opgevraagde URL
http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp, dan plaatst Tomcat 4.x de gegenereerde servlet in de map <tomcat>\work\localhost\examples\jsp\perso\intro:

In deze naam vinden we de URL-http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp-code van de pagina JSP terug. Hierboven zien we dat we toegang hebben tot de Java-code van de servlet die voor de pagina JSP is gegenereerd. In ons voorbeeld is dat de volgende:
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();
// lokale variabelen in de hoofdprocedure
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);
}
}
}
De gegenereerde code is vrij complex. We zullen ons beperken tot de volgende punten:
- De hoofdmethode van de servlet is als volgt:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
Deze methode wordt bij het opstarten van de servlet aangeroepen. We zien dat deze twee parameters ontvangt: het verzoek request van de client en een object response om het antwoord aan de webclient te genereren.
- In de hoofdmethode wordt een object JspWriter met de naam ‘out’ gedeclareerd en vervolgens geïnitialiseerd. Dit object maakt het mogelijk om code HTML naar de client te verzenden via instructies out.print("codeHTML").
- De Java-code
<%
// lokale variabelen in de hoofdprocedure
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
is volledig overgenomen in de hoofdmethode _jspService van de servlet. Hetzelfde geldt voor alle code die zich binnen de tags <%… %> bevindt
- De code HTML van de pagina JSP is het onderwerp van instructies out.print("codeHTML") of out.write(...). Bijvoorbeeld
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
- In dit voorbeeld zijn er geen andere methoden dan de hoofdmethode _jspService.
3.2.6. De methoden en globale variabelen van een pagina JSP
Laten we eens kijken naar de volgende pagina JSP:
<%!
// de vorige tag markeert het begin van het gedeelte met globale variabelen en methoden
// dit gedeelte wordt ongewijzigd overgenomen in de servlet
// een globale variabele
String prenom="inconnu";
// een methode
private String sonChien(){
return "milou";
}//sonChien
// een andere methode
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// einde van het globale gedeelte van de servlet
%>
<%
// de vorige tag geeft aan dat de volgende code wordt opgeslagen
// in de hoofdmethode van de servlet
// lokale variabele in de hoofdmethode
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>
<%
// de naam van zijn vriend
afficheAmi(out);
%>
</center>
</body>
</html>
Deze pagina JSP genereert de volgende webpagina:

Laten we eens kijken hoe de vier bovenstaande regels worden gegenereerd:
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// de naam van zijn vriend
afficheAmi(out);
%>
De bovenstaande regels staan in een <%..%>-tag en zullen dus deel uitmaken van de hoofdmethode _jspService van de servlet die zal worden gegenereerd. Hoe krijgen ze toegang tot de variabelen nom, prenom en de methoden sonChien en afficheAmi?
is een lokale variabele van de hoofdmethode van de pagina JSP en is dus daarin bekend | |
is een globale variabele van de pagina JSP en is dus bekend in de hoofdmethode | |
is een openbare methode van de pagina JSP en is dus toegankelijk vanuit de hoofdmethode | |
is een openbare methode van de pagina JSP en is dus toegankelijk vanuit de hoofdmethode. Merk op dat het object out als parameter aan de methode wordt doorgegeven. Dit is hier verplicht. Het object out wordt namelijk gedeclareerd en geïnitialiseerd in de hoofdmethode van de servlet en is geen globale variabele. |
Laten we nu eens kijken naar de code van de Java-servlet die is gegenereerd op basis van deze pagina JSP, nadat de overbodige code is verwijderd:
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 {
// de vorige tag markeert het begin van het gedeelte met globale variabelen en methoden
// dit gedeelte wordt ongewijzigd overgenomen in de servlet
// een globale variabele
String prenom="inconnu";
// een methode
private String sonChien(){
return "milou";
}//sonChien
// een andere methode
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// einde van het globale gedeelte van de 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");
// de vorige tag geeft aan dat de volgende code wordt opgeslagen
// in de hoofdmethode van de servlet
// lokale variabele in de hoofdmethode
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 ");
// de naam van zijn vriend
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);
}
}
}
Hierboven is te zien dat de Java-code die zich tussen de tags JSP <%! .. %> bevond, volledig is overgenomen en geen deel uitmaakt van de hoofdmethode _jspService van de servlet. De variabelen die in dit gedeelte worden gedeclareerd, zijn dus instantievariabelen en daarmee globaal voor de methoden; hier kunnen ook andere methoden dan _jspService worden gedefinieerd.
// dit gedeelte wordt ongewijzigd overgenomen in de servlet
// een globale variabele
String prenom="inconnu";
// een methode
private String sonChien(){
return "milou";
}//sonChien
// een andere methode
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// einde van het globale gedeelte van de servlet
3.2.7. Implementatie en foutopsporing van JSP-pagina's op de Tomcat-server
Wanneer men een JSP-pagina wil bouwen en deze met de Tomcat-server wil gebruiken, rijst de vraag waar de pagina in de serverstructuur moet worden geplaatst. Er zijn verschillende manieren om dit te doen, waarop we later zullen terugkomen. Voorlopig is de eenvoudigste manier om de pagina JSP in een map van de boomstructuur <tomcat>\webapps\examples\jsp (Tomcat 4.x) te plaatsen, waarbij <tomcat> de installatiemap van Tomcat is. Zo was de URL uit het vorige voorbeeld http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Dit betekent dat de pagina tintin.jsp zich in de map <tomcat>\webapps\examples\jsp\perso\tintin bevond.
Een pagina met de naam JSP wordt vertaald naar een Java-bronbestand, dat vervolgens door Tomcat wordt gecompileerd wanneer de pagina JSP (met de naam URL) door een browser wordt opgevraagd. Er kunnen compilatiefouten optreden. Tomcat 4.x meldt deze in zijn antwoord aan de browser. Het geeft met name aan welke regels in het .java-bestand foutief zijn. De fouten kunnen verschillende oorzaken hebben:
- de code JSP van de pagina is foutief (bijvoorbeeld fouten in de gebruikte JSP-tags)
- de Java-code die in de pagina is opgenomen, is foutief
De eerste oorzaak kan worden uitgesloten door de JSP-code van de pagina te controleren. De tweede oorzaak kan worden uitgesloten door de Java-code te controleren. Dit kan worden gedaan door het .java-bestand dat voor de pagina JSP is gegenereerd, rechtstreeks te compileren met een tool zoals JBuilder, die geavanceerdere debugmogelijkheden biedt dan Tomcat.
3.2.8. Voorbeelden
We nemen het eerder besproken voorbeeld van een servlet waarin een gebruiker een getal uit een lijst kiest en de server hem vertelt welk getal hij heeft gekozen, terwijl hij tegelijkertijd dezelfde lijst terugstuurt met het door de gebruiker gekozen element als geselecteerd element:

Om deze pagina te bouwen, hebben we de code van de servlet overgenomen en als volgt aangepast:
- we hebben de Java-code die geen HTML-code produceerde, ongewijzigd gelaten
- de Java-code die de code HTML produceerde, is omgezet in een combinatie van de code HTML en de code JSP
Dit levert dan de volgende pagina JSP op:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// globale variabelen van de applicatie
// de titel van de pagina
private final String title="Génération d'un formulaire";
// de database met de waarden van de lijst
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// lijstwaarden
private String[] valeurs=null;
// foutmelding
private String msgErreur=null;
// initialisatie van de pagina JSP – wordt slechts één keer uitgevoerd
public void jspInit(){
// vult de waardetabel vanuit een database ODBC
// met de naam DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// verbinding met de database ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// Statement-object
st=connexion.createStatement();
// uitvoering van een SELECT-query om de waarden op te halen
rs=st.executeQuery("select valeur from Tvaleurs");
// de waarden worden opgehaald en in een dynamische array geplaatst
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// de waarde wordt in de lijst opgeslagen
lstValeurs.add(rs.getString("valeur"));
}//while
// omzetting van lijst naar array
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// probleem
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//initialiseren
%>
<%
// code van _jspService die bij elke clientverzoek wordt uitgevoerd
// is er een fout opgetreden bij het initialiseren van de pagina JSP?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %></h3>
</body>
</html>
<%
// einde van jspService
return;
}//if
// wordt de eventuele keuze van de gebruiker opgehaald
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
%>
<%-- geen fout - code HTML van de normale pagina --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST">
<select name="cmbValeurs">
<%
// dynamische weergave van waarden
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>");
}//voor
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// is er een waarde geselecteerd?
if(! choix.equals("")){
// de keuze van de gebruiker wordt weergegeven
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Let op de volgende punten:
- de instructies import van de servlet zijn omgeven door een <% page import="..." %>-richtlijn
- de tag <%! ... %> omvat de globale variabelen en de Java-methoden van de applicatie
- de methode init van de servlet, die slechts één keer wordt uitgevoerd, namelijk bij het laden van de servlet, wordt voor een pagina JSP aangeroepen als: jspInit. Deze twee methoden hebben dezelfde functie. Daarom is hier de code van de methode init van de servlet volledig overgenomen.
- De instantievariabelen van de servlet, die in meerdere methoden bekend moeten zijn, zijn identiek overgenomen. Het gaat hierbij voornamelijk om de variabelen title, valeurs en msgErreur, die vervolgens worden gebruikt in de code JSP.
- De tags <% ... %> omsluiten Java-code die zal worden opgenomen in de methode _jspService, die wordt uitgevoerd wanneer er een verzoek van een client binnenkomt.
- Net als bij de servlet zal de methode _jspService eerst de waarde van de variabele msgErreur controleren om te bepalen of er een foutpagina moet worden gegenereerd. Als er een fout is, genereert de methode de foutpagina en stopt (return).
- Als er geen fout is, genereert de methode het formulier met de lijst met waarden
- daarna controleert het of de gebruiker een getal heeft gekozen; in dat geval wordt dit getal op de gegenereerde pagina weergegeven
Wat is het voordeel ten opzichte van de servlet? Ongetwijfeld een beter overzicht van de gegenereerde code HTML. Maar er blijft nog steeds veel Java-code over die dit overzicht „vervuilt“. Later zullen we een andere methode bekijken, delegatie genaamd, waarbij we het grootste deel van de Java-code in een servlet kunnen plaatsen, terwijl de pagina JSP alleen de code HTML en JSP behoudt. Zo wordt het verwerkingsgedeelte duidelijk gescheiden van het presentatiegedeelte.
3.3. Implementatie van een webapplicatie op de Tomcat-server
We laten nu zien hoe je Java-webapplicaties implementeert met de Tomcat-server. Hoewel wat hier wordt beschreven specifiek is voor deze server, zal de implementatie van een Java-webapplicatie in een andere container J2EE kenmerken vertonen die vergelijkbaar zijn met wat hierna wordt beschreven.
3.3.1. De configuratiebestanden server.xml en web.xml
Tot nu toe hebben we, om onze servlets en pagina's JSP te testen,
- de servlets in de map <tomcat>\webapps\examples\WEB-INF\classes geplaatst. Ze waren toen toegankelijk via de URL http://localhost:8080/examples/servlet/nomServlet
- de pagina's JSP in de boomstructuur <tomcat>\webapps\examples\jsp. Ze waren toen toegankelijk via de URL-http://localhost:8080/examples/jsp/nomPageJSP
We hebben nooit uitgelegd waarom dit zo was. De configuratie van de Tomcat-server wordt vastgelegd in een tekstbestand met de naam server.xml, dat zich in de map <tomcat>\conf bevindt:

Dit tekstbestand is in feite een XML-bestand (eXtended Markup Language). Een XML-document is een tekstdocument dat tags bevat, net als een HTML-document. Maar terwijl de tags van de taal HTML wel duidelijk gedefinieerd zijn, is dat bij de tags van de taal XML niet het geval. Het volgende document is dus een XML-document:
Een XML-document is simpelweg een "opgemaakt" document dat aan bepaalde opmaakregels voldoet:
- tekst met tags in de vorm <xx att1="val1" att2="val2" ....>tekst</xx>
- een tag kan op zichzelf staan en de vorm <xx att1="val1" att2="val2" ..../> hebben
De velden 'atti' worden attributen van de tag 'xx' genoemd en de velden 'vali' zijn de waarden die aan deze attributen zijn gekoppeld. Sommige HTML-documenten zijn geen geldige XML-documenten. De tag HTML <br> is bijvoorbeeld geen geldige XML-tag. Om geldig te zijn, zou deze moeten worden geschreven als <br/>, om te voldoen aan de regel dat elke XML-tag moet worden gesloten. Er is een variant van HTML, genaamd XHTML, gecreëerd om van elk XHTML-document een geldig XML-document te maken. Sommige recentere browsers kunnen XML-bestanden weergeven. Als we het hierboven als voorbeeld getoonde document XML dus personne.xml noemen en het bekijken met IE6, krijgen we de volgende weergave:

IE6 herkent de tags en geeft ze een kleur. Het herkent ook de structuur van het document aan de hand van de tags. Als we het volgende document dus personne2.xml noemen:
en dit bekijken met IE6, krijgen we dezelfde weergave:

IE6 heeft de structuur en de inhoud van het document correct herkend. Het grote voordeel van het document XML ligt juist in deze eigenschap: het is eenvoudig om de structuur en de inhoud van een document XML terug te vinden. Dit gebeurt met een programma dat een XML-parser wordt genoemd. XML-documenten lijken de norm te worden bij de uitwisseling van documenten op het web. Stel dat machine A een DOC-document naar machine B moet verzenden. Het DOC-document wordt samengesteld op basis van de informatie in een DB-A-database. Machine B moet het document DOC opslaan in een database DB-B. De uitwisseling kan als volgt plaatsvinden:
- machine A haalt de gegevens op uit de database DB-A en verpakt deze in een tekstdocument XML
- het document XML wordt via het netwerk naar machine B verzonden
- machine B analyseert het ontvangen document met een parser XML en haalt zowel de structuur als de gegevens eruit (zoals IE6 in ons voorbeeld heeft gedaan). Vervolgens kan machine B de ontvangen gegevens opslaan in de database DB-B
We zullen hier niet verder ingaan op de taal XML, die een heel boek op zich verdient.
Hier wordt Tomcat dus geconfigureerd via het bestand XML server.xml. Als we dit bekijken met IE6, krijgen we een complex document te zien. We zullen ons beperken tot de volgende regels:

Het is de tag <Context ...> die ons hier interesseert. Deze wordt gebruikt om webapplicaties te definiëren. Twee van de attributen zijn het vermelden waard:
- path: dit is de naam van de webapplicatie
- docBase: dit is de map waarin de webapplicatie zich bevindt. Hier is het een relatieve naam: examples. Relatief ten opzichte van welke map? Het antwoord vinden we ook in het bestand server.xml in de volgende regel:

De bovenstaande regel definieert de webserver:
- name: naam van de webserver
- appBase: de root van de documentboomstructuur die hij distribueert. Ook hier hebben we weer een relatieve naam: webapps. Deze is relatief ten opzichte van de installatiemap van de Tomcat-server <tomcat>. Het gaat dus om de map <tomcat>\webapps.
De webapplicatie examples heeft haar documenten in de map examples (zie docBase hierboven). Deze naam is relatief ten opzichte van de root van de webstructuur van de server, c.a.d. <tomcat>\webapps. Het gaat dus om de map <tomcat>\webapps\examples. Laten we deze map eens nader bekijken:

We vinden daar de map WEB-INF\classes waarin we onze servlets hebben opgeslagen om ze te testen. De map WEB-INF bevat een bestand met de naam web.xml:

Dit bestand dient om de webapplicatie examples te configureren. We zullen voorlopig niet ingaan op de details van dit bestand, dat te complex is. We zullen ons beperken tot de volgende paar regels:
<servlet>
<servlet-name>
servletToJsp
</servlet-name>
<servlet-class>
servletToJsp
</servlet-class>
</servlet>
De tag <servlet> wordt gebruikt om een servlet binnen een webapplicatie te definiëren. Ter herinnering: de betreffende webapplicatie is examples. De tag servlet bevat hier twee andere tags:
- <servlet-name>servletToJsp</servlet-name>: definieert de naam van de servlet
- <servlet-name>servletToJsp</servlet-name>: definieert de naam van de klasse die moet worden uitgevoerd wanneer de servlet wordt aangevraagd. In dit voorbeeld hebben de servlet en de bijbehorende klasse dezelfde naam. Dit is niet verplicht.
Hoe wordt de servlet servletToJsp door een browser bij de Tomcat-server opgevraagd?
- De browser vraagt de URL aan via http://localhost:8080/examples/servlet/servletToJsp
- Tomcat analyseert het pad van de servlet /examples/servlet/servletToJsp. Het interpreteert het eerste deel van het pad /examples als de naam van een webapplicatie en zoekt in het configuratiebestand server.xml waar de documenten van deze applicatie zijn opgeslagen. Zoals we eerder hebben gezien, is dat in de map <tomcat>\webapps\examples.
- Tomcat gebruikt de rest van het pad van de servlet om deze te lokaliseren in de webapplicatie examples. Dit pad /servlet/servletToJsp geeft aan dat de servlet met de naam servletToJsp moet worden uitgevoerd. Tomcat leest vervolgens het configuratiebestand web.xml van de applicatie examples, dat hij aantreft in <tomcat>\webapps\examples\WEB-INF. In dit bestand zal hij zien dat de servlet servletToJsp gekoppeld is aan de Java-klasse servletToJsp (zie het bestand web.xml hierboven). Vervolgens zoekt hij deze klasse in de map WEB-INF\classes van de webapplicatie examples, c.a.d. in <tomcat>\webapps\examples\WEB-INF\classes en voert deze uit.

3.3.2. Voorbeeld: implementatie van de webapplicatie ‘lijst’
We gebruiken een servlet die we al eerder hebben besproken en die de gebruiker een lijst met getallen toonde, waaruit hij er één kon kiezen. De servlet bevestigde vervolgens het getal dat hij had gekozen:

Zoals te zien is in het veld Address van de browser hierboven, heette het klassebestand van de servlet gener3. Op basis van de eerder gegeven uitleg:
- URL /examples/servlet/gener3 laat zien dat het gaat om een servlet met de naam gener3 van de webapplicatie examples
- in het bestand web.xml van de applicatie examples is er niets te vinden dat verwijst naar een servlet met de naam gener3. Hoe heeft Tomcat deze dan gevonden? Aangezien ik het volledige bestand web.xml heb doorgenomen, kan ik hier geen zeker antwoord op geven... De vraag blijft onbeantwoord...
We kiezen ervoor om de servlet gener3.class onder de naam lstValeurs te implementeren in een webapplicatie met de naam ‘liste’, die zich bevindt in de map E:\data\serge\Servlets\lstValeurs:

We plaatsen het bestand gener3.class in de map WEB-INF\classes hierboven:

We configureren de webapplicatie ‘liste’ door in het bestand server.xml de volgende regels toe te voegen boven de regels die de webapplicatie manager definiëren:
<!-- Persoonlijk: lstValeurs -->
<Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />
<!-- Tomcat Manager Context -->
<Context path="/manager" docBase="manager" debug="0" privileged="true" />
<!-- Tomcat-voorbeeldencontext -->
<Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........
De regel die de applicatielijst definieert, geeft aan dat deze zich bevindt in de map e:/data/serge/servlets/lstValeurs. We moeten nu het bestand web.xml van deze applicatie definiëren. Dit bestand definieert de enige servlet van de applicatie:
<?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>
Het bovenstaande bestand geeft aan dat de servlet met de naam lstValeurs gekoppeld is aan het klassebestand gener3.class. Dit bestand web.xml moet worden aangemaakt en opgeslagen in de map WEB-INF van de applicatie lijst:

De bovenstaande schermafbeelding toont een map src waarin het bronbestand gener3.java is geplaatst. Deze map hoeft niet te bestaan. Ze is in deze demonstratie niet van belang. We zijn klaar om de tests uit te voeren:
- Sluit Tomcat af en start het opnieuw op, zodat het zijn configuratiebestand server.xml opnieuw leest. We werken hier met Windows. Onder Unix kun je Tomcat dwingen zijn configuratiebestand opnieuw te lezen zonder het af te sluiten.
- Vraag met een browser de pagina URL op via http://localhost:8080/liste/servlet/lstValeurs

We zien dat de vorige URL het trefwoord servlet bevat, net als alle URL van servlets die tot nu toe zijn gebruikt. We kunnen dit achterwege laten door in het bestand web.xml van de lijsttoepassing de servlet lstValeurs te koppelen aan een sjabloon van URL (url-pattern):
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
In de tag <servlet-mapping> koppelen we het pad /waarden aan de servlet lstValeurs die in de voorgaande regels is gedefinieerd. We slaan het nieuwe bestand web.xml op en roepen URL en http://localhost:8080/liste/valeurs op:

3.3.3. Implementatie van de openbare pagina’s van een webapplicatie
We hebben zojuist gezien hoe een webapplicatie wordt geïmplementeerd die uit één enkele servlet bestaat. Een webapplicatie kan uit talrijke componenten bestaan: servlets, JSP-pagina’s, HTML-bestanden, Java-applets, … Waar worden deze onderdelen van de applicatie geplaatst? Als <application> de map van de webapplicatie is, zoals gedefinieerd door het attribuut docBase van de applicatie in het Tomcat-configuratiebestand server.xml, dan hebben we gezien dat de servlets in <application>\WEB-INF\classes werden geplaatst. De overige onderdelen van de applicatie kunnen overal in de mappenstructuur van de map <application> worden geplaatst, behalve in de map WEB-INF. Laten we eens kijken naar de applicatie JSP listvaleurs.jsp die we al hebben besproken:

Deze pagina JSP was opgeslagen in de map <tomcat>\webapps\examples\jsp\perso\listvaleurs. Deze pagina zou een onderdeel kunnen zijn van de eerder geïmplementeerde applicatie „liste”. Laten we het bestand listvaleurs.jsp rechtstreeks in de map van deze applicatie plaatsen:

Laten we nog eens kijken naar de configuratie van de applicatie liste in het bestand server.xml:
Elke URL die begint met het pad /liste wordt beschouwd als onderdeel van de applicatie liste en zal in de aangegeven map worden gezocht. Laten we het bestand URL http://localhost:8080/liste/listvaleurs.jsp met een browser openen:

We hebben inderdaad de verwachte pagina JSP ontvangen.
3.3.4. Initialisatieparameters van een servlet
We hebben gezien dat een servlet wordt geconfigureerd via het bestand <application>\WEB-INF\web.xml, waarbij <application> de map is van de webapplicatie waartoe de servlet behoort. Het is mogelijk om in dit bestand initialisatieparameters voor de servlet op te nemen. Laten we teruggaan naar onze servlet lstValeurs van de webapplicatie liste, waarvan het configuratiebestand er als volgt uitzag:
<?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>
De klasse die aan de servlet is gekoppeld, is de klasse gener3. In de broncode daarvan vinden we de definitie van enkele constanten:
public class gener3 extends HttpServlet{
// de titel van de pagina
private final String title="Génération d'un formulaire";
// de database met lijstwaarden
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
Laten we nog eens de betekenis van de vier hierboven gedefinieerde constanten in herinnering brengen:
titel van het document HTML dat door de servlet is gegenereerd | |
naam DSN van de database ODBC waaruit de servlet gegevens ophaalt | |
naam van een gebruiker met leesrechten voor de voorgaande database | |
zijn wachtwoord |
Als de beheerder van de database DSNValeurs het wachtwoord van de gebruiker admDbValeurs wijzigt, moet de broncode van de servlet worden aangepast en opnieuw worden gecompileerd. Dat is niet erg praktisch. Het servlet-configuratiebestand web.xml biedt ons een alternatief door het mogelijk te maken initialisatieparameters voor de servlet te definiëren met de tag <init-param>:
hiermee kan de naam van de parameter worden gedefinieerd | |
definieert de waarde die aan de vorige parameter is gekoppeld |
De servlet heeft toegang tot zijn initialisatieparameters via de volgende methoden:
methode van de klasse Servlet, waarvan de klasse HttpServlet is afgeleid, die wordt gebruikt voor webprogrammering. Geeft een ServletConfig-object terug dat toegang biedt tot de configuratieparameters van de servlet. | |
methode van de klasse ServletConfig die de waarde van de initialisatieparameter "parameter" retourneert |
We configureren de applicatie liste met het volgende nieuwe bestand web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs2</servlet-name>
<servlet-class>gener5</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</init-param>
<init-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</init-param>
<init-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs2</servlet-name>
<url-pattern>/valeurs2</url-pattern>
</servlet-mapping>
</web-app>
In de applicatie liste definiëren we een tweede servlet met de naam lstValeurs2, gekoppeld aan het klassebestand gener5. Dit bestand is geplaatst in <application>\WEB-INF\classes:

De servlet lstValeurs2 heeft vier initialisatieparameters: title, DSNValeurs, admDbValeurs, mdpDbValeurs. Bovendien is de alias /valeurs2 voor de servlet gedefinieerd met behulp van de tag <servlet-mapping>. Daardoor zal de servlet lstValeurs2 van de applicatie liste toegankelijk zijn via URL en http://localhost:8080/liste/valeurs2.
De broncode van de servlet is als volgt aangepast om de initialisatieparameters van de servlet op te halen:
public class gener5 extends HttpServlet{
// de titel van de pagina
private String title=null;
// de database met lijstwaarden
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
...............
// initialisatie van de servlet
public void init(){
// de initialisatieparameters van de servlet worden opgehaald
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
DSNValeurs=config.getInitParameter("DSNValeurs");
admDbValeurs=config.getInitParameter("admDbValeurs");
mdpDbValeurs=config.getInitParameter("mdpDbValeurs");
//zijn alle parameters opgehaald?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// de tabel met waarden wordt gevuld vanuit een database ODBC
// met de naam DSN: DSNvaleurs
...............
Om de servlet te testen, moet Tomcat opnieuw worden gestart, zodat het rekening houdt met het nieuwe configuratiebestand web.xml van de applicatie liste. Met een browser roep je de URL van de servlet http://localhost:8080/liste/valeurs2 op:

Als een van de initialisatieparameters die de servlet nodig heeft, ontbreekt in het bestand web.xml, krijg je de volgende pagina te zien:

3.3.5. Initialisatieparameters van een webapplicatie
In het vorige voorbeeld heeft alleen de servlet lstValeurs2 toegang tot de parameters title, DSNValeurs, admDbValeurs en mdpDbValeurs. Stel dat een andere servlet van dezelfde applicatie, liste, gegevens nodig heeft uit dezelfde database die de servlet lstValeurs2 gebruikt. In dat geval zouden de parameters DSNValeurs, admDbValeurs en mdpDbValeurs opnieuw moeten worden gedefinieerd in het configuratiegedeelte van het bestand web.xml van de nieuwe servlet. Een andere oplossing is om de parameters die voor meerdere servlets gelden op applicatieniveau te definiëren in plaats van op servletniveau. Het nieuwe bestand web.xml van de applicatie ziet er dan als volgt uit:
<?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>
De nieuwe servlet heet lstValeurs3, is gekoppeld aan het klassebestand gener6 en is toegewezen aan de alias /valeurs3 (servlet-mapping). De parameter title is de enige parameter die in de definitie van de servlet is behouden. De overige parameters zijn in de applicatieconfiguratie ondergebracht in <context-param>-tags. Deze tag wordt gebruikt om applicatiespecifieke informatie te definiëren en niet voor een specifieke servlet of JSP-pagina. Hoe krijgt de Java-servlet toegang tot deze parameters, die vaak contextparameters worden genoemd? De beschikbare methoden om contextinformatie op te halen lijken sterk op die welke worden gebruikt om de initialisatieparameters van een specifieke servlet op te halen:
methode van de klasse Servlet, waarvan de klasse HttpServlet is afgeleid, die wordt gebruikt voor webprogrammering. Geeft een ServletContext-object terug dat toegang biedt tot de configuratieparameters van de applicatie | |
methode van de klasse ServletContext die de waarde van de initialisatieparameter "parameter" retourneert |
De klasse gener6.java brengt de volgende wijzigingen aan in de Java-code van de eerder gebruikte gener5.java:
// de initialisatieparameters van de servlet worden opgehaald
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//zijn alle parameters opgehaald?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// de tabel met waarden wordt gevuld vanuit een database ODBC
// met de naam DSN: DSNvaleurs
...............
De servlet-specifieke parameter title wordt verkregen via een object ServletConfig. De drie andere parameters die op applicatieniveau zijn gedefinieerd, worden verkregen via een object ServletContext. We compileren deze klasse en plaatsen deze, net als de andere, in <application>\WEB-INF\classes:

We starten Tomcat opnieuw op zodat het rekening houdt met het nieuwe web.xml-bestand van de applicatie en vragen om de URL en http://localhost:8080/liste/valeurs3:

3.3.6. Initialisatieparameters voor een pagina JSP
We hebben gezien hoe initialisatieparameters voor een servlet of een webapplicatie kunnen worden gedefinieerd. Is dit ook mogelijk voor een pagina JSP? Laten we teruggaan naar het begin van de code van de reeds besproken pagina listvaleurs.jsp:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// globale variabelen van de applicatie
// de titel van de pagina
private final String title="Génération d'un formulaire";
// de database met lijstwaarden
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
.........
De vier constanten title, DSNValeurs, admDbValeurs en mdpDbValeurs zijn gedefinieerd in het bestand web.xml van de applicatie. De constanten DSNValeurs, admDbValeurs en mdpDbValeurs zijn nu op applicatieniveau gedefinieerd en we kunnen dus aannemen dat een pagina JSP die deel uitmaakt van deze applicatie er toegang toe zal hebben. Dat is inderdaad het geval. We weten dat de pagina JSP zal worden omgezet in een servlet. Deze zal toegang hebben tot de context via de methode getServletContext(). Het geval van de constante title ligt wat lastiger. We hebben deze namelijk op servletniveau gedefinieerd en niet op applicatieniveau, en wel als volgt:
<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>
Voor de pagina JSP is de vorige syntaxis niet meer geschikt, omdat het begrip 'klassebestand' niet meer bestaat. De configuratiesyntaxis van een pagina JSP lijkt echter sterk op die van een servlet. Deze is als volgt:
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
...
</servlet>
In feite wordt een pagina JSP gelijkgesteld aan een servlet waaraan een naam wordt toegekend (servlet-name). In plaats van een klassebestand aan deze servlet te koppelen, wordt het bronbestand van de uit te voeren pagina JSP gekoppeld (jsp-file). De voorgaande paar regels definiëren dus een servlet met de naam JSPlstvaleurs die is gekoppeld aan de pagina JSP /listvaleurs2.jsp. Het pad /listvaleurs2.jsp wordt gemeten ten opzichte van de root van de applicatie. In het geval van onze applicatie liste zou het bestand listvaleurs2.jsp zich bevinden in de map docBase (zie server.xml) van de applicatie liste:

De configuratie van de pagina JSP zal als volgt zijn in het bestand web.xml van de applicatie:
<?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>
De pagina JSP listvaleurs2.jsp bevindt zich in de hoofdmap van de applicatie liste en is gekoppeld aan de servletnaam JSPlstValeurs (servlet-name), die op zijn beurt is gekoppeld aan de alias /jspvaleurs (servlet-mapping). Onze pagina JSP zal dus toegankelijk zijn via URL en http://localhost:8080/liste/jspvaleurs.
De oorspronkelijke pagina JSP (listvaleurs.jsp) wordt gewijzigd in listvaleurs2.jsp en haalt zijn vier initialisatieparameters op in de methode jspInit():
<%!
// globale variabelen van de applicatie
// de titel van de pagina
private String title=null;
// de database met lijstwaarden
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// lijstwaarden
private String[] valeurs=null;
// foutmelding
private String msgErreur=null;
// initialisatie van de pagina JSP – wordt slechts één keer uitgevoerd
public void jspInit(){
// de initialisatieparameters van de servlet worden opgehaald
ServletConfig config=getServletConfig();
title=config.getInitParameter("JSPtitle");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//zijn alle parameters opgehaald?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// vult de waarden in de tabel aan vanuit een database ODBC
// met de naam DSN: DSNvaleurs
..............
De pagina JSP haalt zijn initialisatieparameters op dezelfde manier op als de servlets. Het vorige bestand wordt opgeslagen in de hoofdmap van de webapplicatie liste:

De Tomcat-server wordt opnieuw opgestart om ervoor te zorgen dat het nieuwe configuratiebestand web.xml van de applicatie opnieuw wordt gelezen. Vervolgens kan de URL http://localhost:8080/liste/jspvaleurs worden opgevraagd:

3.3.7. Samenwerking tussen servlets en pagina's JSP binnen een webapplicatie
Wanneer een client een verzoek indient bij een webserver, kan het antwoord worden samengesteld door meerdere servlets en pagina's JSP. Tot nu toe werd het antwoord samengesteld door één enkele servlet of pagina JSP. We hebben gezien dat de JSP-pagina de structuur van het gegenereerde HTML-document beter leesbaar maakt. Deze bevat echter doorgaans ook veel Java-code. Dit kan worden verbeterd door
- de Java-code die niet de HTML-code van het antwoord genereert, in een of meer servlets te plaatsen
- in JSP-pagina’s, de code voor het genereren van de verschillende HTML-documenten die als antwoord naar de client worden verzonden
Op deze manier hopen we de scheiding tussen Java-code en HTML-code te verbeteren. We gaan deze nieuwe structuur toepassen op onze applicatie liste: een Java-servlet lstValeurs4 zal bij het opstarten de waarden uit de database inlezen en vervolgens de verzoeken van de klanten analyseren. Afhankelijk van het resultaat van deze analyse wordt de verzoek van de klant doorgestuurd naar een foutpagina erreur.jsp of naar de pagina waarop de lijst met getallen wordt weergegeven liste.jsp. De applicatie liste zal dus bestaan uit een servlet en twee pagina’s JSP.
Hoe kan een servlet het verzoek dat zij van een client heeft ontvangen, doorgeven aan een andere servlet of aan een pagina JSP? We gebruiken hiervoor de volgende methoden:
methode van de klasse ServletContext die een object RequestDispatcher retourneert. De parameter url is de naam van de URL waaraan we het verzoek van de client willen doorgeven. Deze doorgifte van het verzoek kan alleen plaatsvinden binnen dezelfde applicatie. Daarom is de parameter url een relatief pad binnen de webstructuur van deze applicatie. | |
methode van de interface RequestDispatcher die het verzoek request van de client en het object response, dat moet worden gebruikt om het antwoord op te stellen, doorgeeft aan de voorgaande URL. | |
wanneer een servlet of pagina JSP een verzoek doorgeeft aan een andere servlet of pagina JSP, moet deze doorgaans naast het verzoek van de klant ook andere informatie doorgeven, namelijk informatie die voortkomt uit de eigen verwerking van het verzoek. Met de methode setAttribute van de klasse ServletRequest kunnen attributen worden toegevoegd aan het request-object van de client in een vorm die lijkt op een woordenboek van paren (attribuut, waarde), waarbij attribut de naam van het attribuut is en valeur een willekeurig object dat de waarde ervan vertegenwoordigt. | |
maakt het mogelijk om de waarden van de attributen van een verzoek op te halen. Deze methode wordt gebruikt door de servlet of de pagina JSP waaraan een verzoek is doorgestuurd om de informatie op te halen die eraan is toegevoegd. |
De servlet die het formulier verwerkt, wordt als volgt geconfigureerd in het bestand web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
............
<servlet>
<servlet-name>lstValeurs4</servlet-name>
<servlet-class>gener7</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>JSPerreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
<init-param>
<param-name>JSPliste</param-name>
<param-value>/liste.jsp</param-value>
</init-param>
<init-param>
<param-name>URLservlet</param-name>
<param-value>/liste/valeurs4</param-value>
</init-param>
</servlet>
...........
<servlet-mapping>
<servlet-name>lstValeurs4</servlet-name>
<url-pattern>/valeurs4</url-pattern>
</servlet-mapping>
.......
</web-app>
De servlet lstValeurs4 heeft vier eigen initialisatieparameters:
de titel van het te genereren document HTML | |
de URL van de foutpagina JSP | |
de URL van de pagina JSP met de lijst met getallen | |
de URL die gekoppeld is aan het action-attribuut van het formulier dat wordt weergegeven op de pagina JSPliste. Deze URL is die van de servlet lstValeurs4 |
De servlet krijgt de alias /valeurs4 (servlet-mapping) en is dus toegankelijk via de URL http://localhost:8080/liste/valeurs4. Deze is gekoppeld aan het klassebestand gener7.java, waarvan de volledige broncode als volgt luidt:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener7 extends HttpServlet{
// de titel van de pagina
private String title=null;
// de database met de lijstwaarden
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// de weergavepagina's JSP
private String JSPerreur=null;
private String JSPliste=null;
// de URL van de servlet
private String URLservlet=null;
// lijstwaarden
private String[] valeurs=null;
// foutmelding
private String msgErreur=null;
// -----------------------------------------------------------------
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// msgErreur,title wordt in de verzoekattributen geplaatst
request.setAttribute("msgErreur",msgErreur);
request.setAttribute("title",title);
request.setAttribute("URLservlet",URLservlet);
// is er een fout opgetreden bij het laden van de servlet?
if(msgErreur!=null){
// we sturen de gebruiker door naar een foutpagina JSP
getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
// einde
return;
}
// er is geen fout opgetreden
// de lijst met waarden wordt in de verzoekattributen geplaatst
request.setAttribute("valeurs",valeurs);
// de eventuele keuze van de gebruiker wordt opgehaald
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
request.setAttribute("choix",choix);
// we geven het stokje door aan de pagina JSP waarop de lijst wordt weergegeven
getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
// einde
return;
}//GET
// -----------------------------------------------------------------
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// verwijst door naar GET
doGet(request,response);
}//POST
// -----------------------------------------------------------------
// initialisatie van de servlet
public void init(){
// de initialisatieparameters van de servlet worden opgehaald
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");
//zijn alle parameters opgehaald?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
msgErreur="Configuration incorrecte";
return;
}
// vult de waarden in de tabel aan vanuit een database ODBC
// met de naam DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// verbinding met de database ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// Statement-object
st=connexion.createStatement();
// uitvoering van een SELECT-query om de waarden op te halen
rs=st.executeQuery("select valeur from Tvaleurs");
// de waarden worden opgehaald en in een dynamische array geplaatst
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// de waarde wordt in de lijst opgeslagen
lstValeurs.add(rs.getString("valeur"));
}//while
// omzetting van lijst naar array
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// probleem
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//initialiseren
}//klasse
Nieuw in deze klasse is dat het verzoek van de klant bij een fout wordt doorgestuurd naar de pagina JSPerreur en anders naar de pagina JSPliste. De klasse stelt zelf geen antwoord op. Dat wordt gedaan door de pagina’s JSP, JSPerreur en JSPliste. Voorheen voegde de servlet attributen (setAttribute) toe aan het verzoek van de client:
- een foutmelding msgErreur bij een fout op de pagina JSPerreur
- de weer te geven waarden (valeurs)), de door de gebruiker gekozen waarde (choix), de titel (title) van het formulier, de URL (URLservlet) van het action-attribuut van het formulier voor de pagina JSPliste
Deze klasse wordt gecompileerd en toegevoegd aan de klassen van de applicatie:

De pagina JSP, waarop een foutmelding wordt weergegeven, is als volgt geconfigureerd:
<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>
Het bestand JSP dat bij de foutpagina hoort, heet erreur.jsp en bevindt zich in de hoofdmap van de applicatie:

Het heeft als alias /JSPerreur, waardoor het toegankelijk is via de URL http://localhost:8080/liste/JSPerreur. Het heeft een initialisatieparameter met de naam mainServlet, waarvan de waarde de alias is van de hierboven beschreven hoofd-servlet. Merk op dat deze alias relatief is ten opzichte van de root van de applicatie liste,; anders zou het /liste/valeurs4. zijn. De code van de pagina erreur.jsp is als volgt:
<%
// code van _jspService
// de initialisatieparameter wordt opgehaald: mainServlet
String servletListValeurs=config.getInitParameter("mainServlet");
// we halen het attribuut op msgErreur
String msgErreur=(String)request.getAttribute("msgErreur");
// is het attribuut geldig?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %>)</h3>
</body>
</html>
<%
} else { // attribuut msgErreur ongeldig – terug naar de hoofd-servlet
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}
%>
Deze pagina moet normaal gesproken worden aangeroepen door de voorgaande servlet, die het attribuut msgErreur moet doorgeven. Niets belet echter om de pagina rechtstreeks aan te roepen als men de URL kent. Als blijkt dat het attribuut msgErreur ontbreekt, wordt het verzoek doorgegeven aan de hoofd-servlet. Hier wordt een tag gebruikt die specifiek is voor JSP-pagina’s en waarvan de syntaxis als volgt is:
waarbij URL het URL is van de servlet waaraan het verzoek van de klant wordt doorgestuurd. Als het attribuut msgErreur aanwezig is, wordt de foutpagina weergegeven.
De pagina JSP, waarop de lijst met getallen wordt weergegeven, is als volgt geconfigureerd:
<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>
Het bestand JSP dat bij de foutpagina hoort, heet liste.jsp en bevindt zich in de hoofdmap van de applicatie:

De servlet heeft als alias /JSPliste, waardoor deze toegankelijk is via de http://localhost:8080/liste/JSPliste URL. De servlet heeft een initialisatieparameter met de naam mainServlet, waarvan de waarde de alias van de hoofd-servlet is. De code van de pagina liste.jsp is als volgt:
<%-- pagina met de lijst met waarden --%>
<%
// code van jspService
// de initialisatieparameter wordt opgehaald
String servletListValeurs=config.getInitParameter("mainServlet");
// de attributen van het verzoek afkomstig van de hoofd-servlet worden opgehaald
String title=(String) request.getAttribute("title");
String[] valeurs=(String[]) request.getAttribute("valeurs");
String choix=(String) request.getAttribute("choix");
String URLservlet=(String) request.getAttribute("URLservlet");
// geldige attributen?
if(title==null || valeurs==null || choix==null){
// er is een ongeldig attribuut - we geven het door aan de 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">
<%
// dynamische weergave van de waarden
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>
<%
// is er een waarde gekozen?
if(! choix.equals("")){
// de keuze van de gebruiker wordt weergegeven
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Deze pagina werkt op dezelfde manier als de pagina erreur.jsp. Deze pagina moet normaal gesproken worden aangeroepen door de servlet /liste/valeurs4 en de attributen title, valeurs en choix ontvangen. Als een van deze parameters ontbreekt, wordt de controle overgedragen aan de servlet URLservlet (/liste/valeurs4). Als alle parameters aanwezig zijn, wordt de lijst met getallen weergegeven, evenals het door de gebruiker gekozen getal, indien hij er een heeft gekozen.
Als de URL van de hoofd-servlet wordt aangeroepen, krijgt men het volgende resultaat:

met de volgende broncode (View/Source):
<html>
<head>
<title>Génération d'un formulaire</title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="/liste/valeurs4">
<select name="cmbValeurs">
<option >0</option>
<option >1</option>
<option >2</option>
<option >3</option>
<option >4</option>
<option >6</option>
<option >5</option>
<option >7</option>
<option >8</option>
<option >9</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Dit document HTMl is gegenereerd door de pagina JSP liste.jsp. We zien dat de attributen title, valeurs en URLservlet inderdaad zijn overgenomen.
Ter afsluiting van de samenwerking tussen servlets en pagina’s JSP merken we op dat de pagina’s JSP hier erg kort zijn en geen Java-code bevatten die niet direct bijdraagt aan het genereren van het antwoord HTML. De structuur van de gegenereerde documenten is daardoor beter zichtbaar.
3.4. Levenscyclus van de servlets en pagina’s JSP
3.4.1. De levenscyclus
We richten ons hier op de levenscyclus van servlets. Die van de JSP-pagina's vloeit daaruit voort. Laten we eens kijken naar een servlet die voor het eerst wordt aangeroepen. Er wordt dan een klasse-instantie aangemaakt door de webserver en in het geheugen geladen. Deze zal vervolgens het verzoek afhandelen. Nadat dit is gebeurd, wordt de servlet niet uit het geheugen verwijderd. Het blijft in het geheugen om andere verzoeken te verwerken, zodat de responstijden van de server worden geoptimaliseerd. Het wordt pas uit het geheugen verwijderd wanneer er voldoende tijd is verstreken zonder dat het nieuwe verzoeken heeft verwerkt. Deze tijd is doorgaans configureerbaar binnen de webserver.
Zolang de servlet in het geheugen staat, kan deze meerdere verzoeken tegelijkertijd verwerken. De webserver maakt per verzoek een thread aan, die allemaal gebruikmaken van dezelfde servlet-instantie:
![]() |
Alle bovenstaande threads delen de variabelen van de servlet-instantie. Het kan nodig zijn om de threads te synchroniseren om beschadiging van de servlet-gegevens te voorkomen. We komen hier later op terug.
Bij het laden van een servlet wordt een specifieke methode van de servlet uitgevoerd:
Voor een pagina JSP is dat de methode
public void jspInit(){
}
die wordt uitgevoerd. Hier volgt een voorbeeld van een pagina JSP waarbij de methode jspInit wordt gebruikt:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// globale variabelen en methoden van de pagina JSP
// instantievariabele
int compteur;
// methode om de teller te verhogen
public int getCompteur(){
// de teller wordt verhoogd
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// de teller wordt teruggezet
return compteur;
}
// de methode die wordt uitgevoerd bij het eerste laden van de pagina
public void jspInit(){
// teller initialiseren
compteur=100;
}
%>
De vorige pagina JSP zet een teller in jspInit op 100. Elke volgende aanvraag aan de servlet verhoogt de waarde van deze teller en geeft deze vervolgens weer:
De eerste keer:

De tweede keer:

Hierboven is duidelijk te zien dat de servlet tussen de twee verzoeken niet is ontladen, anders zou de teller bij het tweede verzoek op 101 hebben gestaan. Wanneer de servlet wordt ontladen, wordt de methode
uitgevoerd, mits deze bestaat. Voor de pagina's JSP is dat de methode
public void jspDestroy(){
}
In deze methoden kunnen bijvoorbeeld verbindingen met databases worden gesloten, verbindingen die in de bijbehorende methoden init zijn geopend.
3.4.2. Synchronisatie van verzoeken met een servlet
Laten we teruggaan naar de vorige pagina JSP, die een teller verhoogt en deze terugstuurt naar de webclient. Stel dat er twee gelijktijdige verzoeken zijn. Er worden dan twee threads aangemaakt om deze uit te voeren; deze threads zullen dezelfde servlet-instantie gebruiken, dus in dit geval dezelfde teller. Laten we de code nog eens bekijken die de teller verhoogt:
public int getCompteur(){
// de teller wordt verhoogd
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// teruggeven
return compteur;
}
De incrementatie van de teller is opzettelijk onhandig geschreven. Laten we aannemen dat de uitvoering van de twee threads als volgt verloopt:
![]() |
- op tijdstip T1 wordt de thread TH1 uitgevoerd. Deze leest de teller (=145) in myCompteur, wordt vervolgens onderbroken en verliest de processor. Hij heeft dus geen tijd gehad om myCompteur te verhogen en de nieuwe waarde over te schrijven naar compteur.
- Op het moment van T2 wordt de thread TH2 uitgevoerd. Deze leest de teller (=145) uit myCompteur, waarna hij wordt onderbroken en de processor kwijtraakt. Merk op dat de twee threads verschillende myCompteur-variabelen hebben. Ze delen alleen de instantievariabelen, die globaal zijn voor de methoden.
- Op tijdstip T3 krijgt de thread TH1 weer de controle en wordt voltooid. Hij stuurt dus 146 terug naar zijn client.
- Op tijdstip T4 krijgt de thread TH2 weer de controle en wordt deze voltooid. Ook deze stuurt 146 terug naar zijn client, terwijl hij 147 had moeten terugsturen.
Hier is sprake van een synchronisatieprobleem tussen threads. Wanneer TH1 de teller wil verhogen, moet worden voorkomen dat een andere thread dit tegelijkertijd doet. Om dit probleem duidelijk te maken, herschrijven we de pagina JSP als volgt:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// globale variabelen en methoden van de pagina JSP
// instantievariabele
int compteur;
// methode om de teller te verhogen
public int getCompteur(){
// de teller wordt uitgelezen
int myCompteur=compteur;
// er wordt 10 seconden gepauzeerd
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// de teller wordt verhoogd
compteur=myCompteur+1;
// de waarde wordt teruggezet
return compteur;
}
// de methode die wordt uitgevoerd bij het eerste laden van de pagina
public void jspInit(){
// teller initialiseren
compteur=100;
}
%>
Hier hebben we de thread gedwongen om 10 seconden na het uitlezen van de teller te stoppen. Daardoor zou deze de processor moeten vrijgeven, zodat een andere thread op zijn beurt een teller kan uitlezen die niet is opgehoogd. Wanneer we verzoeken doen via een browser, merken we geen verschil, behalve dan dat we 10 seconden moeten wachten voordat we het resultaat krijgen.

Als we nu twee browservensters openen en twee verzoeken doen die kort na elkaar plaatsvinden:


Dan krijgen we dezelfde tellerwaarde. We kunnen het probleem beter aan het licht brengen met een geprogrammeerde client in plaats van een handmatige, zoals de browser. Hieronder volgt een Perl-client die als volgt wordt aangeroepen:
programma URL N
waarbij
URL de URL van de teller-servlet is
N het aantal verzoeken dat aan deze servlet moet worden gedaan
Hieronder staan de resultaten voor 5 verzoeken, die het probleem van slechte synchronisatie tussen de threads duidelijk laten zien: ze krijgen allemaal dezelfde waarde van de teller.
DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121
De Java-clientcode is als volgt.
import java.net.*;
import java.util.regex.*;
import java.io.*;
public class clientCompteurJSP {
public static void main(String[] params){
// gegevens
String syntaxe="Syntaxe : pg URL nbAppels";
// controle van de parameters
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
// aantal oproepen
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
// de parameters zijn correct – we kunnen verbinding maken met de URL
try{
getCompteurs(urlCompteur,nbAppels);
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
System.exit(4);
}//try-catch
}//main
private static void getCompteurs (URL urlCompteur, int nbAppels)
throws Exception {
// voert nbAppels uit naar URL urlCompteur
// geeft telkens de door de webserver teruggestuurde tellerwaarde weer
// uit urlCompteur wordt de informatie gehaald die nodig is voor de verbinding met de belastingserver
String path=urlCompteur.getPath();
if(path.equals("")) path="/";
String host=urlCompteur.getHost();
int port=urlCompteur.getPort();
if(port==-1) port=urlCompteur.getDefaultPort();
// er worden aanroepen gedaan naar URL
Socket[] clients=new Socket[nbAppels];
for(int i=0;i<nbAppels;i++){
// we maken verbinding met de server
clients[i]=new Socket(host,port);
// er wordt een schrijfstroom naar de server aangemaakt
PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
// URL wordt opgevraagd – verzending van de headers HTTP
OUT.println("GET " + path + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println("");
}//voor
// lokale gegevens
String réponse=null; // antwoord van de server
// het gezochte patroon in het antwoord HTML van de server
Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
// het patroon van een correct antwoord
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
// het resultaat van de vergelijking met het patroon
Matcher résultat=null;
for(int i=0;i<nbAppels;i++){
// elke client leest het antwoord dat de server hem stuurt
// de invoer- en uitvoerstromen van de client worden aangemaakt TCP
BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));
// de eerste regel van het antwoord wordt gelezen
réponse=IN.readLine();
// de regel HTTP wordt vergeleken met het sjabloon van het juiste antwoord
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// er is een probleem met URL
throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
}//if
// lezen we het antwoord tot het einde van de headers
while((réponse=IN.readLine())!=null && ! réponse.equals("")){
}//terwijl
// de headers zijn klaar HTTP - we gaan verder met de code HTML
// om de waarde van de teller op te halen
boolean compteurTrouvé=false;
while((réponse=IN.readLine())!=null){
// we vergelijken de regel met het tellersjabloon
if(! compteurTrouvé){
résultat=modèleCompteur.matcher(réponse);
if(résultat.find()){
// teller gevonden
System.out.println("Compteur="+résultat.group(1));
compteurTrouvé=true;
}//if
}//if
}//while
// klaar
clients[i].close();
}//for
}//getCompteurs
}//klasse
Laten we de bovenstaande code toelichten:
- het programma accepteert twee parameters:
- de URL van de pagina JSP van de teller
- het aantal klanten dat voor deze URL moet worden aangemaakt
- het programma controleert dus eerst of de parameters geldig zijn: of er inderdaad twee zijn, of de eerste qua syntaxis lijkt op een URL en of de tweede een geheel getal >0 is. Om te controleren of de URL syntactisch correct is, gebruiken we de klasse URL en de bijbehorende constructor URL (String), die een object URL aanmaakt op basis van een tekenreeks zoals http://istia.univ-angers.fr. Er wordt een uitzondering gegenereerd als de tekenreeks geen syntactisch geldige URL is. Zo kunnen we de geldigheid van de eerste parameter controleren.
- Zodra de parameters zijn gecontroleerd, wordt de controle overgedragen aan de procedure getCompteurs. Deze procedure maakt nbAppels-clients aan die allemaal tegelijkertijd (of bijna tegelijkertijd) verbinding maken met de URL urlCompteur.
- De poort en de machine waarmee de clients verbinding moeten maken, worden ontleend aan de URL en urlCompteur: [URL].Met getHost() wordt de naam van de machine opgehaald en met [URL].getPort() wordt de poort opgehaald.
- Met een eerste lus kan elke client:
- verbinding te maken met de webserver
- de URL urlCompteur op te vragen
In deze lus wacht de client niet op het antwoord van de server. Het is namelijk de bedoeling dat de server vrijwel gelijktijdige verzoeken ontvangt.
- Een tweede lus stelt elke client in staat om het antwoord dat de server hem stuurt te ontvangen en te verwerken. De verwerking bestaat erin om in het antwoord de regel te vinden die de waarde van de teller bevat en deze weer te geven.
Om het eerder gesignaleerde probleem op te lossen (dezelfde tellerstand naar alle vijf clients verzonden), moeten we de threads van de teldienst op één en hetzelfde object synchroniseren voordat we het kritieke gedeelte voor het lezen en bijwerken van de tellerstand betreden. De nieuwe pagina JSP ziet er als volgt uit:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// globale variabelen en methoden van de pagina JSP
// instantievariabelen
int compteur;
Object verrou=new Object();
// methode om de teller te verhogen
public int getCompteur(){
// de kritieke sectie wordt gesynchroniseerd
synchronized(verrou){
// de teller wordt uitgelezen
int myCompteur=compteur;
// er wordt 10 seconden gepauzeerd
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// de teller wordt verhoogd
compteur=myCompteur+1;
}//gesynchroniseerd
// teruggezet
return compteur;
}//getCompteur
// de methode die wordt uitgevoerd bij het eerste laden van de pagina
public void jspInit(){
// teller initialiseren
compteur=100;
}
%>
Bij uitvoering krijgt men dan de volgende resultaten:
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 de documentatie staat dat de webserver soms meerdere instanties van dezelfde servlet kan aanmaken. In dat geval werkt de bovenstaande synchronisatie niet meer, omdat de variabele verrou lokaal is voor één instantie en dus niet bekend is bij de andere instanties. Hetzelfde geldt voor de variabele compteur. Om ze voor alle instanties globaal te maken, schrijven we:
// klassevariabele
static int compteur;
static Object verrou=new Object();
De rest van de code blijft ongewijzigd.





