Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
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.4 Java Server Pages in Tomcat und Eclipse
    gp 17.4.1 Download und Installation
    gp 17.4.2 Ablageort für eigene JSP-Seiten
    gp 17.4.3 Web-Applikationen
    gp 17.4.4 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.5.5 Entsprechende XML-Tags
  gp 17.6 Implizite Objekte
  gp 17.7 Was der Browser mit auf den Weg gibt – HttpServletRequest
    gp 17.7.1 Verarbeiten der Header
    gp 17.7.2 Hilfsfunktion im Umgang mit Headern
    gp 17.7.3 Übersicht der Browser-Header
  gp 17.8 Formulardaten
  gp 17.9 Das HttpServletResponse-Objekt
    gp 17.9.1 Automatisches Neuladen
    gp 17.9.2 Seiten umlenken
  gp 17.10 JSP-Direktiven
    gp 17.10.1 page-Direktiven im Überblick
    gp 17.10.2 include-Direktive
    gp 17.10.3 Mit JSPs Bilder generieren
  gp 17.11 Aktionen
    gp 17.11.1 Aktion include
    gp 17.11.2 Aktion forward
    gp 17.11.3 Aktion plugin
  gp 17.12 Beans
    gp 17.12.1 Beans in JSP-Seiten anlegen, Attribute setzen und erfragen
    gp 17.12.2 Der schnelle Zugriff auf Parameter
  gp 17.13 Kleine Kekse: die Klasse Cookies
    gp 17.13.1 Cookies erzeugen und setzen
    gp 17.13.2 Cookies vom Servlet einlesen
    gp 17.13.3 Kleine Helfer für Cookies
    gp 17.13.4 Cookie-Status ändern
    gp 17.13.5 Langlebige Cookies
    gp 17.13.6 Ein Warenkorbsystem
  gp 17.14 Sitzungsverfolgung (Session Tracking)
    gp 17.14.1 Das mit einer Sitzung verbundene Objekt HttpSession
    gp 17.14.2 Werte mit einer Sitzung assoziieren und auslesen
    gp 17.14.3 URL-Rewriting
    gp 17.14.4 Zusätzliche Informationen
  gp 17.15 Tag-Libraries
    gp 17.15.1 Standard Tag Library (JSTL)
    gp 17.15.2 Jakarta Taglibs Project
  gp 17.16 Servlets
    gp 17.16.1 Servlets compilieren
    gp 17.16.2 Wohin mit den Servlets: das classes-Verzeichnis
    gp 17.16.3 Servlets mit dem Sysdeo-Plugin unter Eclipse
    gp 17.16.4 Servlet-Mapping
  gp 17.17 Der Lebenszyklus eines Servlets
    gp 17.17.1 Initialisierung in init()
    gp 17.17.2 Abfragen bei service()
    gp 17.17.3 Mehrere Anfragen beim Servlet und die Thread-Sicherheit
    gp 17.17.4 Das Ende eines Servlets
  gp 17.18 Das HttpServletResponse-Objekt
    gp 17.18.1 Wir generieren eine Web-Seite
    gp 17.18.2 Binärdaten senden
    gp 17.18.3 Komprimierte Daten mit Content-Encoding
    gp 17.18.4 Noch mehr über Header, die der Server setzt
  gp 17.19 Objekte und Dateien per POST verschicken
    gp 17.19.1 Datei-Upload
  gp 17.20 Servlets und Sessions
  gp 17.21 Weiterleiten und Einbinden von Servlet-Inhalten
  gp 17.22 Inter-Servlet-Kommunikation
    gp 17.22.1 Daten zwischen Servlets teilen
  gp 17.23 Internationalisierung
    gp 17.23.1 Die Länderkennung des Anfragers auslesen
    gp 17.23.2 Länderkennung für die Ausgabe setzen
    gp 17.23.3 Westeuropäische Texte senden
  gp 17.24 Tomcat: Spezielles
    gp 17.24.1 Tomcat als Service unter Windows NT ausführen
    gp 17.24.2 Interessante Links zum Thema Servlets/JSP


Galileo Computing

17.18 Das HttpServletResponse-Objekdowntop

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.18.1 Wir generieren eine Web-Seite  downtop

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 Argument 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.18.2 Binärdaten senden  downtop

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.

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 Funktion 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 BufferedInputStream. 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.18.3 Komprimierte Daten mit Content-Encoding  downtop

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.18.4 Noch mehr über Header, die der Server setzt  toptop

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


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). Das Argument 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 © 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