Filterprogrammierung: Beispiel package de.rainer_klute.servlet; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** * <p>This demo filter replaces a configurable hostname in a * {@link ServletRequest} by a replacement string.</p> */ public class HostChangeFilter implements Filter { FilterConfig config; String hostname; String replacement; public void init(FilterConfig config) { /* Could be used to fetch the ServletContext. */ this.config = config; hostname = config.getInitParameter("Hostname"); replacement = config.getInitParameter("Replacement"); } Web−Anwendungen mit Java 351 Filterprogrammierung: Beispiel public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { ServletRequest wrapper = new HttpServletRequestWrapper((HttpServletRequest) req) { public String getRemoteHost() { final String s = getRequest().getRemoteHost(); if (s.equals(hostname)) return replacement; else return s; } }; chain.doFilter(wrapper, res); } public void destroy() {} Web−Anwendungen mit Java } 352 Filterprogrammierung Zentrale Methode: FilterChain.doFilter(ServletRequest request, ServletResponse response) Ruft nächsten Filter in der Filterkette oder Ressource Parameter: Wrapper oder Original−Request/Response ServletRequestWrapper, ServletResponseWrapper HttpServletRequestWrapper, HttpServletResponseWrapper Implementieren ServletRequest, ServletResponse, HttpServletRequest, HttpServletResponse Tomcat 4.0.3 erwartet HttpServletRequest und HttpServletResponse. Methoden überschreiben, neue definieren Web−Anwendungen mit Java 353 Filterkonfiguration Konfiguration im Deployment descriptor Definition: <filter> <filter−name>Filter 1</filter−name> <filter−class> de.rainer_klute.servlet.HostChangeFilter </filter−class> <init−param> <param−name>Hostname</param−name> <param−value>mark.rainer−klute.de</param−value> </init−param> <init−param> <param−name>Replacement</param−name> <param−value>foo.bar.de</param−value> </init−param> </filter> Web−Anwendungen mit Java 354 Filterkonfiguration Mapping: <filter−mapping> <filter−name>Filter 1</filter−name> <servlet−name>Snoop</servlet−name> </filter−mapping> <filter−mapping> <filter−name>Filter 2</filter−name> <servlet−name>Snoop</servlet−name> </filter−mapping> Aufrufreihenfolge entspricht der Reihenfolge im Deployment descriptor Im Beispiel: Filter 1 → Filter 2 → Snoop Web−Anwendungen mit Java 355 Anwendungsereignisse Ereignisse, die eine Web−Anwendung als ganzes betreffen Servlet−Kontext Sessions potentiell alle Servlets der Anwendung Web−Anwendungen mit Java 356 Anwendungsereignisse: Interfaces Listener−Interfaces javax.servlet.ServletContextListener Erzeugen und Beenden des Servlet−Kontexts javax.servlet.ServletContextAttributeListener Änderungen an ServletContext−Attributen javax.servlet.http.HttpSessionActivationListener Aktivieren und Deaktivieren von Sessions javax.servlet.http.HttpSessionListener Erzeugen und Beenden von Sessions javax.servlet.http.HttpSessionAttributeListener Änderungen an Session−Attributen Servlet−Container stellt den Implementierungen Event−Objekte zu. Web−Anwendungen mit Java 357 Anwendungsereignisse: Beispiel package de.rainer_klute.servlet; import javax.servlet.*; import javax.servlet.http.*; public class SessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent e) { // ... } public void sessionDestroyed(HttpSessionEvent e) { // ... } } Web−Anwendungen mit Java 358 Listener−Klassen registrieren Registrierung im Deployment Descriptor nötig Beispiel: <listener> <listener−class> de.rainer_klute.servlet.SessionListener </listener−class> </listener> <listener> <listener−class> de.rainer_klute.servlet.FooListener </listener−class> </listener> Web−Anwendungen mit Java 359 JDBC JDBC Web−Anwendungen mit Java 360 JDBC Standard−API für den Java−Zugriff auf relationale Datenbanksysteme Oracle, DB2, Informix, Adabas D PostgreSQL, MySQL usw. Web−Anwendungen mit Java 361 Erwartungen Was Sie nicht erwarten können Einführung in relationale Datenbanksysteme (Relationenalgebra, Relationenkalkül) Anfragesprache SQL Was Sie erwarten können Einführung in JDBC Beispiele mit Datenbanksystem PostgreSQL Web−Anwendungen mit Java 362 Vertiefung Dokumentation zu PostgreSQL lesen Beschreibt nicht nur PostgreSQL selbst, sondern auch... Relationale Datenbanksysteme Anfragesprache SQL Literaturverweise Web−Anwendungen mit Java 363 DBMS−Typen Database Management System Relationales DBMS (RDBMS) Speichert Daten in Tabellen (Relationen). Abfragesprache SQL Objektorientiertes DBMS (OODBMS) Speichert Objekte unmittelbar. Objektrelationales DBMS (ORDBMS) Oberbegriff für Datenbanksysteme Bietet sowohl Objekt− als auch Relationensicht. JDBC unterstützt (objekt−)relationale Datenbanksysteme. Web−Anwendungen mit Java 364 Datenbank Tabellen (Relationen) Daten Beispiele: Artikel eines Katalogs, Bestellungen Views Sichten auf Daten Beispiel: Artikel sortiert nach Umsatz Indizes »Vorsortierungen« von Tabellen Stored Procedures In der Datenbank gespeicherte Programme Web−Anwendungen mit Java 365 Tabellen Darstellung von Relationen (siehe Fachliteratur) Beispiele: PostgreSQL »The most advanced open source database management system« Läuft unter Unix (diverse Varianten, u.a. Linux) und Windows (mit Cygwin) Fast vollständige Unterstützung des Standards SQL92 Transaktionen (im Unterschied zu MySQL) Zusätzliche Features ORDBMS, max. Zellengröße 1 GB, Tupelgröße unbegrenzt u.v.a.m. Geringer Hauptspeicherbedarf Sehr gute Dokumentation Web−Anwendungen mit Java 367 PostgreSQL als Client−/Server−System Client−/Server−System Prozeß postmaster Läuft permanent auf Server−Maschine. Nimmt Verbindungsaufbauwünsche über das Netz entgegen. Datenbank−Client baut Verbindung zum Postmaster auf. Postmaster startet Datenbank−Server−Prozeß postgres für den Client. Web−Anwendungen mit Java 368 PostgreSQL als Client−/Server−System Sitzung Client: schickt SQL−Anweisung zum Server. Server: schickt Ergebnis zum Client. usw. Client beendet Verbindung. Server−Prozeß terminiert. Web−Anwendungen mit Java 369 SQL−Monitor »psql« Zu PostgreSQL gehörender Datenbank−Client Interaktives Arbeiten mit Datenbanken SQL−Anweisungen. Datenbanken anlegen, Tabellen definieren, Daten ändern, Datenbankanfragen absetzen usw. usw. Baut Verbindung zum Datenbank−Server auf (genauer: zum Postmaster−Prozeß). Beispiel: psql −h carne01 eshop Baut Verbindung zum Postmaster auf Rechner carne01 auf und öffnet Datenbank eshop. Weitere Informationen: Kommando »man psql« Web−Anwendungen mit Java 370 SQL−Monitor »psql« SQL−Anweisungen an Datenbank−Server SELECT * FROM artikel; SQL−Anweisungen mit »;« beenden Hilfe zu SQL−Anweisungen Kommando »man create_table« usw. psql−Anweisung »\h create table« Anweisungen an psql Beispiel: \H schaltet HTML−Modus ein oder aus Beispiel: \i datei.sql SQl−Anweisungen aus datei.sql lesen und ausführen Hilfe zu allen psql−Anweisungen mit »\?« Web−Anwendungen mit Java 371 E−Shop−Datenbank einrichten SQL−Monitor psql starten psql −h carne01 template1 Siehe unten: PostgreSQL−Umgebung Öffnet Datenbank template1 (in PostgreSQL immer vorhanden) Datenbank eshop erzeugen CREATE DATABASE eshop; Statt eshop sollte jeder Benutzer einen anderen Datenbanknamen verwenden. Beispiel: CREATE DATABASE eshop_pkjf123; Zu neuer Datenbank eshop wechseln \c eshop Web−Anwendungen mit Java 372 E−Shop−Datenbank einrichten Tabelle artikel definieren CREATE TABLE artikel (id INTEGER PRIMARY KEY, bezeichnung CHARACTER VARYING NOT NULL UNIQUE, text CHARACTER VARYING, preis DECIMAL(10,2) NOT NULL CHECK (preis > 0)); PRIMARY KEY: Wert identifiziert Artikel eindeutig. CHARACTER VARYING: Textfeld variabler Länge NOT NULL: Feld darf nicht leer sein. UNIQUE: Feldwert ist für gesamte Tabelle eindeutig. DECIMAL(10,2): Dezimalwert mit 10 Stellen Genauigkeit und 2 Nachkommastellen CHECK (preis > 0): Wert von preis muß größer 0 sein. Web−Anwendungen mit Java 373 E−Shop−Datenbank einrichten Tabelle artikel mit Daten füllen INSERT INTO artikel (id, bezeichnung, preis) VALUES (4711, ’Duftwasser’, 15.95); INSERT INTO artikel (id, bezeichnung, preis) VALUES (0815, ’ISDN−Telefon’, 123.45); INSERT INTO artikel (id, bezeichnung, preis) VALUES (1234, ’Notebook’, 2500); INSERT INTO artikel (id, bezeichnung, preis) VALUES (538, ’Lolly’, 0.05); DBMS prüft Daten auf Gültigkeit. INSERT INTO artikel (id, bezeichnung, preis) VALUES (0815, ’Lolly’, 0); ExecAppend: rejected due to CHECK constraint artikel_preis Web−Anwendungen mit Java 374 E−Shop−Datenbank einrichten Tabelle kunden definieren und mit Daten füllen CREATE TABLE kunden (id INTEGER PRIMARY KEY, konto INTEGER NOT NULL, blz INTEGER NOT NULL, name CHARACTER VARYING); INSERT INTO kunden VALUES (10017, 123456700, 38070724, ’Karl Käufer’); INSERT INTO kunden VALUES (12659, 234567890, 44050199, ’Karin Kunde’); Weitere Kundendaten wie Rechnungsadresse, Lieferanschrift, Bonität, Rabattklasse usw. werden hier nicht unterstützt. Web−Anwendungen mit Java 375 E−Shop−Datenbank einrichten Tabelle bestellungen definieren CREATE TABLE bestellungen (artikel INTEGER REFERENCES artikel (id), kunde INTEGER REFERENCES kunden (id), anzahl INTEGER NOT NULL); REFERENCES artikel (id): Feld ist Fremdschlüssel. Referenziert Schlüssel id in Tabelle artikel. Referenzierte Zeilen können nicht gelöscht werden (Voreinstellung). Tabelle mit Daten füllen INSERT INTO bestellungen VALUES ( 815, 10017, 1); INSERT INTO bestellungen VALUES (1234, 10017, 1); INSERT INTO bestellungen VALUES ( 815, 12659, 2); Web−Anwendungen mit Java 376 Referentielle Integrität Versuch scheitert, eine referenzierte Zeile zu löschen. DELETE FROM artikel WHERE id = 0815; ERROR: <unnamed> referential integrity violation − key in artikel still referenced from bestellungen Web−Anwendungen mit Java 377 Daten abfragen Tabelle anzeigen: alle Artikel auflisten SELECT * FROM artikel; id | bezeichnung | text | preis −−−−−−+−−−−−−−−−−−−−−+−−−−−−+−−−−−−−−− 4711 | Duftwasser | | 15.95 815 | ISDN−Telefon | | 123.45 1234 | Notebook | | 2500.00 538 | Lolly | | 0.05 (4 rows) SELECT−Anweisung stellt Datenbank−Anfrage (»Query«). Stern (*) selektiert alle Spalten der Tabelle. Web−Anwendungen mit Java 378 Daten abfragen Tabellen kombinieren SELECT * FROM artikel, bestellungen; Kombiniert jede Zeile der ersten Tabelle mit jeder Zeile der zweiten Tabelle (kartesisches Produkt). Daten abfragen Ergebnisrelation auf interessante Tupel einschränken (Selektion) Bestellte Artikel SELECT * FROM artikel, bestellungen WHERE artikel.id = bestellungen.artikel; Web−Anwendungen mit Java 380 Daten abfragen Ergebnisspalten auswählen (Projektion) Bezeichnung der Ergebnisspalten wählen Spalteninhalt dynamisch berechnen SELECT name AS "Name", bezeichnung AS "Artikel", anzahl AS "Stückzahl", preis AS "Einzelpreis", (anzahl * preis) AS "Gesamtpreis" FROM bestellungen, artikel, kunden WHERE artikel.id = bestellungen.artikel AND kunden.id = bestellungen.kunde; PostgreSQL auf den Übungsrechnern PostgreSQL 7.2 installiert in /home/pkjf/pkjf000/postgresql Unterverzeichnisse: bin: Programme, z.B. psql, postmaster lib: Shared libraries, JDBC−Implementierung doc/html: Dokumentation man: Manualseiten Web−Anwendungen mit Java 382 PostgreSQL auf den Übungsrechnern Umgebung(svariablen) für PostgreSQL einrichten Kommandos für Shell sh bzw. bash: Zum automatischen Ausführen beim Einloggen in Datei ~/.bashrc aufnehmen! # Installationsverzeichnis PGROOT="/home/pkjf/pkjf000/postgresql" # Suchpfad für Programme erweitern: PATH="${PGROOT}/bin:${PATH}" # Suchpfad für Shared libraries setzen # (bei Bedarf erweitern): LD_LIBRARY_PATH="${PGROOT}/lib" # Suchpfad für Manualseiten erweitern: MANPATH="${PGROOT}/man:${MANPATH}" Web−Anwendungen mit Java 383 PostgreSQL auf den Übungsrechnern # Suchpfad für Java−Klassen erweitern: CLASSPATH="${CLASSPATH}:${PGROOT}/lib/postgresql.jar" # Port des PostgreSQL−Servers: PGPORT="5432" # Umgebungsvariablen exportieren: export PATH export LD_LIBRARY_PATH export CLASSPATH export PGPORT Shells csh und tcsh: set PGROOT="/home/pkjf/pkjf000/postgresql" setenv PATH "${PGROOT}/bin:${PATH}" # usw. Zum automatischen Ausführen beim Einloggen in Datei ~/.cshrc aufnehmen! Web−Anwendungen mit Java 384 PostgreSQL auf den Übungsrechnern Datenbank−Server läuft auf carne01. Start des Datenbank−Servers erfolgt manuell unter der Benutzerkennung pkjf000: postmaster −D /home/pkjf/pkjf000/postgresql/data \ −i −p 5703 \ >/tmp/pgsql.log 2>&1 & Voreinstellung: maximal 32 gleichzeitige Verbindungen Benutzer pjkf000 bis pkjf099 sind eingerichtet. Web−Anwendungen mit Java 385 Eigene PostgreSQL−Konfiguration PostgreSQL erlaubt den Betrieb eigener Datenbank− Cluster Wichtig, falls Postmaster auf carne01 einmal nicht laufen sollte Verzeichnis für Datenbank−Cluster einrichten und mit initdb initialisieren mkdir /tmp/mycluster initdb −D /tmp/mycluster Leerer Cluster belegt ca. 20 MB Plattenplatz. Sicherheit des Clusters in Datei pg_hba.conf konfigurieren Details siehe Kommentare in dieser Datei Web−Anwendungen mit Java 386 Eigene PostgreSQL−Konfiguration Postmaster starten postmaster −i −D /tmp/mycluster −p 5555 TCP/IP−Zugriff mit −i erlauben Eigenes Cluster−Verzeichnis mit −D angeben Eigenen Port mit −p angeben Weitere Informationen: PostgreSQL−Dokumentation »man postmaster« »man psql« Web−Anwendungen mit Java 387 Sicherheit auf den Übungsrechnern Die PostgreSQL−Konfiguration auf den Übungsrechnern bietet keine hohe Sicherheit. Kein Paßwortschutz Jeder Benutzer kann sich mit jeder Datenbank verbinden. Paßwort bei JDBC−Verbindungen wird ignoriert. Tabellen gehören dem Benutzer, der sie angelegt hat. Zugriffsrechte mit GRANT weitergeben GRANT select ON mytable TO pkjf123; Zugriffsrechte mit REVOKE entziehen REVOKE ALL ON mytable FROM pkjf123; Web−Anwendungen mit Java 388 Sicherheit auf den Übungsrechnern Netzzugriff nur für Übungsrechner carne01 bis carne12 erlaubt Das heißt: kein Zugriff auf die Datenbanken von außen Entspricht dem üblichen Einsatzszenario der Dreischichtenarchitektur Browser → Applikationsserver → DBMS Web−Anwendungen mit Java 389 Dreischichtenarchitektur »Three tier architecture« Browser Firewall (erlaubt nur HTTP und HTTPS) Web−Anwendungen mit Java Applikation s−Server DBMS 390 Wichtige PostgreSQL−Besonderheiten VACUUM [FULL] VACUUM ANALYZE Aktualisiert Datenbankstatistiken für Optimizer EXPLAIN SELECT ... Gewinnt Platz gelöschter Datensätze zurück Ausführungsplan einer Anfrage zeigen Programme initdb initialisiert Datenbank−Cluster pg_dump extrahiert eine komplette Datenbank als SQL− Skript oder in weiteren Formaten. pg_restore restauriert eine Datenbank oder Teile davon. Web−Anwendungen mit Java 392 JDBC−Beispiel: Daten abfragen package de.rainer_klute.jdbc; import java.io.*; import java.sql.*; /** * <p>E−Shop−Bestellungen als HTML−Tabelle.</p> */ public class Bestellungen { public static void main(String argv[]) throws ClassNotFoundException, SQLException { /* JDBC−Treiber laden */ Class.forName("org.postgresql.Driver"); /* JDBC−URL der Datenbank spezifizieren */ String url = "jdbc:postgresql://mark:5712/eshop"; Web−Anwendungen mit Java 393 JDBC−Beispiel: Daten abfragen /* Datenbankverbindung aufbauen */ Connection con = DriverManager.getConnection (url, argv[0], argv[1]); /* Statement erzeugen */ Statement stmt = con.createStatement(); /* SQL−Abfrage ausführen − * Ergebnis ist ein ResultSet */ String query = "SELECT name AS \"Name\"," + " bezeichnung AS \"Artikel\"," + " anzahl AS \"Stückzahl\"," + " preis AS \"Einzelpreis\"," + " (anzahl * preis) AS" + " \"Gesamtpreis\"" + " FROM bestellungen, artikel, kunden" + " WHERE artikel.id = bestellungen.artikel" + " AND kunden.id = bestellungen.kunde"; ResultSet rs = stmt.executeQuery(query); Web−Anwendungen mit Java 394 JDBC−Beispiel: Daten abfragen /* Ergebnisrelation als HTML−Tabelle ausgeben */ PrintStream p = System.out; p.println("<!DOCTYPE HTML PUBLIC " + "\"−//W3C//DTD HTML 4.0//EN//\">"); p.println("<TITLE>SQL−Abfrage mit JDBC</TITLE>"); p.println("<H1>SQL−Abfrage mit JDBC</H1>"); p.println("<H2>Abfrage:</H2>"); p.println("<P>" + query + "</P>"); p.println("<H2>Ergebnistabelle:</H2>"); p.println("<TABLE BORDER=’1’>"); /* Anzahl der Spalten ermitteln */ ResultSetMetaData rsmd = rs.getMetaData(); int columns = rsmd.getColumnCount(); Web−Anwendungen mit Java 395 JDBC−Beispiel: Daten abfragen /* Spaltennamen aus Metadaten ermitteln und in * erste Tabellenzeile ausgeben */ p.println("<TR>"); for (int i = 1; i <= columns; i++) p.println("<TH><P>" + rsmd.getColumnName(i) + "</P></TH>"); p.println("</TR>"); /* Ergebniszeilen ausgeben */ while (rs.next()) { p.println("<TR>"); for (int i = 1; i <= columns; i++) { /* Zelleninhalt ausgeben */ Object o = rs.getObject(i); p.print("<TD><P>" + o + "</P></TD>"); } p.println("</TR>"); } Web−Anwendungen mit Java 396 JDBC−Beispiel: Daten abfragen p.println("</TABLE>"); /* ResultSet schließen */ rs.close(); /* Statement−Objekt schließen */ stmt.close(); } /* Datenbankverbindung schließen */ con.close(); } Web−Anwendungen mit Java 397 JDBC−Konzepte Pakete java.sql enthält JDBC−Basisfunktionalität. javax.sql enthält zusätzliche Funktionalität für Row sets und Connection pooling. Wird hier behandelt. In J2EE (Java 2 Enterprise Edition) enthalten JDBC−Treiber kapseln die DBMS−spezifischen Mechanismen. Aufbau der Verbindung zur Datenbank Datenaustausch Unterschiede in SQL−Dialekten Web−Anwendungen mit Java 398 JDBC−Treiber laden Class.forName("org.postgresql.Driver"); Lädt die Klasse org.postgresql.Driver. JDBC−Treiber registriert sich beim DriverManager. Alternative: JDBC−Treiber beim Programmstart als Property jdbc.drivers spezifizieren java \ −Djdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver:org.postgresql.Driver \ de.rainer_klute.jdbc.Bestellungen Vorteile beim Verwenden von jdbc.drivers: JDBC−Treiber nicht fest vorgeben Nutzung eines anderen JDBC−Treibers erfordert im Idealfall keine Änderung des Quellcodes. Web−Anwendungen mit Java 399 JDBC−Treiber JDBC selbst besteht im wesentlichen aus Interfaces. Connection, Statement, ResultSet usw. Implementierung der Interfaces durch den JDBC− Treiber org.postgresql.jdbc2.Connection, org.postgresql.jdbc2.Statement, org.postgresql.jdbc2.ResultSet usw. JDBC−Treiber stammen in der Regel vom DBMS− Anbieter. Der Entwickler programmiert nur mit den Interfaces, nicht mit den Treiberklassen. Web−Anwendungen mit Java 400