Sie sind auf Seite 1von 34

Servlet Filter

Seminararbeit SS03

von Regina Dietiker

Fachhochschule Aargau
Departement Technik
Studiengang Informatik

Betreuender Dozent: Prof. Dr. D. Gruntz

Windisch, 15. Mai 2003


Abstract

Java Servlet Filters belong to a mighty concept in the Java Servlet API. Used to
attach dynamically functionality to a already running Servlet, they implement
the Chain of Responsibility Pattern. Java Servlet Filters are used to provide
functionality which is not related with the logic of the Servlet itself. A Servlet
Filter stands between the Client and the Servlet and may read and change the
request and response header or data. Java Servlet Filter are widely used to
implement autentication, authorisation, compression, encryption and for many
other tasks.

[java, Servlet Filter, Interceptor, Chain of Responsibility Pattern]


Inhaltsverzeichnis

1 Einleitung 1

2 Was ist ein Servlet Filter 3


2.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Mögliche Verwendungsgebiete . . . . . . . . . . . . . . . . . . . . 3
2.3 Konzepte hinter den Servlet Filter . . . . . . . . . . . . . . . . . 4
2.3.1 Interceptoren . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3.2 Chain of Responsibility Pattern . . . . . . . . . . . . . . . 4

3 Das Servlet Filter Interface 6


3.1 javax.servlet.Filter . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.1.1 init() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.1.2 destroy() . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1.3 doFilter() . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 javax.servlet.FilterChain . . . . . . . . . . . . . . . . . . . . . . . 7
3.3 javax.servlet.FilterConfig . . . . . . . . . . . . . . . . . . . . . . . 8

4 Beispiele von Servlet-Filter 9


4.1 Das Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.2 Logging Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.3 Ein Session Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.4 Servlets und Filter deployen . . . . . . . . . . . . . . . . . . . . . 13
4.5 Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.5.1 Request Wrapper . . . . . . . . . . . . . . . . . . . . . . . 15
4.5.2 Response Wrapper . . . . . . . . . . . . . . . . . . . . . . 16

5 Schluss 18
INHALTSVERZEICHNIS ii

A Compression Filter 21

B web.xml-Skeleton 28

Regina Dietiker
c FH Aargau 15.5.2003
Kapitel 1

Einleitung

Der folgende Text richtet sich an alle, die schnell einen Einstieg in die Welt der
Servlet Filter suchen. Es ist ein Zusammenschnitt aus verschiedenen Quellen des
Internets. Das Internet bietet viele einsatzbereite Open Source Servlet Filter
zum Herunterladen an, mit welchen die vielfältigen Einsatzmöglichkeiten der
Servlet Filter getestet werden können. Zuerst bietet der vorliegende Text eine
kurze Einführung in die Konzepte hinter den Servlet Filter. Danach wird die
massgebliche Schnittstelle erläutert. Zum Schluss wird anhand eines einfachen
Beispiels die Installation eines Servlet Filters gezeigt.
Seit der Java Servlet API 2.3 wurde ein neues mächtiges Konzept in die Java
Servlet API integriert: Java Servlet Filter. Dieses neue Konzept ermöglicht es,
mit drei neuen Schnittstellen Komponenten zu programmieren, die sich einfach
und dynamisch in die Verarbeitungskette zwischen Client und Servlet integrie-
ren lassen. Endlich können kleinere Aufgaben, die nicht direkt mit der Logik
des Servlets zusammenhängen, von selbständigen Komponenten erledigt wer-
den, die sich einfach hinzufügen und gegebenenfalls wieder entfernen lassen.
Dafür muss keine einzige Zeile Code des Servlets geändert werden. Es genügt
die Filter, die dem Servlet zur Seite stehen sollen in der entsprechenden Servlet-
Container-Registrierungsdatei (web.xml bei Tomcat) zu registrieren.
Ein Filter kann auf einfache Art die Kommunikation zwischen Servlet und Cli-
ent beeinflussen. Der Filter hat Zugriff auf die Headerinformation und auf die
Daten der Request- und Response-Pakete, die zwischen Client und Servlet hin-
und hergeschickt werden. Dadurch sind sie sehr geeignet, gewisse Aufgaben in
verschiedenen Bereichen wie Authetifizierung, Authorisierung, Verschlüsselung,
Datenkompression und Transformation in verschiedene Formate zu erledigen.
Die Liste kann noch beliebig erweitert werden.
Das Konzept, das den Java Servlet Filter zugrunde liegt, ist nicht neu: es ist eine
Implementation des Chain of Responsibility Pattern. Bereits in CORBA wurde
das Filter Konzept unter dem Namen Interceptoren angewandt, auch diverse
Servlet Container stellten Filter für Transformationen zur Verfügung, mit dem
Nachteil, dass ein Filter nur für eine Art Servlet Container programmiert wurde
und in keinem anderen lief. Die Realisation in Java stellte jedoch sicher, dass
2

die Wiederverwendbarkeit und die Plattformunabhängigkeit gewährleistet ist.

Regina Dietiker
c FH Aargau 15.5.2003
Kapitel 2

Was ist ein Servlet Filter

2.1 Definition

