Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 17 Servlets und Java Server Pages
gp 17.1 Dynamische Web-Seiten und Servlets
gp 17.1.1 Was sind Servlets?
gp 17.1.2 Was sind Java Server Pages?
gp 17.1.3 Vorteil von JSP/Servlets gegenüber CGI-Programmen
gp 17.2 Vom Client zum Server und wieder zurück
gp 17.2.1 Der bittende Client
gp 17.2.2 Was erzeugt ein Web-Server für eine Antwort?
gp 17.2.3 Wer oder was ist MIME?
gp 17.3 Servlets und Java Server Pages entwickeln und testen
gp 17.3.1 Servlet-Container
gp 17.3.2 Web-Server mit Servlet-Funktionalität
gp 17.3.3 Tomcat
gp 17.4 Java Server Pages in Tomcat und Eclipse
gp 17.4.1 Erster Ablageort für eigene JSP-Seiten
gp 17.4.2 Das Sysdeo-Plugin
gp 17.5 Skript-Elemente
gp 17.5.1 Scriptlets
gp 17.5.2 Ausdrücke
gp 17.5.3 Deklarationen
gp 17.5.4 Kommentare und Quoting
gp 17.6 Web-Applikationen
gp 17.7 Implizite Objekte
gp 17.8 Entsprechende XML-Tags
gp 17.9 Was der Browser mit auf den Weg gibt - HttpServletRequest
gp 17.9.1 Verarbeiten der Header
gp 17.9.2 Hilfsfunktion im Umgang mit Headern
gp 17.9.3 Übersicht der Browser-Header
gp 17.10 Formulardaten
gp 17.11 Das HttpServletResponse-Objekt
gp 17.11.1 Automatisches Neuladen
gp 17.11.2 Seiten umlenken
gp 17.12 JSP-Direktiven
gp 17.12.1 page-Direktiven im Überblick
gp 17.12.2 include-Direktive
gp 17.13 Aktionen
gp 17.13.1 Aktion include
gp 17.13.2 Aktion forward
gp 17.13.3 Aktion plugin
gp 17.14 Beans
gp 17.14.1 Beans in JSP-Seiten anlegen, Attribute setzen und erfragen
gp 17.14.2 Der schnelle Zugriff auf Parameter
gp 17.15 Kleine Kekse: die Klasse Cookies
gp 17.15.1 Cookies erzeugen und setzen
gp 17.15.2 Cookies vom Servlet einlesen
gp 17.15.3 Kleine Helfer für Cookies
gp 17.15.4 Cookie-Status ändern
gp 17.15.5 Langlebige Cookies
gp 17.15.6 Ein Warenkorbsystem
gp 17.16 Sitzungsverfolgung (Session Tracking)
gp 17.16.1 Das mit einer Sitzung verbundene Objekt HttpSession
gp 17.16.2 Werte mit einer Sitzung assoziieren und auslesen
gp 17.16.3 URL-Rewriting
gp 17.16.4 Zusätzliche Informationen
gp 17.17 Tag-Libraries
gp 17.17.1 Standard Tag Library (JSTL)
gp 17.18 Servlets
gp 17.18.1 Servlets compilieren
gp 17.18.2 Wohin mit den Servlets: das classes-Verzeichnis
gp 17.18.3 Servlets mit dem Sysdeo-Plugin unter Eclipse
gp 17.18.4 Servlet-Mapping
gp 17.19 Der Lebenszyklus eines Servlets
gp 17.19.1 Initialisierung in init()
gp 17.19.2 Abfragen bei service()
gp 17.19.3 Mehrere Anfragen beim Servlet und die Thread-Sicherheit
gp 17.19.4 Das Ende eines Servlets
gp 17.20 Das HttpServletResponse-Objekt
gp 17.20.1 Wir generieren eine Web-Seite
gp 17.20.2 Binärdaten senden
gp 17.20.3 Komprimierte Daten mit Content-Encoding
gp 17.20.4 Noch mehr über Header, die der Server setzt
gp 17.21 Objekte und Dateien per POST verschicken
gp 17.21.1 Datei-Upload
gp 17.22 Servlets und Sessions
gp 17.23 Weiterleiten und Einbinden von Servlet-Inhalten
gp 17.24 Inter-Servlet-Kommunikation
gp 17.24.1 Daten zwischen Servlets teilen
gp 17.25 Internationalisierung
gp 17.25.1 Die Länderkennung des Anfragers auslesen
gp 17.25.2 Länderkennung für die Ausgabe setzen
gp 17.25.3 Westeuropäische Texte senden
gp 17.26 Tomcat: Spezielles
gp 17.26.1 Tomcat als Service unter Windows NT ausführen
gp 17.26.2 MIME-Types mit Tomcat verbinden
gp 17.26.3 Servlets beim Start laden
gp 17.27 Ein Servlet generiert WAP-Seiten für das Handy
gp 17.27.1 Ein WAP-Handy simulieren
gp 17.27.2 Übersicht der wichtigsten Tags
gp 17.27.3 Der Gateway
gp 17.27.4 WML-Seiten aufbauen
gp 17.27.5 Interessante Links zum Thema Servlets/JSP


