Wilhelm-Schickard-Institut

Arbeitsbereich Technische Informatik

WS 04/05

    Blatt 3: Servlets
    Abgabetermin 7.12.04

Eine allgemeine (nicht technische) Einführung über Servlets findet Ihr hier.


Zweck der beiden unten vorgestellten Aufgaben ist, daß Ihr erste Erfahrungen mit der Programmierung von Servlets sammelt. In den folgenden Paragraphen findet Ihr das nötige technische Hintergrundwissen für die Programmierung der Aufgaben:

Falls Ihr noch mehr Informationen über Servlet-Programmierung benötigt, ist folgendes Dokument sehr zu empfehlen:

WebSphere Application Server Guide, v. 1.0 (PDF) - Kapitel 3, Servlet Programmierung

und noch weiter Links auf Dokumentation:

Die Aufgaben werden betreut von: Carolin Lacher und Daniel Raible
Wenn Ihr Fragen habt, meldet Euch einfach per eMail oder wärend der Anwesenheitszeit.


Was sind Servlets?

Java Servlets sind spezielle Java-Programme, deren Zweck die Erweiterung der Funktionalität eines Servers ist. Diese Programme laufen serverseitig, auf einer Java Virtual Machine (JVM) auf dem Server. Der Client (Webbrowser) braucht Java nicht zu unterstützen. Eine populäre Beispielanwendung für Servlets ist die Anbindung eines Servers an eine Datenbank.

Zur Kommunikation zwischen Servlet und Server wurde eine plattformunabhängige Schnittstelle definiert, die Java Servlet API. Ein einmal programmiertes Servlet kann also prinzipiell auf jedem Server laufen, der diese API unterstützt.

Die Java Servlet API besteht aus folgenden packages:

  • javax.servlet: Enthält Klassen für die Implementierung von generischen (protokollunabhängigen) Servlets.
  • javax.servlet.http: Enthält Klassen für die Implementierung von Servlets, welche das HTTP-Protokoll benutzen (HttpServlets).


HttpServlets

Kurzeinführung in das HTTP-Protokoll: Das HTTP-Protokoll ist ein einfaches, verbindungsloses Protokoll. Verbindungslos bedeutet, daß zwischen Client (Webbrowser) und Server keine permanente, sondern nur eine temporäre Verbindung besteht.

Wenn der Client etwas vom Server will, schickt er eine sogenannte request an den Server. Eine request enthält einen HTTP-Befehl, die sog. method, welche angibt, was der Server tun soll. Nachdem der Server den Befehl verarbeitet hat, antwortet er mit einer response.

Die meistbenutzten HTTP-Befehle sind die Kommandos GET und POST. Etwas einfach formuliert dient das GET Kommando dazu, Daten vom Server zu bekommen, während POST benutzt wird, um Daten an den Server zu senden.

HttpServlets erstellen: Ein HttpServlet erhält man, indem man die Klasse HttpServlet aus dem package javax.servlet.http erweitert. Ein HttpServlet erhält requests von einem Webserver (s. Grafik unten) und schickt responses zurück. Die wichtigsten Methoden dieser Klasse sind:

  • doGet(HttpServletRequest, HttpServletResponse): Verarbeiten von GET requests.
  • doPost(HttpServletRequest, HttpServletResponse): Verarbeiten von POST requests (also requests, die Daten an das Servlet senden).
  • init(ServletConfig): Wird einmal aufgerufen, wenn das Servlet vom Server geladen wird. Hier kann man Befehle zur Initialisierung des Servlets plazieren (z.B. Daten aus einer Datenbank holen, die sich während der Laufzeit nicht ändern).
  • destroy(): Wird einmal aufgerufen, wenn das Servlet vom Server aus dem Speicher entfernt wird.

Hinweis: In den Aufgaben werdet Ihr nur doGet() bzw. doPost() benötigen.

Wichtig ist zu wissen, daß jedes Servlet-Objekt vom Server nur einmal erzeugt (instanziert) wird, und danach mehrere Threads des Servers (eins pro request) u.U. gleichzeitig auf dieses Objekt zugreifen. Falls also member-Variablen (Klassenvariablen) des Servlets verändert werden, ist es notwendig, die jeweiligen Methoden als synchronized zu deklarieren, um die Konsistenz der Daten zu gewährleisten. Lokale Variablen, die nur innerhalb von doGet() bzw. doPost() Methoden existieren, sind davon natürlich nicht betroffen.

HTTP-Servlet handling GET and POST requests


Soviel zu Servlets im allgemeinen. Ziel der zweiten Aufgabe auf diesem Blatt ist es, das HttpSession Objekt kennenzulernen und ein Servlet zu schreiben, welches mit einem (fertigen) Applet kommuniziert.



Die HttpSession