Ein Servlet Filter ist eine wiederverwendbare Komponente, welche den Inhalt
oder die Headerinformation eines Servlet Request oder einer Servlet Response
lesen oder modifizieren kann. Ein Servlet Filter dient als Präprozessor und/oder
Postprozessor in der Verarbeitungskette zwischen Client und Servlet. Ein Serv-
let Filter ist kein Servlet, er erstellt in der Regel keine Response sondern leitet
die erhaltene Response modifiziiert weiter. In der Verarbeitungskette zwischen
Client und Servlet können auch mehrere Filter hintereinander geschaltet sein.
Filter stellen Dienste bereit, die von mehreren Servlets benutzt werden können.

• Der Servletfilter überprüft den Request bevor das Servlet aufgerufen wird.

• Er kann den Request Header und die Request Daten verändern.

• Er kann den Response Header und die Response Daten verändern.

Abbildung 2.1: Verarbeitungskette mit Filter

2.2 Mögliche Verwendungsgebiete

Servlet Filter können für verschiedene Aufgaben verwendet werden. Besonders


gute Leistungen erbrachten sie bisher in folgenden Bereichen:
2.3. KONZEPTE HINTER DEN SERVLET FILTER 4

• Autentication - Ein Request wird abgelehnt, weil die User-Id keinen Zu-
griff hat

• Logging und Auditing - Benutzerbewegungen werden aufgezeichnet

• Bilder konvertieren

• Daten Kompression - ermöglicht schnellere Downloads

• Localization - Request und Response an einen Ort schicken

• XSL/T Transformationen

• Verschüsselung

• Tokenizing

• Triggering Resource access events

• MIME Type Chaining

• Caching

2.3 Konzepte hinter den Servlet Filter

2.3.1 Interceptoren

Ein Servlet Filter ist kein neues Konzept, bereits in Corba gab es sogenannte
Interceptoren. Interceptoren sollen technische Dienste bereitstellen, die nicht
direkt mit der Logik des Anwendungsprogrammes zu tun haben. Java verwendet
nicht nur im Zusammenhang mit Corba Interceptoren, das Konzept findet sich
auch bei den Enterprise Java Beans (EJB) wieder.
Seit Tomcat 3 ist das Interceptorkonzept auch in die Architektur des Servlet-
containers eingeflossen. In der Version 4 von Tomcat ist das Konzept unter
dem Namen Valve in die Catalina-Architektur eingeflossen. Allerdings sind die
Valven nur für den internen Gebrauch in Catalina bestimmt, wobei der Anwen-
dugsentwicker das Gleiche mit Servlet Filter erreichen kann.

2.3.2 Chain of Responsibility Pattern

Das Chain of Responibility Pattern sagt aus, dass der Sender und der
Empfänger bei der Request-Verarbeitung durch mehrere Zwischenobjekte ent-
koppelt werden sollen. Die Zwischenobjekte bilden eine Verarbeitungskette. Da-
durch wird ein Request solange von einem Objekt zum nächsten geschickt, bis es
beim Empfänger (dem Servlet) ankommt und dann dort entsprechend verarbei-
tet wird. Nach der Verarbeitung des Requests sendet das Servlet die Response
durch die gleiche Verarbeitungskette zurück und die einzelnen Zwischenobjekte
haben wieder die Gelegenheit, die Response zu verändern.

Regina Dietiker
c FH Aargau 15.5.2003
2.3. KONZEPTE HINTER DEN SERVLET FILTER 5

Abbildung 2.2: Chain of Responsibility Pattern

Regina Dietiker
c FH Aargau 15.5.2003
Kapitel 3

Das Servlet Filter Interface

Im Wesentlichen braucht man drei Interfaces, um einen Filter zu programmie-


ren.

3.1 javax.servlet.Filter

javax.servlet.Filter ist das eigentliche Filterinterface. Es besteht aus drei


Methoden

• init()

• destroy()

• doFilter()

Die Methoden init und destroy werden nur einmal aufgerufen. Die doFilter-
Methode ist für das eigentliche Filtern zuständig und wird beliebig oft aufge-
rufen.

3.1.1 init()

init(javax.servlet.FilterConfig config) wird einmal aufgerufen, wenn


der Filter initialisert wird. Ab jetzt stellt er seine Dienste bereit. Der Servlet
Container liefert als Parameter ein FilterConfig Objekt mit, das die Filterkon-
figuration enthält.
Während der Initialisierungsphase kann der Filter über FilterConfig die Initial-
parameter abfragen, die im Deploymentdeskriptor spezifiziert sind. Dafür sind
die folgenden Methoden vorgesehen:

• String getInitParameter(String string)

• Enumeration get InitParameterNames().


3.2. JAVAX.SERVLET.FILTERCHAIN 7

Falls man später im Filter auf den Request Dispatching Mechanismus des Serv-
lets zugreifen will, sollte man sich den ServletContex mit der Methode

ServletContext getServletContest()

merken. Nach der Initialisierung ist der Filter bereit, Requests und Responses
zu filtern. Die weiter unten beschriebene Methode doFilter() übernimmt das
eigentliche Filtern.

3.1.2 destroy()

Wenn ein Filter aus dem Servlet-Container entfernt wird, wird die Methode
destroy() aufgerufen und der Filter kann allfällige Aufräumarbeiten erledigen.

3.1.3 doFilter()