Galileo Computing

17.20 Das HttpServletResponse-Objektdowntop

Das HttpServletResponse-Objekt ist die Rückrichtung vom Servlet zum Client. Bei den JSPs haben wir das implizite Objekt response, das für die Rückrichtung gedacht ist, schon kennen gelernt


Galileo Computing

17.20.1 Wir generieren eine Web-Seitedowntop

Unser folgendes Beispiel führt uns noch einmal zu einer einfachen Web-Seite:

Listing 17.21 FirstServlet2.java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FirstServlet2 extends HttpServlet
{
  public void doGet( HttpServletRequest request, HttpServletResponse response )
      throws ServletException, IOException
  {
    PrintWriter out = response.getWriter();
    out.println( "Oho. Nun mit doGet()!" );
  }
}

Das Objekt HttpServletRequest beinhaltet die Informationen vom Browser. HttpServletResponse ist für die Ausgabe zuständig, die das Servlet liefert und zum Client schickt. Beide Klassen liegen zusammen mit HttpServlet im Paket javax.servlet.http. Das Paket javax.servlet.* wird nur wegen ServletException eingebunden. Im Paket java.io liegt die Klasse Writer. Die IOException müssen wir auffangen, da getWriter() eine Ausnahme auslösen kann.

Eine HTML-Seite statt einer Textseite erzeugen

In den ersten Beispielen haben wir kein spezielles Ausgabeformat gewählt, es war Plain-Text. Damit andere Typen gesendet werden können, beispielsweise HTML oder Binärdaten für Bilder, müssen wir zwei Schritte machen. Zuerst müssen wir dem Browser mitteilen, um welches Ausgabeformat es sich handelt, und dann müssen wir den Datenstrom speziell formatieren. Das Ausgabeformat wird dabei mit Hilfe von MIME-Type definiert. (MIME-Types sind in RFC 2045 definiert.) Dazu schickt das Servlet eine spezielle Kennung, die Content-Type heißt. Da wir diese zum Browser schicken, wenden wir uns an das HttpServletResponse-Objekt. Dies bietet setHeader() an, um beliebige Header zu setzen:

response.setHeader( "Content-Type", "text/html");

Die Methode erhält als Parameter zwei Zeichenketten: den Header und den dazugehörigen Wert. Da jedoch der Header Content-Type so häufig benötigt wird, bietet die Schnittstelle HttpServletResponse dafür die eigene Methode setContentType() an:

response.setContentType( "text/html" );

Um reine (Nur-)Textausgaben zu erzeugen, schreiben wir den Header text/ plain.

Nach dem Setzen folgt der zweite Schritt. Wir schreiben HTML-Text über den Ausgabestrom, nachdem der Header gesetzt ist:

PrintWriter out = response.getWriter();
out.println( "<!DOCTYPE  HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
             "Transitional//EN\">\n" +
             "<HTML>\n" +
             "<HEAD><TITLE>Der Kopf</TITLE></HEAD>\n" +
             "<BODY>\n" +
             "<H1>Das ist HTML in Perfektion</H1>\n" +
             "</BODY></HTML>");

Wie wir sehen, ist es ein sehr aufwändiges Unterfangen, HTML-Code zu erzeugen. Daher wird in diesem Tutorial meistens darauf verzichtet, da wir viele Eigenschaften der Server auch einfach durch Text zeigen können.