Die HttpSession ist eines der interessantesten Objekte der Servlet API. Mit Hilfe dieses Objekts ist es möglich, Daten einem unbekannten Benutzer eindeutig zuzuordnen und über mehrere Schritte zu bearbeiten (Anwendungsbeispiel: Warenkorb). Dazu wird dem Benutzer ein Cookie mit der ID seiner Session geschickt. Die Daten der HttpSession bleiben jedoch auf dem Server. Wenn der Benutzer sich wieder meldet, wird über den Wert des Cookies auf die richtige Session zugegriffen.

All das passiert automatisch und transparent. Als Programmierer muß man sich lediglich um das holen bzw. erzeugen eines Session-Objekts kümmern, sowie um das Schreiben und Lesen der dort gespeicherten Werte. Dies macht das Benutzen von Sessions sehr einfach und attraktiv. Nützliche Methoden in diesem Zusammenhang sind die getSession(boolean) Methode der HttpServletRequest und die verschiedenen Methoden des HttpSession Objekts.



Infos zur Applet <-> Servlet Kommunikation

HttpServlets können nicht nur mit Webbrowsern kommunizieren, sondern auch mit Java Applets, welche im Webbrowser laufen. Zu diesem Zweck stellt das Applet eine Verbindung zum Servlet her. Es gibt mehrere Möglichkeiten, diese Verbindung herzustellen:

  1. Die einfachste Möglichkeit ist, eine HTTP-Verbindung zu benutzen und Daten als Parameter der GET bzw. POST requests an das Servlet zu senden. Dies ist ziemlich einfach, weil HTTP-Verbindungen über das Objekt java.net.URLConnection aufgebaut werden können. Falls Ihr sehen wollt, wie dies konkret geht, könnt Ihr Euch die Datei HttpMessage.java (bei den Rahmenprogrammen) ansehen, die auch von unserem MasterMind Applet benutzt wird.

    Vorteile dieser Methode sind: die einfache Programmierung (Kommunikation über das HTTP-Protokoll) und sie funktioniert auch durch Firewalls hindurch. Es ist auch möglich, damit Java-Objekte zu verschicken, falls diese serializable sind. Nachteil ist, daß die Verbindung jedes Mal vom Applet hergestellt werden muß, da sie nicht dauerhaft ist. U.a. deshalb ist diese Methode auch nicht gerade besonders effizient.

  2. Eine andere Möglichkeit ist die altbewährte Socket-Verbindung. Nachdem das Applet mit dem Servlet verbunden ist, ist bidirektionale, dauerhafte und effiziente Kommunikation möglich. Sowohl einfache Daten, als auch Objekte können verschickt werden. Nachteile: Sie funktioniert nicht durch Firewalls, ist komplizierter zu programmieren (besonders auf der Servlet-Seite), und man muß sein eigenes Kommunikationsprotokoll entwerfen.

  3. Als dritte Möglichkeit kommt die Benutzung der RMI (Remote Method Invocation) Schnittstelle in Frage. Vorteile: Elegant, weil keine requests/responses, sondern nur Methodenaufrufe benutzt werden und objektorientiert. Das Servlet kann Methoden des Applets aufrufen. Funktioniert auch durch Firewalls (aber noch nicht so gut). Nachteile: Kompliziert zu programmieren, da spezielle Stub und Skeleton Klassen notwendig sind. Außerdem ist eine RMI-NamingRegistry notwendig, um Referenzen auf die Objekte zu bekommen. Nicht alle Browser unterstützen defaultmäßig RMI (Stand Ende 98). Außerdem kann das Servlet dann nur mit Java-Clients interagieren.



Das MasterMind Spiel

In der zweiten Aufgabe werden wir uns mit einem Applet-Servlet Paar befassen, mit dem man MasterMind spielen kann. Falls Ihr das Spiel nicht kennt, wird es deshalb hier kurz vorgestellt:

Ziel ist es, einen nichtbekannten, fünfstelligen Zahlencode zu finden (der Spielcode). Dazu kann man beliebig oft einen Spielcode-Kandidaten eingeben. Als Antwort erhält man die Anzahl der Ziffern, die an richtiger Stelle sind und die Anzahl der Ziffern, die zwar im Spielcode enthalten, aber an falscher Stelle sind. Hat man 5 Zahlen an richtiger Stelle, so hat man gewonnen.



Bevor Ihr beginnt:

Im Praktikum benutzen wir Apache als Webserver, gekoppelt mit dem WebSphere Applikationsserver von IBM, welcher die Servlets ausführt. Angebunden ist eine SQL-Datenbank (DB2), in der die Produkte eines fiktiven Pizza-Lieferdienstes abgelegt sind. Im Rahmen der ersten Aufgabe werdet Ihr einige Produktbeschreibungen aus der Datenbank holen. Um dies so einfach wie möglich zu gestalten, gibt es ein package namens pizzasvc.db, das Klassen enthält, welche die Datenbank auf Objekte abbilden. Mehr dazu in der Aufgabenstellung.