Der Servlet-Container ruft bei jedem Request, dem ein Filter zugeordnet ist,
die

doFilter(ServletRequest request, ServletResponse response, FilterChain


filterChain)

auf.Der Filter erhält dabei das Request und das Response Objekt, die er bei-
de lesen und verändern kann. Falls der Filter HTTP-Anfragen verarbeiten soll
muss er die Objekte in javax.servlet.http.HTTPServletRequest respektive ja-
vax.servlet.http.HTTPServletResponse casten. Als dritten Parameter erhält
doFilter noch die FilterChain. Die FilterChain ist ein Interface, das von je-
dem Servlet Container, der Filter unterstützt, implementiert sein muss. In ei-
ner FilterChain sind alle Filter enthalten, die nacheinander für einen speziel-
len Request-Typ aufgerufen werden sollen. Ein Filter muss in der doFilter()-
Methode nach erledigter Filterarbeit die doFilter-Methode des nächsten Filters
in der FilterChain aufrufen. Dadurch wird ein Request durch mehrere Filter
verarbeitet.

3.2 javax.servlet.FilterChain

Das javax.servlet.FilterChain-Interface muss von jedem Servlet Container im-


plementiert werden. Mit der Implementation muss ein Mechanismus zur
Verfügung gestellt werden um mehrere Filter hintereinander aufzurufen. Ei-
ne Implementation von FilterChain-interface wird der doFilter Methode des
Filters übergeben, um dadurch den nächsten Filter aufzurufen.

Regina Dietiker
c FH Aargau 15.5.2003
3.3. JAVAX.SERVLET.FILTERCONFIG 8

3.3 javax.servlet.FilterConfig

Über das javax.servlet.FilterConfig Interface kann man auf die Initialisie-


rungsdaten des Filters zugreifen, welche im web.xml festgelegt werden.

Regina Dietiker
c FH Aargau 15.5.2003
Kapitel 4

Beispiele von Servlet-Filter

4.1 Das Servlet


Dem Servlet (HelloSeminar.java) werden zwei Filter zugeordnet. Der Log-
gingFilter merkt sich den Zeitpunkt des Passierens des Requests und der Re-
sponse und schreibt die daraus berechnete Zugriffszeit zusammen mit anderen
Informationen in ein Logfile. Der SessionFilter lässt nur Requests mit gültiger
Session auf das HelloSeminar-Servlet zugreifen, die anderen leitete er an eine
Login-Seite weiter.

import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
* @author Regina Dietiker, Ic00
*
*/
public class HelloSeminar extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String title = "Hello";
String text = "Hallo Seminarteilnehmer!!!";
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>" + text + "</h1>");
out.println("Enjoy yourselves!!!");
out.println("</body>");
out.println("</html>");
}
}
4.2. LOGGING FILTER 10

4.2 Logging Filter


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* @author Regina Dietiker, Ic00
*
*/
public class LoggingFilter implements Filter{
private FilterConfig config;
public void init(FilterConfig config){
this.config = config;
}

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)


throws ServletException, IOException {
long start = System.currentTimeMillis();
chain.doFilter(req, res);
long end = System.currentTimeMillis();
String sessionId = null;
sessionId = ((HttpServletRequest)req).getSession().getId();
config.getServletContext().log("-------LoggingFilter-Output--------------");
config.getServletContext().log("Session: " + sessionId);
config.getServletContext().log("Request URI" + ((HttpServletRequest)req).getRequestURI());
config.getServletContext().log("Time: " + (end-start) + " Millisekunden");
config.getServletContext().log("------------------------------------------");
config.getServletContext().log("");
}

public void destroy(){


}
}

Der Filter wird aufgerufen, während der Client einen Request an das Servlet
schickt. Der Filter speichert den Zeitpunkt des Passierens des Requests und
nach dem Ausführen des Servlets den Zeitpunkt, an dem die Response wieder
durch den Filter geht. Dadurch kann er die Zugriffszeit auf das Servlet ermitteln.
Diese und andere Informationen aus dem Header schreibt er in eine Log-Datei,
die bei Tomcat unter logs\ zu finden ist.

Auszug aus tomcat/logs/localhost_log.2003-05-14.txt

2003-05-14 15:23:54 StandardWrapper[/webdav:default]: Loading container servlet default


2003-05-14 15:23:54 StandardWrapper[/webdav:invoker]: Loading container servlet invoker
2003-05-14 15:23:55 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:23:55 User: null
2003-05-14 15:23:55 ++++++++++++++++++++++++++++++++++++++++++
2003-05-14 15:23:55 -------LoggingFilter-Output--------------
2003-05-14 15:23:55 Session: 34E731C20C128E5D74C2CB2D75231322
2003-05-14 15:23:55 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:23:55 Time: 78 Millisekunden
2003-05-14 15:23:55 ------------------------------------------
2003-05-14 15:23:55
2003-05-14 15:24:01 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:24:01 User: regina
2003-05-14 15:24:01 ++++++++++++++++++++++++++++++++++++++++++

Regina Dietiker
c FH Aargau 15.5.2003
4.3. EIN SESSION FILTER 11

2003-05-14 15:24:01 -------LoggingFilter-Output--------------