Eine wohlgeformte HTML-Datei besteht aus einer Anweisung zu Beginn, die den XML-Typ über das Tag DOCTYPE angibt. Anschließend folgen die HTML-Anweisungen. Unterschiedliche Hersteller bieten Bibliotheken an, mit denen die Generierung von Web-Seiten über spezielle Hilfsklassen einfacher wird. Der Aufbau der Seite wird dabei als baumartig organisiertes Objekt verwaltet und anschließend in HTML übersetzt. Der Content-Type sollte vor der ersten Ausgabe gesetzt sein.


Galileo Computing

17.20.2 Binärdaten sendendowntop

Um Binärdaten zu senden, muss lediglich der passende Typ im Content-Type eingestellt sein. Dabei kann es sich um Daten handeln, die der Browser interpretieren kann oder nicht.


Beispiel Content-Type bei Bildern
response.setContentType( "image/jpeg" );
response.setContentType( "image/gif" );

Wenn wir den Inhaltstyp - wie oben geschehen - setzen und einen binären Datenstrom mit den Bildinformationen schicken, ist schnell ein Bilder-Servlet geschrieben. Wieder gilt, dass der Dateiname natürlich als Parameter eingeführt werden kann.

Listing 17.22 BinarySender.java

import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class BinarySender extends HttpServlet
{
  public void doGet( HttpServletRequest request,
                     HttpServletResponse response )
      throws ServletException, IOException
  {
    String filename = "C:/WINNT/Profiles/Administrator/"+
                      "Desktop/wirelessduke.jpg";
    InputStream in = new BufferedInputStream(
                              new FileInputStream(filename) );
    String s = URLConnection.guessContentTypeFromStream(in);
    response.setContentType( s );
    byte pic[]= new byte[in.available()];
    in.read( pic );
    OutputStream out = response.getOutputStream();
    out.write( pic );
  }
}

In den Eingabestrom schreiben wir dieses Mal keine Unicode-Zeichen, sondern binäre Daten. Daher ist ein Writer nicht nötig. Stattdessen holen wir uns direkt einen OutputStream mit getOutputStream(). Falls wir uns jedoch schon vorher einen Writer mit get Writer() geholt haben, bestraft uns Tomcat mit einer netten Seite und dem Fehler 500: Es dürfen nicht gleichzeitig Writer und Stream offen sein.

Beherzigen wir dies, können wir einfach mit write() Daten schreiben, und der Client versucht, diese zu interpretieren. Der Content-Type und die Binärdaten müssen zusammenpassen, andernfalls ist das Verhalten nicht immer vorhersehbar.

Falls uns der Datentyp selber nicht klar ist, lässt sich eine kaum bekannte statische Methode aus der Klasse URLConnection nutzen: guessContentTypeFromStream(). Sie versucht mit Hilfe der ersten Bytes auf den Inhalt zu schließen. Da die Methode einen Stream mit mark/reset benötigt (damit die Daten gelesen und dann wieder zurückgesetzt werden können), geben wir ihm einen markierungsfähigen Datenstrom in Form des Buffered InputStream. Findet er den Typ (wenn nicht, liefert die Methode null), wird dieser sofort in setContentType() eingesetzt. Somit können wir die Datei leicht ändern, und die Klasse sucht sich automatisch den richtigen Typ für verbreitete Binärdaten. Unter der Servlet-API gibt es auch die Methode ServletContext.getMimeType(), die das Erkennen des Typs vornimmt.


Galileo Computing

17.20.3 Komprimierte Daten mit Content-Encodingdowntop

Wir wollen unser Wissen über die Header etwas erweitern und noch andere Möglichkeiten des Protokolls HTTP kennen lernen. Ein spezieller Header Content-Encoding setzt den Typ der Kodierung für die Seite. Hier lässt sich eine Komprimierung wie gzip einsetzen. Da dies in Java problemlos über eine spezielle Klasse abgewickelt werden kann, wollen wir ein einfaches Beispiel konstruieren, welches eine komprimierte Textdatei verschickt. Wir setzen dabei Content-Encoding mit folgender Anweisung:

response.setHeader( "Content-Encoding ", "gzip" );

Bevor die Datei vom Browser dargestellt wird, entpackt dieser die Datei selbstständig. Das ist angenehm, denn auf diese Weise lässt sich der Datenverkehr für gut komprimierbare Daten, beispielweise Plain-Text oder HTML, deutlich reduzieren. Der Content-Type bleibt unangetastet.

Listing 17.23 GzipFile.java