3-Tier Modell (Client, Web/App-Server, DB)
[Das 3-Schichten Modell]


Braucht man extra Klassen? (Ja)

Da die Servlets die Java Servlet API benutzen, braucht Ihr die entsprechenden .jar Dateien. Diese findet Ihr auf quak.informatik.uni-tuebingen.de unter /usr/opt/home/cspuser/classes.tar.gz. Die Datei enthält auch das package pizzasvc.db. Ihr solltet ein Verzeichnis in eurem Account erstellen und das .tar.gz Archiv dort entpacken.

Dann müßt Ihr noch die CLASSPATH Variable auf die .jar Dateien setzen. Im Beispiel unten befinden sich die Dateien im Verzeichnis myClasses. Der Befehl muß natürlich in eine Zeile geschrieben werden.

export CLASSPATH=.:/usr/lib/java/lib/classes.zip:
/home/your_id/myClasses/ibmwebas.jar:
/home/your_id/myClasses/jst.jar:
/home/your_id/myClasses/jsdk.jar:
/home/your_id/myClasses/xml4j.jar:
/home/your_id/myClasses/databeans.jar:
/home/your_id/myClasses/

Tip: Der Befehl export ist nur in der Bourne-Again-Shell (bash) verfügbar. Also entweder Ihr startet eine bash (mit bash), oder Ihr nehmt setenv stattdessen.


Gibt es Rahmenprogramme? (Ja)

Die Rahmenprogramme für Aufgabe 1 findet Ihr hier und für Aufgabe 2 hier.


Welches JDK kann benutzt werden?

Ihr solltet das IBM JDK 1.1.7B für Linux benutzen. Dies gibt es auf dem Rechner quak.informatik.uni-tuebingen.de.


Wo muß man die Lösungen ablegen, damit der Server sie findet?

  • Die .class Dateien Eurer Servlets legt Ihr auf quak, in das Verzeichnis /usr/opt/IBMWebAS/servlets/aufgaben/user_id, wobei user_id Eure login ID ist (das Verzeichnis user_id müßt ihr erst noch anlegen).
  • Eure Servlets müssen dem package aufgaben.user_id angehören, sonst findet der Server sie nicht!
  • Eure .html Dateien legt Ihr ebenfalls auf quak in das Verzeichnis /usr/opt/home/cspuser/public_html/user_id (auch hier müßt ihr das Verzeichnis user_id erst noch anlegen).

Wie kann man die .html-Dateien/Servlets aufrufen?

  • Damit das ganze funktioniert, müßt Ihr eine HTML-Einstiegsseite in das oben genannte user_id Verzeichnis legen und diese mit http://quak.informatik.uni-tuebingen.de/~cspuser/user_id/htmlSeite aufrufen.
  • Von dieser Einstiegsseite aus und in den folgenden Seiten/Servlets könnt Ihr dann den relativen Pfad /~cspuser/user_id/Seite benutzen, um .html Seiten aufzurufen.
  • Um Servlets von der Einstiegsseite aufzurufen, müßt Ihr den Pfad /servlet/aufgaben.user_id.dateiname (ohne .class) benutzen.


Ihre Aufgabe:

(Beachten Sie die Tips!!!)

Aufgabe 1

In dieser Aufgabe sollt Ihr ein Servlet erstellen, welches anhand des gesendeten Werts aus dem Formular in der .html Seite Aufg1.html eine Datenbankabfrage startet und eine .html Seite mit der Liste der Produkte erzeugt. Erweitert dazu das Rahmenprogramm Aufg1Servlet.java.

Folgende Teilschritte sind notwendig:

  1. Die Datei Aufg1.html so anpassen, daß Euer Servlet aufgerufen wird und diese in das HTML-Verzeichnis legen, wie oben beschrieben.
  2. Nun könnt Ihr mit der Programmierung des Servlets fortfahren. Hier sind folgende Schritte notwendig:
    • Den Wert auslesen. Den Namen erfahrt Ihr in der .html Seite Aufg1.html.
    • Die Datenbank abfragen: Dazu müßt Ihr die dem Wert entsprechende Produkt-Kategorie aus einer Liste holen. Dann müßt Ihr diese Produkt-Kategorie an die Produkt-Liste übermitteln und die Datenbankabfrage starten. Die dazu benötigten Objekte sind schon im Rahmenprogramm definiert.
    • Den Cache abschalten und die Antwort generieren.
  3. Nach dem Kompilieren des Servlets die .class Datei im Verzeichnis für Servlets ablegen (siehe oben) und die .html Seite aufrufen, um es zu testen.