2003-05-14 15:24:01 Session: 34E731C20C128E5D74C2CB2D75231322
2003-05-14 15:24:01 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:24:01 Time: 1 Millisekunden
2003-05-14 15:24:01 ------------------------------------------
2003-05-14 15:24:09 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:24:09 User: regina
2003-05-14 15:24:09 ++++++++++++++++++++++++++++++++++++++++++
2003-05-14 15:24:09 -------LoggingFilter-Output--------------
2003-05-14 15:24:09 Session: 34E731C20C128E5D74C2CB2D75231322
2003-05-14 15:24:09 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:24:09 Time: 1 Millisekunden
2003-05-14 15:24:09 ------------------------------------------
2003-05-14 15:24:09
2003-05-14 15:24:17 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:24:17 User: null
2003-05-14 15:24:17 ++++++++++++++++++++++++++++++++++++++++++
2003-05-14 15:24:17 -------LoggingFilter-Output--------------
2003-05-14 15:24:17 Session: 2B5B0A7614B3D8B726E1633F3C3170A0
2003-05-14 15:24:17 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:24:18 Time: 2 Millisekunden
2003-05-14 15:24:18 ------------------------------------------
2003-05-14 15:24:18
2003-05-14 15:24:24 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:24:24 User: alexandra
2003-05-14 15:24:24 ++++++++++++++++++++++++++++++++++++++++++
2003-05-14 15:24:24 -------LoggingFilter-Output--------------
2003-05-14 15:24:24 Session: 2B5B0A7614B3D8B726E1633F3C3170A0
2003-05-14 15:24:24 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:24:24 Time: 1 Millisekunden
2003-05-14 15:24:24 ------------------------------------------
2003-05-14 15:24:24
2003-05-14 15:24:27 +++++++SessionFilter++++++++++++++++++++++
2003-05-14 15:24:27 User: alexandra
2003-05-14 15:24:27 ++++++++++++++++++++++++++++++++++++++++++
2003-05-14 15:24:27 -------LoggingFilter-Output--------------
2003-05-14 15:24:27 Session: 2B5B0A7614B3D8B726E1633F3C3170A0
2003-05-14 15:24:27 Request URI/filterBsp/servlet/HelloSeminar
2003-05-14 15:24:27 Time: 0 Millisekunden
2003-05-14 15:24:27 ------------------------------------------
2003-05-14 15:24:27

4.3 Ein Session Filter


Der Session Filter lässt nur Requests mit einer gültigen Session (ein Attributt
namens user ist gesetzt) auf das HelloSeminar-Servlet zugreifen. Falls der User
noch nicht eingeloggt hat und damit noch keine gültige Session existiert, wird
er an eine Login-Seite weitergeleitet, wo er einen Namen eintragen muss. Mit
welchem dann eine gültige Session erstellt wird. Jetzt hat er Zugriff auf das
HelloSeminar-Servlet.

<html>
<head>
<title>Login</title>
</head>

Regina Dietiker
c FH Aargau 15.5.2003
4.3. EIN SESSION FILTER 12

<body>
<h1>Bitte Loggen Sie sich ein</h1>
<center><form action=http://localhost:8080/filterBsp/servlet/LoginServlet method=GET>
<p><font face="Arial,Helvetica"></font>
<p><font face = "Arial, Helvetica">Benutzername:</font>
<p><input type=text name=user>
<p><input type=submit value="einloggen">
</form>
</center>
</body>
</html>

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
* @author Regina Dietiker, Ic00
*
*/
public class SessionFilter implements Filter {
private FilterConfig config;
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig arg0) throws ServletException {
config = arg0;
}
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)


throws ServletException, IOException {
config.getServletContext().log("+++++++SessionFilter++++++++++++++++++++++");
HttpSession session = ((HttpServletRequest)request).getSession(true);
String user = (String)session.getAttribute("user");
PrintWriter out = response.getWriter();
config.getServletContext().log("User: " + user);
config.getServletContext().log("++++++++++++++++++++++++++++++++++++++++++");
if (user==null){
RequestDispatcher rd = request.getRequestDispatcher("/Login.html");
rd.forward(request, response);
}
else{
filterChain.doFilter(request, response);
}
}

/**
* @see javax.servlet.Filter#destroy()
*/
public void destroy() {
}
}

Regina Dietiker
c FH Aargau 15.5.2003
4.4. SERVLETS UND FILTER DEPLOYEN 13

4.4 Servlets und Filter deployen

Um die geschriebenen Servlet-Filter zu benutzen, müssen sie beim Servlet-


Container (hier Tomcat 4.1.18) registriert werden und anschliessend einem oder
mehreren Servlets zugeordnet werden. Das wird gemacht, indem man im selben
web.xml, indem bereits das Servlet registriert ist, noch die entsprechenden Zei-
len einträgt. Wichtig ist, dass innerhalb des web.xml die folgende Reihenfolge
eingehalten wird:

1. Zuerst wird der Filter definiert: ein beligig gewählter Name, der auch
Leerzeichen enthalten kann wird der Filter-Klasse zugeornet, wobei der
Name der Klasse ohne das .class mit allen Paketangaben angegeben
wird. Hier können auch Initialparameter angegeben werden.

2. Der Filter wird einem oder mehreren Servlets zugeordnet.

3. Das Servlet wird definiert: ein frei gewählter Name wird der Servletklasse
(Bezeichnung wieder ohne .class) zugeornet.