import java.io.*;
import java.util.zip.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GzipFile extends HttpServlet
{
  public void doGet( HttpServletRequest request,
                     HttpServletResponse response )
      throws ServletException, IOException
  {
    response.setContentType( "text/html" );
    response.setHeader( "Content-Encoding", "gzip" );
    String filename = "C:/WINNT/Profiles/Administrator/"+
                      "Desktop/tel.html";
    InputStream in = new FileInputStream( filename );
    byte txt[]= new byte[in.available()];
    in.read( txt );
    OutputStream out = new GZIPOutputStream(
                         response.getOutputStream() );
    out.write( txt );
    out.close();
  }
}

Kann der Browser entpacken?

Da nicht jeder Browser automatisch die Daten entpackt, sollte ein schlaues Programm dies vorher erfragen. Glücklicherweise schickt der Browser bei jeder Anfrage ein spezielles Feld Accept-Encoding mit, das sich zum Beispiel nach der Kodierung durchsuchen lässt. Die Zeile

boolean c = request.getHeader("Accept-Encoding").indexof("gzip") >= 0;

erledigt dies und testet, ob Kompression erlaubt ist. Hier nehmen wir den Header der Anfrage auseinander. Dieses Vorgehen beleuchten wir später noch intensiver.

Was wir jedoch jetzt schon sagen können, ist Folgendes: Falls der Client die Kompression unterstützt, wie Netscape unter Unix und IE ab Version 4, so sollten wir dies für HMTL- oder Text-Dateien nutzen. Einem Browser, der die Daten nicht entpacken kann, liefern wir einfach eine ungepackte Version, die er darstellen kann. Andernfalls würde der Browser die komprimierte Version zum Abspeichern anbieten.


Galileo Computing

17.20.4 Noch mehr über Header, die der Server setzttoptop

Bisher kennen wir von der Klasse HttpServletResponse die Methode setHeader() für beliebige Header.1


Beispiel Setze den Header pragma6, damit vom Browser keine Daten im Cache gehalten werden.
response.setHeader( "pragma", "no-cache" );

Mit dieser Aufforderung soll der Browser die Seite jedes Mal neu laden. Das ist bei dynamischen Seiten besonders wichtig, da sie bei jedem Aufruf neu generiert werden und sich Werte ändern können, wie es zum Beispiel bei Warenkorbsystemen der Fall ist. Da wir uns als Applikationsentwickler nicht immer mit dem Namen der Header herumärgern wollen, bietet die Bibliothek einige Spezialfunktionen an.


Beispiel Für den Header Content-Type gibt es die spezielle Methode setContentType().
response.setHeader( "Content-Type", "text/html");
response.setContentType( "text/html" );

Daneben gibt es setContentLength(), die den Header Content-Length setzt. Diese Länge muss nicht gesetzt werden und wird automatisch berechnet. Falsche Längen könnten zu Ausnahmesituationen führen. Der Gebrauch ist jedoch nützlich, wenn vorher die gesamte Web-Seite in einem StringBuffer sb gesammelt und in einem Rutsch übertragen wird. Dann können wir setContentLength(sb.length()) aufrufen.

Um einen Datums-Header zu setzen, existiert setDateHeader(String, long). Der Parameter ist eine beliebige Zeichenkette, die mit einem Datumswert verbunden wird. Das long gibt die Millisekunden seit dem 1.1.1970 an. Die Ausgabe, die erzeugt wird, schreibt einen GMT-String.

Eine weitere Hilfsfunktion ist setIntHeader(), die Zahlenwerte mit Schlüsseln in den Header schreibt. Hier übernimmt die Methode die Konvertierung von String in eine Ganzzahl.

Neben diesen setXXX()-Methoden, die möglicherweise gesetzte Header überschreiben, lässt sich mit containsHeader(String) abfragen, ob Wertepaare schon gesetzt sind. Neben den setXXX()-Methoden gibt es auch entsprechende addXXX()-Methoden, die die Werte nicht überschreiben, sondern hinzufügen. Für Cookies existiert zusätzlich eine Methode mit dem Namen addCookie(), die einen Cookie im Header setzt.






1 Den Header pragma gibt es schon seit HTTP 1.0. In HTTP 1.1 wurde die Cache-Fähigkeit verfeinert, etwa mit cache-control, die den Header pragma ersetzt. Dennoch sollten beide Header gesetzt werden.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de