Tips: Schaut Euch zuerst das Rahmenprogramm an. Dann schaut Euch die Dokumentation der Servlet API an, insbesondere für die Objekte HttpServlet, HttpServletRequest und HttpServletResponse. Für die Datenbankabfrage schaut in der Dokumentation des package pizzasvc.db nach. Links sind unten angegeben.


Aufgabe 2

Zu tun ist folgendes: Es gibt ein Applet, welches eine einfache GUI für ein MasterMind Spiel zur Verfügung stellt. Das Generieren der Spielcodes und das Überprüfen der Spielcode-Kandidaten übernimmt ein Servlet. Das Applet kommuniziert mit dem Servlet über ein einfaches Kommunikationsprotokoll, um dessen Dienste zu benutzen. Die Nachrichten des Protokolls werden vom Applet als Parameter an die request angehängt, mit der das Servlet aufgerufen wird.

Eure Aufgabe ist es, das Protokoll auf der Seite des Servlets zu implementieren. Im folgenden sind die zwei Nachrichten des Protokolls beschrieben und was für jede zu tun ist. Eine detaillierte Auflistung der notwendigen Einzelschritte findet Ihr als Kommentare im Rahmenprogramm mmServlet.java, welches Ihr auch bearbeiten sollt.

Nachricht 1: Parameter "action" mit Wert "code":

In diesem Fall soll das Servlet eine neue Session erstellen (falls noch keine für diesen Kommunikationspartner erzeugt wurde). Es soll einen neuen Spielcode erzeugen (mit createCode()) und diesen in die Session schreiben. Außerdem soll es die Anzahl der gemachten Spielzüge (Versuche) auf 0 setzen und in die Session schreiben.

Nachricht 2: Parameter "action" mit Wert "validate" und Parameter "code" mit Wert: ein möglicher Code (Code-Kandidat).

In dem Fall soll das Servlet:
  1. Den Spielcode aus der Session holen.
  2. Den Code-Kandidaten aus dem request-Parameter "code" holen.
  3. Die Anzahl der gemachten Versuche aus der Session holen, um eins erhöhen und wieder in die Session schreiben.
  4. Spielcode und Code-Kandidaten vergleichen und das Ergebnis an das Servlet zurückschicken. Das Ergebnis hat folgendes Format: "<Anzahl_gemachte_Spielzüge>,<Zahlen an richtiger Stelle>,<Zahlen im Code, aber an falscher Stelle>". Für den Vergleich die mitgelieferte Methode validateCode(..., ...) benutzen.
Zusätzlich müßt Ihr:
  1. Das package Eures Servlets anpassen (aufgaben.userID).
  2. Das Applet mmApplet.java anpassen, so daß Euer Servlet aufgerufen wird (Datei mmApplet.java, String SERVLET_LOC).
  3. Möglicherweise die Seite mmind.html anpassen, so daß Euer Applet aufgerufen wird.
  4. Die entsprechenden Dateien in Eure Verzeichnisse legen: mmind.html, mmApplet.class und HttpMessage.class Dateien in das HTML-Verzeichnis und das Servlet ins Servlet-Verzeichnis.
Tips:
  1. Die einfachste Art, den Wert der Parameter aus der request zu lesen (ist aber deprecated), ist die Methode request.getParameter(parameter_Name), welche ein String zurückgibt (Parsen als Integer notwendig).
  2. Um das Servlet zu testen müßt Ihr das mitgelieferte Applet benutzen. Wichtig: Vorher Cookies im Browser aktivieren !!
  3. Zum Debuggen könnt Ihr nach System.err schreiben und dann im Verzeichnis /var/log in der Datei httpd.error_log nachschauen (auf dem Rechner quak). Die Strings solltet Ihr noch mit Eurer User-ID markieren, weil alle Servlets dort reinschreiben, und dann am besten grep benutzen. Einige Exceptions der WebSphere gehen auch in /opt/IBMWebAS/logs/servlet/servletservice/error_log.
  4. Den Browser-Cache umgeht Ihr, indem Ihr die Gültigkeit der html-Datei auf Null setzt. Das ist z.B. auf selfhtml beschrieben: http://selfhtml.teamone.de/html/kopfdaten/meta.htm#laden
  5. Die Websphere muß Eure .html und .class-Dateien lesen können. Setzt daher die Rechte für die Dateien entsprechend lesbar für alle. Und vergeßt nicht, auch die Rechte für Euer Verzeichnis zu setzen.


Die Zusammenstellung dieser Programme und Dokumentationen sind urheberrechtlich geschützt.
© Copyright 1999-2000 Elias Volanakis.
Alle Rechte vorbehalten.


Abgabe:

Als Abgabe werden funktionierende Versionen der .java Dateien der Servlets erwartet. Und ebenso die User-ID unter der Sie die Dateien abgelegt habt.

Abgabe bitte per Mail an Daniel Raible oder während der Anwesenheitszeit.