4. Der Name des Servlets wird einem oder mehreren bestimmten Pfad(en)
oder einer URL(s) zugeordnet, mit welchen es dann angesprochen werden
kann.

<?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>
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<servlet-name>HelloSeminar</servlet-name>
</filter-mapping>
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<servlet-name>HelloSeminar</servlet-name>
</filter-mapping>

<servlet>
<servlet-name>HelloSeminar</servlet-name>
<servlet-class>HelloSeminar</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloSeminar</servlet-name>

Regina Dietiker
c FH Aargau 15.5.2003
4.5. WRAPPER 14

<url-pattern>/servlet/HelloSeminar</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/servlet/LoginServlet</url-pattern>
</servlet-mapping>
</web-app>

Nachdem die web.xml-Datei entsprechend verändert wurde, muss die Filterklas-


se noch an den darin angegeben Pfad (in das Verzeichnis WEB-INF/classes/)
kopiert werden. Nach dem Verändern des web.xml muss Tomcat neu gestartet
werden. Anschliessend sind die Filter aktiv.

Abbildung 4.1: Ordnerstruktur von Tomcat

4.5 Wrapper

Ein Filter hat die Möglichkeit, das Request oder Response Objekt bei der Wei-
terverarbeitung durch Wrapper1 zu ersetzen.
1
Wrapper auch Decorater genannt. Hängt dynamisch Funktionalität an ein Objekt an, ohne
die Klasse zu verändern. Eine Wrapper-Klasse hat das gleiche Interface wie die Grundklasse,
für einen benutzenden Clienten spielt es keine Rolle ob das Objekt, das er instanziert ein
Wrapper-Objekt ist oder nicht.

Regina Dietiker
c FH Aargau 15.5.2003
4.5. WRAPPER 15

4.5.1 Request Wrapper

Ein Request Wrapper überschreibt normalerweise eine oder mehrere getter-


oder setter-Methode, um andere Werte für Parameter, Attribute oder Header
weiterzuschicken.

Abbildung 4.2: Vererbungshierarchie eines Request Wrappers

Regina Dietiker
c FH Aargau 15.5.2003
4.5. WRAPPER 16

4.5.2 Response Wrapper

Ein Response Wrapper hat die Funktion, die ursprüngliche Response abzufan-
gen, zu transformieren und das Ergebnis an den Client weiterzuschicken. Da-
her überschreibt ein Response Wrapper meist die Methoden getWriter() und
getOutputStream(). So wird die ursprüngliche Response nicht an den Out-
putStream der HTTP-Verbindung geschickt, sondern in den des Wrapppers, so
dass der Filter die Daten nachträglich manipulieren kann.

Abbildung 4.3: Vererbungshierarchie der Response Wrapper

Ein Replace Filter

Das folgende Beispiel soll alle @ durch die Buchstabenkombination AT ersetzen.


Dies ist ein Beispiel, wie man die Response mit Hilfe eines Response-Wrappers
verändern kann. Wenn der Filter zum ersten Mal durchlaufen wird, kapselt er
die Response in den StringServletResponseWrapper und lässt das Paket passie-
ren. Nachdem das Servlet seine Antwort in die gewrappte Response geschrieben
hat und diese auf dem Rückweg wieder zum ReplaceFilter gelangt ist, hat der
Filter Zugriff auf die Response und kann sie verändern. Die veränderte Response
schickt er dann dem Client.

import java.io.*;
import javax.servlet.http.*;

