![]() |
|
|||||
18.1.3 Das erste Servlet
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Methode | Beschreibung |
| GET | Fordert die Web-Seite |
| HEAD | Fordert lediglich den Header einer Web-Seite |
| PUT | Anfrage, eine Web-Seite zu speichern |
| POST | Anhang zu einer benannten Ressource (z. B. eine Web-Seite) |
| DELETE | Fordert zum Löschen einer Web-Seite auf |
| TRACE | Der Client empfängt seine gesendeten Informationen zurück |
Die GET-Anfrage ist die häufigste Methode. Da sie oft mit POST verwechselt wird, wollen wir den Unterschied noch einmal klarstellen.
Anfrage für eine normale Web-Seite, die etwa durch Verfolgen eines Links oder Eintrags in der Browserzeile für die URL gestartet wird. GET wird auch manchmal verwendet, wenn Daten über Formulare an den Server geschickt werden. In diesem Fall werden die Daten URL-kodiert an die URL angehängt. Die so übertragenen Daten können jedoch nicht beliebig lang sein und sind durch das System begrenzt; die Maximalgröße liegt bei etwa 2 Kilobyte. Das schränkt GET-Anfragen für Parameter ein. Herkömmliche CGI-Programme kommen an die Daten über eine Umgebungsvariable. Genau diese sind längenbeschränkt und lassen Daten nicht unendlich groß werden.
Beispiel Eine abgeschickte URL
http://server/servlet?parameter1=wert1¶meter2=wert2 |
Auch POST überträgt Daten an den Server. Doch POST wird vom Browser nur für Formulare innerhalb von <FORM> mit der Anweisung METHOD="POST" verwendet. Die Daten werden nicht einfach an die URL angehängt, sondern in einem Dateistrom übermittelt. Zuerst wird dazu der HTTP-Server kontaktiert und mit Header-Informationen versorgt. Zum Beispiel steht die Länge der Daten im Request-Header Content-length. Danach werden die Daten gesendet, die CGI-Programmen in einem Dateistrom zur Verfügung stehen. Im Gegensatz zu GET gibt es daher bei POST keine Begrenzung des Informationsumfangs. Diese Methode wird auch meistens zur Übermittlung von Login-Daten verwendet, da bei der GET-Methode das Passwort »unkodiert« in der Browserzeile zu sehen wäre.
Die anderen Methoden werden nicht so häufig eingesetzt. Die HEAD-Methode fragt nach den Headern einer Web-Seite, ohne die Datei selbst zu übertragen. So lässt sich zum Beispiel das Datum der Änderung beziehen und dann feststellen, ob die Datei schon im Cache ist. Aus Sicherheitsgründen werden bei den meisten am Markt angebotenen Servern die Anfragemethoden PUT und DELETE nicht angeboten, da mit ihnen direkt auf den Server geschrieben werden kann.
Beantwortet ein Webserver eine Clientanfrage (so genanntes Response), so schickt er das Ergebnis in einer ganz besonderen Form zurück. Diese ist wieder ASCII-basiert und erinnert an die Anfrage:
HTTP/1.1 200 OK
Content-Type: text/plain
Mein erstes Textdokument.
Diese Ausgabe besteht aus einer Statuszeile, einem oder mehreren Antwort-Headern, einer Leerzeile und dem Dokument selbst. Die Statuszeile gliedert sich in drei Bereiche. Als Erstes zeigt die Information dem Browser die Version des HTTP-Protokolls an. Dann folgt eine Ganzzahl, die den Statuscode kodiert. Dieser ist in der Headerbeschreibung der RFC bei http://www.w3.org/Protocols beschrieben. Dann folgt eine kurze Textanzeige, die den Statuscode menschenlesbar macht. Einer der wohl am besten bekannten Statuscodes ist 404. Dieser zeigt an, dass die angeforderte Datei auf dem Server nicht existiert.
Das Servlet erzeugt automatisch die erste und zweite Zeile. Auf diese können wir Einfluss nehmen. Um ordentliche HTML-Seiten zu erstellen, wollen wir zuerst den Content-Type ändern. Danach lernen wir weitere Header kennen und unterschiedliche Statuscodes. Wir werden zudem sehen, dass unterschiedliche Header oft andere Statuscodes bedingen.
Wenn eine Seite vom Server zum Browser geschickt wird, dann erkennt der Anfrager wie von Zauberhand den Typ der verschickten Datei und kann sie passend anzeigen. Woher weiß also der Browser, dass eine HTML-Datei anders darzustellen ist als eine Grafik oder eine Word-Datei mit der Endung .doc, für das ein externes Anzeigeprogramm gestartet werden muss? Der Hinweis auf den Datentyp kommt vom Server mit einer Mitteilung, die sich MIME nennt. MIME ist ursprünglich für E-Mails entwickelt worden, um binäre Dateien zu versenden, doch MIME hat für das Web mittlerweile eine größere Bedeutung bekommen. MIME geht in Bezug auf elektronische Nachrichten durch E-Mails noch etwas weiter, als es im Protokoll HTTP genutzt wird, denn bei der Web-Kommunikation werden lediglich einige zentrale Teile benutzt.
Möchte der Server dem Client den passenden MIME-Type senden, muss er erst einmal herausfinden, was die Datei für einen Typ besitzt. Dazu kann er zwei Taktiken verfolgen. Er kann in die Datei hineinschauen, die ersten Bytes untersuchen und seine Prognose abgeben oder einfach über die Dateiendung gehen. Die Lösung der Dateiendung wird sehr oft bei den Implementierungen von Webservern angewendet. Wenn wir Servlets programmieren, dann spielen wir selbst Server und müssen den MIME-Typ daher selbst angeben.
Wenn die Datei zum Client kommt, geht das Spielchen »wer-mit-wem« weiter, denn der Client muss nun aufgrund des MIME-Typs das passende Anzeigeprogramm finden. In der Regel kann der Browser die Daten direkt anzeigen, wenn es etwa eine HTML-Seite, Textseite oder Grafik ist. Sonst greift meistens ein ActiveX-Control (Microsoft) oder ein Plugin (Netscape) ein. Wenn er diesen MIME-Typ nicht mitgeschickt bekommt (was bei Servlets allerdings nicht passieren kann, da hier bei fehlender Programmierangabe immer ein Standardwert mitgeschickt wird) muss er genauso raten wie der Server.1 Doch bleiben wir bei dem Servlet-Fall, dass der Typ immer mitgeschickt wird. Wie sieht dieser dann aus?
Jeder MIME-Typ besteht aus zwei Hälften. Einer Kategorie und einem Datentyp. Wichtige Kategorien sind text, audio, image, video, application. Wir wollen einige Beispiele aufführen:
| text/html Dies ist der Standardtyp für Web-Seite. Ein Servlet, welches HTML-Seiten generiert, sollte diesen Typ immer setzen. Somit kann der Browser sofort HTML interpretieren und die Seite aufbauen. |
| text/plain Hierbei handelt es sich um eine Textdatei, die nicht vom Browser interpretiert wird. Auch wenn der Text ein HTML-Text ist, baut der Server daraus keine HTML-Seite auf. Typischerweise verbindet der Server normale Textdateien mit dem Typ plain. |
| image Die Datentypen für Grafikformate. Beispiel: image/gif, image/jpeg für GIF und JPEG. |
| audio Sound-Dateien, zum Beispiel audio/basic oder audio/x-wav. |
| video Darstellbare Bewegbilder wie video/mgep oder video/quicktime. |
| application Dazu zählen in der Regel Binärdateien, die von anderen Programmen gelesen oder ausgeführt werden, etwa application/pdf für eine PDF-Datei, application/msword für ein Microsoft Word Dokument oder application/vnd.ms-excel für ein Excel Dokument. |
| multipart Anzeige, das der Datenstrom unterschiedliche Teile beinhaltet. Oft bei E-Mails, die einen Text und einen Datenanteil für den Anhang besitzen. Beispiel: multipart/mixed, multipart/news. |
Mittlerweile gibt es unzählige MIME-Typen und es ist nicht sicher, dass diese immer vom Browser unterstützt werden. Im Zweifelsfall hilft nur ein Blick in die Zuordnungstabelle des Browser. Unter ftp://ftp.isi.edu/in-notes/iana/assignments/media-types lässt sich in Erfahrung bringen, wo welcher MIME-Typ genau definiert ist.
Um Servlets entwickeln und testen zu können, benötigen wir einen Servlet-fähigen Webserver, beziehungsweise einen Servlet-Container. Mittlerweile gibt es eine große Anzahl von Herstellern, deren Server Servlets verwalten.
Servlets und Applets sind konzeptionell ähnlich. Daher kann ein Vergleich gewagt werden: Applets werden vom Web-Browser geladen und gestartet. Den Browser können wir dabei als Container für Applets sehen, der eine Infrastruktur wie die virtuelle Maschine oder Netzwerkeigenschaften bereitstellt. Innerhalb einer Java-Umgebung im Browser können durchaus mehrere Applets parallel eingebunden sein. Genauso verhält es sich mit Servlets. Auch hier benötigen wir einen Container, der alle Servlets verwaltet. Dieser kann entweder in einem Webserver eingebettet sein oder in einen Applikationsserver. Der Container leitet dann Anfragen an das Servlet weiter. Neben der Kommunikation nach außen, verwaltet der Container den Lebenszyklus eines Servlets, genau wie ein Browser darüber wacht, ob das Applet gerade sichtbar ist oder nicht. Bei Servlets sieht ein solcher Vorgang wie folgt aus: Ein Client richtet eine HTTP-Anfrage an den Webserver. Dieser bemerkt, dass es sich um ein Servlet handelt und gibt die Anfrage an den Container weiter. Dieser wiederum verwaltet alle Servlets und spricht genau das Servlet an, das der Benutzer nutzen wollte und übergibt Datenströme zur Ein- und Ausgabe. Das Servlet liest über den Eingabekanal optional Formularinhalte und generiert über den Ausgabestrom eine HTML-Seite, die der Container an den Client weiterreicht.
Sun verzeichnet auf ihrer Web-Seite http://java.sun.com/products/servlet/industry.html eine Liste von servletfähigen Servern. Ein Server ist genau dann servletfähig, wenn er die Java Servlet- und Java Server Pages (JSP)-Spezifikation erfüllt. Die Spezifikation in der Version 2.2 ist vom 17. Dez. 1999. Die Servlets-Komponente kann dabei integraler Bestandteil des Servers sein oder auch ein Zusatz, der nachträglich zu installieren ist. Hier eine Liste der wichtigsten Server:
Apache Tomcat
| Tomcat ist die offizielle Referenzimplementierung. Zum Testen von Servlets und JSP kann Tomcat sowohl als Stand-Alone-Applikation eingesetzt oder als Ergänzung zum Apache-Server eingebunden werden. Tomcat ist ebenso wie Apache frei. Er sitzt allerdings nicht unter der Webadresse tomcat.com, sondern unter http://jakarta.apache.org.2 |
| Java Server Web Development Kit (JSWDK) Das JSWDK ist von Sun und die erste Servlet-Implementierung. Sie ist ebenso wie Tomcat frei verfügbar und unter http://java.sun.com/products/servlet/download.html erreichbar. Sie wird von Sun nicht mehr weiterentwickelt, da nun die Entwicklung in Tomcat weiterläuft. |
| Allaire JRun JRun kann als Plugin in den Enterprise/FastTrack von Netscape, Microsoft IIS, Personal Web Server und weiteren weniger verbreiteten Servern eingesetzt werden. Die Software ist kommerziell, eine eingeschränkte Version mit maximal fünf offenen Verbindungen ist frei. Mehr Informationen gibt es unter http://www.allaire.com/products/jrun. |
| New Atlanta’s ServletExec ServletExec kann als Zusatz in die meisten Webserver unter Solaris, Windows, MacOS, HP-UX und Linux eingebunden werden. Die Nutzung ist frei, Administrationstools sind jedoch kostenpflichtig. Die Firma New Atlanta hat ebenfalls einen freien Servlet-Debugger im Angebot, der sich in die meisten IDEs einklinken lässt. http://newatlanta.com. |
Der Webserver von Sun (Sun’s Java Web Server), eine Implementierung komplett in Java, war der erste Servlet-Server. Mittlerweile hat Sun die Entwicklung eingestellt. Die Entwicklung wird unter dem Netscape/I-Planet-Server fortgesetzt. Weitere Informationen, die Sun zu Servlets bietet, findet sich unter http:/ /java.sun.com/products/servlet/resources.html.
Der Ursprung für dynamische Web-Seiten geht auf die Zeit zurück, als das JDK noch unter 1.0 ausgeliefert wurde. Sun nannte das Paket mit den Klassen und Programmen für die Servlet-Umgebung anfangs Java Servlet Development Kit (JSDK). Das Paket wurde bis zur Version 2.1 gepflegt. Da allerdings das alte JDK (Java Development Kit) in »Java 2 Software Development Kit« (J2SDK) umgenannt wurde, bestand eine Verwechselungsgefahr mit dem JSDK, weshalb Sun das JSDK in »Java Server Web Development Kit« (JSWDK) umbenannte. Zusätzlich nahmen die Entwickler noch Java Server Pages auf. Das JSWDK begann in der Nummerierung wieder bei 1.0.
Nach langer interner Entwicklung hat Sun den Quellcode für das JSWDK der Apache-Gruppe übergeben, sodass die Implementierung jetzt Open-Source ist. Die Apache-Gruppe entwickelte das JSWDK weiter und nannte es fortan »Tomcat«, das nun damit die einzig gültige Referenzimplementierung bildet. Weitere Freigaben werden offen sein und stehen unter dem Java Community Process. Damit endet die Produktlinie und das JSDK und JSWDK kommen ins Archiv. Der Name »Tomcat« wurde von Apache gewählt, da in frühen Entwicklungstagen »Tomcat« der interne Name für die Servlets bei Sun war. Erschwerend im Versions-Wirrwarr ist, dass die Spezifikation für Servlets und JSP unterschiedlichere Versionen als JSDK, JSWDK und jetzt Tomcat haben.
Jeder Server besitzt natürlich unterschiedliche Vorgehensweise bei der Installation und beim Einbinden von Servlets und Verzeichnissen. Wir halten uns hier an die Implementierung von Tomcat in der Version 4.
Der Tomcat-Sever liegt unter http://jakarta.apache.org/tomcat als komprimiertes Archiv zum Laden bereit. Nach dem Auspacken liegen im Pfad jakarta-tomcat unterschiedliche Verzeichnisse, wobei wir unter bin/ die ausführbaren Skripte zum Verwalten des Servers finden. Diese liegen einmal als Batch-Datei für Windows vor und als Shell-Skript für Unix. Wir werden wie üblich in der Windows-Welt bleiben.
Damit der Server gestartet werden kann, muss die Umgebungsvariable JAVA_HOME (nicht CLASSPATH) so gesetzt sein, dass sie auf das JDK zeigt. Am einfachsten modifizieren wir dazu die Batch-Datei catalina.bat (oder tomcat.sh für Unix) und tragen dort in den ersten Zeilen das Verzeichnis für die JVM ein. Ein Ausschnitt:
…
rem
rem $Id: catalina.bat,v 1.14 2001/01/03 19:55:45 craigmcc Exp $
rem ---------------------------------------------------------
set JAVA_HOME=d:\programme\jdk1.3
rem --- Save Environment Variables That May Change ----------
set _CP=%CP%
set _TOMCAT_HOME=%TOMCAT_HOME%
Die Datei catalina.bat ist als eine Art Oberprogramm zu sehen, welches andere Hilfsprogramme aufruft. Befinden wir uns im Installationsverzeichnis, können wir nach dem Anpassen des Pfades den Server starten:
F:\jakarta-tomcat-4.0-b3>bin\catalina run
Using CLASSPATH: .\bin\bootstrap.jar;e:\jdk1.3\lib\tools.jar
Starting service Tomcat-Standalone
Apache Tomcat/4.0-b3
Starting service Tomcat-Apache
| Hinweis Kommt unter Windows 98/Me die Fehlermeldung »Kein Speicherplatz mehr im Umgebungsbereich«, sollte bei den Dateien im Kontexmenü Eigenschaften unter Speicher und Ursprünglicher Umgebungsspeicher zum Beispiel der Wert 4096 eingetragen werden. |
Ohne Veränderung der Voreinstellungen installiert sich der Webserver auf dem lokalen Rechner auf Port 8080. Ist für die Ausgabe ein eigenes Fenster im Hintergrund gewünscht, so macht dies die Option start anstatt run möglich. Ohne Parameter zeigt Tomcat die restlichen Optionen an. Der Aufruf bin\startup aktiviert direkt den Server ohne Parameter.
Im Unterverzeichnis conf liegen diverse XML-Dateien zur Konfiguration. Hier lässt sich beispielsweise der Port anpassen oder das Verzeichnis, in dem die Servlets bzw. JSP-Seiten gesucht werden.
Ein Blick im Browser auf die lokale Adresse http://localhost:8080 zeigt uns die Startseite. Hier finden sich Beispiele und die APIs für das Paket.
Nachdem wir uns über die schönen Servlets gefreut haben, wollen wir natürlich auch eigene programmieren. Dazu müssen wir lediglich den CLASSPATH für den Compiler anpassen, sodass dieser auf die beiden Jar-Archive servlet.jar und jsp.jar zeigt. Die Archive liegen bei Tomcat im Ordner lib.3 Wenn die Enterprise Version von Java (J2EE) installiert ist, sind die beiden Archive schon im Pfad eingebunden und wir müssen servlet.jar nun nicht mehr extra einbinden.
| Tipp Der Download für eine neue Version lohnt sich dennoch, denn die Servlet-API und -Implementierung ändert sich häufig. |
import java.io.*;
import javax.servlet.*;
public class FirstServlet extends GenericServlet
{
public void service( ServletRequest request,
ServletResponse response )
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
out.println ( "'Chr! Schnarch! Razong! Chr! Chr! Rapüh!'"+
" (Disneys beste Comics, Band 5, S. 218)");
}
Alle Servlets implementieren die Schnittstelle javax.servlet.Servlet. Die Klasse GenericServlet implementiert diese Methoden und stellt uns eine einfache Klasse bereit, von der wir erben können. Verlangt der Client vom Webserver ein Servlet, so bildet der Servlet-Container ein Exemplar der Servlet-Klasse (in unserem Fall FirstServlet) und ruft nach der Initialisierung auf dem Objekt die Methode service() auf. Sie nimmt zwei Argumente an: ein ServletRequest und ein ServletResponse-Objekt.
Über request lassen sich alle Anfrageparameter erfragen. In der Unterklasse HTTPServletRequest kommen zusätzlich Formular-Werte oder ein HTTP-Anfrage-Typ dazu.
Mit response schicken wir die Daten, die zum Webclient zurückgehen, wie HTTP-Response (200, 404), Antwort-Header (Content-Type, Set-Cookie) zurück. Aber am Wichtigsten ist die Methode getWriter(), die uns eine Referenz auf ein Writer-Objekt liefert, damit wir die HTML-Elemente für die Seite abschicken. (Vorsicht! Ein System vor dem out ist diesmal nicht nötig – dieser Fehler schleicht sich schon mal ein.) Für Binärdaten können wir uns auch einen normalen OutputStream besorgen, damit wir zum Beispiel Bilder schicken können.
| Hinweis So gut wie alle Servlets erweitern die Unterklasse HttpServlet von GenericServlet Ein HttpServlet enthält wichtige Arbeitsfunktionen für das Protokoll HTTP. Eine Erweiterung von GenericServlet ist eher unüblich, es sei denn, nicht-HTTP Protokolle wie FTP werden angeboten. |
|
Mit diesen Informationen lässt sich das Servlet zwar compilieren, aber wir brauchen noch einen Ort, an dem es abgelegt wird. Damit wir nicht erst lange in den Konfigurations-dateien editieren müssen, legen wir unser FirstServlet.class in das Verzeichnis, in dem auch die anderen Testservlets von Tomcat liegen. Sie sind dort mit Quellcode abgelegt und laden direkt zum Anschauen ein. Das Verzeichnis ist
<jakarta-tomcat-Pfad>\webapps\examples\WEB-INF\classes
Um nun den Server zur Ausführung unseres FirstServlets zu bewegen, machen wir die Pfad-Angabe nicht in der URL, sondern wir schreiben
http://localhost:8080/examples/servlet/FirstServlet
Diese Übersetzung von URL-Anfrage in die entsprechende Klasse nennt sich Servlet-Mapping und wird vom Servlet-Container übernommen. Nach der Servlet-Spezifikation lautet die URL pfad/servlet/klassenname. Der Server in der Testinstallation nimmt nur die Angabe examples zur Unterscheidung hinzu. Wo nun die Klassendateien wirklich liegen, ist ganz unterschiedlich. Das definiert eine Konfigurationsdatei. Die Server können dieses Mapping unterschiedlich vornehmen.
Wenn wir das Servlet ändern und neu compilieren, dann reicht es aus, den Inhalt im Browser zu aktualisieren (F5 im Internet Explorer (IE)). Ein vernünftiger Container sollte dann die Klasse automatisch neu laden. Was aber auf den ersten Blick so einfach aussieht, ist beim genaueren Hinsehen schon etwas komplizierter. Denn es ist nicht damit getan, nur Veränderungen zu Überwachen. Das Problem ist, dass der Sevlet-Container die geladen Klassen wieder freigeben und neu laden muss.
Wer das automatische Neuladen vermeiden möchte, der muss bei Tomcat in der Konfigurationdatei conf/server.xml den Eintrag reloadable="true" auf false setzen. Dadurch ergibt sich ein Geschwindigkeitsvorteil, da nicht vor jedem Starten geprüft werden muss, ob sich das Servlet geändert hat.
Der Container für Servlets registriert eine Anfrage durch den Client und lädt das Servlet in den Speicher. Da Servlets normale Klassen sind, übernimmt ein spezieller Klassenlader diese Aufgabe. Die Abarbeitung findet anschließend in einem Thread statt, der die Methoden des Servlet-Objekts aufruft.
Wir wollen nun verfolgen, wie der Container die Arbeit an das Servlet delegiert. Über die Schnittstelle Servlet werden drei elementare Methoden für Initialisierung, Abarbeitung der Anfragen und Beendigung vorgeschrieben. Der Ablauf dieser Methoden nennt sich Lebenszyklus eines Servlets.
Die folgende Aufzählung zeigt alle Methoden, die die Schnittstelle Servlet für alle Java-Servlets vorschreibt.
interface javax.servlet.Servlet |
| void init( ServletConfig config ) Wird zu Anfang eines Dienstes aufgerufen. |
| void service( ServletRequest req, ServletResponse res ) Der Container leitetet die Anfrage an das Servlet an diese Stelle. |
| void destroy() Wird am Ende eines Servlets vom Container genau einmal aufgerufen. |
| ServletConfig getServletConfig() Liefert ein ServletConfig-Objekt, welches Initialisierungs- und Startparameter kapselt. |
| String getServletInfo() Liefert Informationen über das Servlet wie Autor, Version und Copyright. |
Beispiel Ein Servlet implementiert getServletInfo(), um Informationen an den Servlet-Container zu geben.
public class FirstServlet extends GenericServlet { public void service( ServletRequest request, ServletResponse response ) { } public String getServletInfo() { return "Ich bin der erste Erguss seiner Servlet-Fähigkeiten"; } |
Nachdem der Klassenlader genau ein Exemplar der Servlet-Klasse geladen hat, ruft der Container die init()-Methode des Servlets auf. Die Servletspezifikation macht allerdings keine Aussage darüber, wann init() ausgeführt wird. Dies kann der Fall sein, wenn das Servlet geladen wird, aber noch keine Anfrage ansteht, wenn die erste Anfrage kommt oder aufgrund einer globalen Initialisierung. In init() kann das Servlet sich initialisieren und beispielsweise Netzwerk- oder Datenbankverbindungen aufbauen.
Neben init() existiert init(ServletConfig) mit einem Konfigurationsobjekt, vom Typ der Schnittstelle ServletConfig. Die wichtigste Methode ist getServletContext(), die ein ServletContext-Objekt liefert, welches sich zum Beispiel zur Pfadumsetzung eignet. Wenn wir diese parametrisierte Methode überschreiben, dann dürfen wir es nicht versäumen, super() mit dem Parameter ServletConfig aufzurufen und es somit weiterzuleiten:
public void init( ServletConfig config ) throws ServletException
{
super.init( config );
...
Die init()-Methode in der Oberklasse über super() anzusprechen, ist die einzige Möglichkeit, die uns Zugang zum Konfigurationsobjekt verschafft. Der Container rückt es nur an dieser Stelle heraus. Daher bleibt uns nichts anders übrig, als entweder init(ServletConfig) mit super() zu verwenden oder init() ohne Parameter zu verwenden, um das ServletConfig-Objekt zu bekommen.
Falls wir das ServletConfig-Objekt in init(ServletConfig) nicht benötigen, ist es einfacher, nur init() zu überschreiben. Das sieht dann in der Klasse GenericServlet (davon erbt HttpServlet) so aus:
public void init(ServletConfig config) throws ServletException {
this.config = config;
log("init");
this.init();
Der Container ruft eigentlich nur init(ServletConfig) auf, und wie wir sehen, ruft die Methode dann selbstständig init() auf. Eine Referenz auf das Konfigurations-Objekt wird in GenericServlet gespeichert, auf die über getServletConfig() zugegriffen werden kann. Die Referenz-Variable ist private, so wie es sich gehört.
Während externe CGI-Programm beispielsweise Datenbankverbindungen für jede Anfrage neu aufbauen müssen, muss ein Servlet dies nicht. (Dadurch ergibt sich natürlich ein satter Geschwindigkeitsvorteil.) Dies funktioniert aber nur deswegen, da für jede Servlet-Instanz in dessen Lebenszyklus nur einmal die init()-Methode aufgerufen wird. Alle Anfragen werden in einem eigenen Thread behandelt und dieser ruft die Methode service() auf. init() muss nicht threadsicher sein. service() kann von mehreren Threads parallel augerufen werden und threadsicher sein.
| Hinweis Obwohl in der Regel der Servlet-Server mehrere Threads initialisiert, die auf die init()-Methode zugreifen, kann die init()-Methode mehrfach aufgerufen werden, um mehrere Exemplare einer Servlet-Klasse zu erzeugen. Dann ist es ungünstig, Ressourcen wie Datenbankverbindungen immer neu anzufordern, obwohl vielleicht schon eine andere init()-Methode dies gemacht hat. Da es mehrere Exemplare der Servlet-Klasse im Servlet-Container geben kann, sollten statische Initialisierungen nicht in der init()-Methode durchgeführt werden. |
Beispiel Wenn ein Servlet erwacht, wollen wir eine fiktive Datenbankverbindung für alle Servlets dieser Klasse aufbauen.
public class DBServlet extends GenericServlet { static DBConnection con; public void init() { if ( con == null ) con = DB.getConnection(); } public void service( ServletRequest request, ServletResponse response ) throws ServletException, IOException { ... } |
Besser gelöst ist die Implementierung mit einer Fabrik-Methode seitens der Datenbank. Dann kümmert sie sich darum, dass es nur ein Exemplar gibt. Die Referenz wird dann ebenfalls von der Datenbank und nicht vom Servlet gespeichert.
In unserem ersten Beispiel haben wir uns der Klasse GenericServlet bedient, um eine service()-Methode zu überschreiben, die auf beliebige Anfragen des Clients antwortet. In der Regel wollen wir aber bei unterschiedlichen Anfragen unterschiedlich reagieren. Eine Anfrage ist zum Beispiel GET. Diese Form wird dann benutzt, wenn im Browser vom Benutzer eine URL eingetragen oder ein Verweis verfolgt wird. Neben GET-Anfragen gibt es noch POST-Anfragen, die für Formulare Verwendung finden.
Damit wir auf GET-Anfragen anders reagieren können als auf POST-Anfagen, sind zwei Möglichkeiten denkbar:Entweder können wir die service()-Methode überschreiben, den Typ herausfinden und dann die Anfrage behandeln, oder wir verwenden eine andere Klasse, nämlich HttpServlet. Sie implementiert service() so, dass Anfragen an Methoden wie doGet(), doPost() und entsprechende Methoden weitergeleitet werden. Bei eigenen Anfragen wollen wir im Folgenden immer HttpServlet erweitern und service() nicht mehr direkt verwenden, sondern die entsprechenden doXXX()-Methoden.
|
Die service()-Methode der Klasse HttpServlet erfragt die verwendete Methode (GET oder POST) mit der Dienstmethode getMethod() vom aktuellen Request. Kurz skizziert hat sie folgendes Format:
protected void service( HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod ();
if (method.equals(METHOD_GET)) {
...
} else if (method.equals (METHOD_HEAD)) {
...
} else if (method.equals (METHOD_POST)) {
doPost (req, resp);
} else {
...
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,
errMsg);