/**
* @author Regina Dietiker, Ic00
*
*/
public class StringServletResponseWrapper extends HttpServletResponseWrapper {
CharArrayWriter writer = new CharArrayWriter();
public StringServletResponseWrapper(HttpServletResponse response){
super(response);
}

public PrintWriter getWriter() throws java.io.IOException{


return new PrintWriter(writer);

Regina Dietiker
c FH Aargau 15.5.2003
4.5. WRAPPER 17

public String toString(){


return writer.toString();
}
}

import java.io.*;
import javax.servlet.http.*;
import javax.servlet.*;

public class ReplaceFilter implements Filter{


public void init(FilterConfig config){
}

public void doFilter(ServletRequest request, ServletResponse response,


FilterChain chain) throws IOException, ServletException{
HttpServletResponse res = (HttpServletResponse)response;
StringServletResponseWrapper wrapper = new StringServletResponseWrapper(res);
chain.doFilter(request, wrapper);
String responseString = wrapper.toString();
String oldString = new String("@");
String newString = new String("AT");
responseString.replaceAll(oldString, newString);
response.setContentLength(responseString.length());
response.getWriter().write(responseString);
}

public void destroy(){


}
}

Regina Dietiker
c FH Aargau 15.5.2003
Kapitel 5

Schluss

Servlet Filter sind ein mächtiges Instrument, das dem Programmierer erlaubt,
einem Servlet gewisse Funktionalitäten hinzuzufügen, ohne bestehenden Code
zu ändern. Die neue Funktionalität wird zudem noch mit wenig Code und einer
kleinen Schnittstelle (3 Interfaces) hinzugefügt. Dadurch bleibt das Servlet mit
seinen zusätzlichhen Funktionen übersichtlich.
Servlet Filter sind besonders nützlich um Funktionalitäten im Bereich von

• Logging,

• Sicherheit: Verschlüsselung, Authetifizierung, Zugriffsverwaltung,

• Daten Kompression und

• Formatänderung, Transformationem

zu implementieren. Aber laufend kommen neue Anwendungen dazu.


Die Filter sind ein gutes Beispiel für wiederverwendbare Komponenten. Sie
bieten die Möglichkeit Code, der nicht direkt mit der Logik des Servlets zusam-
menhängt, vom eigentlichen Servlet zu trennen und trotzdem die gewünschte
Funktionalität bereitzustellen. Dadurch wird die Modularität erhöht und das
Dokumentieren, Debuggen und Wiederverwenden wesentlich vereinfacht.
Um die Response eines Servlets oder einer JSP-Seite in ein anderes Format
zu transformieren, wurden bisher proprietäre Filtermechanismen benötigt, die
von den verschiedenen Servletcontainer bereit gestellt wurden. Mit den contai-
nerunabhängigen Sevlet Filter ist die Plattformunabhängigkeit auch in diesem
Bereich wieder gewährleistet.
Um einen Servlet Filter mit einem Servlet zusammen zu benutzen, muss man
den Filter in der gleichen web.xml-Datei registrieren, in der auch das Servlet
registriert wurde. Als erstes erscheint die Zuordnung des Filternamens zu der
Filterklasse, anschliessend wird der Filternamen noch dem betreffenden Serv-
let zugeordnet. Danach erfolgen die bereits vorhandenen Servlet Definitionen
19

und Zuordnungen. Dabei muss beachtet werden, dass die Reihenfolge genau so
eingehalten wird.

Regina Dietiker
c FH Aargau 15.5.2003
Ehrlichkeitserklärung

Hiermit bestätige ich, dass dieser Bericht nicht unter Verwendung unerlaubter
Hilfsmittel verfasst wurde.
Anhang A

Compression Filter

Ein weiteres, etwas komplexeres Beispiel für die Anwendung von Wrapper im
Zusammenhang mit Filter ist der folgende Kompressionsfilter. Der Compressi-
on Filter steht unter der Apache Software Licence und wurde von Amy Roh
geschrieben. Der Filter steht den Tomcat-Benutzern unter den Examples zur
Verfügung.

/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*/

package compressionFilters;

import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Implementation of <code>javax.servlet.Filter</code> used to compress
* the ServletResponse if it is bigger than a threshold.
*
* @author Amy Roh
* @author Dmitri Valdin
* @version $Revision: 1.8 $, $Date: 2002/07/16 17:19:13 $
*/

public class CompressionFilter implements Filter{


private FilterConfig config = null;
private int minThreshold = 128;
protected int compressionThreshold;
private int debug = 0;

public void init(FilterConfig filterConfig) {


config = filterConfig;
if (filterConfig != null) {
String value = filterConfig.getInitParameter("debug");
if (value!=null) {
debug = Integer.parseInt(value);
} else {
debug = 0;
}
String str = filterConfig.getInitParameter("compressionThreshold");
if (str!=null) {
compressionThreshold = Integer.parseInt(str);
if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
if (debug > 0) {
System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
System.out.println("compressionThreshold set to " + minThreshold);
22

}
compressionThreshold = minThreshold;
}
} else {
compressionThreshold = 0;
}

} else {
compressionThreshold = 0;
}
}

public void destroy() {


this.config = null;
}

public void doFilter ( ServletRequest request, ServletResponse response,


FilterChain chain ) throws IOException, ServletException {
if (debug > 0) {
System.out.println("@doFilter");
}

if (compressionThreshold == 0) {
if (debug > 0) {
System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression");
}
chain.doFilter(request, response);
return;
}

boolean supportCompression = false;


if (request instanceof HttpServletRequest) {
if (debug > 1) {
System.out.println("requestURI = " + ((HttpServletRequest)request).getRequestURI());
}

// Are we allowed to compress ?


String s = (String) ((HttpServletRequest)request).getParameter("gzip");
if ("false".equals(s)) {
if (debug > 0) {
System.out.println("got parameter gzip=false --> don’t compress, just chain filter");
}
chain.doFilter(request, response);
return;
}

Enumeration e =
((HttpServletRequest)request).getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
if (name.indexOf("gzip") != -1) {

if (debug > 0) {
System.out.println("supports compression");
}
supportCompression = true;
} else {
if (debug > 0) {
System.out.println("no support for compresion");
}
}
}
}

if (!supportCompression) {
if (debug > 0) {
System.out.println("doFilter gets called wo compression");
}
chain.doFilter(request, response);
return;
} else {
if (response instanceof HttpServletResponse) {
CompressionServletResponseWrapper wrappedResponse =
new CompressionServletResponseWrapper((HttpServletResponse)response);
wrappedResponse.setDebugLevel(debug);
wrappedResponse.setCompressionThreshold(compressionThreshold);
if (debug > 0) {
System.out.println("doFilter gets called with compression");
}
try {
chain.doFilter(request, wrappedResponse);
} finally {
wrappedResponse.finishResponse();
}
return;
}
}
}

public void setFilterConfig(FilterConfig filterConfig) {

Regina Dietiker
c FH Aargau 15.5.2003
23

init(filterConfig);
}

public FilterConfig getFilterConfig() {


return config;
}
}

Wenn der Filter geladen wird, sucht die init-Methode nach init-Parametern (in
der web.xml-Datei), welche festlegen, ab welcher Grösse ein Paket komprimiert
werden soll. Die doFilter-Methode prüft zuerst, ob das Pakett komprimiert
werden darf und übergibt gegebenenfalls das Paket an den Wrapper. Wenn im
Wrapper getOutputStream() oder getWriter() aufgerufen wird, wird statt
dem normalen Stream oder Writer ein CompressionResponseStream zurückge-
geben, welcher von ServletOutputStream abgeleitet ist und den Stream mit
java.util.zip.GZIPOutputStream komprimiert.
/*
* CompressionServletResponseWrapper.java
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
*
*/

package compressionFilters;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
* @author Amy Roh
* @author Dmitri Valdin
* @version $Revision: 1.9 $, $Date: 2002/09/16 19:55:27 $
*/

public class CompressionServletResponseWrapper extends HttpServletResponseWrapper {


public CompressionServletResponseWrapper(HttpServletResponse response) {
super(response);
origResponse = response;
if (debug > 1) {
System.out.println("CompressionServletResponseWrapper constructor gets called");
}
}

protected HttpServletResponse origResponse = null;


protected static final String info = "CompressionServletResponseWrapper";
protected ServletOutputStream stream = null;
protected PrintWriter writer = null;
protected int threshold = 0;
protected String contentType = null;
public void setContentType(String contentType) {
if (debug > 1) {
System.out.println("setContentType to "+contentType);
}
this.contentType = contentType;
origResponse.setContentType(contentType);
}

public void setCompressionThreshold(int threshold) {


if (debug > 1) {
System.out.println("setCompressionThreshold to " + threshold);
}
this.threshold = threshold;
}

public void setDebugLevel(int debug) {


this.debug = debug;
}

Regina Dietiker
c FH Aargau 15.5.2003
24

public ServletOutputStream createOutputStream() throws IOException {


if (debug > 1) {
System.out.println("createOutputStream gets called");
}

CompressionResponseStream stream = new CompressionResponseStream(origResponse);


stream.setDebugLevel(debug);
stream.setBuffer(threshold);

return stream;

public void finishResponse() {


try {
if (writer != null) {
writer.close();
} else {
if (stream != null)
stream.close();
}
} catch (IOException e) {
}
}

public void flushBuffer() throws IOException {


if (debug > 1) {
System.out.println("flush buffer @ CompressionServletResponseWrapper");
}
((CompressionResponseStream)stream).flush();

public ServletOutputStream getOutputStream() throws IOException {

if (writer != null)
throw new IllegalStateException("getWriter() has already been called for this response");

if (stream == null)
stream = createOutputStream();
if (debug > 1) {
System.out.println("stream is set to "+stream+" in getOutputStream");
}

return (stream);

public PrintWriter getWriter() throws IOException {

if (writer != null)
return (writer);

if (stream != null)
throw new IllegalStateException("getOutputStream() has already been called for this response");

stream = createOutputStream();
if (debug > 1) {
System.out.println("stream is set to "+stream+" in getWriter");
}

String charEnc = origResponse.getCharacterEncoding();


if (debug > 1) {
System.out.println("character encoding is " + charEnc);
}

if (charEnc != null) {
writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
} else {
writer = new PrintWriter(stream);
}

return (writer);

public void setContentLength(int length) {


}

private static String getCharsetFromContentType(String type) {

if (type == null) {
return null;
}
int semi = type.indexOf(";");
if (semi == -1) {
return null;

Regina Dietiker
c FH Aargau 15.5.2003
25

}
String afterSemi = type.substring(semi + 1);
int charsetLocation = afterSemi.indexOf("charset=");
if(charsetLocation == -1) {
return null;
} else {
String afterCharset = afterSemi.substring(charsetLocation + 8);
String encoding = afterCharset.trim();
return encoding;
}
}

/*
* CompressionResponseStream.java
* The Apache Software License, Version 1.1
*
*/

package compressionFilters;

import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

/**
* @author Amy Roh
* @author Dmitri Valdin
* @version $Revision: 1.6 $, $Date: 2002/07/16 17:15:18 $
*/

public class CompressionResponseStream


extends ServletOutputStream {

public CompressionResponseStream(HttpServletResponse response) throws IOException{

super();
closed = false;
this.response = response;
this.output = response.getOutputStream();

protected int compressionThreshold = 0;


private int debug = 0;
protected byte[] buffer = null;
protected int bufferCount = 0;
protected GZIPOutputStream gzipstream = null;
protected boolean closed = false;
protected int length = -1;
protected HttpServletResponse response = null;
protected ServletOutputStream output = null;
public void setDebugLevel(int debug) {
this.debug = debug;
}

protected void setBuffer(int threshold) {


compressionThreshold = threshold;
buffer = new byte[compressionThreshold];
if (debug > 1) {
System.out.println("buffer is set to "+compressionThreshold);
}
}

public void close() throws IOException {

if (debug > 1) {
System.out.println("close() @ CompressionResponseStream");
}
if (closed)
throw new IOException("This output stream has already been closed");

if (gzipstream != null) {
flushToGZip();
gzipstream.close();
gzipstream = null;
} else {
if (bufferCount > 0) {
if (debug > 2) {
System.out.print("output.write(");
System.out.write(buffer, 0, bufferCount);
System.out.println(")");
}
output.write(buffer, 0, bufferCount);
bufferCount = 0;

Regina Dietiker
c FH Aargau 15.5.2003
26

}
}

output.close();
closed = true;

public void flush() throws IOException {

if (debug > 1) {
System.out.println("flush() @ CompressionResponseStream");
}
if (closed) {
throw new IOException("Cannot flush a closed output stream");
}

if (gzipstream != null) {
gzipstream.flush();
}

public void flushToGZip() throws IOException {

if (debug > 1) {
System.out.println("flushToGZip() @ CompressionResponseStream");
}
if (bufferCount > 0) {
if (debug > 1) {
System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
}
writeToGZip(buffer, 0, bufferCount);
bufferCount = 0;
}

public void write(int b) throws IOException {

if (debug > 1) {
System.out.println("write "+b+" in CompressionResponseStream ");
}
if (closed)
throw new IOException("Cannot write to a closed output stream");

if (bufferCount >= buffer.length) {


flushToGZip();
}

buffer[bufferCount++] = (byte) b;

public void write(byte b[]) throws IOException {

write(b, 0, b.length);

public void write(byte b[], int off, int len) throws IOException {

if (debug > 1) {
System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
}
if (debug > 2) {
System.out.print("write(");
System.out.write(b, off, len);
System.out.println(")");
}

if (closed)
throw new IOException("Cannot write to a closed output stream");

if (len == 0)
return;

if (len <= (buffer.length - bufferCount)) {


System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
flushToGZip();

if (len <= (buffer.length - bufferCount)) {


System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
writeToGZip(b, off, len);

Regina Dietiker
c FH Aargau 15.5.2003
27

public void writeToGZip(byte b[], int off, int len) throws IOException {

if (debug > 1) {
System.out.println("writeToGZip, len = " + len);
}
if (debug > 2) {
System.out.print("writeToGZip(");
System.out.write(b, off, len);
System.out.println(")");
}
if (gzipstream == null) {
if (debug > 1) {
System.out.println("new GZIPOutputStream");
}
gzipstream = new GZIPOutputStream(output);
response.addHeader("Content-Encoding", "gzip");
}
gzipstream.write(b, off, len);
}

public boolean closed() {

return (this.closed);
}
}

/*
* CompressionFilterTestServlet.java
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.

*/

package compressionFilters;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;

/**
* Very Simple test servlet to test compression filter
* @author Amy Roh
* @version $Revision: 1.4 $, $Date: 2002/06/18 19:53:25 $
*/

public class CompressionFilterTestServlet extends HttpServlet {


public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletOutputStream out = response.getOutputStream();
response.setContentType("text/plain");
Enumeration e = ((HttpServletRequest)request).getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
out.println(name);
if (name.indexOf("gzip") != -1) {
out.println("gzip supported -- able to compress");
}
else {
out.println("gzip not supported");
}
}
out.println("Compression Filter Test Servlet");
out.close();
}

Regina Dietiker
c FH Aargau 15.5.2003
Anhang B

web.xml-Skeleton

<?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>
<filter>
<filter-name>Name des Filters</filter-name>
<filter-class>BspFilter</filter-class>
<init-param>
<param-name>text</param-name>
<param-value>das ist ein bsp</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Name des Filters</filter-name>
<servlet-name>ServletBeispiel</servlet-name>
</filter-mapping>

<servlet>
<servlet-name>ServletBeispiel</servlet-name>
<servlet-class>BspServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>ServletBeispiel</servlet-name>
<url-pattern>/bsp</url-pattern>
</servlet-mapping>
</web-app>
Abbildungsverzeichnis

2.1 Verarbeitungskette mit Filter . . . . . . . . . . . . . . . . . . . . 3


2.2 Chain of Responsibility Pattern . . . . . . . . . . . . . . . . . . . 5

4.1 Ordnerstruktur von Tomcat . . . . . . . . . . . . . . . . . . . . . 14


4.2 Vererbungshierarchie eines Request Wrappers . . . . . . . . . . . 15
4.3 Vererbungshierarchie der Response Wrapper . . . . . . . . . . . . 16
Literaturverzeichnis

[1] Hunter, Jason Filter code with Servlet 2.3 model. JavaWorld
http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters p.html
2001.

[2] Sun Micorsystems


The Essential of Filters. Sun Microsystems, Inc.
http://java.sun.com/products/servlet/Filters.html.

[3] Wang, Dapeng Mehr als KaffeeFilter Javamagazin


http://www.javamagazin.de/itr/online artikel/psecom,id,291,nodeid,11.html
2003.

[4] Gamma, Erich; Helm, Richard; Johnson, Ralf; Vlissides John


Design Patterns. Elements of Reusable Object-Oriented Software
Addison Wesley, 1995.

[5] Gould, Steven


Servlets in Apache Tomcat und BEA Systems’ WEBLogic Server. Deploy
servlets annd Web applications in two popular application servers
Java World
http://www.javaworld.com/javaworld/jw-02-2001/jw-0223-servletweblogic-
p3.html